Repository: cloud-custodian/cloud-custodian

Issue:
AWS RedShift Reserved Nodes
**Describe the solution you'd like**
Parity of aws.rds-reserved / aws.ec2-reserved with a new aws.redshift-reserved resource.


**Additional context**
I would like the ability to identify redshift reserved nodes in the same way as rds and ec2.

Discussion:

Patch:
diff --git a/c7n/resources/glue.py b/c7n/resources/glue.py
index e985ebc6d..d46953f56 100644
--- a/c7n/resources/glue.py
+++ b/c7n/resources/glue.py
@@ -528,6 +528,9 @@ class GlueDataCatalog(ResourceManager):
     def resources(self):
         return self.filter_resources(self._get_catalog_encryption_settings())
 
+    def get_resources(self, resource_ids):
+        return [{'CatalogId': self.config.account_id}]
+
 
 @GlueDataCatalog.action_registry.register('set-encryption')
 class GlueDataCatalogEncryption(BaseAction):
diff --git a/c7n/resources/redshift.py b/c7n/resources/redshift.py
index 901ae6ebd..09b7190c4 100644
--- a/c7n/resources/redshift.py
+++ b/c7n/resources/redshift.py
@@ -988,3 +988,18 @@ class RedshiftSnapshotRevokeAccess(BaseAction):
                             ', '.join(
                                 [s['SnapshotIdentifier'] for s in futures[f]]),
                             f.exception()))
