Skip to content

github-workflows-dojo360-container-promotion

Multi-environment container deployment promotion through prescribed deployment paths with automated approval gates and E2E testing

active
IDE:
claude
codex
vscode
Version:
1.0.0
Owner:pcorazao
github-actions
workflow
dojo360

GitHub Copilot Skill: Container CD Promotion Workflow

Overview

The Container CD Promotion workflow (container-cd-promotion.yml) is a Dojo360 workflow that provides combined promotion and deployment of containerized applications through an opinionated sequence of workflow jobs across multiple environments. This workflow reuses the individual Container-CD workflow for each environment stage while maintaining consistency through approval gates and deployment path validation.

When to Use This Workflow:

  • Promoting container deployments across multiple environments (dev → qa → crt → prd)
  • Implementing gated approvals between environment promotions
  • Maintaining consistent deployment artifacts across all environments
  • Managing multi-environment container deployments with a single workflow
  • Deploying to AWS ECS or Azure ACS across environment tiers

Key Features

1. Deployment Path Validation

  • Validates user-provided deployment-path against promotion-path in metadata
  • Ensures deployments follow prescribed environment progression
  • Prevents out-of-sequence environment deployments
  • Fails fast if deployment path doesn't match promotion path

2. Environment-Based Approvals

  • Configurable approval gates between environments
  • GitHub environment protection rules integration
  • Trigger jobs act as gateways to next environment
  • Supports EFIX branches with user-defined stages

3. Single Workflow Management

  • Manages CD across all deployment stages with one workflow
  • Maintains consistent GitHub reference across all environments
  • Supports tag, default branch, and EFIX branch deployments
  • In-flight promotion workflows unaffected by feature branch merges

4. E2E Testing Integration

  • Stage-specific E2E test execution
  • Configurable test execution per environment
  • Dynamic workflow file execution
  • Custom test input parameters

5. Docker Tag Retrieval

  • Supports retrieving docker-tag from CI workflow outputs
  • Reduces manual input requirements
  • Seamless CI/CD pipeline integration
  • Automated artifact tracking

6. Multi-Cloud Support

  • AWS (awsChc20, awsOptum) for ECS deployments
  • Azure (azureOptum) for ACS deployments
  • Cloud-specific container registry integration (ECR, ACR)
  • OIDC authentication for both clouds

Prerequisites

Before using this workflow, ensure the following requirements are met:

1. Dojo360 Metadata API Onboarding

  • Product must be onboarded to Dojo360 Metadata API
  • Alternatively, create a local metadata file
  • Reference: Working with CloudBricks

2. OIDC Configuration

  • AWS: Configure OIDC when cloud-type is awsOptum
  • Azure: Configure OIDC when cloud-type is azureOptum

3. GitHub Environment Configuration

  • Each environment must have at least one required reviewer
  • Environment protection rules configured with required reviewers
  • Failure to configure reviewers may cause workflow failures

4. Container Images

  • Docker images available in specified registry
  • Container registry accessible from deployment environments
  • Images tagged appropriately for promotion

Requirements

  • Terraform: ~> 1.9.x
  • AWS Provider: ~> 5.xx (for AWS operations)
  • AzureRM Provider: ~> 3.xx (for Azure operations)
  • GCP Provider: ~> 6.xx (for GCP operations)
  • GitHub Runner: uhg-runner (for Optum security compliance)

Input Reference

Required Inputs

All Container CD Promotion deployments require the following inputs:

InputTypeDescription
aide-idstringAIDE ID to fetch metadata
cloud-typestringCloud type for metadata (see Supported Cloud Types)
deployment-pathstringDeployment path (e.g., dev, qa, cert, prd)
domainstringDomain to fetch metadata
team-namestringTeam name to fetch metadata

Optional Inputs

