home / skills / giuseppe-trisciuoglio / developer-kit / aws-cloudformation-task-ecs-deploy-gh

This skill guides deploying ECS tasks and services via GitHub Actions with CloudFormation, enabling secure OIDC authentication and multi-environment pipelines.

npx playbooks add skill giuseppe-trisciuoglio/developer-kit --skill aws-cloudformation-task-ecs-deploy-gh

Review the files below or copy the command above to add this skill to your agents.

Files (3)
SKILL.md
25.9 KB
---
name: aws-cloudformation-task-ecs-deploy-gh
description: Provides patterns to deploy ECS tasks and services with GitHub Actions CI/CD. Use when building Docker images, pushing to ECR, updating ECS task definitions, deploying ECS services, integrating with CloudFormation stacks, configuring AWS OIDC authentication for GitHub Actions, and implementing production-ready container deployment pipelines. Supports ECS deployments with proper security (OIDC or IAM keys), multi-environment support, blue/green deployments, ECR private repositories with image scanning, and CloudFormation infrastructure updates.
category: aws
tags: [aws, cloudformation, github-actions, ecs, ecr, deployment, ci-cd, oidc, containers, docker, cd]
version: 1.0.0
allowed-tools: Read, Write, Bash
---

# AWS CloudFormation Task ECS Deploy with GitHub Actions

Comprehensive skill for deploying ECS containers using GitHub Actions CI/CD pipelines with CloudFormation infrastructure management.

## Overview

Deploy containerized applications to Amazon ECS using GitHub Actions workflows. This skill covers the complete deployment pipeline: authentication with AWS (OIDC recommended), building Docker images, pushing to Amazon ECR, updating task definitions, and deploying ECS services. Integrate with CloudFormation for infrastructure-as-code management and implement production-grade deployment strategies.

## When to Use

**Use this skill when:**
- Deploying Docker containers to Amazon ECS
- Setting up GitHub Actions CI/CD pipelines for AWS
- Configuring AWS authentication for GitHub Actions (OIDC or IAM keys)
- Building and pushing Docker images to Amazon ECR
- Updating ECS task definitions dynamically
- Implementing blue/green or rolling deployments
- Managing CloudFormation stacks from CI/CD
- Setting up multi-environment deployments (dev/staging/prod)
- Configuring private ECR repositories with image scanning
- Automating container deployment with proper security practices

**Trigger phrases:**
- "Deploy to ECS with GitHub Actions"
- "Set up CI/CD for ECS containers"
- "Configure GitHub Actions for AWS deployment"
- "Build and push Docker image to ECR"
- "Update ECS task definition from CI/CD"
- "Implement blue/green deployment for ECS"
- "Deploy CloudFormation stack from GitHub Actions"

## Instructions

Follow these steps to set up ECS deployment with GitHub Actions:

1. **Configure AWS Authentication**: Set up OIDC provider for GitHub Actions
2. **Create IAM Roles**: Define roles for deployment actions
3. **Set Up ECR Repository**: Create repository with image scanning
4. **Create ECS Cluster**: Define cluster infrastructure
5. **Configure Task Definition**: Set up task and container definitions
6. **Set Up ECS Service**: Configure service with deployment strategy
7. **Create GitHub Workflow**: Define CI/CD pipeline steps
8. **Configure Secrets**: Store credentials securely in GitHub Secrets

For complete examples, see the [EXAMPLES.md](references/examples.md) file.

## Examples

The following examples demonstrate common deployment patterns:

### Example 1: GitHub Actions Workflow

```yaml
name: Deploy to ECS
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v4
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
          aws-region: us-east-1
      - name: Login to Amazon ECR
        uses: aws-actions/amazon-ecr-login@v2
      - name: Build, tag, and push image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: my-app
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
```

### Example 2: Update Task Definition

```yaml
- name: Update ECS Task Definition
  id: task-def
  uses: aws-actions/amazon-ecs-render-task-definition@v1
  with:
    task-definition: task-definition.json
    container-name: my-app
    image: ${{ steps.login-ecr.outputs.registry }}/my-app:${{ github.sha }}

- name: Deploy to ECS
  uses: aws-actions/amazon-ecs-deploy-task-definition@v1
  with:
    task-definition: ${{ steps.task-def.outputs.task-definition }}
    service: my-service
    cluster: my-cluster
```

### Example 3: OIDC Provider Setup

