You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AWS SAM模板If条件失效问题:Cognito用户池部署报错排查

问题:CloudFormation模板中!If条件未正确生效,触发Mapping条目不存在错误

我有多个运行中的AWS CloudFormation栈,包含活跃的Cognito用户池,因各种原因无法删除这些池。现在希望通过同一模板部署新Cognito池时采用新命名规范,但部署时遇到错误。

模板代码

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: >
  Testing

# Global Parameters
Parameters:
  Environment:
    Type: String
    Default: dev
    Description: (required) Environment type
    AllowedValues:
      - dev
      - prod
  CognitoStack:
    Type: String
    Default: true
    Description: (required) Use default cognito or old cognito
    AllowedValues:
      - true
      - false

Mappings:
  HkCognitoUserPoolFixed:
    heka-dev:
      id: 'heka_dev_cognito_user_pool'
  HkCognitoUserPoolClientFixed:
    heka-dev:
      id: 'heka_dev_cognito_user_pool_client'

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 300
    MemorySize: 512
    Runtime: python3.8

Conditions:
  DefaultCognito: !Equals [ !Ref CognitoStack, true ]

Resources:
  # Cognito Stuff
  HkCognitoUserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      #      UserPoolName: !Sub ${AWS::StackName}_${Environment}_cognito_user_pool
      UserPoolName: !If [ DefaultCognito, !Sub "${AWS::StackName}_${Environment}_cognito_user_pool", !FindInMap [ HkCognitoUserPoolFixed, !Ref 'AWS::StackName', 'id' ] ]
      Policies:
        PasswordPolicy:
          MinimumLength: 8
      UsernameAttributes:
        - email
      Schema:
        - AttributeDataType: String
          Name: email
          Required: true
        - AttributeDataType: String
          Name: phone_number
          Required: true
        - AttributeDataType: String
          Name: mrn
      AdminCreateUserConfig:
        AllowAdminCreateUserOnly: false
      AutoVerifiedAttributes:
        - email
        - phone_number

  HkCognitoUserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      UserPoolId: !Ref HkCognitoUserPool
      #      ClientName: !Sub ${AWS::StackName}_${Environment}_cognito_user_pool_client
      ClientName: !If [ DefaultCognito, !Sub "${AWS::StackName}_${Environment}_cognito_user_pool_client", !FindInMap [ HkCognitoUserPoolClientFixed, !Ref 'AWS::StackName', 'id' ] ]
      GenerateSecret: false
      ExplicitAuthFlows:
        - ALLOW_REFRESH_TOKEN_AUTH
        - ALLOW_USER_SRP_AUTH
        - ALLOW_USER_PASSWORD_AUTH
        - ALLOW_CUSTOM_AUTH

部署参数与错误信息

  • 部署参数:Environment=dev;CognitoStack=true
  • 错误信息:
Error: Failed to create changeset for the stack: itcc-5, ex: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state: For expression "Status" we matched expected path: "FAILED" Status: FAILED. Reason: Template error: Unable to get mapping for HkCognitoUserPoolClientFixed::itcc-5::id
Create Change Set has failed: Command did not exit successfully, exit code: 1
itcc-5 has failed: Command did not exit successfully, exit code: 1

根本原因

CloudFormation在模板验证阶段会预解析所有表达式,包括!If的两个分支,无论条件是否成立。你的栈名itcc-5不存在于HkCognitoUserPoolClientFixed和HkCognitoUserPoolFixed这两个Mapping中,因此即使CognitoStack=true(走true分支),CloudFormation仍会验证else分支的!FindInMap,发现找不到对应键就抛出错误。


解决方案

方案1:用条件控制资源创建(推荐)

将新、旧Cognito资源拆分为独立资源,通过Condition控制只创建符合条件的资源,这样CloudFormation不会解析未启用的资源属性,彻底避免Mapping键缺失的问题:

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: >
  Testing

Parameters:
  Environment:
    Type: String
    Default: dev
    Description: (required) Environment type
    AllowedValues:
      - dev
      - prod
  CognitoStack:
    Type: String
    Default: true
    Description: (required) Use default cognito or old cognito
    AllowedValues:
      - true
      - false

