Skip to content
SEC11

SEC11-BP06 - Deploy software programmatically

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

View code
import * as cdk from 'aws-cdk-lib';
import * as codepipeline from 'aws-cdk-lib/aws-codepipeline';
import * as codepipeline_actions from 'aws-cdk-lib/aws-codepipeline-actions';
import * as codebuild from 'aws-cdk-lib/aws-codebuild';
import * as codecommit from 'aws-cdk-lib/aws-codecommit';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';

export class SecureDeploymentPipelineStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Source repository
    const repository = new codecommit.Repository(this, 'ApplicationRepository', {
      repositoryName: 'secure-application',
      description: 'Application source code with security controls'
    });

    // Artifact bucket for pipeline artifacts
    const artifactBucket = new s3.Bucket(this, 'PipelineArtifacts', {
      bucketName: `pipeline-artifacts-${this.account}-${this.region}`,
      encryption: s3.BucketEncryption.S3_MANAGED,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      versioned: true,
      lifecycleRules: [{
        id: 'DeleteOldArtifacts',
        expiration: cdk.Duration.days(30)
      }]
    });

    // SNS topic for deployment notifications
    const deploymentTopic = new sns.Topic(this, 'DeploymentNotifications', {
      displayName: 'Deployment Pipeline Notifications'
    });

    // Security scanning project
    const securityScanProject = new codebuild.Project(this, 'SecurityScanProject', {
      projectName: 'security-scan-project',
      description: 'Security scanning and compliance validation',
      source: codebuild.Source.codeCommit({
        repository: repository
      }),
      environment: {
        buildImage: codebuild.LinuxBuildImage.STANDARD_5_0,
        computeType: codebuild.ComputeType.MEDIUM,
        privileged: true
      },
      buildSpec: codebuild.BuildSpec.fromObject({
        version: '0.2',
        phases: {
          install: {
            'runtime-versions': {
              nodejs: '18',
              python: '3.9'
            },
            commands: [
              'echo Installing security scanning tools...',
              'npm install -g @aws-cdk/cdk-nag',
              'pip install bandit safety checkov',
              'curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin',
              'curl -sSfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin'
            ]
          },
          pre_build: {
            commands: [
              'echo Starting security scans...',
              'echo Logging in to Amazon ECR...',
              'aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com'
            ]
          },
          build: {
            commands: [
              // Static Application Security Testing (SAST)
              'echo "Running SAST scans..."',
              'bandit -r src/ -f json -o bandit-report.json || true',
              'safety check --json --output safety-report.json || true',
              
              // Infrastructure as Code scanning
              'echo "Running IaC security scans..."',
              'checkov -d infrastructure/ --framework cloudformation --output json --output-file checkov-report.json || true',
              
              // CDK Nag scanning
              'echo "Running CDK Nag..."',
              'cd infrastructure && npm install && npm run build',
              'npx cdk-nag --app "npx ts-node app.ts" --output cdk-nag-report.json || true',
              
              // Container image scanning
              'echo "Building and scanning container images..."',
              'docker build -t app:latest .',
              'trivy image --format json --output trivy-report.json app:latest || true',
              'grype app:latest -o json --file grype-report.json || true',
              
              // Dependency scanning
              'echo "Running dependency scans..."',
              'npm audit --json > npm-audit-report.json || true',
              'pip-audit --format=json --output=pip-audit-report.json || true'
            ]
          },
          post_build: {
            commands: [
              'echo "Processing security scan results..."',
              'python scripts/process-security-results.py',
              'echo "Security scanning completed"'
            ]
          }
        },
        artifacts: {
          files: [
            '**/*',
            'security-reports/**/*'
          ]
        }
      }),
      role: new iam.Role(this, 'SecurityScanRole', {
        assumedBy: new iam.ServicePrincipal('codebuild.amazonaws.com'),
        managedPolicies: [
          iam.ManagedPolicy.fromAwsManagedPolicyName('AWSCodeBuildDeveloperAccess'),
          iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonEC2ContainerRegistryPowerUser')
        ],
        inlinePolicies: {
          SecurityScanPolicy: new iam.PolicyDocument({
            statements: [
              new iam.PolicyStatement({
                effect: iam.Effect.ALLOW,
                actions: [
                  'inspector2:*',
                  'securityhub:*',
                  's3:GetObject',
                  's3:PutObject'
                ],
                resources: ['*']
              })
            ]
          })
        }
      })
    });

    // Build project
    const buildProject = new codebuild.Project(this, 'BuildProject', {
      projectName: 'application-build-project',
      description: 'Build and package application',
      source: codebuild.Source.codeCommit({
        repository: repository
      }),
      environment: {
        buildImage: codebuild.LinuxBuildImage.STANDARD_5_0,
        computeType: codebuild.ComputeType.MEDIUM,
        privileged: true
      },
      buildSpec: codebuild.BuildSpec.fromObject({
        version: '0.2',
        phases: {
          install: {
            'runtime-versions': {
              nodejs: '18'
            }
          },
          pre_build: {
            commands: [
              'echo Logging in to Amazon ECR...',
              'aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com',
              'REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/secure-app',
              'COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)',
              'IMAGE_TAG=${COMMIT_HASH:=latest}'
            ]
          },
          build: {
            commands: [
              'echo Build started on `date`',
              'echo Building the Docker image...',
              'docker build -t $REPOSITORY_URI:latest .',
              'docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG'
            ]
          },
          post_build: {
            commands: [
              'echo Build completed on `date`',
              'echo Pushing the Docker images...',
              'docker push $REPOSITORY_URI:latest',
              'docker push $REPOSITORY_URI:$IMAGE_TAG',
              'echo Writing image definitions file...',
              'printf \'[{"name":"secure-app","imageUri":"%s"}]\' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json'
            ]
          }
        },
        artifacts: {
          files: [
            'imagedefinitions.json',
            'infrastructure/**/*'
          ]
        }
      })
    });

    // Deployment approval Lambda
    const deploymentApprovalFunction = new lambda.Function(this, 'DeploymentApprovalFunction', {
      runtime: lambda.Runtime.PYTHON_3_9,
      handler: 'index.lambda_handler',
      code: lambda.Code.fromInline(`
import json
import boto3
import os

def lambda_handler(event, context):
    codepipeline = boto3.client('codepipeline')
    
    # Extract pipeline information
    job_id = event['CodePipeline.job']['id']
    input_artifacts = event['CodePipeline.job']['data']['inputArtifacts']
    
    try:
        # Process security scan results
        security_results = process_security_results(input_artifacts)
        
        # Determine if deployment should be approved
        approval_decision = evaluate_security_results(security_results)
        
        if approval_decision['approved']:
            codepipeline.put_job_success_result(jobId=job_id)
            send_notification(f"Deployment approved: {approval_decision['reason']}")
        else:
            codepipeline.put_job_failure_result(
                jobId=job_id,
                failureDetails={'message': approval_decision['reason'], 'type': 'JobFailed'}
            )
            send_notification(f"Deployment rejected: {approval_decision['reason']}")
            
    except Exception as e:
        codepipeline.put_job_failure_result(
            jobId=job_id,
            failureDetails={'message': str(e), 'type': 'JobFailed'}
        )
        send_notification(f"Deployment approval failed: {str(e)}")
    
    return {'statusCode': 200}

def process_security_results(input_artifacts):
    # Process security scan results from artifacts
    # Implementation would parse JSON reports from security scanning
    return {
        'critical_vulnerabilities': 0,
        'high_vulnerabilities': 2,
        'medium_vulnerabilities': 5,
        'compliance_score': 85
    }

def evaluate_security_results(results):
    # Define security thresholds
    if results['critical_vulnerabilities'] > 0:
        return {'approved': False, 'reason': 'Critical vulnerabilities detected'}
    
    if results['high_vulnerabilities'] > 5:
        return {'approved': False, 'reason': 'Too many high-severity vulnerabilities'}
    
    if results['compliance_score'] < 80:
        return {'approved': False, 'reason': 'Compliance score below threshold'}
    
    return {'approved': True, 'reason': 'Security validation passed'}

def send_notification(message):
    sns = boto3.client('sns')
    sns.publish(
        TopicArn=os.environ['NOTIFICATION_TOPIC'],
        Subject='Deployment Security Validation',
        Message=message
    )
      `),
      environment: {
        'NOTIFICATION_TOPIC': deploymentTopic.topicArn
      }
    });

    // Grant permissions to the Lambda function
    deploymentTopic.grantPublish(deploymentApprovalFunction);

    // Pipeline artifacts
    const sourceOutput = new codepipeline.Artifact('SourceOutput');
    const securityScanOutput = new codepipeline.Artifact('SecurityScanOutput');
    const buildOutput = new codepipeline.Artifact('BuildOutput');

    // Create the pipeline
    const pipeline = new codepipeline.Pipeline(this, 'SecureDeploymentPipeline', {
      pipelineName: 'secure-deployment-pipeline',
      artifactBucket: artifactBucket,
      stages: [
        {
          stageName: 'Source',
          actions: [
            new codepipeline_actions.CodeCommitSourceAction({
              actionName: 'Source',
              repository: repository,
              branch: 'main',
              output: sourceOutput,
              trigger: codepipeline_actions.CodeCommitTrigger.EVENTS
            })
          ]
        },
        {
          stageName: 'SecurityScan',
          actions: [
            new codepipeline_actions.CodeBuildAction({
              actionName: 'SecurityScan',
              project: securityScanProject,
              input: sourceOutput,
              outputs: [securityScanOutput]
            })
          ]
        },
        {
          stageName: 'SecurityApproval',
          actions: [
            new codepipeline_actions.LambdaInvokeAction({
              actionName: 'SecurityApproval',
              lambda: deploymentApprovalFunction,
              inputs: [securityScanOutput]
            })
          ]
        },
        {
          stageName: 'Build',
          actions: [
            new codepipeline_actions.CodeBuildAction({
              actionName: 'Build',
              project: buildProject,
              input: sourceOutput,
              outputs: [buildOutput]
            })
          ]
        },
        {
          stageName: 'DeployToStaging',
          actions: [
            new codepipeline_actions.EcsDeployAction({
              actionName: 'DeployToStaging',
              service: stagingService, // ECS service reference
              input: buildOutput
            })
          ]
        },
        {
          stageName: 'ProductionApproval',
          actions: [
            new codepipeline_actions.ManualApprovalAction({
              actionName: 'ProductionApproval',
              notificationTopic: deploymentTopic,
              additionalInformation: 'Please review staging deployment and approve for production'
            })
          ]
        },
        {
          stageName: 'DeployToProduction',
          actions: [
            new codepipeline_actions.EcsDeployAction({
              actionName: 'DeployToProduction',
              service: productionService, // ECS service reference
              input: buildOutput
            })
          ]
        }
      ]
    });

    // Output pipeline information
    new cdk.CfnOutput(this, 'PipelineName', {
      value: pipeline.pipelineName,
      description: 'Name of the deployment pipeline'
    });

    new cdk.CfnOutput(this, 'RepositoryCloneUrl', {
      value: repository.repositoryCloneUrlHttp,
      description: 'Repository clone URL'
    });
  }
}

