Skip to main content

Deploy S3 Bucket using Crossplane

This example demonstrates how to deploy Crossplane resources in your Kubernetes Cluster via Port Actions.

We will show you how to utilize Port Actions to open a Merge Request in your Gitlab/GitHub Project, which commits a Crossplane manifest that describes an S3 Bucket in AWS.

We will cover using both GitLab and GitHub Pipelines to create Merge/Pull Requests that commit a file to the project.

GitOps Setup:

To streamline deployments with this example, ensure you have a GitOps tool configured. Otherwise, refer to this guide for an end to end tutorial of creating resources in Kubernetes with Crossplane and ArgoCD.

Prerequisites​

  • Kubernetes cluster.
  • Crossplane installed in your cluster:
  • GitOps mechanism of your choosing which synchronizes files from your Gitlab Project to your Kubernetes cluster.

Port Configuration​

  1. Head over to the Builder page to create the following blueprints:
  • Click on the + Blueprint button.
  • Click on the {...} Edit JSON button.
  • Copy and paste the following JSON configuration into the editor.
  • Click Save.
Port S3Bucket Blueprint
{
"identifier": "s3bucket",
"title": "S3Bucket",
"icon": "Crossplane",
"schema": {
"properties": {
"aws_region": {
"title": "AWS Region",
"icon": "AWS",
"type": "string"
}
},
"required": ["aws_region"]
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}

  1. To create the Port action, go to the self-service page:
    • Click on the + New Action button.
    • Choose the s3bucket blueprint and click Next.
    • Click on the {...} Edit JSON button.
    • Copy and paste the following JSON configuration into the editor.
    • Click Save
Port Action
MODIFICATION REQUIRED

Make sure to replace <GITHUB_ORG> and <GITHUB_REPO> with your GitHub organization and repository names respectively.

{
"identifier": "crossplane_s3_bucket",
"title": "Crossplane S3 Bucket",
"icon": "Crossplane",
"userInputs": {
"properties": {
"aws_region": {
"icon": "AWS",
"title": "AWS Region",
"type": "string",
"default": "us-east-1",
"enum": ["us-east-1", "eu-west-1"],
"enumColors": {
"us-east-1": "lightGray",
"eu-west-1": "lightGray"
}
},
"bucket_name": {
"title": "Bucket Name",
"type": "string",
"description": "Has to be globally unique as per AWS limitations"
}
},
"required": ["aws_region", "bucket_name"],
"order": ["bucket_name", "aws_region"]
},
"invocationMethod": {
"type": "GITHUB",
"org": "<GITHUB_ORG>",
"repo": "<GITHUB_REPO>",
"workflow": "create-and-push-image.yml",
"omitUserInputs": false,
"omitPayload": false,
"reportWorkflowStatus": true
},
"trigger": "CREATE",
"description": "Creates a crossplane file for a new S3 Bucket",
"requiredApproval": false
}

Git Workflows​

  1. Create a GitHub repository / Gitlab project called crossplane_deployer. If using GitLab configure a Pipeline Trigger Token.
tip

We recommend creating a dedicated repository for your Port action workflows.

  1. Inside your crossplane_deployer repository, create a folder named crossplane-templates on the main branch.

  2. Create a template file named s3bucket-crossplane.yaml in the crossplane-templates directory.

s3bucket-crossplane.yaml
# s3bucket-crossplane.yaml

apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
name: {{ bucket_name }}
spec:
forProvider:
region: {{ aws_region }}
providerConfigRef:
name: default
  1. Create the following GitHub Action secrets:
    • PORT_CLIENT_ID - Port Client ID learn more.
    • PORT_CLIENT_SECRET - Port Client Secret learn more.
    • MY_GITHUB_TOKEN - a Classic Personal Access Token with the repo scope and the following permissions: pull_requests:write (to create PR) and contents:write (to merge PR)

  1. Install Port's GitHub app by clicking here.

  1. Create a workflow file under .github/workflows/create-s3-manifest.yml with the following content:
GitHub workflow
name: Create New S3 Bucket Crossplane Manifest

on:
workflow_dispatch:
inputs:
bucket_name:
description: "Name of the s3 bucket"
required: true
aws_region:
description: "AWS Region for the cluster"
required: true
port_payload:
required: true
description: >-
Port's payload, including details for who triggered the action and
general context (blueprint, run id, etc...)

jobs:
create-manifest:
runs-on: ubuntu-latest
steps:
- name: Inform execution of request to create a new manifest
id: promote
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_payload).context.runId }}
logMessage: "About to create a crossplane manifest for a new s3 bucket..."

