Bulk tagging all instances in an Auto Scaling Group (ASG) using AWS CLI and JMESPath Expressions
Tags: aws, ec2, tagging, devops
Published: 2023-09-05 08:23:20

---
title: Bulk tagging all instances in an Auto Scaling Group (ASG) using AWS CLI and JMESPath Expressions
published: true
date: 2023-09-04 00:00:00 UTC
tags: aws,ec2,tagging,DevOps
canonical_url: https://sathyasays.com/2023/09/04/bulk-tag-instances-in-asg-aws-cli/
cover_image: https://i.sathyabh.at/ss/aws-cli-tags.jpg
---

_Cover image generated by [Stable Diffusion Online](https://stablediffusionweb.com/) with the prompt: applying name tags to a server in a data center._

### Why?

I wanted to tag all the instances in an [AWS Auto Scaling Group (ASG)](https://aws.amazon.com/ec2/autoscaling/) with some tags that would be used for reporting. The first order of business was to update the ASG with the relevant tags. This was done by applying the tag to the ASG using Infrastructure as Code (Terraform, in this case) so that all new instances launched would get the tag. However, the existing nodes would not inherit the tags.

While I could recycle the nodes, [instance refresh makes it](https://docs.aws.amazon.com/autoscaling/ec2/userguide/asg-instance-refresh.html) that much easier. Still, I figured bulk tagging the instances in the ASG was a simpler way to set the tags.

### Filtering with AWS CLI

Since I wanted to tag instances of a specific ASG, I tried to use `jq` on the output of the `aws ec2 describe-auto-scaling-groups` command and further slice and dice the data. This was getting too painful, so I started to dig into AWS CLI’s docs and found out that the AWS CLI can [apply client-side filtering](https://docs.aws.amazon.com/cli/latest/userguide/cli-usage-filter.html#cli-usage-filter-client-side) using the `--query` option and [server-side filtering](https://docs.aws.amazon.com/cli/latest/userguide/cli-usage-filter.html#cli-usage-filter-server-side) using the `--filter` option.

### Querying Auto Scaling Group Instances

The `aws autoscaling describe-auto-scaling-groups` command has a response structure as shown below:

```json
[
    {
        "AutoScalingGroupName": "name",
        "AutoScalingGroupARN": "arn",
        "LaunchConfigurationName": "lc-name",
        "MinSize": 1,
        "MaxSize": 1,
        "DesiredCapacity": 1,
        "DefaultCooldown": 300,
        "AvailabilityZones": [
            "ap-southeast-1a",
            "ap-southeast-1c"
        ],
        "LoadBalancerNames": [],
        "TargetGroupARNs": [],
        "HealthCheckType": "EC2",
        "HealthCheckGracePeriod": 0,
        "Instances": [
            {
                "InstanceId": "",
                "InstanceType": "",
                "AvailabilityZone": "",
                "LifecycleState": "",
                "HealthStatus": "",
                "LaunchConfigurationName": "",
                "ProtectedFromScaleIn": false
            }
        ],
        "CreatedTime": "",
        "SuspendedProcesses": [],
        "VPCZoneIdentifier": "",
        "EnabledMetrics": [],
        "Tags": [],
        "TerminationPolicies": [
            "Default"
        ],
        "NewInstancesProtectedFromScaleIn": false,
        "ServiceLinkedRoleARN": "arn",
        "TrafficSources": []
    }
]

```

To query, the AWS CLI uses expressions created using [JMESPath Syntax](https://jmespath.org/). From the response above, we want to select the instance IDs of a specific Auto Scaling Group. In JMESPath query, the question mark `?` is used to filter and select elements. Thus, to filter based on AutoScaling Group name, the JMESPath Expression would be as shown below, replacing `asg-name` with the actual name of the Auto Scaling Group:

```bash
AutoScalingGroups[?AutoScalingGroupName==`asg-name`]
```

### Filtering Instances Based on Auto Scaling Group

Since I further want to select only the instance IDs, the JMESPath expression of the instance ID `.Instances[*].InstanceId` can be chained to the above query condition. Thus, the command to fetch the instance IDs from a specific Auto Scaling Group becomes as shown below, taking care to replace `region` and `asg-name` with the region name and the ASG name, respectively.

```bash
aws autoscaling describe-auto-scaling-groups --region region --query 'AutoScalingGroups[?AutoScalingGroupName==`asg-name`].Instances[*].InstanceId'
```

Now that there’s a list of instance IDs, these IDs can be passed over to the `aws ec2 create-tags` command to apply the tag to all the instances.

```bash
aws ec2 create-tags --region ap-southeast-1 --tags Key=tagKey,Value=tagValue --resources $(aws autoscaling describe-auto-scaling-groups --region region --query 'AutoScalingGroups[?AutoScalingGroupName==`asg-name`].Instances[*].InstanceId' --output text)
```

### Verifying the results

Applying what I learned above, I verified that all the instances have been correctly tagged by using the below command, taking care to replace region and asg-name with the region name and the ASG name, respectively.

```bash
aws ec2 describe-instances --region ap-southeast-1 --filters "Name=tag:aws:autoscaling:groupName,Values='asg-name'" --query "Reservations[].Instances[].{Instance:InstanceId,Name:Tags[?Key=='Name']|[0].Value}" --output table

----------------------
| DescribeInstances  |
+--------------------+--------------+
| Instance           | Name         |
+-------------------+---------------+
| i-0xdeadbeef456242 | asg-nme-1234 |
| i-0xdeadbeef454142 | asg-nme-1235 |
+-----------------------------------+
```

In the above command, the `--filter` option applies server-side filtering and fetches the instances that have the tag, `tag:aws:autoscaling:groupName` as the tag key and `asg-name` as the tag value. This is then chained with the client-side filter `--query` to display the instance ID and the name of the instance by fetching the tag with key Name.