```yaml
OidcProvider:
  Type: AWS::IAM::OIDCProvider
  Properties:
    Url: https://token.actions.githubusercontent.com
    ClientIdList:
      - sts.amazonaws.com
    ThumbprintList:
      - 6938fd4d98bab03faabd97ca34b7b5dbd06c4ee5

GitHubActionsRole:
  Type: AWS::IAM::Role
  Properties:
    AssumeRolePolicyDocument:
      Version: "2012-10-17"
      Statement:
        - Effect: Allow
          Principal:
            Federated: !Ref OidcProvider
          Action: sts:AssumeRoleWithWebIdentity
          Condition:
            StringEquals:
              token.actions.githubusercontent.com:aud: sts.amazonaws.com
              token.actions.githubusercontent.com:sub: repo:my-org/my-repo:ref:refs/heads/main
```

For complete production-ready examples, see [EXAMPLES.md](references/examples.md).

## Quick Start

### Basic ECS Deployment Workflow

```yaml
name: Deploy to ECS
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-ecs-role
          aws-region: us-east-1

      - name: Login to ECR
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build and push image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: my-app
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

      - name: Update task definition
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        id: render-task
        with:
          task-definition: task-definition.json
          container-name: my-app
          image: ${{ steps.login-ecr.outputs.registry }}/my-app:${{ github.sha }}

      - name: Deploy to ECS
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.render-task.outputs.task-definition }}
          service: my-service
          cluster: my-cluster
          wait-for-service-stability: true
```

## Authentication Methods

### OIDC Authentication (Recommended)

OpenID Connect (OIDC) provides secure, passwordless authentication between GitHub Actions and AWS.

**Prerequisites:**

1. Create IAM role with trust policy for GitHub:

```yaml
Type: AWS::IAM::Role
Properties:
  RoleName: github-actions-ecs-role
  AssumeRolePolicyDocument:
    Version: '2012-10-17'
    Statement:
      - Effect: Allow
        Principal:
          Federated: !Sub 'arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com'
        Action: sts:AssumeRoleWithWebIdentity
        Condition:
          StringEquals:
            token.actions.githubusercontent.com:aud: sts.amazonaws.com
          StringLike:
            token.actions.githubusercontent.com:sub: repo:my-org/my-repo:*
  ManagedPolicyArns:
    - arn:aws:iam::aws:policy/AmazonECS_FullAccess
    - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess
```

2. Configure GitHub Actions workflow:

```yaml
- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::123456789012:role/github-actions-ecs-role
    aws-region: us-east-1
```

### IAM Key Authentication (Legacy)

```yaml
- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v4
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: us-east-1
```

Store credentials in GitHub repository secrets (Settings → Secrets and variables → Actions).

## Build and Push to ECR

### ECR Repository CloudFormation Template

```yaml
ECRRepository:
  Type: AWS::ECR::Repository
  Properties:
    RepositoryName: my-app
    ImageScanningConfiguration:
      ScanOnPush: true
    ImageTagMutability: IMMUTABLE
    LifecyclePolicy:
      LifecyclePolicyText: |
        {
          "rules": [
            {
              "rulePriority": 1,
              "description": "Keep last 30 images",
              "selection": {
                "tagStatus": "tagged",
                "tagPrefixList": ["v"],
                "countType": "imageCountMoreThan",
                "countNumber": 30
              },
              "action": {
                "type": "expire"
              }
            }
          ]
        }
```

### Build and Push Step

```yaml
- name: Login to ECR
  id: login-ecr
  uses: aws-actions/amazon-ecr-login@v2

- name: Set up Docker Buildx
  uses: docker/setup-buildx-action@v3

- name: Build and push
  uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: |
      ${{ steps.login-ecr.outputs.registry }}/my-app:${{ github.sha }}
      ${{ steps.login-ecr.outputs.registry }}/my-app:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max
    build-args: |
      BUILD_DATE=${{ github.event.head_commit.timestamp }}
      VERSION=${{ github.sha }}
```

## Task Definition Management

### Basic Task Definition

**task-definition.json:**

```json
{
  "family": "my-app-task",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::123456789012:role/ecsTaskRole",
  "containerDefinitions": [
    {
      "name": "my-app",
      "image": "PLACEHOLDER_IMAGE",
      "portMappings": [
        {
          "containerPort": 8080,
          "protocol": "tcp"
        }
      ],
      "environment": [
        {
          "name": "ENVIRONMENT",
          "value": "production"
        }
      ],
      "secrets": [
        {
          "name": "DB_PASSWORD",
          "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-app/db-password"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/my-app",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs",
          "awslogs-create-group": "true"
        }
      }
    }
  ]
}
```