Mappings:
  HkCognitoUserPoolFixed:
    heka-dev:
      id: 'heka_dev_cognito_user_pool'
  HkCognitoUserPoolClientFixed:
    heka-dev:
      id: 'heka_dev_cognito_user_pool_client'

Globals:
  Function:
    Timeout: 300
    MemorySize: 512
    Runtime: python3.8

Conditions:
  UseNewCognito: !Equals [ !Ref CognitoStack, true ]
  UseOldCognito: !Equals [ !Ref CognitoStack, false ]

Resources:
  # 新Cognito用户池(仅当UseNewCognito为true时创建)
  HkCognitoUserPoolNew:
    Type: AWS::Cognito::UserPool
    Condition: UseNewCognito
    Properties:
      UserPoolName: !Sub "${AWS::StackName}_${Environment}_cognito_user_pool"
      Policies:
        PasswordPolicy:
          MinimumLength: 8
      UsernameAttributes:
        - email
      Schema:
        - AttributeDataType: String
          Name: email
          Required: true
        - AttributeDataType: String
          Name: phone_number
          Required: true
        - AttributeDataType: String
          Name: mrn
      AdminCreateUserConfig:
        AllowAdminCreateUserOnly: false
      AutoVerifiedAttributes:
        - email
        - phone_number

  # 新Cognito用户池客户端(仅当UseNewCognito为true时创建)
  HkCognitoUserPoolClientNew:
    Type: AWS::Cognito::UserPoolClient
    Condition: UseNewCognito
    Properties:
      UserPoolId: !Ref HkCognitoUserPoolNew
      ClientName: !Sub "${AWS::StackName}_${Environment}_cognito_user_pool_client"
      GenerateSecret: false
      ExplicitAuthFlows:
        - ALLOW_REFRESH_TOKEN_AUTH
        - ALLOW_USER_SRP_AUTH
        - ALLOW_USER_PASSWORD_AUTH
        - ALLOW_CUSTOM_AUTH

  # 旧Cognito用户池(仅当UseOldCognito为true时创建)
  HkCognitoUserPoolOld:
    Type: AWS::Cognito::UserPool
    Condition: UseOldCognito
    Properties:
      UserPoolName: !FindInMap [ HkCognitoUserPoolFixed, !Ref 'AWS::StackName', 'id' ]
      Policies:
        PasswordPolicy:
          MinimumLength: 8
      UsernameAttributes:
        - email
      Schema:
        - AttributeDataType: String
          Name: email
          Required: true
        - AttributeDataType: String
          Name: phone_number
          Required: true
        - AttributeDataType: String
          Name: mrn
      AdminCreateUserConfig:
        AllowAdminCreateUserOnly: false
      AutoVerifiedAttributes:
        - email
        - phone_number

  # 旧Cognito用户池客户端(仅当UseOldCognito为true时创建)
  HkCognitoUserPoolClientOld:
    Type: AWS::Cognito::UserPoolClient
    Condition: UseOldCognito
    Properties:
      UserPoolId: !Ref HkCognitoUserPoolOld
      ClientName: !FindInMap [ HkCognitoUserPoolClientFixed, !Ref 'AWS::StackName', 'id' ]
      GenerateSecret: false
      ExplicitAuthFlows:
        - ALLOW_REFRESH_TOKEN_AUTH
        - ALLOW_USER_SRP_AUTH
        - ALLOW_USER_PASSWORD_AUTH
        - ALLOW_CUSTOM_AUTH

方案2:补充Mapping条目(临时快速解决)

如果只是临时测试,可以在Mapping中添加当前栈名itcc-5的条目,值可以随便填写(因为条件为true时不会用到):

Mappings:
  HkCognitoUserPoolFixed:
    heka-dev:
      id: 'heka_dev_cognito_user_pool'
    itcc-5:
      id: 'temp_dummy_id'
  HkCognitoUserPoolClientFixed:
    heka-dev:
      id: 'heka_dev_cognito_user_pool_client'
    itcc-5:
      id: 'temp_dummy_client_id'

这种方法适合快速验证,但长期维护所有可能的栈名会很繁琐,不推荐作为长期方案。


内容的提问来源于stack exchange,提问作者Ishan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 01:52:18