OpenMCF logoOpenMCF

Loading...

CI/CD Integration

OpenMCF is designed to run unattended in CI/CD pipelines. This guide covers patterns for GitHub Actions, GitLab CI, and general automation, including credential injection, non-interactive execution, manifest validation, and Kustomize overlay selection.

Non-Interactive Flags

By default, OpenMCF prompts for confirmation before making changes. In CI/CD, use --yes or --auto-approve to skip the prompt:

# Either flag works — they are equivalent
openmcf pulumi up -f manifest.yaml --yes
openmcf apply -f manifest.yaml --auto-approve

Both flags are available on deployment commands (apply, pulumi up, tofu apply, terraform apply, destroy).

Validation in Pipelines

Run manifest validation as an early step in your pipeline. Validation catches schema errors, type mismatches, and constraint violations in seconds — before any cloud API call:

openmcf validate -f manifest.yaml

Exit code 0 means valid. Any non-zero exit code indicates validation failure, which should stop the pipeline.

GitHub Actions

Basic Deployment (AWS)

name: Deploy Infrastructure

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install OpenMCF
        run: |
          curl -sSL https://get.openmcf.org | bash

      - name: Validate manifest
        run: openmcf validate -f ops/aws/database.yaml

      - name: Deploy
        run: openmcf pulumi up -f ops/aws/database.yaml --yes
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: us-west-2

Kustomize with Branch-Based Overlays

name: Deploy API

on:
  push:
    branches: [main, develop]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Determine overlay
        id: env
        run: |
          if [ "${{ github.ref }}" == "refs/heads/main" ]; then
            echo "overlay=prod" >> $GITHUB_OUTPUT
          else
            echo "overlay=dev" >> $GITHUB_OUTPUT
          fi

      - name: Deploy
        run: |
          openmcf pulumi up \
            --kustomize-dir services/api/kustomize \
            --overlay ${{ steps.env.outputs.overlay }} \
            --yes
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

GCP with Service Account

name: Deploy to GCP

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Deploy
        run: openmcf pulumi up -f ops/gcp/database.yaml --yes
        env:
          GOOGLE_APPLICATION_CREDENTIALS: ${{ runner.temp }}/gcp-key.json

      # Write the service account key to a temp file
      - name: Setup GCP credentials
        run: echo '${{ secrets.GCP_SERVICE_ACCOUNT_KEY }}' > ${{ runner.temp }}/gcp-key.json

Alternatively, use the -p flag with a base64-encoded key stored as a secret:

      - name: Create provider config
        run: |
          echo "service_account_key_base64: ${{ secrets.GCP_SA_KEY_BASE64 }}" > /tmp/gcp-cred.yaml

      - name: Deploy
        run: openmcf pulumi up -f ops/gcp/database.yaml -p /tmp/gcp-cred.yaml --yes

Azure with Service Principal

name: Deploy to Azure

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Deploy
        run: openmcf pulumi up -f ops/azure/cluster.yaml --yes
        env:
          ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}
          ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }}
          ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
          ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}

Dynamic Image Tags

Use --set to inject build-time values like image tags:

      - name: Deploy with commit SHA
        run: |
          openmcf pulumi up \
            -f ops/k8s/api.yaml \
            --set spec.container.image.tag=${{ github.sha }} \
            --yes

GitLab CI

Basic Deployment

stages:
  - validate
  - deploy

validate:
  stage: validate
  script:
    - openmcf validate -f ops/aws/database.yaml

deploy:
  stage: deploy
  script:
    - openmcf pulumi up -f ops/aws/database.yaml --yes
  variables:
    AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID
    AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
    AWS_REGION: us-west-2
  only:
    - main

Kustomize with Branch-Based Overlays

deploy:
  stage: deploy
  script:
    - |
      if [ "$CI_COMMIT_BRANCH" == "main" ]; then
        OVERLAY="prod"
      elif [ "$CI_COMMIT_BRANCH" == "staging" ]; then
        OVERLAY="staging"
      else
        OVERLAY="dev"
      fi
    - openmcf pulumi up --kustomize-dir services/api/kustomize --overlay $OVERLAY --yes
  only:
    - main
    - staging
    - develop

Protected Environments

Use GitLab's environment feature for deployment approvals:

deploy-prod:
  stage: deploy
  script:
    - openmcf pulumi up -f ops/database.yaml --yes
  environment:
    name: production
  when: manual
  only:
    - main

Credential Injection Patterns

Store Credentials as CI/CD Secrets

PlatformWhere to StoreNotes
GitHub ActionsSettings > Secrets and variables > ActionsUse repository or organization secrets
GitLab CISettings > CI/CD > VariablesMark as "Protected" and "Masked"
JenkinsCredentials ManagerUse "Secret text" or "Secret file" types

Multi-Environment Credentials

Use environment-specific secrets for different deployment targets:

# GitHub Actions with environments
jobs:
  deploy-prod:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Deploy
        run: openmcf pulumi up -f ops/database.yaml --yes
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

GitHub Actions environments can require reviewers for production deployments, providing an approval gate before infrastructure changes are applied.

Pipeline Best Practices

Validate before deploying. Run openmcf validate as an early pipeline step. Failed validation should block deployment.

Use preview/plan before apply. In production pipelines, run openmcf pulumi preview or openmcf plan first, then apply in a separate step or with manual approval:

openmcf pulumi preview -f manifest.yaml
# Review output
openmcf pulumi up -f manifest.yaml --yes

Pin manifest versions. If manifests are in a separate repository, reference a specific commit or tag rather than a branch to ensure reproducible deployments.

Use --set for build-time values only. Inject dynamic values like image tags and commit SHAs via --set. Keep permanent configuration in the manifest file and commit it to version control.

Separate validate and deploy stages. Validation runs in seconds and can run on every pull request. Deployment should only run on merge to the target branch.

What's Next

  • Credentials — Quick reference for all provider credentials
  • AWS Provider Setup — AWS IAM for CI/CD
  • GCP Provider Setup — GCP service accounts for CI/CD
  • Azure Provider Setup — Azure service principals for CI/CD
  • Kustomize Integration — Multi-environment overlay workflows
  • Advanced Usage — Runtime overrides and combining techniques

Next article

Migrating to OpenMCF

Migrating to OpenMCF If you are already using Terraform, OpenTofu, or Pulumi to manage infrastructure, OpenMCF does not replace your IaC engine. It wraps it with a consistent manifest layer, proto-based validation, and a unified CLI. This guide explains what changes when you adopt OpenMCF, what stays the same, and how to migrate existing infrastructure. What Changes, What Stays the Same What Changes What Stays the Same From Raw Terraform to OpenMCF Before: Terraform Project A typical Terraform...
Read next article