Skip to main content

Check out Port for yourselfย 

Create cloud resources using IaC

This guide shows how to open a pull request in your Git repositoryโ€”directly from Portโ€”to create a new cloud resource using GitOps.

Prerequisitesโ€‹

  • A Port account with permissions to create self-service actions.
  • The Git Integration that is relevant for you needs to be installed.
  • A repository in your Git provider in which you can create a workflow/pipeline. We recommend naming it Port-actions.

Common use casesโ€‹

  • Platform engineers can define the logic of creating a cloud resource, including standard enforcement, security and permission boundaries.
  • Developers can easily create and track cloud resources directly from Port.

Set up data modelโ€‹

To enable tracking of cloud resource definitions in Port, we'll add a new URL property to the existing Repository blueprint. This property will point to the location of IaC templates in your Git repository, letting us seamlessly link infrastructure code to each repository.

Add a resource definitions propertyโ€‹

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

  1. Go to your Data model page.

  2. Expand your Repository 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 repositories, 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 page of your portal.

  2. Click on the + Action button in the top-right corner :

  3. Fill the basic form with the following:

    • Title: Enter Create s3 bucket

    • Identifier Toggle the switch icon off and type a create_s3_bucket

    • Description: Enter the description (e.g., Create an s3 bucket)

    • Icon: Type s3 and choose the Icon (optional)

    • Operation: Choose Day-2 from the dropdown

    • Blueprint: Choose Repository from the dropdown


  4. Click on Next, and add the following inputs: Name and Visibility.

    To create the Name input field:

    • Click on + Input.

    • Enter the Title Name.

    • Select the Type Text.

    • Add the Description (optional).

    • Set Required to True.

    • Click on the Create button.



    To create the Visibility input field:

    • Click on + Input.

    • Enter the Title Visibility.

    • Choose the Type Select.

    • Set Required to True.

    • Configure Additional Information:

      • Item Type: String.
      • Limit: 1 option.
      • Add Options:
        • Option 1: public (choose a color).
        • Option 2: private (choose a color).
    • Click on the Create button.



  5. Click on Next to configure the Backend.

Now we'll define the backend of the action. Port supports multiple invocation types, depending on the Git provider you are using.

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.

  2. 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.



  3. 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.
  4. 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: githubRepository
    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.


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 Execute. On the top right corner of the page, click on the action button to view the action progress:



  2. 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: