Skip to main content

Create cloud resources using IaC

This guide takes 8 minutes to complete, and aims to demonstrate:

  • A complete flow to create a resource using IaC.
  • The simplicity of communicating with Port from a self-service action backend.
Prerequisites
  • This guide assumes you have a Port account and that you have finished the onboarding process. We will use the Service blueprint that was created during the onboarding process.
  • You will need a Git repository (Github, GitLab, or Bitbucket) in which you can place a workflow/pipeline that we will use in this guide. If you don't have one, we recommend creating a new repository named Port-actions.

The goal of this guide

In this guide we will open a pull-request in our Git repository from within Port to create a new cloud resource using gitops.

After completing it, you will get a sense of how it can benefit different personas in your organization:

  • Platform engineers will be able to define powerful actions that developers can use within controlled permission boundaries.
  • Developers will be able to easily create and track cloud resources from Port.

Add a URL to your new resource's definition

In this guide we will add a new property to our service blueprint, which we can use to access our cloud resource definitions.

  1. Go to your Data model page.

  2. Expand your service blueprint, then click on + New property.

  3. Choose URL as the type, fill it like this and click Save:

This property is empty for now in all services, we will fill it as part of the action we're about to create 😎

Setup the action's frontend

  1. Head to the Self-service tab in your Port application. Since you completed the onboarding process, you should already have an action named Create s3 bucket. Hover over the action, click the ... button in the top right corner, and choose "Edit":

  2. The action's basic details should look like the image below. When ready, click on the User Form tab to proceed.

  3. The action's user form should look like the image below. When ready, click on the Backend tab to proceed.

  4. Now we'll define the backend type of the action. Port supports multiple invocation types, one of them should be selected for you depending on the Git provider you selected in the beginning of the onboarding process.

Fill out the form with your values:

  • Replace the Organization and Repository values with your values (this is where the workflow will reside and run).

  • Name the workflow port-create-bucket.yml.

  • Fill out your workflow details:


  • Scroll down to the Configure the invocation payload section.
    This is where you can define which data will be sent to your backend each time the action is executed.

    For this example, we will send some details that our backend needs to know - the inputs, along with the entity and the id of the action run.
    Copy the following JSON snippet and paste it in the payload code box:

    {
    "port_context": {
    "entity": "{{ .entity.identifier }}",
    "runId": "{{ .run.id }}"
    },
    "name": "{{ .inputs.name }}",
    "visibility": "{{ .inputs.visibility }}"
    }

The last step is customizing the action's permissions. For simplicity's sake, we will use the default settings. For more information, see the permissions page. Click Create.

The action's frontend is now ready 🥳

Setup the action's backend

Now we want to write the logic that our action will trigger.

  1. First, let's create the necessary token and secrets. If you've already completed the scaffold a new service guide, you should already have these configured and you can skip this step.
  • Go to your Github tokens page, create a personal access token with repo and admin:org scope, and copy it (this token is needed to create a pull-request from our workflow).

    • Go to your Port application, click on the ... in the top right corner, then click Credentials. Copy your Client ID and Client secret.
  1. In the repository where your workflow will reside, create 3 new secrets under Settings->Secrets and variables->Actions:
  • ORG_ADMIN_TOKEN - the personal access token you created in the previous step.
  • PORT_CLIENT_ID - the client ID you copied from your Port app.
  • PORT_CLIENT_SECRET - the client secret you copied from your Port app.


  1. Now let's create the workflow file that contains our logic. Our workflow will consist of 3 steps:
  • Creating a copy of the template file in the selected service's repository and replacing its variables with the data from the action's input.
  • Creating a pull request in the selected service's repository to add the new resource.
  • Reporting & logging the action result back to Port, and updating the relevant service's Resource definitions property with the URL of the service's resources directory.

Under .github/workflows/, create a new file named port-create-bucket.yml and use the following snippet as its content:

