AWS Cloudformation-如何在 json/yaml 模板中处理字符串大写或小写
AWS Cloudformation- How to do string Uppercase or lowercase in json/yaml template
我正在使用 AWS CloudFormation,我创建了一个模板,我在其中要求用户 select 环境。
根据 selected 值我创建了资源。用户必须在 DEV、QA、PROD、UAT 等之间使用 select,但是当我将此值作为 S3 存储桶名称的后缀 (-downloads.com) 时,它是不允许的,因为 S3 存储桶名称中不允许使用大写字母。
所以我在 JSON 中进行了更改,其中我使用 fn::Transform 和 "Condition":"Lower"
但随后在创建以下资源时发生错误。
No transform named 871247504605::String found.. Rollback requested by user.
下面是我的 CloudFormation JSON
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Provides nesting for required stacks to deploy a full resource of ****",
"Metadata": {
"AWS::CloudFormation::Interface": {
"ParameterGroups": [
{
"Label": {
"default": "Enviroment Selection"
},
"Parameters": [
"selectedEnv"
]
}
],
"ParameterLabels": {
"selectedEnv": {
"default": "Please select Enviroment"
}
}
}
},
"Parameters": {
"selectedEnv": {
"Type": "String",
"Default": "DEV",
"AllowedValues": [
"DEV",
"QA",
"UAT",
"PROD"
]
}
},
"Resources": {
"S3BucketName": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": {
"Fn::Join": [
"",
[
{
"Fn::Transform": {
"Name": "MyString",
"Parameters": {
"InputString": {
"Ref": "selectedEnv"
},
"Operation": "Lower"
}
}
},
"-deployment.companyname.com"
]
]
},
"PublicAccessBlockConfiguration": {
"BlockPublicAcls": "true",
"BlockPublicPolicy": "true",
"IgnorePublicAcls": "true",
"RestrictPublicBuckets": "true"
},
"Tags": [
{
"Key": "ENV",
"Value": {
"Ref": "selectedEnv"
}
},
{
"Key": "Name",
"Value": {
"Fn::Join": [
"",
[
{
"Ref": "selectedEnv"
},
"deployments"
]
]
}
}
]
},
"Metadata": {
"AWS::CloudFormation::Designer": {
"id": "c81705e6-6c88-4a3d-bc49-80d8736bd88e"
}
}
},
"QueueForIOT": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": {
"Fn::Join": [
"",
[
{
"Ref": "selectedEnv"
},
"QueueForIOT"
]
]
},
"DelaySeconds": "0",
"MaximumMessageSize": "262144",
"MessageRetentionPeriod": "345600",
"ReceiveMessageWaitTimeSeconds": "20",
"VisibilityTimeout": "30"
},
"Metadata": {
"AWS::CloudFormation::Designer": {
"id": "6484fbb7-a188-4a57-a40e-ba9bd69d4597"
}
}
}
},
"Outputs": {
"Help": {
"Description": "This is description",
"Value": ""
}
}
}
我的问题是,我想为 S3 存储桶或任何其他资源设置小写或有时大写的值。如何做到这一点?
附上模板创建错误的图片。
我得到了这个问题的答案。
为此,我使用了 Mappings JSON,其中我添加了一些值,例如 If Selected value is DEV then use dev, If QA then qa like this, and used below JSON which used Fn :FindInMap
[
{
"Fn::FindInMap": [
"Enviroment",
"PlatformName",
{
"Ref": "selectedEnv"
}
]
},
"clientname"
]
下面是映射 JSON:
"Mappings" : {
"Enviroment" : {
"PlatformName" : {
"DEV" : "dev",
"QA" : "qa",
"UAT" : "uat",
"PROD" : "prod"
}
}
}
您可以使用 CloudFormation 宏执行此操作。
Parameters:
InputString:
Default: "This is a test input string"
Type: String
Resources:
S3Bucket:
Type: "AWS::S3::Bucket"
Properties:
Tags:
- Key: Upper
Value:
'Fn::Transform':
- Name: 'String'
Parameters:
InputString: !Ref InputString
Operation: Upper
Below is from the AWS Documentation
AWS CloudFormation 宏的工作原理
使用宏处理模板有两个主要步骤:创建宏本身,然后使用宏对模板执行处理。
要创建宏定义,您需要创建以下内容:
执行模板处理的 AWS Lambda 函数。此 Lambda 函数接受片段或整个模板,以及您定义的任何其他参数。它 returns 已处理的模板片段或整个模板作为响应。
AWS::CloudFormation::Macro
类型的资源,使用户能够从 AWS CloudFormation 模板中调用 Lambda 函数。此资源指定要为此宏调用的 Lambda 函数的 ARN,以及用于协助调试的其他可选属性。要在帐户中创建此资源,请创建一个包含 AWS::CloudFormation::Macro
资源的堆栈模板,然后从该模板创建一个堆栈。
要使用宏,请在模板中引用宏:
要处理模板的一部分或片段,请在相对于要转换的模板内容定位的 Fn::Transform 函数中引用宏。当使用Fn::Transform时,您还可以传递它需要的任何指定参数。
要处理整个模板,请在模板的转换部分引用宏。
接下来,您通常会创建一个更改集,然后执行它。 (处理宏可以添加您可能不知道的多个资源。为确保您了解宏引入的所有更改,我们强烈建议您使用更改集。)AWS CloudFormation 传递指定的模板内容,以及与任何额外的指定参数,到宏资源中指定的 Lambda 函数。 Lambda 函数 returns 处理后的模板内容,无论是片段还是整个模板。
调用模板中的所有宏后,AWS CloudFormation 会生成一个包含已处理模板内容的更改集。查看更改集后,执行它以应用更改。
例如:
AWSTemplateFormatVersion: 2010-09-09
Resources:
TransformExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: [lambda.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: ['logs:*']
Resource: 'arn:aws:logs:*:*:*'
TransformFunction:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import traceback
def handler(event, context):
response = {
"requestId": event["requestId"],
"status": "success"
}
try:
operation = event["params"]["Operation"]
input = event["params"]["InputString"]
no_param_string_funcs = ["Upper", "Lower", "Capitalize", "Title", "SwapCase"]
if operation in no_param_string_funcs:
response["fragment"] = getattr(input, operation.lower())()
elif operation == "Strip":
chars = None
if "Chars" in event["params"]:
chars = event["params"]["Chars"]
response["fragment"] = input.strip(chars)
elif operation == "Replace":
old = event["params"]["Old"]
new = event["params"]["New"]
response["fragment"] = input.replace(old, new)
elif operation == "MaxLength":
length = int(event["params"]["Length"])
if len(input) <= length:
response["fragment"] = input
elif "StripFrom" in event["params"]:
if event["params"]["StripFrom"] == "Left":
response["fragment"] = input[len(input)-length:]
elif event["params"]["StripFrom"] != "Right":
response["status"] = "failure"
else:
response["fragment"] = input[:length]
else:
response["status"] = "failure"
except Exception as e:
traceback.print_exc()
response["status"] = "failure"
response["errorMessage"] = str(e)
return response
Handler: index.handler
Runtime: python3.6
Role: !GetAtt TransformExecutionRole.Arn
TransformFunctionPermissions:
Type: AWS::Lambda::Permission
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !GetAtt TransformFunction.Arn
Principal: 'cloudformation.amazonaws.com'
Transform:
Type: AWS::CloudFormation::Macro
Properties:
Name: 'String'
Description: Provides various string processing functions
FunctionName: !GetAtt TransformFunction.Arn
先生Young 是正确的,这是您需要用来调用宏的语法。
但是,他们和文档都没有提到的关键因素是,为了调用转换宏,您需要先将此堆栈部署到您的帐户中,然后才能使用自述文件中列出的功能。
我认为文档可以在这方面进行澄清,我会看看是否可以 PR 进行澄清
接受的答案建议使用 CloudFormation 宏,另一个答案建议使用 FindInMap
。
FindInMap
在这里不是很有用,因为它只适用于硬编码值。
宏建议可行,但需要大量设置(在单独的堆栈中声明宏,确保您的部署者角色有权调用 Lambda,并且您的 CloudFormation 堆栈是使用 CAPABILITY_AUTO_EXPAND
部署的, 等等)。
在模板中声明自定义资源是可行的,IMO 比依赖宏涉及的工作更少。这是一个 CFN 片段,它调整了您询问的 S3 存储桶资源,演示了自定义资源的使用,该资源将小写任意 S3 存储桶名称:
# Custom resource to transform input to lowercase.
LowerCaseLambda:
Type: 'AWS::Lambda::Function'
Properties:
Description: Returns the lowercase version of a string
MemorySize: 256
Runtime: python3.8
Handler: index.lambda_handler
Role: !GetAtt LowerCaseLambdaRole.Arn
Timeout: 30
Code:
ZipFile: |
import cfnresponse
def lambda_handler(event, context):
output = event['ResourceProperties'].get('InputString', '').lower()
responseData = {'OutputString': output}
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
LowerCaseLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- "lambda.amazonaws.com"
Action:
- "sts:AssumeRole"
Policies:
- PolicyName: "lambda-write-logs"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "arn:aws:logs:*:*"
S3BucketName:
Type: Custom::Lowercase
Properties:
ServiceToken: !GetAtt LowerCaseLambda.Arn
InputString: !Ref selectedEnv
S3Bucket:
BucketName: !Join
- ''
- - !GetAtt S3BucketName.OutputString
- "-deployment.companyname.com"
我正在使用 AWS CloudFormation,我创建了一个模板,我在其中要求用户 select 环境。
根据 selected 值我创建了资源。用户必须在 DEV、QA、PROD、UAT 等之间使用 select,但是当我将此值作为 S3 存储桶名称的后缀 (-downloads.com) 时,它是不允许的,因为 S3 存储桶名称中不允许使用大写字母。
所以我在 JSON 中进行了更改,其中我使用 fn::Transform 和 "Condition":"Lower" 但随后在创建以下资源时发生错误。
No transform named 871247504605::String found.. Rollback requested by user.
下面是我的 CloudFormation JSON
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Provides nesting for required stacks to deploy a full resource of ****",
"Metadata": {
"AWS::CloudFormation::Interface": {
"ParameterGroups": [
{
"Label": {
"default": "Enviroment Selection"
},
"Parameters": [
"selectedEnv"
]
}
],
"ParameterLabels": {
"selectedEnv": {
"default": "Please select Enviroment"
}
}
}
},
"Parameters": {
"selectedEnv": {
"Type": "String",
"Default": "DEV",
"AllowedValues": [
"DEV",
"QA",
"UAT",
"PROD"
]
}
},
"Resources": {
"S3BucketName": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": {
"Fn::Join": [
"",
[
{
"Fn::Transform": {
"Name": "MyString",
"Parameters": {
"InputString": {
"Ref": "selectedEnv"
},
"Operation": "Lower"
}
}
},
"-deployment.companyname.com"
]
]
},
"PublicAccessBlockConfiguration": {
"BlockPublicAcls": "true",
"BlockPublicPolicy": "true",
"IgnorePublicAcls": "true",
"RestrictPublicBuckets": "true"
},
"Tags": [
{
"Key": "ENV",
"Value": {
"Ref": "selectedEnv"
}
},
{
"Key": "Name",
"Value": {
"Fn::Join": [
"",
[
{
"Ref": "selectedEnv"
},
"deployments"
]
]
}
}
]
},
"Metadata": {
"AWS::CloudFormation::Designer": {
"id": "c81705e6-6c88-4a3d-bc49-80d8736bd88e"
}
}
},
"QueueForIOT": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": {
"Fn::Join": [
"",
[
{
"Ref": "selectedEnv"
},
"QueueForIOT"
]
]
},
"DelaySeconds": "0",
"MaximumMessageSize": "262144",
"MessageRetentionPeriod": "345600",
"ReceiveMessageWaitTimeSeconds": "20",
"VisibilityTimeout": "30"
},
"Metadata": {
"AWS::CloudFormation::Designer": {
"id": "6484fbb7-a188-4a57-a40e-ba9bd69d4597"
}
}
}
},
"Outputs": {
"Help": {
"Description": "This is description",
"Value": ""
}
}
}
我的问题是,我想为 S3 存储桶或任何其他资源设置小写或有时大写的值。如何做到这一点?
附上模板创建错误的图片。
我得到了这个问题的答案。 为此,我使用了 Mappings JSON,其中我添加了一些值,例如 If Selected value is DEV then use dev, If QA then qa like this, and used below JSON which used Fn :FindInMap
[ { "Fn::FindInMap": [ "Enviroment", "PlatformName", { "Ref": "selectedEnv" } ] }, "clientname" ]
下面是映射 JSON:
"Mappings" : { "Enviroment" : { "PlatformName" : { "DEV" : "dev", "QA" : "qa", "UAT" : "uat", "PROD" : "prod" } } }
您可以使用 CloudFormation 宏执行此操作。
Parameters:
InputString:
Default: "This is a test input string"
Type: String
Resources:
S3Bucket:
Type: "AWS::S3::Bucket"
Properties:
Tags:
- Key: Upper
Value:
'Fn::Transform':
- Name: 'String'
Parameters:
InputString: !Ref InputString
Operation: Upper
Below is from the AWS Documentation
AWS CloudFormation 宏的工作原理
使用宏处理模板有两个主要步骤:创建宏本身,然后使用宏对模板执行处理。
要创建宏定义,您需要创建以下内容:
执行模板处理的 AWS Lambda 函数。此 Lambda 函数接受片段或整个模板,以及您定义的任何其他参数。它 returns 已处理的模板片段或整个模板作为响应。
AWS::CloudFormation::Macro
类型的资源,使用户能够从 AWS CloudFormation 模板中调用 Lambda 函数。此资源指定要为此宏调用的 Lambda 函数的 ARN,以及用于协助调试的其他可选属性。要在帐户中创建此资源,请创建一个包含AWS::CloudFormation::Macro
资源的堆栈模板,然后从该模板创建一个堆栈。
要使用宏,请在模板中引用宏:
要处理模板的一部分或片段,请在相对于要转换的模板内容定位的 Fn::Transform 函数中引用宏。当使用Fn::Transform时,您还可以传递它需要的任何指定参数。
要处理整个模板,请在模板的转换部分引用宏。
接下来,您通常会创建一个更改集,然后执行它。 (处理宏可以添加您可能不知道的多个资源。为确保您了解宏引入的所有更改,我们强烈建议您使用更改集。)AWS CloudFormation 传递指定的模板内容,以及与任何额外的指定参数,到宏资源中指定的 Lambda 函数。 Lambda 函数 returns 处理后的模板内容,无论是片段还是整个模板。
调用模板中的所有宏后,AWS CloudFormation 会生成一个包含已处理模板内容的更改集。查看更改集后,执行它以应用更改。
例如:
AWSTemplateFormatVersion: 2010-09-09
Resources:
TransformExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: [lambda.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: ['logs:*']
Resource: 'arn:aws:logs:*:*:*'
TransformFunction:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import traceback
def handler(event, context):
response = {
"requestId": event["requestId"],
"status": "success"
}
try:
operation = event["params"]["Operation"]
input = event["params"]["InputString"]
no_param_string_funcs = ["Upper", "Lower", "Capitalize", "Title", "SwapCase"]
if operation in no_param_string_funcs:
response["fragment"] = getattr(input, operation.lower())()
elif operation == "Strip":
chars = None
if "Chars" in event["params"]:
chars = event["params"]["Chars"]
response["fragment"] = input.strip(chars)
elif operation == "Replace":
old = event["params"]["Old"]
new = event["params"]["New"]
response["fragment"] = input.replace(old, new)
elif operation == "MaxLength":
length = int(event["params"]["Length"])
if len(input) <= length:
response["fragment"] = input
elif "StripFrom" in event["params"]:
if event["params"]["StripFrom"] == "Left":
response["fragment"] = input[len(input)-length:]
elif event["params"]["StripFrom"] != "Right":
response["status"] = "failure"
else:
response["fragment"] = input[:length]
else:
response["status"] = "failure"
except Exception as e:
traceback.print_exc()
response["status"] = "failure"
response["errorMessage"] = str(e)
return response
Handler: index.handler
Runtime: python3.6
Role: !GetAtt TransformExecutionRole.Arn
TransformFunctionPermissions:
Type: AWS::Lambda::Permission
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !GetAtt TransformFunction.Arn
Principal: 'cloudformation.amazonaws.com'
Transform:
Type: AWS::CloudFormation::Macro
Properties:
Name: 'String'
Description: Provides various string processing functions
FunctionName: !GetAtt TransformFunction.Arn
先生Young 是正确的,这是您需要用来调用宏的语法。
但是,他们和文档都没有提到的关键因素是,为了调用转换宏,您需要先将此堆栈部署到您的帐户中,然后才能使用自述文件中列出的功能。
我认为文档可以在这方面进行澄清,我会看看是否可以 PR 进行澄清
接受的答案建议使用 CloudFormation 宏,另一个答案建议使用 FindInMap
。
FindInMap
在这里不是很有用,因为它只适用于硬编码值。
宏建议可行,但需要大量设置(在单独的堆栈中声明宏,确保您的部署者角色有权调用 Lambda,并且您的 CloudFormation 堆栈是使用 CAPABILITY_AUTO_EXPAND
部署的, 等等)。
在模板中声明自定义资源是可行的,IMO 比依赖宏涉及的工作更少。这是一个 CFN 片段,它调整了您询问的 S3 存储桶资源,演示了自定义资源的使用,该资源将小写任意 S3 存储桶名称:
# Custom resource to transform input to lowercase.
LowerCaseLambda:
Type: 'AWS::Lambda::Function'
Properties:
Description: Returns the lowercase version of a string
MemorySize: 256
Runtime: python3.8
Handler: index.lambda_handler
Role: !GetAtt LowerCaseLambdaRole.Arn
Timeout: 30
Code:
ZipFile: |
import cfnresponse
def lambda_handler(event, context):
output = event['ResourceProperties'].get('InputString', '').lower()
responseData = {'OutputString': output}
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
LowerCaseLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- "lambda.amazonaws.com"
Action:
- "sts:AssumeRole"
Policies:
- PolicyName: "lambda-write-logs"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "arn:aws:logs:*:*"
S3BucketName:
Type: Custom::Lowercase
Properties:
ServiceToken: !GetAtt LowerCaseLambda.Arn
InputString: !Ref selectedEnv
S3Bucket:
BucketName: !Join
- ''
- - !GetAtt S3BucketName.OutputString
- "-deployment.companyname.com"