I have a bash script that I want to run on my instances but I only want the second portion of the script to run if a value is true. Also, I would prefer to not have the if statement take place in the script.
Parameters:
#TestParameter = TRUE
Resource:
UserData:
Fn::Sub: |
echo "This is a test example"
#If TestParameter is true:
echo "Only is parameter is true"
In my opinion, it's better to construct Sub the parameter into the script:
UserData:
Fn::Sub: |
echo "This is a test example"
if ${TestParameter}; then
echo "Only is parameter is true"
fi
But since you don't want to have the if inside the script, you'll have to construct the body of the script in the Cloudformation template. Building strings in CFN is always messy. Try something like this:
UserData:
Fn::Join: ["\n", ["echo 'This is a test example'",
['Fn::If': [!Equals [!Ref TestParameter, "true"], "echo 'Parameter is true'", ""]]]]
Related
I have a commadelimted list and template snippet like:
AWSTemplateFormatVersion: 2010-09-09
Parameters:
IPWhitelist:
Description: Comma-delimited list of CIDR blocks.
Type: CommaDelimitedList
Default: '1.1.1.1/32, 2.2.2.2/32, 3.3.3.3/32'
Resources:
EC2Instance:
Type: 'AWS::EC2::Instance'
CreationPolicy:
ResourceSignal:
Timeout: PT5M
Metadata:
'AWS::CloudFormation::Init':
configSets:
foobar_setup:
- configure_foo
configure_foo:
commands:
01_config:
command: !Sub |
IFS=', ' read -r -a array <<< "$(echo ${IPWhitelist} | sed -e 's/[][]//g')"
for IP in "${!array[#]}";do echo $IP >> /foo/bar/allowed_ips.txt;done
I'd like to run the following command in the Init type commands key:
IFS=', ' read -r -a array <<< "$(echo ${IPWhitelist} | sed -e 's/[][]//g')"
for IP in "${array[#]}";do echo $IP >> /etc/squid/allowed_ips.txt;done
so for the array as the doc says
To write a dollar sign and curly braces (${}) literally, add an
exclamation point (!) after the open curly brace, such as ${!Literal}.
AWS CloudFormation resolves this text as ${Literal}.
What about the first line? how to substitute cloudformation parameter inside bash command substitution $()?
Error message:
Template contains errors.: Template error: variable IPWhitelist in
Fn::Sub expression does not resolve to a string
How do I get public IP of EC2 instance and write to a text file in UserData. Tried the following, but as expected it wrote the text literally, rather than resolving it.
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
echo "Public IP: " !GetAtt Bastion.PublicIp > /home/ec2-user/readme.txt
Thanks in advance! (PS : It has to be yaml)
I'm writing the cloudformation template that includes ec2 instance. In userdata block, I want to create a file with some content. In the file, I'm initializing local variable MY_MESSAGE, but next, after the template is deployed this variable is not shown in the file.
original temlate:
EC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-03368e982f317ae48
InstanceType: t2.micro
KeyName: ec2
UserData:
!Base64 |
#!/bin/bash
cat <<EOF > /etc/aws-kinesis/start.sh
#!/bin/sh
MY_MESSAGE="Hello World"
echo $MY_MESSAGE
output file in ec2 instance:
#!/bin/sh
MY_MESSAGE="Hello World"
echo
As you can see variable MY_MESSAGE does not exist in echo block.
You can put EOF in quotes: "EOF":
UserData:
!Base64 |
#!/bin/bash
cat <<"EOF" > /etc/aws-kinesis/start.sh
#!/bin/sh
MY_MESSAGE="Hello World"
echo $MY_MESSAGE
EOF
I am getting this error when trying to use !Sub with variables in UserData in CloudFormation:
Template error: One or more Fn::Sub intrinsic functions don't specify expected arguments. Specify a string as first argument, and an optional second argument to specify a mapping of values to replace in the string
Why do I get this error?
This is my code:
LinuxEC2Instance:
Type: AWS::EC2::Instance
Properties:
UserData:
Fn::Base64: !Sub
- arn_id: !If [TestEnvironment, 'id1', 'id2']
- key: !If [TestEnvironment, 'key1', 'key2']
- |
ARN_ID=${arn_id}
KEY=${key}
echo $ARN_ID
echo $KEY
The first argument to Sub must be string. Thus you should change order in your UserData. For example:
Fn::Base64:
!Sub
- |
#!/bin/bash -xe
ARN_ID=${arn_id}
KEY=${key}
echo $ARN_ID
echo $KEY
- arn_id: !If [TestEnvironment, 'id1', 'id2']
key: !If [TestEnvironment, 'key1', 'key2']
Ok so reading the documentation on the The intrinsic function Fn::Sub. I can use a literal block to specify the user data script.
UserData:
"Fn::Base64":
!Sub |
#!/bin/bash -xe
yum update -y aws-cfn-bootstrap
/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --configsets wordpress_install --region ${AWS::Region}
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerGroup --region ${AWS::Region}
and I can uses a mapping to substitute the ${Domain} variable with the resulting value from the Ref function.
Name: !Sub
- www.${Domain}
- { Domain: !Ref RootDomainName }
But what if I need to use a mapping substitute inside a literal block? Like for example:
"Fn::Base64": !Sub |
<powershell>
Write-host "My Domain is www.${Domain},{ Domain: !Ref RootDomainName }"
</powershell>
This example does not work, and I haven't been unable to find a method that does. Any ideas? The first example makes userdata scripts much easier to write and looks cleaner, but without being able to to use !Ref or !Findinmap it's usefulness is reduced dramatically.
Anyone got any ideas?
Since I arrived to this page through Google, and then found the solution through a different wording here(literally):
How to use !FindInMap in !Sub | userdata section
I'll just add it to save some frustrated searching to others.
Essentially, you have to write your example using the 2nd syntax but a bit more verbosely:
Fn::Base64:
Fn::Sub:
- |+
<powershell>
Write-host "My Domain is www.${Domain}"
</powershell>
- Domain:
Fn::Ref: RootDomainName
You may be able to shorten it a bit, but as the original poster said, mind your commas, quoting and usage of short forms.
P.S.: If the first solution has already served your purpose, you should mark it thus.
In that case you would write it simply as ${RootDomainName}. Local resouces in the same stack can just be mapped by using their resource name.