Reputation: 551
I have a CF template where I supply a parameter and a conditional checks it to determine how to construct a bucket name. If it's prod, it should be "name". If it's not prod, it should be "name_environment" like this:
# # # # # # # # # # # # # # # #
# #
# Input Parameters #
# Prefix: t3st-acc0un7-123 #
# Stage: dev #
# #
# Expected S3 Name Output #
# t3st-acc0un7-123-dev #
# t3st-acc0un7-123-dev-2 #
# #
# # # # # # # # # # # # # # # #
# #
# Input Parameters #
# Prefix: t3st-acc0un7-123 #
# Stage: prod #
# #
# Expected S3 Name Output #
# t3st-acc0un7-123 #
# t3st-acc0un7-123-2 #
# #
# # # # # # # # # # # # # # # #
Here's my template that does this:
Parameters:
Prefix:
Type: String
Default: t3st-acc0un7-123
Stage:
Type: String
AllowedPattern: "([a-z]|[0-9])+"
Conditions:
IsProdStage:
Fn::Equals:
- !Ref Stage
- prod
Resources:
TestBucket:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::If:
- IsProdStage
- !Ref Prefix
- !Join
- '-'
-
- !Ref Prefix
- !Ref Stage
TestBucket2:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::If:
- IsProdStage
- !Join
- '-'
-
- !Ref Prefix
- '2'
- !Join
- '-'
-
- !Ref Prefix
- !Ref Stage
- '2'
In the first sample template, the conditional and join logic are duplicated. I basically want to store the value of the conditional somewhere to call from each subsequent function, instead of duplicating the logic.
In this next example, I try to use custom resources to call a dummy lambda (because ServiceToken is required) so I can set a Value property on the TestCustomResource custom resource based on the condition & input and read from it from the other resources I create.
Parameters:
Prefix:
Type: String
Default: t3st-acc0un7-123
Stage:
Type: String
AllowedPattern: "([a-z]|[0-9])+"
Conditions:
IsProdStage:
Fn::Equals:
- !Ref Stage
- prod
Resources:
TestBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !GetAtt TestCustomResource.Value
TestBucket2:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join
- '-'
-
- !GetAtt TestCustomResource.Value
- 2
TestCustomResource:
Type: Custom::Codswallop
Properties:
ServiceToken: !GetAtt DummyLambda.Arn
Value:
Fn::If:
- IsProdStage
- !Ref Prefix
- !Join
- '-'
-
- !Ref Prefix
- !Ref Stage
DummyLambda:
Type: "AWS::Lambda::Function"
Properties:
Code:
ZipFile: >
print("")
Handler: lambda_function.lambda_handler
Role: !GetAtt DummyRole.Arn
Runtime: python3.6
DummyRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
RoleName: DummyRole
I know it's a little hacky with the dummy lambda, but this seems like it would be very useful functionality to be able to store a computed value for use around the template. The second example gives an error like so: TestCustomResource - Custom Resource failed to stabilize in expected time.
(It's possible the answer is to use multiple templates that depend on previous CF output values or nested templates.)
Upvotes: 0
Views: 798
Reputation: 269101
Do not use a Custom Resource for fake purposes.
The function needs to "call back" to CloudFormation when the operation is done. Your custom resource has no code, so it will never call back, so your template will never complete.
If your first example works, stick with it. Your second option (aside from not working) is difficult for future IT people to understand and maintain.
Always go for easy future maintenance over elegant hacks. (Spoken by a person who has had to maintain other people's elegant hacks.)
Upvotes: 1