+
+
+@resources.register('redshift-reserved')
+class ReservedNode(QueryResourceManager):
+
+    class resource_type(TypeInfo):
+        service = 'redshift'
+        name = id = 'ReservedNodeId'
+        date = 'StartTime'
+        enum_spec = (
+            'describe_reserved_nodes', 'ReservedNodes', None)
+        filter_name = 'ReservedNodes'
+        filter_type = 'list'
+        arn_type = "reserved-nodes"
+        permissions_enum = ('redshift:DescribeReservedNodes',)
diff --git a/c7n/resources/resource_map.py b/c7n/resources/resource_map.py
index 04b466b87..f43d5829f 100644
--- a/c7n/resources/resource_map.py
+++ b/c7n/resources/resource_map.py
@@ -130,6 +130,7 @@ ResourceMap = {
     "aws.redshift": "c7n.resources.redshift.Redshift",
     "aws.redshift-snapshot": "c7n.resources.redshift.RedshiftSnapshot",
     "aws.redshift-subnet-group": "c7n.resources.redshift.RedshiftSubnetGroup",
+    "aws.redshift-reserved": "c7n.resources.redshift.ReservedNode",
     "aws.rest-account": "c7n.resources.apigw.RestAccount",
     "aws.rest-api": "c7n.resources.apigw.RestApi",
     "aws.rest-resource": "c7n.resources.apigw.RestResource",


Test patch:
diff --git a/tests/data/cwe/event-cloud-trail-catalog-put-resource-policy.json b/tests/data/cwe/event-cloud-trail-catalog-put-resource-policy.json
new file mode 100644
index 000000000..69211950a
--- /dev/null
+++ b/tests/data/cwe/event-cloud-trail-catalog-put-resource-policy.json
@@ -0,0 +1,46 @@
+{
+    "version": "0",
+    "id": "4186524f-4444-4444-4444-9c149bfbbbe2",
+    "detail-type": "AWS API Call via CloudTrail",
+    "source": "aws.glue",
+    "account": "644160558196",
+    "time": "2020-06-04T15:18:28Z",
+    "region": "us-east-1",
+    "resources": [],
+    "detail": {
+      "eventVersion": "1.05",
+      "userIdentity": {
+        "type": "IAMUser",
+        "principalId": "qwefqffasdfdfsda",
+        "arn": "arn:aws:iam::644160558196:user/fake@fakeemail.com",
+        "accountId": "644160558196",
+        "accessKeyId": "1345351435",
+        "userName": "fake@fakeemail.com",
+        "sessionContext": {
+          "sessionIssuer": {},
+          "webIdFederationData": {},
+          "attributes": {
+            "mfaAuthenticated": "false",
+            "creationDate": "2020-06-04T15:06:07Z"
+          }
+        }
+      },
+      "eventTime": "2020-06-04T15:18:28Z",
+      "eventSource": "glue.amazonaws.com",
+      "eventName": "PutResourcePolicy",
+      "awsRegion": "us-east-1",
+      "sourceIPAddress": "99.99.99.99",
+      "userAgent": "console.amazonaws.com",
+      "requestParameters": {
+        "policyHashCondition": "qwerwqerewqrewqr==",
+        "policyInJson": "{\n  \"Version\" : \"2012-10-17\",\n  \"Statement\" : [ {\n    \"Effect\" : \"Allow\",\n    \"Principal\" : \"*\",\n    \"Action\" : \"glue:*\",\n    \"Resource\" : \"arn:aws:glue:us-east-1:644160558196:catalog\",\n    \"Condition\": {\n        \"StringEquals\": {\n            \"aws:PrincipalOrgID\": \"o-4amkskbcf3\"\n        }        \n    }\n  } ]\n}"
+      },
+      "responseElements": {
+        "policyHash": "eqwewrewqerwerewr=="
+      },
+      "requestID": "562ac246-0da8-4444-4444-7053b5ecf789",
+      "eventID": "1f5ed731-4444-4a5c-4444-57f55e636789",
+      "eventType": "AwsApiCall"
+    },
+    "debug": true
+  }
\ No newline at end of file
diff --git a/tests/data/cwe/event-cloud-trail-catalog-set-encryption.json b/tests/data/cwe/event-cloud-trail-catalog-set-encryption.json
new file mode 100644
index 000000000..9db76cf06
--- /dev/null
+++ b/tests/data/cwe/event-cloud-trail-catalog-set-encryption.json
@@ -0,0 +1,52 @@
+{
+    "version": "0",
+    "id": "b782ed03-9c34-41d8-4444-bc7c6a5e2cfd",
+    "detail-type": "AWS API Call via CloudTrail",
+    "source": "aws.glue",
+    "account": "644160558196",
+    "time": "2020-06-05T16:10:47Z",
+    "region": "us-east-1",
+    "resources": [],
+    "detail": {
+        "eventVersion": "1.05",
+        "userIdentity": {
+            "type": "AssumedRole",
+            "principalId": "23454235342523:custodian-net-change-encryption",
+            "arn": "arn:aws:sts::644160558196:assumed-role/CloudCustodianRole/custodian-net-change-encryption",
+            "accountId": "644160558196",
+            "accessKeyId": "1525415421513245",
+            "sessionContext": {
+                "sessionIssuer": {
+                    "type": "Role",
+                    "principalId": "23454235342523",
+                    "arn": "arn:aws:iam::644160558196:role/CloudCustodianRole",
+                    "accountId": "644160558196",
+                    "userName": "CloudCustodianRole"
+                },
+                "webIdFederationData": {},
+                "attributes": {
+                    "mfaAuthenticated": "false",
+                    "creationDate": "2020-06-05T16:09:41Z"
+                }
+            }
+        },
+        "eventTime": "2020-06-05T16:10:47Z",
+        "eventSource": "glue.amazonaws.com",
+        "eventName": "PutDataCatalogEncryptionSettings",
+        "awsRegion": "us-east-1",
+        "sourceIPAddress": "35.171.244.77",
+        "userAgent": "CloudCustodian(net-change-encryption)/0.9.2 Python/3.8.3 Linux/4.14.165-102.205.amzn2.x86_64 exec-env/AWS_Lambda_python3.8 Botocore/1.15.49",
+        "requestParameters": {
+            "dataCatalogEncryptionSettings": {
+                "encryptionAtRest": {
+                    "catalogEncryptionMode": "SSE-KMS"
+                }
+            }
+        },
+        "responseElements": null,
+        "requestID": "b86bcd00-4444-4127-a74c-6c5f04d6534c",
+        "eventID": "3cda2b5f-a6c7-4c68-4444-93b0f6e1963c",
+        "eventType": "AwsApiCall"
+    },
+    "debug": true
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_1.json b/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_1.json
new file mode 100644
index 000000000..15a04d4de
--- /dev/null
+++ b/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_1.json
@@ -0,0 +1,14 @@
+{
+    "status_code": 200,
+    "data": {
+        "DataCatalogEncryptionSettings": {
+            "EncryptionAtRest": {
+                "CatalogEncryptionMode": "DISABLED"
+            },
+            "ConnectionPasswordEncryption": {
+                "ReturnConnectionPasswordEncrypted": false
+            }
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_2.json b/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_2.json
new file mode 100644
index 000000000..c80b917c2
--- /dev/null
+++ b/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_2.json
@@ -0,0 +1,15 @@
+{
+    "status_code": 200,
+    "data": {
+        "DataCatalogEncryptionSettings": {
+            "EncryptionAtRest": {
+                "CatalogEncryptionMode": "SSE-KMS",
+                "SseAwsKmsKeyId": "alias/aws/glue"
+            },
+            "ConnectionPasswordEncryption": {
+                "ReturnConnectionPasswordEncrypted": false
+            }
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_catalog_change_encryption_event/glue.PutDataCatalogEncryptionSettings_1.json b/tests/data/placebo/test_catalog_change_encryption_event/glue.PutDataCatalogEncryptionSettings_1.json
new file mode 100644
index 000000000..5b2170a07
--- /dev/null
+++ b/tests/data/placebo/test_catalog_change_encryption_event/glue.PutDataCatalogEncryptionSettings_1.json
@@ -0,0 +1,6 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_catalog_change_rbp_event/glue.GetResourcePolicy_1.json b/tests/data/placebo/test_catalog_change_rbp_event/glue.GetResourcePolicy_1.json
new file mode 100644
index 000000000..7ff26e2ad
--- /dev/null
+++ b/tests/data/placebo/test_catalog_change_rbp_event/glue.GetResourcePolicy_1.json
@@ -0,0 +1,28 @@
+{
+    "status_code": 200,
+    "data": {
+        "PolicyInJson": "{\n  \"Version\" : \"2012-10-17\",\n  \"Statement\" : [ {\n    \"Effect\" : \"Allow\",\n    \"Principal\" : \"*\",\n    \"Action\" : \"glue:*\",\n    \"Resource\" : \"arn:aws:glue:us-east-1:644160558196:catalog\",\n    \"Condition\" : {\n      \"StringEquals\" : {\n        \"aws:PrincipalOrgID\" : \"o-4amkskbcf3\"\n      }\n    }\n  }, {\n    \"Effect\" : \"Allow\",\n    \"Principal\" : \"*\",\n    \"Action\" : \"glue:*\",\n    \"Resource\" : \"arn:aws:glue:us-east-1:644160558196:catalog\",\n    \"Condition\" : {\n      \"StringEquals\" : {\n        \"aws:PrincipalOrgID\" : \"o-4amkskbcf1\"\n      }\n    }\n  } ]\n}",
+        "PolicyHash": "vQI9F/KPAi+BKiDBvNPXSw==",
+        "CreateTime": {
+            "__class__": "datetime",
+            "year": 2020,
+            "month": 6,
+            "day": 5,
+            "hour": 12,
+            "minute": 26,
+            "second": 46,
+            "microsecond": 47000
+        },
+        "UpdateTime": {
+            "__class__": "datetime",
+            "year": 2020,
+            "month": 6,
+            "day": 5,
+            "hour": 12,
+            "minute": 26,
+            "second": 46,
+            "microsecond": 47000
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_catalog_change_rbp_event/glue.GetResourcePolicy_2.json b/tests/data/placebo/test_catalog_change_rbp_event/glue.GetResourcePolicy_2.json
new file mode 100644
index 000000000..7ff26e2ad
--- /dev/null
+++ b/tests/data/placebo/test_catalog_change_rbp_event/glue.GetResourcePolicy_2.json
@@ -0,0 +1,28 @@
+{
+    "status_code": 200,
+    "data": {
+        "PolicyInJson": "{\n  \"Version\" : \"2012-10-17\",\n  \"Statement\" : [ {\n    \"Effect\" : \"Allow\",\n    \"Principal\" : \"*\",\n    \"Action\" : \"glue:*\",\n    \"Resource\" : \"arn:aws:glue:us-east-1:644160558196:catalog\",\n    \"Condition\" : {\n      \"StringEquals\" : {\n        \"aws:PrincipalOrgID\" : \"o-4amkskbcf3\"\n      }\n    }\n  }, {\n    \"Effect\" : \"Allow\",\n    \"Principal\" : \"*\",\n    \"Action\" : \"glue:*\",\n    \"Resource\" : \"arn:aws:glue:us-east-1:644160558196:catalog\",\n    \"Condition\" : {\n      \"StringEquals\" : {\n        \"aws:PrincipalOrgID\" : \"o-4amkskbcf1\"\n      }\n    }\n  } ]\n}",
+        "PolicyHash": "vQI9F/KPAi+BKiDBvNPXSw==",
+        "CreateTime": {
+            "__class__": "datetime",
+            "year": 2020,
+            "month": 6,
+            "day": 5,
+            "hour": 12,
+            "minute": 26,
+            "second": 46,
+            "microsecond": 47000
+        },
+        "UpdateTime": {
+            "__class__": "datetime",
+            "year": 2020,
+            "month": 6,
+            "day": 5,
+            "hour": 12,
+            "minute": 26,
+            "second": 46,
+            "microsecond": 47000
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_catalog_change_rbp_event/glue.GetResourcePolicy_3.json b/tests/data/placebo/test_catalog_change_rbp_event/glue.GetResourcePolicy_3.json
new file mode 100644
index 000000000..a45197edd
--- /dev/null
+++ b/tests/data/placebo/test_catalog_change_rbp_event/glue.GetResourcePolicy_3.json
@@ -0,0 +1,28 @@
+{
+    "status_code": 200,
+    "data": {
+        "PolicyInJson": "{\n  \"Version\" : \"2012-10-17\",\n  \"Statement\" : [ {\n    \"Effect\" : \"Allow\",\n    \"Principal\" : \"*\",\n    \"Action\" : \"glue:*\",\n    \"Resource\" : \"arn:aws:glue:us-east-1:644160558196:catalog\",\n    \"Condition\" : {\n      \"StringEquals\" : {\n        \"aws:PrincipalOrgID\" : \"o-4amkskbcf1\"\n      }\n    }\n  } ]\n}",
+        "PolicyHash": "hfAqfsBoPt7ZUGlZjU5mMQ==",
+        "CreateTime": {
+            "__class__": "datetime",
+            "year": 2020,
+            "month": 6,
+            "day": 5,
+            "hour": 12,
+            "minute": 26,
+            "second": 52,
+            "microsecond": 531000
+        },
+        "UpdateTime": {
+            "__class__": "datetime",
+            "year": 2020,
+            "month": 6,
+            "day": 5,
+            "hour": 12,
+            "minute": 26,
+            "second": 52,
+            "microsecond": 531000
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_catalog_change_rbp_event/glue.PutResourcePolicy_1.json b/tests/data/placebo/test_catalog_change_rbp_event/glue.PutResourcePolicy_1.json
new file mode 100644
index 000000000..e08bc9386
--- /dev/null
+++ b/tests/data/placebo/test_catalog_change_rbp_event/glue.PutResourcePolicy_1.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "PolicyHash": "hfAqfsBoPt7ZUGlZjU5mMQ==",
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_redshift_reserved_node_query/redshift.DescribeReservedNodes_1.json b/tests/data/placebo/test_redshift_reserved_node_query/redshift.DescribeReservedNodes_1.json
new file mode 100644
index 000000000..27e032eb5
--- /dev/null
+++ b/tests/data/placebo/test_redshift_reserved_node_query/redshift.DescribeReservedNodes_1.json
@@ -0,0 +1,28 @@
+{
+    "status_code": 200,
+    "data": {
+        "ReservedNodes": [
+            {
+                "ReservedNodeId": "1ba8e2e3-bc01-4d65-b35d-a4a3e931547e",
+                "ReservedNodeOfferingId": "ceb6a579-cf4c-4343-be8b-d832c45ab51c",
+                "NodeType": "dc2.xlarge",
+                "StartTime": "2020-06-07T11:08:39.051Z",
+                "Duration": 31536000,
+                "FixedPrice": 1380.0,
+                "UsagePrice": 0.0,
+                "CurrencyCode": "USD",
+                "NodeCount": 1,
+                "State": "active",
+                "OfferingType": "All Upfront",
+                "RecurringCharges": [
+                    {
+                        "RecurringChargeAmount": 0.0,
+                        "RecurringChargeFrequency": "Hourly"
+                    }
+                ],
+                "ReservedNodeOfferingType": "Regular"
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+ }
diff --git a/tests/test_glue.py b/tests/test_glue.py
index 5057a3215..d4bfe5b82 100644
--- a/tests/test_glue.py
+++ b/tests/test_glue.py
@@ -15,6 +15,7 @@ from .common import BaseTest
 import time
 import json
 from c7n.exceptions import PolicyValidationError
+from .common import event_data
 
 
 class TestGlueConnections(BaseTest):
@@ -587,3 +588,102 @@ class TestGlueDataCatalog(BaseTest):
                 "actions": [{"type": "remove-statements", "statement_ids": "matched"}],
             }
         )
+
+    def test_catalog_change_encryption_event(self):
+        session_factory = self.replay_flight_data("test_catalog_change_encryption_event")
+        session = session_factory()
+        client = session.client("glue")
+        before_cat_setting = client.get_data_catalog_encryption_settings()
+        self.assertJmes(
+            'DataCatalogEncryptionSettings.EncryptionAtRest.CatalogEncryptionMode',
+            before_cat_setting,
+            'DISABLED'
+        )
+        self.assertJmes(
+            'DataCatalogEncryptionSettings.EncryptionAtRest.SseAwsKmsKeyId',
+            before_cat_setting,
+            None
+        )
+        p = self.load_policy(
+            {
+                "name": "net-change-rbp-cross-account",
+                "resource": "glue-catalog",
+                "mode": {
+                    "type": "cloudtrail",
+                    "role": "arn:aws:iam::644160558196:role/CloudCustodianRole",
+                    "events": [
+                        {
+                            "source": "glue.amazonaws.com",
+                            "event": "PutDataCatalogEncryptionSettings",
+                            "ids": "userIdentity.accountId"
+                        }
+                    ],
+                },
+                'filters': [{
+                    'type': 'value',
+                    'key': 'DataCatalogEncryptionSettings.EncryptionAtRest.SseAwsKmsKeyId',
+                    'value': 'alias/skunk/trails',
+                    'op': 'ne'},
+                ],
+                "actions": [
+                    {
+                        "type": "set-encryption",
+                        "attributes": {
+                            "EncryptionAtRest": {
+                                "CatalogEncryptionMode": "SSE-KMS"
+                            }
+                        }
+                    }
+                ],
+            },
+            session_factory=session_factory,
+        )
+        p.push(event_data("event-cloud-trail-catalog-set-encryption.json"), None)
+        after_cat_setting = client.get_data_catalog_encryption_settings()
+        self.assertJmes(
+            'DataCatalogEncryptionSettings.EncryptionAtRest.CatalogEncryptionMode',
+            after_cat_setting,
+            'SSE-KMS'
+        )
+        self.assertJmes(
+            'DataCatalogEncryptionSettings.EncryptionAtRest.SseAwsKmsKeyId',
+            after_cat_setting,
+            'alias/aws/glue'
+        )
+
+    def test_catalog_change_rbp_event(self):
+        session_factory = self.replay_flight_data("test_catalog_change_rbp_event")
+        session = session_factory()
+        client = session.client("glue")
+        before_cat_setting = client.get_resource_policy()
+        assert('o-4amkskbcf3' in before_cat_setting.get('PolicyInJson'))
+        p = self.load_policy(
+            {
+                "name": "net-change-rbp-cross-account",
+                "resource": "glue-catalog",
+                "mode": {
+                    "type": "cloudtrail",
+                    "role": "arn:aws:iam::644160558196:role/CloudCustodianRole",
+                    "events": [
+                        {
+                            "source": "glue.amazonaws.com",
+                            "event": "PutResourcePolicy",
+                            "ids": "awsRegion"
+                        }
+                    ],
+                },
+                "filters": [
+                    {
+                        "type": "cross-account",
+                        "whitelist_orgids": [
+                            "o-4amkskbcf1"
+                        ]
+                    }
+                ],
+                "actions": [{"type": "remove-statements", "statement_ids": "matched"}],
+            },
+            session_factory=session_factory,
+        )
+        p.push(event_data("event-cloud-trail-catalog-put-resource-policy.json"), None)
+        after_cat_setting = client.get_resource_policy()
+        assert('o-4amkskbcf3' not in after_cat_setting.get('PolicyInJson'))
diff --git a/tests/test_redshift.py b/tests/test_redshift.py
index 9fde7a1a4..ad49179f3 100644
--- a/tests/test_redshift.py
+++ b/tests/test_redshift.py
@@ -742,3 +742,18 @@ class TestRedshiftLogging(BaseTest):
         result.pop('ResponseMetadata')
 
         self.assertFalse(result["LoggingEnabled"])
+
+
+class TestReservedNode(BaseTest):
+    def test_redshift_reserved_node_query(self):
+        session_factory = self.replay_flight_data("test_redshift_reserved_node_query")
+        p = self.load_policy(
+            {
+                "name": "redshift-reserved",
+                "resource": "aws.redshift-reserved"
+            },
+            session_factory=session_factory,
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        self.assertEqual(resources[0]["ReservedNodeId"], "1ba8e2e3-bc01-4d65-b35d-a4a3e931547e")
diff --git a/tests/zpill.py b/tests/zpill.py
index 64f62430f..7835b5147 100644
--- a/tests/zpill.py
+++ b/tests/zpill.py
@@ -271,7 +271,7 @@ class RedPill(pill.Pill):
             response_data['ResponseMetadata'] = {}
 
         response_data = json.dumps(response_data, default=serialize)
-        response_data = re.sub("\d{12}", ACCOUNT_ID, response_data)  # noqa
+        response_data = re.sub(r"\d{12}", ACCOUNT_ID, response_data)  # noqa
         response_data = json.loads(response_data, object_hook=deserialize)
 
         super(RedPill, self).save_response(service, operation, response_data,
diff --git a/tools/c7n_sentry/test_sentry.py b/tools/c7n_sentry/test_sentry.py
index f3a8b3cc9..9c8f1fa78 100644
--- a/tools/c7n_sentry/test_sentry.py
+++ b/tools/c7n_sentry/test_sentry.py
@@ -48,7 +48,7 @@ msg = """2016-07-07 19:14:24,160 - ERROR - custodian.output - Error while execut
 
 
 msg2 = """
-2016-08-09 19:16:28,943 - ERROR - custodian.output - Error while executing policy Traceback (most recent call last):\n  File "/usr/local/custodian/lib/python2.7/site-packages/c7n/policy.py", line 234, in poll\n results = a.process(resources)\n File "/usr/local/custodian/lib/python2.7/site-packages/c7n/resources/rds.py", line 291, in process\n client.delete_db_instance(**params)\n  File "/usr/local/custodian/lib/python2.7/site-packages/botocore/client.py", line 278, in _api_call\n return self._make_api_call(operation_name, kwargs)\n File "/usr/local/custodian/lib/python2.7/site-packages/botocore/client.py", line 572, in _make_api_call\n raise ClientError(parsed_response, operation_name)\m ClientError: An error occurred (InvalidParameterValue) when calling the DeleteDBInstance operation: The parameter FinalDBSnapshotIdentifier is not a valid identifier. Identifiers must begin with a letter; must contain only ASCII letters, digits, and hyphens; and must not end with a hyphen or contain two consecutive hyphens.""" # NOQA
+2016-08-09 19:16:28,943 - ERROR - custodian.output - Error while executing policy Traceback (most recent call last):\n  File "/usr/local/custodian/lib/python2.7/site-packages/c7n/policy.py", line 234, in poll\n results = a.process(resources)\n File "/usr/local/custodian/lib/python2.7/site-packages/c7n/resources/rds.py", line 291, in process\n client.delete_db_instance(**params)\n  File "/usr/local/custodian/lib/python2.7/site-packages/botocore/client.py", line 278, in _api_call\n return self._make_api_call(operation_name, kwargs)\n File "/usr/local/custodian/lib/python2.7/site-packages/botocore/client.py", line 572, in _make_api_call\n raise ClientError(parsed_response, operation_name)\n ClientError: An error occurred (InvalidParameterValue) when calling the DeleteDBInstance operation: The parameter FinalDBSnapshotIdentifier is not a valid identifier. Identifiers must begin with a letter; must contain only ASCII letters, digits, and hyphens; and must not end with a hyphen or contain two consecutive hyphens.""" # NOQA
 
 if __name__ == '__main__':
     unittest.main()