Integrating Terraform with Backstage

In the drive to enhance developer experience and streamline infrastructure workflows, integrating Infrastructure as Code (IaC) tools with developer portals has become a strategic imperative. Backstage, a popular open-source framework for building developer portals, combined with Terraform, the de facto standard for IaC, offers a potent solution for enabling developer self-service, enforcing standardization, and improving visibility.

Why Bridge Backstage and Terraform?

The core motivation is to empower developers. By integrating Terraform into Backstage, organizations allow development teams to provision and manage necessary infrastructure—like databases, CI/CD pipelines, or cloud storage—directly from a familiar interface. This abstracts away the underlying complexities of HCL and cloud provider APIs, enabling faster development cycles. Furthermore, it ensures that all provisioned infrastructure adheres to organizational best practices, security policies, and governance standards through pre-defined, validated templates. This synergy solves common pain points like slow provisioning, inconsistent tooling, and lack of discoverability for infrastructure resources.

Navigating the Integration Landscape

Several paths exist for this integration:

  1. The Open-Source Route: Leveraging Backstage's Scaffolder with custom CI/CD pipelines to run Terraform CLI commands offers maximum flexibility. This approach provides complete control but demands significant engineering effort for setup, maintenance, and security.
  2. Terraform Cloud: HashiCorp's managed service can offload Terraform execution and state management. Backstage plugins can provide visibility and sometimes control over Terraform Cloud workspaces. This simplifies operations but introduces costs and reliance on the HashiCorp ecosystem, which is exclusively Terraform-centric.
  3. Terraform Cloud Alternatives like Scalr: Platforms such as Scalr offer a compelling alternative for managing Terraform (and other tools like OpenTofu and Terragrunt) at scale, presenting distinct advantages in a Backstage integration.

Conceptual Code Sample (Backstage Plugin interacting with Scalr API):

// backstage-scalr-plugin-component.ts (Simplified)
// Assuming a Scalr API client library or direct fetch calls
async function provisionWithScalr(environmentId: string, workspaceName: string, variables: Record<string, any>) {
  const SCALR_API_ENDPOINT = 'https://mycompany.scalr.io/api/iac/v3'; // Example
  const SCALR_TOKEN = '...'; // Securely obtained token

  try {
    // 1. Find or create workspace (simplified)
    // This might involve checking if a workspace with `workspaceName` exists in `environmentId`
    // or creating it if it doesn't.

    // 2. Create a new configuration version (if not VCS-driven)
    // This would involve uploading the Terraform code or pointing to a VCS commit.

    // 3. Create and run a deployment (plan & apply)
    const response = await fetch(`${SCALR_API_ENDPOINT}/environments/${environmentId}/workspaces/${workspaceName}/runs`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${SCALR_TOKEN}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        data: {
          type: 'runs',
          attributes: {
            message: 'Deployment triggered from Backstage',
            'is-destroy': false,
            // 'auto-apply': true, // If desired
            variables: Object.entries(variables).map(([key, value]) => ({
                name: key,
                value: String(value),
                sensitive: false,
                hcl: false, // Or true if it's HCL
            })),
            // Specify configuration version, etc.
          },
        },
      }),
    });

    if (!response.ok) {
      throw new Error(`Scalr API error: ${response.statusText}`);
    }
    const run = await response.json();
    console.log(`Scalr deployment ${run.data.id} initiated.`);
    // Monitor run status via further API calls or Scalr UI

  } catch (error) {
    console.error('Error provisioning with Scalr:', error);
  }
}

Conceptual Code Sample (Backstage Plugin interacting with TFC API):

