SEC11-BP06: Deploy software programmatically

Deploy software programmatically where possible. This approach reduces the likelihood of deployment errors, provides consistency across environments, and enables automated security controls and compliance checks.

Implementation guidance

Programmatic software deployment is essential for maintaining security, consistency, and reliability across your application lifecycle. By automating deployments, you eliminate human error, ensure reproducible processes, and enable comprehensive security controls at every stage.

Key steps for implementing this best practice:

  1. Establish Infrastructure as Code (IaC):
    • Use AWS CloudFormation, CDK, or Terraform for infrastructure provisioning
    • Version control all infrastructure definitions
    • Implement infrastructure testing and validation
    • Create reusable infrastructure components and modules
    • Establish infrastructure change management processes
  2. Implement automated CI/CD pipelines:
    • Set up continuous integration with automated testing
    • Configure continuous deployment with approval gates
    • Implement blue-green or canary deployment strategies
    • Create rollback mechanisms and disaster recovery procedures
    • Integrate security scanning and compliance checks
  3. Configure deployment automation:
    • Use AWS CodeDeploy, ECS, or Kubernetes for application deployment
    • Implement automated configuration management
    • Set up environment-specific deployment configurations
    • Create deployment monitoring and health checks
    • Establish deployment artifact management
  4. Integrate security controls:
    • Implement security scanning in deployment pipelines
    • Configure automated compliance validation
    • Set up runtime security monitoring
    • Create security approval workflows
    • Establish security incident response automation
  5. Implement deployment governance:
    • Create deployment policies and approval processes
    • Set up audit logging and compliance reporting
    • Implement change management and approval workflows
    • Establish deployment metrics and monitoring
    • Create disaster recovery and business continuity procedures
  6. Enable observability and monitoring:
    • Implement comprehensive logging and monitoring
    • Set up alerting and notification systems
    • Create deployment dashboards and reporting
    • Establish performance and security metrics
    • Configure automated incident response

Implementation examples

Example 1: AWS CDK deployment pipeline with security controls

Example 2: Terraform-based infrastructure deployment with security validation

yaml

.gitlab-ci.yml

stages:

  • security-scan
  • build
  • security-validation
  • deploy-staging
  • integration-tests
  • deploy-production

variables: DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: “/certs” AWS_DEFAULT_REGION: us-west-2 ECR_REPOSITORY: $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/secure-app

Security scanning stage

sast-scan: stage: security-scan image: python:3.9 before_script: - pip install bandit safety semgrep script: - echo “Running Static Application Security Testing…” - bandit -r src/ -f json -o bandit-report.json || true - safety check –json –output safety-report.json || true - semgrep –config=auto –json –output=semgrep-report.json src/ || true - python scripts/process-sast-results.py artifacts: reports: sast: sast-report.json paths: - “*-report.json” expire_in: 1 week only: - main - develop - merge_requests

dependency-scan: stage: security-scan image: node:18 script: - echo “Running dependency vulnerability scanning…” - npm audit –audit-level=moderate –json > npm-audit-report.json || true - npx audit-ci –config audit-ci.json artifacts: paths: - npm-audit-report.json expire_in: 1 week only: - main - develop - merge_requests

container-scan: stage: security-scan image: docker:latest services: - docker:dind before_script: - apk add –no-cache curl - curl -sSfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s – -b /usr/local/bin script: - echo “Building container image for scanning…” - docker build -t temp-scan-image:latest . - echo “Running container security scan…” - trivy image –format json –output trivy-report.json temp-scan-image:latest - trivy image –exit-code 1 –severity HIGH,CRITICAL temp-scan-image:latest artifacts: paths: - trivy-report.json expire_in: 1 week only: - main - develop - merge_requests

iac-scan: stage: security-scan image: python:3.9 before_script: - pip install checkov - curl -s https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash script: - echo “Running Infrastructure as Code security scanning…” - checkov -d infrastructure/ –framework terraform –output json –output-file checkov-report.json || true - tfsec infrastructure/ –format json –out tfsec-report.json || true - python scripts/process-iac-results.py artifacts: paths: - “*-report.json” expire_in: 1 week only: - main - develop - merge_requests

Build stage