InputTypeDefaultDescription
container-registrystring""Cloud-specific container registry (ECR for AWS, ACR for Azure)
deployment-controller-typestringECSTriggers CODE_DEPLOY if set to CODE_DEPLOY
docker-imagestring""Fully qualified path of image to pull for deployment
docker-repositorystringdocker.repo1.uhc.comDocker registry from which to pull container image
docker-tagstring""Tag associated with image to pull from repository
e2e-tests-enabled-stagesstring''Controls E2E test job execution after deployment in each stage
e2e-workflow-filestringN/AName of workflow/action file to execute dynamically
e2e-workflow-inputsstringN/AJSON data containing additional input overrides for workflow
jfrog-project-keystring""Key for identifying JFrog project in SaaS Artifactory
prm-base-urlstringhttps://prm.optum.comBase URL of PRM Instance to read secrets from
refstringHEADBranch, tag, or SHA to check out
remote-state-file-namestring""Filename of remote state file
remote-state-folder-namestring""Name of remote state folder
runner-labelsstring""Comma-separated Runner labels for workflow
terraform-directorystringtfDirectory path relative to repository hosting Terraform code
terraform-prm-secretsstringN/AComma-separated list of PRM secrets to map to tfvars
terraform-provider-network-mirrorstringhttps://repo1.uhc.com/artifactory/api/terraform/terraform-virtual/providers/Proxy URL as mirror for Terraform provider
terraform-vars-filesstringN/AComma-separated list of tfvars files
terraform-vars-valuesstringN/AJSON string of tfvars
terraform-versionstring1.9.2Version of Terraform to use
terraform-volcan-secretsstring""Comma-separated list of Volcan secrets to map to tfvars
volcan-base-urlstringvolcan-cloud.optum.comBase URL of Volcan Instance to target

Required Secrets

GitHub Token (GH_TOKEN)

The workflow requires GH_TOKEN to be configured as a secret:

secrets:
  GH_TOKEN: ${{ secrets.GH_TOKEN }}

Note: GH_TOKEN must have appropriate permissions for repository operations and environment access.

Usage Examples

1. Basic Multi-Environment Promotion

name: Container Promotion
on:
  workflow_dispatch:
    inputs:
      ref:
        description: 'Git reference to deploy'
        required: false
        type: string

permissions:
  id-token: write
  contents: write
  actions: read
  pull-requests: write
  security-events: write
  checks: write
  issues: read

jobs:
  promote:
    runs-on: uhg-runner
    uses: dojo360/pipelines-workflows/.github/workflows/container-cd-promotion.yml@beta
    with:
      aide-id: "<change me>"
      cloud-type: "awsOptum"
      deployment-path: "dev-qa-prd"
      domain: "<change me>"
      team-name: "<change me>"
      docker-repository: "centraluhg.jfrog.io"
      docker-image: "/your-project-docker-np-loc/your-app"
      docker-tag: "1.0.0"
      ref: ${{ inputs.ref }}
    secrets:
      GH_TOKEN: ${{ secrets.GH_TOKEN }}

2. Promotion with Dynamic Docker Tag from CI

name: Build and Promote Container
on:
  push:
    branches:
      - main

permissions:
  id-token: write
  contents: write
  actions: read
  pull-requests: write
  security-events: write
  checks: write
  issues: read

jobs:
  build:
    runs-on: uhg-runner
    uses: uhg-pipelines/ci-workflows/.github/workflows/node-npm-ci.yml@v2
    with:
      jfrog-project-key: your-project-key
      docker-tags: centraluhg.jfrog.io/your-project-docker-np-loc/your-app:${{ github.run_number }}
    secrets:
      GH_TOKEN: ${{ secrets.GH_TOKEN }}

  promote:
    needs: build
    runs-on: uhg-runner
    uses: dojo360/pipelines-workflows/.github/workflows/container-cd-promotion.yml@beta
    with:
      aide-id: "<change me>"
      cloud-type: "awsOptum"
      deployment-path: "dev-qa-crt-prd"
      domain: "<change me>"
      team-name: "<change me>"
      docker-repository: "centraluhg.jfrog.io"
      docker-image: "/your-project-docker-np-loc/your-app"
      docker-tag: ${{ needs.build.outputs.uploaded-docker-tag }}
    secrets:
      GH_TOKEN: ${{ secrets.GH_TOKEN }}

3. Promotion with E2E Testing

name: Container Promotion with E2E Tests
on:
  workflow_dispatch:
    inputs:
      deployment-path:
        description: 'Deployment path'
        required: true
        type: choice
        options:
          - dev
          - dev-qa
          - dev-qa-prd

permissions:
  id-token: write
  contents: write
  actions: read
  pull-requests: write
  security-events: write
  checks: write
  issues: read

