#
ドキュメント

Document

自分のための備忘録です。

AWS Copilot

AWS Copilot は Amazon ECS CLI の後継に当たるものです。

https://aws.amazon.com/jp/blogs/news/introducing-aws-copilot/

Ref.

インストール・アップデート( Homebrew を使用しない )

Homebrew はアップグレードが面倒なので手動でインストールがおすすめ。
手動インストール方法は以下のとおり(バージョンを上げる場合も同様のコマンドを発行する)。

$ curl -Lo copilot https://github.com/aws/copilot-cli/releases/latest/download/copilot-darwin && chmod u+x copilot && sudo mv copilot /usr/local/bin/copilot && copilot --help

ref.

概要(サンプル)

プロジェクト構成

 |-- first-app
      |
      |-- .docker
          |
          |-- apache
              |
              |-- Dockerfile
          |
          |-- php
              |
              |-- Dockerfile
      |
      |- src
          |
          |-- apache
              |
              |-- index.html
          |
          |-- php
              |
              |-- script.php

ファイルを作成

Service用

.docker/apache/Dockerfile

FROM apache:alpine
EXPOSE 80
COPY ./src/apache /usr/local/apache2/htdocs/

src/apache/index.html

<html>
    <head>
        <title>Apache Hello World</title>
    </head>
    <body>
       Hello World from Apache
    </body>
</html>

Job用

.docker/php/Dockerfie

FROM php:7.4-cli
COPY ./src/php /usr/src/myapp
WORKDIR /usr/src/myapp
CMD [ "php", "./script.php" ]

src/php/script.php

<?php
var_dump('hello world');

構築フロー

  1. Applicationを作成:$ copilot app init {{app name}}
  2. Environmentを初期化:$ copilot env init
  3. Environment:$ copilot env deploy
  4. Serviceを追加:$ copilot svc init
  5. Serviceをデプロイ:$ copilot svc deploy
  6. Jobを追加:$ copilot job init
  7. Jobをデプロイ:$ copilot job deploy

1. Applicationを作成

$ env AWS_PROFILE=default copilot app init first-app
  • AWS first-app-infrastructure-rolesスタックが作成
    • AdministrationRole AWS::IAM::Role
    • ExecutionRole AWS::IAM::Role
  • AWS SSM パラメータストアに/copilot/applications/first-app登録
  • ローカル copilotディレクトリ作成
  • ローカル copilot/.workspace.yml作成

※ プロファイルdefaultを使用する場合はAWS_PROFILE環境変数で指定する必要はないが、プロファイルの指定忘れを防ぐために明示的に指定する癖をつける。

2. Environmentを初期化

例としてstage環境を作成します。

VPCCIDR range10.0.0.0/16します。 VPC CIDR rangePublic Subnet(2つ)のCIDR rangePraivate Subnet(2つ)のCIDR rangeを指定してstage環境を作成。

$ copilot env init -n stage --override-vpc-cidr 10.1.0.0/16 \
  --override-public-cidrs 10.1.0.0/24,10.1.1.0/24 \
  --override-private-cidrs 10.1.2.0/24,10.1.3.0/24
  • AWS StackSet-first-app-infrastructure-XXXXX-XXXXX-XXXスタック作成
  • AWS first-app-stageスタック作成
  • AWS SSM パラメータストアに/copilot/applications/first-app/environments/stage登録
  • ローカル copilot/environments/stage/manifest.yml作成
# The manifest for the "stage" environment.
# Read the full specification for the "Environment" type at:
#  https://aws.github.io/copilot-cli/docs/manifest/environment/

# Your environment name will be used in naming your resources like VPC, cluster, etc.
name: stage
type: Environment

# Import your own VPC and subnets or configure how they should be created.
network:
  vpc:
    cidr: 10.150.0.0/16
    subnets:
      public:
        - cidr: 10.1.0.0/24
          az: ap-northeast-1a
        - cidr: 10.1.1.0/24
          az: ap-northeast-1c
        -
      private:
        - cidr: 10.1.2.0/24
          az: ap-northeast-1a
        - cidr: 10.1.3.0/24
          az: ap-northeast-1c

# Configure the load balancers in your environment, once created.
# http:
#   public:
#   private:

# Configure observability for your environment resources.
observability:
  container_insights: false

3. Environmentをデプロイ

