AWS CloudFormation – 如何在JSON/YAML模板中将字符串转换为大写或小写。

huangapple go评论67阅读模式
英文:

AWS Cloudformation- How to do string Uppercase or lowercase in json/yaml template

问题

I am working on AWS CloudFormation and I created one template in which I asked the user to select an environment.

On the basis of the selected value, I created the resources. The user has to select between DEV, QA, PROD, UAT, etc. but when I suffix this value to the S3 bucket name (-downloads.com), it is not allowed because capital letters are not allowed in S3 bucket names.

So I did a change in JSON where I use fn::Transform with "Condition":"Lower" but then while creating resources, the following error occurs:

No transform named 871247504605::String found.. Rollback requested by the user.

Below is my 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": "Environment Selection"
                    },
                    "Parameters": [
                        "selectedEnv"
                    ]
                }
            ],
            "ParameterLabels": {
                "selectedEnv": {
                    "default": "Please select Environment"
                }
            }
        }
    },
    "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": ""
        }
    }
}

My question is, I want to convert the selected value to lowercase or sometimes uppercase for the S3 bucket or any other resources. How can I do this?

Image of template creation error attached.

英文:

AWS CloudFormation – 如何在JSON/YAML模板中将字符串转换为大写或小写。

I am working on AWS CloudFormation and I created one template in which I asked user to select Environment.

On the basis of selected value I created the resources. User have to select between DEV, QA, PROD, UAT etc. but when I suffix this value to S3 bucket name (-downloads.com) it not allowed because capital letter is not allowed in S3 bucket name.

So I did change in JSON where I use fn::Transform with "Condition":"Lower"
but then while creating resources below error occurs.

> No transform named 871247504605::String found.. Rollback requested by user.

Below is my 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": ""
}
}
}

My question is, I want to do lowercase or sometimes uppercase value for S3 bucket or any other resources. How to do this?

Image of template creation error attached.

答案1

得分: 6

Mr. Young 是正确的,这是你需要使用来调用宏的语法。

然而,他们和文档都未提及的关键因素是,在调用所需的转换宏之前,您需要将此堆栈部署到您的帐户中,然后才能使用ReadMe中列出的函数。

https://github.com/awslabs/aws-cloudformation-templates/blob/master/aws/services/CloudFormation/MacrosExamples/StringFunctions/string.yaml

我认为文档在这方面可能需要澄清,我将尝试提交一个澄清的PR。

英文:

Mr. Young is correct, that is the syntax you need to use to invoke the macros.

HOWEVER, the key factor which both they and the documentation failed to mention is that in order to invoke the transform macros that you need to deploy this stack into your accounts BEFORE you can use the functions listed in the ReadMe.

https://github.com/awslabs/aws-cloudformation-templates/blob/master/aws/services/CloudFormation/MacrosExamples/StringFunctions/string.yaml

I think the docs could be clarified in this regard, I'll see if I can PR a clarification

答案2

得分: 5

我得到了这个问题的答案。
为此,我使用了Mappings JSON,在其中添加了像如果选择的值是DEV,那么使用dev,如果是QA,那么使用qa等等的值,并且使用了下面的JSON,其中使用了Fn:FindInMap

[
{
"Fn::FindInMap": [
"Enviroment",
"PlatformName",
{
"Ref": "selectedEnv"
}
]
},
"clientname"
]

以下是Mappings JSON:

"Mappings" : {
"Enviroment" : {
"PlatformName" : {
"DEV" : "dev",
"QA" : "qa",
"UAT" : "uat",
"PROD" : "prod"
}
}
}

英文:

I got the answer of this question.
For this I have used Mappings JSON in which I have added values like 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"
> ]

Below is the Mappings JSON:

> "Mappings" : {
"Enviroment" : {
"PlatformName" : {
"DEV" : "dev",
"QA" : "qa",
"UAT" : "uat",
"PROD" : "prod"
}
}
}

答案3

得分: 2

以下是您要翻译的内容:

You can do this with a CloudFormation macro.

https://github.com/awslabs/aws-cloudformation-templates/tree/master/aws/services/CloudFormation/MacrosExamples/StringFunctions

Below is from the AWS Documentation

How AWS CloudFormation macros work

There are two major steps to processing templates using macros: creating the macro itself, and then using the macro to perform processing on your templates.

To create a macro definition, you need to create the following:

  • An AWS Lambda function to perform the template processing. This Lambda function accepts either a snippet or an entire template, and any additional parameters that you define. It returns the processed template snippet or the entire template as a response.

  • A resource of type AWS::CloudFormation::Macro, which enables users to call the Lambda function from within AWS CloudFormation templates. This resource specifies the ARN of the Lambda function to invoke for this macro, and additional optional properties to assist with debugging. To create this resource within an account, author a stack template that includes a AWS::CloudFormation::Macro resource, and then create a stack from the template.

