如何将值归因于在模板中的堆栈中创建的参数?

How do I attribute values to parameters created within stacks in a template?

部署 CloudFormation 模板时出现以下错误。

✖ 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

我不知道如何将值归因于指定的参数,因为它们是在堆栈中创建的。例如 ContainerSG 和 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

最重要的问题是,我如何将值归因于与 CloudFormation 模板中的堆栈一起创建的参数?

非常感谢有人对此提供帮助。谢谢。

编辑:这是完整的 cloudformation 文件,一些参数(内存、版本号等)在管道中指定,因此它们不需要在模板中赋予它们任何值:

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'

如果全部在一个文件中,则不需要任何参数。

您始终可以转到 CloudFormation 文档中所有资源的 return 部分。在这种情况下,我们可以看到一个 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 函数,如果你想获得一些更奇特的资源属性。

这个 cheat sheet 也非常有助于了解何时使用 Ref 或何时使用 GetAtt。

在您的情况下,您可以使用 !Ref LogGroup 获取日志组的名称或使用 !Ref ContainerSecurityGroup 获取安全组的名称。