Deploy S3 Bucket using Crossplane
This guide walks you through setting up a workflow that deploys Crossplane resources in your Kubernetes cluster.
The workflow we will create opens a pull request (GitHub) or merge request (GitLab) in your Git project, committing a Crossplane manifest that describes an S3 bucket in AWS. It also registers the bucket in your Port catalog so you can track it from the moment it is requested.
Common use cases
- Let developers provision cloud resources without writing Crossplane manifests by hand.
- Keep every provisioned resource behind code review, since the workflow proposes a change rather than applying one.
- Track requested buckets, their region, and their manifest pull request from a single catalog page.
Prerequisites
- A Port account with permissions to build workflows.
- A Kubernetes cluster.
- Crossplane installed in your cluster:
- Crossplane installation guide.
- Crossplane AWS quickstart guide, which covers deploying a Crossplane Provider and a ProviderConfig.
- A GitOps tool that syncs manifests from your Git project to your cluster. If you do not have one yet, follow our end to end guide for creating Kubernetes resources with Crossplane and ArgoCD.
- The Git integration relevant for you, with actions processing enabled:
How it works
The setup has two parts:
- The workflow - a self-service trigger node collects the bucket name and region, an upsert entity node registers the bucket in the catalog, and an integration action node triggers your Git provider's pipeline.
- The backend - a CI pipeline that renders the Crossplane manifest from a template, opens a pull/merge request with it, and writes the request URL back to the bucket entity.
The manifest reaches your cluster once the request is merged and your GitOps tool syncs it.
This workflow creates a pull/merge request. Without a GitOps tool watching the repository, the manifest is never applied to your cluster.
Set up the data model
Head over to the Data model page to create the S3 bucket blueprint:
- Click on the
+ Blueprintbutton. - Click on the
{...} Edit JSONbutton. - Copy and paste the following JSON configuration into the editor.
- Click
Save.
S3 bucket blueprint (click to expand)
{
"identifier": "s3bucket",
"title": "S3 Bucket",
"icon": "Crossplane",
"schema": {
"properties": {
"aws_region": {
"title": "AWS region",
"icon": "AWS",
"type": "string"
},
"status": {
"title": "Status",
"type": "string",
"enum": ["Manifest requested", "Awaiting merge", "Deployed"],
"enumColors": {
"Manifest requested": "lightGray",
"Awaiting merge": "yellow",
"Deployed": "green"
}
},
"manifest_pr": {
"title": "Manifest pull request",
"type": "string",
"format": "url"
}
},
"required": ["aws_region"]
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}
The status property moves from Manifest requested to Awaiting merge when the pipeline opens the request. Set it to Deployed from your GitOps tool or a follow-up workflow once the manifest is synced to the cluster.
Set up the backend
Now we will write the logic that the workflow triggers.
-
Create a GitHub repository or GitLab project called
crossplane_deployer.Dedicated pipelines repositoryWe recommend keeping the pipelines your workflows trigger in a dedicated repository, separate from your application code.
-
Inside
crossplane_deployer, create a folder namedcrossplane-templateson themainbranch. -
Create a template file named
s3bucket-crossplane.yamlin that folder:crossplane-templates/s3bucket-crossplane.yaml (click to expand)
apiVersion: s3.aws.upbound.io/v1beta1kind: Bucketmetadata:name: BUCKET_NAMEspec:forProvider:region: AWS_REGIONproviderConfigRef:name: defaultThe
BUCKET_NAMEandAWS_REGIONplaceholders are replaced by the pipeline at run time. -
Add the pipeline and its credentials:
- GitHub
- GitLab
Create the following GitHub Actions secrets in the crossplane_deployer repository:
PORT_CLIENT_ID- your Port client ID, learn more.PORT_CLIENT_SECRET- your Port client secret, learn more.CREATOR_TOKEN- a classic personal access token with thereposcope, used to open the pull request.
Then create a workflow file under .github/workflows/create-s3-manifest.yml:GitHub Actions pipeline (click to expand)
Create the following GitLab CI/CD variables in the crossplane_deployer project:
ACCESS_TOKEN- a personal access token with theapiandwrite_repositoryscopes, used to push the branch and open the merge request.PORT_CLIENT_ID- your Port client ID, learn more.PORT_CLIENT_SECRET- your Port client secret, learn more.
Then create a .gitlab-ci.yml file in the main branch:GitLab CI pipeline (click to expand)
Build the workflow
Now we will create the workflow that registers the bucket and triggers the pipeline.
-
Go to the workflows page in Port.
-
Click + Workflow.
-
Fill out the Create new workflow form, then click Confirm.
-
Click the
{...}button to open the JSON editor and paste the workflow JSON below:- GitHub
- GitLab
Modification requiredReplace
<GITHUB_ORG>with your GitHub organization, and<YOUR_GITHUB_INTEGRATION_ID>with the installation ID of your GitHub integration from the Data sources page.Deploy S3 bucket workflow JSON (click to expand)
{"identifier": "deploy_s3_bucket_crossplane","title": "Deploy S3 bucket using Crossplane","icon": "Crossplane","description": "Commit a Crossplane manifest for a new S3 bucket and open a pull request","nodes": [{"identifier": "trigger","title": "Request an S3 bucket","config": {"type": "SELF_SERVE_TRIGGER","userInputs": {"properties": {"bucket_name": {"type": "string","title": "Bucket name","description": "Has to be globally unique as per AWS limitations"},"aws_region": {"type": "string","title": "AWS region","icon": "AWS","default": "us-east-1","enum": ["us-east-1", "eu-west-1"],"enumColors": {"us-east-1": "lightGray","eu-west-1": "lightGray"}}},"required": ["bucket_name", "aws_region"],"order": ["bucket_name", "aws_region"]}}},{"identifier": "register_bucket","title": "Register bucket in Port","config": {"type": "UPSERT_ENTITY","blueprintIdentifier": "s3bucket","mapping": {"identifier": "{{ .outputs.trigger.bucket_name }}","title": "{{ .outputs.trigger.bucket_name }}","properties": {"aws_region": "{{ .outputs.trigger.aws_region }}","status": "Manifest requested"}}}},{"identifier": "create_manifest_pr","title": "Create manifest pull request","config": {"type": "INTEGRATION_ACTION","installationId": "<YOUR_GITHUB_INTEGRATION_ID>","integrationProvider": "github-ocean","integrationInvocationType": "dispatch_workflow","integrationActionExecutionProperties": {"org": "<GITHUB_ORG>","repo": "crossplane_deployer","workflow": "create-s3-manifest.yml","workflowInputs": {"bucket_name": "{{ .outputs.trigger.bucket_name }}","aws_region": "{{ .outputs.trigger.aws_region }}","port_run_id": "{{ .workflowRun.identifier }}","triggered_by": "{{ .workflowRun.trigger.by.email }}"},"reportWorkflowStatus": true}}}],"connections": [{"sourceIdentifier": "trigger","targetIdentifier": "register_bucket"},{"sourceIdentifier": "register_bucket","targetIdentifier": "create_manifest_pr"}]}Modification requiredReplace
<GROUP_NAME>with the GitLab group that holds yourcrossplane_deployerproject, and<YOUR_GITLAB_INTEGRATION_ID>with the installation ID of your GitLab integration from the Data sources page.Deploy S3 bucket workflow JSON (click to expand)
{"identifier": "deploy_s3_bucket_crossplane","title": "Deploy S3 bucket using Crossplane","icon": "Crossplane","description": "Commit a Crossplane manifest for a new S3 bucket and open a merge request","nodes": [{"identifier": "trigger","title": "Request an S3 bucket","config": {"type": "SELF_SERVE_TRIGGER","userInputs": {"properties": {"bucket_name": {"type": "string","title": "Bucket name","description": "Has to be globally unique as per AWS limitations"},"aws_region": {"type": "string","title": "AWS region","icon": "AWS","default": "us-east-1","enum": ["us-east-1", "eu-west-1"],"enumColors": {"us-east-1": "lightGray","eu-west-1": "lightGray"}}},"required": ["bucket_name", "aws_region"],"order": ["bucket_name", "aws_region"]}}},{"identifier": "register_bucket","title": "Register bucket in Port","config": {"type": "UPSERT_ENTITY","blueprintIdentifier": "s3bucket","mapping": {"identifier": "{{ .outputs.trigger.bucket_name }}","title": "{{ .outputs.trigger.bucket_name }}","properties": {"aws_region": "{{ .outputs.trigger.aws_region }}","status": "Manifest requested"}}}},{"identifier": "create_manifest_mr","title": "Create manifest merge request","config": {"type": "INTEGRATION_ACTION","installationId": "<YOUR_GITLAB_INTEGRATION_ID>","integrationProvider": "gitlab-v2","integrationInvocationType": "trigger_pipeline","integrationActionExecutionProperties": {"project": "<GROUP_NAME>/crossplane_deployer","ref": "main","pipelineVariables": {"BUCKET_NAME": "{{ .outputs.trigger.bucket_name }}","AWS_REGION": "{{ .outputs.trigger.aws_region }}","PORT_RUN_ID": "{{ .workflowRun.identifier }}","TRIGGERED_BY": "{{ .workflowRun.trigger.by.email }}"},"reportPipelineStatus": true}}}],"connections": [{"sourceIdentifier": "trigger","targetIdentifier": "register_bucket"},{"sourceIdentifier": "register_bucket","targetIdentifier": "create_manifest_mr"}]} -
Click Apply changes.
Secrets are not supported with integration actions. Both nodes above authenticate through your installed Git integration, so the pipeline credentials stay in your CI provider rather than in Port.
Execute the workflow
-
Head to the Self-service page of your Port application.
-
Find Deploy S3 bucket using Crossplane and click on it.
-
Enter a globally unique bucket name, choose an AWS region, and click
Execute. -
Follow the workflow's progress in the Workflow runs tab. Each node shows its status, and you can expand a node to inspect its output and logs.
-
Once the run completes, a new pull/merge request is waiting in
crossplane_deployerwith the S3 bucket manifest, and the bucket appears in your catalog with its status set toAwaiting mergeand a link to the request.
Merge the request, and your GitOps tool applies the manifest to your cluster 🚀
Next steps
- Connect Port's AWS exporter to enrich the bucket entity with properties ingested directly from AWS. See the setup instructions and example configurations.
- Add a webhook node to notify a Slack channel when a manifest request is opened.
- Add an event-triggered workflow that flips the bucket status to
Deployedwhen your GitOps tool reports a successful sync.