Manage a Developer Environment Lifecycle
In this example you are going to create a developer environment in AWS then report its information to Port as a developer environment entity.
Our developer environment will include:
- An SQS queue which receives messages.
- A Lambda function that is triggered by messages sent to the queue.
- An S3 bucket that stores the artifacts generated by the S3 function.
Prerequisitesโ
Since we are creating a lambda function, we need to provide its source code. You can download a basic Nodejs Lambda template by clicking here (be sure to save the .zip
file next to the main.tf
file).
You will need to create a developer environment blueprint to follow this example:
- API
- Terraform
{
"identifier": "developerEnvironment",
"description": "",
"title": "Developer Environment",
"icon": "Environment",
"schema": {
"properties": {
"bucketUrl": {
"type": "string",
"title": "Bucket URL",
"format": "url"
},
"lambdaUrl": {
"type": "string",
"title": "Lambda URL",
"format": "url"
},
"queueUrl": {
"type": "string",
"title": "Queue URL",
"format": "url"
},
"memorySize": {
"type": "number",
"title": "Memory Size"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}
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_blueprint" "developer_environment" {
identifier = "developerEnvironment"
icon = "Environment"
title = "Developer Environment"
properties = {
string_props = {
"bucketUrl" = {
title = "Bucket URL"
format = "url"
required = false
}
"queueUrl" = {
title = "Queue URL"
format = "url"
required = false
}
"lambdaUrl" = {
title = "Lambda URL"
format = "url"
required = false
}
}
number_props = {
"memorySize" = {
title = "Memory Size"
required = false
}
}
}
}
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:
Complete Terraform definition file
terraform {
required_providers {
port = {
source = "port-labs/port-labs"
version = "~> 2.0.3"
}
}
}
provider "aws" {
access_key = "YOUR_ACCESS_KEY_ID"
secret_key = "YOUR_SECRET_ACCESS_KEY"
region = "eu-west-1"
}
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 "aws_s3_bucket" "port_terraform_example_dev_env_bucket" {
bucket = "my-port-terraform-example-dev-env-bucket"
}
resource "aws_s3_bucket_acl" "port_terraform_example_dev_env_bucket-acl" {
bucket = aws_s3_bucket.port_terraform_example_dev_env_bucket.id
acl = "private"
}
resource "aws_sqs_queue" "port_terraform_example_dev_env_queue" {
name = "terraform-example-queue.fifo"
fifo_queue = true
content_based_deduplication = true
}
resource "aws_iam_role" "iam_for_lambda" {
name = "iam_for_lambda"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "attach_sqs_policy_for_lambda_role" {
role = aws_iam_role.iam_for_lambda.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole"
}
resource "aws_lambda_function" "port_terraform_example_dev_env_lambda" {
depends_on = [
aws_sqs_queue.port_terraform_example_dev_env_queue
]
filename = "lambda_function_payload.zip"
function_name = "dev-env-function"
role = aws_iam_role.iam_for_lambda.arn
handler = "index.test"
runtime = "nodejs16.x"
}
resource "aws_lambda_function_url" "example_function_url" {
function_name = aws_lambda_function.port_terraform_example_dev_env_lambda.function_name
authorization_type = "NONE"
}
resource "aws_lambda_event_source_mapping" "event_source_mapping" {
event_source_arn = aws_sqs_queue.port_terraform_example_dev_env_queue.arn
enabled = true
function_name = aws_lambda_function.port_terraform_example_dev_env_lambda.arn
batch_size = 1
}
resource "port_entity" "dev_env" {
depends_on = [
aws_s3_bucket.port_terraform_example_dev_env_bucket,
aws_lambda_function.port_terraform_example_dev_env_lambda,
aws_sqs_queue.port_terraform_example_dev_env_queue,
aws_lambda_function_url.example_function_url
]
identifier = aws_lambda_function.port_terraform_example_dev_env_lambda.function_name
title = aws_lambda_function.port_terraform_example_dev_env_lambda.function_name
blueprint = "developerEnvironment"
properties = {
string_props = {
"bucketUrl" = "https://${aws_s3_bucket.port_terraform_example_dev_env_bucket.bucket_domain_name}"
"queueUrl" = aws_sqs_queue.port_terraform_example_dev_env_queue.id
"lambdaUrl" = aws_lambda_function_url.example_function_url.function_url
}
number_props = {
"memorySize" = aws_lambda_function.port_terraform_example_dev_env_lambda.memory_size
}
}
}
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
.
To use this example yourself, simply replace the placeholders for access_key
, secret_key
, client_id
and secret
and then run the following commands to setup your new backend, create the new infrastructure and update the software catalog:
# install modules and create an initial state
terraform init
# To view Terraform's planned changes based on your .tf definition file:
terraform plan
# To apply the changes and update the catalog
terraform apply
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 "aws" {
access_key = "YOUR_ACCESS_KEY_ID"
secret_key = "YOUR_SECRET_ACCESS_KEY"
region = "eu-west-1"
}
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
.
Defining the AWS resourcesโ
This part includes defining the following AWS resources:
- An S3 bucket;
- A SQS FIFO queue;
- An IAM policy for the Lambda function, with access to the new SQS queue;
- A Lambda function with a function URL and event source mapping to the SQS queue.
resource "aws_s3_bucket" "port_terraform_example_dev_env_bucket" {
bucket = "my-port-terraform-example-dev-env-bucket"
}
resource "aws_s3_bucket_acl" "port_terraform_example_dev_env_bucket-acl" {
bucket = aws_s3_bucket.port_terraform_example_dev_env_bucket.id
acl = "private"
}
resource "aws_sqs_queue" "port_terraform_example_dev_env_queue" {
name = "terraform-example-queue.fifo"
fifo_queue = true
content_based_deduplication = true
}
resource "aws_iam_role" "iam_for_lambda" {
name = "iam_for_lambda"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "attach_sqs_policy_for_lambda_role" {
role = aws_iam_role.iam_for_lambda.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole"
}
resource "aws_lambda_function" "port_terraform_example_dev_env_lambda" {
depends_on = [
aws_sqs_queue.port_terraform_example_dev_env_queue
]
filename = "lambda_function_payload.zip"
function_name = "dev-env-function"
role = aws_iam_role.iam_for_lambda.arn
handler = "index.test"
runtime = "nodejs16.x"
}
resource "aws_lambda_function_url" "example_function_url" {
function_name = aws_lambda_function.port_terraform_example_dev_env_lambda.function_name
authorization_type = "NONE"
}
resource "aws_lambda_event_source_mapping" "event_source_mapping" {
event_source_arn = aws_sqs_queue.port_terraform_example_dev_env_queue.arn
enabled = true
function_name = aws_lambda_function.port_terraform_example_dev_env_lambda.arn
batch_size = 1
}
Creating the developer env entity matching the new environmentโ
This part includes configuring the developerEnvironment
blueprint and creating an entity for our new bucket:
resource "port_entity" "dev_env" {
depends_on = [
aws_s3_bucket.port_terraform_example_dev_env_bucket,
aws_lambda_function.port_terraform_example_dev_env_lambda,
aws_sqs_queue.port_terraform_example_dev_env_queue,
aws_lambda_function_url.example_function_url
]
identifier = aws_lambda_function.port_terraform_example_dev_env_lambda.function_name
title = aws_lambda_function.port_terraform_example_dev_env_lambda.function_name
blueprint = "developerEnvironment"
properties = {
string_props = {
"bucketName" = aws_s3_bucket.port_terraform_example_dev_env_bucket.id
"queueUrl" = aws_sqs_queue.port_terraform_example_dev_env_queue.id
"lambdaUrl" = aws_lambda_function_url.example_function_url.function_url
}
}
}
Note how we use a depends_on
block on the new developer environment entity, this is required because the developerEnvironment
blueprint has to exist before the entity can be created. In addition, the entity relies on values that will only be available after the AWS resources are created.
Resultโ
After running terraform apply
you will see the resources in AWS, and the matching developerEnvironment
entity in Port.
Continue reading to learn how to make updates and how to cleanup.
Updating the developer environment and the matching entityโ
Since the new developer environment entity maps dynamic properties directly from the AWS resources defined in Terraform. It will also update when one of the developer environment resources change.
For example, let's increase the memory size of the Lambda function:
resource "aws_lambda_function" "port_terraform_example_dev_env_lambda" {
depends_on = [
aws_sqs_queue.port_terraform_example_dev_env_queue
]
filename = "lambda_function_payload.zip"
function_name = "dev-env-function"
role = aws_iam_role.iam_for_lambda.arn
handler = "index.handler"
runtime = "nodejs16.x"
memory_size = 256 # was 128
}
Now by running terraform apply
, both the Lambda resources will change, as well as the memorySize
property of the matching entity.
Cleanupโ
To cleanup your environment, you can run the command terraform destroy
, which will delete all of the resources you created in this example (including the AWS infrastructure and matching Port entity).