AWS Copilot は Amazon ECS CLI の後継に当たるものです。
https://aws.amazon.com/jp/blogs/news/introducing-aws-copilot/
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
.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>
.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');
$ copilot app init {{app name}}
$ copilot env init
$ copilot env deploy
$ copilot svc init
$ copilot svc deploy
$ copilot job init
$ copilot job deploy
$ env AWS_PROFILE=default copilot app init first-app
AWS
first-app-infrastructure-roles
スタックが作成AWS
SSM パラメータストアに/copilot/applications/first-app
登録ローカル
copilot
ディレクトリ作成ローカル
copilot/.workspace.yml
作成※ プロファイルdefaultを使用する場合はAWS_PROFILE
環境変数で指定する必要はないが、プロファイルの指定忘れを防ぐために明示的に指定する癖をつける。
例としてstage
環境を作成します。
VPC
のCIDR range
を10.0.0.0/16
します。
VPC CIDR range
、Public Subnet
(2つ)のCIDR range
、Praivate 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
$ 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]
サービス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
サービス`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) ✗
ローカル
Docker イメージをビルドローカル
ECRに
Push`AWS
first-app-stage-apacheスタック作成AWS
AppRunner
サービス起動(Request-Driven Web Service)$ 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.yml
をcontext
をプロジェクトルートに設定するように修正。
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.
$ 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.
ローカル
Docker イメージをビルドローカル
ECRに
Push`AWS
first-app-stage-phpスタック作成AWS
Step Functions
> ステートマシン
> first-app-stage-php
として実行※ CopilotのJobはECSのスケジュールタスクではなくStep Functions
のステートマシンとしてコンテナを実行する。
インエターネット接続する必要があるJobはPパブリックサブネットに作成する。
https://aws.github.io/copilot-cli/ja/docs/developing/addons/workload/
分かりやすいのでしっかりと読む。
manifest.yml
のvariables
に定義する必要はない)。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}}/
$ 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
各Copilotコマンドで作成されるロール。
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:
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:
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}
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:
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:
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:
- "*"
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:
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:
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:
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:
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