Create An AWS EC2 Instance
Overviewโ
In the following guide, you are going to create a self-service action in Port that executes a GitHub workflow to create an EC2 Instance in AWS using Terraform templates.
Prerequisitesโ
- 
A GitHub repository to contain your action resources i.e. the github workflow file. 
- 
An AWS Account or IAM user with permission to create access keys. Learn more 
- 
An SSH Key Pair to connect with the provisioned instance. Learn more 
- 
Install the Ports GitHub app from here. 
- 
In your GitHub repository, go to Settings > Secrets and add the following secrets: - PORT_CLIENT_ID- Port Client ID learn more
- PORT_CLIENT_SECRET- Port Client Secret learn more
- TF_USER_AWS_KEY- An aws access key with the right iam permission to create an ec2 instance learn more
- TF_USER_AWS_SECRET- An aws access key secret with permission to create an ec2 instance learn more
- TF_USER_AWS_REGION- The aws region where you would like to provision your ec2 instance.
 
- 
Create a blueprint in Port for the EC2 Instance. 
EC2 Instance Blueprint
{
  "identifier": "ec2Instance",
  "description": "This blueprint represents an AWS EC2 instance in our software catalog.",
  "title": "EC2 Instance",
  "icon": "EC2",
  "schema": {
    "properties": {
      "instance_state": {
        "type": "string",
        "title": "Instance State",
        "description": "The state of the EC2 instance (e.g., running, stopped).",
        "enum": ["pending", "running", "shutting-down", "terminated", "stopping", "stopped"],
        "enumColors": {
          "pending": "yellow",
          "running": "green",
          "shutting-down": "pink",
          "stopped": "purple",
          "stopping": "orange",
          "terminated": "red"
        }
      },
      "instance_type": {
        "type": "string",
        "title": "Instance Type",
        "description": "The type of EC2 instance (e.g., t2.micro, m5.large)."
      },
      "availability_zone": {
        "type": "string",
        "title": "Availability Zone",
        "description": "The Availability Zone where the EC2 instance is deployed."
      },
      "public_dns": {
        "type": "string",
        "title": "Public DNS",
        "description": "The public DNS name assigned to the EC2 instance."
      },
      "public_ip": {
        "type": "string",
        "title": "Public IP Address",
        "description": "The public IP address assigned to the EC2 instance."
      },
      "private_dns": {
        "type": "string",
        "title": "Private DNS",
        "description": "The private DNS name assigned to the EC2 instance within its VPC."
      },
      "private_ip": {
        "type": "string",
        "title": "Private IP Address",
        "description": "The private IP address assigned to the EC2 instance within its VPC."
      },
      "monitoring": {
        "type": "boolean",
        "title": "Monitoring",
        "description": "Indicates if detailed monitoring is enabled for the EC2 instance."
      },
      "security_group_ids": {
        "type": "array",
        "title": "Security Group IDs",
        "description": "The list of security group IDs assigned to the EC2 instance."
      },
      "key_name": {
        "type": "string",
        "title": "Key Name",
        "description": "The name of the key pair associated with the EC2 instance."
      }
    },
    "required": []
  },
  "mirrorProperties": {},
  "calculationProperties": {},
  "aggregationProperties": {},
  "relations": {}
}
GitHub Workflowโ
- 
Create a folder in a directory of your choice within your github repository to host the terraform template files. 
- 
Create the following terraform templates ( main.tf,variables.tfandoutputs.tf) within the created folder.
We recommend creating a dedicated repository for the workflows that are used by Port actions.
main.tf
data "aws_ami" "ubuntu" {
    most_recent = true
    filter {
        name   = "name"
        values = ["ubuntu/images/hvm-ssd/*20.04-amd64-server-*"]
    }
    filter {
        name   = "virtualization-type"
        values = ["hvm"]
    }
    owners = ["099720109477"] # Canonical
}
provider "aws" {
  region  = var.aws_region
}
resource "aws_instance" "app_server" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = var.ec2_instance_type
  key_name      = var.pem_key_name
  tags = {
    Name = var.ec2_name
  }
}
variables.tf
variable "ec2_name" {
  type = string
}
variable "pem_key_name" {
  type = string
}
variable "aws_region" {
  type = string
}
variable "ec2_instance_type" {
  type = string
}
outputs.tf
output "instance_id" {
  description = "The unique identifier for the provisioned EC2 instance."
  value       = aws_instance.app_server.id
}
output "instance_state" {
  description = "The state of the EC2 instance (e.g., running, stopped)."
  value       = aws_instance.app_server.instance_state
}
output "instance_type" {
  description = "The type of EC2 instance (e.g., t2.micro, m5.large)."
  value       = aws_instance.app_server.instance_type
}
output "availability_zone" {
  description = "The Availability Zone where the EC2 instance is deployed."
  value       = aws_instance.app_server.availability_zone
}
output "public_dns" {
  description = "The public DNS name assigned to the EC2 instance."
  value       = aws_instance.app_server.public_dns
}
output "public_ip" {
  description = "The public IP address assigned to the EC2 instance."
  value       = aws_instance.app_server.public_ip
}
output "private_dns" {
  description = "The private DNS name assigned to the EC2 instance within its VPC."
  value       = aws_instance.app_server.private_dns
}
output "private_ip" {
  description = "The private IP address assigned to the EC2 instance within its VPC."
  value       = aws_instance.app_server.private_ip
}
output "monitoring" {
  description = "Indicates if detailed monitoring is enabled for the EC2 instance."
  value       = aws_instance.app_server.monitoring
}
output "security_group_ids" {
  description = "The list of security group IDs assigned to the EC2 instance."
  value       = aws_instance.app_server.vpc_security_group_ids
}
output "key_name" {
  description = "The name of the key pair associated with the EC2 instance."
  value       = aws_instance.app_server.key_name
}
output "subnet_id" {
  description = "The ID of the subnet to which the instance is attached."
  value       = aws_instance.app_server.subnet_id
}
output "tags" {
  description = "A map of tags assigned to the resource."
  value       = aws_instance.app_server.tags
}
- Create a Github Workflowfile under.github/workflows/create-an-ec2-instance.yamlwith the following content:
GitHub workflow
Replace <TERRAFORM-TEMPLATE-DIR> with the directory created to host your terraform templates.
name: Provision AN EC2 Instance
on:
  workflow_dispatch:
    inputs:
      ec2_name:
        description: EC2 name
        required: true
        default: 'App Server'
        type: string
      ec2_instance_type:
        description: EC2 instance type
        required: false
        default: "t3.micro"
        type: string
      pem_key_name:
        description: EC2 pem key
        required: true
        type: string
      port_context:
        required: true
        description: includes blueprint, run ID, and entity identifier from Port.
jobs:
  provision-ec2:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v3
        with:
          node-version: '14'
      - name: Log starting of EC2 Instance creation
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          operation: PATCH_RUN
          runId: ${{ fromJson(inputs.port_context).run_id }}
          logMessage: |
              About to create ec2 instance ${{ github.event.inputs.ec2_name }} .. โด๏ธ
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: '${{ secrets.TF_USER_AWS_KEY }}'
          aws-secret-access-key: '${{ secrets.TF_USER_AWS_SECRET }}'
          aws-region: '${{ secrets.TF_USER_AWS_REGION }}'
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_wrapper: false
      - name: Terraform Apply
        id:   apply
        env:
          TF_VAR_ec2_name:  "${{ github.event.inputs.ec2_name }}"
          TF_VAR_pem_key_name: "${{ github.event.inputs.pem_key_name}}"
          TF_VAR_aws_region: "${{ secrets.TF_USER_AWS_REGION }}"
          TF_VAR_ec2_instance_type: "${{ github.event.inputs.ec2_instance_type}}"
        run: |
          cd <TERRAFORM-TEMPLATE-DIR>
          terraform init
          terraform validate
          terraform plan
          terraform apply -auto-approve
      - name: Set Outputs
        id: set_outputs
        run: |
          cd <TERRAFORM-TEMPLATE-DIR>
          echo "instance_id=$(terraform output -raw instance_id)" >> $GITHUB_ENV
          echo "instance_state=$(terraform output -raw instance_state)" >> $GITHUB_ENV
          echo "instance_type=$(terraform output -raw instance_type)" >> $GITHUB_ENV
          echo "availability_zone=$(terraform output -raw availability_zone)" >> $GITHUB_ENV
          echo "public_dns=$(terraform output -raw public_dns)" >> $GITHUB_ENV
          echo "public_ip=$(terraform output -raw public_ip)" >> $GITHUB_ENV
          echo "private_dns=$(terraform output -raw private_dns)" >> $GITHUB_ENV
          echo "private_ip=$(terraform output -raw private_ip)" >> $GITHUB_ENV
          echo "monitoring=$(terraform output -raw monitoring)" >> $GITHUB_ENV
          security_group_ids_json=$(terraform output -json security_group_ids | jq -c .)
          echo "security_group_ids=$security_group_ids_json" >> $GITHUB_ENV
          echo "key_name=$(terraform output -raw key_name)" >> $GITHUB_ENV
          echo "subnet_id=$(terraform output -raw subnet_id)" >> $GITHUB_ENV
          tags=$(terraform output -json tags | jq -c .)
          echo "tags=$tags" >> $GITHUB_ENV
      - name: Create a log message
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          operation: PATCH_RUN
          runId: ${{ fromJson(inputs.port_context).run_id }}
          logMessage: |
              EC2 Instance created successfully โ
     - name: Report Created Instance to Port
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          baseUrl: https://api.getport.io
          operation: PATCH_RUN
          runId: ${{ fromJson(inputs.port_context).run_id }}
          logMessage: "Upserting created EC2 Instance to Port ... "
      - name: UPSERT EC2 Instance Entity
        uses: port-labs/port-github-action@v1
        with:
          identifier: "${{ steps.display_outputs.outputs.instance_id }}"
          title: "${{ inputs.ec2_name }}"
          blueprint: ${{ fromJson(inputs.port_context).blueprint }}
          properties: |-
            {
              "instance_state": "${{ env.instance_state }}",
              "instance_type": "${{ env.instance_type }}",
              "availability_zone": "${{ env.availability_zone }}",
              "public_dns": "${{ env.public_dns }}",
              "public_ip": "${{ env.public_ip }}",
              "private_dns": "${{ env.private_dns }}",
              "private_ip": "${{ env.private_ip }}",
              "monitoring": ${{ env.monitoring }},
              "security_group_ids": ${{ env.security_group_ids }},
              "key_name": "${{ env.key_name }}",
              "subnet_id": "${{ env.subnet_id }}",
              "tags": ${{ env.tags }}
            }
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          baseUrl: https://api.getport.io
          operation: UPSERT
          runId: ${{ fromJson(inputs.port_context).run_id }}
      - name: Log After Upserting Entity
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          baseUrl: https://api.getport.io
          operation: PATCH_RUN
          runId: ${{ fromJson(inputs.port_context).run_id }}
          logMessage: "Entity upserting was successful โ
