Repository: cloud-custodian/cloud-custodian

Issue:
Bug: Elasticache Snapshot `copy-cluster-tags` action errors out if source cluster has more than 1 node
 Elasticache Snapshots `describe_snapshots` response doesn't contain `CacheClusterId ` if the source cluster has more than one node, so `copy-cluster-tags` errors out [here](https://github.com/capitalone/cloud-custodian/blob/master/c7n/resources/elasticache.py#L445). 


Discussion:
traceback?
```
Traceback (most recent call last):
File "c7n/commands.py", line 237, in run
policy()
File "c7n/policy.py", line 707, in __call__
resources = mode.run()
File "c7n/policy.py", line 254, in run
results = a.process(resources)
File "c7n/resources/elasticache.py", line 445, in process
if s['CacheClusterId'] not in clusters:
KeyError: u'CacheClusterId'
```
and a policy that triggers?
```
- name: cache-snapshot-copy-tags
      resource: cache-snapshot
      filters:
        - SnapshotStatus: "available"
      actions:
        - type: copy-cluster-tags
          tags: ['app', 'env']
```

Resolving patch:
diff --git a/c7n/resources/aws.py b/c7n/resources/aws.py
--- a/c7n/resources/aws.py
+++ b/c7n/resources/aws.py
@@ -146,10 +146,16 @@ def get_service_region_map(regions, resource_types):
         region_name='us-east-1',
         aws_access_key_id='never',
         aws_secret_access_key='found')
+    normalized_types = []
+    for r in resource_types:
+        if r.startswith('aws.'):
+            normalized_types.append(r[4:])
+        else:
+            normalized_types.append(r)
 
     resource_service_map = {
         r: clouds['aws'].resources.get(r).resource_type.service
-        for r in resource_types if r != 'account'}
+        for r in normalized_types if r != 'account'}
     # support for govcloud and china, we only utilize these regions if they
     # are explicitly passed in on the cli.
     partition_regions = {}
diff --git a/c7n/resources/elasticache.py b/c7n/resources/elasticache.py
--- a/c7n/resources/elasticache.py
+++ b/c7n/resources/elasticache.py
@@ -216,7 +216,7 @@ class SnapshotElastiCacheCluster(BaseAction):
     permissions = ('elasticache:CreateSnapshot',)
 
     def process(self, clusters):
-        with self.executor_factory(max_workers=3) as w:
+        with self.executor_factory(max_workers=2) as w:
             futures = []
             for cluster in clusters:
                 if not _cluster_eligible_for_snapshot(cluster):
@@ -427,7 +427,7 @@ class CopyClusterTags(BaseAction):
     schema = type_schema(
         'copy-cluster-tags',
         tags={'type': 'array', 'items': {'type': 'string'}, 'minItems': 1},
-        required = ('tags',))
+        required=('tags',))
 
     def get_permissions(self):
         perms = self.manager.get_resource_manager('cache-cluster').get_permissions()
@@ -435,27 +435,52 @@ def get_permissions(self):
         return perms
 
     def process(self, snapshots):
-        log.info("Modifying %d ElastiCache snapshots", len(snapshots))
         client = local_session(self.manager.session_factory).client('elasticache')
-        clusters = {
-            cluster['CacheClusterId']: cluster for cluster in
-            self.manager.get_resource_manager('cache-cluster').resources()}
+        clusters = {r['CacheClusterId']: r for r in
+                    self.manager.get_resource_manager('cache-cluster').resources()}
+        copyable_tags = self.data.get('tags')
 
         for s in snapshots:
