Repository: cloud-custodian/cloud-custodian

Issue:
Incorrect parsing of Data LifeCycle Manager policy tags
### Describe the bug

When running a dlm policy that uses tags in the filter, get the error: AttributeError: 'str' object has no attribute 'get'


### What did you expect to happen?

Expected c7n to filter the resources based on given tag values.

### Cloud Provider

Amazon Web Services (AWS)

### Cloud Custodian version and dependency information

```shell
Custodian:   0.9.18
Python:      3.9.7 (tags/v3.9.7:1016ef3, Aug 30 2021, 20:19:38) [MSC v.1929 64 bit (AMD64)]
Platform:   win32
Using venv:  True
Docker: False
Installed:

argcomplete==2.0.0
attrs==22.1.0
boto3==1.24.44
botocore==1.27.44
docutils==0.17.1
importlib-metadata==4.12.0
importlib-resources==5.7.1
jmespath==1.0.1
jsonschema==4.9.0
pyrsistent==0.18.1
python-dateutil==2.8.2
pyyaml==6.0
s3transfer==0.6.0
six==1.16.0
tabulate==0.8.10
typing-extensions==4.2.0
urllib3==1.26.11
zipp==3.8.0
```


### Policy

```shell
policies:

  - name: no-error
    resource: aws.dlm-policy
    description: "This policy does not hit the error"
    filters:
      - type: value
        key: StatusMessage
        op: not-equal
        value: "ENABLED"

  - name: error
    resource: aws.dlm-policy
    description: "This policy errors due to tag bug in filter"
    filters:
      - type: value
        key: StatusMessage
        op: not-equal
        value: "ENABLED"
      - not:
        - "tag:some-tag": "some-value"

  - name: error2
    resource: aws.dlm-policy
    description: "This policy also errors due to tag bug in filter"
    filters:
      - type: value
        key: "tag:some-tag"
        value: absent
```


### Relevant log/traceback output