"
Port Configurationโ
- Create the Port action on the ec2Instanceblueprint:- Head to the self-service page.
- Click on the + New Actionbutton.
- Click on the {...} Edit JSONbutton.
- Copy and paste the following JSON configuration into the editor:
 
Port Action: Create An EC2 Instance
Make sure to replace <GITHUB_ORG> and <GITHUB_REPO> with your GitHub organization and repository names respectively.
{
  "identifier": "create_ec2_instance",
  "title": "Create Instance",
  "icon": "EC2",
  "description": "Create An EC2 Instance from Port",
  "trigger": {
    "type": "self-service",
    "operation": "CREATE",
    "userInputs": {
      "properties": {
        "pem_key_name": {
          "title": "Pem Key Name",
          "description": "EC2 .pem key pair name",
          "icon": "EC2",
          "type": "string"
        },
        "ec2_name": {
          "icon": "EC2",
          "title": "EC2_Name",
          "description": "Name of the instance",
          "type": "string"
        },
        "ec2_instance_type": {
          "title": "EC2 Instance Type",
          "description": "EC2 instance type",
          "icon": "EC2",
          "type": "string",
          "default": "t2.micro",
          "enum": ["t2.micro", "t2.medium", "t2.large", "t2.xlarge", "t2.2xlarge"]
        }
      },
      "required": ["ec2_name", "pem_key_name"],
      "order": ["ec2_name", "ec2_instance_type", "pem_key_name"]
    },
    "blueprintIdentifier": "ec2Instance"
  },
  "invocationMethod": {
    "type": "GITHUB",
    "org": "<GITHUB_ORG>",
    "repo": "<GITHUB_REPO>",
    "workflow": "create-ec2-instance.yaml",
    "workflowInputs": {
      "pem_key_name": "{{.inputs.\"pem_key_name\"}}",
      "ec2_name": "{{.inputs.\"ec2_name\"}}",
      "ec2_instance_type": "{{.inputs.\"ec2_instance_type\"}}",
      "port_context": {
        "blueprint": "{{.action.blueprint}}",
        "entity": "{{.entity.identifier}}",
        "run_id": "{{.run.id}}",
        "relations": "{{.entity.relations}}"
      }
    },
    "reportWorkflowStatus": true
  },
  "requiredApproval": false
}
Let's test it!โ
- Head to the Self Service hub
- Click on the Create An EC2 Instanceaction
- Fill the pop-up form with details of the EC2 Instance you wish to create
- Click on Execute
- Wait for the EC2 Instance to be created in AWS