Skip to main content

Crossplane Ep 15: Building an IDP with Backstage

Rachmat Hidayat
Author
Rachmat Hidayat
Learn & sharing insights on TypeScript, Go, Kubernetes, DevOps, DevSecOps, SRE, Platform Engineering, AI/ML Engineering, and MLOps.
crossplane - This article is part of a series.
Part 15: This Article
The goal of Platform Engineering is to reduce cognitive load on Application Developers. Asking a Java developer to memorize the OpenAPI schema of a PostgreSQLInstance YAML file is still too much friction. The holy grail of our industry is the Self-Service Portal. Let’s build a Web GUI for Crossplane using Backstage.

1. What is Backstage?
#

Backstage is an open-source framework created by Spotify. It is used to build Internal Developer Portals (IDPs).

It acts as a centralized catalog for all microservices in a company. More importantly, it features the Software Templates engine.

Software Templates allow Platform Engineers to design beautiful Web UIs (React forms) that capture developer inputs (like “Storage Size” or “Environment”). When the developer clicks “Create”, Backstage executes a CI pipeline in the background.


2. The Backstage -> GitOps -> Crossplane Pipeline
#

We are going to stitch together everything we have learned in this entire series into one elegant flow.

The Workflow:

  1. Developer logs into Backstage Web UI.
  2. Developer fills out a web form for a new Database.
  3. Developer clicks “Submit”.
  4. Backstage automatically generates the PostgreSQLInstance YAML Claim and commits it directly to the app-infrastructure GitHub repository.
  5. ArgoCD detects the new Git commit and applies the YAML to the Kubernetes cluster.
  6. The Validating Webhook inspects the Claim for compliance.
  7. Crossplane receives the Claim and executes the Go Composition Function.
  8. The AWS Provider boots the physical RDS database.
  9. Crossplane writes the live AWS Endpoint URL back to the Claim Status.
  10. Backstage reads the Kubernetes API and displays the live DB URL to the developer on the Web UI.

Zero human intervention. Zero terminal commands. Absolute security.


3. Creating the Backstage Template
#

To achieve Step 4, we must write a Backstage Template.

A Backstage template is a YAML file that defines a UI form using JSON Schema, and then defines an action (like writing a file to GitHub).

Create a file named template-database.yaml in your Backstage repository:

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: create-postgres-db
  title: Provision a PostgreSQL Database
  description: Request a compliant RDS instance from the Platform Team.
spec:
  owner: platform-engineering
  type: service

  # 1. The Web UI Form Definition
  parameters:
    - title: Database Details
      required:
        - dbName
        - storageGB
        - environment
      properties:
        dbName:
          title: Database Name
          type: string
          description: The logical name of your database.
        storageGB:
          title: Storage Size (GB)
          type: integer
          minimum: 10
          maximum: 500 # Client-side validation!
        environment:
          title: Target Environment
          type: string
          enum: ["dev", "staging", "prod"]

  # 2. The Actions to execute when "Submit" is clicked
  steps:
    # Action A: Render the Crossplane YAML file dynamically
    - id: render-yaml
      name: Render Crossplane Claim
      action: fetch:template
      input:
        # We point it to a skeleton file in our repo (see below)
        url: ./skeleton
        values:
          name: ${{ parameters.dbName }}
          storage: ${{ parameters.storageGB }}
          env: ${{ parameters.environment }}

    # Action B: Push the rendered YAML to the GitOps Repo
    - id: publish-github
      name: Commit to GitOps Repository
      action: publish:github:pull-request
      input:
        repoUrl: github.com?repo=app-infrastructure&owner=acmecorp
        branchName: create-db-${{ parameters.dbName }}
        title: "Infrastructure Request: ${{ parameters.dbName }}"
        description: "Automated PR generated by Backstage."

The Skeleton File
#

Inside the ./skeleton directory referenced above, you place the raw Crossplane YAML with Backstage variables injected.

./skeleton/database-claim.yaml:

apiVersion: database.acmecorp.com/v1alpha1
kind: PostgreSQLInstance
metadata:
  name: ${{ values.name }}
  namespace: ${{ values.env }}
spec:
  parameters:
    storageGB: ${{ values.storage }}
    environment: ${{ values.env }}

4. The Developer Experience
#

Let’s look at the result.

  1. The Developer opens Backstage (https://developer.acmecorp.com).
  2. They click the “Create…” button.
  3. They see a clean, company-branded form titled “Provision a PostgreSQL Database”.
  4. They type auth-service-db, 50, and select dev from the dropdown.
  5. They click “Submit”.

A loading bar appears. Behind the scenes, Backstage opens a Pull Request on GitHub. The PR contains the perfectly formatted Crossplane Claim.

The developer’s Tech Lead clicks “Approve” on the GitHub PR.

ArgoCD and Crossplane take over. Five minutes later, the developer receives a Slack notification containing the live connection string for their brand new AWS RDS instance.


Conclusion
#

Congratulations. You have reached the pinnacle of Platform Engineering.

In this series, you learned that Crossplane is not just an alternative to Terraform. It is a completely different paradigm.

  • You learned how to harness the Kubernetes Reconciliation loop to prevent drift (Ep 1).
  • You learned how to build custom APIs using XRDs (Ep 4).
  • You learned how to inject global variables using EnvironmentConfigs (Ep 7).
  • You escaped the limitations of YAML by writing Go Composition Functions (Ep 11).
  • You built a mathematical firewall using Validating Webhooks (Ep 14).
  • Finally, you wrapped the entire architecture in a beautiful Web UI using Backstage (Ep 15).

You have transformed your Kubernetes cluster into a Universal Control Plane.

Thank you for following along with the Crossplane learning path!

crossplane - This article is part of a series.
Part 15: This Article