// backstage-tfc-plugin-component.ts (Simplified)
import { tfeApiRef } from '@backstage/plugin-terraform-common';
// ...
async function provisionWithTFC(workspaceId: string, variables: Record<string, any>) {
  const tfeApi = useApi(tfeApiRef);
  try {
    // 1. Create a new run
    const run = await tfeApi.createRun({
      workspaceId,
      message: 'Run triggered from Backstage',
      // Potentially set autoApply: true if desired
    });

    // 2. (Optional) Upload configuration versions if not VCS-driven
    // 3. (Optional) Set variables for the run
    await tfeApi.setVariables({
        workspaceId,
        runId: run.id, // Or apply to workspace directly
        variables: Object.entries(variables).map(([key, value]) => ({
            key,
            value: String(value),
            category: 'terraform',
            sensitive: false,
        })),
    });

    // 4. (If not auto-apply) Apply the run after plan review
    // This might involve manual steps in TFC UI or further API calls
    console.log(`Terraform Cloud run ${run.id} created. Review and apply in TFC.`);

  } catch (error) {
    console.error('Error provisioning with Terraform Cloud:', error);
  }
}

Conceptual Code Sample (Backstage Scaffolder Template Action):

# scaffolder-template.yaml
# ...
steps:
  - id: trigger-terraform-pipeline
    name: Trigger Terraform CI/CD Pipeline
    action: 'http:request' # Or a custom action for your CI/CD system
    input:
      url: 'https://my-ci-cd.example.com/api/v1/trigger'
      method: 'POST'
      headers:
        Content-Type: 'application/json'
        Authorization: 'Bearer ${{ parameters.ciCdToken }}'
      body:
        repository: '${{ parameters.gitRepoUrl }}'
        branch: 'main'
        terraformAction: 'apply'
        variables:
          instance_type: '${{ parameters.instanceType }}'
          region: '${{ parameters.region }}'
# ...

In the CI/CD pipeline (e.g., Jenkins, GitLab CI, GitHub Actions):

# ci-cd-pipeline-script.sh
terraform init
terraform validate
terraform plan -out=tfplan -var="instance_type=$INSTANCE_TYPE" -var="region=$REGION"
# Potentially add an approval gate here
terraform apply -auto-approve tfplan

Considering Your Options: The Scalr Perspective

While each approach has its merits, organizations with complex environments or those seeking greater adaptability may find platforms like Scalr particularly aligned with their needs. Scalr's architecture, for instance, provides notable backend flexibility, allowing teams to use their existing state storage solutions rather than being tied to a proprietary backend. This can be a significant advantage for enterprises with established practices.

Furthermore, Scalr's hierarchical model for organizing accounts, environments, and workspaces, coupled with its approach to sharing credentials and policies, can map more effectively to the intricate structures of larger organizations. This facilitates more granular governance and simpler management across diverse teams and projects. When Backstage aims to be the single pane of glass for a wide array of infrastructure needs, the underlying IaC management platform's ability to support this complexity without imposing undue restrictions becomes critical.

Summary Comparison

Feature

Open-Source (Custom CI/CD)

Terraform Cloud

Scalr (and similar alternatives)

Control & Flexibility

Very High

Medium

High

Engineering Effort

High

Low

Medium

Cost

Potentially Low (infra)

Usage-based (HashiCorp)

Subscription-based

State Management

Self-managed

Managed by TFC

Flexible (self or managed by Scalr)

Governance

Custom implementation

TFC Policies (OPA)

Scalr Policies (OPA), RBAC, Hierarchy

Multi-IaC Tooling

Yes (with custom work)

Terraform only

Yes (Terraform, OpenTofu, etc.)

Vendor Lock-in

Low

Medium (HashiCorp ecosystem)

Low

Setup Complexity

High

Medium

Medium

Scalability

Depends on CI/CD setup

High

High

Community Support

Large (Backstage, TF)

Large (Terraform)

Growing

Making the Right Choice

Ultimately, the decision on how to integrate Terraform with Backstage depends on an organization's specific requirements, existing infrastructure, technical expertise, and budget. The open-source path offers unparalleled customization, while Terraform Cloud provides a managed, albeit Terraform-specific, solution. For those prioritizing flexibility, robust governance in multifaceted environments, and broader IaC tool support, exploring alternatives like Scalr is a prudent step. The goal remains a seamless developer experience, underpinned by powerful, governed, and efficient infrastructure automation.