Example 2: Terraform-based infrastructure deployment with security validation

View code
# main.tf - Secure infrastructure deployment
terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  
  backend "s3" {
    bucket         = "terraform-state-bucket"
    key            = "secure-app/terraform.tfstate"
    region         = "us-west-2"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

provider "aws" {
  region = var.aws_region
  
  default_tags {
    tags = {
      Environment   = var.environment
      Project       = var.project_name
      ManagedBy     = "Terraform"
      SecurityScan  = "Required"
    }
  }
}

# Security scanning and validation
resource "null_resource" "security_validation" {
  triggers = {
    always_run = timestamp()
  }
  
  provisioner "local-exec" {
    command = <<-EOT
      echo "Running security validation..."
      
      # Run Checkov for IaC security scanning
      checkov -d . --framework terraform --output json --output-file checkov-results.json
      
      # Run tfsec for Terraform security scanning
      tfsec . --format json --out tfsec-results.json
      
      # Run Terrascan for policy validation
      terrascan scan -t terraform -f . -o json --output terrascan-results.json
      
      # Process results and fail if critical issues found
      python3 scripts/process-terraform-security-results.py
    EOT
  }
}

# VPC with security controls
module "vpc" {
  source = "./modules/secure-vpc"
  
  name_prefix        = "${var.project_name}-${var.environment}"
  cidr_block         = var.vpc_cidr
  availability_zones = var.availability_zones
  
  enable_flow_logs    = true
  enable_dns_hostnames = true
  enable_dns_support   = true
  
  # Security configurations
  enable_network_acls = true
  enable_nat_gateway  = true
  
  tags = local.common_tags
  
  depends_on = [null_resource.security_validation]
}

# ECS cluster with security configurations
resource "aws_ecs_cluster" "main" {
  name = "${var.project_name}-${var.environment}"
  
  configuration {
    execute_command_configuration {
      kms_key_id = aws_kms_key.ecs_key.arn
      logging    = "OVERRIDE"
      
      log_configuration {
        cloud_watch_encryption_enabled = true
        cloud_watch_log_group_name     = aws_cloudwatch_log_group.ecs_exec.name
      }
    }
  }
  
  setting {
    name  = "containerInsights"
    value = "enabled"
  }
  
  tags = local.common_tags
}

# KMS key for encryption
resource "aws_kms_key" "ecs_key" {
  description             = "KMS key for ECS encryption"
  deletion_window_in_days = 7
  enable_key_rotation     = true
  
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "Enable IAM User Permissions"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
        }
        Action   = "kms:*"
        Resource = "*"
      }
    ]
  })
  
  tags = local.common_tags
}