### Dynamic Task Definition Update

```yaml
- name: Render task definition
  uses: aws-actions/amazon-ecs-render-task-definition@v1
  id: render-task
  with:
    task-definition: task-definition.json
    container-name: my-app
    image: ${{ steps.login-ecr.outputs.registry }}/my-app:${{ github.sha }}

- name: Deploy task definition
  uses: aws-actions/amazon-ecs-deploy-task-definition@v1
  with:
    task-definition: ${{ steps.render-task.outputs.task-definition }}
    service: my-service
    cluster: my-cluster
    wait-for-service-stability: true
    deploy_timeout: 30 minutes
```

## ECS Deployment Strategies

### Rolling Deployment (Default)

```yaml
- name: Deploy to ECS (Rolling)
  uses: aws-actions/amazon-ecs-deploy-task-definition@v1
  with:
    task-definition: ${{ steps.render-task.outputs.task-definition }}
    service: my-service
    cluster: my-cluster
    wait-for-service-stability: true
```

**CloudFormation Service Configuration:**

```yaml
ECSService:
  Type: AWS::ECS::Service
  Properties:
    ServiceName: my-service
    Cluster: !Ref ECSCluster
    TaskDefinition: !Ref TaskDefinition
    DeploymentConfiguration:
      MaximumPercent: 200
      MinimumHealthyPercent: 100
      DeploymentCircuitBreaker:
        Enable: true
        Rollback: true
    HealthCheckGracePeriodSeconds: 60
    EnableECSManagedTags: true
    PropagateTags: SERVICE
```

### Blue/Green Deployment with CodeDeploy

```yaml
- name: Deploy to ECS (Blue/Green)
  uses: aws-actions/amazon-ecs-deploy-task-definition@v1
  with:
    task-definition: ${{ steps.render-task.outputs.task-definition }}
    service: my-service
    cluster: my-cluster
    codedeploy-appspec: appspec.yaml
    codedeploy-application: my-app
    codedeploy-deployment-group: my-deployment-group
    wait-for-service-stability: true
```

**appspec.yaml:**

```yaml
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: <TASK_DEFINITION>
        LoadBalancerInfo:
          ContainerName: my-app
          ContainerPort: 8080
        PlatformVersion: "1.4.0"
```

**CloudFormation CodeDeploy Configuration:**

```yaml
CodeDeployApplication:
  Type: AWS::CodeDeploy::Application
  Properties:
    ApplicationName: my-app
    ComputePlatform: ECS

CodeDeployDeploymentGroup:
  Type: AWS::CodeDeploy::DeploymentGroup
  Properties:
    ApplicationName: !Ref CodeDeployApplication
    DeploymentGroupName: my-deployment-group
    ServiceRoleArn: !Ref CodeDeployServiceRole
    DeploymentConfigName: CodeDeployDefault.ECSAllAtOnce
    DeploymentStyle:
      DeploymentType: BLUE_GREEN
      DeploymentOption: WITH_TRAFFIC_CONTROL
    AutoRollbackConfiguration:
      Enabled: true
      Events:
        - DEPLOYMENT_FAILURE
        - DEPLOYMENT_STOP_ON_ALARM
        - DEPLOYMENT_STOP_ON_REQUEST
    AlarmConfiguration:
      Alarms:
        - !Ref CPUPercentageAlarm
        - !Ref MemoryPercentageAlarm
    BlueGreenDeploymentConfiguration:
      TerminateBlueInstancesOnDeploymentSuccess:
        Action: TERMINATE
        WaitTimeInMinutes: 5
      DeploymentReadyOption:
        ActionOnTimeout: CONTINUE_DEPLOYMENT
        WaitTimeInMinutes: 0
    LoadBalancerInfo:
      TargetGroupPairInfoList:
        - TargetGroups:
            - Ref: BlueTargetGroup
            - Ref: GreenTargetGroup
          ProdTrafficRoute:
            ListenerArns:
              - !Ref ProductionListener
```

## CloudFormation Integration

### Update Stack from GitHub Actions

```yaml
- name: Deploy CloudFormation stack
  run: |
    aws cloudformation deploy \
      --template-file infrastructure/ecs-stack.yaml \
      --stack-name my-app-ecs \
      --capabilities CAPABILITY_NAMED_IAM \
      --parameter-overrides \
        Environment=production \
        DesiredCount=2 \
        CPU=256 \
        Memory=512 \
        ImageUrl=${{ steps.login-ecr.outputs.registry }}/my-app:${{ github.sha }}
```

