Repository: cloud-custodian/cloud-custodian

Issue:
ElastiCache - Add support for replication-groups resources
@kapilt Currently ElastiCache only can see resources that are created in a 'Cluster' mode.  If creating an ElastiCache NOT in cluster mode, c7n doesn't pick it up as it is a resource type of **replication-group** and would need to be able to pickup the replicationGroupId from the event as the resource Id.  https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elasticache.html#ElastiCache.Client.describe_replication_groups



Discussion:

Resolving patch:
diff --git a/c7n/resources/elasticache.py b/c7n/resources/elasticache.py
--- a/c7n/resources/elasticache.py
+++ b/c7n/resources/elasticache.py
@@ -458,3 +458,16 @@ def _cluster_eligible_for_snapshot(cluster):
         cluster['Engine'] != 'memcached' and not
         TTYPE.match(cluster['CacheNodeType'])
     )
+
+
+@resources.register('elasticache-group')
+class ElastiCacheReplicationGroup(QueryResourceManager):
+
+    class resource_type(TypeInfo):
+        service = "elasticache"
+        enum_spec = ('describe_replication_groups',
+                     'ReplicationGroups[]', None)
+        arn_type = 'replicationgroup'
+        id = name = dimension = 'ReplicationGroupId'
+
+    permissions = ('elasticache:DescribeReplicationGroups',)
diff --git a/c7n/resources/resource_map.py b/c7n/resources/resource_map.py
--- a/c7n/resources/resource_map.py
+++ b/c7n/resources/resource_map.py
@@ -51,6 +51,7 @@
     "aws.elasticbeanstalk": "c7n.resources.elasticbeanstalk.ElasticBeanstalk",
     "aws.elasticbeanstalk-environment": (
         "c7n.resources.elasticbeanstalk.ElasticBeanstalkEnvironment"),
+    "aws.elasticache-group": "c7n.resources.elasticache.ElastiCacheReplicationGroup",
     "aws.elasticsearch": "c7n.resources.elasticsearch.ElasticSearchDomain",
     "aws.elb": "c7n.resources.elb.ELB",
     "aws.emr": "c7n.resources.emr.EMRCluster",


Test patch:
diff --git a/tests/data/placebo/test_elasticache_replication_group/elasticache.DescribeReplicationGroups_1.json b/tests/data/placebo/test_elasticache_replication_group/elasticache.DescribeReplicationGroups_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_elasticache_replication_group/elasticache.DescribeReplicationGroups_1.json
@@ -0,0 +1,73 @@
+{
+    "status_code": 200,
+    "data": {
+        "ReplicationGroups": [
+            {
+                "ReplicationGroupId": "test-c7n-rg",
+                "Description": "test-c7n-rg",
+                "Status": "available",
+                "PendingModifiedValues": {},
+                "MemberClusters": [
+                    "test-c7n-rg-001",
+                    "test-c7n-rg-002"
+                ],
+                "NodeGroups": [
+                    {
+                        "NodeGroupId": "0001",
+                        "Status": "available",
+                        "PrimaryEndpoint": {
+                            "Address": "test-c7n-rg.qgcgte.ng.0001.use1.cache.amazonaws.com",
+                            "Port": 6379
+                        },
+                        "ReaderEndpoint": {
+                            "Address": "test-c7n-rg-ro.qgcgte.ng.0001.use1.cache.amazonaws.com",
+                            "Port": 6379
+                        },
+                        "NodeGroupMembers": [
+                            {
+                                "CacheClusterId": "test-c7n-rg-001",
+                                "CacheNodeId": "0001",
+                                "ReadEndpoint": {
+                                    "Address": "test-c7n-rg-001.qgcgte.0001.use1.cache.amazonaws.com",
+                                    "Port": 6379
+                                },
+                                "PreferredAvailabilityZone": "us-east-1a",
+                                "CurrentRole": "primary"
+                            },
+                            {
+                                "CacheClusterId": "test-c7n-rg-002",
+                                "CacheNodeId": "0001",
+                                "ReadEndpoint": {
+                                    "Address": "test-c7n-rg-002.qgcgte.0001.use1.cache.amazonaws.com",
+                                    "Port": 6379
+                                },
+                                "PreferredAvailabilityZone": "us-east-1a",
+                                "CurrentRole": "replica"
+                            }
+                        ]
+                    }
+                ],
+                "SnapshottingClusterId": "test-c7n-rg-002",
+                "AutomaticFailover": "enabled",
+                "SnapshotRetentionLimit": 1,
+                "SnapshotWindow": "03:00-04:00",
+                "ClusterEnabled": false,
+                "CacheNodeType": "cache.r5.large",
+                "AuthTokenEnabled": false,
+                "TransitEncryptionEnabled": false,
+                "AtRestEncryptionEnabled": false
+            }
+        ],
+        "ResponseMetadata": {
+            "RequestId": "ca18b602-dd47-44d2-97b0-0b02dea5556d",
+            "HTTPStatusCode": 200,
+            "HTTPHeaders": {
+                "x-amzn-requestid": "ca18b602-dd47-44d2-97b0-0b02dea5556d",
+                "content-type": "text/xml",
+                "content-length": "2848",
+                "date": "Tue, 11 Feb 2020 18:12:43 GMT"
+            },
+            "RetryAttempts": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/test_elasticache.py b/tests/test_elasticache.py
--- a/tests/test_elasticache.py
+++ b/tests/test_elasticache.py
@@ -458,3 +458,15 @@ def test_elasticache_add_security_group(self):
         self.assertEqual(len(resources[0]["SecurityGroups"]), 1)
         self.assertEqual(len(clean_resources[0]["SecurityGroups"]), 2)
         self.assertEqual(len(clean_resources), 3)
+
+
+class TestElastiCacheReplicationGroup(BaseTest):
+
+    def test_elasticache_replication_group(self):
+        session_factory = self.replay_flight_data("test_elasticache_replication_group")
+        p = self.load_policy(
+            {"name": "elasticache-rg", "resource": "elasticache-group"},
+            session_factory=session_factory,)
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        self.assertEqual(resources[0]['ReplicationGroupId'], 'test-c7n-rg')