Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

enter image description here

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.

like image 963
Vikramsinh Gaikwad Avatar asked Jan 06 '20 09:01

Vikramsinh Gaikwad


People also ask

Are CloudFormation templates case sensitive?

To create a stack you run the aws cloudformation create-stack command. You must provide the stack name, the location of a valid template, and any input parameters. Parameters are separated with a space and the key names are case sensitive.

Which two types of syntax can be used within an AWS CloudFormation template?

You can author AWS CloudFormation templates in JSON or YAML formats. We support all AWS CloudFormation features and functions for both formats, including in AWS CloudFormation Designer.

How do you pass parameters in CloudFormation?

You can pass multiple values for individual parameters in an AWS CloudFormation template using one of these ways: Use AWS-specific parameter types to select values from a prepopulated list of existing AWS values from an AWS account. Use CommaDelimitedList parameter types to specify your own values.

What part of a CloudFormation template allows you to pass values into the template?

The Resources section is the only required section. Some sections in a template can be in any order. However, as you build your template, it can be helpful to use the logical order shown in the following list because values in one section might refer to values from a previous section.


3 Answers

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

like image 64
Claudiu Moise Avatar answered Oct 09 '22 02:10

Claudiu Moise


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

like image 25
Vikramsinh Gaikwad Avatar answered Oct 09 '22 03:10

Vikramsinh Gaikwad


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"
like image 2
Josh Kupershmidt Avatar answered Oct 09 '22 02:10

Josh Kupershmidt