build-application: stage: build image: docker:latest services: - docker:dind before_script: - apk add –no-cache aws-cli - aws ecr get-login-password –region $AWS_DEFAULT_REGION | docker login –username AWS –password-stdin $ECR_REPOSITORY script: - echo “Building application container…” - export IMAGE_TAG=${CI_COMMIT_SHORT_SHA} - docker build -t $ECR_REPOSITORY:$IMAGE_TAG . - docker tag $ECR_REPOSITORY:$IMAGE_TAG $ECR_REPOSITORY:latest - echo “Pushing container to ECR…” - docker push $ECR_REPOSITORY:$IMAGE_TAG - docker push $ECR_REPOSITORY:latest - echo “IMAGE_TAG=$IMAGE_TAG” > build.env artifacts: reports: dotenv: build.env dependencies: - sast-scan - dependency-scan - container-scan - iac-scan only: - main - develop

Security validation stage

security-validation: stage: security-validation image: python:3.9 before_script: - pip install boto3 requests script: - echo “Running comprehensive security validation…” - python scripts/security-gate-validation.py - echo “Security validation completed successfully” dependencies: - build-application only: - main - develop

Staging deployment

deploy-staging: stage: deploy-staging image: name: hashicorp/terraform:1.5 entrypoint: [””] before_script: - apk add –no-cache aws-cli - terraform –version - aws –version script: - echo “Deploying to staging environment…” - cd infrastructure/ - terraform init - terraform workspace select staging || terraform workspace new staging - terraform plan -var=”environment=staging” -var=”image_tag=$IMAGE_TAG” -out=staging.tfplan - terraform apply -auto-approve staging.tfplan - echo “Staging deployment completed” environment: name: staging url: https://staging.example.com dependencies: - security-validation only: - main - develop

Integration tests

integration-tests: stage: integration-tests image: python:3.9 before_script: - pip install pytest requests script: - echo “Running integration tests against staging…” - python -m pytest tests/integration/ –staging-url=https://staging.example.com - echo “Integration tests completed successfully” dependencies: - deploy-staging only: - main - develop

Production deployment (manual approval required)

deploy-production: stage: deploy-production image: name: hashicorp/terraform:1.5 entrypoint: [””] before_script: - apk add –no-cache aws-cli script: - echo “Deploying to production environment…” - cd infrastructure/ - terraform init - terraform workspace select production || terraform workspace new production - terraform plan -var=”environment=production” -var=”image_tag=$IMAGE_TAG” -out=production.tfplan - terraform apply -auto-approve production.tfplan - echo “Production deployment completed” - python ../scripts/post-deployment-validation.py –environment=production environment: name: production url: https://app.example.com when: manual dependencies: - integration-tests only: - main <!– CODE SNIPPET HIDDEN - Original content below:


CODE SNIPPET WILL BE PROVIDED SOON –>

python

scripts/security-gate-validation.py

import json import sys import os from typing import Dict, List, Any

class SecurityGateValidator: def init(self): self.security_thresholds = { ‘critical_vulnerabilities’: 0, ‘high_vulnerabilities’: 5, ‘medium_vulnerabilities’: 20, ‘sast_critical_issues’: 0, ‘sast_high_issues’: 3, ‘dependency_critical’: 0, ‘dependency_high’: 5, ‘iac_critical_issues’: 0, ‘iac_high_issues’: 2 }

    self.validation_results = {
        'passed': True,
        'violations': [],
        'summary': {}
    }

def validate_security_reports(self):
    """Validate all security scan reports against thresholds"""
    
    print("Starting security gate validation...")
    
    # Validate SAST results
    self.validate_sast_results()
    
    # Validate dependency scan results
    self.validate_dependency_results()
    
    # Validate container scan results
    self.validate_container_results()
    
    # Validate IaC scan results
    self.validate_iac_results()
    
    # Generate final report
    self.generate_validation_report()
    
    # Exit with appropriate code
    if not self.validation_results['passed']:
        print("❌ Security gate validation FAILED")
        sys.exit(1)
    else:
        print("✅ Security gate validation PASSED")
        sys.exit(0)