### CloudFormation Stack with ECS Resources

**ecs-stack.yaml:**

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: ECS Fargate Service with CloudFormation

Parameters:
  Environment:
    Type: String
    AllowedValues: [dev, staging, prod]
  DesiredCount:
    Type: Number
    Default: 2
  CPU:
    Type: String
    Default: '256'
  Memory:
    Type: String
    Default: '512'
  ImageUrl:
    Type: String

Resources:
  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Sub '${Environment}-cluster'
      ClusterSettings:
        - Name: containerInsights
          Value: enabled

  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Sub '${Environment}-task'
      Cpu: !Ref CPU
      Memory: !Ref Memory
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn
      TaskRoleArn: !GetAtt TaskRole.Arn
      ContainerDefinitions:
        - Name: my-app
          Image: !Ref ImageUrl
          PortMappings:
            - ContainerPort: 8080
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref LogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: ecs

  ECSService:
    Type: AWS::ECS::Service
    DependsOn: LoadBalancerListener
    Properties:
      ServiceName: !Sub '${Environment}-service'
      Cluster: !Ref ECSCluster
      TaskDefinition: !Ref TaskDefinition
      DesiredCount: !Ref DesiredCount
      LaunchType: FARGATE
      NetworkConfiguration:
        AwsvpcConfiguration:
          Subnets:
            - !Ref PrivateSubnetA
            - !Ref PrivateSubnetB
          SecurityGroups:
            - !Ref ContainerSecurityGroup
          AssignPublicIp: DISABLED
      LoadBalancers:
        - ContainerName: my-app
          ContainerPort: 8080
          TargetGroupArn: !Ref TargetGroup
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 100
        DeploymentCircuitBreaker:
          Enable: true
          Rollback: true
      HealthCheckGracePeriodSeconds: 60

Outputs:
  ServiceURL:
    Description: Service URL
    Value: !Sub 'http://${LoadBalancer.DNSName}'
```

### Stack Outputs for Cross-Stack References

```yaml
Outputs:
  ClusterName:
    Description: ECS Cluster Name
    Value: !Ref ECSCluster
    Export:
      Name: !Sub '${AWS::StackName}-ClusterName'

  ServiceName:
    Description: ECS Service Name
    Value: !Ref ECSService
    Export:
      Name: !Sub '${AWS::StackName}-ServiceName'

  TaskDefinitionArn:
    Description: Task Definition ARN
    Value: !Ref TaskDefinition
    Export:
      Name: !Sub '${AWS::StackName}-TaskDefinitionArn'
```

## Best Practices

### Security

1. **Use OIDC authentication** instead of long-lived IAM keys
2. **Implement least privilege IAM roles** with specific permissions
3. **Enable ECR image scanning** on push
4. **Use AWS Secrets Manager** for sensitive data
5. **Encrypt ECR repositories** with KMS
6. **VPC endpoints** for ECR and ECS without internet gateway
7. **Security groups** restrict access to minimum required

### IAM Role Permissions

```yaml
ECSDeployRole:
  Type: AWS::IAM::Role
  Properties:
    AssumeRolePolicyDocument:
      Version: '2012-10-17'
      Statement:
        - Effect: Allow
          Principal:
            Federated: !Sub 'arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com'
          Action: sts:AssumeRoleWithWebIdentity
          Condition:
            StringEquals:
              token.actions.githubusercontent.com:aud: sts.amazonaws.com
            StringLike:
              token.actions.githubusercontent.com:sub: repo:${GitHubOrg}/${GitHubRepo}:*
    Policies:
      - PolicyName: ECSDeployPolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - ecs:DescribeServices
                - ecs:DescribeTaskDefinition
                - ecs:DescribeTasks
                - ecs:ListTasks
                - ecs:RegisterTaskDefinition
                - ecs:UpdateService
              Resource: !Sub 'arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:*'
            - Effect: Allow
              Action:
                - ecr:GetAuthorizationToken
                - ecr:BatchCheckLayerAvailability
                - ecr:GetDownloadUrlForLayer
                - ecr:GetRepositoryPolicy
                - ecr:DescribeRepositories
                - ecr:ListImages
                - ecr:DescribeImages
                - ecr:BatchGetImage
                - ecr:InitiateLayerUpload
                - ecr:UploadLayerPart
                - ecr:CompleteLayerUpload
                - ecr:PutImage
              Resource: !Sub 'arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${ECRRepositoryName}'
            - Effect: Allow
              Action:
                - cloudformation:DescribeStacks
                - cloudformation:CreateStack
                - cloudformation:UpdateStack
                - cloudformation:DescribeStackEvents
              Resource: !Sub 'arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${CloudFormationStackName}/*'
