我想从 CloudFormation 模板中的参数存储中读取数据库的 URL。对于单个 URL 来说这很容易,但我不知道如何在不同的环境下更改 URL。
我有四个环境(开发、集成、预生产和生产),它们的详细信息存储在 Parameter Store 中的四个不同路径上:
/database/dev/url
/database/int/url
/database/ppe/url
/database/prod/url
我现在想在通过 CloudFormation 部署时选择正确的数据库 URL。我该怎么做?
Parameters:
Environment:
Type: String
Default: dev
AllowedValues:
- dev
- int
- ppe
- prod
DatabaseUrl:
Type: 'AWS::SSM::Parameter::Value<String>'
# Obviously the '+' operator here won't work - so what do I do?
Default: '/database/' + Environment + '/url'
这个功能并不像人们希望的那么简洁。您必须实际传递要从参数存储中查找的每个参数的名称/路径。
模板:
AWSTemplateFormatVersion: 2010-09-09
Description: Example
Parameters:
BucketNameSuffix:
Type: AWS::SSM::Parameter::Value<String>
Default: /example/dev/BucketNameSuffix
Resources:
Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub parameter-store-example-${BucketNameSuffix}
如果您不向模板传递任何参数,
BucketNameSuffix
将使用存储在 /example/dev/BucketNameSuffix
中的值填充。例如,如果您想使用 prod
值(由 /example/prod/BucketNameSuffix
指向),则应为参数 BucketNameSuffix
指定值,但不应传递实际值,而应将参数的替代名称传递给使用,这样你就会通过/example/prod/BucketNameSuffix
。
aws cloudformation update-stack --stack-name example-dev \
--template-body file://./example-stack.yml
aws cloudformation update-stack --stack-name example-prod \
--template-body file://./example-stack.yml \
--parameters ParameterKey=BucketNameSuffix,ParameterValue=/example/prod/BucketNameSuffix
关于此的一篇不太好的 AWS 博客文章:https://aws.amazon.com/blogs/mt/integrating-aws-cloudformation-with-aws-systems-manager-parameter-store/
因为传递百万个无意义的参数看起来很愚蠢,所以我实际上可能会生成一个特定于环境的模板并在生成的模板中设置正确的
Default:
,因此对于prod
环境Default
将是/example/prod/BucketNameSuffix
然后我可以更新 prod
不传递任何参数的堆栈。
您可以使用动态引用
使用 AWS Systems Manager Parameter Store 中存储的参数填充 CloudFormation 模板在这个人为的示例中,我们使用
resolve:ssm
进行两次查找,并使用 !Join
和 !Sub
替换环境
AWSTemplateFormatVersion: 2010-09-09
Parameters:
Environment:
Type: String
Default: prod
AllowedValues:
- prod
- staging
Resources:
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
ContainerDefinitions:
Image: !Join
- ''
- - 'docker.io/bitnami/redis@'
- !Sub '{{resolve:ssm:/app/${Environment}/digest-sha/redis}}'
Environment:
- Name: DB_HOST
Value: !Sub '{{resolve:ssm:/app/${Environment}/db-host}}'
您可以在此处使用Fn::Join。
这是一些伪代码。
您必须将环境作为参数,您已经这样做了。
在需要DatabaseUrl的资源中创建所需的字符串。
Resources :
Instance :
Type : 'AWS::Some::Resource'
Properties :
DatabaseURL : !Join [ "", [ "/database/", !Ref "Environment" , "/url ] ]
希望这有帮助。
注意:您无法使用某些计算逻辑动态为参数赋值。定义参数的所有值都应作为输入给出。
我喜欢 Fn::Sub,它更干净且易于阅读。
!Sub "/database/${Environment}/url"
我陷入了同样的问题,以下是我的发现:
我们可以在从 CloudFormation 写入 SSM 参数存储时编写值和描述,如下所示:
LambdaARN:
Type: AWS::SSM::Parameter
Properties:
Type: String
Description: !Sub "Lambda ARN from ${AWS::StackName}"
Name: !Sub "/secure/${InstallationId}/${AWS::StackName}/lambda-function-arn"
Value: !GetAtt LambdaFunction.Arn
我们无法组合值/默认值来在 SSM 参数存储中查找。如下:
Parameters:
...
LambdaARN:
Type: Type: AWS::SSM::Parameter::Value<String>
Value: !Sub "/secure/${InstallationId}/teststack/lambda-function-arn"
AWS 文档[1] 不允许这样做。两个(子/连接)功能都不起作用。以下是我收到的错误:
调用CreateChangeSet时发生错误(ValidationError) 操作:模板格式错误:每个默认成员必须是 字符串。
在创建堆栈时组合和传递值可以这样完成:
Parameters:
...
LambdaARN:
Type: Type: AWS::SSM::Parameter::Value<String>
....
$ aws cloudformation deploy --template-file cfn.yml --stack-name mystack --parameter-overrides 'LambdaARN=/secure/devtest/teststack/lambda_function-arn'
如果您在将值放入 Parameter Store 时添加自定义标签,它将覆盖 CFN 添加的默认标签。
默认标签:
- aws:cloudformation:stack-id
- aws:cloudformation:stack-name
- aws:cloudformation:logical-id
{{解析:ssm:/我的/值:1}}
最后一个字段是版本。不同的版本可以指向不同的环境。
[1] https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html
[2] https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-paramstore-labels.html
[3] https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-parameter.html
扩展@jbasko的示例,您还可以将键值参数映射放在单独的JSON文件中,并为每个环境使用不同的JSON文件。
云形成文件看起来是一样的:
AWSTemplateFormatVersion: 2010-09-09
Description: Example
Parameters:
BucketNameSuffix:
Type: AWS::SSM::Parameter::Value<String>
Default: /example/dev/BucketNameSuffix
Resources:
Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub parameter-store-example-${BucketNameSuffix}
然后你就可以为每个环境拥有一个 JSON 文件。例如,生产参数文件如下所示:
[
{
"ParameterKey": "BucketNameSuffix",
"ParameterValue": "/example/prod/BucketNameSuffix"
}
]
生产环境的部署如下所示:
aws cloudformation update-stack --stack-name example-prod \
--template-body file://./example-stack.yml \
--parameters file://./production-parameters.json
因此,您需要做的就是每次部署到环境时传递正确的文件。