def validate_sast_results(self):
    """Validate Static Application Security Testing results"""
    
    print("Validating SAST results...")
    
    # Process Bandit results
    if os.path.exists('bandit-report.json'):
        with open('bandit-report.json', 'r') as f:
            bandit_data = json.load(f)
            
        high_issues = len([r for r in bandit_data.get('results', []) 
                         if r.get('issue_severity') == 'HIGH'])
        critical_issues = len([r for r in bandit_data.get('results', []) 
                             if r.get('issue_severity') == 'CRITICAL'])
        
        if critical_issues > self.security_thresholds['sast_critical_issues']:
            self.validation_results['violations'].append({
                'type': 'SAST_CRITICAL_ISSUES',
                'count': critical_issues,
                'threshold': self.security_thresholds['sast_critical_issues'],
                'tool': 'Bandit'
            })
            self.validation_results['passed'] = False
        
        if high_issues > self.security_thresholds['sast_high_issues']:
            self.validation_results['violations'].append({
                'type': 'SAST_HIGH_ISSUES',
                'count': high_issues,
                'threshold': self.security_thresholds['sast_high_issues'],
                'tool': 'Bandit'
            })
            self.validation_results['passed'] = False
    
    # Process Semgrep results
    if os.path.exists('semgrep-report.json'):
        with open('semgrep-report.json', 'r') as f:
            semgrep_data = json.load(f)
        
        critical_findings = len([r for r in semgrep_data.get('results', []) 
                               if r.get('extra', {}).get('severity') == 'ERROR'])
        
        if critical_findings > self.security_thresholds['sast_critical_issues']:
            self.validation_results['violations'].append({
                'type': 'SAST_CRITICAL_ISSUES',
                'count': critical_findings,
                'threshold': self.security_thresholds['sast_critical_issues'],
                'tool': 'Semgrep'
            })
            self.validation_results['passed'] = False

def validate_dependency_results(self):
    """Validate dependency vulnerability scan results"""
    
    print("Validating dependency scan results...")
    
    if os.path.exists('npm-audit-report.json'):
        with open('npm-audit-report.json', 'r') as f:
            audit_data = json.load(f)
        
        vulnerabilities = audit_data.get('vulnerabilities', {})
        
        critical_count = 0
        high_count = 0
        
        for vuln_name, vuln_data in vulnerabilities.items():
            severity = vuln_data.get('severity', '').lower()
            if severity == 'critical':
                critical_count += 1
            elif severity == 'high':
                high_count += 1
        
        if critical_count > self.security_thresholds['dependency_critical']:
            self.validation_results['violations'].append({
                'type': 'DEPENDENCY_CRITICAL_VULNERABILITIES',
                'count': critical_count,
                'threshold': self.security_thresholds['dependency_critical'],
                'tool': 'npm audit'
            })
            self.validation_results['passed'] = False
        
        if high_count > self.security_thresholds['dependency_high']:
            self.validation_results['violations'].append({
                'type': 'DEPENDENCY_HIGH_VULNERABILITIES',
                'count': high_count,
                'threshold': self.security_thresholds['dependency_high'],
                'tool': 'npm audit'
            })
            self.validation_results['passed'] = False

def validate_container_results(self):
    """Validate container security scan results"""
    
    print("Validating container scan results...")
    
    if os.path.exists('trivy-report.json'):
        with open('trivy-report.json', 'r') as f:
            trivy_data = json.load(f)
        
        critical_count = 0
        high_count = 0
        
        for result in trivy_data.get('Results', []):
            for vuln in result.get('Vulnerabilities', []):
                severity = vuln.get('Severity', '').upper()
                if severity == 'CRITICAL':
                    critical_count += 1
                elif severity == 'HIGH':
                    high_count += 1
        
        if critical_count > self.security_thresholds['critical_vulnerabilities']:
            self.validation_results['violations'].append({
                'type': 'CONTAINER_CRITICAL_VULNERABILITIES',
                'count': critical_count,
                'threshold': self.security_thresholds['critical_vulnerabilities'],
                'tool': 'Trivy'
            })
            self.validation_results['passed'] = False
        
        if high_count > self.security_thresholds['high_vulnerabilities']:
            self.validation_results['violations'].append({
                'type': 'CONTAINER_HIGH_VULNERABILITIES',
                'count': high_count,
                'threshold': self.security_thresholds['high_vulnerabilities'],
                'tool': 'Trivy'
            })
            self.validation_results['passed'] = False

