Repository: cloud-custodian/cloud-custodian

Issue:
AWS: Add interpolation to the kms-key tag action
**Is your feature request related to a problem? Please describe.**
I'm not able to use '{now}' in the value when using the tag action for kms-key resources.

**Describe the solution you'd like**
I would like to be able to use '{now}' when tagging kms-key resources.

**Additional context**
I looked at the code a bit. The kms-key resource is using the `UniversalTag` class in `tags.py`, which unlike the `Tag` class in the same file that is used by many other AWS resource types, does not have an `interpolate_values` method. It's not a simple as just replicating that method in `UniversalTag` because the content of the `tags` variable is not in the same format in the two cases.



Discussion:
Thanks @griffint61 for flagging this. The idea of supporting more interpolation during tagging has come up before, and you're raising a good point about consistency among tagging operations also. This may be a good issue to talk through during our weekly community meetings. I'll look to get this on the agenda for the next meeting (Tuesday 8/17 at 2:00 PM Eastern). The agenda will be available [here](https://hackmd.io/@c7n).
I guess I should have mentioned in the original post, if you do attempt to use "{now}" during KMS tagging, it's not that it simply doesn't give the desired result. It blows up on you because the curly braces get passed through to the API, and AWS doesn't like the curly braces in tag values. The result is an exception.


Resolving patch:
diff --git a/c7n/tags.py b/c7n/tags.py
--- a/c7n/tags.py
+++ b/c7n/tags.py
@@ -425,13 +425,20 @@ def process_resource_set(self, client, resource_set, tags):
             Tags=tags,
             DryRun=self.manager.config.dryrun)
 
-    def interpolate_values(self, tags):
+    def interpolate_single_value(self, tag):
+        """Interpolate in a single tag value.
+        """
         params = {
             'account_id': self.manager.config.account_id,
             'now': utils.FormatDate.utcnow(),
             'region': self.manager.config.region}
+        return tag.format(**params)
+
+    def interpolate_values(self, tags):
+        """Interpolate in a list of tags - 'old' ec2 format
+        """
         for t in tags:
-            t['Value'] = t['Value'].format(**params)
+            t['Value'] = self.interpolate_single_value(t['Value'])
 
     def get_client(self):
         return utils.local_session(self.manager.session_factory).client(
@@ -864,6 +871,8 @@ def process(self, resources):
         if msg:
             tags[tag] = msg
 
+        self.interpolate_values(tags)
+
         batch_size = self.data.get('batch_size', self.batch_size)
         client = self.get_client()
 
@@ -876,6 +885,12 @@ def process_resource_set(self, client, resource_set, tags):
         return universal_retry(
             client.tag_resources, ResourceARNList=arns, Tags=tags)
 
+    def interpolate_values(self, tags):
+        """Interpolate in a list of tags - 'new' resourcegroupstaggingapi format
+        """
+        for key in list(tags.keys()):
+            tags[key] = self.interpolate_single_value(tags[key])
+
     def get_client(self):
         # For global resources, manage tags from us-east-1
         region = (getattr(self.manager.resource_type, 'global_resource', None)


Test patch:
diff --git a/tests/test_tags.py b/tests/test_tags.py
--- a/tests/test_tags.py
+++ b/tests/test_tags.py
@@ -4,6 +4,7 @@
 module to test some universal tagging infrastructure not directly exposed.
 """
 import time
+from freezegun import freeze_time
 from mock import MagicMock, call
 
 from c7n.tags import universal_retry, coalesce_copy_user_tags
@@ -48,6 +49,82 @@ def test_tag_action_filter_call(test, tag_action_filter_call):
     test.assertEqual(instance.state['Name'], 'stopping')
 
 
+class TagInterpolationTest(BaseTest):
+    def __tag_interpolation_helper(self, resource_name, resources):
+
+        mock_factory = MagicMock()
+        mock_factory.region = 'us-east-1'
+
+        create_tags = mock_factory().client(resource_name).create_tags
+
+        tag_resources = mock_factory().client('resourcegroupstaggingapi').tag_resources
+        tag_resources.return_value = {}
+
+        policy = self.load_policy(
+            {
+                "name": "test-tag-interpolation",
+                "resource": resource_name,
+                "actions": [
+                    {
+                        "type": "tag",
+                        "tags": {
+                            "tag_account_id": "{account_id}",
+                            "tag_now": "{now}",
+                            "tag_region": "{region}",
+                        },
+                    }
+                ],
+            },
+            session_factory=mock_factory,
+        )
+        policy.resource_manager.actions[0].process(resources)
+
+        return (create_tags, tag_resources)
+
+    @freeze_time("2022-06-27 12:34:56")
+    def test_ec2_tag_interpolation(self):
+        (create_tags, _) = self.__tag_interpolation_helper(
+            'ec2', [{'InstanceId': 'i-12345'}]
+        )
+        create_tags.assert_called_once_with(
+            Resources=['i-12345'],
+            Tags=[
+                {'Key': 'tag_account_id', 'Value': self.account_id},
+                {'Key': 'tag_now', 'Value': '2022-06-27 12:34:56'},
+                {'Key': 'tag_region', 'Value': 'us-east-1'},
+            ],
+            DryRun=False,
+        )
+
+    @freeze_time("2022-06-27 12:34:56")
+    def test_rds_tag_interpolation(self):
+        (_, tag_resources) = self.__tag_interpolation_helper(
+            'rds', [{'DBInstanceIdentifier': 'xxx', 'DBInstanceArn': 'arn:xxx'}]
+        )
+        tag_resources.assert_called_once_with(
+            ResourceARNList=['arn:xxx'],
+            Tags={
+                'tag_account_id': self.account_id,
+                'tag_now': '2022-06-27 12:34:56',
+                'tag_region': 'us-east-1',
+            },
+        )
+
+    @freeze_time("2022-06-27 12:34:56")
+    def test_kms_key_tag_interpolation(self):
+        (_, tag_resources) = self.__tag_interpolation_helper(
+            'kms-key', [{'KeyId': 'xxx', 'Arn': 'arn:xxx'}]
+        )
+        tag_resources.assert_called_once_with(
+            ResourceARNList=['arn:xxx'],
+            Tags={
+                'tag_account_id': self.account_id,
+                'tag_now': '2022-06-27 12:34:56',
+                'tag_region': 'us-east-1',
+            },
+        )
+
+
 class UniversalTagTest(BaseTest):
 
     def test_auto_tag_registration(self):