```

### Performance

1. **Docker layer caching** with GitHub Actions cache
2. **Multi-stage builds** to minimize image size
3. **Parallel deployments** across multiple environments
4. **Fargate Spot** for cost savings on non-critical workloads
5. **CloudWatch Logs** with appropriate retention policies

### Cost Optimization

1. **ECR lifecycle policies** to clean up old images
2. **Fargate Spot** instances for development/testing
3. **Right-sized task CPU and memory**
4. **Auto-scaling** based on metrics
5. **Scheduled scaling** for predictable traffic patterns

### Multi-Environment Deployments

```yaml
name: Deploy to ECS
on:
  push:
    branches: [main, staging, develop]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    strategy:
      matrix:
        environment: [dev, staging, prod]
    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-ecs-${{ matrix.environment }}
          aws-region: us-east-1

      - name: Deploy to ${{ matrix.environment }}
        run: |
          aws cloudformation deploy \
            --template-file infrastructure/ecs-stack.yaml \
            --stack-name my-app-${{ matrix.environment }} \
            --parameter-overrides \
              Environment=${{ matrix.environment }} \
              ImageUrl=${{ steps.build-image.outputs.image-url }}
```

### Monitoring and Observability

1. **CloudWatch Container Insights** for ECS metrics
2. **Application logs** centralized in CloudWatch Logs
3. **Health checks** with proper grace periods
4. **CloudWatch Alarms** for deployment failures
5. **X-Ray tracing** for distributed tracing

## Further Reading

- [Reference Documentation](./reference.md) - Detailed technical reference for GitHub Actions syntax, OIDC configuration, ECR operations, task definitions, and CloudFormation integration
- [Production Examples](./examples.md) - Complete, production-ready workflows including basic deployments, multi-environment setups, blue/green deployments, private ECR with scanning, CloudFormation stack updates, and full CI/CD pipelines with testing

## Key Concepts

- **GitHub Actions**: Automate workflows from GitHub repository events
- **OIDC Authentication**: Secure, tokenless authentication between GitHub and AWS
- **ECR**: Amazon Elastic Container Registry for Docker image storage
- **ECS**: Amazon Elastic Container Service for container orchestration
- **Fargate**: Serverless compute engine for ECS without managing servers
- **Task Definition**: Blueprint for ECS containers (similar to Pod spec in Kubernetes)
- **Service**: Long-running ECS service with load balancing and auto-scaling
- **CloudFormation**: Infrastructure as Code for AWS resource management
- **Blue/Green Deployment**: Zero-downtime deployment strategy with CodeDeploy

## Common Troubleshooting

**Authentication failures:**
- Verify OIDC trust relationship matches GitHub organization/repository
- Check IAM role has proper permissions for ECR and ECS
- Ensure GitHub Actions repository has `id-token: write` permission

**Deployment failures:**
- Check CloudWatch Logs for application errors
- Verify task definition matches service requirements
- Ensure sufficient CPU/memory in Fargate cluster
- Review health check configuration

**ECR push failures:**
- Verify repository exists and permissions are correct
- Check image tag format and registry URL
- Ensure Docker daemon is running in GitHub Actions runner
- Verify image size doesn't exceed ECR limits

**CloudFormation rollback:**
- Review stack events in AWS Console
- Check parameter values match resource constraints
- Verify IAM role has `cloudformation:UpdateStack` permission
- Enable termination protection for production stacks

## Related Skills

- [aws-cloudformation-ecs](../aws-cloudformation-ecs/) - Design and implement ECS cluster architecture
- [aws-sdk-java-v2-ecs](../../aws-java/aws-sdk-java-v2-ecs/) - Programmatic ECS management with Java SDK
- [aws-cloudformation](../aws-cloudformation/) - Core CloudFormation patterns and best practices

## Constraints and Warnings

### Resource Limits

- **GitHub Actions Limits**: GitHub Actions has usage limits per account (minutes, storage)
- **Workflow File Size**: Workflow files cannot exceed 1 MB in size
- **Job Matrix Limits**: Matrix jobs have limits on total combinations
- **Artifact Retention**: Artifacts and logs have default 90-day retention

### Authentication Constraints

- **OIDC Tokens**: OIDC tokens have limited lifetime (typically 5 minutes)
- **Role Session Duration**: IAM role session duration maximum is 12 hours
- **Permission Scope**: GitHub OIDC role should have least-privilege permissions
- **Cross-Account Access**: Cross-account deployments require appropriate trust relationships

### Operational Constraints

- **Deployment Speed**: CloudFormation deployments may take time; don't assume instant completion
- **Stack Locking**: Stacks cannot be updated while another deployment is in progress
- **ECR Rate Limits**: ECR has API rate limits that may affect large deployments
- **ECS Service Limits**: ECS has limits on tasks per service and services per cluster

### Security Constraints

- **Secret Management**: Never store secrets in GitHub repository or workflow files
- **OIDC Provider**: OIDC provider must be configured in AWS account before first use
- **Repository Access**: Workflow secrets are scoped to repository; organization secrets differ
- **Token Security**: GitHub tokens should have minimum required permissions (contents: read, id-token: write)

### Cost Considerations

- **GitHub Actions**: Minutes beyond free tier incur costs based on runner type
- **ECR Storage**: Container images stored in ECR incur monthly storage costs
- **ECS/Fargate**: Fargate tasks incur costs for vCPU and memory resources
- **Data Transfer**: Data transfer between GitHub runners and AWS resources incurs costs

### Deployment Constraints

- **Stack Drift**: Manual changes to resources cause stack drift
- **Change Set Limits**: Multiple change sets in progress can cause confusion
- **Rollback Limits**: Failed deployments roll back by default; can be disabled
- **Timeout Settings**: Deployment timeouts should account for application startup time

## Additional Files

Overview

This skill provides production-ready patterns to deploy Amazon ECS tasks and services using GitHub Actions and AWS CloudFormation. It covers secure authentication (OIDC or IAM keys), building and pushing Docker images to ECR, rendering task definitions, and orchestrating rolling or blue/green deployments. The goal is repeatable, secure CI/CD for multi-environment container workloads.

How this skill works

Workflows authenticate GitHub Actions to AWS (OIDC recommended), log in to ECR, build and push container images, render task definitions with the new image, and deploy updated task definitions to ECS. CloudFormation templates manage infrastructure (ECR, IAM roles, OIDC provider, ECS cluster, services, and CodeDeploy configuration) so deployments are repeatable and environment-specific. Optional steps include image scanning, lifecycle policies, and wait-for-service-stability checks.

When to use it

  • Deploy Docker containers to ECS from GitHub Actions
  • Set up CI/CD that builds, tags, and pushes images to ECR
  • Update ECS task definitions and deploy services automatically
  • Implement blue/green or rolling deployment strategies
  • Manage infrastructure with CloudFormation from CI/CD
  • Support multi-environment pipelines (dev/stage/prod) with secure auth

Best practices

  • Prefer OIDC for GitHub→AWS authentication to avoid long-lived secrets
  • Use immutable image tags (commit SHA) and ECR image scanning on push
  • Keep task definitions parameterized and render images at deploy time
  • Set deployment circuit breakers, health checks, and wait-for-stability in workflows
  • Use CloudFormation for IAM, OIDC provider, and CodeDeploy resources to enforce least privilege
  • Configure lifecycle policy on ECR and enable image tag immutability

Example use cases

  • CI pipeline that builds with Buildx, pushes multi-arch images to ECR, and deploys an updated task definition
  • Blue/green production deployment via CodeDeploy with CloudFormation-managed deployment groups and alarms
  • Multi-environment stacks where workflows choose CloudFormation parameters per environment
  • Quickstart workflow that uses aws-actions/configure-aws-credentials with OIDC to assume a deploy role and run aws cloudformation deploy
  • Pipeline that updates container secrets from Secrets Manager and redeploys services after image push

FAQ

Which authentication method should I use for GitHub Actions to access AWS?

Use OIDC to assume an AWS role from GitHub Actions. It eliminates long-lived keys and enforces repo/branch trust conditions. IAM access keys are supported only as a fallback.

How do I ensure a new image is used by ECS tasks?

Render the task definition in the workflow with the ECR image tag (commit SHA), then deploy the rendered task definition to the service; enable wait-for-service-stability to verify successful rollout.