jobs:
  promote:
    runs-on: uhg-runner
    uses: dojo360/pipelines-workflows/.github/workflows/container-cd-promotion.yml@beta
    with:
      aide-id: "<change me>"
      cloud-type: "awsOptum"
      deployment-path: ${{ inputs.deployment-path }}
      domain: "<change me>"
      team-name: "<change me>"
      docker-repository: "centraluhg.jfrog.io"
      docker-image: "/your-project-docker-np-loc/your-app"
      docker-tag: "stable"
      e2e-tests-enabled-stages: "qa,prd"
      e2e-workflow-file: "e2e-tests.yml"
      e2e-workflow-inputs: |
        {
          "test-suite": "smoke",
          "environment": "${{ inputs.deployment-path }}"
        }
    secrets:
      GH_TOKEN: ${{ secrets.GH_TOKEN }}

4. Promotion with PRM Secrets

name: Container Promotion with PRM Secrets
on:
  push:
    tags:
      - 'v*'

permissions:
  id-token: write
  contents: write
  actions: read
  pull-requests: write
  security-events: write
  checks: write
  issues: read

jobs:
  promote:
    runs-on: uhg-runner
    uses: dojo360/pipelines-workflows/.github/workflows/container-cd-promotion.yml@beta
    with:
      aide-id: "<change me>"
      cloud-type: "awsOptum"
      deployment-path: "dev-qa-crt-prd"
      domain: "<change me>"
      team-name: "<change me>"
      docker-repository: "centraluhg.jfrog.io"
      docker-image: "/your-project-docker-np-loc/your-app"
      docker-tag: ${{ github.ref_name }}
      terraform-directory: "terraform/ecs"
      terraform-prm-secrets: "db_password,api_key,jwt_secret"
      prm-base-url: "https://prm.optum.com"
    secrets:
      GH_TOKEN: ${{ secrets.GH_TOKEN }}

5. Blue-Green Deployment Promotion

name: Blue-Green Container Promotion
on:
  workflow_dispatch:
    inputs:
      deployment-path:
        description: 'Deployment path'
        required: true
        type: string
        default: 'dev-qa-prd'
      docker-tag:
        description: 'Docker image tag'
        required: true
        type: string

permissions:
  id-token: write
  contents: write
  actions: read
  pull-requests: write
  security-events: write
  checks: write
  issues: read

jobs:
  promote:
    runs-on: uhg-runner
    uses: dojo360/pipelines-workflows/.github/workflows/container-cd-promotion.yml@beta
    with:
      aide-id: "<change me>"
      cloud-type: "awsOptum"
      deployment-path: ${{ inputs.deployment-path }}
      domain: "<change me>"
      team-name: "<change me>"
      docker-repository: "centraluhg.jfrog.io"
      docker-image: "/your-project-docker-np-loc/your-app"
      docker-tag: ${{ inputs.docker-tag }}
      deployment-controller-type: "CODE_DEPLOY"
      terraform-directory: "terraform/ecs-bg"
      container-registry: "ecr"
    secrets:
      GH_TOKEN: ${{ secrets.GH_TOKEN }}

6. Azure ACS Promotion with Custom Terraform Variables

name: Azure ACS Container Promotion
on:
  workflow_dispatch:
    inputs:
      environment-path:
        description: 'Deployment environments'
        required: true
        type: choice
        options:
          - dev-qa
          - dev-qa-prd
      image-tag:
        description: 'Container image tag'
        required: true
        type: string

permissions:
  id-token: write
  contents: write
  actions: read
  pull-requests: write
  security-events: write
  checks: write
  issues: read

jobs:
  promote:
    runs-on: uhg-runner
    uses: dojo360/pipelines-workflows/.github/workflows/container-cd-promotion.yml@beta
    with:
      aide-id: "<change me>"
      cloud-type: "azureOptum"
      deployment-path: ${{ inputs.environment-path }}
      domain: "<change me>"
      team-name: "<change me>"
      docker-repository: "centraluhg.jfrog.io"
      docker-image: "/your-project-docker-np-loc/your-app"
      docker-tag: ${{ inputs.image-tag }}
      container-registry: "acr"
      terraform-directory: "terraform/acs"
      terraform-vars-files: "common.tfvars,azure.tfvars"
      terraform-vars-values: |
        {
          "image_tag": "${{ inputs.image-tag }}",
          "deployment_timestamp": "${{ github.run_number }}"
        }
    secrets:
      GH_TOKEN: ${{ secrets.GH_TOKEN }}

7. Production Promotion with Full Validation

name: Production Container Promotion
on:
  workflow_dispatch:
    inputs:
      deployment-path:
        description: 'Deployment path'
        required: true
        type: choice
        options:
          - dev-qa-crt-prd
      docker-tag:
        description: 'Docker tag to promote'
        required: true
        type: string
      run-e2e:
        description: 'Run E2E tests'
        required: true
        type: boolean
        default: true

