How can I tag a root volume from an instance created by CloudFormation?
I want to tag the root volume of my Amazon Elastic Compute Cloud (Amazon EC2) instances that are created through AWS CloudFormation.
Short description
The tag property of the EC2 instance resource doesn't extend to the volumes that are created through CloudFormation. Tagging can restrict the control that you have over your instances. Tagging helps you manage the costs of specific resources and restrict AWS Identity and Access Management (IAM) policies. Tagging also helps you exert similar control over other resources.
Bootstrapping with CloudFormation allows you to tag the Amazon Elastic Block Store (Amazon EBS) root volume of your instance. The bootstrapping method is done through the UserData property of the AWS::EC2::Instance resource. To perform bootstrapping, use AWS Command Line Interface (AWS CLI) commands or standard Windows PowerShell commands after creating your instance.
Note: If you receive errors when running AWS CLI commands, make sure that you’re using the most recent AWS CLI version.
Resolution
Create an instance with a CloudFormation template
1. Open the CloudFormation console.
2. Choose Create Stack, and then choose Design template.
3. In the code editor, on the Parameters tab, choose Template.
4. For Choose template language, choose YAML.
5. Copy either of the following JSON or YAML templates, and then paste that copied template into your code editor.
JSON template:
{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "AWS CloudFormation Sample Template Tagging Root Volumes of EC2 Instances: This template shows you how to automatically tag the root volume of the EC2 instances that are created through the AWS CloudFormation template. This is done through the UserData property of the AWS::EC2::Instance resource. **WARNING** This template creates two Amazon EC2 instances and an IAM role. You will be billed for the AWS resources used if you create a stack from this template.", "Parameters": { "KeyName": { "Type": "AWS::EC2::KeyPair::KeyName", "Description": "Name of an existing EC2 KeyPair to enable SSH access to the ECS instances." }, "InstanceType": { "Description": "EC2 instance type", "Type": "String", "Default": "t2.micro", "AllowedValues": [ "t2.micro", "t2.small", "t2.medium", "t2.large", "m3.medium", "m3.large", "m3.xlarge", "m3.2xlarge", "m4.large", "m4.xlarge", "m4.2xlarge", "m4.4xlarge", "m4.10xlarge", "c4.large", "c4.xlarge", "c4.2xlarge", "c4.4xlarge", "c4.8xlarge", "c3.large", "c3.xlarge", "c3.2xlarge", "c3.4xlarge", "c3.8xlarge", "r3.large", "r3.xlarge", "r3.2xlarge", "r3.4xlarge", "r3.8xlarge", "i2.xlarge", "i2.2xlarge", "i2.4xlarge", "i2.8xlarge" ], "ConstraintDescription": "Please choose a valid instance type." }, "InstanceAZ": { "Description": "EC2 AZ.", "Type": "AWS::EC2::AvailabilityZone::Name", "ConstraintDescription": "Must be the name of an Availability Zone." }, "WindowsAMIID": { "Description": "The Latest Windows 2016 AMI taken from the public Systems Manager Parameter Store", "Type": "AWS::SSM::Parameter::Value<String>", "Default": "/aws/service/ami-windows-latest/Windows_Server-2016-English-Full-Base" }, "LinuxAMIID": { "Description": "The Latest Amazon Linux 2 AMI taken from the public Systems Manager Parameter Store", "Type": "AWS::SSM::Parameter::Value<String>", "Default": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" } }, "Resources": { "WindowsInstance": { "Type": "AWS::EC2::Instance", "Properties": { "ImageId": { "Ref": "WindowsAMIID" }, "InstanceType": { "Ref": "InstanceType" }, "AvailabilityZone": { "Ref": "InstanceAZ" }, "IamInstanceProfile": { "Ref": "InstanceProfile" }, "KeyName": { "Ref": "KeyName" }, "UserData": { "Fn::Base64": { "Fn::Join": [ "", [ "<powershell>\n", "try {\n", "$AWS_AVAIL_ZONE=(Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/placement/availability-zone' -UseBasicParsing).Content\n ", "$AWS_REGION=$AWS_AVAIL_ZONE.Substring(0,$AWS_AVAIL_ZONE.length-1)\n ", "$AWS_INSTANCE_ID=(Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/instance-id' -UseBasicParsing).Content\n ", "$ROOT_VOLUME_IDS=((Get-EC2Instance -Region $AWS_REGION -InstanceId $AWS_INSTANCE_ID).Instances.BlockDeviceMappings | where-object DeviceName -match '/dev/sda1').Ebs.VolumeId\n ", "$tag = New-Object Amazon.EC2.Model.Tag\n ", "$tag.key = \"MyRootTag\"\n ", "$tag.value = \"MyRootVolumesValue\"\n ", "New-EC2Tag -Resource $ROOT_VOLUME_IDS -Region $AWS_REGION -Tag $tag\n", "}\n", "catch {\n", "Write-Output $PSItem\n", "}\n", "</powershell>\n" ] ] } }, "Tags": [ { "Key": "Name", "Value": { "Ref": "AWS::StackName" } } ], "BlockDeviceMappings": [ { "DeviceName": "/dev/sdm", "Ebs": { "VolumeType": "io1", "Iops": "200", "DeleteOnTermination": "true", "VolumeSize": "10" } } ] } }, "LinuxInstance": { "Type": "AWS::EC2::Instance", "Properties": { "ImageId": { "Ref": "LinuxAMIID" }, "InstanceType": { "Ref": "InstanceType" }, "AvailabilityZone": { "Ref": "InstanceAZ" }, "IamInstanceProfile": { "Ref": "InstanceProfile" }, "KeyName": { "Ref": "KeyName" }, "UserData": { "Fn::Base64": { "Fn::Join": [ "", [ "#!/bin/sh\n", "AWS_AVAIL_ZONE=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone)\n", "AWS_REGION=${AWS_AVAIL_ZONE::-1}\n", "AWS_INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id)\n", "ROOT_VOLUME_IDS=$(aws ec2 describe-instances --region $AWS_REGION --instance-id $AWS_INSTANCE_ID --output text --query Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId)\n", "aws ec2 create-tags --resources $ROOT_VOLUME_IDS --region $AWS_REGION --tags Key=MyRootTag,Value=MyRootVolumesValue\n" ] ] } }, "Tags": [ { "Key": "Name", "Value": { "Ref": "AWS::StackName" } } ], "BlockDeviceMappings": [ { "DeviceName": "/dev/sdm", "Ebs": { "VolumeType": "io1", "Iops": "200", "DeleteOnTermination": "true", "VolumeSize": "10" } } ] } }, "InstanceRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "ec2.amazonaws.com" ] }, "Action": [ "sts:AssumeRole" ] } ] }, "Path": "/", "Policies": [ { "PolicyName": "taginstancepolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ec2:Describe*" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "ec2:CreateTags" ], "Resource": [ { "Fn::Sub": "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:volume/*" }, { "Fn::Sub": "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:instance/*" } ] } ] } } ] } }, "InstanceProfile": { "Type": "AWS::IAM::InstanceProfile", "Properties": { "Path": "/", "Roles": [ { "Ref": "InstanceRole" } ] } } } }
YAML template:
AWSTemplateFormatVersion: 2010-09-09 Description: >- AWS CloudFormation Sample Template Tagging Root Volumes of EC2 Instances: This template shows you how to automatically tag the root volume of the EC2 instances that are created through the AWS CloudFormation template. This is done through the UserData property of the AWS::EC2::Instance resource. **WARNING** This template creates two Amazon EC2 instances and an IAM role. You will be billed for the AWS resources used if you create a stack from this template. Parameters: KeyName: Type: 'AWS::EC2::KeyPair::KeyName' Description: Name of an existing EC2 KeyPair to enable SSH access to the ECS instances. InstanceType: Description: EC2 instance type Type: String Default: t2.micro AllowedValues: - t2.micro - t2.small - t2.medium - t2.large - m3.medium - m3.large - m3.xlarge - m3.2xlarge - m4.large - m4.xlarge - m4.2xlarge - m4.4xlarge - m4.10xlarge - c4.large - c4.xlarge - c4.2xlarge - c4.4xlarge - c4.8xlarge - c3.large - c3.xlarge - c3.2xlarge - c3.4xlarge - c3.8xlarge - r3.large - r3.xlarge - r3.2xlarge - r3.4xlarge - r3.8xlarge - i2.xlarge - i2.2xlarge - i2.4xlarge - i2.8xlarge ConstraintDescription: Please choose a valid instance type. InstanceAZ: Description: EC2 AZ. Type: 'AWS::EC2::AvailabilityZone::Name' ConstraintDescription: Must be the name of an Availability Zone. WindowsAMIID: Description: >- The Latest Windows 2016 AMI taken from the public Systems Manager Parameter Store Type: 'AWS::SSM::Parameter::Value<String>' Default: /aws/service/ami-windows-latest/Windows_Server-2016-English-Full-Base LinuxAMIID: Description: >- The Latest Amazon Linux 2 AMI taken from the public Systems Manager Parameter Store Type: 'AWS::SSM::Parameter::Value<String>' Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 Resources: WindowsInstance: Type: 'AWS::EC2::Instance' Properties: ImageId: !Ref WindowsAMIID InstanceType: !Ref InstanceType AvailabilityZone: !Ref InstanceAZ IamInstanceProfile: !Ref InstanceProfile KeyName: !Ref KeyName UserData: !Base64 'Fn::Join': - '' - - | <powershell> - | try { - >- $AWS_AVAIL_ZONE=(Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/placement/availability-zone' -UseBasicParsing).Content - |- $AWS_REGION=$AWS_AVAIL_ZONE.Substring(0,$AWS_AVAIL_ZONE.length-1) - >- $AWS_INSTANCE_ID=(Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/instance-id' -UseBasicParsing).Content - >- $ROOT_VOLUME_IDS=((Get-EC2Instance -Region $AWS_REGION -InstanceId $AWS_INSTANCE_ID).Instances.BlockDeviceMappings | where-object DeviceName -match '/dev/sda1').Ebs.VolumeId - |- $tag = New-Object Amazon.EC2.Model.Tag - |- $tag.key = "MyRootTag" - |- $tag.value = "MyRootVolumesValue" - > New-EC2Tag -Resource $ROOT_VOLUME_IDS -Region $AWS_REGION -Tag $tag - | } - | catch { - | Write-Output $PSItem - | } - | </powershell> Tags: - Key: Name Value: !Ref 'AWS::StackName' BlockDeviceMappings: - DeviceName: /dev/sdm Ebs: VolumeType: io1 Iops: '200' DeleteOnTermination: 'true' VolumeSize: '10' LinuxInstance: Type: 'AWS::EC2::Instance' Properties: ImageId: !Ref LinuxAMIID InstanceType: !Ref InstanceType AvailabilityZone: !Ref InstanceAZ IamInstanceProfile: !Ref InstanceProfile KeyName: !Ref KeyName UserData: !Base64 'Fn::Join': - '' - - | #!/bin/sh - > AWS_AVAIL_ZONE=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone) - | AWS_REGION=${AWS_AVAIL_ZONE::-1} - > AWS_INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id) - > ROOT_VOLUME_IDS=$(aws ec2 describe-instances --region $AWS_REGION --instance-id $AWS_INSTANCE_ID --output text --query Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId) - > aws ec2 create-tags --resources $ROOT_VOLUME_IDS --region $AWS_REGION --tags Key=MyRootTag,Value=MyRootVolumesValue Tags: - Key: Name Value: !Ref 'AWS::StackName' BlockDeviceMappings: - DeviceName: /dev/sdm Ebs: VolumeType: io1 Iops: '200' DeleteOnTermination: 'true' VolumeSize: '10' InstanceRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' Path: / Policies: - PolicyName: taginstancepolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - 'ec2:Describe*' Resource: '*' - Effect: Allow Action: - 'ec2:CreateTags' Resource: - !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:volume/*' - !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:instance/*' InstanceProfile: Type: 'AWS::IAM::InstanceProfile' Properties: Path: / Roles: - !Ref InstanceRole
6. In the UserData section of the template, update --tags Key=Name,Value=newAMI to match your requirements for a Linux instance. For a Windows instance, update $tag.key="MyRootTag" and $tag.value="MyRootVolumesValue". See the following UserData section examples for Linux and Windows.
Linux example:
#Linux UserData UserData: Fn::Base64: !Sub | #!/bin/bash AWS_AVAIL_ZONE=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone) AWS_REGION="`echo \"$AWS_AVAIL_ZONE\" | sed 's/[a-z]$//'`" AWS_INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id) ROOT_VOLUME_IDS=$(aws ec2 describe-instances --region $AWS_REGION --instance-id $AWS_INSTANCE_ID --output text --query Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId) aws ec2 create-tags --resources $ROOT_VOLUME_IDS --region $AWS_REGION --tags Key=MyRootTag,Value=MyRootVolumesValue
Windows example:
#Windows UserData with standard Powershell commands (no AWS CLI installed) UserData: Fn::Base64: !Sub | <powershell> try { $AWS_AVAIL_ZONE=(Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/placement/availability-zone' -UseBasicParsing).Content $AWS_REGION=$AWS_AVAIL_ZONE.Substring(0,$AWS_AVAIL_ZONE.length-1) $AWS_INSTANCE_ID=(Invoke-WebRequest -Uri 'http://169.254.169.254/latest/meta-data/instance-id' -UseBasicParsing).Content $ROOT_VOLUME_IDS=((Get-EC2Instance -Region $AWS_REGION -InstanceId $AWS_INSTANCE_ID).Instances.BlockDeviceMappings | where-object DeviceName -match '/dev/sda1').Ebs.VolumeId $tag = New-Object Amazon.EC2.Model.Tag $tag.key = "MyRootTag" $tag.value = "MyRootVolumesValue" New-EC2Tag -Resource $ROOT_VOLUME_IDS -Region $AWS_REGION -Tag $tag } catch { Write-Output $PSItem } </powershell>
Important: To use the AWS CLI commands with UserData, you must install the AWS CLI within the Amazon Machine Image (AMI) of your EC2 instances. The AWS CLI is installed by default on all Amazon Linux AMIs. You must also attach an instance profile to your EC2 instances. The instance profile includes the permissions to call the ec2:DescribeInstances and ec2:CreateTags APIs only on EC2 volumes and instances within the AWS Region and account.
7. Choose the Create stack icon.
8. For Stack name, enter a name for your stack.
9. In the Parameters section, enter the appropriate information based on the needs of your environment, including your instance type, EC2 key pair, and AMI.
10. Choose Next.
11. In the Options section, enter the appropriate information for your stack, and then choose Next.
12. To enable the CloudFormation stack to create an IAM resource, select the "I acknowledge that AWS CloudFormation might create IAM resources" check box.
13. Choose Create.
Tag the root volume of the instance
1. Open the Amazon EC2 console.
2. In the navigation pane, in the Elastic Block Store section, choose Volumes.
3. In the Filter field, enter the tag that you set in the CloudFormation stack to confirm that the volume was tagged.
Relevant content
- asked 3 years agolg...
- asked 3 years agolg...
- asked 3 years agolg...
- asked 3 years agolg...
- AWS OFFICIALUpdated 2 months ago
- AWS OFFICIALUpdated 2 months ago
- AWS OFFICIALUpdated a month ago