resource "aws_kms_alias" "ecs_key" {
  name          = "alias/${var.project_name}-${var.environment}-ecs"
  target_key_id = aws_kms_key.ecs_key.key_id
}

# CloudWatch log group for ECS Exec
resource "aws_cloudwatch_log_group" "ecs_exec" {
  name              = "/aws/ecs/${var.project_name}-${var.environment}/exec"
  retention_in_days = 30
  kms_key_id        = aws_kms_key.ecs_key.arn
  
  tags = local.common_tags
}

# ECS task definition with security configurations
resource "aws_ecs_task_definition" "app" {
  family                   = "${var.project_name}-${var.environment}"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = var.task_cpu
  memory                   = var.task_memory
  execution_role_arn       = aws_iam_role.ecs_execution_role.arn
  task_role_arn           = aws_iam_role.ecs_task_role.arn
  
  container_definitions = jsonencode([
    {
      name  = "app"
      image = "${var.ecr_repository_url}:${var.image_tag}"
      
      essential = true
      
      portMappings = [
        {
          containerPort = var.container_port
          protocol      = "tcp"
        }
      ]
      
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          "awslogs-group"         = aws_cloudwatch_log_group.app.name
          "awslogs-region"        = var.aws_region
          "awslogs-stream-prefix" = "ecs"
        }
      }
      
      environment = [
        {
          name  = "ENVIRONMENT"
          value = var.environment
        }
      ]
      
      secrets = [
        {
          name      = "DATABASE_PASSWORD"
          valueFrom = aws_ssm_parameter.db_password.arn
        }
      ]
      
      # Security configurations
      readonlyRootFilesystem = true
      user                   = "1000:1000"
      
      linuxParameters = {
        capabilities = {
          drop = ["ALL"]
        }
      }
      
      healthCheck = {
        command     = ["CMD-SHELL", "curl -f http://localhost:${var.container_port}/health || exit 1"]
        interval    = 30
        timeout     = 5
        retries     = 3
        startPeriod = 60
      }
    }
  ])
  
  tags = local.common_tags
}