To use a macro, reference the macro in your template:

  • To process a section, or snippet, of a template, reference the macro in a Fn::Transform function located relative to the template content you want to transform. When using Fn::Transform, you can also pass any specified parameters it requires.

  • To process an entire template, reference the macro in the Transform section of the template.

Next, you typically create a change set and then execute it. (Processing macros can add multiple resources that you might not be aware of. To ensure that you're aware of all of the changes introduced by macros, we strongly advise that you use change sets.) AWS CloudFormation passes the specified template content, along with any additional specified parameters, to the Lambda function specified in the macro resource. The Lambda function returns the processed template content, be it a snippet or an entire template.

After all macros in the template have been called, AWS CloudFormation generates a change set that includes the processed template content. After you review the change set, execute it to apply the changes.

FOR EXAMPLE:

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
英文:

You can do this with a CloudFormation macro.

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

https://github.com/awslabs/aws-cloudformation-templates/tree/master/aws/services/CloudFormation/MacrosExamples/StringFunctions

Below is from the AWS Documentation


How AWS CloudFormation macros work

There are two major steps to processing templates using macros: creating the macro itself, and then using the macro to perform processing on your templates.

To create a macro definition, you need to create the following:

  • An AWS Lambda function to perform the template processing. This Lambda function accepts either a snippet or an entire template, and any additional parameters that you define. It returns the processed template snippet or the entire template as a response.

  • A resource of type AWS::CloudFormation::Macro, which enables users to call the Lambda function from within AWS CloudFormation templates. This resource specifies the ARN of the Lambda function to invoke for this macro, and additional optional properties to assist with debugging. To create this resource within an account, author a stack template that includes a AWS::CloudFormation::Macro resource, and then create a stack from the template.

To use a macro, reference the macro in your template:

  • To process a section, or snippet, of a template, reference the macro in a Fn::Transform function located relative to the template content you want to transform. When using Fn::Transform, you can also pass any specified parameters it requires.

  • To process an entire template, reference the macro in the Transform section of the template.

Next, you typically create a change set and then execute it. (Processing macros can add multiple resources that you might not be aware of. To ensure that you're aware of all of the changes introduced by macros, we strongly advise that you use change sets.) AWS CloudFormation passes the specified template content, along with any additional specified parameters, to the Lambda function specified in the macro resource. The Lambda function returns the processed template content, be it a snippet or an entire template.

After all macros in the template have been called, AWS CloudFormation generates a change set that includes the processed template content. After you review the change set, execute it to apply the changes.


FOR EXAMPLE:

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

答案4

得分: 2

接受的答案建议使用CloudFormation宏,另一个答案建议使用FindInMap

在这里,FindInMap 不是很有用,因为它只能用于硬编码的值。

宏建议将起作用,但需要相当多的设置(在单独的堆栈中声明宏,在您的部署角色具有调用Lambda的权限,以及您的CloudFormation堆栈使用CAPABILITY_AUTO_EXPAND 部署等等)。

在模板中声明自定义资源将起作用,而且在我看来比依赖于宏需要更少的工作。以下是一个CFN片段,适应了您所询问的S3存储桶资源,演示了使用自定义资源的用法,该资源将任意S3存储桶名称转换为小写:

英文:

The accepted answer suggested using a CloudFormation macro, and another answer suggesting using FindInMap.

FindInMap is not very useful here, since it would only work with hardcoded values.

The macro suggestion will work, but requires quite a bit of setup (declare the macro in a separate stack, ensure your deployer role has permission to invoke the Lambda, and your CloudFormation stack is deployed with CAPABILITY_AUTO_EXPAND, and so on).

Declaring a custom resource within the template will work and IMO involves less work than relying on the macro. Here's a CFN snippet, adapting the S3 bucket resource you were asking about, demonstrating the use of a custom resource which will lowercase an arbitrary S3 bucket name:

  # 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"

答案5

得分: 0

只返回翻译好的部分:

JSON:

{
    "Fn::Transform": {
        "Name": "宏名称",
        "Parameters": {
            "Key": "值"
        }
    }
}

YAML:

Fn::Transform:
  Name: 宏名称
  Parameters:
    Key: 

Ref:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-transform.html

英文:

Simply you can use:
Fn::Transform

JSON:

{
"Fn::Transform": {
"Name": "macro name",
"Parameters": {
"Key": "value"
}
}
}

YAML:

Fn::Transform:
Name : macro name
Parameters :
Key : value

Ref:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-transform.html

huangapple
  • 本文由 发表于 2020年1月6日 17:12:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/59609353.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定