-            if s['CacheClusterId'] not in clusters:
+            # For replicated/sharded clusters it is possible for each
+            # shard to have separate tags, we go ahead and tag the
+            # snap with the union of tags with overlaps getting the
+            # last value (arbitrary if mismatched).
+            if 'CacheClusterId' not in s:
+                cluster_ids = [ns['CacheClusterId'] for ns in s['NodeSnapshots']]
+            else:
+                cluster_ids = [s['CacheClusterId']]
+
+            copy_tags = {}
+            for cid in sorted(cluster_ids):
+                if cid not in clusters:
+                    continue
+
+                cluster_tags = {t['Key']: t['Value'] for t in clusters[cid]['Tags']}
+                snap_tags = {t['Key']: t['Value'] for t in s.get('Tags', ())}
+
+                for k, v in cluster_tags.items():
+                    if copyable_tags and k not in copyable_tags:
+                        continue
+                    if k.startswith('aws:'):
+                        continue
+                    if snap_tags.get(k, '') == v:
+                        continue
+                    copy_tags[k] = v
+
+            if not copy_tags:
+                continue
+
+            if len(set(copy_tags).union(set(snap_tags))) > 50:
+                self.log.error(
+                    "Cant copy tags, max tag limit hit on snapshot:%s",
+                    s['SnapshotName'])
                 continue
 
             arn = self.manager.generate_arn(s['SnapshotName'])
-            tags_cluster = clusters[s['CacheClusterId']]['Tags']
-            only_tags = self.data.get('tags', [])  # Specify tags to copy
-            extant_tags = {t['Key']: t['Value'] for t in s.get('Tags', ())}
-            copy_tags = []
-
-            for t in tags_cluster:
-                if t['Key'] in only_tags and t['Value'] != extant_tags.get(t['Key'], ""):
-                    copy_tags.append(t)
             self.manager.retry(
-                client.add_tags_to_resource, ResourceName=arn, Tags=copy_tags)
+                client.add_tags_to_resource,
+                ResourceName=arn,
+                Tags=[{'Key': k, 'Value': v} for k, v in copy_tags.items()])
 
 
 def _cluster_eligible_for_snapshot(cluster):


Test patch:
diff --git a/tests/common.py b/tests/common.py
--- a/tests/common.py
+++ b/tests/common.py
@@ -52,6 +52,7 @@
 skip_if_not_validating = unittest.skipIf(
     not C7N_VALIDATE, reason='We are not validating schemas.')
 
