github-workflows-dojo360-container-promotion
Multi-environment container deployment promotion through prescribed deployment paths with automated approval gates and E2E testing
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-pathagainstpromotion-pathin 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-tagfrom 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-typeisawsOptum- Reference: AWS OIDC
- Azure: Configure OIDC when
cloud-typeisazureOptum- Reference: Azure OIDC
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:
| Input | Type | Description |
|---|---|---|
aide-id | string | AIDE ID to fetch metadata |
cloud-type | string | Cloud type for metadata (see Supported Cloud Types) |
deployment-path | string | Deployment path (e.g., dev, qa, cert, prd) |
domain | string | Domain to fetch metadata |
team-name | string | Team name to fetch metadata |
Optional Inputs
| Input | Type | Default | Description |
|---|---|---|---|
container-registry | string | "" | Cloud-specific container registry (ECR for AWS, ACR for Azure) |
deployment-controller-type | string | ECS | Triggers CODE_DEPLOY if set to CODE_DEPLOY |
docker-image | string | "" | Fully qualified path of image to pull for deployment |
docker-repository | string | docker.repo1.uhc.com | Docker registry from which to pull container image |
docker-tag | string | "" | Tag associated with image to pull from repository |
e2e-tests-enabled-stages | string | '' | Controls E2E test job execution after deployment in each stage |
e2e-workflow-file | string | N/A | Name of workflow/action file to execute dynamically |
e2e-workflow-inputs | string | N/A | JSON data containing additional input overrides for workflow |
jfrog-project-key | string | "" | Key for identifying JFrog project in SaaS Artifactory |
prm-base-url | string | https://prm.optum.com | Base URL of PRM Instance to read secrets from |
ref | string | HEAD | Branch, tag, or SHA to check out |
remote-state-file-name | string | "" | Filename of remote state file |
remote-state-folder-name | string | "" | Name of remote state folder |
runner-labels | string | "" | Comma-separated Runner labels for workflow |
terraform-directory | string | tf | Directory path relative to repository hosting Terraform code |
terraform-prm-secrets | string | N/A | Comma-separated list of PRM secrets to map to tfvars |
terraform-provider-network-mirror | string | https://repo1.uhc.com/artifactory/api/terraform/terraform-virtual/providers/ | Proxy URL as mirror for Terraform provider |
terraform-vars-files | string | N/A | Comma-separated list of tfvars files |
terraform-vars-values | string | N/A | JSON string of tfvars |
terraform-version | string | 1.9.2 | Version of Terraform to use |
terraform-volcan-secrets | string | "" | Comma-separated list of Volcan secrets to map to tfvars |
volcan-base-url | string | volcan-cloud.optum.com | Base 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:
parse_deployment_path: Validates deployment path against promotion pathenvironment_1: Deploys to first environmenttrigger_environment_2: Approval gate for second environmentenvironment_2: Deploys to second environmenttrigger_environment_3: Approval gate for third environmentenvironment_3: Deploys to third environmenttrigger_environment_4: Approval gate for fourth environmentenvironment_4: Deploys to fourth environmenttrigger_environment_5: Approval gate for fifth environmentenvironment_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-pathdoesn't matchpromotion-pathin metadata- Environments specified in wrong order
- Missing environment in promotion path
Solution:
- Verify metadata
promotion-pathconfiguration - Ensure
deployment-pathmatches or is subset ofpromotion-path - Check environment names match exactly (case-sensitive)
- 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-registryconfiguration for cloud-specific registries
Solution:
- Verify image exists:
docker pull <repository>/<image>:<tag> - Check docker-repository, docker-image, and docker-tag values
- Ensure proper authentication configured
- For AWS, set
container-registry: "ecr" - 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:
- Configure required reviewers in GitHub environment settings
- Verify email notification settings for approvers
- Check environment protection rules are enabled
- 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:
- Verify secret exists in PRM at https://prm.optum.com
- Check secret names match exactly (case-sensitive)
- Ensure team has access to required secrets
- Validate
prm-base-urlconfiguration
Issue 5: E2E Tests Not Executing
Symptom: E2E test job doesn't run after deployment.
Possible Causes:
e2e-tests-enabled-stagesdoesn't include current environmente2e-workflow-filedoesn't exist- E2E workflow file has errors
Solution:
- Verify environment name is in
e2e-tests-enabled-stages - Check E2E workflow file exists at
.github/workflows/<e2e-workflow-file> - Test E2E workflow independently
- 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:
- Wait for previous deployment to complete
- Use Terraform Operations workflow to unlock state
- Ensure only one promotion runs at a time
- 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:
- Verify OIDC configuration for cloud-type
- Check
jfrog-project-keyvalue - Ensure registry accessible from runner
- Review authentication logs
Related Workflows
- Container CD: Single-environment container deployment
- Infrastructure Promotion: Multi-environment infrastructure promotion
- Serverless CD Promotion: Multi-environment serverless promotion
- Generate Promotion: Dynamically generate promotion workflows
Additional Resources
- Dojo360 Workflows Documentation
- Container Sample Apps
- Working with CloudBricks
- Deployment and Promotion Guide
- SaaS Artifactory Guide
Support
For assistance with this workflow:
- Dojo360 Support: https://dojo360.optum.com/support
- GitHub Issues: Report issues in your repository
- Team Channels: Contact your CloudBricks° team in Microsoft Teams
- Sample Workflows: https://github.com/dojo360/pipelines-container-sample-apps
Related Assets
github-workflows-dojo360-azure-infrastructure
Deploy Azure infrastructure using Terraform with PCAM vaulted access and native Azure authentication through Dojo360 Azure Infrastructure workflow
Owner: pcorazao
github-workflows-dojo360-container-cd
Deploy containerized applications to AWS ECS/Azure ACS using Dojo360 Container CD workflow with blue-green and rolling update strategies
Owner: pcorazao
github-workflows-dojo360-database
Automate database schema updates using Liquibase via the Dojo360 database workflow (with rollback and validation patterns)
Owner: pcorazao
github-workflows-dojo360-database-promotion
Promote Liquibase database changes across environments (dev→qa→cert→prod) with deployment-path validation and approval gates
Owner: pcorazao
github-workflows-dojo360-dockerfile-ci
Build and scan container images from a Dockerfile using Optum golden images and the recommended UHG reusable workflow
Owner: pcorazao
github-workflows-dojo360-dotnet-ci
Build, test, and scan .NET apps using the recommended UHG reusable CI workflow, with optional publish/pack and container builds
Owner: pcorazao

