Repository: cloud-custodian/cloud-custodian

Issue:
Add encryption action for Kinesis Streams



Discussion:

Resolving patch:
diff --git a/c7n/resources/kinesis.py b/c7n/resources/kinesis.py
--- a/c7n/resources/kinesis.py
+++ b/c7n/resources/kinesis.py
@@ -44,6 +44,31 @@ def augment(self, resources):
             self, super(KinesisStream, self).augment(resources))
 
 
+@KinesisStream.action_registry.register('encrypt')
+class Encrypt(Action):
+
+    schema = type_schema('encrypt',
+                         key={'type': 'string'},
+                         required=('key',))
+
+    permissions = ("kinesis:UpdateStream",)
+
+    def process(self, resources):
+        # get KeyId
+        key = "alias/" + self.data.get('key')
+        self.key_id = local_session(self.manager.session_factory).client(
+            'kms').describe_key(KeyId=key)['KeyMetadata']['KeyId']
+        client = local_session(self.manager.session_factory).client('kinesis')
+        for r in resources:
+            if not r['StreamStatus'] == 'ACTIVE':
+                continue
+            client.start_stream_encryption(
+                StreamName=r['StreamName'],
+                EncryptionType='KMS',
+                KeyId=self.key_id
+            )
+
+
 @KinesisStream.action_registry.register('delete')
 class Delete(Action):
 


Test patch:
diff --git a/tests/data/placebo/test_kinesis_encrypt/kinesis.CreateStream_1.json b/tests/data/placebo/test_kinesis_encrypt/kinesis.CreateStream_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_kinesis_encrypt/kinesis.CreateStream_1.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "0be781e4-b255-4a2a-99be-b12937fca1c0", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "0be781e4-b255-4a2a-99be-b12937fca1c0", 
+                "x-amz-id-2": "s1hUdfPPGBxdCKpdVVK1ouGQKCX4NRAvrK0FyoslbGXqLvSnEGXCPQatbByXQBzvaJ10hSxWLOQoi6rOLIcPlg==", 
+                "content-length": "0", 
+                "date": "Tue, 14 Aug 2018 10:01:09 GMT", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_kinesis_encrypt/kinesis.DescribeStream_1.json b/tests/data/placebo/test_kinesis_encrypt/kinesis.DescribeStream_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_kinesis_encrypt/kinesis.DescribeStream_1.json
@@ -0,0 +1,52 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "da712176-7c8c-9817-8878-2c27b42cfde5", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "da712176-7c8c-9817-8878-2c27b42cfde5", 
+                "content-length": "557", 
+                "x-amz-id-2": "06AHhU8LB0t7Fe5/FEpjt53U3dpYCqMT4zUYxxfz6SCR3EoNrRd6HVjAgFaIdWltUhBQHpNa5jZIs+lA7+JdYi9ocBVKnOVm", 
+                "server": "Apache-Coyote/1.1", 
+                "date": "Sat, 12 Aug 2017 14:05:04 GMT", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }, 
+        "StreamDescription": {
+            "HasMoreShards": false, 
+            "RetentionPeriodHours": 24, 
+            "StreamName": "sock-drawer", 
+            "Shards": [
+                {
+                    "ShardId": "shardId-000000000000", 
+                    "HashKeyRange": {
+                        "EndingHashKey": "340282366920938463463374607431768211455", 
+                        "StartingHashKey": "0"
+                    }, 
+                    "SequenceNumberRange": {
+                        "StartingSequenceNumber": "49575983792626831963983842622784465898296159642543194114"
+                    }
+                }
+            ], 
+            "StreamARN": "arn:aws:kinesis:us-east-1:644160558196:stream/sock-drawer", 
+            "EnhancedMonitoring": [
+                {
+                    "ShardLevelMetrics": []
+                }
+            ], 
+            "StreamCreationTimestamp": {
+                "hour": 10, 
+                "__class__": "datetime", 
+                "month": 8, 
+                "second": 43, 
+                "microsecond": 0, 
+                "year": 2017, 
+                "day": 12, 
+                "minute": 0
+            }, 
+            "StreamStatus": "ACTIVE"
+        }
+    }
+}
diff --git a/tests/data/placebo/test_kinesis_encrypt/kinesis.DescribeStream_2.json b/tests/data/placebo/test_kinesis_encrypt/kinesis.DescribeStream_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_kinesis_encrypt/kinesis.DescribeStream_2.json
@@ -0,0 +1,54 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "da712176-7c8c-9817-8878-2c27b42cfde5", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "da712176-7c8c-9817-8878-2c27b42cfde5", 
+                "content-length": "557", 
+                "x-amz-id-2": "06AHhU8LB0t7Fe5/FEpjt53U3dpYCqMT4zUYxxfz6SCR3EoNrRd6HVjAgFaIdWltUhBQHpNa5jZIs+lA7+JdYi9ocBVKnOVm", 
+                "server": "Apache-Coyote/1.1", 
+                "date": "Sat, 12 Aug 2017 14:05:04 GMT", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }, 
+        "StreamDescription": {
+            "HasMoreShards": false, 
+            "RetentionPeriodHours": 24, 
+            "StreamName": "sock-drawer", 
+            "Shards": [
+                {
+                    "ShardId": "shardId-000000000000", 
+                    "HashKeyRange": {
+                        "EndingHashKey": "340282366920938463463374607431768211455", 
+                        "StartingHashKey": "0"
+                    }, 
+                    "SequenceNumberRange": {
+                        "StartingSequenceNumber": "49575983792626831963983842622784465898296159642543194114"
+                    }
+                }
+            ], 
+            "StreamARN": "arn:aws:kinesis:us-east-1:644160558196:stream/sock-drawer", 
+            "EnhancedMonitoring": [
+                {
+                    "ShardLevelMetrics": []
+                }
+            ],
+            "EncryptionType": "KMS",
+            "KeyId": "arn:aws:kms:us-east-1:123456789012:alias/aws/kinesis",
+            "StreamCreationTimestamp": {
+                "hour": 10, 
+                "__class__": "datetime", 
+                "month": 8, 
+                "second": 43, 
+                "microsecond": 0, 
+                "year": 2017, 
+                "day": 12, 
+                "minute": 0
+            }, 
+            "StreamStatus": "ACTIVE"
+        }
+    }
+}
diff --git a/tests/data/placebo/test_kinesis_encrypt/kinesis.ListStreams_1.json b/tests/data/placebo/test_kinesis_encrypt/kinesis.ListStreams_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_kinesis_encrypt/kinesis.ListStreams_1.json
@@ -0,0 +1,22 @@
+{
+    "status_code": 200, 
+    "data": {
+        "StreamNames": [
+            "sock-drawer"
+        ], 
+        "HasMoreStreams": false, 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "cb6d2d76-ae4e-80ed-9964-20278305229d", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "cb6d2d76-ae4e-80ed-9964-20278305229d", 
+                "content-length": "54", 
+                "x-amz-id-2": "/uufCCQh1HZn4rcMzqDFr1B+rfqc/h1+aLrNTiHEeeHoNqX88c1HDT71FjFwapV5y/kAh8IhCfhCkfZWlsULIOz2tlJrgJX0", 
+                "server": "Apache-Coyote/1.1", 
+                "date": "Sat, 12 Aug 2017 14:05:04 GMT", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_kinesis_encrypt/kinesis.StartStreamEncryption_1.json b/tests/data/placebo/test_kinesis_encrypt/kinesis.StartStreamEncryption_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_kinesis_encrypt/kinesis.StartStreamEncryption_1.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "8ed1c773-342d-467a-949c-6fd4be79dc87", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "8ed1c773-342d-467a-949c-6fd4be79dc87", 
+                "x-amz-id-2": "HmGkgfLhGKX0p0ReJ7B+Kmv4R3Nx16Gtxk8jgk27YuTve9pP5JHMB65YNk97wr7SAkxDSXAeiWV1YiS30Q9T5Q==", 
+                "content-length": "0", 
+                "date": "Tue, 14 Aug 2018 10:01:23 GMT", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_kinesis_encrypt/kms.CreateAlias_1.json b/tests/data/placebo/test_kinesis_encrypt/kms.CreateAlias_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_kinesis_encrypt/kms.CreateAlias_1.json
@@ -0,0 +1,15 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "5131a535-c14d-11e7-a1ad-7d6f3a6f809f", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "5131a535-c14d-11e7-a1ad-7d6f3a6f809f", 
+                "content-length": "0", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_kinesis_encrypt/kms.CreateKey_1.json b/tests/data/placebo/test_kinesis_encrypt/kms.CreateKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_kinesis_encrypt/kms.CreateKey_1.json
@@ -0,0 +1,36 @@
+{
+    "status_code": 200, 
+    "data": {
+        "KeyMetadata": {
+            "Origin": "AWS_KMS", 
+            "KeyId": "b5f0e072-1669-48e5-b110-c4989287ba54", 
+            "Description": "West SQS encryption key", 
+            "KeyManager": "CUSTOMER", 
+            "Enabled": true, 
+            "KeyUsage": "ENCRYPT_DECRYPT", 
+            "KeyState": "Enabled", 
+            "CreationDate": {
+                "hour": 6, 
+                "__class__": "datetime", 
+                "month": 11, 
+                "second": 47, 
+                "microsecond": 647000, 
+                "year": 2017, 
+                "day": 4, 
+                "minute": 45
+            }, 
+            "Arn": "arn:aws:kms:us-east-1:644160558196:key/b5f0e072-1669-48e5-b110-c4989287ba54", 
+            "AWSAccountId": "644160558196"
+        }, 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "5115b884-c14d-11e7-a1ad-7d6f3a6f809f", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "5115b884-c14d-11e7-a1ad-7d6f3a6f809f", 
+                "content-length": "358", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_kinesis_encrypt/kms.DeleteAlias_1.json b/tests/data/placebo/test_kinesis_encrypt/kms.DeleteAlias_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_kinesis_encrypt/kms.DeleteAlias_1.json
@@ -0,0 +1,15 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "63b83b42-c14d-11e7-adc3-dd5d0fda9112", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "63b83b42-c14d-11e7-adc3-dd5d0fda9112", 
+                "content-length": "0", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_kinesis_encrypt/kms.DescribeKey_1.json b/tests/data/placebo/test_kinesis_encrypt/kms.DescribeKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_kinesis_encrypt/kms.DescribeKey_1.json
@@ -0,0 +1,36 @@
+{
+    "status_code": 200, 
+    "data": {
+        "KeyMetadata": {
+            "Origin": "AWS_KMS", 
+            "KeyId": "b5f0e072-1669-48e5-b110-c4989287ba54", 
+            "Description": "West SQS encryption key", 
+            "KeyManager": "CUSTOMER", 
+            "Enabled": true, 
+            "KeyUsage": "ENCRYPT_DECRYPT", 
+            "KeyState": "Enabled", 
+            "CreationDate": {
+                "hour": 6, 
+                "__class__": "datetime", 
+                "month": 11, 
+                "second": 47, 
+                "microsecond": 647000, 
+                "year": 2017, 
+                "day": 4, 
+                "minute": 45
+            }, 
+            "Arn": "arn:aws:kms:us-east-1:644160558196:key/b5f0e072-1669-48e5-b110-c4989287ba54", 
+            "AWSAccountId": "644160558196"
+        }, 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "636538c0-c14d-11e7-b9b8-396ffefb6e91", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "636538c0-c14d-11e7-b9b8-396ffefb6e91", 
+                "content-length": "358", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_kinesis_encrypt/kms.DisableKey_1.json b/tests/data/placebo/test_kinesis_encrypt/kms.DisableKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_kinesis_encrypt/kms.DisableKey_1.json
@@ -0,0 +1,15 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "63bfdc6e-c14d-11e7-adc3-dd5d0fda9112", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "63bfdc6e-c14d-11e7-adc3-dd5d0fda9112", 
+                "content-length": "0", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_kinesis_encrypt/tagging.GetResources_1.json b/tests/data/placebo/test_kinesis_encrypt/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_kinesis_encrypt/tagging.GetResources_1.json
@@ -0,0 +1,18 @@
+{
+    "status_code": 200, 
+    "data": {
+        "PaginationToken": "", 
+        "ResourceTagMappingList": [], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "3e252497-7f67-11e7-9836-a96970d9def5", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "3e252497-7f67-11e7-9836-a96970d9def5", 
+                "date": "Sat, 12 Aug 2017 14:05:05 GMT", 
+                "content-length": "50", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/test_kinesis.py b/tests/test_kinesis.py
--- a/tests/test_kinesis.py
+++ b/tests/test_kinesis.py
@@ -54,6 +54,23 @@ def test_stream_delete(self):
         ]
         self.assertEqual(stream["StreamStatus"], "DELETING")
 
+    def test_stream_encrypt(self):
+        factory = self.replay_flight_data("test_kinesis_encrypt")
+        p = self.load_policy(
+            {
+                "name": "kstream",
+                "resource": "kinesis",
+                "filters": [{"StreamName": "sock-drawer"}],
+                "actions": [{"type": "encrypt", "key": "aws/kinesis"}],
+            },
+            session_factory=factory,
+        )
+        p.run()
+        stream = factory().client("kinesis").describe_stream(StreamName="sock-drawer")[
+            "StreamDescription"
+        ]
+        self.assertEqual(stream["EncryptionType"], "KMS")
+
     def test_hose_query(self):
         factory = self.replay_flight_data("test_kinesis_hose_query")
         p = self.load_policy(