0

I am trying to get and export an SSM parameter as an environment variable to an EC2 using the UserData section of Cloudformation.

The script is trying to append for e.g export WHATS_HER_NAME=Sherlyn to the /etc/profile file. But all i see in the /etc/profile is export WHATS_HER_NAME=. The value is not present. I use amazon linux 2 ami.

here is my cloudformation template.

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "Ec2Instance": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
        "IamInstanceProfile": {
          "Ref": "Ec2instanceProfileTest"
        },
        "UserData": {
          "Fn::Base64": {
            "Fn::Join": [
              "\n",
              [
                "#!/bin/bash -xe",
                "yum update -y aws-cfn-bootstrap",
                {
                  "Fn::Sub": "/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource Ec2Instance --configsets default --region ${AWS::Region}"
                },
                {
                  "Fn::Sub": "/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource EC2Instance --region ${AWS::Region}"
                },
                {
                  "Fn::Sub": "echo \"export WHATS_HER_NAME=$(aws ssm get-parameter --name WhatsHerName --region ${AWS::Region} --query 'Parameter.Value')\" >> /etc/profile"
                }
              ]
            ]
          }
        }
      }
    },
    "GetSSMParameterPolicy": {
      "Type": "AWS::IAM::Policy",
      "Properties": {
        "PolicyName": "GetSsmProperty",
        "PolicyDocument": {
          "Statement": [
            {
              "Effect": "Allow",
              "Resource": "arn:aws:ssm:ap-southeast-2:012345678901:parameter/WhatsHerName",
              "Action": [
                "ssm:GetParameters",
                "ssm:GetParameter"
              ]
            },
            {
              "Effect": "Allow",
              "Resource": "*",
              "Action": [
                "ssm:DescribeParameters"
              ]
            }
          ]
        },
        "Roles": [
          {
            "Ref": "InstanceRole"
          }
        ]
      }
    },
    "InstanceRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "ec2.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/"
      }
    },
    "BasicParameter": {
      "Type": "AWS::SSM::Parameter",
      "Properties": {
        "Name": "WhatsHerName",
        "Type": "String",
        "Value": "Sherlyn"
      }
    }
  }
}

any help would be highly appreciated.

Arun Kamalanathan
  • 8,107
  • 4
  • 23
  • 39
  • I would try something like `"Fn::Sub": "echo \"export WHATS_HER_NAME=$(aws ssm get-parameter --name WhatsHerName --region ${AWS::Region} --query 'Parameter.Value' 2> /tmp/find-the-error)\" >> /etc/profile"` then `sudo cat /tmp/find-the-error` once the machine starts up. `$(...)` discards stderr but this will write it to a file, instead, and perhaps give you an idea of why it isn't working. – Michael - sqlbot Jan 04 '20 at 03:02
  • @Michael-sqlbot i tried, but the `/tmp/find-the-error` file not created – Arun Kamalanathan Jan 04 '20 at 14:22
  • That's quite unexpected. – Michael - sqlbot Jan 04 '20 at 15:33

2 Answers2

2

I am not a fan of using JSON for CloudFormation templates so I cannot offer the solution in JSON, but here it is in YAML.

UserData:
  Fn::Base64: !Sub
  - |
    #!/bin/bash -xe
    yum update -y aws-cfn-bootstrap
    /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource Ec2Instance --configsets default --region ${AWS::Region}
    /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource EC2Instance --region ${AWS::Region}
    echo export WHATS_HER_NAME=${WhatsHerNameParameter} >> /etc/profile
  - WhatsHerNameParameter: {{resolve:ssm:WhatsHerName:1}}

You can read more about using AWS Systems Manager Parameter Store Secure String parameters in AWS CloudFormation templates

The snippet above is substituting ${AWS::StackName} and ${AWS::Region} and when it gets to ${WhatsHerNameParameter} it checks for the SSM parameter and substitutes that into the UserData.

This mean that the UserData is complete before it gets to the EC2 instance.

George Rushby
  • 1,305
  • 8
  • 13
  • but are you not getting `WhatsHerNameParameter: {{resolve:ssm:WhatsHerName:1}}` after the `export` statment? – Arun Kamalanathan Jan 04 '20 at 22:56
  • No, because you are substituting it it is resolved before the export (similar to a DependsOn) – George Rushby Jan 05 '20 at 04:35
  • It should be noted here that if you were to use this for a sensitive string like a password, this approach would be inherently insecure as the value could easily show up in cleartext in logs etc. – caseyjmorton Aug 23 '23 at 18:27
0

I see two issues:

  1. Your instance doesn't depend on the parameter so the parameter can be created after the instance. When the instance is trying to read the empty parameter, it's just not there. Use DependsOn: [ BasicParameter ].
  2. You didn't include Ec2instanceProfileTest in your sample code. Are you sure it properly uses GetSSMParameterPolicy? If you run that aws ssm get-parameter command after the stack is done, can you get the value properly? If you can't, there might be a permission error. Check the result.
kichik
  • 33,220
  • 7
  • 94
  • 114