Question

I am creating an instance through the cloud formation script.

The only way I found to attach an OS partition was through "BlockDeviceMappings" property. (I've tried to use "Volumes" property before, but the instance could not be mounted, the system told me that /dev/sda was already mapped and rolled back the instance creation)

Here is the relevant portion of my template:

  "Resources" :
  {
    "Ec2Instance" :
    {
      "Type" : "AWS::EC2::Instance",
      "Properties" :
      {
        "BlockDeviceMappings" :
        [{
          "DeviceName" : "/dev/sda",
          "Ebs" :
          {
            "VolumeSize" : { "Ref" : "RootVolumeSize" },
            "SnapshotId" :
            { "Fn::FindInMap" : [ "RegionMap",
              { "Ref" : "AWS::Region" }, "RootVolumeSnapshotId" ]
            }
          }
        }],
        ...
       }
     }

My question is, how can I tag the Ebs volume, that I am creating here with "BlockDeviceMappings" property? I did not find the obvious solution.

Thanks.

Was it helpful?

Solution

Was able to make it work through an AWS CLI interface, IAM role, and UserData initialization.

Added this to AWS::EC2::Instance:Properties:UserData

{ "Fn::Base64" : { "Fn::Join" : [ "\n", [
  "#!/bin/bash",
  "set -eux",
  "exec > >(tee /tmp/user-data.log | logger -t user-data -s 2>/dev/console) 2>&1",
  { "Fn::Join" : [ "", [
    "AWS_STACK_NAME='", { "Ref" : "AWS::StackName" }, "'"
  ]]},
  { "Fn::Join" : [ "", [
    "AWS_ROOT_VOLUME_SNAPSHOT_ID='",
      { "Fn::FindInMap" :
         [ "RegionMap", { "Ref" : "AWS::Region" }, "RootVolumeSnapshotId" ]},
      "'"
  ]]},
  "AWS_INSTANCE_ID=$( curl http://169.254.169.254/latest/meta-data/instance-id )",
  "",
  "AWS_HOME=/opt/aws",
  "AWS_BIN_DIR=\"${AWS_HOME}/bin\"",
  "export EC2_HOME=\"${AWS_HOME}/apitools/ec2\"",
  "export JAVA_HOME=/etc/alternatives/jre_1.7.0",
  "",
  "ROOT_DISK_ID=$(",
  "    \"${AWS_BIN_DIR}/ec2-describe-volumes\" \\",
  "        --filter \"attachment.instance-id=${AWS_INSTANCE_ID}\" \\",
  "        --show-empty-fields \\",
  "      | grep '^VOLUME' \\",
  "      | awk '{printf \"%s,%s\\n\", $4, $2}' \\",
  "      | grep '^${AWS_ROOT_VOLUME_SNAPSHOT_ID}' \\",
  "      | cut --delimiter=, --fields=2",
  "    exit ${PIPESTATUS[0]}",
  "  )",
  "\"${AWS_BIN_DIR}/ec2-create-tags \\",
  "  \"${ROOT_DISK_ID}\" \\",
  "  --tag \"Name=${AWS_STACK_NAME}-root\"",
  ""
]]}}

Also have to add a reference to an IAM role that can describe volumes and create tags.

Added this to "Resources" section:

"InstanceProfile" :
{
  "Type" : "AWS::IAM::InstanceProfile",
  "Properties" :
  {
    "Path" : "/",
    "Roles" : [ "ec2-tag-instance" ]
  }
}

Referenced this profile in the Instance resource:

"Ec2Instance" :
{
  "Type" : "AWS::EC2::Instance",
  "Properties" :
  {
    ...
    "IamInstanceProfile" : {"Ref" : "InstanceProfile"},
    ...
  }
}

And in IAM UI create a new Role called ec2-tag-instance, and assign this policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:Describe*",
        "ec2:CreateTags"
      ],
      "Resource": "*"
    }
  ]
}

This said, would be much nicer if BlockDeviceMappings:Ebs had supported Tags element.

OTHER TIPS

Thanks Alex,

I found there's a double quotation mark missing after ec2-create-tags :)

  "\"${AWS_BIN_DIR}/ec2-create-tags\" \\",
  "  \"${ROOT_DISK_ID}\" \\",
  "  --tag \"Name=${AWS_STACK_NAME}-root\"",

Besides, if the instances are not residing in the default us-east-1 region, please also specify "--region REGION" in the ec2-create-tags and ec2-describe-volumes commands.

If your CloudFormation stack is tagged and you want your EC2 attached volumes to copy over the tags from the stack you can use the below UserData value.

Fn::Base64: !Sub |
    #!/bin/bash -xe
    exec > /tmp/part-001.log 2>&1
    # --==Tagging Attached Volumes==--
    TAGS=$(aws cloudformation describe-stacks --stack-name ${AWS::StackName} --query 'Stacks[0].Tags' --region ${AWS::Region})
    EC2_INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
    EBS_IDS=$(aws ec2 describe-volumes --filters Name=attachment.instance-id,Values="$EC2_INSTANCE_ID" --region ${AWS::Region} --query 'Volumes[*].[VolumeId]' --out text | tr "\n" " ")
    aws ec2 create-tags --resources $EBS_IDS --tags "$TAGS" --region ${AWS::Region}
    TAGS=$(echo $TAGS | tr "Key" "key" | tr "Value" "value")
    aws ecs tag-resource --resource-arn arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:cluster/${EcsClusterName} --tags "$TAGS"

  1. Write all stdout and stderr to file for debugging:

    `exec > /tmp/part-001.log 2>&1

  2. (requires permission) Get the tags from the stack:

    TAGS=$(aws cloudformation describe-stacks --stack-name ${AWS::StackName} --query 'Stacks[0].Tags' --region ${AWS::Region})

  3. Get the EC2 instance id from the metadata endpoint:

    EC2_INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)

  4. (requires permission) Get the EBS IDS:

    EBS_IDS=$(aws ec2 describe-volumes --filters Name=attachment.instance-id,Values="$EC2_INSTANCE_ID" --region ${AWS::Region} --query 'Volumes[*].[VolumeId]' --out text | tr "\n" " ")

  5. (requires permission) Add tags to the EBS volumes: aws ec2 create-tags --resources $EBS_IDS --tags "$TAGS" --region ${AWS::Region}

  6. Format tags for ECS tagging:

    TAGS=$(echo $TAGS | tr "Key" "key" | tr "Value" "value")

  7. (requires permission) Tag the ECS cluster:

    aws ecs tag-resource --resource-arn arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:cluster/${EcsClusterName} --tags "$TAGS"

The policy should look like this:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ec2:DeleteTags",
                "ec2:CreateTags",
                "ecs:TagResource", 
                "cloudformation:DescribeStacks"
            ],
            "Resource": "*"
        }
    ]
}

You can use CloudFormation template to create resources like CloudWatch event rule and Lambda. The lambda invocation can get triggered not only when there is instance creation event but when updates happen to instance tags.

This is what I did in my ec2 userdata. I think it's much simpler than above answer.

Key=<Your Tag Name>
Value=<Your Tag Value>
Region=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | grep -oP "(?<=\"region\" : \")[^\"]+")

aws ec2 create-tags --resources $(aws ec2 describe-instances --instance-ids $(curl -s http://169.254.169.254/latest/meta-data/instance-id) --region $Region | grep -oP "(?<=\"VolumeId\": \")[^\"]+") --tags Key=$Key,Value=$Value --region $Region
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top