aws-cloudformation / aws-cloudformation/cloudformation-coverage-roadmap
AWS::CloudFormation::CustomResource - Lambda Backed CustomResource's ResourceProperties converts int and boolean properties to strings
- Dominant language
- No language data
- Stars
- 1.1k
- Forks
- 62
- PR merge metrics
- No merged PRs in 30d
Description
### Name of the resource
AWS::CloudFormation::CustomResource
### Resource Name
AWS::CloudFormation::CustomResource
### Issue Description
Created a Custom Resource with boolean and int properties. When these properties are accessed in the Lambda function, they are all converted to string.
```yaml
S3CustomResource:
Type: Custom::S3CustomResource
Properties:
ServiceToken: !GetAtt AWSLambdaFunction.Arn
bucket_name: ferdous-sample-bucket-name-5 ## Additional parameter here
boolean_property: true ## Additional parameter here
integer_property: 14 ## Additional parameter here
```
### Expected Behavior
Although the AWS Cloud formation docs although mentions that the `ResourceProperties` is a JSON Object. And JSON object can contain Integers and Booleans.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-requests.html
### Observed Behavior
Conducted a short experiment to verify if the `ResourceProperties` in `event` of the custom resources are passed with the correct data type. Used the following Custom Resource configuration
```yaml
Resources:
SampleS3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName:
DeletionPolicy: Delete
S3CustomResource:
Type: Custom::S3CustomResource
Properties:
ServiceToken: !GetAtt AWSLambdaFunction.Arn
bucket_name:
boolean_property: true
integer_property: 14
AWSLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Description: "Empty an S3 bucket!"
FunctionName: !Sub '${AWS::StackName}-${AWS::Region}-lambda'
Handler: index.handler
Role: !GetAtt AWSLambdaExecutionRole.Arn
Timeout: 360
Runtime: python3.8
Code:
ZipFile: |
import boto3
import cfnresponse
def handler(event, context):
# Get request type
the_event = event['RequestType']
print("The event is: ", str(the_event))
response_data = {}
s3 = boto3.client('s3')
# Retrieve parameters (bucket name)
bucket_name = event['ResourceProperties']['bucket_name']
response_data["bucket_name"] = str(type(event['ResourceProperties']['bucket_name']))
response_data["boolean_property"] = str(type(event['ResourceProperties']['boolean_property']))
response_data["integer_property"] = str(type(event['ResourceProperties']['integer_property']))
try:
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
except Exception as e:
print("Execution failed...")
print(str(e))
response_data['Data'] = str(e)
cfnresponse.send(event, context, cfnresponse.FAILED, response_data)
AWSLambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action:
- sts:AssumeRole
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Version: '2012-10-17'
Path: "/"
Policies:
- PolicyDocument:
Statement:
- Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Effect: Allow
Resource: arn:aws:logs:*:*:*
Version: '2012-10-17'
PolicyName: !Sub ${AWS::StackName}-${AWS::Region}-AWSLambda-CW
- PolicyDocument:
Statement:
- Action:
- s3:PutObject
- s3:DeleteObject
- s3:List*
Effect: Allow
Resource:
- !Sub arn:aws:s3:::${SampleS3Bucket}
- !Sub arn:aws:s3:::${SampleS3Bucket}/*
Version: '2012-10-17'
PolicyName: !Sub ${AWS::StackName}-${AWS::Region}-AWSLambda-S3
RoleName: !Sub ${AWS::StackName}-${AWS::Region}-AWSLambdaExecutionRole
```
After applying it, the following logs appear
```
{
"Status": "SUCCESS",
"Reason": "See the details in CloudWatch Log Stream: ...",
"PhysicalResourceId": "...",
"StackId": "...",
"RequestId": "4fe1e63e-dc02-4087-a1e8-541efe0ad011",
"LogicalResourceId": "S3CustomResource",
"NoEcho": false,
"Data": {
"bucket_name": "",
"boolean_property": "",
"integer_property": ""
}
}
```
The properties are always returning String even though they have been passed as String, Boolean and Integer.
### Test Cases
Pass any integer or boolean key value pair in the `Properties` section and you can see that the properties received in the `event['ResourceProperties']` object are all in string format.
```
S3CustomResource:
Type: Custom::S3CustomResource
Properties:
ServiceToken: !GetAtt AWSLambdaFunction.Arn
bucket_name:
boolean_property: true
integer_property: 14
```
### Other Details
Conducted a short experiment to verify if the `properties` of the custom resources are passed with the correct data type if the Custom Resource is applied from local machine, instead of using AILM. Used the following Custom Resource configuration in a test account to verify that.
```yaml
Resources:
SampleS3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ferdous-sample-bucket-name-5
DeletionPolicy: Delete
S3CustomResource:
Type: Custom::S3CustomResource
Properties:
ServiceToken: !GetAtt AWSLambdaFunction.Arn
bucket_name: ferdous-sample-bucket-name-5 ## Additional parameter here
boolean_property: true ## Additional parameter here
integer_property: 14 ## Additional parameter here
AWSLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Description: "Empty an S3 bucket!"
FunctionName: !Sub '${AWS::StackName}-${AWS::Region}-lambda'
Handler: index.handler
Role: !GetAtt AWSLambdaExecutionRole.Arn
Timeout: 360
Runtime: python3.8
Code:
ZipFile: |
import boto3
import cfnresponse
def handler(event, context):
# Get request type
the_event = event['RequestType']
print("The event is: ", str(the_event))
response_data = {}
s3 = boto3.client('s3')
# Retrieve parameters (bucket name)
bucket_name = event['ResourceProperties']['bucket_name']
response_data["bucket_name"] = str(type(event['ResourceProperties']['bucket_name']))
response_data["boolean_property"] = str(type(event['ResourceProperties']['boolean_property']))
response_data["integer_property"] = str(type(event['ResourceProperties']['integer_property']))
try:
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
except Exception as e:
print("Execution failed...")
print(str(e))
response_data['Data'] = str(e)
cfnresponse.send(event, context, cfnresponse.FAILED, response_data)
AWSLambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action:
- sts:AssumeRole
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Version: '2012-10-17'
Path: "/"
Policies:
- PolicyDocument:
Statement:
- Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Effect: Allow
Resource: arn:aws:logs:*:*:*
Version: '2012-10-17'
PolicyName: !Sub ${AWS::StackName}-${AWS::Region}-AWSLambda-CW
- PolicyDocument:
Statement:
- Action:
- s3:PutObject
- s3:DeleteObject
- s3:List*
Effect: Allow
Resource:
- !Sub arn:aws:s3:::${SampleS3Bucket}
- !Sub arn:aws:s3:::${SampleS3Bucket}/*
Version: '2012-10-17'
PolicyName: !Sub ${AWS::StackName}-${AWS::Region}-AWSLambda-S3
RoleName: !Sub ${AWS::StackName}-${AWS::Region}-AWSLambdaExecutionRole
```
After applying it from a local setup to the `testaccount-ferdous`, the following logs appear
```
{
"Status": "SUCCESS",
"Reason": "See the details in CloudWatch Log Stream: 2022/01/18/[$LATEST]b02a0c9fdb9a4737b2c37f8752eb5590",
"PhysicalResourceId": "2022/01/18/[$LATEST]b02a0c9fdb9a4737b2c37f8752eb5590",
"StackId": "arn:aws:cloudformation:eu-west-1:438171217252:stack/custom-resource-test-5/a0404ef0-783d-11ec-99aa-06a8de4b0d85",
"RequestId": "4fe1e63e-dc02-4087-a1e8-541efe0ad011",
"LogicalResourceId": "S3CustomResource",
"NoEcho": false,
"Data": {
"bucket_name": "",
"boolean_property": "",
"integer_property": ""
}
}
```
The properties are always returning String even though they have been passed as String, Boolean and Integer.
```
S3CustomResource:
Type: Custom::S3CustomResource
Properties:
ServiceToken: !GetAtt AWSLambdaFunction.Arn
bucket_name: ferdous-sample-bucket-name-5 ## Additional parameter here
boolean_property: true ## Additional parameter here
integer_property: 14 ## Additional parameter here
```
Although the AWS Cloud formation docs although mentions that the `ResourceProperties` is a JSON Object. And JSON object can contain Integers and Booleans.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-requests.html
The next course of action is to look into the issues of CloudFormation to see if it's already reported. If not, then create an issue and follow up on that. And for the time being, keep using the manual type mapping that we have been doing in the CustomResource Lambda Functions.
Contributor guide
Research direction
Start with the AWS CloudFormation custom-resource request documentation and reproduce the issue using the YAML template and Python Lambda handler shown here. Compare the types in event['ResourceProperties'] with the declared boolean and integer values; done would require determining whether this is an AWS service behavior and documenting or escalating the result, since no repository file or test is identified.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, python
- Domain
- cloud, infrastructure
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100