def validate_iac_results(self):
    """Validate Infrastructure as Code scan results"""
    
    print("Validating IaC scan results...")
    
    # Process Checkov results
    if os.path.exists('checkov-report.json'):
        with open('checkov-report.json', 'r') as f:
            checkov_data = json.load(f)
        
        failed_checks = checkov_data.get('results', {}).get('failed_checks', [])
        
        critical_issues = len([c for c in failed_checks 
                             if c.get('severity') == 'CRITICAL'])
        high_issues = len([c for c in failed_checks 
                         if c.get('severity') == 'HIGH'])
        
        if critical_issues > self.security_thresholds['iac_critical_issues']:
            self.validation_results['violations'].append({
                'type': 'IAC_CRITICAL_ISSUES',
                'count': critical_issues,
                'threshold': self.security_thresholds['iac_critical_issues'],
                'tool': 'Checkov'
            })
            self.validation_results['passed'] = False
        
        if high_issues > self.security_thresholds['iac_high_issues']:
            self.validation_results['violations'].append({
                'type': 'IAC_HIGH_ISSUES',
                'count': high_issues,
                'threshold': self.security_thresholds['iac_high_issues'],
                'tool': 'Checkov'
            })
            self.validation_results['passed'] = False
    
    # Process tfsec results
    if os.path.exists('tfsec-report.json'):
        with open('tfsec-report.json', 'r') as f:
            tfsec_data = json.load(f)
        
        critical_issues = len([r for r in tfsec_data.get('results', []) 
                             if r.get('severity') == 'CRITICAL'])
        high_issues = len([r for r in tfsec_data.get('results', []) 
                         if r.get('severity') == 'HIGH'])
        
        if critical_issues > self.security_thresholds['iac_critical_issues']:
            self.validation_results['violations'].append({
                'type': 'IAC_CRITICAL_ISSUES',
                'count': critical_issues,
                'threshold': self.security_thresholds['iac_critical_issues'],
                'tool': 'tfsec'
            })
            self.validation_results['passed'] = False

def generate_validation_report(self):
    """Generate comprehensive validation report"""
    
    print("\n" + "="*60)
    print("SECURITY GATE VALIDATION REPORT")
    print("="*60)
    
    if self.validation_results['passed']:
        print("✅ Overall Status: PASSED")
    else:
        print("❌ Overall Status: FAILED")
    
    print(f"\nViolations Found: {len(self.validation_results['violations'])}")
    
    if self.validation_results['violations']:
        print("\nViolation Details:")
        for violation in self.validation_results['violations']:
            print(f"  - {violation['type']}: {violation['count']} "
                  f"(threshold: {violation['threshold']}) - {violation['tool']}")
    
    print("\nSecurity Thresholds:")
    for threshold, value in self.security_thresholds.items():
        print(f"  - {threshold}: {value}")
    
    print("="*60)
    
    # Save detailed report
    with open('security-gate-report.json', 'w') as f:
        json.dump(self.validation_results, f, indent=2)

if name == “main”: validator = SecurityGateValidator() validator.validate_security_reports() <!– CODE SNIPPET HIDDEN - Original content below:

### Example 4: Kubernetes deployment with GitOps and security controls

CODE SNIPPET WILL BE PROVIDED SOON –>

yaml

k8s-deployment/base/deployment.yaml

apiVersion: apps/v1 kind: Deployment metadata: name: secure-app labels: app: secure-app version: v1 spec: replicas: 3 selector: matchLabels: app: secure-app template: metadata: labels: app: secure-app version: v1 annotations: # Security annotations container.apparmor.security.beta.kubernetes.io/app: runtime/default seccomp.security.alpha.kubernetes.io/pod: runtime/default spec: serviceAccountName: secure-app-sa automountServiceAccountToken: false

  # Security context for the pod
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault
  
  containers:
  - name: app
    image: 123456789012.dkr.ecr.us-west-2.amazonaws.com/secure-app:latest
    imagePullPolicy: Always
    
    ports:
    - containerPort: 8080
      name: http
      protocol: TCP
    
    # Security context for the container
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      runAsNonRoot: true
      runAsUser: 1000
      runAsGroup: 1000
      capabilities:
        drop:
        - ALL
        add:
        - NET_BIND_SERVICE
    
    # Resource limits
    resources:
      requests:
        memory: "256Mi"
        cpu: "250m"
      limits:
        memory: "512Mi"
        cpu: "500m"
    
    # Health checks
    livenessProbe:
      httpGet:
        path: /health
        port: http
      initialDelaySeconds: 30
      periodSeconds: 10
      timeoutSeconds: 5
      failureThreshold: 3
    
    readinessProbe:
      httpGet:
        path: /ready
        port: http
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 3
      failureThreshold: 3
    
    # Environment variables
    env:
    - name: PORT
      value: "8080"
    - name: ENVIRONMENT
      value: "production"
    
    # Secrets from AWS Secrets Manager
    envFrom:
    - secretRef:
        name: app-secrets
    
    # Volume mounts for writable directories
    volumeMounts:
    - name: tmp-volume
      mountPath: /tmp
    - name: cache-volume
      mountPath: /app/cache
  
  volumes:
  - name: tmp-volume
    emptyDir: {}
  - name: cache-volume
    emptyDir: {}
  
  # Node selection and affinity
  nodeSelector:
    kubernetes.io/os: linux
  
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - secure-app
          topologyKey: kubernetes.io/hostname

