Manage integration mapping using Terraform
In this example we'll use the Import State feature of Terraform, then manage our Port integration's mapping with Terraform.
Prerequisitesโ
You will need to create an integration within the Port UI, For example the Kubernetes Exporter
As you create the integration, take note of the installation Id.
Getting startedโ
In order to interact with the API you will need an API token.
Getting an API token involves 2 steps:
- Finding your Port API credentials.
- Making an API request to generate a valid token.
Find your Port credentialsโ
Get your Port credentials
To get your Port credentials, go to your Port application, click on the ...
button in the top right corner, and select Credentials
. Here you can view and copy your CLIENT_ID
and CLIENT_SECRET
:
Download your integration configuration into a fileโ
Here are some code examples showing how to download the integration configuration in various programming languages:
- cURL
- Python
- Javascript
#/usr/bin/env bash
# Dependencies to install:
# For apt:
# $ sudo apt-get install jq
# For yum:
# $ sudo yum install jq
INSTALLATION_ID="YOUR_INSTALLATION_ID"
CLIENT_ID="YOUR_CLIENT_ID"
CLIENT_SECRET="YOUR_CLIENT_SECRET"
ACCESS_TOKEN=$(curl -s --location -X POST 'https://api.getport.io/v1/auth/access_token' \
--header 'Content-Type: application/json' \
--data-raw """{
\"clientId\": \"${CLIENT_ID}\",
\"clientSecret\": \"${CLIENT_SECRET}\"
}""" | jq -r '.accessToken')
curl -s -X GET \
"https://api.getport.io/v1/integration/${INSTALLATION_ID}?byField=installationId" \
-H 'accept: */*' \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
| jq '.integration.config' > ./${INSTALLATION_ID}.json
# Dependencies to install:
# $ python -m pip install requests
import json
import requests
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'
INSTALLATION_ID = 'YOUR_INSTALLATION_ID'
API_URL = 'https://api.getport.io/v1'
credentials = {'clientId': CLIENT_ID, 'clientSecret': CLIENT_SECRET}
token_response = requests.post(f'{API_URL}/auth/access_token', json=credentials)
access_token = token_response.json()['accessToken']
headers = {
'Authorization': f'Bearer {access_token}'
}
integration_response = requests.get(
f'{API_URL}/integration/{INSTALLATION_ID}?byField=installationId',
headers=headers)
with open(f'{INSTALLATION_ID}.json', 'w') as file:
file.write(json.dumps(integration_response.json()['integration']['config']))
// Dependencies to install:
// $ npm install axios --save
const fs = require('node:fs/promises');
const axios = require("axios").default;
const CLIENT_ID = "YOUR_CLIENT_ID";
const CLIENT_SECRET = "YOUR_CLIENT_SECRET";
const INSTALLATION_ID = "YOUR_INSTALLATION_ID";
const API_URL = "https://api.getport.io/v1";
(async () => {
const response = await axios.post(`${API_URL}/auth/access_token`, {
clientId: CLIENT_ID,
clientSecret: CLIENT_SECRET,
});
const accessToken = response.data.accessToken;
const config = {
headers: {
Authorization: `Bearer ${accessToken}`,
}
};
const integrationResponse = await axios.post(
`${API_URL}/v1/integration/${INSTALLATION_ID}?byField=installationId`,
config)
await fs.writeFile(
`./${INSTALLATION_ID}.json`,
JSON.stringify(integrationResponse.data.integration.config)
)
})();
The baseUrl
, port_region
, port.baseUrl
, portBaseUrl
, port_base_url
and OCEAN__PORT__BASE_URL
parameters are used to select which instance or Port API will be used.
Port exposes two API instances, one for the EU region of Port, and one for the US region of Port.
- If you use the EU region of Port (https://app.getport.io), your API URL is
https://api.getport.io
. - If you use the US region of Port (https://app.us.getport.io), your API URL is
https://api.us.getport.io
.
Here is the complete main.tf
file:
main.tf
dependencyNotice that the main.tf
file references the downloaded file generated in the previous command
See the file("${path.module}/...")
function
Complete Terraform definition file
terraform {
required_providers {
port = {
source = "port-labs/port-labs"
version = "~> 2.0.3"
}
}
provider "port" {
client_id = "YOUR_CLIENT_ID" # or set the environment variable PORT_CLIENT_ID
secret = "YOUR_CLIENT_SECRET" # or set the environment variable PORT_CLIENT_SECRET
base_url = "https://api.getport.io"
}
resource "port_integration" "tf_{my-installation-id}" {
installation_id = "{my-installation-id}"
installation_app_type = "{my-installation-type}"
title = "{my-installation-title}"
version = ""
# The reason for the jsonencode|jsondecode is
# to preserve the exact syntax as terraform expects,
# this resolves conflicts in the state caused by indents
config = jsonencode(jsondecode(file("${path.module}/{my-installation-id}.json")))
}
The baseUrl
, port_region
, port.baseUrl
, portBaseUrl
, port_base_url
and OCEAN__PORT__BASE_URL
parameters are used to select which instance or Port API will be used.
Port exposes two API instances, one for the EU region of Port, and one for the US region of Port.
- If you use the EU region of Port (https://app.getport.io), your API URL is
https://api.getport.io
. - If you use the US region of Port (https://app.us.getport.io), your API URL is
https://api.us.getport.io
.
Let's break down the definition file and understand the different parts:
Module importsโ
This part includes importing and setting up the required Terraform providers and modules:
terraform {
required_providers {
port = {
source = "port-labs/port-labs"
version = "~> 2.0.3"
}
}
}
provider "port" {
client_id = "YOUR_CLIENT_ID" # or set the environment variable PORT_CLIENT_ID
secret = "YOUR_CLIENT_SECRET" # or set the environment variable PORT_CLIENT_SECRET
base_url = "https://api.getport.io"
}
The baseUrl
, port_region
, port.baseUrl
, portBaseUrl
, port_base_url
and OCEAN__PORT__BASE_URL
parameters are used to select which instance or Port API will be used.
Port exposes two API instances, one for the EU region of Port, and one for the US region of Port.
- If you use the EU region of Port (https://app.getport.io), your API URL is
https://api.getport.io
. - If you use the US region of Port (https://app.us.getport.io), your API URL is
https://api.us.getport.io
.
Port Terraform Provider Integration Resourceโ
This part includes declaring the Port integration and pointing the downloaded json file as the source of the integration configuration
resource "port_integration" "tf_{my-installation-id}" {
installation_id = "{my-installation-id}"
installation_app_type = "{my-installation-type}"
title = "{my-installation-title}"
version = ""
# The reason for the jsonencode|jsondecode is
# to preserve the exact syntax as terraform expects,
# this resolves conflicts in the state caused by indents
config = jsonencode(jsondecode(file("${path.module}/{my-installation-id}.json")))
}
Since Terraform is very explicit when writing and reading the state, we use jsonencode
and jsondecode
on the raw JSON file to make sure we do not have a conflict simply by having a bit different formatting than the Terraform JSON formatting.
To use this example yourself, simply replace the placeholders for installation_id
, client_id
and secret
and then run the following commands to setup terraform, import the state and verifying that everything was imported:
# install modules and create an initial state
terraform init
# import state from the Port API
terraform import port_integration.tf_{my-installation-id} {my-installation-id}
# To view Terraform's planned changes based on your .tf definition file:
terraform plan
Resultโ
After running terraform plan
you should see that there are not changes from the imported state.