$ copilot env deploy -n stage
✔ Proposing infrastructure changes for the first-app-stage environment.
- Creating the infrastructure for the first-app-stage environment.            [update complete]  [76.3s]
  - An ECS cluster to group your services                                     [create complete]  [3.9s]
  - A security group to allow your containers to talk to each other           [create complete]  [6.4s]
  - An Internet Gateway to connect to the public internet                     [create complete]  [15.6s]
  - Private subnet 1 for resources with no internet access                    [create complete]  [2.6s]
  - Private subnet 2 for resources with no internet access                    [create complete]  [2.6s]
  - A custom route table that directs network traffic for the public subnets  [create complete]  [8.7s]
  - Public subnet 1 for resources that can access the internet                [create complete]  [2.6s]
  - Public subnet 2 for resources that can access the internet                [create complete]  [2.6s]
  - A private DNS namespace for discovering services within the environment   [create complete]  [42.7s]
  - A Virtual Private Cloud to control networking of your AWS resources       [create complete]  [11.6s]

4. Serviceを追加

サービスapacheを追加します。

$ copilot svc init -n apache

Note: It's best to run this command in the root of your workspace.
Service type: Request-Driven Web Service
Service name: apache
Dockerfile: Enter custom path for your Dockerfile
Dockerfile: .docker/apache/Dockerfile
✔ Wrote the manifest for service apache at copilot/apache/manifest.yml
Your manifest contains configurations like your container size and port (:80).

✔ Created ECR repositories for service apache..
  • AWS SSMパラメータストアに/copilot/applications/first-app/components/apache登録
  • AWS ECRリポジトリを作成(first-app/apache)
  • ローカル copilot/apache/manifest.yml作成

contextをプロジェクトルートに設定するように修正。

# 筆者注 コメントを省略

name: apache
type: Request-Driven Web Service

image:
-  build: .docker/apache/Dockerfile
+  build: 
+    context: .
+    dockerfile: .docker/apache/Dockerfile  
  port: 80

cpu: 1024
memory: 2048

5. Serviceをデプロイ

サービス`apacheをデプロイ。

$ copilot svc deploy -n apache -e stage
# ...
- Creating the infrastructure for stack first-app-test-nginx                      [create complete]  [244.1s]
  - An IAM Role for App Runner to use on your behalf to pull your image from ECR  [create complete]  [19.6s]
  - An IAM role to control permissions for the containers in your service         [create complete]  [22.1s]
  - An App Runner service to run and manage your containers                       [create complete]  [212.5s]
✔ Deployed service nginx.
Recommended follow-up action:
    You can access your service at https://{{xxxxx}}.awsapprunner.com over the internet. <======== App RunnerのURL
➜  first-app git:(master) ✗
  1. ローカル Docker イメージをビルド
  2. ローカル ECRPush`
  3. AWS first-app-stage-apacheスタック作成
  4. AWS AppRunner サービス起動(Request-Driven Web Service)

6. Jobを追加

$ copilot job init -n php
  • AWS SSMパラメータストアに/copilot/applications/first-app/components/php登録
  • AWS ECRリポジトリを作成(first-app/php)
  • ローカル copilot/apache/manifest.yml作成

copilot/php-job/manifest.ymlcontextをプロジェクトルートに設定するように修正。

name: php-job
type: Scheduled Job

on:
  schedule: "@every 2m"

image:
  # Docker build arguments. For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/scheduled-job/#image-build
-  build: .docker/php/Dockerfile
+  build:
+    context: .
+    dockerfile: .docker/php/Dockerfile

cpu: 256       # Number of CPU units for the task.
memory: 512    # Amount of memory in MiB used by the task.

7. Jobをデプロイ

$ copilot job deploy --n php -e stage

# ...
✔ Proposing infrastructure changes for stack first-app-stage-php
- Creating the infrastructure for stack first-app-stage-php                 [create complete]  [151.9s]
  - Update your environment's shared resources                              [create complete]  [2.8s]
  - An IAM role to update your environment stack                            [create complete]  [22.5s]
  - An IAM Role for the Fargate agent to make AWS API calls on your behalf  [create complete]  [22.5s]
  - A CloudWatch log group to hold your service logs                        [create complete]  [2.8s]
  - A CloudWatch event rule to trigger the job's state machine              [create complete]  [59.6s]
  - An IAM role for a state machine to run ECS tasks in your cluster        [create complete]  [23.4s]
  - A state machine to invoke your job and handle retry and timeout logic   [create complete]  [3.9s]
  - An ECS task definition to group your containers and run them on ECS     [create complete]  [0.0s]
  - An IAM role to control permissions for the containers in your tasks     [create complete]  [22.5s]
