Repository: cloud-custodian/cloud-custodian

Issue:
aws - CW Log Group kms-key filter
**Is your feature request related to a problem? Please describe.**
We would like to encrypt CW Log groups - I see that a filtering on the kmsKeyId value is already possible, but an actual kms-key filter would be helpful for the alias functionality. 

**Describe the solution you'd like**
Implement `kms-key` filter

**Describe alternatives you've considered**
N/A

**Additional context**
N/A


Discussion:

Resolving patch:
diff --git a/c7n/resources/cw.py b/c7n/resources/cw.py
--- a/c7n/resources/cw.py
+++ b/c7n/resources/cw.py
@@ -8,6 +8,7 @@
 from c7n.filters import Filter, MetricsFilter
 from c7n.filters.core import parse_date
 from c7n.filters.iamaccess import CrossAccountAccessFilter
+from c7n.filters.kms import KmsRelatedFilter
 from c7n.query import QueryResourceManager, ChildResourceManager, TypeInfo
 from c7n.manager import resources
 from c7n.resolver import ValuesFrom
@@ -437,6 +438,29 @@ def process_resource_set(self, client, accounts, resources):
         return results
 
 
+@LogGroup.filter_registry.register('kms-key')
+class KmsFilter(KmsRelatedFilter):
+    """
+    Filter a resource by its associcated kms key and optionally the aliasname
+    of the kms key by using 'c7n:AliasName'
+
+    :example:
+
+    .. code-block:: yaml
+
+        policies:
+          - name: cw-log-group-kms-key-filter
+            resource: log-group
+            filters:
+              - type: kms-key
+                key: c7n:AliasName
+                value: "^(alias/cw)"
+                op: regex
+    """
+
+    RelatedIdsExpression = 'kmsKeyId'
+
+
 @LogGroup.action_registry.register('set-encryption')
 class EncryptLogGroup(BaseAction):
     """Encrypt/Decrypt a log group


Test patch:
diff --git a/tests/data/placebo/test_log_group_kms_filter/kms.DescribeKey_1.json b/tests/data/placebo/test_log_group_kms_filter/kms.DescribeKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_log_group_kms_filter/kms.DescribeKey_1.json
@@ -0,0 +1,21 @@
+{
+    "status_code": 200,
+    "data": {
+        "KeyMetadata": {
+            "AWSAccountId": "123456789012",
+            "KeyId": "beedfd51-5158-44ae-a95f-d514f61abfb3",
+            "Arn": "arn:aws:kms:us-east-1:123456789012:key/beedfd51-5158-44ae-a95f-d514f61abfb3",
+            "Enabled": true,
+            "Description": "",
+            "KeyUsage": "ENCRYPT_DECRYPT",
+            "KeyState": "Enabled",
+            "Origin": "AWS_KMS",
+            "KeyManager": "CUSTOMER",
+            "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
+            "EncryptionAlgorithms": [
+                "SYMMETRIC_DEFAULT"
+            ]
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_log_group_kms_filter/kms.DescribeKey_2.json b/tests/data/placebo/test_log_group_kms_filter/kms.DescribeKey_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_log_group_kms_filter/kms.DescribeKey_2.json
@@ -0,0 +1,21 @@
+{
+    "status_code": 200,
+    "data": {
+        "KeyMetadata": {
+            "AWSAccountId": "123456789012",
+            "KeyId": "beedfd51-5158-44ae-a95f-d514f61abfb3",
+            "Arn": "arn:aws:kms:us-east-1:123456789012:key/beedfd51-5158-44ae-a95f-d514f61abfb3",
+            "Enabled": true,
+            "Description": "",
+            "KeyUsage": "ENCRYPT_DECRYPT",
+            "KeyState": "Enabled",
+            "Origin": "AWS_KMS",
+            "KeyManager": "CUSTOMER",
+            "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
+            "EncryptionAlgorithms": [
+                "SYMMETRIC_DEFAULT"
+            ]
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_log_group_kms_filter/kms.ListAliases_1.json b/tests/data/placebo/test_log_group_kms_filter/kms.ListAliases_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_log_group_kms_filter/kms.ListAliases_1.json
@@ -0,0 +1,14 @@
+{
+    "status_code": 200,
+    "data": {
+        "Aliases": [
+            {
+                "AliasName": "alias/cw",
+                "AliasArn": "arn:aws:kms:us-east-1:123456789012:alias/cw",
+                "TargetKeyId": "beedfd51-5158-44ae-a95f-d514f61abfb3"
+            }
+        ],
+        "Truncated": false,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_log_group_kms_filter/kms.ListAliases_2.json b/tests/data/placebo/test_log_group_kms_filter/kms.ListAliases_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_log_group_kms_filter/kms.ListAliases_2.json
@@ -0,0 +1,14 @@
+{
+    "status_code": 200,
+    "data": {
+        "Aliases": [
+            {
+                "AliasName": "alias/cw",
+                "AliasArn": "arn:aws:kms:us-east-1:123456789012:alias/cw",
+                "TargetKeyId": "beedfd51-5158-44ae-a95f-d514f61abfb3"
+            }
+        ],
+        "Truncated": false,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_log_group_kms_filter/kms.ListAliases_3.json b/tests/data/placebo/test_log_group_kms_filter/kms.ListAliases_3.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_log_group_kms_filter/kms.ListAliases_3.json
@@ -0,0 +1,14 @@
+{
+    "status_code": 200,
+    "data": {
+        "Aliases": [
+            {
+                "AliasName": "alias/cw",
+                "AliasArn": "arn:aws:kms:us-east-1:123456789012:alias/cw",
+                "TargetKeyId": "beedfd51-5158-44ae-a95f-d514f61abfb3"
+            }
+        ],
+        "Truncated": false,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_log_group_kms_filter/logs.DescribeLogGroups_1.json b/tests/data/placebo/test_log_group_kms_filter/logs.DescribeLogGroups_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_log_group_kms_filter/logs.DescribeLogGroups_1.json
@@ -0,0 +1,15 @@
+{
+    "status_code": 200,
+    "data": {
+        "logGroups": [
+            {
+                "logGroupName": "test",
+                "metricFilterCount": 0,
+                "arn": "arn:aws:logs:us-east-1:123456789012:log-group:test:*",
+                "storedBytes": 0,
+                "kmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/beedfd51-5158-44ae-a95f-d514f61abfb3"
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_log_group_kms_filter/tagging.GetResources_1.json b/tests/data/placebo/test_log_group_kms_filter/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_log_group_kms_filter/tagging.GetResources_1.json
@@ -0,0 +1,8 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_log_group_kms_filter/tagging.GetResources_2.json b/tests/data/placebo/test_log_group_kms_filter/tagging.GetResources_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_log_group_kms_filter/tagging.GetResources_2.json
@@ -0,0 +1,8 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/test_cwl.py b/tests/test_cwl.py
--- a/tests/test_cwl.py
+++ b/tests/test_cwl.py
@@ -47,6 +47,28 @@ def test_cross_account(self):
         self.assertEqual(len(resources), 1)
         self.assertEqual(resources[0]["c7n:CrossAccountViolations"], ["1111111111111"])
 
+    def test_kms_filter(self):
+        session_factory = self.replay_flight_data('test_log_group_kms_filter')
+        kms = session_factory().client('kms')
+        p = self.load_policy(
+            {
+                'name': 'test-log-group-kms-filter',
+                'resource': 'log-group',
+                'filters': [
+                    {
+                        'type': 'kms-key',
+                        'key': 'c7n:AliasName',
+                        'value': 'alias/cw'
+                    }
+                ]
+            },
+            session_factory=session_factory
+        )
+        resources = p.run()
+        self.assertTrue(len(resources), 1)
+        aliases = kms.list_aliases(KeyId=resources[0]['kmsKeyId'])
+        self.assertEqual(aliases['Aliases'][0]['AliasName'], 'alias/cw')
+
     def test_age_normalize(self):
         factory = self.replay_flight_data("test_log_group_age_normalize")
         p = self.load_policy({