如何避免CloudFormation部署栈时删除手动创建的Cognito用户池
问题描述
我在AWS控制台手动创建了一个Cognito用户池,近期打算将其添加到CloudFormation模板中,供栈内其他资源引用,但不希望CloudFormation对其进行修改或删除。不过每次部署模板时,都会看到如下变更集,其中显示该Cognito用户池被标记为删除:
CloudFormation stack changeset ----------------------------------------------------------------------------------------------------------------------------------------------------- Operation LogicalResourceId ResourceType Replacement ----------------------------------------------------------------------------------------------------------------------------------------------------- * Modify WebSocketConnectFunction AWS::Lambda::Function False * Modify WebSocketConnectIntegration AWS::ApiGatewayV2::Integration False * Modify WebSocketDefaultFunction AWS::Lambda::Function False * Modify WebSocketDefaultIntegration AWS::ApiGatewayV2::Integration False * Modify WebSocketDisconnectFunction AWS::Lambda::Function False * Modify WebSocketDisconnectIntegration AWS::ApiGatewayV2::Integration False - Delete CognitoUserPool AWS::Cognito::UserPool N/A
提供的template.yaml内容如下:
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Web Application Backend Parameters: ExistingCognitoUserPoolId: Type: String Description: ID of the existing Cognito User Pool Default: ap-example-2_aKzfw0K4N Resources: WebSocketApi: Type: AWS::ApiGatewayV2::Api Properties: Name: WebSocketAPI ProtocolType: WEBSOCKET RouteSelectionExpression: $request.body.action WebSocketConnectIntegration: Type: AWS::ApiGatewayV2::Integration Properties: ApiId: !Ref WebSocketApi IntegrationType: AWS_PROXY IntegrationUri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${WebSocketConnectFunction.Arn}/invocations WebSocketDisconnectIntegration: Type: AWS::ApiGatewayV2::Integration Properties: ApiId: !Ref WebSocketApi IntegrationType: AWS_PROXY IntegrationUri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${WebSocketDisconnectFunction.Arn}/invocations WebSocketDefaultIntegration: Type: AWS::ApiGatewayV2::Integration Properties: ApiId: !Ref WebSocketApi IntegrationType: AWS_PROXY IntegrationUri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${WebSocketDefaultFunction.Arn}/invocations WebSocketConnectRoute: Type: AWS::ApiGatewayV2::Route Properties: ApiId: !Ref WebSocketApi RouteKey: $connect AuthorizationType: NONE Target: !Sub integrations/${WebSocketConnectIntegration} WebSocketDisconnectRoute: Type: AWS::ApiGatewayV2::Route Properties: ApiId: !Ref WebSocketApi RouteKey: $disconnect AuthorizationType: NONE Target: !Sub integrations/${WebSocketDisconnectIntegration} WebSocketDefaultRoute: Type: AWS::ApiGatewayV2::Route Properties: ApiId: !Ref WebSocketApi RouteKey: $default AuthorizationType: NONE Target: !Sub integrations/${WebSocketDefaultIntegration} WebSocketConnectFunction: Type: AWS::Serverless::Function Properties: Handler: server.wsConnectHandler Runtime: nodejs18.x CodeUri: . Tracing: Active OnConnectPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !Ref WebSocketConnectFunction Principal: apigateway.amazonaws.com WebSocketDisconnectFunction: Type: AWS::Serverless::Function Properties: Handler: server.wsDisconnectHandler Runtime: nodejs18.x CodeUri: . Tracing: Active OnDisconnectPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !Ref WebSocketDisconnectFunction Principal: apigateway.amazonaws.com WebSocketDefaultFunction: Type: AWS::Serverless::Function Properties: Handler: server.wsDefaultHandler Runtime: nodejs18.x CodeUri: . Tracing: Active Timeout: 900 Policies: - Statement: Effect: Allow Action: - execute-api:ManageConnections Resource: - !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${WebSocketApi}/Test/POST/@connections/* OnDefaultPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !Ref WebSocketDefaultFunction Principal: apigateway.amazonaws.com Outputs: WebSocketApiUrl: Value: !Sub wss://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/Stage Description: URL of the WebSocket API CognitoUserPoolId: Value: !Ref ExistingCognitoUserPoolId Description: ID of the existing Cognito User Pool
解决方案
出现这个问题的核心原因:你的CloudFormation栈历史中曾经管理过CognitoUserPool这个逻辑资源,现在模板中移除了该资源定义,CloudFormation会默认尝试删除它。要实现仅引用已有用户池、不被CloudFormation管理的目标,按以下步骤操作:
确认并清理栈的资源关联
检查栈的历史变更记录,确认是否曾经在模板的Resources节中定义过CognitoUserPool资源。如果是,需要先将该资源从栈的管理中剥离:- 使用AWS CLI执行:
aws cloudformation delete-stack --stack-name <你的栈名> --retain-resources CognitoUserPool,这会删除栈但保留指定的用户池资源,之后该资源将脱离CloudFormation管理。 - 或者通过CloudFormation控制台删除栈时,勾选保留
CognitoUserPool资源。
- 使用AWS CLI执行:
正确引用已有用户池
保持现有模板中的ExistingCognitoUserPoolId参数,栈内其他需要关联该用户池的资源(比如API Gateway授权器)直接通过!Ref ExistingCognitoUserPoolId引用即可,绝对不要在Resources节中定义该用户池资源。验证变更集
完成上述操作后,重新生成变更集,确认CognitoUserPool的删除操作不再出现,其他资源的变更符合预期后再部署。
内容的提问来源于stack exchange,提问作者Flavio Andrade
相关产品推荐
相关产品推荐