✔ Deployed php.
  1. ローカル Docker イメージをビルド
  2. ローカル ECRPush`
  3. AWS first-app-stage-phpスタック作成
  4. AWS Step Functions > ステートマシン > first-app-stage-phpとして実行

※ CopilotのJobはECSのスケジュールタスクではなくStep Functionsのステートマシンとしてコンテナを実行する。

実行__d5ada47e-504f-5d2d-3a9e-3cf27489abf1_7fbdbc01-b975-b455-350b-54f6c3248699___Step_Functions___アジアパシフィック__東京_

インエターネット接続する必要があるJobはPパブリックサブネットに作成する。

  • ジョブはパブリックサブネットに作成する
  • パブリックサブネットにInternet Gatewayが作成される
  • パブリックサブネットにInternet Gatewayへのルートが設定される

作成スタック

CloudFormation_-_スタック

Addon

Ref.

https://aws.github.io/copilot-cli/ja/docs/developing/addons/workload/
分かりやすいのでしっかりと読む。

要件

出力(Outputs)

  • ワークロードのOutputsはMyVariableならMY_VARIABLEとして自動的にコンテナに渡される(manifest.ymlvariablesに定義する必要はない)。

ネストされたスタック

  • Addonsはそれを定義したサービス・ジョブや環境のネストされたスタックになる

要点

Addonはワークロード (Load Balanced Web Service や Scheduled Job など)を親に持つネストされたCloiudFormationのスタックになる。

Service の Manifestとデフォルトでは統合されていない任意の AWS サービスを Copilot CLI では Addon という形で追加できます。Addon の例として Service から読み書きをする必要がある DyanmoDB テーブルや S3 バケット、RDS Aurora Serverless クラスターなどが考えられます。

https://aws.github.io/copilot-cli/ja/docs/developing/additional-aws-resources/

Addonはサービスだけでなくジョブでも使用できます。
copilot/{job/service}/addons/ディレクトリにCloudFormation(CFN)テンプレート(.yml)を配置します。

通常 addons/ ディレクトリ以下の各ファイルは独立した Addon を表し AWS CloudFormation (CFN) テンプレート と解釈されます。

https://aws.github.io/copilot-cli/ja/docs/developing/additional-aws-resources/

addonsに配置したCFNテンプレートには、パラメーターとしてApp、Env、Nameが渡されます。
テンプレートにのParametersにApp、Env、Nameを定義する必要があります。

Addon テンプレートには任意の有効な CloudFormation テンプレートを用いることができます。しかしデフォルトでは Copilot は App, Env そして Name パラメーターを渡すため、必要であれば Conditions セクション または Mappings セクション でリソースのプロパティをカスタマイズできます。

https://aws.github.io/copilot-cli/ja/docs/developing/additional-aws-resources/

 Parameters:
  App:
    Type: String
    Description: Your application's name.
  Env:
    Type: String
    Description: The environment name your service, job, or workflow is being deployed to.
  Name:
    Type: String
    Description: The name of the service, job, or workflow being deployed.

ログ

  • ロググループは/copilot/{{アプリケーション}}-{{環境}}-{{Job}}/
  • 失効の期間は1ヶ月

アプリケーションを削除

  • $ copilot app deleteで一括して削除できるかもしれないがService/Job -> Environments -> Applicationの順で削除するのが安全
  • ↑の手順を踏めばcopilot app deleteで削除できない場合も削除できることがよくある
// Service/Jobを削除
$ copilot svc delete -n {{service name}}
$ copilot job delete -n {{job name}}
// Environmentsを削除
$ copilot env delete -n {{env name}}
// Applicationを削除
$ copilot app delete -n {{app name}}

以下でCloudFormationスタック、VPC、ECRなどが削除されます。

$ copilot app delete --name first-app

備考

Role

各Copilotコマンドで作成されるロール。

copilot app init

AdministrationRole

AdministrationRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref AdminRoleName
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: cloudformation.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: AssumeRole-AWSCloudFormationStackSetExecutionRole
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - sts:AssumeRole
                Resource:
                  - !Sub 'arn:${AWS::Partition}:iam::*:role/${AdminRoleName}'

ExecutionRole

ExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref ExecutionRoleName
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS: !GetAtt AdministrationRole.Arn
            Action:
              - sts:AssumeRole
      Path: /
      Policies:
      - PolicyName: ExecutionRolePolicy
        PolicyDocument:
          Version: "2012-10-17"
          Statement:
              - Sid: StackSetRequiredPermissions
                Effect: Allow
                Action:
                  - cloudformation:*
                  - s3:*
                  - sns:*
                Resource: "*"
              - Sid: ManageKMSKeys
                Effect: Allow
                Action:
                  - kms:*
                Resource: "*"
              - Sid: ManageECRRepos
                Effect: Allow
                Action:
                  - ecr:DescribeImageScanFindings
                  - ecr:GetLifecyclePolicyPreview
                  - ecr:CreateRepository
                  - ecr:GetDownloadUrlForLayer
                  - ecr:GetAuthorizationToken
                  - ecr:ListTagsForResource
                  - ecr:ListImages
                  - ecr:DeleteLifecyclePolicy
                  - ecr:DeleteRepository
                  - ecr:SetRepositoryPolicy
                  - ecr:BatchGetImage
                  - ecr:DescribeImages
                  - ecr:DescribeRepositories
                  - ecr:BatchCheckLayerAvailability
                  - ecr:GetRepositoryPolicy
                  - ecr:GetLifecyclePolicy
                  - ecr:TagResource
                Resource: "*"

DNSDelegationRole

 DNSDelegationRole:
    Type: AWS::IAM::Role
    Condition: DelegateDNS
    Properties:
      RoleName: !Ref DNSDelegationRoleName
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS:  !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:root
            Action:
              - sts:AssumeRole
          - Effect: Allow
            Principal:
              AWS:
                - !Sub arn:${AWS::Partition}:iam::876424506091:root
            Action:
              - sts:AssumeRole
      Path: /
      Policies:
      - PolicyName: DNSDelegationPolicy
        PolicyDocument:
          Version: "2012-10-17"
          Statement:
              - Sid: HostedZoneReadRecords
                Effect: Allow
                Action:
                  - route53:Get*
                  - route53:List*
                Resource: "*"
              - Sid: HostedZoneUpdate
                Effect: Allow
                Action:
                  - route53:ChangeResourceRecordSets
                Resource:
                  - !Sub arn:${AWS::Partition}:route53:::hostedzone/${AppHostedZone}
                  - !Sub arn:${AWS::Partition}:route53:::hostedzone/${AppDomainHostedZoneID}

copilot env init

CloudformationExecutionRole

  CloudformationExecutionRole:
    Metadata:
      'aws:copilot:description': 'An IAM Role for AWS CloudFormation to manage resources'
    DeletionPolicy: Retain
    Type: AWS::IAM::Role
    DependsOn: VPC
    Properties:
      RoleName: !Sub ${AWS::StackName}-CFNExecutionRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - 'cloudformation.amazonaws.com'
            - 'lambda.amazonaws.com'
          Action: sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: executeCfn
          # This policy is more permissive than the managed PowerUserAccess
          # since it allows arbitrary role creation, which is needed for the
          # ECS task role specified by the customers.
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
            -
              Effect: Allow
              NotAction:
                - 'organizations:*'
                - 'account:*'
              Resource: '*'
            -
              Effect: Allow
              Action:
                - 'organizations:DescribeOrganization'
                - 'account:ListRegions'
              Resource: '*'

EnvironmentManagerRole

  EnvironmentManagerRole:
 Metadata:
   'aws:copilot:description': 'An IAM Role to describe resources in your environment'
 DeletionPolicy: Retain
 Type: AWS::IAM::Role
 DependsOn: CloudformationExecutionRole
 Properties:
   RoleName: !Sub ${AWS::StackName}-EnvManagerRole
   AssumeRolePolicyDocument:
     Version: '2012-10-17'
     Statement:
     - Effect: Allow
       Principal:
         AWS: !Sub ${ToolsAccountPrincipalARN}
       Action: sts:AssumeRole
   Path: /
   Policies:
   - PolicyName: root
     PolicyDocument:
       Version: '2012-10-17'
       Statement:
       - Sid: CloudwatchLogs
         Effect: Allow
         Action: [
           "logs:GetLogRecord",
           "logs:GetQueryResults",
           "logs:StartQuery",
           "logs:GetLogEvents",
           "logs:DescribeLogStreams",
           "logs:StopQuery",
           "logs:TestMetricFilter",
           "logs:FilterLogEvents",
           "logs:GetLogGroupFields",
           "logs:GetLogDelivery"
         ]
         Resource: "*"
       - Sid: Cloudwatch
         Effect: Allow
         Action: [
           "cloudwatch:DescribeAlarms"
         ]
         Resource: "*"
       - Sid: ECS
         Effect: Allow
         Action: [
           "ecs:ListAttributes",
           "ecs:ListTasks",
           "ecs:DescribeServices",
           "ecs:DescribeTaskSets",
           "ecs:ListContainerInstances",
           "ecs:DescribeContainerInstances",
           "ecs:DescribeTasks",
           "ecs:DescribeClusters",
           "ecs:UpdateService",
           "ecs:PutAttributes",
           "ecs:StartTelemetrySession",
           "ecs:StartTask",
           "ecs:StopTask",
           "ecs:ListServices",
           "ecs:ListTaskDefinitionFamilies",
           "ecs:DescribeTaskDefinition",
           "ecs:ListTaskDefinitions",
           "ecs:ListClusters",
           "ecs:RunTask"
         ]
         Resource: "*"
       - Sid: ExecuteCommand
         Effect: Allow
         Action: [
           "ecs:ExecuteCommand"
         ]
         Resource: "*"
         Condition:
           StringEquals:
             'aws:ResourceTag/copilot-application': !Sub '${AppName}'
             'aws:ResourceTag/copilot-environment': !Sub '${EnvironmentName}' 
       - Sid: CloudFormation
         Effect: Allow
         Action: [
           "cloudformation:CancelUpdateStack",
           "cloudformation:CreateChangeSet",
           "cloudformation:CreateStack",
           "cloudformation:DeleteChangeSet",
           "cloudformation:DeleteStack",
           "cloudformation:Describe*",
           "cloudformation:DetectStackDrift",
           "cloudformation:DetectStackResourceDrift",
           "cloudformation:ExecuteChangeSet",
           "cloudformation:GetTemplate",
           "cloudformation:GetTemplateSummary",
           "cloudformation:UpdateStack",
           "cloudformation:UpdateTerminationProtection"
         ]
         Resource: "*"
       - Sid: GetAndPassCopilotRoles
         Effect: Allow
         Action: [
           "iam:GetRole",
           "iam:PassRole"
         ]
         Resource: "*"
         Condition:
           StringEquals:
             'iam:ResourceTag/copilot-application': !Sub '${AppName}'
             'iam:ResourceTag/copilot-environment': !Sub '${EnvironmentName}'
       - Sid: ECR
         Effect: Allow
         Action: [
           "ecr:BatchGetImage",
           "ecr:BatchCheckLayerAvailability",
           "ecr:CompleteLayerUpload",
           "ecr:DescribeImages",
           "ecr:DescribeRepositories",
           "ecr:GetDownloadUrlForLayer",
           "ecr:InitiateLayerUpload",
           "ecr:ListImages",
           "ecr:ListTagsForResource",
           "ecr:PutImage",
           "ecr:UploadLayerPart",
           "ecr:GetAuthorizationToken"
         ]
         Resource: "*"
       - Sid: ResourceGroups
         Effect: Allow
         Action: [
           "resource-groups:GetGroup",
           "resource-groups:GetGroupQuery",
           "resource-groups:GetTags",
           "resource-groups:ListGroupResources",
           "resource-groups:ListGroups",
           "resource-groups:SearchResources"
         ]
         Resource: "*"
       - Sid: SSM
         Effect: Allow
         Action: [
           "ssm:DeleteParameter",
           "ssm:DeleteParameters",
           "ssm:GetParameter",
           "ssm:GetParameters",
           "ssm:GetParametersByPath"
         ]
         Resource: "*"
       - Sid: SSMSecret
         Effect: Allow
         Action: [
           "ssm:PutParameter",
           "ssm:AddTagsToResource"
         ]
         Resource:
           - !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/copilot/${AppName}/${EnvironmentName}/secrets/*'
       - Sid: ELBv2
         Effect: Allow
         Action: [
           "elasticloadbalancing:DescribeLoadBalancerAttributes",
           "elasticloadbalancing:DescribeSSLPolicies",
           "elasticloadbalancing:DescribeLoadBalancers",
           "elasticloadbalancing:DescribeTargetGroupAttributes",
           "elasticloadbalancing:DescribeListeners",
           "elasticloadbalancing:DescribeTags",
           "elasticloadbalancing:DescribeTargetHealth",
           "elasticloadbalancing:DescribeTargetGroups",
           "elasticloadbalancing:DescribeRules"
         ]
         Resource: "*"
       - Sid: BuiltArtifactAccess
         Effect: Allow
         Action: [
           "s3:ListBucketByTags",
           "s3:GetLifecycleConfiguration",
           "s3:GetBucketTagging",
           "s3:GetInventoryConfiguration",
           "s3:GetObjectVersionTagging",
           "s3:ListBucketVersions",
           "s3:GetBucketLogging",
           "s3:ListBucket",
           "s3:GetAccelerateConfiguration",
           "s3:GetBucketPolicy",
           "s3:GetObjectVersionTorrent",
           "s3:GetObjectAcl",
           "s3:GetEncryptionConfiguration",
           "s3:GetBucketRequestPayment",
           "s3:GetObjectVersionAcl",
           "s3:GetObjectTagging",
           "s3:GetMetricsConfiguration",
           "s3:HeadBucket",
           "s3:GetBucketPublicAccessBlock",
           "s3:GetBucketPolicyStatus",
           "s3:ListBucketMultipartUploads",
           "s3:GetBucketWebsite",
           "s3:ListJobs",
           "s3:GetBucketVersioning",
           "s3:GetBucketAcl",
           "s3:GetBucketNotification",
           "s3:GetReplicationConfiguration",
           "s3:ListMultipartUploadParts",
           "s3:GetObject",
           "s3:GetObjectTorrent",
           "s3:GetAccountPublicAccessBlock",
           "s3:ListAllMyBuckets",
           "s3:DescribeJob",
           "s3:GetBucketCORS",
           "s3:GetAnalyticsConfiguration",
           "s3:GetObjectVersionForReplication",
           "s3:GetBucketLocation",
           "s3:GetObjectVersion",
           "kms:Decrypt"
         ]
         Resource: "*"
       - Sid: EC2
         Effect: Allow
         Action: [
           "ec2:DescribeSubnets",
           "ec2:DescribeSecurityGroups",
           "ec2:DescribeNetworkInterfaces"
         ]
         Resource: "*"
       - Sid: AppRunner
         Effect: Allow
         Action: [
           "apprunner:DescribeService",
           "apprunner:ListOperations",
           "apprunner:ListServices",
           "apprunner:PauseService",
           "apprunner:ResumeService",
           "apprunner:StartDeployment"
         ]
         Resource: "*"
       - Sid: Tags
         Effect: Allow
         Action: [
           "tag:GetResources"
         ]
         Resource: "*"
       - Sid: ApplicationAutoscaling
         Effect: Allow
         Action: [
           "application-autoscaling:DescribeScalingPolicies"
         ]
         Resource: "*"
       - Sid: DeleteRoles
         Effect: Allow
         Action: [
           "iam:DeleteRole",
           "iam:ListRolePolicies",
           "iam:DeleteRolePolicy"
         ]
         Resource:
           - !GetAtt CloudformationExecutionRole.Arn
           - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWS::StackName}-EnvManagerRole"
       - Sid: DeleteEnvStack
         Effect: Allow
         Action:
           - 'cloudformation:DescribeStacks'
           - 'cloudformation:DeleteStack'
         Resource:
           - !Sub 'arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*'

CustomResourceRole

  CustomResourceRole:
    Type: AWS::IAM::Role
    Condition: DelegateDNS
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          -
            Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: "DNSandACMAccess"
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - "acm:ListCertificates"
                  - "acm:RequestCertificate"
                  - "acm:DescribeCertificate"
                  - "acm:GetCertificate"
                  - "acm:DeleteCertificate"
                  - "acm:AddTagsToCertificate"
                  - "sts:AssumeRole"
                  - "logs:*"
                  - "route53:ChangeResourceRecordSets"
                  - "route53:Get*"
                  - "route53:Describe*"
                  - "route53:ListResourceRecordSets"
                  - "route53:ListHostedZonesByName"
                Resource:
                  - "*"

copilot job init

EnvControllerRole

EnvControllerRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: "EnvControllerStackUpdate"
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - cloudformation:DescribeStacks
                  - cloudformation:UpdateStack
                Resource: !Sub 'arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AppName}-${EnvName}/*'
                Condition:
                  StringEquals:
                    'cloudformation:ResourceTag/copilot-application': !Sub '${AppName}'
                    'cloudformation:ResourceTag/copilot-environment': !Sub '${EnvName}'
        - PolicyName: "EnvControllerRolePass"
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - iam:PassRole
                Resource: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AppName}-${EnvName}-CFNExecutionRole'
                Condition:
                  StringEquals:
                    'iam:ResourceTag/copilot-application': !Sub '${AppName}'
                    'iam:ResourceTag/copilot-environment': !Sub '${EnvName}'
      ManagedPolicyArns:
        - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

ExecutionRole

  ExecutionRole:
    Metadata:
      'aws:copilot:description': 'An IAM Role for the Fargate agent to make AWS API calls on your behalf'
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: 'sts:AssumeRole'
      Policies:
        - PolicyName: !Join ['', [!Ref AppName, '-', !Ref EnvName, '-', !Ref WorkloadName, SecretsPolicy]]
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: 'Allow'
                Action:
                  - 'ssm:GetParameters'
                Resource:
                  - !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/*'
                Condition:
                  StringEquals:
                    'ssm:ResourceTag/copilot-application': !Sub '${AppName}'
                    'ssm:ResourceTag/copilot-environment': !Sub '${EnvName}'
              - Effect: 'Allow'
                Action:
                  - 'secretsmanager:GetSecretValue'
                Resource:
                  - !Sub 'arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*'
                Condition:
                  StringEquals:
                    'secretsmanager:ResourceTag/copilot-application': !Sub '${AppName}'
                    'secretsmanager:ResourceTag/copilot-environment': !Sub '${EnvName}'
              - Effect: 'Allow'
                Action:
                  - 'kms:Decrypt'
                Resource:
                  - !Sub 'arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/*'
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy'

TaskRole


TaskRole:
   Metadata:
     'aws:copilot:description': 'An IAM role to control permissions for the containers in your tasks'
    Type: AWS::IAM::Role
   Properties:
     ManagedPolicyArns:
       - Fn::GetAtt: [AddonsStack, Outputs.SqsAccessPolicyArn]
     AssumeRolePolicyDocument:
       Statement:
         - Effect: Allow
           Principal:
             Service: ecs-tasks.amazonaws.com
           Action: 'sts:AssumeRole'
     Policies:
       - PolicyName: 'DenyIAMExceptTaggedRoles'
         PolicyDocument:
           Version: '2012-10-17'
           Statement:
             - Effect: 'Deny'
               Action: 'iam:*'
               Resource: '*'
             - Effect: 'Allow'
               Action: 'sts:AssumeRole'
               Resource:
                 - !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/*'
               Condition:
                 StringEquals:
                   'iam:ResourceTag/copilot-application': !Sub '${AppName}'
                   'iam:ResourceTag/copilot-environment': !Sub '${EnvName}'
 Rule:
   Metadata:
     'aws:copilot:description': "A CloudWatch event rule to trigger the job's state machine"
   Type: AWS::Events::Rule
   Properties:
     ScheduleExpression: !Ref Schedule
     State: ENABLED
     Targets:
       - Arn: !Ref StateMachine
         Id: statemachine
         RoleArn: !GetAtt RuleRole.Arn

RuleRole

RuleRole:
   Type: AWS::IAM::Role
   Properties:
     AssumeRolePolicyDocument:
       Statement:
         - Effect: Allow
           Principal:
             Service: events.amazonaws.com
           Action: sts:AssumeRole
     Policies:
       - PolicyName: EventRulePolicy
         PolicyDocument:
           Statement:
             - Effect: Allow
               Action: states:StartExecution
               Resource: !Ref StateMachine
 StateMachine:
   Metadata:
     'aws:copilot:description': 'A state machine to invoke your job and handle retry and timeout logic'
   Type: AWS::StepFunctions::StateMachine
   Properties:
     StateMachineName: !Sub '${AppName}-${EnvName}-${WorkloadName}'
     RoleArn: !GetAtt StateMachineRole.Arn
     LoggingConfiguration:
       Destinations:
         - CloudWatchLogsLogGroup:
             LogGroupArn: !GetAtt LogGroup.Arn
       IncludeExecutionData: True
       Level: ALL
     DefinitionSubstitutions:
       ContainerName: !Ref WorkloadName
       Cluster:
         Fn::ImportValue: !Sub '${AppName}-${EnvName}-ClusterId'
       TaskDefinition: !Ref TaskDefinition
       Partition: !Ref AWS::Partition
       Subnets:
         Fn::Join:
           - '","'
           - Fn::Split:
               - ','
               - Fn::ImportValue: !Sub '${AppName}-${EnvName}-PublicSubnets'
       AssignPublicIp: ENABLED
       SecurityGroups:
         Fn::Join:
           - '","'
           - - Fn::ImportValue: !Sub "${AppName}-${EnvName}-EnvironmentSecurityGroup"
     DefinitionString: "{\n  \"Version\": \"1.0\",\n  \"Comment\": \"Run AWS Fargate task\",\n  \"StartAt\": \"Run Fargate Task\",\n  \"States\": {\n    \"Run Fargate Task\": {\n      \"Type\": \"Task\",\n      \"Resource\": \"arn:${Partition}:states:::ecs:runTask.sync\",\n      \"Parameters\": {\n        \"LaunchType\": \"FARGATE\",\n        \"PlatformVersion\": \"LATEST\",\n        \"Cluster\": \"${Cluster}\",\n        \"TaskDefinition\": \"${TaskDefinition}\",\n        \"PropagateTags\": \"TASK_DEFINITION\",\n        \"Group.$\": \"$$.Execution.Name\",\n        \"NetworkConfiguration\": {\n          \"AwsvpcConfiguration\": {\n            \"Subnets\": [\"${Subnets}\"],\n            \"AssignPublicIp\": \"${AssignPublicIp}\",\n            \"SecurityGroups\": [\"${SecurityGroups}\"]\n          }\n        }\n      },\n      \"End\": true\n    }\n  }\n}  

StateMachineRole

  StateMachineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: states.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: StateMachine
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action: iam:PassRole
                Resource:
                  - !GetAtt ExecutionRole.Arn
                  - !GetAtt TaskRole.Arn
              - Effect: Allow
                Action: ecs:RunTask
                Resource: !Ref TaskDefinition
                Condition:
                  ArnEquals:
                    'ecs:cluster':
                      Fn::Sub:
                        - arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:cluster/${ClusterID}
                        - ClusterID:
                            Fn::ImportValue: !Sub '${AppName}-${EnvName}-ClusterId'
              - Effect: Allow
                Action:
                  - ecs:StopTask
                  - ecs:DescribeTasks
                Resource: "*"
                Condition:
                  ArnEquals:
                    'ecs:cluster':
                      Fn::Sub:
                        - arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:cluster/${ClusterID}
                        - ClusterID:
                            Fn::ImportValue: !Sub '${AppName}-${EnvName}-ClusterId'
              - Effect: Allow
                Action:
                  - logs:CreateLogDelivery
                  - logs:GetLogDelivery
                  - logs:UpdateLogDelivery
                  - logs:DeleteLogDelivery
                  - logs:ListLogDeliveries
                  - logs:PutResourcePolicy
                  - logs:DescribeResourcePolicies
                  - logs:DescribeLogGroups
                Resource: "*" # CWL doesn't support resource-level permissions
              - Effect: Allow
                Action:
                  - events:PutTargets
                  - events:PutRule
                  - events:DescribeRule
                Resource: !Sub arn:${AWS::Partition}:events:${AWS::Region}:${AWS::AccountId}:rule/StepFunctionsGetEventsForECSTaskRule
  AddonsStack:
    Metadata:
      'aws:copilot:description': 'An Addons CloudFormation Stack for your additional AWS resources'
    Type: AWS::CloudFormation::Stack # Needed for #1848
    DependsOn: EnvControllerAction
    Condition: HasAddons
    Properties:
      Parameters:
        App: !Ref AppName
        Env: !Ref EnvName
        Name: !Ref WorkloadName
      TemplateURL: !Ref AddonsTemplateURL