# ECS service with security configurations
resource "aws_ecs_service" "app" {
  name            = "${var.project_name}-${var.environment}"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.app.arn
  desired_count   = var.desired_count
  launch_type     = "FARGATE"
  
  platform_version = "1.4.0"
  
  network_configuration {
    subnets          = module.vpc.private_subnet_ids
    security_groups  = [aws_security_group.ecs_tasks.id]
    assign_public_ip = false
  }
  
  load_balancer {
    target_group_arn = aws_lb_target_group.app.arn
    container_name   = "app"
    container_port   = var.container_port
  }
  
  deployment_configuration {
    maximum_percent         = 200
    minimum_healthy_percent = 100
    
    deployment_circuit_breaker {
      enable   = true
      rollback = true
    }
  }
  
  enable_execute_command = true
  
  depends_on = [
    aws_lb_listener.app,
    aws_iam_role_policy_attachment.ecs_execution_role_policy
  ]
  
  tags = local.common_tags
}

# Security group for ECS tasks
resource "aws_security_group" "ecs_tasks" {
  name_prefix = "${var.project_name}-${var.environment}-ecs-tasks"
  vpc_id      = module.vpc.vpc_id
  
  ingress {
    protocol        = "tcp"
    from_port       = var.container_port
    to_port         = var.container_port
    security_groups = [aws_security_group.alb.id]
    description     = "Allow inbound from ALB"
  }
  
  egress {
    protocol    = "-1"
    from_port   = 0
    to_port     = 0
    cidr_blocks = ["0.0.0.0/0"]
    description = "Allow all outbound traffic"
  }
  
  tags = merge(local.common_tags, {
    Name = "${var.project_name}-${var.environment}-ecs-tasks"
  })
}