- name: Checkout code
uses: actions/checkout@v4

- name: Create crossplane manifest for s3 bucket
id: create-manifest
env:
BUCKET_FILE_PATH: "manifests/s3bucket"
CROSSPLANE_TEMPLATE_PATH: "crossplane-templates/s3bucket-crossplane.yaml"
run: |
mkdir -p $BUCKET_FILE_PATH
BUCKET_FILE_NAME="${BUCKET_FILE_PATH}/s3bucket-${{ inputs.bucket_name }}.yaml"

cp $CROSSPLANE_TEMPLATE_PATH $BUCKET_FILE_NAME

sed -i "s/{{ bucket_name }}/${{ inputs.bucket_name }}/g" $BUCKET_FILE_NAME
sed -i "s/{{ aws_region }}/${{ inputs.aws_region }}/g" $BUCKET_FILE_NAME

git add $BUCKET_FILE_NAME

- name: Create Pull Request
id: create-pr
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.CREATOR_TOKEN }}
commit-message: Added ${{ inputs.bucket_name }} s3 bucket crossplane manifest
committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
author: ${{ github.actor }} <${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com>
signoff: false
branch: deployment/${{ fromJson(inputs.port_payload).context.runId }}
title: "[Deployment] Add ${{ inputs.bucket_name }} s3 bucket crossplane manifest"
body: |
This PR is automatically generated by Port.
It contains the crossplane manifest for the s3 bucket ${{ inputs.bucket_name }}.

The manifest is generated based on the blueprint: **${{ fromJson(inputs.port_payload).context.blueprint }}**.

**Run ID**: ${{ fromJson(inputs.port_payload).context.runId }}.
**Triggered by**: ${{ fromJson(inputs.port_payload).trigger.by.user.email }}.
**Triggered at**: ${{ fromJson(inputs.port_payload).trigger.at }}.
**Triggered from**: ${{ fromJson(inputs.port_payload).trigger.origin }}.


- Auto-generated by [port-actions][1]

[1]: https://app.getport.io/organization/run?runId=${{ fromJson(inputs.port_payload).context.runId }}
labels: |
deployment
automated pr
assignees: ${{ fromJson(inputs.port_payload).trigger.by.user.email }}

- name: Inform Port about pull request creation status - Success
if: steps.create-pr.outputs.pull-request-url != ''
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_payload).context.runId }}
logMessage: |
Pull request created successfully. URL: ${{ steps.create-pr.outputs.pull-request-url }}.


- name: Inform Port about pull request creation status - Failure
if: steps.create-pr.outputs.pull-request-url == ''
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_payload).context.runId }}
logMessage: |
Failed to create pull request. Please check the logs for more details.

Disclaimer

This example focuses on creating either Merge Requests in GitLab or Pull Requests in GitHub. To automatically deploy the manifest to your Kubernetes cluster, you will also need to set up a GitOps tool.

  1. Trigger the action from the Self-service tab of your Port application.
    You will notice a new Pull Request / Merge Request was created, commiting the S3 Bucket manifest.

Next steps​

In this example we did not create the Port entity for the S3 bucket.

  • You can Connect Port's AWS exporter to make sure all of the properties and entities are automatically ingested from AWS.
    • You can learn how to setup Port's AWS exporter here.
    • You can see example configurations and use cases here.