Repository: cloud-custodian/cloud-custodian

Issue:
rds-cluster tagging / marking support
The ``rds-cluster`` resource currently supports tag augmentation, but not setting/changing/removing tags or mark/unmark. Given what I can tell from the [boto3 RDS client](https://boto3.readthedocs.io/en/latest/reference/services/rds.html), the implementation of these for ``rds-cluster`` should be almost identical to those from the ``rds`` resource.


Discussion:
@jantman - Is #1348 what you are looking for?

edit: just re-read what you wrote.  I think that may be coming in a future PR.  #1315 ?
@scotwk #1315 is closer to what I'm looking for but it doesn't appear to effect RDS, probably because the RDS tagging API calls are significantly different from most of the other APIs.
I'm going to try and cut a PR for this in the morning.

Resolving patch:
diff --git a/c7n/resources/rdscluster.py b/c7n/resources/rdscluster.py
--- a/c7n/resources/rdscluster.py
+++ b/c7n/resources/rdscluster.py
@@ -23,6 +23,7 @@
 import c7n.filters.vpc as net_filters
 from c7n.manager import resources
 from c7n.query import QueryResourceManager
+from c7n import tags
 from c7n.utils import (
     type_schema, local_session, snapshot_identifier, chunks, get_retry)
 
@@ -31,6 +32,9 @@
 filters = FilterRegistry('rds-cluster.filters')
 actions = ActionRegistry('rds-cluster.actions')
 
+filters.register('tag-count', tags.TagCountFilter)
+filters.register('marked-for-op', tags.TagActionFilter)
+
 
 @resources.register('rds-cluster')
 class RDSCluster(QueryResourceManager):
@@ -81,6 +85,104 @@ def process_tags(db):
         return list(w.map(process_tags, dbs))
 
 
+@actions.register('mark-for-op')
+class TagDelayedAction(tags.TagDelayedAction):
+    """Mark a RDS cluster for specific custodian action
+
+    :example:
+
+        .. code-block: yaml
+
+            policies:
+              - name: mark-for-delete
+                resource: rds-cluster
+                filters:
+                  - type: default-vpc
+                actions:
+                  - type: mark-for-op
+                    op: delete
+                    days: 7
+    """
+    schema = type_schema(
+        'mark-for-op', rinherit=tags.TagDelayedAction.schema)
+    permissions = ('rds:AddTagsToResource',)
+
+    batch_size = 5
+
+    def process(self, dbs):
+        return super(TagDelayedAction, self).process(dbs)
+
+    def process_resource_set(self, dbs, tags):
+        client = local_session(self.manager.session_factory).client('rds')
+        for db in dbs:
+            arn = self.manager.generate_arn(db['DBClusterIdentifier'])
+            client.add_tags_to_resource(ResourceName=arn, Tags=tags)
+
+
+@actions.register('tag')
+@actions.register('mark')
+class Tag(tags.Tag):
+    """Mark/tag a RDS cluster with a key/value
+
+    :example:
+
+        .. code-block: yaml
+
+            policies:
+              - name: rds-cluster-owner-tag
+                resource: rds-cluster
+                filters:
+                  - "tag:OwnerName": absent
+                actions:
+                  - type: tag
+                    key: OwnerName
+                    value: OwnerName
+    """
+
+    concurrency = 2
+    batch_size = 5
+    permissions = ('rds:AddTagsToResource',)
+
+    def process_resource_set(self, dbs, ts):
+        client = local_session(
+            self.manager.session_factory).client('rds')
+        for db in dbs:
+            arn = self.manager.generate_arn(db['DBClusterIdentifier'])
+            client.add_tags_to_resource(ResourceName=arn, Tags=ts)
+
+
+@actions.register('remove-tag')
+@actions.register('unmark')
+class RemoveTag(tags.RemoveTag):
+    """Removes a tag or set of tags from RDS clusters
+
+    :example:
+
+        .. code-block: yaml
+
+            policies:
+              - name: rds-unmark-cluster
+                resource: rds-cluster
+                filters:
+                  - "tag:ExpiredTag": present
+                actions:
+                  - type: unmark
+                    tags: ["ExpiredTag"]
+    """
+
+    concurrency = 2
+    batch_size = 5
+    permissions = ('rds:RemoveTagsFromResource',)
+
+    def process_resource_set(self, dbs, tag_keys):
+        client = local_session(
+            self.manager.session_factory).client('rds')
+        for db in dbs:
+            arn = self.manager.generate_arn(db['DBClusterIdentifier'])
+            client.remove_tags_from_resource(
+                ResourceName=arn, TagKeys=tag_keys)
+
+
 @filters.register('security-group')
 class SecurityGroupFilter(net_filters.SecurityGroupFilter):
 


Test patch:
diff --git a/tests/data/placebo/test_rdscluster_mark_and_match/rds.AddTagsToResource_1.json b/tests/data/placebo/test_rdscluster_mark_and_match/rds.AddTagsToResource_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_mark_and_match/rds.AddTagsToResource_1.json
@@ -0,0 +1,16 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "66823bbd-6bd0-11e7-b392-6379faaa3b00", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "66823bbd-6bd0-11e7-b392-6379faaa3b00", 
+                "date": "Tue, 18 Jul 2017 15:47:27 GMT", 
+                "content-length": "213", 
+                "content-type": "text/xml"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_mark_and_match/rds.DescribeDBClusters_1.json b/tests/data/placebo/test_rdscluster_mark_and_match/rds.DescribeDBClusters_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_mark_and_match/rds.DescribeDBClusters_1.json
@@ -0,0 +1,99 @@
+{
+    "status_code": 200, 
+    "data": {
+        "DBClusters": [
+            {
+                "MasterUsername": "c7ntest", 
+                "ReaderEndpoint": "c7ntest.cluster-ro-c4afdoxtvswa.us-east-2.rds.amazonaws.com", 
+                "ReadReplicaIdentifiers": [], 
+                "VpcSecurityGroups": [
+                    {
+                        "Status": "active", 
+                        "VpcSecurityGroupId": "sg-8b788fe2"
+                    }
+                ], 
+                "HostedZoneId": "Z2XHWR1WZ565X2", 
+                "Status": "available", 
+                "MultiAZ": true, 
+                "LatestRestorableTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 57, 
+                    "microsecond": 477000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 45
+                }, 
+                "PreferredBackupWindow": "09:10-09:40", 
+                "DBSubnetGroup": "default", 
+                "AllocatedStorage": 1, 
+                "BackupRetentionPeriod": 1, 
+                "PreferredMaintenanceWindow": "sat:03:02-sat:03:32", 
+                "Engine": "aurora", 
+                "Endpoint": "c7ntest.cluster-c4afdoxtvswa.us-east-2.rds.amazonaws.com", 
+                "AssociatedRoles": [], 
+                "EarliestRestorableTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 43, 
+                    "microsecond": 758000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 22
+                }, 
+                "IAMDatabaseAuthenticationEnabled": false, 
+                "ClusterCreateTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 44, 
+                    "microsecond": 906000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 21
+                }, 
+                "EngineVersion": "5.6.10a", 
+                "DBClusterIdentifier": "c7ntest", 
+                "DbClusterResourceId": "cluster-R5WF6NWSEGLSIKCJCXV65ANRUE", 
+                "DBClusterMembers": [
+                    {
+                        "IsClusterWriter": true, 
+                        "DBClusterParameterGroupStatus": "in-sync", 
+                        "PromotionTier": 1, 
+                        "DBInstanceIdentifier": "c7ntest"
+                    }, 
+                    {
+                        "IsClusterWriter": false, 
+                        "DBClusterParameterGroupStatus": "in-sync", 
+                        "PromotionTier": 1, 
+                        "DBInstanceIdentifier": "c7ntest-us-east-2b"
+                    }
+                ], 
+                "DBClusterArn": "arn:aws:rds:us-east-2:123456789012:cluster:c7ntest", 
+                "StorageEncrypted": false, 
+                "DatabaseName": "c7ntest", 
+                "DBClusterParameterGroup": "default.aurora5.6", 
+                "AvailabilityZones": [
+                    "us-east-2a", 
+                    "us-east-2b", 
+                    "us-east-2c"
+                ], 
+                "Port": 3306
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "66326e36-6bd0-11e7-893b-ab96af5b89ad", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "66326e36-6bd0-11e7-893b-ab96af5b89ad", 
+                "vary": "Accept-Encoding", 
+                "content-length": "3030", 
+                "content-type": "text/xml", 
+                "date": "Tue, 18 Jul 2017 15:47:26 GMT"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_mark_and_match/rds.DescribeDBClusters_2.json b/tests/data/placebo/test_rdscluster_mark_and_match/rds.DescribeDBClusters_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_mark_and_match/rds.DescribeDBClusters_2.json
@@ -0,0 +1,99 @@
+{
+    "status_code": 200, 
+    "data": {
+        "DBClusters": [
+            {
+                "MasterUsername": "c7ntest", 
+                "ReaderEndpoint": "c7ntest.cluster-ro-c4afdoxtvswa.us-east-2.rds.amazonaws.com", 
+                "ReadReplicaIdentifiers": [], 
+                "VpcSecurityGroups": [
+                    {
+                        "Status": "active", 
+                        "VpcSecurityGroupId": "sg-8b788fe2"
+                    }
+                ], 
+                "HostedZoneId": "Z2XHWR1WZ565X2", 
+                "Status": "available", 
+                "MultiAZ": true, 
+                "LatestRestorableTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 57, 
+                    "microsecond": 477000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 45
+                }, 
+                "PreferredBackupWindow": "09:10-09:40", 
+                "DBSubnetGroup": "default", 
+                "AllocatedStorage": 1, 
+                "BackupRetentionPeriod": 1, 
+                "PreferredMaintenanceWindow": "sat:03:02-sat:03:32", 
+                "Engine": "aurora", 
+                "Endpoint": "c7ntest.cluster-c4afdoxtvswa.us-east-2.rds.amazonaws.com", 
+                "AssociatedRoles": [], 
+                "EarliestRestorableTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 43, 
+                    "microsecond": 758000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 22
+                }, 
+                "IAMDatabaseAuthenticationEnabled": false, 
+                "ClusterCreateTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 44, 
+                    "microsecond": 906000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 21
+                }, 
+                "EngineVersion": "5.6.10a", 
+                "DBClusterIdentifier": "c7ntest", 
+                "DbClusterResourceId": "cluster-R5WF6NWSEGLSIKCJCXV65ANRUE", 
+                "DBClusterMembers": [
+                    {
+                        "IsClusterWriter": true, 
+                        "DBClusterParameterGroupStatus": "in-sync", 
+                        "PromotionTier": 1, 
+                        "DBInstanceIdentifier": "c7ntest"
+                    }, 
+                    {
+                        "IsClusterWriter": false, 
+                        "DBClusterParameterGroupStatus": "in-sync", 
+                        "PromotionTier": 1, 
+                        "DBInstanceIdentifier": "c7ntest-us-east-2b"
+                    }
+                ], 
+                "DBClusterArn": "arn:aws:rds:us-east-2:123456789012:cluster:c7ntest", 
+                "StorageEncrypted": false, 
+                "DatabaseName": "c7ntest", 
+                "DBClusterParameterGroup": "default.aurora5.6", 
+                "AvailabilityZones": [
+                    "us-east-2a", 
+                    "us-east-2b", 
+                    "us-east-2c"
+                ], 
+                "Port": 3306
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "66f12abc-6bd0-11e7-8ae2-6d1eefe234f2", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "66f12abc-6bd0-11e7-8ae2-6d1eefe234f2", 
+                "vary": "Accept-Encoding", 
+                "content-length": "3030", 
+                "content-type": "text/xml", 
+                "date": "Tue, 18 Jul 2017 15:47:28 GMT"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_mark_and_match/rds.DescribeDBClusters_3.json b/tests/data/placebo/test_rdscluster_mark_and_match/rds.DescribeDBClusters_3.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_mark_and_match/rds.DescribeDBClusters_3.json
@@ -0,0 +1,99 @@
+{
+    "status_code": 200, 
+    "data": {
+        "DBClusters": [
+            {
+                "MasterUsername": "c7ntest", 
+                "ReaderEndpoint": "c7ntest.cluster-ro-c4afdoxtvswa.us-east-2.rds.amazonaws.com", 
+                "ReadReplicaIdentifiers": [], 
+                "VpcSecurityGroups": [
+                    {
+                        "Status": "active", 
+                        "VpcSecurityGroupId": "sg-8b788fe2"
+                    }
+                ], 
+                "HostedZoneId": "Z2XHWR1WZ565X2", 
+                "Status": "available", 
+                "MultiAZ": true, 
+                "LatestRestorableTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 57, 
+                    "microsecond": 477000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 45
+                }, 
+                "PreferredBackupWindow": "09:10-09:40", 
+                "DBSubnetGroup": "default", 
+                "AllocatedStorage": 1, 
+                "BackupRetentionPeriod": 1, 
+                "PreferredMaintenanceWindow": "sat:03:02-sat:03:32", 
+                "Engine": "aurora", 
+                "Endpoint": "c7ntest.cluster-c4afdoxtvswa.us-east-2.rds.amazonaws.com", 
+                "AssociatedRoles": [], 
+                "EarliestRestorableTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 43, 
+                    "microsecond": 758000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 22
+                }, 
+                "IAMDatabaseAuthenticationEnabled": false, 
+                "ClusterCreateTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 44, 
+                    "microsecond": 906000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 21
+                }, 
+                "EngineVersion": "5.6.10a", 
+                "DBClusterIdentifier": "c7ntest", 
+                "DbClusterResourceId": "cluster-R5WF6NWSEGLSIKCJCXV65ANRUE", 
+                "DBClusterMembers": [
+                    {
+                        "IsClusterWriter": true, 
+                        "DBClusterParameterGroupStatus": "in-sync", 
+                        "PromotionTier": 1, 
+                        "DBInstanceIdentifier": "c7ntest"
+                    }, 
+                    {
+                        "IsClusterWriter": false, 
+                        "DBClusterParameterGroupStatus": "in-sync", 
+                        "PromotionTier": 1, 
+                        "DBInstanceIdentifier": "c7ntest-us-east-2b"
+                    }
+                ], 
+                "DBClusterArn": "arn:aws:rds:us-east-2:123456789012:cluster:c7ntest", 
+                "StorageEncrypted": false, 
+                "DatabaseName": "c7ntest", 
+                "DBClusterParameterGroup": "default.aurora5.6", 
+                "AvailabilityZones": [
+                    "us-east-2a", 
+                    "us-east-2b", 
+                    "us-east-2c"
+                ], 
+                "Port": 3306
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "6739570b-6bd0-11e7-a4f7-696ce6484d55", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "6739570b-6bd0-11e7-a4f7-696ce6484d55", 
+                "vary": "Accept-Encoding", 
+                "content-length": "3030", 
+                "content-type": "text/xml", 
+                "date": "Tue, 18 Jul 2017 15:47:28 GMT"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_1.json b/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_1.json
@@ -0,0 +1,22 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "665b79d8-6bd0-11e7-b392-6379faaa3b00", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "665b79d8-6bd0-11e7-b392-6379faaa3b00", 
+                "date": "Tue, 18 Jul 2017 15:47:27 GMT", 
+                "content-length": "438", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": [
+            {
+                "Value": "Resource does not meet policy: delete@2017/07/19", 
+                "Key": "custodian_next"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_2.json b/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_2.json
@@ -0,0 +1,22 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "66cbc7cf-6bd0-11e7-8110-65c21ac11bf6", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "66cbc7cf-6bd0-11e7-8110-65c21ac11bf6", 
+                "date": "Tue, 18 Jul 2017 15:47:27 GMT", 
+                "content-length": "438", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": [
+            {
+                "Value": "Resource does not meet policy: delete@2017/07/19", 
+                "Key": "custodian_next"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_3.json b/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_3.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_3.json
@@ -0,0 +1,22 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "67126e28-6bd0-11e7-8bc1-657a32d20107", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "67126e28-6bd0-11e7-8bc1-657a32d20107", 
+                "date": "Tue, 18 Jul 2017 15:47:27 GMT", 
+                "content-length": "438", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": [
+            {
+                "Value": "Resource does not meet policy: delete@2017/07/19", 
+                "Key": "custodian_next"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_4.json b/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_4.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_4.json
@@ -0,0 +1,22 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "675e924e-6bd0-11e7-a4f7-696ce6484d55", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "675e924e-6bd0-11e7-a4f7-696ce6484d55", 
+                "date": "Tue, 18 Jul 2017 15:47:28 GMT", 
+                "content-length": "438", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": [
+            {
+                "Value": "Resource does not meet policy: delete@2017/07/19", 
+                "Key": "custodian_next"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_5.json b/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_5.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_mark_and_match/rds.ListTagsForResource_5.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "678ea2af-6bd0-11e7-8110-65c21ac11bf6", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "678ea2af-6bd0-11e7-8110-65c21ac11bf6", 
+                "date": "Tue, 18 Jul 2017 15:47:29 GMT", 
+                "content-length": "293", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": []
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_mark_and_match/rds.RemoveTagsFromResource_1.json b/tests/data/placebo/test_rdscluster_mark_and_match/rds.RemoveTagsFromResource_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_mark_and_match/rds.RemoveTagsFromResource_1.json
@@ -0,0 +1,16 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "678071da-6bd0-11e7-8110-65c21ac11bf6", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "678071da-6bd0-11e7-8110-65c21ac11bf6", 
+                "date": "Tue, 18 Jul 2017 15:47:29 GMT", 
+                "content-length": "223", 
+                "content-type": "text/xml"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_tag_and_remove/rds.AddTagsToResource_1.json b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.AddTagsToResource_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.AddTagsToResource_1.json
@@ -0,0 +1,16 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "c483b414-6bce-11e7-a67b-b3f0325f1236", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "c483b414-6bce-11e7-a67b-b3f0325f1236", 
+                "date": "Tue, 18 Jul 2017 15:35:45 GMT", 
+                "content-length": "213", 
+                "content-type": "text/xml"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_tag_and_remove/rds.DescribeDBClusters_1.json b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.DescribeDBClusters_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.DescribeDBClusters_1.json
@@ -0,0 +1,99 @@
+{
+    "status_code": 200, 
+    "data": {
+        "DBClusters": [
+            {
+                "MasterUsername": "c7ntest", 
+                "ReaderEndpoint": "c7ntest.cluster-ro-c4afdoxtvswa.us-east-2.rds.amazonaws.com", 
+                "ReadReplicaIdentifiers": [], 
+                "VpcSecurityGroups": [
+                    {
+                        "Status": "active", 
+                        "VpcSecurityGroupId": "sg-8b788fe2"
+                    }
+                ], 
+                "HostedZoneId": "Z2XHWR1WZ565X2", 
+                "Status": "available", 
+                "MultiAZ": true, 
+                "LatestRestorableTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 1, 
+                    "microsecond": 326000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 34
+                }, 
+                "PreferredBackupWindow": "09:10-09:40", 
+                "DBSubnetGroup": "default", 
+                "AllocatedStorage": 1, 
+                "BackupRetentionPeriod": 1, 
+                "PreferredMaintenanceWindow": "sat:03:02-sat:03:32", 
+                "Engine": "aurora", 
+                "Endpoint": "c7ntest.cluster-c4afdoxtvswa.us-east-2.rds.amazonaws.com", 
+                "AssociatedRoles": [], 
+                "EarliestRestorableTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 43, 
+                    "microsecond": 758000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 22
+                }, 
+                "IAMDatabaseAuthenticationEnabled": false, 
+                "ClusterCreateTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 44, 
+                    "microsecond": 906000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 21
+                }, 
+                "EngineVersion": "5.6.10a", 
+                "DBClusterIdentifier": "c7ntest", 
+                "DbClusterResourceId": "cluster-R5WF6NWSEGLSIKCJCXV65ANRUE", 
+                "DBClusterMembers": [
+                    {
+                        "IsClusterWriter": true, 
+                        "DBClusterParameterGroupStatus": "in-sync", 
+                        "PromotionTier": 1, 
+                        "DBInstanceIdentifier": "c7ntest"
+                    }, 
+                    {
+                        "IsClusterWriter": false, 
+                        "DBClusterParameterGroupStatus": "in-sync", 
+                        "PromotionTier": 1, 
+                        "DBInstanceIdentifier": "c7ntest-us-east-2b"
+                    }
+                ], 
+                "DBClusterArn": "arn:aws:rds:us-east-2:123456789012:cluster:c7ntest", 
+                "StorageEncrypted": false, 
+                "DatabaseName": "c7ntest", 
+                "DBClusterParameterGroup": "default.aurora5.6", 
+                "AvailabilityZones": [
+                    "us-east-2a", 
+                    "us-east-2b", 
+                    "us-east-2c"
+                ], 
+                "Port": 3306
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "c4351f43-6bce-11e7-893b-ab96af5b89ad", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "c4351f43-6bce-11e7-893b-ab96af5b89ad", 
+                "vary": "Accept-Encoding", 
+                "content-length": "3030", 
+                "content-type": "text/xml", 
+                "date": "Tue, 18 Jul 2017 15:35:45 GMT"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_tag_and_remove/rds.DescribeDBClusters_2.json b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.DescribeDBClusters_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.DescribeDBClusters_2.json
@@ -0,0 +1,99 @@
+{
+    "status_code": 200, 
+    "data": {
+        "DBClusters": [
+            {
+                "MasterUsername": "c7ntest", 
+                "ReaderEndpoint": "c7ntest.cluster-ro-c4afdoxtvswa.us-east-2.rds.amazonaws.com", 
+                "ReadReplicaIdentifiers": [], 
+                "VpcSecurityGroups": [
+                    {
+                        "Status": "active", 
+                        "VpcSecurityGroupId": "sg-8b788fe2"
+                    }
+                ], 
+                "HostedZoneId": "Z2XHWR1WZ565X2", 
+                "Status": "available", 
+                "MultiAZ": true, 
+                "LatestRestorableTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 1, 
+                    "microsecond": 326000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 34
+                }, 
+                "PreferredBackupWindow": "09:10-09:40", 
+                "DBSubnetGroup": "default", 
+                "AllocatedStorage": 1, 
+                "BackupRetentionPeriod": 1, 
+                "PreferredMaintenanceWindow": "sat:03:02-sat:03:32", 
+                "Engine": "aurora", 
+                "Endpoint": "c7ntest.cluster-c4afdoxtvswa.us-east-2.rds.amazonaws.com", 
+                "AssociatedRoles": [], 
+                "EarliestRestorableTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 43, 
+                    "microsecond": 758000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 22
+                }, 
+                "IAMDatabaseAuthenticationEnabled": false, 
+                "ClusterCreateTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 7, 
+                    "second": 44, 
+                    "microsecond": 906000, 
+                    "year": 2017, 
+                    "day": 18, 
+                    "minute": 21
+                }, 
+                "EngineVersion": "5.6.10a", 
+                "DBClusterIdentifier": "c7ntest", 
+                "DbClusterResourceId": "cluster-R5WF6NWSEGLSIKCJCXV65ANRUE", 
+                "DBClusterMembers": [
+                    {
+                        "IsClusterWriter": true, 
+                        "DBClusterParameterGroupStatus": "in-sync", 
+                        "PromotionTier": 1, 
+                        "DBInstanceIdentifier": "c7ntest"
+                    }, 
+                    {
+                        "IsClusterWriter": false, 
+                        "DBClusterParameterGroupStatus": "in-sync", 
+                        "PromotionTier": 1, 
+                        "DBInstanceIdentifier": "c7ntest-us-east-2b"
+                    }
+                ], 
+                "DBClusterArn": "arn:aws:rds:us-east-2:123456789012:cluster:c7ntest", 
+                "StorageEncrypted": false, 
+                "DatabaseName": "c7ntest", 
+                "DBClusterParameterGroup": "default.aurora5.6", 
+                "AvailabilityZones": [
+                    "us-east-2a", 
+                    "us-east-2b", 
+                    "us-east-2c"
+                ], 
+                "Port": 3306
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "c4d2978e-6bce-11e7-8bc1-657a32d20107", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "c4d2978e-6bce-11e7-8bc1-657a32d20107", 
+                "vary": "Accept-Encoding", 
+                "content-length": "3030", 
+                "content-type": "text/xml", 
+                "date": "Tue, 18 Jul 2017 15:35:46 GMT"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_tag_and_remove/rds.ListTagsForResource_1.json b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.ListTagsForResource_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.ListTagsForResource_1.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "c45f14de-6bce-11e7-8110-65c21ac11bf6", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "c45f14de-6bce-11e7-8110-65c21ac11bf6", 
+                "date": "Tue, 18 Jul 2017 15:35:45 GMT", 
+                "content-length": "293", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": []
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_tag_and_remove/rds.ListTagsForResource_2.json b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.ListTagsForResource_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.ListTagsForResource_2.json
@@ -0,0 +1,22 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "c4abfc7b-6bce-11e7-b392-6379faaa3b00", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "c4abfc7b-6bce-11e7-b392-6379faaa3b00", 
+                "date": "Tue, 18 Jul 2017 15:35:45 GMT", 
+                "content-length": "390", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": [
+            {
+                "Value": "hello world", 
+                "Key": "xyz"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_tag_and_remove/rds.ListTagsForResource_3.json b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.ListTagsForResource_3.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.ListTagsForResource_3.json
@@ -0,0 +1,22 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "c4f42912-6bce-11e7-b392-6379faaa3b00", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "c4f42912-6bce-11e7-b392-6379faaa3b00", 
+                "date": "Tue, 18 Jul 2017 15:35:46 GMT", 
+                "content-length": "390", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": [
+            {
+                "Value": "hello world", 
+                "Key": "xyz"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_tag_and_remove/rds.ListTagsForResource_4.json b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.ListTagsForResource_4.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.ListTagsForResource_4.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "c5254b38-6bce-11e7-b392-6379faaa3b00", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "c5254b38-6bce-11e7-b392-6379faaa3b00", 
+                "date": "Tue, 18 Jul 2017 15:35:46 GMT", 
+                "content-length": "293", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": []
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rdscluster_tag_and_remove/rds.RemoveTagsFromResource_1.json b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.RemoveTagsFromResource_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rdscluster_tag_and_remove/rds.RemoveTagsFromResource_1.json
@@ -0,0 +1,16 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "c51a9ca9-6bce-11e7-a4f7-696ce6484d55", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "c51a9ca9-6bce-11e7-a4f7-696ce6484d55", 
+                "date": "Tue, 18 Jul 2017 15:35:47 GMT", 
+                "content-length": "223", 
+                "content-type": "text/xml"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/test_rdscluster.py b/tests/test_rdscluster.py
--- a/tests/test_rdscluster.py
+++ b/tests/test_rdscluster.py
@@ -13,7 +13,9 @@
 # limitations under the License.
 from __future__ import absolute_import, division, print_function, unicode_literals
 
+from c7n.executor import MainThreadExecutor
 from c7n.resources.rdscluster import RDSCluster
+from c7n import tags
 
 from .common import BaseTest
 
@@ -156,6 +158,110 @@ def test_rdscluster_tag_augment(self):
         resources = p.run()
         self.assertEqual(len(resources), 1)
 
+    def test_rdscluster_tag_and_remove(self):
+        self.patch(RDSCluster, 'executor_factory', MainThreadExecutor)
+        session_factory = self.replay_flight_data(
+            'test_rdscluster_tag_and_remove'
+        )
+        client = session_factory().client('rds')
+
+        p = self.load_policy({
+            'name': 'rds-cluster-tag',
+            'resource': 'rds-cluster',
+            'filters': [
+                {'DBClusterIdentifier': 'c7ntest'}
+            ],
+            'actions': [
+                {'type': 'tag', 'key': 'xyz', 'value': 'hello world'}
+            ]},
+            session_factory=session_factory)
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+
+        arn = p.resource_manager.generate_arn(
+            resources[0]['DBClusterIdentifier'])
+
+        tags = client.list_tags_for_resource(ResourceName=arn)
+        tag_map = {t['Key']: t['Value'] for t in tags['TagList']}
+        self.assertTrue('xyz' in tag_map)
+
+        policy = self.load_policy({
+            'name': 'rds-cluster-remove-tag',
+            'resource': 'rds-cluster',
+            'filters': [
+                {'tag:xyz': 'not-null'}
+            ],
+            'actions': [
+                {'type': 'remove-tag', 'tags': ['xyz']}
+            ]},
+            session_factory=session_factory)
+        resources = policy.run()
+        self.assertEqual(len(resources), 1)
+
+        tags = client.list_tags_for_resource(ResourceName=arn)
+        tag_map = {t['Key']: t['Value'] for t in tags['TagList']}
+        self.assertFalse('xyz' in tag_map)
+
+    def test_rdscluster_mark_match_unmark(self):
+        session_factory = self.replay_flight_data(
+            'test_rdscluster_mark_and_match'
+        )
+        client = session_factory().client('rds')
+
+        # mark
+        p = self.load_policy({
+            'name': 'rds-mark',
+            'resource': 'rds-cluster',
+            'filters': [
+                {'DBClusterIdentifier': 'c7ntest'}],
+            'actions': [
+                {'type': 'mark-for-op', 'tag': 'custodian_next', 'days': 1,
+                 'op': 'delete'}
+            ]},
+            session_factory=session_factory)
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+
+        # assert marked
+        arn = p.resource_manager.generate_arn(
+            resources[0]['DBClusterIdentifier']
+        )
+        tags = client.list_tags_for_resource(ResourceName=arn)
+        tag_map = {t['Key']: t['Value'] for t in tags['TagList']}
+        self.assertTrue('custodian_next' in tag_map)
+
+        # match marked
+        policy = self.load_policy({
+            'name': 'rds-mark-filter',
+            'resource': 'rds-cluster',
+            'filters': [
+                {'type': 'marked-for-op', 'tag': 'custodian_next',
+                 'op': 'delete', 'skew': 1}
+            ]},
+            session_factory=session_factory)
+        resources = policy.run()
+        self.assertEqual(len(resources), 1)
+
+        # unmark
+        policy = self.load_policy({
+            'name': 'rds-mark-filter',
+            'resource': 'rds-cluster',
+            'filters': [
+                {'type': 'marked-for-op', 'tag': 'custodian_next',
+                 'op': 'delete', 'skew': 1}
+            ],
+            'actions': [
+                {'type': 'unmark', 'tags': ['custodian_next']}
+            ]},
+            session_factory=session_factory)
+        resources = policy.run()
+        self.assertEqual(len(resources), 1)
+
+        # assert unmarked
+        tags = client.list_tags_for_resource(ResourceName=arn)
+        tag_map = {t['Key']: t['Value'] for t in tags['TagList']}
+        self.assertFalse('custodian_next' in tag_map)
+
 
 class RDSClusterSnapshotTest(BaseTest):