+
 class TestConfig(Config):
     config_args = {
         "metrics_enabled": False,
diff --git a/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.AddTagsToResource_1.json b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.AddTagsToResource_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.AddTagsToResource_1.json
@@ -0,0 +1,34 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "2d336d70-5079-11e8-a0d2-c9ad4b264331", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "2d336d70-5079-11e8-a0d2-c9ad4b264331", 
+                "date": "Sat, 05 May 2018 15:30:01 GMT", 
+                "content-length": "616", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": [
+            {
+                "Value": "MegaCache", 
+                "Key": "App"
+            }, 
+            {
+                "Value": "Dev", 
+                "Key": "Env"
+            }, 
+            {
+                "Value": "12", 
+                "Key": "Zone"
+            }, 
+            {
+                "Value": "Blue", 
+                "Key": "Color"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.DescribeCacheClusters_1.json b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.DescribeCacheClusters_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.DescribeCacheClusters_1.json
@@ -0,0 +1,270 @@
+{
+    "status_code": 200, 
+    "data": {
+        "CacheClusters": [
+            {
+                "CacheClusterId": "megacache-0001-001", 
+                "ReplicationGroupId": "megacache", 
+                "CacheClusterStatus": "available", 
+                "SnapshotRetentionLimit": 0, 
+                "ClientDownloadLandingPage": "https://console.aws.amazon.com/elasticache/home#client-download:", 
+                "CacheNodeType": "cache.t2.medium", 
+                "TransitEncryptionEnabled": false, 
+                "Engine": "redis", 
+                "CacheSecurityGroups": [], 
+                "NumCacheNodes": 1, 
+                "AutoMinorVersionUpgrade": true, 
+                "SecurityGroups": [
+                    {
+                        "Status": "active", 
+                        "SecurityGroupId": "sg-aa6c90c3"
+                    }
+                ], 
+                "PendingModifiedValues": {}, 
+                "PreferredMaintenanceWindow": "sat:05:30-sat:06:30", 
+                "CacheSubnetGroupName": "cache-net-group", 
+                "AuthTokenEnabled": false, 
+                "AtRestEncryptionEnabled": false, 
+                "EngineVersion": "3.2.10", 
+                "CacheClusterCreateTime": {
+                    "hour": 14, 
+                    "__class__": "datetime", 
+                    "month": 5, 
+                    "second": 56, 
+                    "microsecond": 591000, 
+                    "year": 2018, 
+                    "day": 5, 
+                    "minute": 5
+                }, 
+                "PreferredAvailabilityZone": "us-east-2b", 
+                "SnapshotWindow": "01:30-02:30", 
+                "CacheParameterGroup": {
+                    "CacheNodeIdsToReboot": [], 
+                    "CacheParameterGroupName": "default.redis3.2.cluster.on", 
+                    "ParameterApplyStatus": "in-sync"
+                }
+            }, 
+            {
+                "CacheClusterId": "megacache-0001-002", 
+                "ReplicationGroupId": "megacache", 
+                "CacheClusterStatus": "available", 
+                "SnapshotRetentionLimit": 0, 
+                "ClientDownloadLandingPage": "https://console.aws.amazon.com/elasticache/home#client-download:", 
+                "CacheNodeType": "cache.t2.medium", 
+                "TransitEncryptionEnabled": false, 
+                "Engine": "redis", 
+                "CacheSecurityGroups": [], 
+                "NumCacheNodes": 1, 
+                "AutoMinorVersionUpgrade": true, 
+                "SecurityGroups": [
+                    {
+                        "Status": "active", 
+                        "SecurityGroupId": "sg-aa6c90c3"
+                    }
+                ], 
+                "PendingModifiedValues": {}, 
+                "PreferredMaintenanceWindow": "sat:05:30-sat:06:30", 
+                "CacheSubnetGroupName": "cache-net-group", 
+                "AuthTokenEnabled": false, 
+                "AtRestEncryptionEnabled": false, 
+                "EngineVersion": "3.2.10", 
+                "CacheClusterCreateTime": {
+                    "hour": 14, 
+                    "__class__": "datetime", 
+                    "month": 5, 
+                    "second": 56, 
+                    "microsecond": 591000, 
+                    "year": 2018, 
+                    "day": 5, 
+                    "minute": 5
+                }, 
+                "PreferredAvailabilityZone": "us-east-2a", 
+                "SnapshotWindow": "01:30-02:30", 
+                "CacheParameterGroup": {
+                    "CacheNodeIdsToReboot": [], 
+                    "CacheParameterGroupName": "default.redis3.2.cluster.on", 
+                    "ParameterApplyStatus": "in-sync"
+                }
+            }, 
+            {
+                "CacheClusterId": "megacache-0001-003", 
+                "ReplicationGroupId": "megacache", 
+                "CacheClusterStatus": "available", 
+                "SnapshotRetentionLimit": 0, 
+                "ClientDownloadLandingPage": "https://console.aws.amazon.com/elasticache/home#client-download:", 
+                "CacheNodeType": "cache.t2.medium", 
+                "TransitEncryptionEnabled": false, 
+                "Engine": "redis", 
+                "CacheSecurityGroups": [], 
+                "NumCacheNodes": 1, 
+                "AutoMinorVersionUpgrade": true, 
+                "SecurityGroups": [
+                    {
+                        "Status": "active", 
+                        "SecurityGroupId": "sg-aa6c90c3"
+                    }
+                ], 
+                "PendingModifiedValues": {}, 
+                "PreferredMaintenanceWindow": "sat:05:30-sat:06:30", 
+                "CacheSubnetGroupName": "cache-net-group", 
+                "AuthTokenEnabled": false, 
+                "AtRestEncryptionEnabled": false, 
+                "EngineVersion": "3.2.10", 
+                "CacheClusterCreateTime": {
+                    "hour": 14, 
+                    "__class__": "datetime", 
+                    "month": 5, 
+                    "second": 56, 
+                    "microsecond": 591000, 
+                    "year": 2018, 
+                    "day": 5, 
+                    "minute": 5
+                }, 
+                "PreferredAvailabilityZone": "us-east-2b", 
+                "SnapshotWindow": "01:30-02:30", 
+                "CacheParameterGroup": {
+                    "CacheNodeIdsToReboot": [], 
+                    "CacheParameterGroupName": "default.redis3.2.cluster.on", 
+                    "ParameterApplyStatus": "in-sync"
+                }
+            }, 
+            {
+                "CacheClusterId": "megacache-0002-001", 
+                "ReplicationGroupId": "megacache", 
+                "CacheClusterStatus": "available", 
+                "SnapshotRetentionLimit": 0, 
+                "ClientDownloadLandingPage": "https://console.aws.amazon.com/elasticache/home#client-download:", 
+                "CacheNodeType": "cache.t2.medium", 
+                "TransitEncryptionEnabled": false, 
+                "Engine": "redis", 
+                "CacheSecurityGroups": [], 
+                "NumCacheNodes": 1, 
+                "AutoMinorVersionUpgrade": true, 
+                "SecurityGroups": [
+                    {
+                        "Status": "active", 
+                        "SecurityGroupId": "sg-aa6c90c3"
+                    }
+                ], 
+                "PendingModifiedValues": {}, 
+                "PreferredMaintenanceWindow": "sat:05:30-sat:06:30", 
+                "CacheSubnetGroupName": "cache-net-group", 
+                "AuthTokenEnabled": false, 
+                "AtRestEncryptionEnabled": false, 
+                "EngineVersion": "3.2.10", 
+                "CacheClusterCreateTime": {
+                    "hour": 14, 
+                    "__class__": "datetime", 
+                    "month": 5, 
+                    "second": 56, 
+                    "microsecond": 591000, 
+                    "year": 2018, 
+                    "day": 5, 
+                    "minute": 5
+                }, 
+                "PreferredAvailabilityZone": "us-east-2a", 
+                "SnapshotWindow": "01:30-02:30", 
+                "CacheParameterGroup": {
+                    "CacheNodeIdsToReboot": [], 
+                    "CacheParameterGroupName": "default.redis3.2.cluster.on", 
+                    "ParameterApplyStatus": "in-sync"
+                }
+            }, 
+            {
+                "CacheClusterId": "megacache-0002-002", 
+                "ReplicationGroupId": "megacache", 
+                "CacheClusterStatus": "available", 
+                "SnapshotRetentionLimit": 0, 
+                "ClientDownloadLandingPage": "https://console.aws.amazon.com/elasticache/home#client-download:", 
+                "CacheNodeType": "cache.t2.medium", 
+                "TransitEncryptionEnabled": false, 
+                "Engine": "redis", 
+                "CacheSecurityGroups": [], 
+                "NumCacheNodes": 1, 
+                "AutoMinorVersionUpgrade": true, 
+                "SecurityGroups": [
+                    {
+                        "Status": "active", 
+                        "SecurityGroupId": "sg-aa6c90c3"
+                    }
+                ], 
+                "PendingModifiedValues": {}, 
+                "PreferredMaintenanceWindow": "sat:05:30-sat:06:30", 
+                "CacheSubnetGroupName": "cache-net-group", 
+                "AuthTokenEnabled": false, 
+                "AtRestEncryptionEnabled": false, 
+                "EngineVersion": "3.2.10", 
+                "CacheClusterCreateTime": {
+                    "hour": 14, 
+                    "__class__": "datetime", 
+                    "month": 5, 
+                    "second": 56, 
+                    "microsecond": 591000, 
+                    "year": 2018, 
+                    "day": 5, 
+                    "minute": 5
+                }, 
+                "PreferredAvailabilityZone": "us-east-2b", 
+                "SnapshotWindow": "01:30-02:30", 
+                "CacheParameterGroup": {
+                    "CacheNodeIdsToReboot": [], 
+                    "CacheParameterGroupName": "default.redis3.2.cluster.on", 
+                    "ParameterApplyStatus": "in-sync"
+                }
+            }, 
+            {
+                "CacheClusterId": "megacache-0002-003", 
+                "ReplicationGroupId": "megacache", 
+                "CacheClusterStatus": "available", 
+                "SnapshotRetentionLimit": 0, 
+                "ClientDownloadLandingPage": "https://console.aws.amazon.com/elasticache/home#client-download:", 
+                "CacheNodeType": "cache.t2.medium", 
+                "TransitEncryptionEnabled": false, 
+                "Engine": "redis", 
+                "CacheSecurityGroups": [], 
+                "NumCacheNodes": 1, 
+                "AutoMinorVersionUpgrade": true, 
+                "SecurityGroups": [
+                    {
+                        "Status": "active", 
+                        "SecurityGroupId": "sg-aa6c90c3"
+                    }
+                ], 
+                "PendingModifiedValues": {}, 
+                "PreferredMaintenanceWindow": "sat:05:30-sat:06:30", 
+                "CacheSubnetGroupName": "cache-net-group", 
+                "AuthTokenEnabled": false, 
+                "AtRestEncryptionEnabled": false, 
+                "EngineVersion": "3.2.10", 
+                "CacheClusterCreateTime": {
+                    "hour": 14, 
+                    "__class__": "datetime", 
+                    "month": 5, 
+                    "second": 56, 
+                    "microsecond": 591000, 
+                    "year": 2018, 
+                    "day": 5, 
+                    "minute": 5
+                }, 
+                "PreferredAvailabilityZone": "us-east-2b", 
+                "SnapshotWindow": "01:30-02:30", 
+                "CacheParameterGroup": {
+                    "CacheNodeIdsToReboot": [], 
+                    "CacheParameterGroupName": "default.redis3.2.cluster.on", 
+                    "ParameterApplyStatus": "in-sync"
+                }
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "2cdaed2c-5079-11e8-a0d2-c9ad4b264331", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "2cdaed2c-5079-11e8-a0d2-c9ad4b264331", 
+                "date": "Sat, 05 May 2018 15:30:00 GMT", 
+                "content-length": "10457", 
+                "content-type": "text/xml"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.DescribeSnapshots_1.json b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.DescribeSnapshots_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.DescribeSnapshots_1.json
@@ -0,0 +1,92 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "2cb4a0ae-5079-11e8-8aae-d99659c87397", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "2cb4a0ae-5079-11e8-8aae-d99659c87397", 
+                "date": "Sat, 05 May 2018 15:30:00 GMT", 
+                "content-length": "2171", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "Snapshots": [
+            {
+                "Engine": "redis", 
+                "CacheParameterGroupName": "default.redis3.2.cluster.on", 
+                "VpcId": "vpc-5f6c9936", 
+                "NodeSnapshots": [
+                    {
+                        "SnapshotCreateTime": {
+                            "hour": 14, 
+                            "__class__": "datetime", 
+                            "month": 5, 
+                            "second": 40, 
+                            "microsecond": 0, 
+                            "year": 2018, 
+                            "day": 5, 
+                            "minute": 32
+                        }, 
+                        "CacheNodeId": "0001", 
+                        "CacheClusterId": "megacache-0001-003", 
+                        "NodeGroupId": "0001", 
+                        "CacheNodeCreateTime": {
+                            "hour": 14, 
+                            "__class__": "datetime", 
+                            "month": 5, 
+                            "second": 56, 
+                            "microsecond": 591000, 
+                            "year": 2018, 
+                            "day": 5, 
+                            "minute": 5
+                        }, 
+                        "CacheSize": "5 MB"
+                    }, 
+                    {
+                        "SnapshotCreateTime": {
+                            "hour": 14, 
+                            "__class__": "datetime", 
+                            "month": 5, 
+                            "second": 40, 
+                            "microsecond": 0, 
+                            "year": 2018, 
+                            "day": 5, 
+                            "minute": 32
+                        }, 
+                        "CacheNodeId": "0001", 
+                        "CacheClusterId": "megacache-0002-003", 
+                        "NodeGroupId": "0002", 
+                        "CacheNodeCreateTime": {
+                            "hour": 14, 
+                            "__class__": "datetime", 
+                            "month": 5, 
+                            "second": 56, 
+                            "microsecond": 591000, 
+                            "year": 2018, 
+                            "day": 5, 
+                            "minute": 5
+                        }, 
+                        "CacheSize": "5 MB"
+                    }
+                ], 
+                "NumNodeGroups": 2, 
+                "SnapshotName": "zero-bytes", 
+                "ReplicationGroupId": "megacache", 
+                "AutoMinorVersionUpgrade": true, 
+                "SnapshotRetentionLimit": 1, 
+                "AutomaticFailover": "enabled", 
+                "SnapshotStatus": "available", 
+                "SnapshotSource": "manual", 
+                "SnapshotWindow": "01:30-02:30", 
+                "EngineVersion": "3.2.10", 
+                "CacheSubnetGroupName": "cache-net-group", 
+                "ReplicationGroupDescription": " ", 
+                "Port": 6379, 
+                "PreferredMaintenanceWindow": "sat:05:30-sat:06:30", 
+                "CacheNodeType": "cache.t2.medium"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.ListTagsForResource_1.json b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.ListTagsForResource_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.ListTagsForResource_1.json
@@ -0,0 +1,22 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "2c992969-5079-11e8-8aae-d99659c87397", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "2c992969-5079-11e8-8aae-d99659c87397", 
+                "date": "Sat, 05 May 2018 15:29:59 GMT", 
+                "content-length": "396", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": [
+            {
+                "Value": "MegaCache", 
+                "Key": "App"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.ListTagsForResource_2.json b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.ListTagsForResource_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/elasticache.ListTagsForResource_2.json
@@ -0,0 +1,34 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "2d406601-5079-11e8-8aae-d99659c87397", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "2d406601-5079-11e8-8aae-d99659c87397", 
+                "date": "Sat, 05 May 2018 15:30:00 GMT", 
+                "content-length": "624", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": [
+            {
+                "Value": "MegaCache", 
+                "Key": "App"
+            }, 
+            {
+                "Value": "Dev", 
+                "Key": "Env"
+            }, 
+            {
+                "Value": "12", 
+                "Key": "Zone"
+            }, 
+            {
+                "Value": "Blue", 
+                "Key": "Color"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/tagging.GetResources_1.json b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/tagging.GetResources_1.json
@@ -0,0 +1,28 @@
+{
+    "status_code": 200, 
+    "data": {
+        "PaginationToken": "", 
+        "ResourceTagMappingList": [
+            {
+                "ResourceARN": "arn:aws:elasticache:us-east-2:644160558196:snapshot:zero-bytes", 
+                "Tags": [
+                    {
+                        "Value": "MegaCache", 
+                        "Key": "App"
+                    }
+                ]
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "2ccb8436-5079-11e8-8b32-43aec20c9601", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "2ccb8436-5079-11e8-8b32-43aec20c9601", 
+                "date": "Sat, 05 May 2018 15:30:00 GMT", 
+                "content-length": "173", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/tagging.GetResources_2.json b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/tagging.GetResources_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_elasticache_sharded_copy_cluster_tags/tagging.GetResources_2.json
@@ -0,0 +1,117 @@
+{
+    "status_code": 200, 
+    "data": {
+        "PaginationToken": "", 
+        "ResourceTagMappingList": [
+            {
+                "ResourceARN": "arn:aws:elasticache:us-east-2:644160558196:cluster:megacache-0001-001", 
+                "Tags": [
+                    {
+                        "Value": "MegaCache", 
+                        "Key": "App"
+                    }, 
+                    {
+                        "Value": "Dev", 
+                        "Key": "Env"
+                    }
+                ]
+            }, 
+            {
+                "ResourceARN": "arn:aws:elasticache:us-east-2:644160558196:cluster:megacache-0001-002", 
+                "Tags": [
+                    {
+                        "Value": "MegaCache", 
+                        "Key": "App"
+                    }, 
+                    {
+                        "Value": "Dev", 
+                        "Key": "Env"
+                    }, 
+                    {
+                        "Value": "Dev", 
+                        "Key": "Origin"
+                    }
+                ]
+            }, 
+            {
+                "ResourceARN": "arn:aws:elasticache:us-east-2:644160558196:cluster:megacache-0001-003", 
+                "Tags": [
+                    {
+                        "Value": "MegaCache", 
+                        "Key": "App"
+                    }, 
+                    {
+                        "Value": "Blue", 
+                        "Key": "Color"
+                    }, 
+                    {
+                        "Value": "Dev", 
+                        "Key": "Env"
+                    }
+                ]
+            }, 
+            {
+                "ResourceARN": "arn:aws:elasticache:us-east-2:644160558196:cluster:megacache-0002-001", 
+                "Tags": [
+                    {
+                        "Value": "MegaCache", 
+                        "Key": "App"
+                    }, 
+                    {
+                        "Value": "Dev", 
+                        "Key": "Env"
+                    }, 
+                    {
+                        "Value": "3", 
+                        "Key": "Zone"
+                    }
+                ]
+            }, 
+            {
+                "ResourceARN": "arn:aws:elasticache:us-east-2:644160558196:cluster:megacache-0002-002", 
+                "Tags": [
+                    {
+                        "Value": "MegaCache", 
+                        "Key": "App"
+                    }, 
+                    {
+                        "Value": "Dev", 
+                        "Key": "Env"
+                    }, 
+                    {
+                        "Value": "1", 
+                        "Key": "Zone"
+                    }
+                ]
+            }, 
+            {
+                "ResourceARN": "arn:aws:elasticache:us-east-2:644160558196:cluster:megacache-0002-003", 
+                "Tags": [
+                    {
+                        "Value": "MegaCache", 
+                        "Key": "App"
+                    }, 
+                    {
+                        "Value": "Dev", 
+                        "Key": "Env"
+                    }, 
+                    {
+                        "Value": "12", 
+                        "Key": "Zone"
+                    }
+                ]
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "2d238f22-5079-11e8-9d05-41469817b695", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "2d238f22-5079-11e8-9d05-41469817b695", 
+                "date": "Sat, 05 May 2018 15:30:01 GMT", 
+                "content-length": "1147", 
+                "content-type": "application/x-amz-json-1.1"
+            }
+        }
+    }
+}
\ 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
@@ -76,12 +76,36 @@ def test_elasticache_cluster_simple_filter(self):
         resources = p.run()
         self.assertEqual(len(resources), 3)
 
+    def test_elasticache_sharded_snapshot_copy_tags(self):
+        factory = self.replay_flight_data(
+            'test_elasticache_sharded_copy_cluster_tags')
+        client = factory().client('elasticache')
+        snap_tags = {
+            t['Key']: t['Value'] for t in client.list_tags_for_resource(
+                ResourceName='arn:aws:elasticache:us-east-2:644160558196:snapshot:zero-bytes'
+            )['TagList']}
+        self.assertEqual(snap_tags, {'App': 'MegaCache'})
+        p = self.load_policy({
+            'name': 'test-copy-cluster-tags',
+            'resource': 'cache-snapshot',
+            'actions': [
+                {'type': 'copy-cluster-tags', 'tags': ['App', 'Env', 'Zone', 'Color']}]},
+            config=Config.empty(region='us-east-2'), session_factory=factory)
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        self.assertEqual(resources[0]['SnapshotName'], 'zero-bytes')
+        arn = p.resource_manager.get_arns(resources)[0]
+        snap_tags = {
+            t['Key']: t['Value'] for t in
+            client.list_tags_for_resource(ResourceName=arn)['TagList']}
+        self.assertEqual(
+            snap_tags, {'App': 'MegaCache', 'Color': 'Blue', 'Env': 'Dev', 'Zone': '12'})
+
     def test_elasticache_snapshot_copy_cluster_tags(self):
         session_factory = self.replay_flight_data(
             'test_elasticache_copy_cluster_tags')
-
-        results = session_factory().client(
-            'elasticache').list_tags_for_resource(
+        client = session_factory().client('elasticache')
+        results = client.list_tags_for_resource(
                 ResourceName='arn:aws:elasticache:us-east-1:644160558196:snapshot:myec-backup')['TagList']
         tags = {t['Key']: t['Value'] for t in results}
         self.assertEqual(tags, {})
@@ -98,9 +122,7 @@ def test_elasticache_snapshot_copy_cluster_tags(self):
         resources = policy.run()
         arn = policy.resource_manager.generate_arn(
             resources[0]['SnapshotName'])
-        results = session_factory().client(
-            'elasticache').list_tags_for_resource(ResourceName=arn)['TagList']
-
+        results = client.list_tags_for_resource(ResourceName=arn)['TagList']
         tags = {t['Key']: t['Value'] for t in results}
         self.assertEqual(tags['tagkey'], 'tagval')