Skip to main content

EKS as a service action (EKSaaS)

This is a guide to integrate Port and Upbound, using their combination as a software catalog and self-service hub for developers, and a platform management tool for the platform team.

At the end of this guide, you should be able to interact with Upbound using Port, provide EKSaaS using Upbound's capabilities and control planes as a backend, while reporting data regarding deployed clusters back to Port.

Clean slate start

The demo starts off on a completely clean slate - an empty Upbound organization, an empty Git repository, and an empty Port environment.

Prerequisites

Upbound

Before following the guide, you will need to set up an Upbound organization, initialize it and keep track of some information:

  • Save the Organization ID for later;
  • Set up an EKSaaS configuration in the Upbound organization;
  • Deploy a control plane (or many) and save their identifiers for later;
  • Create an API token and save it for later:
    • Click on the dropdown over your name at the top right corner;
    • Click on "My Account";
    • On the left side menu, select "API Tokens";
    • Click on "Create New Token";
    • Enter a name for your token and click "Create Token";
    • Save the access ID and token for later.

Port

Git repository

The actions backend, and the state of the different control planes will be handled in a GitHub repository. For Port to interact with the new GitHub repo, you will need Port's GitHub app to be installed (install Port's GitHub app).

Create a new git repository, and make sure that Port's GitHub app is installed on it either by:

  • Installing Port's GitHub app on all the repositories in the used GitHub organization;
  • Or by adding the new repository to the allowed repository list of Port's GitHub app in the organization.

Also make sure that GitHub actions allow creating and approving pull requests in your repository.

Note: Save the name of your new repository for later.

After completing the prerequisites step, you can start following the guide.

Setting up the git repository

Create all the necessary files and directory structure

  • Create a GitHub repository
  • Create a folder at .github/workflows/
  • Create .yaml files using the collapsed file contents below:
Apply Clusters (click to expand)
apply-clusters.yaml
name: Apply Cluster changes

on:
workflow_dispatch:
inputs:
port_context:
type: string

jobs:
apply-clusters:
runs-on: ubuntu-latest
env:
UPBOUND_ORG_ID: <ENTER_UPBOUND_ORG_ID> # `Organization ID` we set aside earlier
steps:
- uses: actions/checkout@v4
with:
persist-credentials: true
ref: main
- name: Install Kubectl
uses: azure/setup-kubectl@v3
id: install-kubectl
- name: Install Upbound CLI
run: |
curl -sL "https://cli.upbound.io" | sh
sudo mv up /usr/local/bin/
- name: Connect to Upbound using CLI and apply manifests to all of the control planes
run: |
up login -t ${{ secrets.UPBOUND_TOKEN }}
cd .up/clusters
for CONTROL_PLANE in */ ; do
# Remove trailing slash to get the clean cluster name
CONTROL_PLANE=${CONTROL_PLANE%/}

# Get the kube config for the specific control plane
echo "Fetching kubeconfig for ${CONTROL_PLANE}"
up ctp kubeconfig get -a ${{ env.UPBOUND_ORG_ID }} ${CONTROL_PLANE} -f kubeconfig.yaml --token ${{ secrets.UPBOUND_TOKEN }}
echo "Applying manifests"
if find "$CONTROL_PLANE" -maxdepth 1 -type f -name "*.yaml" | read -r; then
kubectl --kubeconfig kubeconfig.yaml apply -f ./${CONTROL_PLANE}/ --recursive
else
echo "Control plane directory is empty"
# exit 1
fi
done
Approve cluster request (click to expand)
approve-cluster-request.yaml
name: Approve new cluster PR

on:
workflow_dispatch:
inputs:
port_context:
required: true
description: "Port's context"
type: string

jobs:
approve-cluster-request-call:
runs-on: ubuntu-latest

steps:
- name: Inform starting of approving EKS cluster request
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).runId }}
logMessage: "Approving EKS cluster request: ${{ fromJson(inputs.port_context).entity.properties.title }}"

- uses: actions/checkout@v4
with:
persist-credentials: true

- name: Merge Pull Request
uses: juliangruber/merge-pull-request-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
number: ${{ fromJson(inputs.port_context).entity.properties.request_pr_number }}
method: squash

- name: "Report new EKS cluster to Port"
if: ${{ always() }}
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
operation: UPSERT
identifier: ${{ fromJson(inputs.port_context).entity.properties.title }}
runId: ${{ fromJson(inputs.port_context).runId }}
title: ${{ fromJson(inputs.port_context).entity.properties.title }}
blueprint: eks_cluster
properties: |
{
"node_size": "${{ fromJson(inputs.port_context).entity.properties.node_size }}",
"node_count": "${{ fromJson(inputs.port_context).entity.properties.node_count }}"
}
relations: |
{
"upbound_control_plane": "${{ fromJson(inputs.port_context).entity.relations.control_plane }}"
}

- name: Inform that EKS cluster request has been approved
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).runId }}
icon: GitHubActions
logMessage: "Approved EKS cluster request, and created new Port entity for the EKS cluster🚀 Applying Clusters to Upbound control plane..."

approve-cluster-request-port:
runs-on: ubuntu-latest
steps:
- 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).context.runId }}
icon: GitHubActions
logMessage: "Approving EKS cluster request: ${{ fromJson(inputs.port_context).entity.identifier }}"
- uses: actions/checkout@v4
with:
persist-credentials: true

- name: Merge Pull Request
uses: juliangruber/merge-pull-request-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
number: ${{ fromJson(inputs.port_context).entity.properties.request_pr_number }}
method: squash

- name: "Report new EKS cluster to Port"
if: ${{ always() }}
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
identifier: ${{ fromJson(inputs.port_context).payload.entity.identifier }}
title: ${{ fromJson(inputs.port_context).payload.entity.identifier }}
runId: ${{ fromJson(inputs.port_context).context.runId }}
blueprint: eks_cluster
properties: |
{
"node_size": "${{ fromJson(inputs.port_context).entity.properties.node_size }}",
"node_count": ${{ fromJson(inputs.port_context).entity.properties.node_count }}
}
relations: |
{
"upbound_control_plane": "${{ fromJson(inputs.port_context).entity.relations.upbound_control_plane }}"
}

- name: "Approve EKS cluster request"
if: ${{ always() }}
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
identifier: ${{ fromJson(inputs.port_context).payload.entity.identifier }}
blueprint: eks_cluster_request
runId: ${{ fromJson(inputs.port_context).context.runId }}
properties: |
{
"status": "Approved",
"eks_cluster": "${{ fromJson(inputs.port_context).payload.entity.identifier }}"
}
- 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).context.runId }}
icon: GitHubActions
logMessage: "Approved EKS cluster request, and created new Port entity for the EKS cluster🚀 Applying Clusters to Upbound control plane..."


apply-cluster-changes:
uses: ./.github/workflows/apply-clusters.yaml
if: ${{ always() }}
secrets: inherit
needs:
- approve-cluster-request-call
- approve-cluster-request-port

update-port:
runs-on: ubuntu-latest
needs:
- apply-cluster-changes
if: ${{ always() }}
steps:
- name: Inform cluster applied to Upbound
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).runId }}
logMessage: "Applied cluster to Upbound successfuly✅"
Delete cluster (click to expand)
delete-cluster.yaml
name: Delete Cluster

on:
workflow_dispatch:
inputs:
port_context:
required: true
description: "Port's payload"
type: string

jobs:
delete-cluster:
runs-on: ubuntu-latest
env:
UPBOUND_ORG_ID: <ENTER_UPBOUND_ORG_ID> # `Organization ID` we set aside earlier
steps:
- name: Inform starting of deleting EKS cluster
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).runId }}
icon: GithubActions
logMessage: "Initiating deletion job 🏗️"

- uses: actions/checkout@v4
with:
persist-credentials: true
ref: main

- name: Install Kubectl
uses: azure/setup-kubectl@v3
id: install-kubectl
- name: Install Upbound CLI
run: |
curl -sL "https://cli.upbound.io" | sh
sudo mv up /usr/local/bin/

- 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).runId }}
icon: GithubActions
logMessage: "Connecting to Upbound control plane 🛰️"

- name: Connect to Upbound using CLI + Fetch kubeconfig
run: |
up login -t ${{ secrets.UPBOUND_TOKEN }}
up ctp kubeconfig get -a ${{ env.UPBOUND_ORG_ID }} ${{ fromJson(inputs.port_context).entity.relations.upbound_control_plane }} -f kubeconfig.yaml --token ${{ secrets.UPBOUND_TOKEN }}

- 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).runId }}
icon: GithubActions
logMessage: |
❌ Deleteing CRDs from Upbound + claim files from the repository for:
Control plane: ${{ fromJson(inputs.port_context).entity.relations.upbound_control_plane }}
Cluster: ${{ fromJson(inputs.port_context).entity }} ❌

- name: Delete cluster from Upbound
run: |
kubectl --kubeconfig kubeconfig.yaml delete -f .up/clusters/${{ fromJson(inputs.port_context).entity.relations.upbound_control_plane }}/${{ fromJson(inputs.port_context).context.entity }}.yaml

- name: Delete cluster yaml file
run: |
git rm -f .up/clusters/${{ fromJson(inputs.port_context).entity.relations.upbound_control_plane }}/${{ fromJson(inputs.port_context).context.entity }}.yaml
git status

- name: Create Pull Request
id: create-pr
uses: peter-evans/create-pull-request@v4
with:
add-paths: .up/clusters
branch: "DELETE-CLUSTER-REQUEST-${{ fromJson(inputs.port_context).entity }}"
title: "Delete cluster request: ${{ fromJson(inputs.port_context).entity }}"
commit-message: "Delete cluster in upbound called ${{ fromJson(inputs.port_context).entity.identifier }}"

- name: Merge Pull Request
uses: juliangruber/merge-pull-request-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
number: ${{ steps.create-pr.outputs.pull-request-number }}
method: squash

- name: "Delete EKS cluster from Port"
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
identifier: ${{ fromJson(inputs.port_context).entity }}
runId: ${{ fromJson(inputs.port_context).runId }}
blueprint: eks_cluster
operation: DELETE

- 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).runId }}
icon: GithubActions
status: "SUCCESS"
summary: "Deletion successful🚀"
logMessage: "Deletion successful ✅ Deleted EKS Cluster Port entity for: ${{ fromJson(inputs.port_context).entity }}"

Deny cluster request (click to expand)
deny-cluster-request.yaml
name: Deny cluster request

on:
workflow_dispatch:
inputs:
port_context:
required: true
description: "Port's payload"
type: string

jobs:
deny-cluster-request-port:
if: github.event.inputs.port_context != ''
runs-on: ubuntu-latest
steps:
- name: Inform starting of denying EKS cluster request
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).context.runId }}
logMessage: "Denying EKS cluster request: ${{ fromJson(inputs.port_context).entity.identifier }}"

- uses: actions/checkout@v4
with:
persist-credentials: true

- name: Close Pull Request
uses: peter-evans/close-pull@v3
with:
pull-request-number: ${{ fromJson(inputs.port_context).entity.properties.request_pr_number }}
comment: "Cluster request ${{ fromJson(inputs.port_context).entity.identifier }} was denied ❌. Pull request closed."
delete-branch: false
token: ${{ secrets.GITHUB_TOKEN }}

- name: "Deny EKS cluster request"
if: ${{ always() }}
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
identifier: ${{ fromJson(inputs.port_context).entity.identifier }}
blueprint: eks_cluster_request
runId: ${{ fromJson(inputs.port_context).context.runId }}
properties: |
{
"status": "Denied"
}

- name: Inform that EKS cluster request has been denied
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).context.runId }}
summary: "Request denied."
status: "SUCCESS"
logMessage: "Request updated - status 'denied' ❌"

New cluster request (click to expand)
new-cluster-request.yaml
name: Create new cluster PR

on:
workflow_dispatch:
inputs:
control-plane:
type: string
required: true
cluster-name:
type: string
required: true
description: The cluster name to request
node-count:
type: string
required: false
description: Number of nodes for the cluster
default: "1"
node-size:
required: false
description: "Node size"
type: choice
default: small
options:
- small
- medium
- large
port_context:
type: string
required: true
description: Port Payload