```shell
2022-08-29 09:05:07,591: custodian.policy:INFO policy:no-error resource:aws.dlm-policy region:us-east-2 count:1 time:0.00
2022-08-29 09:05:07,628: custodian.output:ERROR Error while executing policy
Traceback (most recent call last):
  File "C:\Users\user\custodian\lib\site-packages\c7n\policy.py", line 293, in run
    resources = self.policy.resource_manager.resources()
  File "C:\Users\user\custodian\lib\site-packages\c7n\query.py", line 532, in resources
    resources = self.filter_resources(resources)
  File "C:\Users\user\custodian\lib\site-packages\c7n\manager.py", line 111, in filter_resources
    resources = f.process(resources, event)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 378, in process
    return self.process_set(resources, event)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 397, in process_set
    resources = f.process(resources, event)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 553, in process
    return super(ValueFilter, self).process(resources, event)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 190, in process
    return list(filter(self, resources))
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 540, in __call__
    matched = self.match(i)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 576, in match
    r = self.get_resource_value(self.k, i)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 556, in get_resource_value
    return super(ValueFilter, self).get_resource_value(k, i, self.data.get('value_regex'))
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 236, in get_resource_value
    if t.get('Key') == tk:
AttributeError: 'str' object has no attribute 'get'
2022-08-29 09:05:07,649: custodian.commands:ERROR Error while executing policy error, continuing
Traceback (most recent call last):
  File "C:\Users\user\custodian\lib\site-packages\c7n\commands.py", line 301, in run
    policy()
  File "C:\Users\user\custodian\lib\site-packages\c7n\policy.py", line 1286, in __call__
    resources = mode.run()
  File "C:\Users\user\custodian\lib\site-packages\c7n\policy.py", line 293, in run
    resources = self.policy.resource_manager.resources()
  File "C:\Users\user\custodian\lib\site-packages\c7n\query.py", line 532, in resources
    resources = self.filter_resources(resources)
  File "C:\Users\user\custodian\lib\site-packages\c7n\manager.py", line 111, in filter_resources
    resources = f.process(resources, event)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 378, in process
    return self.process_set(resources, event)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 397, in process_set
    resources = f.process(resources, event)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 553, in process
    return super(ValueFilter, self).process(resources, event)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 190, in process
    return list(filter(self, resources))
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 540, in __call__
    matched = self.match(i)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 576, in match
    r = self.get_resource_value(self.k, i)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 556, in get_resource_value
    return super(ValueFilter, self).get_resource_value(k, i, self.data.get('value_regex'))
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 236, in get_resource_value
    if t.get('Key') == tk:
AttributeError: 'str' object has no attribute 'get'
2022-08-29 09:05:07,675: custodian.output:ERROR Error while executing policy
Traceback (most recent call last):
  File "C:\Users\user\custodian\lib\site-packages\c7n\policy.py", line 293, in run
    resources = self.policy.resource_manager.resources()
  File "C:\Users\user\custodian\lib\site-packages\c7n\query.py", line 532, in resources
    resources = self.filter_resources(resources)
  File "C:\Users\user\custodian\lib\site-packages\c7n\manager.py", line 111, in filter_resources
    resources = f.process(resources, event)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 553, in process
    return super(ValueFilter, self).process(resources, event)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 190, in process
    return list(filter(self, resources))
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 540, in __call__
    matched = self.match(i)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 576, in match
    r = self.get_resource_value(self.k, i)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 556, in get_resource_value
    return super(ValueFilter, self).get_resource_value(k, i, self.data.get('value_regex'))
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 236, in get_resource_value
    if t.get('Key') == tk:
AttributeError: 'str' object has no attribute 'get'
2022-08-29 09:05:07,690: custodian.commands:ERROR Error while executing policy error2, continuing
Traceback (most recent call last):
  File "C:\Users\user\custodian\lib\site-packages\c7n\commands.py", line 301, in run
    policy()
  File "C:\Users\user\custodian\lib\site-packages\c7n\policy.py", line 1286, in __call__
    resources = mode.run()
  File "C:\Users\user\custodian\lib\site-packages\c7n\policy.py", line 293, in run
    resources = self.policy.resource_manager.resources()
  File "C:\Users\user\custodian\lib\site-packages\c7n\query.py", line 532, in resources
    resources = self.filter_resources(resources)
  File "C:\Users\user\custodian\lib\site-packages\c7n\manager.py", line 111, in filter_resources
    resources = f.process(resources, event)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 553, in process
    return super(ValueFilter, self).process(resources, event)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 190, in process
    return list(filter(self, resources))
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 540, in __call__
    matched = self.match(i)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 576, in match
    r = self.get_resource_value(self.k, i)
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 556, in get_resource_value
    return super(ValueFilter, self).get_resource_value(k, i, self.data.get('value_regex'))
  File "C:\Users\user\custodian\lib\site-packages\c7n\filters\core.py", line 236, in get_resource_value
    if t.get('Key') == tk:
AttributeError: 'str' object has no attribute 'get'
2022-08-29 09:05:07,694: custodian.commands:ERROR The following policies had errors while executing
 - error
 - error2
```


### Extra information or context

It appears that the c7n filter code expects the returned tag info to be an array of objects with 'Key' and 'Value' keys, but the DLM API returns a 'Tags' object with each tag being a key/value.

```
$ aws dlm get-lifecycle-policies
{
    "Policies": [
        {
            "PolicyId": "policy-003ec5a7028453d10",
            "Description": "testing",
            "State": "DISABLED",
            "Tags": {
                "k1": "v1",
                "k2": "v2"
            },
            "PolicyType": "EBS_SNAPSHOT_MANAGEMENT"
        }
    ]
}
```


Discussion:

Resolving patch:
diff --git a/c7n/resources/dlm.py b/c7n/resources/dlm.py
--- a/c7n/resources/dlm.py
+++ b/c7n/resources/dlm.py
@@ -2,6 +2,8 @@
 # SPDX-License-Identifier: Apache-2.0
 from c7n.manager import resources
 from c7n.query import QueryResourceManager, TypeInfo
+from c7n.tags import Tag, RemoveTag
+from c7n.utils import get_partition
 
 
 @resources.register('dlm-policy')
@@ -15,5 +17,46 @@ class resource_type(TypeInfo):
         detail_spec = ('get_lifecycle_policy', 'PolicyId', 'PolicyId', 'Policy')
         filter_name = 'PolicyIds'
         filter_type = 'list'
-        arn = False
+        arn = True
         cfn_type = 'AWS::DLM::LifecyclePolicy'
+        # arn:aws:dlm:us-east-1:532725030595:policy/policy-0e23a047d0fdb7761
+
+    def augment(self, resources):
+        super().augment(resources)
+        for r in resources:
+            r['Tags'] = [{'Key': k, 'Value': v} for k, v in r.get('Tags', {}).items()]
+        return resources
+
+    def get_arns(self, resources):
+        partition = get_partition(self.region)
+        return [
+            f"arn:{partition}:dlm:{self.region}:{self.account_id}:policy/{r['PolicyId']}"
+            for r in resources
+        ]
+
+
+@DLMPolicy.action_registry.register('tag')
+class TagDLMPolicy(Tag):
+
+    permissions = ('dlm:TagResource', )
+
+    def process_resource_set(self, client, resource_set, tags):
+        arns = self.manager.get_arns(resource_set)
+        for arn in arns:
+            client.tag_resource(
+                ResourceArn=arn,
+                Tags={t['Key']: t['Value'] for t in tags})
+
+
+@DLMPolicy.action_registry.register('remove-tag')
+class DLMPolicyRemoveTag(RemoveTag):
+
+    permissions = ('dlm:UntagResource', )
+
+    def process_resource_set(self, client, resource_set, tag_keys):
+        arns = self.manager.get_arns(resource_set)
+        for arn in arns:
+            client.untag_resource(
+                ResourceArn=arn,
+                TagKeys=tag_keys
+            )