apiVersion: v1 kind: ServiceAccount metadata: name: secure-app-sa annotations: eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/SecureAppRole automountServiceAccountToken: false


apiVersion: v1 kind: Service metadata: name: secure-app-service labels: app: secure-app spec: type: ClusterIP ports:

  • port: 80 targetPort: http protocol: TCP name: http selector: app: secure-app

apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: secure-app-network-policy spec: podSelector: matchLabels: app: secure-app policyTypes:

  • Ingress
  • Egress ingress:
  • from:
    • namespaceSelector: matchLabels: name: ingress-nginx ports:
    • protocol: TCP port: 8080 egress:
  • to: [] ports:
    • protocol: TCP port: 443 # HTTPS
    • protocol: TCP port: 53 # DNS
    • protocol: UDP port: 53 # DNS

apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: secure-app-pdb spec: minAvailable: 2 selector: matchLabels: app: secure-app <!– CODE SNIPPET HIDDEN - Original content below:


CODE SNIPPET WILL BE PROVIDED SOON –>

yaml

.github/workflows/gitops-deployment.yml

name: GitOps Deployment with Security

on: push: branches: [ main ] pull_request: branches: [ main ]

env: AWS_REGION: us-west-2 EKS_CLUSTER_NAME: secure-cluster ECR_REPOSITORY: secure-app

jobs: security-scan: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3

- name: Run Kubernetes security scan
  uses: azure/k8s-lint@v1
  with:
    manifests: |
      k8s-deployment/base/deployment.yaml

- name: Run Polaris security scan
  run: |
    curl -L https://github.com/FairwindsOps/polaris/releases/latest/download/polaris_linux_amd64.tar.gz | tar xz
    ./polaris audit --audit-path k8s-deployment/ --format json > polaris-report.json

- name: Run Falco rules validation
  run: |
    docker run --rm -v $(pwd):/workspace falcosecurity/falco:latest \
      falco --validate /workspace/security/falco-rules.yaml

- name: Upload security reports
  uses: actions/upload-artifact@v3
  with:
    name: k8s-security-reports
    path: "*-report.json"

build-and-push: needs: security-scan runs-on: ubuntu-latest if: github.ref == ‘refs/heads/main’

steps:
- name: Checkout code
  uses: actions/checkout@v3

- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v2
  with:
    aws-access-key-id: $
    aws-secret-access-key: $
    aws-region: $

- name: Login to Amazon ECR
  id: login-ecr
  uses: aws-actions/amazon-ecr-login@v1

- name: Build, tag, and push image to Amazon ECR
  env:
    ECR_REGISTRY: $
    IMAGE_TAG: $
  run: |
    docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
    docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest
    docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
    docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
    echo "IMAGE_URI=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

outputs:
  image-uri: $

update-manifests: needs: build-and-push runs-on: ubuntu-latest

steps:
- name: Checkout GitOps repository
  uses: actions/checkout@v3
  with:
    repository: company/gitops-manifests
    token: $
    path: gitops-repo

- name: Update Kubernetes manifests
  env:
    IMAGE_URI: $
  run: |
    cd gitops-repo
    
    # Update image in Kustomization
    sed -i "s|newTag:.*|newTag: ${GITHUB_SHA}|g" overlays/production/kustomization.yaml
    
    # Commit and push changes
    git config user.name "GitHub Actions"
    git config user.email "actions@github.com"
    git add .
    git commit -m "Update secure-app image to ${GITHUB_SHA}"
    git push

deploy-staging: needs: [security-scan, build-and-push] runs-on: ubuntu-latest environment: staging

steps:
- name: Checkout code
  uses: actions/checkout@v3

- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v2
  with:
    aws-access-key-id: $
    aws-secret-access-key: $
    aws-region: $

- name: Update kubeconfig
  run: |
    aws eks update-kubeconfig --region $ --name $-staging