jobs:
create-cluster-request:
runs-on: ubuntu-latest
outputs:
pr-id: ${{ steps.create-pr.outputs.pull-request-number }}
steps:
- name: Inform starting of creating EKS cluster request
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).runId }}
icon: GithubActions
logMessage: "Initiating EKS clutser request job 🏗️"

- uses: actions/checkout@v4
with:
persist-credentials: true

- name: Manipulate YAML file
run: |
if [[ ! -f ".up/clusters/${{ inputs.control-plane }}/${{ inputs.cluster-name }}.yaml" ]]; then
mkdir -p .up/clusters/${{ inputs.control-plane }}
cp .up/examples/cluster.yaml .up/clusters/${{ inputs.control-plane }}/${{ inputs.cluster-name }}.yaml
else
echo "This cluster already exists!"
exit 1
fi
yq -i e '.metadata.name = "${{ inputs.cluster-name }}"' .up/clusters/${{ inputs.control-plane }}/${{ inputs.cluster-name }}.yaml
yq -i e '.spec.id = "${{ inputs.cluster-name }}"' .up/clusters/${{ inputs.control-plane }}/${{ inputs.cluster-name }}.yaml
yq -i e '.spec.parameters.nodes.count = ${{ inputs.node-count }}' .up/clusters/${{ inputs.control-plane }}/${{ inputs.cluster-name }}.yaml
yq -i e '.spec.parameters.nodes.size = "${{ inputs.node-size }}"' .up/clusters/${{ inputs.control-plane }}/${{ inputs.cluster-name }}.yaml
yq -i e '.spec.writeConnectionSecretToRef.name = "${{ inputs.cluster-name }}-kubeconfig"' .up/clusters/${{ inputs.control-plane }}/${{ inputs.cluster-name }}.yaml

echo "New cluster's YAML:"
cat .up/clusters/${{ inputs.control-plane }}/${{ inputs.cluster-name }}.yaml

- 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).runId }}
icon: GithubActions
logMessage: |
Cluster request information:
Upbound control plane name: ${{ inputs.control-plane }}
Cluster name: ${{ inputs.cluster-name }}
Node size: ${{ inputs.node-size }}
Node Count: ${{ inputs.node-count }}

Creating pull request for the new cluster⏳


- name: Create Pull Request
id: create-pr
uses: peter-evans/create-pull-request@v4
with:
branch: "CLUSTER-REQUEST-${{ inputs.cluster-name }}"
title: "New cluster request: ${{ inputs.cluster-name }}"
commit-message: "Create new cluster in upbound called ${{ inputs.cluster-name }}"

- name: "Report new EKS cluster request to Port"
if: ${{ fromJson(inputs.port_context).action == 'request_new_cluster' }}
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
identifier: ${{ inputs.cluster-name }}
title: ${{ inputs.cluster-name }}
blueprint: eks_cluster_request
runId: ${{ fromJson(inputs.port_context).runId }}
properties: |
{
"request_pr_url": "${{ steps.create-pr.outputs.pull-request-url }}",
"request_pr_number": ${{ steps.create-pr.outputs.pull-request-number }},
"node_size": "${{ inputs.node-size }}",
"node_count": "${{ inputs.node-count }}"
}
relations: |
{
"upbound_control_plane": "${{ inputs.control-plane }}"
}

- uses: port-labs/port-github-action@v1
if: ${{ fromJson(inputs.port_context).action == 'request_new_cluster' }}
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
operation: PATCH_RUN
runId: ${{ fromJson(inputs.port_context).runId }}
link: ${{ steps.create-pr.outputs.pull-request-url }}
icon: GithubActions
logMessage: "Pull request created: ${{ steps.create-pr.outputs.pull-request-url }}"

- uses: port-labs/port-github-action@v1
if: ${{ fromJson(inputs.port_context).action == 'create_new_cluster' }}
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
operation: PATCH_RUN
runId: ${{ fromJson(inputs.port_context).runId }}
icon: GithubActions
logMessage: "Creation job run, auto-approving cluster request..."