Github workflow (click to expand)
name: Create cloud resource
on:
workflow_dispatch:
inputs:
port_context:
required: true
description: Includes the entity identifier, and the action's run id
name:
required: true
description: The name of the new resource
type: string
visibility:
required: true
description: The visibility of the new resource
type: string
jobs:
createResource:
runs-on: ubuntu-latest
steps:
# Checkout the workflow's repository
- uses: actions/checkout@v4
# Checkout the service's repository
- uses: actions/checkout@v4
with:
repository: "${{ github.repository_owner }}/${{ fromJson(inputs.port_context).entity }}"
path: ./targetRepo
token: ${{ secrets.ORG_ADMIN_TOKEN }}
- name: Copy template file
run: |
mkdir -p ./targetRepo/resources
cp templates/cloudResource.tf ./targetRepo/resources/${{ inputs.name }}.tf
- name: Update new file data
run: |
sed -i 's/{{ bucket_name }}/${{ inputs.name }}/' ./targetRepo/resources/${{ inputs.name }}.tf
sed -i 's/{{ bucket_acl }}/${{ inputs.visibility }}/' ./targetRepo/resources/${{ inputs.name }}.tf
- name: Open a pull request
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.ORG_ADMIN_TOKEN }}
path: ./targetRepo
commit-message: Create new resource - ${{ inputs.name }}
committer: GitHub <noreply@github.com>
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
signoff: false
branch: new-resource-${{ inputs.name }}
delete-branch: true
title: Create new resource - ${{ inputs.name }}
body: |
Create new ${{ inputs.visibility }} resource - ${{ inputs.name }}
draft: false
create-entity-in-port-and-update-run:
runs-on: ubuntu-latest
needs: createResource
steps:
- name: UPSERT Entity
uses: port-labs/port-github-action@v1
with:
identifier: ${{ fromJson(inputs.port_context).entity }}
blueprint: service
properties: |-
{
"resource_definitions": "${{ github.server_url }}/${{ github.repository_owner }}/${{ fromJson(inputs.port_context).entity }}/blob/main/resources/"
}
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
baseUrl: https://api.getport.io
operation: UPSERT
runId: ${{ fromJson(inputs.port_context).runId }}
- name: Create a log message
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).runId }}
logMessage: Pull request created successfully for "${{ inputs.name }}" 🚀
Selecting a Port API URL by account region

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.

  1. We will now create a simple .tf file that will serve as a template for our new resource:
  • In your source repository (port-actions for example), create a file named cloudResource.tf under /templates/ (it's path should be /templates/cloudResource.tf).
  • Copy the following snippet and paste it in the file's contents:
cloudResource.tf (click to expand)
cloudResource.tf
resource "aws_s3_bucket" "example" {
provider = aws.bucket_region
name = "{{ bucket_name }}"
acl = "{{ bucket_acl }}"
}

All done! The action is ready to be executed 🚀


Execute the action

After creating an action, it will appear under the Self-service tab of your Port application:

  1. Click on Execute.

  2. Enter a name for your s3 bucket and choose a visibility, select any service from the list and click Execute. A small popup will appear, click on View details:

  3. This page provides details about the action run. We can see that the backend returned Success and the pull-request was created successfully:

Access the bucket's definition from Port

You may have noticed that even though we updated the service's Resource definitions URL, it still leads to a non-existent page. This is because we do not have any resources in the repository yet, let's take care of that:

  1. Merge the pull-request.

  2. Go to the entity page of the service that you executed the action for:

  3. Click on the Resource definitions link to access the service's resources.

All done! You can now create resources for your services directly from Port 💪🏽

Possible daily routine integrations

  • Send a slack message to relevant people in the organization, notifying about the new resource.
  • Send a weekly/monthly report for managers/devops showing the new resources created in this timeframe and their owners.

Conclusion

Developer portals need to support and integrate with git-ops practices seamlessly. Developers should be able to perform routine tasks independently, without having to create bottlenecks within the organization.
With Port, platform engineers can design precise and flexible self-service actions for their developers, while integrating with many different backends to suit your specific needs.

More relevant guides and examples: