Repository: cloud-custodian/cloud-custodian

Issue:
Add support for SSM parameter store
Specifically, provide support for finding resources stored insecurely . . . I'm working on this.


Discussion:

Resolving patch:
diff --git a/c7n/resources/__init__.py b/c7n/resources/__init__.py
--- a/c7n/resources/__init__.py
+++ b/c7n/resources/__init__.py
@@ -87,6 +87,7 @@ def load_resources():
     import c7n.resources.sns
     import c7n.resources.storagegw
     import c7n.resources.sqs
+    import c7n.resources.ssm
     import c7n.resources.support
     import c7n.resources.vpc
     import c7n.resources.waf
diff --git a/c7n/resources/ssm.py b/c7n/resources/ssm.py
new file mode 100644
--- /dev/null
+++ b/c7n/resources/ssm.py
@@ -0,0 +1,36 @@
+# Copyright 2016-2017 Capital One Services, LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+
+from c7n.query import QueryResourceManager
+from c7n.manager import resources
+from c7n.utils import get_retry
+
+
+@resources.register('ssm-parameter')
+class SSMParameter(QueryResourceManager):
+
+    class resource_type(object):
+        service = 'ssm'
+        enum_spec = ('describe_parameters', 'Parameters', None)
+        name = "Name"
+        id = "Name"
+        filter_name = None
+        dimension = None
+        universal_taggable = True
+
+    retry = staticmethod(get_retry(('Throttled',)))
+    permissions = ('ssm:GetParameters',
+                   'ssm:DescribeParameters')


Test patch:
diff --git a/tests/data/placebo/test_ssm_parameter_not_secure/ssm.DeleteParameters_1.json b/tests/data/placebo/test_ssm_parameter_not_secure/ssm.DeleteParameters_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ssm_parameter_not_secure/ssm.DeleteParameters_1.json
@@ -0,0 +1,21 @@
+{
+    "status_code": 200, 
+    "data": {
+        "InvalidParameters": [
+            "secure-test-value", 
+            "test-value"
+        ], 
+        "DeletedParameters": [], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "af51bb7f-bb9b-43ec-bcdb-1ed8cddda2ef", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "af51bb7f-bb9b-43ec-bcdb-1ed8cddda2ef", 
+                "date": "Thu, 16 Aug 2018 16:55:57 GMT", 
+                "content-length": "79", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ssm_parameter_not_secure/ssm.DescribeParameters_1.json b/tests/data/placebo/test_ssm_parameter_not_secure/ssm.DescribeParameters_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ssm_parameter_not_secure/ssm.DescribeParameters_1.json
@@ -0,0 +1,51 @@
+{
+    "status_code": 200, 
+    "data": {
+       "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "c78732aa-5328-4574-91be-8fcd2ba4bf37", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "c78732aa-5328-4574-91be-8fcd2ba4bf37", 
+                "date": "Thu, 16 Aug 2018 15:48:35 GMT", 
+                "content-length": "2345", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }, 
+        "Parameters": [
+            {
+                "LastModifiedUser": "arn:aws:iam::123456789:user/test-user", 
+                "LastModifiedDate": {
+                    "hour": 6, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 45, 
+                    "microsecond": 517000, 
+                    "year": 2018, 
+                    "day": 20, 
+                    "minute": 37
+                }, 
+                "Version": 1, 
+                "Type": "String", 
+                "Name": "test-name"
+            }, 
+            {
+                "KeyId": "alias/aws/ssm", 
+                "Name": "secure-test-name", 
+                "LastModifiedDate": {
+                    "hour": 11, 
+                    "__class__": "datetime", 
+                    "month": 8, 
+                    "second": 35, 
+                    "microsecond": 951000, 
+                    "year": 2018, 
+                    "day": 16, 
+                    "minute": 48
+                }, 
+                "Version": 3, 
+                "LastModifiedUser": "arn:aws:iam::123456789:user/test-user", 
+                "Type": "SecureString"
+            }
+        ]
+    }
+}
diff --git a/tests/data/placebo/test_ssm_parameter_not_secure/ssm.PutParameter_1.json b/tests/data/placebo/test_ssm_parameter_not_secure/ssm.PutParameter_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ssm_parameter_not_secure/ssm.PutParameter_1.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200, 
+    "data": {
+        "Version": 4, 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "2f7d96b9-1a3f-4328-81c4-a865aafcf63c", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "2f7d96b9-1a3f-4328-81c4-a865aafcf63c", 
+                "date": "Thu, 16 Aug 2018 15:26:02 GMT", 
+                "content-length": "13", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ssm_parameter_not_secure/ssm.PutParameter_2.json b/tests/data/placebo/test_ssm_parameter_not_secure/ssm.PutParameter_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ssm_parameter_not_secure/ssm.PutParameter_2.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200, 
+    "data": {
+        "Version": 2, 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "0a5ecaf0-954a-493f-ac3b-6009899cf23d", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "0a5ecaf0-954a-493f-ac3b-6009899cf23d", 
+                "date": "Thu, 16 Aug 2018 15:26:02 GMT", 
+                "content-length": "13", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/test_ssm.py b/tests/test_ssm.py
new file mode 100644
--- /dev/null
+++ b/tests/test_ssm.py
@@ -0,0 +1,49 @@
+# Copyright 2017 Capital One Services, LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+from .common import BaseTest, functional
+
+
+class TestSsm(BaseTest):
+
+    @functional
+    def test_ssm_parameter_not_secure(self):
+        session_factory = self.replay_flight_data("test_ssm_parameter_not_secure")
+        client = session_factory().client("ssm")
+
+        client.put_parameter(Name='test-name',
+                             Type='String',
+                             Overwrite=True,
+                             Value='test-value')
+
+        client.put_parameter(Name='secure-test-name',
+                             Type='SecureString',
+                             Overwrite=True,
+                             Value='secure-test-value')
+
+        p = self.load_policy(
+            {
+                "name": "ssm-parameter-not-secure",
+                "resource": "ssm-parameter",
+                "filters": [{"type": "value",
+                             "op": "ne",
+                             "key": "Type",
+                             "value": "SecureString"}]
+            },
+            session_factory=session_factory,
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        self.addCleanup(client.delete_parameters, Names=['test-name', 'secure-test-name'])