force-approve-request:
uses: ./.github/workflows/approve-cluster-request.yaml
if: ${{ fromJson(inputs.port_context).action == 'create_new_cluster' }}
secrets: inherit
needs: create-cluster-request
with:
pr-id: ${{ needs.create-cluster-request.outputs.pr-id }}
cluster-name: ${{ inputs.cluster-name }}
node-count: ${{ inputs.node-count }}
node-size: ${{ inputs.node-size }}
run-id: ${{ fromJson(inputs.port_context).runId }}
control-plane: ${{ inputs.control-plane }}
  • .up/clusters/ - Create this folder in the repository. It will hold subdirectories which will correspond to different Upbound control planes. Each subdirectory will hold all the XR manifests to be deployed in the specific control plane.

  • .up/examples/cluster.yaml - Create this file in the repository and use the content below:

Upbound cluster file (click to respond)
cluster.yaml
apiVersion: k8s.starter.org/v1alpha1
kind: KubernetesCluster
metadata:
name: my-cluster
namespace: default
spec:
id: my-cluster
parameters:
nodes:
count: 3
size: small
services:
operators:
prometheus:
version: "34.5.1"
writeConnectionSecretToRef:
name: my-cluster-kubeconfig

Create repository secrets for the GitHub actions to use

Follow GitHub's guide to add required secrets to the repository. These are the secrets that need to be created:

  • UPBOUND_TOKEN - The Upbound organization's API token;
  • UPBOUND_ORG_ID - The Upbound organization's ID;
  • PORT_CLIENT_ID - The Port organization's client id;
  • PORT_CLIENT_SECRET - The Port organization's client secret.

Setting up Port

Starting with a clean Port organization, you will have to create some Port components. These components are Port blueprints and Port actions.

Creating blueprints

Create the following blueprints in the order that they appear below:

Upbound Control Plane (click to expand)
{
"identifier": "upbound_control_plane",
"title": "Upbound Control Plane",
"icon": "Cluster",
"schema": {
"properties": {},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}
EKS Cluster (click to expand)
{
"identifier": "eks_cluster",
"title": "EKS Cluster",
"icon": "Cluster",
"schema": {
"properties": {
"node_count": {
"icon": "Node",
"title": "Node Count",
"type": "number",
"description": "The cluster's node count"
},
"node_size": {
"icon": "Node",
"title": "Node Size",
"description": "The cluster's node size",
"type": "string",
"enum": [
"small",
"medium",
"large"
],
"enumColors": {
"small": "lightGray",
"medium": "lightGray",
"large": "lightGray"
}
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {
"claim_file_url": {
"title": "Claim file URL",
"icon": "Github",
"calculation": "\"https://github.com/port-demo/port-upbound-demo/blob/main/.up/clusters/\" + .identifier + \".yaml\"",
"type": "string",
"format": "url"
}
},
"relations": {
"upbound_control_plane": {
"title": "Upbound Control Plane",
"description": "The Upbound control plane for this cluster",
"target": "upbound_control_plane",
"required": false,
"many": false
}
}
}
EKS Cluster Request (click to expand)
{
"identifier": "eks_cluster_request",
"title": "EKS Cluster Request",
"icon": "Book",
"schema": {
"properties": {
"request_pr_url": {
"icon": "Github",
"title": "Request PR URL",
"type": "string",
"description": "The cluster request's PR URL",
"format": "url"
},
"request_pr_number": {
"icon": "Github",
"title": "Request PR Number",
"type": "number",
"minimum": 0
},
"node_count": {
"icon": "Node",
"title": "Node Count",
"type": "number",
"description": "Amount of nodes for this cluster"
},
"node_size": {
"icon": "Node",
"title": "Node Size",
"type": "string",
"description": "The node size for the cluster nodes",
"enum": [
"small",
"medium",
"large"
],
"enumColors": {
"small": "lightGray",
"medium": "lightGray",
"large": "lightGray"
}
},
"status": {
"icon": "BlankPage",
"title": "Status",
"description": "Status of the cluster request (Pending/Approved)",
"type": "string",
"default": "Pending",
"enum": [
"Pending",
"Approved",
"Denied"
],
"enumColors": {
"Pending": "yellow",
"Approved": "green",
"Denied": "red"
}
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {
"eks_cluster": {
"title": "EKS Cluster",
"description": "The cluster created for this request",
"target": "eks_cluster",
"required": false,
"many": false
},
"upbound_control_plane": {
"title": "Upbound Control Plane",
"description": "The control plane this cluster was requested for",
"target": "upbound_control_plane",
"required": false,
"many": false
}
}
}

You will need to create these blueprints in your Port organization.

Blueprint creation

Blueprint creation may fail if they are not created in the order that they appear above

Creating actions

Below are self-service actions which define the different actions we need, to trigger the different GitHub workflows.

These actions are defined on their appropriate blueprints.

Customizing GitHub values

Remember to change CHANGE_TO_YOUR_GITHUB_ORG_NAME and CHANGE_TO_YOUR_REPO_NAME values in the action blueprint. You need to replace these with your appropriate GitHub organization name and repository name.

Request New Cluster (click to expand)

Creates a EKS Cluster Request to request a new Upbound Cluster

{
"identifier": "eks_cluster_request_new_cluster",
"title": "Request new cluster",
"icon": "GithubActions",
"trigger": {
"type": "self-service",
"operation": "CREATE",
"userInputs": {
"properties": {
"cluster-name": {
"title": "Cluster Name",
"type": "string"
},
"node-size": {
"title": "Node Size",
"type": "string",
"default": "small",
"enum": [
"small",
"medium",
"large"
],
"enumColors": {
"small": "lightGray",
"medium": "lightGray",
"large": "lightGray"
}
},
"node-count": {
"icon": "DefaultProperty",
"title": "Node Count",
"type": "string",
"default": "1"
},
"control-plane": {
"icon": "DefaultProperty",
"title": "Upbound control plane",
"type": "string",
"blueprint": "upbound_control_plane",
"format": "entity"
}
},
"required": [
"cluster-name",
"control-plane"
],
"order": [
"control-plane",
"cluster-name",
"node-size",
"node-count"
]
},
"blueprintIdentifier": "eks_cluster"
},
"invocationMethod": {
"type": "GITHUB",
"org": "CHANGE_TO_YOUR_GITHUB_ORG_NAME",
"repo": "CHANGE_TO_YOUR_REPO_NAME",
"workflow": "new-cluster-request.yaml",
"workflowInputs": {
"cluster-name": "{{.inputs.\"cluster-name\"}}",
"node-size": "{{.inputs.\"node-size\"}}",
"node-count": "{{.inputs.\"node-count\"}}",
"control-plane": "{{.inputs.\"control-plane\" | if type == \"array\" then map(.identifier) else .identifier end}}",
"port_context": {
"action": "{{ .action.identifier[(\"eks_cluster_\" | length):] }}",
"runId": "{{.run.id}}"
}
},
"reportWorkflowStatus": true
},
"requiredApproval": false,
"publish": true
}
Approve Cluster Request (click to expand)

Grants approval to a request to create a new Upbound Cluster

{
"identifier": "eks_cluster_request_approve_cluster_request",
"title": "Approve Cluster Request",
"icon": "GithubActions",
"trigger": {
"type": "self-service",
"operation": "DAY-2",
"userInputs": {
"properties": {},
"required": []
},
"blueprintIdentifier": "eks_cluster_request"
},
"invocationMethod": {
"type": "GITHUB",
"org": "CHANGE_TO_YOUR_GITHUB_ORG_NAME",
"repo": "CHANGE_TO_YOUR_REPO_NAME",
"workflow": "approve-cluster-request.yaml",
"workflowInputs": {
"port_context": {
"runId": "{{.run.id}}",
"entity": "{{.entity}}"
}
},
"reportWorkflowStatus": true
},
"requiredApproval": false,
"publish": true
}
Deny Cluster Request (click to expand)

Denies a request to create a new Upbound cluster

{
"identifier": "eks_cluster_request_deny_cluster_request",
"title": "Deny cluster request",
"icon": "Alert",
"description": "Deny this EKS cluster request",
"trigger": {
"type": "self-service",
"operation": "DAY-2",
"userInputs": {
"properties": {},
"required": []
},
"blueprintIdentifier": "eks_cluster_request"
},
"invocationMethod": {
"type": "GITHUB",
"org": "CHANGE_TO_YOUR_GITHUB_ORG_NAME",
"repo": "CHANGE_TO_YOUR_REPO_NAME",
"workflow": "deny-cluster-request.yaml",
"workflowInputs": {
"port_context": {
"entity": "{{.entity}}",
"runId": "{{.run.id}}"
}
},
"reportWorkflowStatus": true
},
"requiredApproval": false,
"publish": true
}
Create New Cluster (click to expand)

Creates a new Upbound Cluster and an EKS Cluster entity

{
"identifier": "eks_cluster_create_new_cluster",
"title": "Create new cluster",
"icon": "GithubActions",
"trigger": {
"type": "self-service",
"operation": "CREATE",
"userInputs": {
"properties": {
"cluster-name": {
"title": "Cluster Name",
"type": "string"
},
"node-size": {
"title": "Node Size",
"type": "string",
"default": "small",
"enum": [
"small",
"medium",
"large"
],
"enumColors": {
"small": "lightGray",
"medium": "lightGray",
"large": "lightGray"
}
},
"node-count": {
"icon": "DefaultProperty",
"title": "Node Count",
"type": "string",
"default": "1"
},
"control-plane": {
"title": "Upbound control plane",
"type": "string",
"blueprint": "upbound_control_plane",
"format": "entity"
}
},
"required": [
"cluster-name",
"control-plane"
],
"order": [
"control-plane",
"cluster-name",
"node-size",
"node-count"
]
},
"blueprintIdentifier": "eks_cluster"
},
"invocationMethod": {
"type": "GITHUB",
"org": "CHANGE_TO_YOUR_GITHUB_ORG_NAME",
"repo": "CHANGE_TO_YOUR_REPO_NAME",
"workflow": "new-cluster-request.yaml",
"workflowInputs": {
"cluster-name": "{{.inputs.\"cluster-name\"}}",
"node-size": "{{.inputs.\"node-size\"}}",
"node-count": "{{.inputs.\"node-count\"}}",
"control-plane": "{{.inputs.\"control-plane\" | if type == \"array\" then map(.identifier) else .identifier end}}",
"port_context": {
"action": "{{ .action.identifier[(\"eks_cluster_\" | length):] }}",
"runId": "{{.run.id}}"
}
},
"reportWorkflowStatus": true
},
"requiredApproval": false,
"publish": true
}
Delete EKS Cluster (click to expand)

Deletes an existing Upbound Cluster and its corresponding EKS Cluster entity on Port

{
"identifier": "eks_cluster_delete_eks_cluster",
"title": "Delete EKS Cluster",
"icon": "Alert",
"trigger": {
"type": "self-service",
"operation": "DELETE",
"userInputs": {
"properties": {},
"required": [],
"order": []
},
"blueprintIdentifier": "eks_cluster"
},
"invocationMethod": {
"type": "GITHUB",
"org": "CHANGE_TO_YOUR_GITHUB_ORG_NAME",
"repo": "CHANGE_TO_YOUR_REPO_NAME",
"workflow": "delete-cluster.yaml",
"workflowInputs": {
"port_context": {
"runId": "{{.run.id}}",
"entity": "{{.entity}}"
}
},
"reportWorkflowStatus": true
},
"requiredApproval": true,
"approvalNotification": {
"type": "email"
},
"publish": true
}

Creating Upbound control plane Port entities

After setting up the Port blueprints and actions, we need to insert some entities manually.

These entities will represent the different Upbound control planes which were created in your Upbound organisation.

To do this, follow these steps:

  1. Navigate to the Upbound control planes catalog page.

  2. Click the Manually add Upbound Control Plane button (or the + Upbound Control plane at the top right of the page).

  1. In the identifier field, insert the Upbound control plane identifier which we saved earlier and click create (do this step multiple times if there are more than 1 control plane).

Using Port

At this point, everything should be set up. Browse to your Self-service page to view the different actions you defined in Port, and try them out