- name: Deploy to staging
  env:
    IMAGE_URI: $
  run: |
    # Update image in deployment
    sed -i "s|image:.*|image: ${IMAGE_URI}|g" k8s-deployment/overlays/staging/deployment.yaml
    
    # Apply manifests
    kubectl apply -k k8s-deployment/overlays/staging/
    
    # Wait for rollout to complete
    kubectl rollout status deployment/secure-app -n staging --timeout=300s
    
    # Run post-deployment security checks
    kubectl run security-check --rm -i --restart=Never --image=aquasec/kube-bench:latest -- --version 1.20

- name: Run integration tests
  run: |
    # Get service endpoint
    STAGING_URL=$(kubectl get service secure-app-service -n staging -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
    
    # Run integration tests
    python tests/integration_tests.py --url http://${STAGING_URL}

security-validation: needs: deploy-staging runs-on: ubuntu-latest

steps:
- name: Run runtime security validation
  run: |
    # Run Falco for runtime security monitoring
    kubectl apply -f https://raw.githubusercontent.com/falcosecurity/falco/master/examples/k8s_audit_config/falco-k8s-audit-rules.yaml
    
    # Check for security policy violations
    kubectl get events --field-selector type=Warning -n staging
    
    # Validate network policies
    kubectl describe networkpolicy secure-app-network-policy -n staging

deploy-production: needs: [deploy-staging, security-validation] runs-on: ubuntu-latest environment: production if: github.ref == ‘refs/heads/main’

steps:
- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v2
  with:
    aws-access-key-id: $
    aws-secret-access-key: $
    aws-region: $

- name: Update kubeconfig
  run: |
    aws eks update-kubeconfig --region $ --name $-production

- name: Deploy to production with canary
  env:
    IMAGE_URI: $
  run: |
    # Deploy canary version (10% traffic)
    kubectl patch deployment secure-app -p '{"spec":{"template":{"spec":{"containers":[{"name":"app","image":"'${IMAGE_URI}'"}]}}}}'
    kubectl patch deployment secure-app -p '{"spec":{"replicas":1}}'
    
    # Wait for canary deployment
    kubectl rollout status deployment/secure-app --timeout=300s
    
    # Monitor canary metrics for 5 minutes
    sleep 300
    
    # Check error rates and performance metrics
    python scripts/validate-canary-metrics.py
    
    # If validation passes, complete the rollout
    kubectl patch deployment secure-app -p '{"spec":{"replicas":3}}'
    kubectl rollout status deployment/secure-app --timeout=300s

- name: Post-deployment validation
  run: |
    # Validate deployment health
    kubectl get pods -l app=secure-app -n production
    
    # Check security compliance
    kubectl run compliance-check --rm -i --restart=Never --image=aquasec/kube-bench:latest
    
    # Validate network policies are active
    kubectl describe networkpolicy secure-app-network-policy -n production
    
    # Send deployment notification
    curl -X POST $ \
      -H 'Content-type: application/json' \
      --data '{"text":"✅ Production deployment completed successfully for commit '$'"}' ```

AWS services to consider

AWS CodePipeline

Fully managed continuous delivery service that helps you automate your release pipelines for fast and reliable application and infrastructure updates.

AWS CodeBuild

Fully managed continuous integration service that compiles source code, runs tests, and produces software packages that are ready to deploy.

AWS CodeDeploy

Deployment service that automates application deployments to Amazon EC2 instances, on-premises instances, serverless Lambda functions, or Amazon ECS services.

AWS CloudFormation

Infrastructure as code service that helps you model and set up your Amazon Web Services resources using templates.

AWS CDK

Open-source software development framework to define cloud infrastructure in code and provision it through AWS CloudFormation.

Amazon ECS

Fully managed container orchestration service that makes it easy to deploy, manage, and scale containerized applications.

Amazon EKS

Managed Kubernetes service that makes it easy to run Kubernetes on AWS without needing to install and operate your own Kubernetes clusters.

AWS Systems Manager

Management service that helps you automatically collect software inventory, apply OS patches, create system images, and configure Windows and Linux operating systems.

Benefits of deploying software programmatically

  • Consistency and reliability: Eliminates human error and ensures reproducible deployments
  • Enhanced security: Enables automated security controls and compliance validation
  • Faster time to market: Accelerates deployment cycles through automation
  • Improved auditability: Provides complete audit trails and deployment history
  • Risk reduction: Enables automated rollback and disaster recovery procedures
  • Scalability: Supports deployment across multiple environments and regions
  • Cost optimization: Reduces manual effort and operational overhead
  • Compliance support: Ensures consistent application of security and governance policies