permissions:
  id-token: write
  contents: write
  actions: read
  pull-requests: write
  security-events: write
  checks: write
  issues: read

jobs:
  validate-inputs:
    runs-on: uhg-runner
    steps:
      - name: Validate Docker Tag
        run: |
          if [[ ! "${{ inputs.docker-tag }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
            echo "Error: Docker tag must follow semantic versioning (x.y.z)"
            exit 1
          fi

  promote:
    needs: validate-inputs
    runs-on: uhg-runner
    uses: dojo360/pipelines-workflows/.github/workflows/container-cd-promotion.yml@beta
    with:
      aide-id: "<change me>"
      cloud-type: "awsOptum"
      deployment-path: ${{ inputs.deployment-path }}
      domain: "<change me>"
      team-name: "<change me>"
      docker-repository: "centraluhg.jfrog.io"
      docker-image: "/your-project-docker-np-loc/your-app"
      docker-tag: ${{ inputs.docker-tag }}
      deployment-controller-type: "CODE_DEPLOY"
      terraform-directory: "terraform/production"
      terraform-prm-secrets: "db_password,api_key,redis_password,jwt_secret"
      e2e-tests-enabled-stages: ${{ inputs.run-e2e && 'crt,prd' || '' }}
      e2e-workflow-file: "production-e2e-tests.yml"
      e2e-workflow-inputs: |
        {
          "test-suite": "full",
          "notify-on-failure": true
        }
      terraform-vars-files: "common.tfvars,production.tfvars"
      terraform-version: "1.9.2"
    secrets:
      GH_TOKEN: ${{ secrets.GH_TOKEN }}

  notify-completion:
    needs: promote
    runs-on: uhg-runner
    if: always()
    steps:
      - name: Notify Teams
        run: |
          echo "Production promotion completed: ${{ needs.promote.result }}"
          # Add Teams webhook notification here

Workflow Execution Flow

The Container CD Promotion workflow presents the following jobs:

  1. parse_deployment_path: Validates deployment path against promotion path
  2. environment_1: Deploys to first environment
  3. trigger_environment_2: Approval gate for second environment
  4. environment_2: Deploys to second environment
  5. trigger_environment_3: Approval gate for third environment
  6. environment_3: Deploys to third environment
  7. trigger_environment_4: Approval gate for fourth environment
  8. environment_4: Deploys to fourth environment
  9. trigger_environment_5: Approval gate for fifth environment
  10. environment_5: Deploys to fifth environment

Note: Number of jobs varies based on deployment-path input. Trigger jobs act as gateways when GitHub environment protection rules are enabled.

Best Practices

1. Deployment Path Management

Structure your metadata to support clear promotion paths:

{
  "promotion_path": ["dev", "qa", "crt", "prd"],
  "environments": {
    "dev": { "auto_deploy": true },
    "qa": { "auto_deploy": false },
    "crt": { "auto_deploy": false },
    "prd": { "auto_deploy": false }
  }
}

Always ensure deployment-path is a subset or exact match of promotion-path:

  • ✅ Valid: dev-qa-prd (subset of promotion path)
  • ✅ Valid: dev-qa (subset of promotion path)
  • ❌ Invalid: qa-dev (wrong order)
  • ❌ Invalid: dev-prd (skips qa)

2. Environment Protection Rules

Configure GitHub environment protection:

  • Add required reviewers for each environment
  • Set deployment branch restrictions
  • Enable wait timer for production deployments
  • Configure environment secrets per environment

3. Docker Image Management

Follow semantic versioning for container images:

# Tag format: major.minor.patch
docker-tag: "1.2.3"

# Or use build numbers for non-production
docker-tag: "1.0.${{ github.run_number }}"

# Or use git tags
docker-tag: ${{ github.ref_name }}

4. Terraform State Management

Use consistent remote state configuration:

with:
  remote-state-folder-name: "ecs-state"
  remote-state-file-name: "terraform.tfstate"

Ensure backend configuration in Terraform code:

terraform {
  backend "azurerm" {
    # Configuration managed by workflow
  }
}

5. Secret Management

Use PRM for sensitive configuration:

with:
  terraform-prm-secrets: "db_password,api_key,redis_password"
  prm-base-url: "https://prm.optum.com"

In Terraform code:

variable "db_password" {
  description = "Database password from PRM"
  type        = string
  sensitive   = true
}

variable "api_key" {
  description = "API key from PRM"
  type        = string
  sensitive   = true
}

6. E2E Testing Strategy

Enable E2E tests strategically:

# Run E2E only in higher environments
e2e-tests-enabled-stages: "crt,prd"

# Or test all environments
e2e-tests-enabled-stages: "dev,qa,crt,prd"

7. CI/CD Integration

Chain CI and CD workflows:

jobs:
  build:
    uses: uhg-pipelines/ci-workflows/.github/workflows/node-npm-ci.yml@v2
    # ... outputs docker-tag

  promote:
    needs: build
    uses: dojo360/pipelines-workflows/.github/workflows/container-cd-promotion.yml@beta
    with:
      docker-tag: ${{ needs.build.outputs.uploaded-docker-tag }}

8. Approval Workflow

Set up approval notifications:

  • Configure GitHub notifications for approvers
  • Document approval criteria
  • Set SLAs for approval response times
  • Use Teams/Slack webhooks for notifications

Troubleshooting

Issue 1: Deployment Path Validation Failure

Symptom: Workflow fails at parse_deployment_path job with path mismatch error.

Possible Causes:

  • deployment-path doesn't match promotion-path in metadata
  • Environments specified in wrong order
  • Missing environment in promotion path

Solution:

  1. Verify metadata promotion-path configuration
  2. Ensure deployment-path matches or is subset of promotion-path
  3. Check environment names match exactly (case-sensitive)
  4. Review deployment path order matches promotion sequence

Issue 2: Docker Image Pull Failures

Symptom: Container deployment fails to pull Docker image.

Possible Causes:

  • Image doesn't exist in specified repository
  • Incorrect image tag
  • Authentication issues with container registry
  • Missing container-registry configuration for cloud-specific registries

Solution:

  1. Verify image exists: docker pull <repository>/<image>:<tag>
  2. Check docker-repository, docker-image, and docker-tag values
  3. Ensure proper authentication configured
  4. For AWS, set container-registry: "ecr"
  5. For Azure, set container-registry: "acr"

Issue 3: Environment Approval Timeout

Symptom: Workflow waits indefinitely at trigger job.

Possible Causes:

  • No required reviewers configured for environment
  • Reviewers not receiving notifications
  • Environment protection rules not properly configured

Solution:

  1. Configure required reviewers in GitHub environment settings
  2. Verify email notification settings for approvers
  3. Check environment protection rules are enabled
  4. Test notification delivery

Issue 4: PRM Secret Not Found

Symptom: Terraform deployment fails with missing variable error.

Possible Causes:

  • Secret not configured in PRM
  • Incorrect secret name in terraform-prm-secrets
  • Insufficient permissions to access PRM secrets
  • PRM base URL incorrect

Solution:

  1. Verify secret exists in PRM at https://prm.optum.com
  2. Check secret names match exactly (case-sensitive)
  3. Ensure team has access to required secrets
  4. Validate prm-base-url configuration

Issue 5: E2E Tests Not Executing

Symptom: E2E test job doesn't run after deployment.

Possible Causes:

  • e2e-tests-enabled-stages doesn't include current environment
  • e2e-workflow-file doesn't exist
  • E2E workflow file has errors

Solution:

  1. Verify environment name is in e2e-tests-enabled-stages
  2. Check E2E workflow file exists at .github/workflows/<e2e-workflow-file>
  3. Test E2E workflow independently
  4. Review E2E workflow logs for errors

Issue 6: Terraform State Lock

Symptom: Terraform operations fail with state lock error.

Possible Causes:

  • Previous deployment didn't complete
  • State lock not released properly
  • Concurrent deployments to same environment

Solution:

  1. Wait for previous deployment to complete
  2. Use Terraform Operations workflow to unlock state
  3. Ensure only one promotion runs at a time
  4. Review state lock configuration

Issue 7: Container Registry Authentication

Symptom: Workflow fails to authenticate with container registry.

Possible Causes:

  • OIDC not configured properly
  • JFrog project key missing or incorrect
  • Registry credentials expired

Solution:

  1. Verify OIDC configuration for cloud-type
  2. Check jfrog-project-key value
  3. Ensure registry accessible from runner
  4. Review authentication logs

Related Workflows

Additional Resources

Support

For assistance with this workflow:

Related Assets