Test patch:
diff --git a/tests/data/placebo/test_dlm_remove_tag/dlm.GetLifecyclePolicies_1.json b/tests/data/placebo/test_dlm_remove_tag/dlm.GetLifecyclePolicies_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_dlm_remove_tag/dlm.GetLifecyclePolicies_1.json
@@ -0,0 +1,18 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "Policies": [
+            {
+                "PolicyId": "policy-0e23a047d0fdb7761",
+                "Description": "test",
+                "State": "ENABLED",
+                "Tags": {
+                    "asdf": "qwer",
+                    "foo": "bar"
+                },
+                "PolicyType": "EBS_SNAPSHOT_MANAGEMENT"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_dlm_remove_tag/dlm.GetLifecyclePolicies_2.json b/tests/data/placebo/test_dlm_remove_tag/dlm.GetLifecyclePolicies_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_dlm_remove_tag/dlm.GetLifecyclePolicies_2.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "Policies": [
+            {
+                "PolicyId": "policy-0e23a047d0fdb7761",
+                "Description": "test",
+                "State": "ENABLED",
+                "Tags": {
+                    "asdf": "qwer"
+                },
+                "PolicyType": "EBS_SNAPSHOT_MANAGEMENT"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_dlm_remove_tag/dlm.GetLifecyclePolicy_1.json b/tests/data/placebo/test_dlm_remove_tag/dlm.GetLifecyclePolicy_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_dlm_remove_tag/dlm.GetLifecyclePolicy_1.json
@@ -0,0 +1,71 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "Policy": {
+            "PolicyId": "policy-0e23a047d0fdb7761",
+            "Description": "test",
+            "State": "ENABLED",
+            "StatusMessage": "ENABLED",
+            "ExecutionRoleArn": "arn:aws:iam::644160558196:role/service-role/AWSDataLifecycleManagerDefaultRole",
+            "DateCreated": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 29,
+                "hour": 11,
+                "minute": 1,
+                "second": 18,
+                "microsecond": 841000
+            },
+            "DateModified": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 29,
+                "hour": 11,
+                "minute": 1,
+                "second": 19,
+                "microsecond": 18000
+            },
+            "PolicyDetails": {
+                "PolicyType": "EBS_SNAPSHOT_MANAGEMENT",
+                "ResourceTypes": [
+                    "VOLUME"
+                ],
+                "ResourceLocations": [
+                    "CLOUD"
+                ],
+                "TargetTags": [
+                    {
+                        "Key": "ActionNeeded",
+                        "Value": "foo"
+                    }
+                ],
+                "Schedules": [
+                    {
+                        "Name": "Schedule 1",
+                        "CopyTags": false,
+                        "CreateRule": {
+                            "Location": "CLOUD",
+                            "Interval": 12,
+                            "IntervalUnit": "HOURS",
+                            "Times": [
+                                "09:00"
+                            ]
+                        },
+                        "RetainRule": {
+                            "Count": 1,
+                            "Interval": 0
+                        }
+                    }
+                ]
+            },
+            "Tags": {
+                "asdf": "qwer",
+                "foo": "bar"
+            },
+            "PolicyArn": "arn:aws:dlm:us-east-1:644160558196:policy/policy-0e23a047d0fdb7761"
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_dlm_remove_tag/dlm.GetLifecyclePolicy_2.json b/tests/data/placebo/test_dlm_remove_tag/dlm.GetLifecyclePolicy_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_dlm_remove_tag/dlm.GetLifecyclePolicy_2.json
@@ -0,0 +1,70 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "Policy": {
+            "PolicyId": "policy-0e23a047d0fdb7761",
+            "Description": "test",
+            "State": "ENABLED",
+            "StatusMessage": "ENABLED",
+            "ExecutionRoleArn": "arn:aws:iam::644160558196:role/service-role/AWSDataLifecycleManagerDefaultRole",
+            "DateCreated": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 29,
+                "hour": 11,
+                "minute": 1,
+                "second": 18,
+                "microsecond": 841000
+            },
+            "DateModified": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 29,
+                "hour": 11,
+                "minute": 1,
+                "second": 19,
+                "microsecond": 18000
+            },
+            "PolicyDetails": {
+                "PolicyType": "EBS_SNAPSHOT_MANAGEMENT",
+                "ResourceTypes": [
+                    "VOLUME"
+                ],
+                "ResourceLocations": [
+                    "CLOUD"
+                ],
+                "TargetTags": [
+                    {
+                        "Key": "ActionNeeded",
+                        "Value": "foo"
+                    }
+                ],
+                "Schedules": [
+                    {
+                        "Name": "Schedule 1",
+                        "CopyTags": false,
+                        "CreateRule": {
+                            "Location": "CLOUD",
+                            "Interval": 12,
+                            "IntervalUnit": "HOURS",
+                            "Times": [
+                                "09:00"
+                            ]
+                        },
+                        "RetainRule": {
+                            "Count": 1,
+                            "Interval": 0
+                        }
+                    }
+                ]
+            },
+            "Tags": {
+                "asdf": "qwer"
+            },
+            "PolicyArn": "arn:aws:dlm:us-east-1:644160558196:policy/policy-0e23a047d0fdb7761"
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_dlm_remove_tag/dlm.UntagResource_1.json b/tests/data/placebo/test_dlm_remove_tag/dlm.UntagResource_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_dlm_remove_tag/dlm.UntagResource_1.json
@@ -0,0 +1,6 @@
+{
+    "status_code": 204,
+    "data": {
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_dlm_tag/dlm.GetLifecyclePolicies_1.json b/tests/data/placebo/test_dlm_tag/dlm.GetLifecyclePolicies_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_dlm_tag/dlm.GetLifecyclePolicies_1.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "Policies": [
+            {
+                "PolicyId": "policy-0e23a047d0fdb7761",
+                "Description": "test",
+                "State": "ENABLED",
+                "Tags": {
+                    "asdf": "qwer"
+                },
+                "PolicyType": "EBS_SNAPSHOT_MANAGEMENT"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_dlm_tag/dlm.GetLifecyclePolicies_2.json b/tests/data/placebo/test_dlm_tag/dlm.GetLifecyclePolicies_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_dlm_tag/dlm.GetLifecyclePolicies_2.json
@@ -0,0 +1,18 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "Policies": [
+            {
+                "PolicyId": "policy-0e23a047d0fdb7761",
+                "Description": "test",
+                "State": "ENABLED",
+                "Tags": {
+                    "asdf": "qwer",
+                    "bar": "baz"
+                },
+                "PolicyType": "EBS_SNAPSHOT_MANAGEMENT"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_dlm_tag/dlm.GetLifecyclePolicy_1.json b/tests/data/placebo/test_dlm_tag/dlm.GetLifecyclePolicy_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_dlm_tag/dlm.GetLifecyclePolicy_1.json
@@ -0,0 +1,70 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "Policy": {
+            "PolicyId": "policy-0e23a047d0fdb7761",
+            "Description": "test",
+            "State": "ENABLED",
+            "StatusMessage": "ENABLED",
+            "ExecutionRoleArn": "arn:aws:iam::644160558196:role/service-role/AWSDataLifecycleManagerDefaultRole",
+            "DateCreated": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 29,
+                "hour": 11,
+                "minute": 1,
+                "second": 18,
+                "microsecond": 841000
+            },
+            "DateModified": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 29,
+                "hour": 11,
+                "minute": 1,
+                "second": 19,
+                "microsecond": 18000
+            },
+            "PolicyDetails": {
+                "PolicyType": "EBS_SNAPSHOT_MANAGEMENT",
+                "ResourceTypes": [
+                    "VOLUME"
+                ],
+                "ResourceLocations": [
+                    "CLOUD"
+                ],
+                "TargetTags": [
+                    {
+                        "Key": "ActionNeeded",
+                        "Value": "foo"
+                    }
+                ],
+                "Schedules": [
+                    {
+                        "Name": "Schedule 1",
+                        "CopyTags": false,
+                        "CreateRule": {
+                            "Location": "CLOUD",
+                            "Interval": 12,
+                            "IntervalUnit": "HOURS",
+                            "Times": [
+                                "09:00"
+                            ]
+                        },
+                        "RetainRule": {
+                            "Count": 1,
+                            "Interval": 0
+                        }
+                    }
+                ]
+            },
+            "Tags": {
+                "asdf": "qwer"
+            },
+            "PolicyArn": "arn:aws:dlm:us-east-1:644160558196:policy/policy-0e23a047d0fdb7761"
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_dlm_tag/dlm.GetLifecyclePolicy_2.json b/tests/data/placebo/test_dlm_tag/dlm.GetLifecyclePolicy_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_dlm_tag/dlm.GetLifecyclePolicy_2.json
@@ -0,0 +1,71 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "Policy": {
+            "PolicyId": "policy-0e23a047d0fdb7761",
+            "Description": "test",
+            "State": "ENABLED",
+            "StatusMessage": "ENABLED",
+            "ExecutionRoleArn": "arn:aws:iam::644160558196:role/service-role/AWSDataLifecycleManagerDefaultRole",
+            "DateCreated": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 29,
+                "hour": 11,
+                "minute": 1,
+                "second": 18,
+                "microsecond": 841000
+            },
+            "DateModified": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 29,
+                "hour": 11,
+                "minute": 1,
+                "second": 19,
+                "microsecond": 18000
+            },
+            "PolicyDetails": {
+                "PolicyType": "EBS_SNAPSHOT_MANAGEMENT",
+                "ResourceTypes": [
+                    "VOLUME"
+                ],
+                "ResourceLocations": [
+                    "CLOUD"
+                ],
+                "TargetTags": [
+                    {
+                        "Key": "ActionNeeded",
+                        "Value": "foo"
+                    }
+                ],
+                "Schedules": [
+                    {
+                        "Name": "Schedule 1",
+                        "CopyTags": false,
+                        "CreateRule": {
+                            "Location": "CLOUD",
+                            "Interval": 12,
+                            "IntervalUnit": "HOURS",
+                            "Times": [
+                                "09:00"
+                            ]
+                        },
+                        "RetainRule": {
+                            "Count": 1,
+                            "Interval": 0
+                        }
+                    }
+                ]
+            },
+            "Tags": {
+                "asdf": "qwer",
+                "bar": "baz"
+            },
+            "PolicyArn": "arn:aws:dlm:us-east-1:644160558196:policy/policy-0e23a047d0fdb7761"
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_dlm_tag/dlm.TagResource_1.json b/tests/data/placebo/test_dlm_tag/dlm.TagResource_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_dlm_tag/dlm.TagResource_1.json
@@ -0,0 +1,6 @@
+{
+    "status_code": 204,
+    "data": {
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/test_dlm.py b/tests/test_dlm.py
--- a/tests/test_dlm.py
+++ b/tests/test_dlm.py
@@ -26,3 +26,75 @@ def test_dlm_query(self):
                  'Name': 'Default Schedule',
                  'RetainRule': {'Count': 5}}],
              'TargetTags': [{'Key': 'App', 'Value': 'Zebra'}]})
+
+    def test_dlm_tag(self):
+        factory = self.replay_flight_data('test_dlm_tag')
+        p = self.load_policy(
+            {
+                'name': 'tag-dlm',
+                'resource': 'dlm-policy',
+                'filters': [
+                    {'tag:bar': 'absent'}
+                ],
+                'actions': [
+                    {
+                        'type': 'tag',
+                        'tags': {
+                            'bar': 'baz'
+                        }
+                    }
+                ]
+            },
+            session_factory=factory
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+
+        p = self.load_policy(
+            {
+                'name': 'tag-dlm',
+                'resource': 'dlm-policy',
+                'filters': [
+                    {'tag:bar': 'present'}
+                ]
+            },
+            session_factory=factory
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+
+    def test_dlm_remove_tag(self):
+        factory = self.replay_flight_data('test_dlm_remove_tag')
+        p = self.load_policy(
+            {
+                'name': 'tag-dlm',
+                'resource': 'dlm-policy',
+                'filters': [
+                    {'tag:foo': 'present'}
+                ],
+                'actions': [
+                    {
+                        'type': 'remove-tag',
+                        'tags': [
+                            'foo'
+                        ]
+                    }
+                ]
+            },
+            session_factory=factory
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+
+        p = self.load_policy(
+            {
+                'name': 'tag-dlm',
+                'resource': 'dlm-policy',
+                'filters': [
+                    {'tag:foo': 'present'}
+                ]
+            },
+            session_factory=factory
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 0)
diff --git a/tests/test_policy.py b/tests/test_policy.py
--- a/tests/test_policy.py
+++ b/tests/test_policy.py
@@ -189,7 +189,7 @@ def test_resource_arn_override_generator(self):
 
         overrides = overrides.difference(
             {'account', 's3', 'hostedzone', 'log-group', 'rest-api', 'redshift-snapshot',
-             'rest-stage', 'codedeploy-app', 'codedeploy-group', 'fis-template'})
+             'rest-stage', 'codedeploy-app', 'codedeploy-group', 'fis-template', 'dlm-policy', })
         if overrides:
             raise ValueError("unknown arn overrides in %s" % (", ".join(overrides)))