A tool for simplifying swarming of fixing AWS tags

pip install aws-tag-a-day==0.1.10



A tool for simplifying multiple people fixing tags by proposing a tag a day for resources with those missing tags.

Build Status PyPI version


pip install aws-tag-a-day


# Generate configuration file
mkdir -p ~/.config/tagaday/
cat > ~/.config/tagaday/config.yml <<EOY
dynamodb-table-name: 'tag-proposals'
dynamodb-table-region: 'eu-west-2'
- rds
- ec2
- s3
- emr
- us-east-1
- us-west-1
- eu-west-1
- ap-southeast-1
- ap-northeast-1
- Project
- Owner
- Name
- Service
- Availability
- Stack

# Create dynamodb table defined in the above config file.
# If the table exists already, the utility will not overwrite it.

# Start proposing tags


There are a lot of tools for handling batch tagging, but not many tools for aiding in filling in empty tags on a large scale.

The suggested workflow is:

  1. Create configuration file.
  2. Create a DynamoDB table by running tag-a-day-initialise. This is not a destructive operation, and existing tables will not be modified or removed. The utility will throw an error if the table already exists.
  3. Have 1 or more people start using tag-a-day.
  4. Reconcile any divergent or duplicate tags by running tag-a-day-reconcile, and discuss any duplicate tagging suggestions.
  5. Apply the tags to the resources.

NOTE: Steps 4 and 5 are not yet implemented by this utility.


Supported Services

Configuration File & CLI options

Configuration File Key Type CLI Option Type Description
regions: List --regions Comma separated string List of regions to audit resources in
services: List --services Comma separated string List of services to audit
required-tags: List --required-tags Comma separated string List of tags which must be present on all resources
dynamodb-table-name: String --dynamodb-table-name String Name of the DynamoDB table to propose tags to. If used in conjunction with tag-a-day-initialise, this will be the DynamoDB table to be created.
dynamodb-table-region: String --dynamodb-table-region String AWS Region in which to look for --dynamodb-table-name. If used in conjunction with tag-a-day-initialise this will be the region which the DynamoDB table will be created in.
--resource-ids Comma separated string List of resource ids, to filter only for specific resources to propose tags for.
--profile-path String File path pointing to a folder where tag profiles are located
--profile String File name (including .yaml/.yml suffix) point to a particular tagging profile in --profile-path


aws-tag-a-day is built on a plugin architecture, using entry_point in setuptools.

To add more TagHandlers, you can either add classes to this repo, or create a new python package with its own, and hook into the plugin architecture using the tag_a_day.tag_handlers entrypoint.

  1. Create a new class, inheriting from

    from import Service
    class CustomTagHandler(Service): pass
  2. Set a unique name for the Tag handler

    from import Service
    class CustomTagHandler(Service):
  3. Create two stub methods, resources and handler matching the signatures below:

    from import Service
    class VpcTagHandler(Service):
       def resources(self, session):
       def handler(self, resource, expected_tags, region, session, cache, proposals):
  4. Implement resources(...) to return an iterable. If using boto3 resources, this should look like:

      def resources(self, session):
        return session.resource('ec2').vpc.all()

    If using boto3 client, don't forget to implement pagination, and should look like:

      def resources(self, session):
        ec2 = session.client('ec2')
        paginator = ec2.get_paginator('describe_vpcs')
        for page in paginator.paginate():
          for vpc in page:
            yield vpc 
  5. Implement handle(...) to yield a payload describing the tag proposal (example is using boto3.resources):

      def handle(self, vpc, expected_tags, region, session, cache, proposals):
        # This boilerplate logic will handle checking the tags which have already been
        # evaluated for this user.
        evaluated_tags = self._progress.evaluated_tags(vpc.vpc_id)
        vpc_info, missing_tags = \
            self._build_tag_sets(expected_tags, evaluated_tags, vpc.tags)
        # Check if the user has proposed values for all the missing tags 
        if self._progress.has_finished(vpc.vpc_id, expected_tags):
            # Print a skip message
        if any(missing_tags):
          # Print information about this resource, which could be useful 
          # to provide context around tagging.
            ("VpcID", vpc.vpc_id),
          # Allow the user to skip auditing this resource       
          if self._user_skip():
          # Build our user prompt to ask for new tags
          tag_prompt = self._build_tag_prompt(missing_tags)
          for tag_key in missing_tags:
            # Yield a proposal for a new tag key/value pair for the given
            # resource id.
            yield {
              'resource_id': vpc.vpc_id,
              'tag_key': tag_key,
              'tag_value': tag_prompt(tag_key),
  6. Finally, add the entrypoint by extending

    from setuptools import setup
        'tag_a_day.tag_handlers': [
          'vpc = my_package.my_module:VPCTagHandler',