Repository: cloud-custodian/cloud-custodian

Issue:
Marking the aws acm certificate that are not in use. 
Can we mark an ACM certificate, that is not in use? Something is like this?

policies:
  - name: acm-certificate-mark-for-ops
    resource: aws.acm-certificate
    filters:
    - type: value
      key: InUse
      value: No
    actions:
    - type: marked-for-op
      tag: custodian_cleanup
      op: delete


Discussion:

Resolving patch:
diff --git a/c7n/resources/acm.py b/c7n/resources/acm.py
--- a/c7n/resources/acm.py
+++ b/c7n/resources/acm.py
@@ -16,7 +16,7 @@
 from c7n.actions import BaseAction
 from c7n.manager import resources
 from c7n.query import QueryResourceManager, DescribeSource, ConfigSource, TypeInfo
-from c7n.tags import universal_augment
+from c7n.tags import universal_augment, register_universal_tags
 from c7n.utils import type_schema, local_session
 
 
@@ -53,6 +53,9 @@ def augment(self, resources):
             super(DescribeCertificate, self).augment(resources))
 
 
+register_universal_tags(Certificate.filter_registry, Certificate.action_registry)
+
+
 @Certificate.action_registry.register('delete')
 class CertificateDeleteAction(BaseAction):
     """Action to delete an ACM Certificate


Test patch:
diff --git a/tests/data/placebo/test_certificate_tag_untag_mark/acm.DescribeCertificate_1.json b/tests/data/placebo/test_certificate_tag_untag_mark/acm.DescribeCertificate_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_certificate_tag_untag_mark/acm.DescribeCertificate_1.json
@@ -0,0 +1,62 @@
+{
+    "status_code": 200,
+    "data": {
+        "Certificate": {
+            "CertificateArn": "arn:aws:acm:us-east-1:644160558196:certificate/70e45883-48b0-4024-88a3-2c145f4fe5c0",
+            "DomainName": "*.pratyush.com",
+            "SubjectAlternativeNames": [
+                "*.pratyush.com"
+            ],
+            "DomainValidationOptions": [
+                {
+                    "DomainName": "*.pratyush.com",
+                    "ValidationEmails": [
+                        "webmaster@pratyush.com",
+                        "postmaster@pratyush.com",
+                        "privacy@1and1.com",
+                        "hostmaster@pratyush.com",
+                        "admin@pratyush.com",
+                        "administrator@pratyush.com"
+                    ],
+                    "ValidationDomain": "pratyush.com",
+                    "ValidationStatus": "PENDING_VALIDATION",
+                    "ValidationMethod": "EMAIL"
+                }
+            ],
+            "Subject": "CN=*.pratyush.com",
+            "Issuer": "Amazon",
+            "CreatedAt": {
+                "__class__": "datetime",
+                "year": 2019,
+                "month": 8,
+                "day": 6,
+                "hour": 11,
+                "minute": 53,
+                "second": 30,
+                "microsecond": 0
+            },
+            "Status": "PENDING_VALIDATION",
+            "KeyAlgorithm": "RSA-2048",
+            "SignatureAlgorithm": "SHA256WITHRSA",
+            "InUseBy": [],
+            "Type": "AMAZON_ISSUED",
+            "KeyUsages": [],
+            "ExtendedKeyUsages": [],
+            "RenewalEligibility": "INELIGIBLE",
+            "Options": {
+                "CertificateTransparencyLoggingPreference": "ENABLED"
+            }
+        },
+        "ResponseMetadata": {
+            "RequestId": "3657835c-1f65-48c4-8b2b-3394e9b397c3",
+            "HTTPStatusCode": 200,
+            "HTTPHeaders": {
+                "x-amzn-requestid": "3657835c-1f65-48c4-8b2b-3394e9b397c3",
+                "content-type": "application/x-amz-json-1.1",
+                "content-length": "863",
+                "date": "Tue, 06 Aug 2019 18:24:12 GMT"
+            },
+            "RetryAttempts": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_certificate_tag_untag_mark/acm.ListCertificates_1.json b/tests/data/placebo/test_certificate_tag_untag_mark/acm.ListCertificates_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_certificate_tag_untag_mark/acm.ListCertificates_1.json
@@ -0,0 +1,22 @@
+{
+    "status_code": 200,
+    "data": {
+        "CertificateSummaryList": [
+            {
+                "CertificateArn": "arn:aws:acm:us-east-1:644160558196:certificate/70e45883-48b0-4024-88a3-2c145f4fe5c0",
+                "DomainName": "*.pratyush.com"
+            }
+        ],
+        "ResponseMetadata": {
+            "RequestId": "cc249f5e-9557-41fb-a4e1-d1bfc8c19c6f",
+            "HTTPStatusCode": 200,
+            "HTTPHeaders": {
+                "x-amzn-requestid": "cc249f5e-9557-41fb-a4e1-d1bfc8c19c6f",
+                "content-type": "application/x-amz-json-1.1",
+                "content-length": "163",
+                "date": "Tue, 06 Aug 2019 18:24:12 GMT"
+            },
+            "RetryAttempts": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_certificate_tag_untag_mark/acm.ListTagsForCertificate_1.json b/tests/data/placebo/test_certificate_tag_untag_mark/acm.ListTagsForCertificate_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_certificate_tag_untag_mark/acm.ListTagsForCertificate_1.json
@@ -0,0 +1,22 @@
+{
+    "status_code": 200,
+    "data": {
+        "Tags": [
+            {
+                "Key": "custodian_cleanup",
+                "Value": "Resource does not meet policy: delete@2019/08/07"
+            }
+        ],
+        "ResponseMetadata": {
+            "RequestId": "6e4df3da-c851-4084-9ec3-6f7fa93e2933",
+            "HTTPStatusCode": 200,
+            "HTTPHeaders": {
+                "x-amzn-requestid": "6e4df3da-c851-4084-9ec3-6f7fa93e2933",
+                "content-type": "application/x-amz-json-1.1",
+                "content-length": "97",
+                "date": "Tue, 06 Aug 2019 18:24:13 GMT"
+            },
+            "RetryAttempts": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_certificate_tag_untag_mark/tagging.GetResources_1.json b/tests/data/placebo/test_certificate_tag_untag_mark/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_certificate_tag_untag_mark/tagging.GetResources_1.json
@@ -0,0 +1,28 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [
+            {
+                "ResourceARN": "arn:aws:acm:us-east-1:644160558196:certificate/70e45883-48b0-4024-88a3-2c145f4fe5c0",
+                "Tags": [
+                    {
+                        "Key": "target-tag",
+                        "Value": "target-tag-value"
+                    }
+                ]
+            }
+        ],
+        "ResponseMetadata": {
+            "RequestId": "7b71f071-6887-4ea3-8e17-38810a22d947",
+            "HTTPStatusCode": 200,
+            "HTTPHeaders": {
+                "x-amzn-requestid": "7b71f071-6887-4ea3-8e17-38810a22d947",
+                "content-type": "application/x-amz-json-1.1",
+                "content-length": "208",
+                "date": "Tue, 06 Aug 2019 18:24:12 GMT"
+            },
+            "RetryAttempts": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_certificate_tag_untag_mark/tagging.TagResources_1.json b/tests/data/placebo/test_certificate_tag_untag_mark/tagging.TagResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_certificate_tag_untag_mark/tagging.TagResources_1.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200,
+    "data": {
+        "FailedResourcesMap": {},
+        "ResponseMetadata": {
+            "RequestId": "e916ffc9-c6ba-407f-a4d3-a5c75c118787",
+            "HTTPStatusCode": 200,
+            "HTTPHeaders": {
+                "x-amzn-requestid": "e916ffc9-c6ba-407f-a4d3-a5c75c118787",
+                "content-type": "application/x-amz-json-1.1",
+                "content-length": "25",
+                "date": "Tue, 06 Aug 2019 18:24:13 GMT"
+            },
+            "RetryAttempts": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_certificate_tag_untag_mark/tagging.UntagResources_1.json b/tests/data/placebo/test_certificate_tag_untag_mark/tagging.UntagResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_certificate_tag_untag_mark/tagging.UntagResources_1.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200,
+    "data": {
+        "FailedResourcesMap": {},
+        "ResponseMetadata": {
+            "RequestId": "61acf6e0-1b7c-4fb6-a23d-eb682b3f09fd",
+            "HTTPStatusCode": 200,
+            "HTTPHeaders": {
+                "x-amzn-requestid": "61acf6e0-1b7c-4fb6-a23d-eb682b3f09fd",
+                "content-type": "application/x-amz-json-1.1",
+                "content-length": "25",
+                "date": "Tue, 06 Aug 2019 18:24:13 GMT"
+            },
+            "RetryAttempts": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/test_acm.py b/tests/test_acm.py
--- a/tests/test_acm.py
+++ b/tests/test_acm.py
@@ -59,3 +59,23 @@ def test_certificate_delete_in_use_error(self):
         client = factory().client("acm")
         arn = "arn:aws:acm:us-west-2:644160558196:certificate/b867707e-33c3-4024-b45d-b6133c3b4c05"
         self.assertTrue(client.get_certificate(CertificateArn=arn))
+
+    def test_certificate_tag_untag_mark(self):
+        factory = self.replay_flight_data("test_certificate_tag_untag_mark")
+        p = self.load_policy(
+            {
+                "name": "acm-tag",
+                "resource": "acm-certificate",
+                "filters": [{"tag:target-tag": "present"}],
+                "actions": [
+                    {"type": "remove-tag", "tags": ["target-tag"]},
+                    {"type": "mark-for-op", "tag": "custodian_cleanup", "op": "delete", "days": 1}],
+            },
+            session_factory=factory,
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        client = factory().client("acm")
+        tag = client.list_tags_for_certificate(CertificateArn=resources[0]['CertificateArn'])
+        self.assertEqual(len(tag.get('Tags')), 1)
+        self.assertEqual(tag.get('Tags')[0]['Key'], "custodian_cleanup")