# Application Load Balancer with security configurations
resource "aws_lb" "app" {
  name               = "${var.project_name}-${var.environment}"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = module.vpc.public_subnet_ids
  
  enable_deletion_protection = var.environment == "production" ? true : false
  
  drop_invalid_header_fields = true
  
  access_logs {
    bucket  = aws_s3_bucket.alb_logs.id
    prefix  = "alb-logs"
    enabled = true
  }
  
  tags = local.common_tags
}

# WAF for application protection
resource "aws_wafv2_web_acl" "app" {
  name  = "${var.project_name}-${var.environment}"
  scope = "REGIONAL"
  
  default_action {
    allow {}
  }
  
  # AWS Managed Rules
  rule {
    name     = "AWSManagedRulesCommonRuleSet"
    priority = 1
    
    override_action {
      none {}
    }
    
    statement {
      managed_rule_group_statement {
        name        = "AWSManagedRulesCommonRuleSet"
        vendor_name = "AWS"
      }
    }
    
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "CommonRuleSetMetric"
      sampled_requests_enabled   = true
    }
  }
  
  rule {
    name     = "AWSManagedRulesKnownBadInputsRuleSet"
    priority = 2
    
    override_action {
      none {}
    }
    
    statement {
      managed_rule_group_statement {
        name        = "AWSManagedRulesKnownBadInputsRuleSet"
        vendor_name = "AWS"
      }
    }
    
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "KnownBadInputsRuleSetMetric"
      sampled_requests_enabled   = true
    }
  }
  
  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "${var.project_name}-${var.environment}-waf"
    sampled_requests_enabled   = true
  }
  
  tags = local.common_tags
}

# Associate WAF with ALB
resource "aws_wafv2_web_acl_association" "app" {
  resource_arn = aws_lb.app.arn
  web_acl_arn  = aws_wafv2_web_acl.app.arn
}

# CloudWatch alarms for monitoring
resource "aws_cloudwatch_metric_alarm" "high_cpu" {
  alarm_name          = "${var.project_name}-${var.environment}-high-cpu"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "2"
  metric_name         = "CPUUtilization"
  namespace           = "AWS/ECS"
  period              = "300"
  statistic           = "Average"
  threshold           = "80"
  alarm_description   = "This metric monitors ecs cpu utilization"
  alarm_actions       = [aws_sns_topic.alerts.arn]
  
  dimensions = {
    ServiceName = aws_ecs_service.app.name
    ClusterName = aws_ecs_cluster.main.name
  }
  
  tags = local.common_tags
}

# SNS topic for alerts
resource "aws_sns_topic" "alerts" {
  name              = "${var.project_name}-${var.environment}-alerts"
  kms_master_key_id = aws_kms_key.ecs_key.arn
  
  tags = local.common_tags
}

# Local values
locals {
  common_tags = {
    Environment = var.environment
    Project     = var.project_name
    ManagedBy   = "Terraform"
  }
}

# Data sources
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
View code
### Example 3: GitLab CI/CD pipeline with comprehensive security controls

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

View code

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 }

View code
    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()

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

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

View code
  # 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

View code

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

View code
- 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’

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

- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v2
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: ${{ env.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: ${{ steps.login-ecr.outputs.registry }}
    IMAGE_TAG: ${{ github.sha }}
  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: ${{ steps.build-and-push.outputs.IMAGE_URI }}

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

View code
steps:
- name: Checkout GitOps repository
  uses: actions/checkout@v3
  with:
    repository: company/gitops-manifests
    token: ${{ secrets.GITOPS_TOKEN }}
    path: gitops-repo

- name: Update Kubernetes manifests
  env:
    IMAGE_URI: ${{ needs.build-and-push.outputs.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

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

- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v2
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: ${{ env.AWS_REGION }}

- name: Update kubeconfig
  run: |
    aws eks update-kubeconfig --region ${{ env.AWS_REGION }} --name ${{ env.EKS_CLUSTER_NAME }}-staging

- name: Deploy to staging
  env:
    IMAGE_URI: ${{ needs.build-and-push.outputs.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

View code
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’

View code
steps:
- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v2
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: ${{ env.AWS_REGION }}

- name: Update kubeconfig
  run: |
    aws eks update-kubeconfig --region ${{ env.AWS_REGION }} --name ${{ env.EKS_CLUSTER_NAME }}-production

- name: Deploy to production with canary
  env:
    IMAGE_URI: ${{ needs.build-and-push.outputs.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 ${{ secrets.SLACK_WEBHOOK_URL }} \
      -H 'Content-type: application/json' \
      --data '{"text":"✅ Production deployment completed successfully for commit '${{ github.sha }}'"}'
View code

## AWS services to consider

<div class="aws-service">
  <div class="aws-service-content">
    <h4>AWS CodePipeline</h4>
    <p>Fully managed continuous delivery service that helps you automate your release pipelines for fast and reliable application and infrastructure updates.</p>
  </div>
</div>

<div class="aws-service">
  <div class="aws-service-content">
    <h4>AWS CodeBuild</h4>
    <p>Fully managed continuous integration service that compiles source code, runs tests, and produces software packages that are ready to deploy.</p>
  </div>
</div>

<div class="aws-service">
  <div class="aws-service-content">
    <h4>AWS CodeDeploy</h4>
    <p>Deployment service that automates application deployments to Amazon EC2 instances, on-premises instances, serverless Lambda functions, or Amazon ECS services.</p>
  </div>
</div>

<div class="aws-service">
  <div class="aws-service-content">
    <h4>AWS CloudFormation</h4>
    <p>Infrastructure as code service that helps you model and set up your Amazon Web Services resources using templates.</p>
  </div>
</div>

<div class="aws-service">
  <div class="aws-service-content">
    <h4>AWS CDK</h4>
    <p>Open-source software development framework to define cloud infrastructure in code and provision it through AWS CloudFormation.</p>
  </div>
</div>

<div class="aws-service">
  <div class="aws-service-content">
    <h4>Amazon ECS</h4>
    <p>Fully managed container orchestration service that makes it easy to deploy, manage, and scale containerized applications.</p>
  </div>
</div>

<div class="aws-service">
  <div class="aws-service-content">
    <h4>Amazon EKS</h4>
    <p>Managed Kubernetes service that makes it easy to run Kubernetes on AWS without needing to install and operate your own Kubernetes clusters.</p>
  </div>
</div>

<div class="aws-service">
  <div class="aws-service-content">
    <h4>AWS Systems Manager</h4>
    <p>Management service that helps you automatically collect software inventory, apply OS patches, create system images, and configure Windows and Linux operating systems.</p>
  </div>
</div>

## 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

## Related resources

<div class="related-resources">
  <h2>Related Resources</h2>
  <ul>
    <li><a href="https://docs.aws.amazon.com/wellarchitected/latest/framework/sec_appsec_deploy_software_programmatically.html">AWS Well-Architected Framework - Deploy software programmatically</a></li>
    <li><a href="https://docs.aws.amazon.com/codepipeline/latest/userguide/welcome.html">AWS CodePipeline User Guide</a></li>
    <li><a href="https://docs.aws.amazon.com/codebuild/latest/userguide/welcome.html">AWS CodeBuild User Guide</a></li>
    <li><a href="https://docs.aws.amazon.com/codedeploy/latest/userguide/welcome.html">AWS CodeDeploy User Guide</a></li>
    <li><a href="https://docs.aws.amazon.com/cdk/v2/guide/home.html">AWS CDK Developer Guide</a></li>
    <li><a href="https://aws.amazon.com/blogs/devops/implementing-gitops-with-aws-codepipeline/">Implementing GitOps with AWS CodePipeline</a></li>
    <li><a href="https://aws.amazon.com/blogs/containers/securing-amazon-eks-with-aws-app-mesh-and-aws-x-ray/">Securing Amazon EKS with AWS App Mesh and AWS X-Ray</a></li>
  </ul>
</div>