shantanuo
shantanuo

Reputation: 32326

Using Exported values

I am able to export the keys using this cloudformation template...

https://github.com/shantanuo/cloudformation/blob/master/restricted.template.txt

But how do I import the saved keys directly into "UserData" section of another template? I tried this, but does not work...

aws-ec2-assign-elastic-ip --access-key !Ref {"Fn::ImportValue" : "accessKey" } --secret-key --valid-ips 35.174.198.170

The rest of the template (without access and secret key reference) is working as expected.

https://github.com/shantanuo/cloudformation/blob/master/security.template2.txt

Upvotes: 4

Views: 1519

Answers (2)

WarrenG
WarrenG

Reputation: 1850

So, if this is your script that does the export (sorry, this one is in yaml)

AWSTemplateFormatVersion: '2010-09-09'
Metadata:
  License: Apache-2.0
Description: 'AWS CloudFormation Sample Template'

Parameters:
  NewUsername:
    NoEcho: 'false'
    Type: String
    Description: New account username
    MinLength: '1'
    MaxLength: '41'
    ConstraintDescription: the username must be between 1 and 41 characters
  Password:
    NoEcho: 'true'
    Type: String
    Description: New account password
    MinLength: '1'
    MaxLength: '41'
    ConstraintDescription: the password must be between 1 and 41 characters

Resources:
  CFNUser:
    Type: AWS::IAM::User
    Properties:
      LoginProfile:
        Password: !Ref 'Password'
      UserName : !Ref 'NewUsername'
  CFNAdminGroup:
    Type: AWS::IAM::Group
  Admins:
    Type: AWS::IAM::UserToGroupAddition
    Properties:
      GroupName: !Ref 'CFNAdminGroup'
      Users: [!Ref 'CFNUser']
  CFNAdminPolicies:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: CFNAdmins
      PolicyDocument:
        Statement:
        - Effect: Allow
          Action: '*'
          Resource: '*'
          Condition:
            StringEquals:
              aws:RequestedRegion:
              - ap-south-1
              - us-east-1
      Groups: [!Ref 'CFNAdminGroup']
  CFNKeys:
    Type: AWS::IAM::AccessKey
    Properties:
      UserName: !Ref 'CFNUser'

Outputs:
  AccessKey:
    Value: !Ref 'CFNKeys'
    Description: AWSAccessKeyId of new user
    Export:
      Name: 'accessKey'
  SecretKey:
    Value: !GetAtt [CFNKeys, SecretAccessKey]
    Description: AWSSecretAccessKey of new user
    Export:
      Name: 'secretKey'

Then here is an example of how you would import those values in userdata in the import cloudformation script:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Test instance stack",
  "Parameters": {
    "KeyName": {
      "Description": "The EC2 Key Pair to allow SSH access to the instance",
      "Type": "AWS::EC2::KeyPair::KeyName"
    },
    "BaseImage": {
      "Description": "The AMI to use for machines.",
      "Type": "String"
    },
    "VPCID": {
      "Description": "ID of the VPC",
      "Type": "String"
    },
    "SubnetID": {
      "Description": "ID of the subnet",
      "Type": "String"
    }
  },
  "Resources": {
    "InstanceSecGrp": {
      "Type": "AWS::EC2::SecurityGroup",
      "Properties": {
        "GroupDescription": "Instance Security Group",
        "SecurityGroupIngress": [{
          "IpProtocol": "-1",
          "CidrIp": "0.0.0.0/0"
        }],
        "SecurityGroupEgress": [{
          "IpProtocol": "-1",
          "CidrIp": "0.0.0.0/0"
        }],
        "VpcId": {
          "Ref": "VPCID"
        }
      }
    },
    "SingleInstance": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
        "KeyName": {
          "Ref": "KeyName"
        },
        "ImageId": {
          "Ref": "BaseImage"
        },
        "InstanceType": "t2.micro",
        "Monitoring": "false",
        "BlockDeviceMappings": [{
          "DeviceName": "/dev/xvda",
          "Ebs": {
            "VolumeSize": "20",
            "VolumeType": "gp2"
          }
        }],
        "NetworkInterfaces": [{
          "GroupSet": [{
            "Ref": "InstanceSecGrp"
          }],
          "AssociatePublicIpAddress": "true",
          "DeviceIndex": "0",
          "DeleteOnTermination": "true",
          "SubnetId": {
            "Ref": "SubnetID"
          }
        }],
        "UserData": {
          "Fn::Base64": {
            "Fn::Join": ["", [
              "#!/bin/bash -xe\n",
              "yum install httpd -y\n",
              "sudo sh -c \"echo ",
              { "Fn::ImportValue" : "secretKey" },
              " >> /home/ec2-user/mysecret.txt\" \n",
              "sudo sh -c \"echo ",
              { "Fn::ImportValue" : "accessKey" },
              " >> /home/ec2-user/myaccesskey.txt\" \n"
            ]]
          }
        }
      }
    }
  }
}

In this example I am just echoing the value of the import into a file. If you ssh onto the SingleInstance and check the logs at /var/lib/cloud/instance/scripts/part-001 then you will see what the user data script looks like on the server itself. In my case the contents of that file is (values aren't real for the keys):

#!/bin/bash -xe
yum install httpd -y
sudo sh -c "echo hAc7/TJA123143235ASFFgKWkKSjIC4 >> /home/ec2-user/mysecret.txt"
sudo sh -c "echo AKIAQ123456789123D >> /home/ec2-user/myaccesskey.txt"

Using this as a starting point you can do whatever you need to with the import value.

I've tested all of this with the exact scripts above and it all works.

Upvotes: 4

shantanuo
shantanuo

Reputation: 32326

What is suggested in the comments seems to be correct. I can directly refer to the name (for e.g. 'accessKey' in this case) using ImportValue!

AWSTemplateFormatVersion: '2010-09-09'
Metadata:
  License: Apache-2.0
Description: 'AWS CloudFormation Sample Template'

Resources:
  CFNUser:
    Type: AWS::IAM::User

Outputs:
  AccessKey:
    Value: 
      Fn::ImportValue: accessKey
    Description: AWSAccessKeyId of new user

For e.g. the above template will return the value of accessKey if it is already exported by some other template.

Upvotes: 0

Related Questions