I'm getting the following error when deploying my CloudFormation template.
✖ Failed to create the stack.
An error occurred (ValidationError) when calling the CreateStack operation: Parameters: [ClusterArn, TaskRoleArn, Env, Subnets, ExecRoleArn, ClusterLogGroup, ContainerSG] must have values
I don't know how to attribute values to the parameters specified because they are being created within the stack. For instance, the ContainerSG and LogGroup :
Parameters:
ContainerSG:
Description: The container security group
Type: String
ClusterLogGroup:
Description: The cluster log group
Type: String
Resources
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join [ '/', ['/aws', 'ecs', !Ref Env]]
ContainerSecurityGroup:
Type : AWS::EC2::SecurityGroup
Properties :
GroupDescription : "ECS Containers Security Group"
VpcId : '{{resolve:ssm:/ca/config/network/vpc_id:1}}'
GroupName: security-group-cmr
SecurityGroupIngress :
- IpProtocol : tcp
FromPort : 8080
ToPort : 8080
CidrIp : 10.49.63.0/24
- IpProtocol : tcp
FromPort : 8080
ToPort : 8080
CidrIp : 10.93.0.0/16
- IpProtocol : tcp
FromPort : 8080
ToPort : 8080
CidrIp : 10.97.0.0/16
- IpProtocol : tcp
FromPort : 8080
ToPort : 8080
CidrIp : 10.50.128.0/21
- IpProtocol : tcp
FromPort : 8080
ToPort : 8080
CidrIp : 10.50.144.0/24
- IpProtocol : tcp
FromPort : 8080
ToPort : 8080
CidrIp : 172.25.0.0/16
The bottom-line question is, how do I attribute values to parameters that are being created along with stacks inside a CloudFormation Template?
Would appreciate someone's help on this. Thank you.
EDIT : This is the complete cloudformation file, some of the parameters (memory, buildnumber, etc) are specified within the pipeline so that's the reason they don't need any values attributed to them within the template :
Description: "Generic Research Template for Application ECS Cluster with an ECS Service"
Parameters:
Env:
Description: the runtime environment
Type: String
AllowedValues:
- dev
- uat
- prod
ServiceName:
Description: The service name
Type: String
BuildNumber:
Description: The bitbucket build number
Type: String
AppCode:
Description: The application's app code
Type: String
Subnets:
Description: Choose which db subnets for the instance
Type: List<AWS::EC2::Subnet::Id>
ContainerPort:
Description: The container port
Type: String
Default: 8080
DesiredCount:
Description: The number of desired tasks for this service
Type: Number
Default: 1
ClusterLogGroup:
Description: The cluster log group
Type: String
Cpu:
Description: allocated cpu credits
Type: Number
Default: 1024
Memory:
Description: hard cap for memory
Type: String
Default: 2048
ClusterArn:
Description: The ECS Cluster ARN
Type: String
ExecRoleArn:
Description: The ARN of the cluster execution role
Type: String
TaskRoleArn:
Description: The ARN of the cluster task role
Type: String
ContainerSG:
Description: The container security group
Type: String
ExcludeTaskDef:
Description: Flag indiciating that the task definition resource should not be created as part of this stack
Type: Number
AllowedValues:
- 0
- 1
Default: 0
Conditions:
IncludeTaskDef: !Equals [!Ref ExcludeTaskDef, 0]
Resources:
ECSService:
Type: AWS::ECS::Service
Properties:
ServiceName: !Sub ${Env}-${AppCode}-${ServiceName}-service
Cluster: !Ref ClusterArn
TaskDefinition: !Ref TaskDefinition
DeploymentConfiguration:
MinimumHealthyPercent: 0
MaximumPercent: 100
DesiredCount: !Ref DesiredCount
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: DISABLED
Subnets:
- !Select [0, !Ref Subnets ]
- !Select [1, !Ref Subnets ]
SecurityGroups:
- !Ref ContainerSG
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Condition: IncludeTaskDef
Properties:
Family: !Sub ${Env}-${AppCode}-${ServiceName}
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: !Ref Cpu
Memory: !Ref Memory
ExecutionRoleArn: !Ref ExecRoleArn
TaskRoleArn: !Ref TaskRoleArn
ContainerDefinitions:
- Name: !Sub ${Env}-${AppCode}-${ServiceName}
Image: !Sub 558043318296.dkr.ecr.us-east-1.amazonaws.com/com.cambridge.gir/${AppCode}-${ServiceName}:${BuildNumber}
Essential: True
PortMappings:
- ContainerPort: !Ref ContainerPort
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref ClusterLogGroup
awslogs-region: us-east-1
awslogs-stream-prefix: !Ref ServiceName
Environment:
- Name: Environment
Value: !Ref Env
- Name: Timezone
Value: "-Duser.timezone=America/New_York"
ClusterKmsKey:
Type: AWS::KMS::Key
Properties:
Description: "Encrypts secrets for apps in the cluster"
KeyPolicy:
Version: '2012-10-17'
Id: !Sub ${Env}-${AppCode}-kms-key
Statement:
- Sid: 'Enable IAM User Permission'
Effect: 'Allow'
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Action:
- kms:*
Resource: '*'
- Sid: 'Allow ECS Tasks to use the key'
Effect: 'Allow'
Principal:
Service: 'ecs-tasks.amazonaws.com'
Action:
- 'kms:GenerateDataKey'
- 'kms:Encrypt'
- 'kms:Decrypt'
Resource: '*'
- Sid: 'Allow SNS to use the key'
Effect: 'Allow'
Principal:
Service: 'sns.amazonaws.com'
Action:
- 'kms:GenerateDataKey'
- 'kms:Encrypt'
- 'kms:Decrypt'
Resource: '*'
- Sid: 'Allow SQS to use the key'
Effect: 'Allow'
Principal:
Service: 'sqs.amazonaws.com'
Action:
- 'kms:GenerateDataKey'
- 'kms:Encrypt'
- 'kms:Decrypt'
Resource: '*'
- Sid: 'Allow ES to use the key'
Effect: 'Allow'
Principal:
Service: 'es.amazonaws.com'
Action:
- 'kms:GenerateDataKey'
- 'kms:Encrypt'
- 'kms:Decrypt'
Resource: '*'
EcsCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Sub ${Env}-${AppCode}-ecs-cluster
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join [ '/', ['/aws', 'ecs', !Ref Env, !Ref AppCode]]
ContainerSecurityGroup:
Type : AWS::EC2::SecurityGroup
Properties :
GroupDescription : "ECS Containers Security Group"
VpcId : '{{resolve:ssm:/ca/config/network/vpc_id:1}}'
GroupName : !Sub ${Env}-${ServiceName}-sg
SecurityGroupIngress :
- IpProtocol : tcp
FromPort : 8080
ToPort : 8080
CidrIp : 10.49.63.0/24
- IpProtocol : tcp
FromPort : 8080
ToPort : 8080
CidrIp : 10.93.0.0/16
- IpProtocol : tcp
FromPort : 8080
ToPort : 8080
CidrIp : 10.97.0.0/16
- IpProtocol : tcp
FromPort : 8080
ToPort : 8080
CidrIp : 10.50.128.0/21
- IpProtocol : tcp
FromPort : 8080
ToPort : 8080
CidrIp : 10.50.144.0/24
- IpProtocol : tcp
FromPort : 8080
ToPort : 8080
CidrIp : 172.25.0.0/16
ClusterExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${Env}-${AppCode}-execution-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy'
- 'arn:aws:iam::aws:policy/AmazonSSMFullAccess'
- 'arn:aws:iam::aws:policy/SecretsManagerReadWrite'
Policies:
- PolicyName: !Sub ${Env}-${AppCode}-execution-role-policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AllowSecretsAccessForContainers
Effect: Allow
Action:
- 'kms:Decrypt'
- 'ssm:GetParameters'
Resource:
- !Sub "arn:aws:ssm:*:*:parameter/${Env}/${AppCode}/*"
- !GetAtt ClusterKmsKey.Arn
- Sid: AllowECRPull
Effect: Allow
Action:
- "ecr:GetAuthorizationToken"
- "ecr:BatchCheckLayerAvailability"
- "ecr:GetDownloadUrlForLayer"
- "ecr:BatchGetImage"
Resource:
- "*"
FargateTaskRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${Env}-${AppCode}-fargate-task-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: 'sts:AssumeRole'
If it is all in one file, you don't need any parameters.
You can always go to the return section of all the resources in the CloudFormation documentation. In this case we can see that a LogGroup returns it's name when you use the Ref function. Sometimes you can use the Ref function, sometimes you need to use the Fn:GetAtt function if you want to get some more exotic properties of resources.
This cheat sheet is also really helpful to see when to use Ref or when to use GetAtt.
In your case you can for example just use !Ref LogGroup
to get the name of your log group or !Ref ContainerSecurityGroup
to get the name of the security group.