# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 AWSTemplateFormatVersion: 2010-09-09 Description: CloudFormation template that represents a load balanced web service on Amazon ECS. Metadata: Version: v1.29.0 Parameters: AppName: Type: String EnvName: Type: String WorkloadName: Type: String ContainerImage: Type: String ContainerPort: Type: Number TaskCPU: Type: String TaskMemory: Type: String TaskCount: Type: Number DNSDelegated: Type: String AllowedValues: [true, false] LogRetention: Type: Number AddonsTemplateURL: Description: 'URL of the addons nested stack template within the S3 bucket.' Type: String Default: "" EnvFileARN: Description: 'URL of the environment file.' Type: String Default: "" EnvFileARNFornginx: Description: 'URL of the environment file for the nginx sidecar.' Type: String Default: "" EnvFileARNFortls: Description: 'URL of the environment file for the tls sidecar.' Type: String Default: "" TargetContainer: Type: String TargetPort: Type: Number NLBAliases: Type: String Default: "" NLBPort: Type: String Conditions: IsGovCloud: !Equals [!Ref "AWS::Partition", "aws-us-gov"] HasAssociatedDomain: !Equals [!Ref DNSDelegated, true] HasAddons: !Not [!Equals [!Ref AddonsTemplateURL, ""]] HasEnvFile: !Not [!Equals [!Ref EnvFileARN, ""]] HasEnvFileFortls: !Not [!Equals [!Ref EnvFileARNFortls, ""]] HasEnvFileFornginx: !Not [!Equals [!Ref EnvFileARNFornginx, ""]] Resources: # If a bucket URL is specified, that means the template exists. LogGroup: Metadata: 'aws:copilot:description': 'A CloudWatch log group to hold your service logs' Type: AWS::Logs::LogGroup Properties: LogGroupName: !Join ['', [/copilot/, !Ref AppName, '-', !Ref EnvName, '-', !Ref WorkloadName]] RetentionInDays: !Ref LogRetention TaskDefinition: Metadata: 'aws:copilot:description': 'An ECS task definition to group your containers and run them on ECS' Type: AWS::ECS::TaskDefinition DependsOn: LogGroup Properties: Family: !Join ['', [!Ref AppName, '-', !Ref EnvName, '-', !Ref WorkloadName]] NetworkMode: awsvpc RequiresCompatibilities: - FARGATE Cpu: !Ref TaskCPU Memory: !Ref TaskMemory ExecutionRoleArn: !GetAtt ExecutionRole.Arn TaskRoleArn: !GetAtt TaskRole.Arn ContainerDefinitions: - Name: !Ref WorkloadName Image: !Ref ContainerImage Environment: - Name: COPILOT_APPLICATION_NAME Value: !Sub '${AppName}' - Name: COPILOT_SERVICE_DISCOVERY_ENDPOINT Value: prod.my-app.local - Name: COPILOT_ENVIRONMENT_NAME Value: !Sub '${EnvName}' - Name: COPILOT_SERVICE_NAME Value: !Sub '${WorkloadName}' - Name: LOG_LEVEL Value: "info" EnvironmentFiles: - !If - HasEnvFile - Type: s3 Value: !Ref EnvFileARN - !Ref AWS::NoValue LogConfiguration: LogDriver: awslogs Options: awslogs-region: !Ref AWS::Region awslogs-group: !Ref LogGroup awslogs-stream-prefix: copilot PortMappings: - ContainerPort: 80 Protocol: tcp Name: target - ContainerPort: 8081 Protocol: tcp - Name: nginx Image: 1234567890.dkr.ecr.us-west-2.amazonaws.com/proxy:cicdtest PortMappings: - ContainerPort: 85 Protocol: tcp Environment: - Name: COPILOT_APPLICATION_NAME Value: !Sub '${AppName}' - Name: COPILOT_SERVICE_DISCOVERY_ENDPOINT Value: prod.my-app.local - Name: COPILOT_ENVIRONMENT_NAME Value: !Sub '${EnvName}' - Name: COPILOT_SERVICE_NAME Value: !Sub '${WorkloadName}' EnvironmentFiles: - !If - HasEnvFileFornginx - Type: s3 Value: !Ref EnvFileARNFornginx - !Ref: AWS::NoValue LogConfiguration: LogDriver: awslogs Options: awslogs-region: !Ref AWS::Region awslogs-group: !Ref LogGroup awslogs-stream-prefix: copilot - Name: tls Image: 1234567890.dkr.ecr.us-west-2.amazonaws.com/proxy:cicdtest PortMappings: - ContainerPort: 82 Protocol: tcp - ContainerPort: 443 Protocol: tcp - ContainerPort: 8085 Protocol: tcp Environment: - Name: COPILOT_APPLICATION_NAME Value: !Sub '${AppName}' - Name: COPILOT_SERVICE_DISCOVERY_ENDPOINT Value: prod.my-app.local - Name: COPILOT_ENVIRONMENT_NAME Value: !Sub '${EnvName}' - Name: COPILOT_SERVICE_NAME Value: !Sub '${WorkloadName}' EnvironmentFiles: - !If - HasEnvFileFortls - Type: s3 Value: !Ref EnvFileARNFortls - !Ref: AWS::NoValue LogConfiguration: LogDriver: awslogs Options: awslogs-region: !Ref AWS::Region awslogs-group: !Ref LogGroup awslogs-stream-prefix: copilot 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: Version: '2012-10-17' 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/*' - !If # Optional IAM permission required by ECS task def env file # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/taskdef-envfiles.html#taskdef-envfiles-iam # Example EnvFileARN: arn:aws:s3:::stackset-demo-infrastruc-pipelinebuiltartifactbuc-11dj7ctf52wyf/manual/1638391936/env - HasEnvFile - PolicyName: !Join ['', [!Ref AppName, '-', !Ref EnvName, '-', !Ref WorkloadName, GetEnvFilePolicy]] PolicyDocument: Version: '2012-10-17' Statement: - Effect: 'Allow' Action: - 's3:GetObject' Resource: - !Ref EnvFileARN - Effect: 'Allow' Action: - 's3:GetBucketLocation' Resource: - !Join - '' - - 'arn:' - !Ref AWS::Partition - ':s3:::' - !Select [0, !Split ['/', !Select [5, !Split [':', !Ref EnvFileARN]]]] - !Ref AWS::NoValue - !If - HasEnvFileFornginx - PolicyName: !Join [ '', [ !Ref AppName, '-', !Ref EnvName, '-', !Ref WorkloadName, GetEnvFilePolicyFornginx ] ] PolicyDocument: Version: '2012-10-17' Statement: - Effect: 'Allow' Action: - 's3:GetObject' Resource: - !Ref EnvFileARNFornginx - Effect: 'Allow' Action: - 's3:GetBucketLocation' Resource: - !Join - '' - - 'arn:' - !Ref AWS::Partition - ':s3:::' - !Select [ 0, !Split [ '/', !Select [ 5, !Split [ ':', !Ref EnvFileARNFornginx ] ] ] ] - !Ref AWS::NoValue - !If - HasEnvFileFortls - PolicyName: !Join ['', [!Ref AppName, '-', !Ref EnvName, '-', !Ref WorkloadName, GetEnvFilePolicyFortls]] PolicyDocument: Version: '2012-10-17' Statement: - Effect: 'Allow' Action: - 's3:GetObject' Resource: - !Ref EnvFileARNFortls - Effect: 'Allow' Action: - 's3:GetBucketLocation' Resource: - !Join - '' - - 'arn:' - !Ref AWS::Partition - ':s3:::' - !Select [0, !Split ['/', !Select [5, !Split [':', !Ref EnvFileARNFortls]]]] - !Ref AWS::NoValue 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: AssumeRolePolicyDocument: Version: '2012-10-17' 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}' DiscoveryService: Metadata: 'aws:copilot:description': 'Service discovery for your services to communicate within the VPC' Type: AWS::ServiceDiscovery::Service Properties: Description: Discovery Service for the Copilot services DnsConfig: RoutingPolicy: MULTIVALUE DnsRecords: - TTL: 10 Type: A - TTL: 10 Type: SRV HealthCheckCustomConfig: FailureThreshold: 1 Name: !Ref WorkloadName NamespaceId: Fn::ImportValue: !Sub '${AppName}-${EnvName}-ServiceDiscoveryNamespaceID' EnvControllerAction: Metadata: 'aws:copilot:description': "Update your environment's shared resources" Type: Custom::EnvControllerFunction Properties: ServiceToken: !GetAtt EnvControllerFunction.Arn Workload: !Ref WorkloadName EnvStack: !Sub '${AppName}-${EnvName}' Parameters: [Aliases] EnvVersion: v1.42.0 EnvControllerFunction: Type: AWS::Lambda::Function Properties: Handler: "index.handler" Timeout: 900 MemorySize: 512 Role: !GetAtt 'EnvControllerRole.Arn' Runtime: nodejs16.x EnvControllerRole: Metadata: 'aws:copilot:description': "An IAM role to update your environment stack" 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 Service: Metadata: 'aws:copilot:description': 'An ECS service to run and maintain your tasks in the environment cluster' Type: AWS::ECS::Service DependsOn: - NLBListener - NLBListener1 - NLBListener2 - NLBListener3 Properties: PlatformVersion: LATEST Cluster: Fn::ImportValue: !Sub '${AppName}-${EnvName}-ClusterId' TaskDefinition: !Ref TaskDefinition DesiredCount: !Ref TaskCount DeploymentConfiguration: DeploymentCircuitBreaker: Enable: true Rollback: true MinimumHealthyPercent: 100 MaximumPercent: 200 Alarms: !If - IsGovCloud - !Ref AWS::NoValue - Enable: false AlarmNames: [] Rollback: true PropagateTags: SERVICE LaunchType: FARGATE ServiceConnectConfiguration: !If - IsGovCloud - !Ref AWS::NoValue - Enabled: False NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: ENABLED Subnets: Fn::Split: - ',' - Fn::ImportValue: !Sub '${AppName}-${EnvName}-PublicSubnets' SecurityGroups: - Fn::ImportValue: !Sub '${AppName}-${EnvName}-EnvironmentSecurityGroup' - !Ref NLBSecurityGroup # This may need to be adjusted if the container takes a while to start up HealthCheckGracePeriodSeconds: 60 LoadBalancers: - ContainerName: tls ContainerPort: 82 TargetGroupArn: !Ref NLBTargetGroup - ContainerName: fe ContainerPort: 8081 TargetGroupArn: !Ref NLBTargetGroup1 - ContainerName: tls ContainerPort: 8085 TargetGroupArn: !Ref NLBTargetGroup2 - ContainerName: nginx ContainerPort: 85 TargetGroupArn: !Ref NLBTargetGroup3 ServiceRegistries: - RegistryArn: !GetAtt DiscoveryService.Arn Port: !Ref TargetPort PublicNetworkLoadBalancer: Metadata: 'aws:copilot:description': 'A Network Load Balancer to distribute public traffic to your service' Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Scheme: internet-facing Subnets: Fn::Split: - "," - Fn::ImportValue: !Sub '${AppName}-${EnvName}-PublicSubnets' Type: network NLBListener: Metadata: 'aws:copilot:description': 'A TLS listener on port `443` that forwards traffic to your tasks' Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: !Ref NLBTargetGroup Type: forward LoadBalancerArn: !Ref PublicNetworkLoadBalancer Port: 443 Protocol: TLS Certificates: - CertificateArn: !Ref NLBCertValidatorAction SslPolicy: ELBSecurityPolicy-TLS13-1-2-2021-06 NLBListener1: Metadata: 'aws:copilot:description': 'A TLS listener on port `8081` that forwards traffic to your tasks' Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: !Ref NLBTargetGroup1 Type: forward LoadBalancerArn: !Ref PublicNetworkLoadBalancer Port: 8081 Protocol: TLS Certificates: - CertificateArn: !Ref NLBCertValidatorAction SslPolicy: ELBSecurityPolicy-TLS13-1-2-2021-06 NLBListener2: Metadata: 'aws:copilot:description': 'A TCP listener on port `8082` that forwards traffic to your tasks' Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: !Ref NLBTargetGroup2 Type: forward LoadBalancerArn: !Ref PublicNetworkLoadBalancer Port: 8082 Protocol: TCP NLBListener3: Metadata: 'aws:copilot:description': 'A TCP listener on port `8083` that forwards traffic to your tasks' Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: !Ref NLBTargetGroup3 Type: forward LoadBalancerArn: !Ref PublicNetworkLoadBalancer Port: 8083 Protocol: TCP NLBTargetGroup: Metadata: 'aws:copilot:description': 'A target group to connect the network load balancer to your service on port 82' Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Port: 82 Protocol: TCP TargetGroupAttributes: - Key: deregistration_delay.timeout_seconds Value: 60 # ECS Default is 300; Copilot default is 60. TargetType: ip VpcId: Fn::ImportValue: !Sub "${AppName}-${EnvName}-VpcId" NLBTargetGroup1: Metadata: 'aws:copilot:description': 'A target group to connect the network load balancer to your service on port 8081' Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Port: 8081 Protocol: TCP TargetGroupAttributes: - Key: deregistration_delay.timeout_seconds Value: 60 # ECS Default is 300; Copilot default is 60. TargetType: ip VpcId: Fn::ImportValue: !Sub "${AppName}-${EnvName}-VpcId" NLBTargetGroup2: Metadata: 'aws:copilot:description': 'A target group to connect the network load balancer to your service on port 8085' Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Port: 8085 Protocol: TCP TargetGroupAttributes: - Key: deregistration_delay.timeout_seconds Value: 60 # ECS Default is 300; Copilot default is 60. TargetType: ip VpcId: Fn::ImportValue: !Sub "${AppName}-${EnvName}-VpcId" NLBTargetGroup3: Metadata: 'aws:copilot:description': 'A target group to connect the network load balancer to your service on port 85' Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Port: 85 Protocol: TCP TargetGroupAttributes: - Key: deregistration_delay.timeout_seconds Value: 60 # ECS Default is 300; Copilot default is 60. TargetType: ip VpcId: Fn::ImportValue: !Sub "${AppName}-${EnvName}-VpcId" NLBSecurityGroup: Metadata: 'aws:copilot:description': 'A security group for your network load balancer to route traffic to service' Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Allow access from the network load balancer to service SecurityGroupIngress: - CidrIp: 10.0.0.0/24 Description: Ingress to allow access from Network Load Balancer subnet FromPort: 82 IpProtocol: TCP ToPort: 82 - CidrIp: 10.0.0.0/24 Description: Ingress to allow access from Network Load Balancer subnet FromPort: 8081 IpProtocol: TCP ToPort: 8081 - CidrIp: 10.0.0.0/24 Description: Ingress to allow access from Network Load Balancer subnet FromPort: 8085 IpProtocol: TCP ToPort: 8085 - CidrIp: 10.0.0.0/24 Description: Ingress to allow access from Network Load Balancer subnet FromPort: 85 IpProtocol: TCP ToPort: 85 - CidrIp: 10.1.0.0/24 Description: Ingress to allow access from Network Load Balancer subnet FromPort: 82 IpProtocol: TCP ToPort: 82 - CidrIp: 10.1.0.0/24 Description: Ingress to allow access from Network Load Balancer subnet FromPort: 8081 IpProtocol: TCP ToPort: 8081 - CidrIp: 10.1.0.0/24 Description: Ingress to allow access from Network Load Balancer subnet FromPort: 8085 IpProtocol: TCP ToPort: 8085 - CidrIp: 10.1.0.0/24 Description: Ingress to allow access from Network Load Balancer subnet FromPort: 85 IpProtocol: TCP ToPort: 85 Tags: - Key: Name Value: !Sub 'copilot-${AppName}-${EnvName}-${WorkloadName}-nlb' VpcId: Fn::ImportValue: !Sub "${AppName}-${EnvName}-VpcId" NLBCustomDomainAction: Metadata: 'aws:copilot:description': "Add A-records for your Network Load Balancer aliases" Type: Custom::NLBCustomDomainFunction Condition: HasAssociatedDomain Properties: ServiceToken: !GetAtt NLBCustomDomainFunction.Arn PublicAccessHostedZoneID: !GetAtt PublicNetworkLoadBalancer.CanonicalHostedZoneID PublicAccessDNS: !GetAtt PublicNetworkLoadBalancer.DNSName EnvHostedZoneId: Fn::ImportValue: !Sub "${AppName}-${EnvName}-HostedZone" EnvName: !Ref EnvName AppName: !Ref AppName ServiceName: !Ref WorkloadName RootDNSRole: arn:aws:iam::123456789123:role/my-app-DNSDelegationRole DomainName: example.com Aliases: - nlb.example.com NLBCustomDomainFunction: Type: AWS::Lambda::Function Condition: HasAssociatedDomain Properties: Handler: "index.handler" Timeout: 900 MemorySize: 512 Role: !GetAtt 'NLBCustomDomainRole.Arn' Runtime: nodejs16.x NLBCustomDomainRole: Metadata: 'aws:copilot:description': "An IAM role to update the environment Route 53 hosted zone" Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: "NLBCustomDomainPolicy" PolicyDocument: Version: '2012-10-17' Statement: - Sid: AllowAssumeRole Effect: Allow Action: sts:AssumeRole Resource: "*" - Sid: EnvHostedZoneUpdateAndWait Effect: Allow Action: route53:ChangeResourceRecordSets Resource: !Sub - arn:${AWS::Partition}:route53:::hostedzone/${EnvHostedZone} - EnvHostedZone: Fn::ImportValue: !Sub "${AppName}-${EnvName}-HostedZone" - Sid: EnvHostedZoneRead Effect: Allow Action: - route53:ListResourceRecordSets - route53:GetChange Resource: "*" ManagedPolicyArns: - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole NLBCertValidatorAction: Metadata: 'aws:copilot:description': "Request and validate the certificate for your Network Load Balancer" Type: Custom::NLBCertValidatorFunction Condition: HasAssociatedDomain Properties: ServiceToken: !GetAtt NLBCertValidatorFunction.Arn LoadBalancerDNS: !GetAtt PublicNetworkLoadBalancer.DNSName EnvHostedZoneId: Fn::ImportValue: !Sub "${AppName}-${EnvName}-HostedZone" EnvName: !Ref EnvName AppName: !Ref AppName ServiceName: !Ref WorkloadName RootDNSRole: arn:aws:iam::123456789123:role/my-app-DNSDelegationRole DomainName: example.com Aliases: - nlb.example.com NLBCertValidatorFunction: Type: AWS::Lambda::Function Condition: HasAssociatedDomain Properties: Handler: "index.handler" Timeout: 900 MemorySize: 512 Role: !GetAtt 'NLBCertValidatorRole.Arn' Runtime: nodejs16.x NLBCertValidatorRole: Metadata: 'aws:copilot:description': "An IAM role to request and validate a certificate for your service" Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: "NLBCertValidatorPolicy" PolicyDocument: Version: '2012-10-17' Statement: - Sid: AllowAssumeRole Effect: Allow Action: sts:AssumeRole Resource: "*" - Sid: EnvHostedZoneUpdateAndWait Effect: Allow Action: route53:ChangeResourceRecordSets Resource: !Sub - arn:${AWS::Partition}:route53:::hostedzone/${EnvHostedZone} - EnvHostedZone: Fn::ImportValue: !Sub "${AppName}-${EnvName}-HostedZone" - Sid: EnvHostedZoneRead Effect: Allow Action: - route53:ListResourceRecordSets - route53:GetChange Resource: "*" - Sid: ServiceCertificateDelete Effect: Allow Action: acm:DeleteCertificate Resource: "*" Condition: StringEquals: 'aws:ResourceTag/copilot-application': !Sub '${AppName}' 'aws:ResourceTag/copilot-environment': !Sub '${EnvName}' 'aws:ResourceTag/copilot-service': !Sub '${WorkloadName}' - Sid: TaggedResourcesRead Effect: Allow Action: tag:GetResources Resource: "*" - Sid: ServiceCertificateCreate Effect: Allow Action: - acm:RequestCertificate - acm:AddTagsToCertificate Resource: "*" Condition: StringEquals: 'aws:ResourceTag/copilot-application': !Sub '${AppName}' 'aws:ResourceTag/copilot-environment': !Sub '${EnvName}' 'aws:ResourceTag/copilot-service': !Sub '${WorkloadName}' - Sid: CertificateRead Effect: Allow Action: acm:DescribeCertificate Resource: "*" ManagedPolicyArns: - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 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 Outputs: DiscoveryServiceARN: Description: ARN of the Discovery Service. Value: !GetAtt DiscoveryService.Arn Export: Name: !Sub ${AWS::StackName}-DiscoveryServiceARN PublicNetworkLoadBalancerDNSName: Value: !GetAtt PublicNetworkLoadBalancer.DNSName Export: Name: !Sub ${AWS::StackName}-PublicNetworkLoadBalancerDNSName