Repository: cloud-custodian/cloud-custodian

Issue:
aws - delete action of directory/cloud-directory services
### Describe the feature

Add delete action to directory and cloud-directory.

### Extra information or context

_No response_


Discussion:

Resolving patch:
diff --git a/c7n/resources/directory.py b/c7n/resources/directory.py
--- a/c7n/resources/directory.py
+++ b/c7n/resources/directory.py
@@ -2,9 +2,10 @@
 # SPDX-License-Identifier: Apache-2.0
 from c7n.manager import resources
 from c7n.query import QueryResourceManager, TypeInfo
-from c7n.utils import local_session
+from c7n.utils import local_session, type_schema, QueryParser
 from c7n.filters.vpc import SecurityGroupFilter, SubnetFilter, VpcFilter
 from c7n.tags import Tag, RemoveTag, universal_augment, TagDelayedAction, TagActionFilter
+from c7n.actions import BaseAction
 
 
 @resources.register('directory')
@@ -109,6 +110,34 @@ def process_resource_set(self, client, directories, tags):
 Directory.filter_registry.register('marked-for-op', TagActionFilter)
 Directory.action_registry.register('mark-for-op', TagDelayedAction)
 
+@Directory.action_registry.register('delete')
+class DirectoryDelete(BaseAction):
+    """Delete a directory.
+
+    :example:
+
+    .. code-block:: yaml
+
+            policies:
+              - name: delete-directory
+                resource: aws.directory
+                filters:
+                    - Name: test.example.com
+                actions:
+                  - delete
+    """
+    schema = type_schema('delete')
+    permissions = ('ds:DeleteDirectory',)
+
+    def process(self, resources):
+        client = local_session(
+            self.manager.session_factory).client('ds')
+
+        for r in resources:
+            self.manager.retry(
+                client.delete_directory,
+                DirectoryId=r['DirectoryId'])
+
 
 @resources.register('cloud-directory')
 class CloudDirectory(QueryResourceManager):
@@ -122,3 +151,75 @@ class resource_type(TypeInfo):
         universal_taggable = object()
 
     augment = universal_augment
+
+    def resources(self, query=None):
+        query_filters = CloudDirectoryQueryParser.parse(self.data.get('query', []))
+        query = query or {}
+        if query_filters:
+            query['Filters'] = query_filters
+        return super(CloudDirectory, self).resources(query=query)
+
+@CloudDirectory.action_registry.register('delete')
+class CloudDirectoryDelete(BaseAction):
+    """Delete a cloud directory.
+
+    .. code-block:: yaml
+
+       policies:
+         - name: delete-cloud-directory
+           resource: aws.cloud-directory
+           filters:
+             - Name: test-cloud
+           actions:
+             - type: delete
+    """
+    schema = type_schema('delete')
+    permissions = ('clouddirectory:DeleteDirectory',
+                   'clouddirectory:DisableDirectory',)
+
+    def process(self, resources):
+        client = local_session(
+            self.manager.session_factory).client('clouddirectory')
+        for r in resources:
+            self.manager.retry(
+                    client.disable_directory,
+                    DirectoryArn=r['DirectoryArn'])
+
+        for r in resources:
+            self.manager.retry(
+                client.delete_directory,
+                DirectoryArn=r['DirectoryArn'])
+
+@CloudDirectory.action_registry.register('disable')
+class CloudDirectoryDisable(BaseAction):
+    """Disable a cloud directory.
+
+    .. code-block:: yaml
+
+       policies:
+         - name: disable-cloud-directory
+           resource: aws.cloud-directory
+           filters:
+             - Name: test-cloud
+           actions:
+             - type: disable
+    """
+    schema = type_schema('disable')
+    permissions = ('clouddirectory:DisableDirectory',)
+
+    def process(self, resources):
+        client = local_session(
+            self.manager.session_factory).client('clouddirectory')
+        for r in resources:
+            self.manager.retry(
+                    client.disable_directory,
+                    DirectoryArn=r['DirectoryArn'])
+
+class CloudDirectoryQueryParser(QueryParser):
+    QuerySchema = {
+        'name': str,
+        'directoryArn': str,
+        'state': str,
+    }
+
+    type_name = 'CloudDirectory'


Test patch:
diff --git a/tests/data/placebo/test_cloud_directory_delete/clouddirectory.DeleteDirectory_1.json b/tests/data/placebo/test_cloud_directory_delete/clouddirectory.DeleteDirectory_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cloud_directory_delete/clouddirectory.DeleteDirectory_1.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "DirectoryArn": "arn:aws:clouddirectory:us-east-1:644160558196:directory/AZcvaFTzgUYLl7vFDgr8FJU"
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cloud_directory_delete/clouddirectory.DisableDirectory_1.json b/tests/data/placebo/test_cloud_directory_delete/clouddirectory.DisableDirectory_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cloud_directory_delete/clouddirectory.DisableDirectory_1.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "DirectoryArn": "arn:aws:clouddirectory:us-east-1:644160558196:directory/AZcvaFTzgUYLl7vFDgr8FJU"
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cloud_directory_delete/clouddirectory.ListDirectories_1.json b/tests/data/placebo/test_cloud_directory_delete/clouddirectory.ListDirectories_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cloud_directory_delete/clouddirectory.ListDirectories_1.json
@@ -0,0 +1,23 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "Directories": [
+            {
+                "Name": "test-cloud",
+                "DirectoryArn": "arn:aws:clouddirectory:us-east-1:644160558196:directory/AZcvaFTzgUYLl7vFDgr8FJU",
+                "State": "ENABLED",
+                "CreationDateTime": {
+                    "__class__": "datetime",
+                    "year": 2023,
+                    "month": 6,
+                    "day": 14,
+                    "hour": 7,
+                    "minute": 30,
+                    "second": 0,
+                    "microsecond": 149000
+                }
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cloud_directory_delete/clouddirectory.ListDirectories_2.json b/tests/data/placebo/test_cloud_directory_delete/clouddirectory.ListDirectories_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cloud_directory_delete/clouddirectory.ListDirectories_2.json
@@ -0,0 +1,24 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "Directories": [
+            {
+                "Name": "test-cloud",
+                "DirectoryArn": "arn:aws:clouddirectory:us-east-1:644160558196:directory/AZcvaFTzgUYLl7vFDgr8FJU",
+                "State": "DELETED",
+                "CreationDateTime": {
+                    "__class__": "datetime",
+                    "year": 2023,
+                    "month": 6,
+                    "day": 14,
+                    "hour": 7,
+                    "minute": 30,
+                    "second": 0,
+                    "microsecond": 149000
+                }
+            }
+        ],
+        "NextToken": "CAISgAIIARLZAQgDEtQBANoI+qsZoF5aDF3J64ghqBoVowqpcCiuVBbEqm6v4ujBeyJAbiI6IlBhZ2luYXRpb25Db250aW51YXRpb25EVE8iLCJuZXh0S2V5IjoiQUFBQUFBQUFDV1BoQVFFQkk1b1dEUE5pU2pHRWl4V1hZZk42eDNSNXhqbEJQMG14cEN6T3hKc09HU2RsY0dZN1FXWjFablJ3Y0dGMGEwTm5hVlowZERsb2QxTkdWMDA3IiwicHJldmlvdXNSZXF1ZXN0VGltZSI6MTY4Njc0MjMzODM1M30aILiWcolauOHhQa3D4zd0CkY1cFXgMrbhtD3vota31FW0"
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cloud_directory_delete/tagging.GetResources_1.json b/tests/data/placebo/test_cloud_directory_delete/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cloud_directory_delete/tagging.GetResources_1.json
@@ -0,0 +1,8 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cloud_directory_disable/clouddirectory.DisableDirectory_1.json b/tests/data/placebo/test_cloud_directory_disable/clouddirectory.DisableDirectory_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cloud_directory_disable/clouddirectory.DisableDirectory_1.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "DirectoryArn": "arn:aws:clouddirectory:us-east-1:644160558196:directory/AZcvaFTzgUYLl7vFDgr8FJU"
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cloud_directory_disable/clouddirectory.ListDirectories_1.json b/tests/data/placebo/test_cloud_directory_disable/clouddirectory.ListDirectories_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cloud_directory_disable/clouddirectory.ListDirectories_1.json
@@ -0,0 +1,23 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "Directories": [
+            {
+                "Name": "test-cloud",
+                "DirectoryArn": "arn:aws:clouddirectory:us-east-1:644160558196:directory/AZcvaFTzgUYLl7vFDgr8FJU",
+                "State": "ENABLED",
+                "CreationDateTime": {
+                    "__class__": "datetime",
+                    "year": 2023,
+                    "month": 6,
+                    "day": 14,
+                    "hour": 7,
+                    "minute": 30,
+                    "second": 0,
+                    "microsecond": 149000
+                }
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cloud_directory_disable/clouddirectory.ListDirectories_2.json b/tests/data/placebo/test_cloud_directory_disable/clouddirectory.ListDirectories_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cloud_directory_disable/clouddirectory.ListDirectories_2.json
@@ -0,0 +1,24 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {},
+        "Directories": [
+            {
+                "Name": "test-cloud",
+                "DirectoryArn": "arn:aws:clouddirectory:us-east-1:644160558196:directory/AZcvaFTzgUYLl7vFDgr8FJU",
+                "State": "DISABLED",
+                "CreationDateTime": {
+                    "__class__": "datetime",
+                    "year": 2023,
+                    "month": 6,
+                    "day": 14,
+                    "hour": 7,
+                    "minute": 30,
+                    "second": 0,
+                    "microsecond": 149000
+                }
+            }
+        ],
+        "NextToken": "CAISgAIIARLZAQgDEtQBANoI+qsZoF5aDF3J64ghqBoVowqpcCiuVBbEqm6v4ujBeyJAbiI6IlBhZ2luYXRpb25Db250aW51YXRpb25EVE8iLCJuZXh0S2V5IjoiQUFBQUFBQUFDV1BoQVFFQkk1b1dEUE5pU2pHRWl4V1hZZk42eDNSNXhqbEJQMG14cEN6T3hKc09HU2RsY0dZN1FXWjFablJ3Y0dGMGEwTm5hVlowZERsb2QxTkdWMDA3IiwicHJldmlvdXNSZXF1ZXN0VGltZSI6MTY4Njc0MjIyMDE5N30aIFS6PDjlZu2h7XnSrHtudK4OnSYlbOmDRQEJIwUkMyWa"
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cloud_directory_disable/tagging.GetResources_1.json b/tests/data/placebo/test_cloud_directory_disable/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cloud_directory_disable/tagging.GetResources_1.json
@@ -0,0 +1,8 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_directory_delete/ds.DeleteDirectory_1.json b/tests/data/placebo/test_directory_delete/ds.DeleteDirectory_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_directory_delete/ds.DeleteDirectory_1.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "DirectoryId": "d-90679603dd",
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_directory_delete/ds.DescribeDirectories_1.json b/tests/data/placebo/test_directory_delete/ds.DescribeDirectories_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_directory_delete/ds.DescribeDirectories_1.json
@@ -0,0 +1,104 @@
+{
+    "status_code": 200,
+    "data": {
+        "DirectoryDescriptions": [
+            {
+                "DirectoryId": "d-90677cc91c",
+                "Name": "corp.amazonworkspaces.com",
+                "ShortName": "corp",
+                "Size": "Small",
+                "Alias": "d-90677cc91c",
+                "AccessUrl": "d-90677cc91c.awsapps.com",
+                "DnsIpAddrs": [
+                    "172.16.1.157",
+                    "172.16.0.18"
+                ],
+                "Stage": "Active",
+                "LaunchTime": {
+                    "__class__": "datetime",
+                    "year": 2020,
+                    "month": 7,
+                    "day": 9,
+                    "hour": 18,
+                    "minute": 29,
+                    "second": 14,
+                    "microsecond": 188000
+                },
+                "StageLastUpdatedDateTime": {
+                    "__class__": "datetime",
+                    "year": 2023,
+                    "month": 5,
+                    "day": 26,
+                    "hour": 13,
+                    "minute": 12,
+                    "second": 38,
+                    "microsecond": 219000
+                },
+                "Type": "SimpleAD",
+                "VpcSettings": {
+                    "VpcId": "vpc-0598080981e0332f9",
+                    "SubnetIds": [
+                        "subnet-083ab4dd1a8b6eaa6",
+                        "subnet-0544e30702e2e1ad4"
+                    ],
+                    "SecurityGroupId": "sg-08f62b8c3dd4669bc",
+                    "AvailabilityZones": [
+                        "us-east-1b",
+                        "us-east-1a"
+                    ]
+                },
+                "SsoEnabled": false,
+                "DesiredNumberOfDomainControllers": 0
+            },
+            {
+                "DirectoryId": "d-90679603dd",
+                "Name": "test.example.com",
+                "ShortName": "test",
+                "Size": "Small",
+                "Alias": "d-90679603dd",
+                "AccessUrl": "d-90679603dd.awsapps.com",
+                "DnsIpAddrs": [
+                    "10.0.0.177",
+                    "10.0.1.42"
+                ],
+                "Stage": "Creating",
+                "LaunchTime": {
+                    "__class__": "datetime",
+                    "year": 2023,
+                    "month": 5,
+                    "day": 30,
+                    "hour": 8,
+                    "minute": 45,
+                    "second": 10,
+                    "microsecond": 986000
+                },
+                "StageLastUpdatedDateTime": {
+                    "__class__": "datetime",
+                    "year": 2023,
+                    "month": 5,
+                    "day": 30,
+                    "hour": 8,
+                    "minute": 45,
+                    "second": 14,
+                    "microsecond": 749000
+                },
+                "Type": "SimpleAD",
+                "VpcSettings": {
+                    "VpcId": "vpc-0d4f819d13b730139",
+                    "SubnetIds": [
+                        "subnet-0707ce1b2a6305995",
+                        "subnet-0472841b16adf7476"
+                    ],
+                    "SecurityGroupId": "sg-0ec7770651852c408",
+                    "AvailabilityZones": [
+                        "us-east-1a",
+                        "us-east-1b"
+                    ]
+                },
+                "SsoEnabled": false,
+                "DesiredNumberOfDomainControllers": 0
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_directory_delete/ds.DescribeDirectories_2.json b/tests/data/placebo/test_directory_delete/ds.DescribeDirectories_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_directory_delete/ds.DescribeDirectories_2.json
@@ -0,0 +1,105 @@
+{
+    "status_code": 200,
+    "data": {
+        "DirectoryDescriptions": [
+            {
+                "DirectoryId": "d-90677cc91c",
+                "Name": "corp.amazonworkspaces.com",
+                "ShortName": "corp",
+                "Size": "Small",
+                "Alias": "d-90677cc91c",
+                "AccessUrl": "d-90677cc91c.awsapps.com",
+                "DnsIpAddrs": [
+                    "172.16.1.157",
+                    "172.16.0.18"
+                ],
+                "Stage": "Active",
+                "LaunchTime": {
+                    "__class__": "datetime",
+                    "year": 2020,
+                    "month": 7,
+                    "day": 9,
+                    "hour": 18,
+                    "minute": 29,
+                    "second": 14,
+                    "microsecond": 188000
+                },
+                "StageLastUpdatedDateTime": {
+                    "__class__": "datetime",
+                    "year": 2023,
+                    "month": 5,
+                    "day": 26,
+                    "hour": 13,
+                    "minute": 12,
+                    "second": 38,
+                    "microsecond": 219000
+                },
+                "Type": "SimpleAD",
+                "VpcSettings": {
+                    "VpcId": "vpc-0598080981e0332f9",
+                    "SubnetIds": [
+                        "subnet-083ab4dd1a8b6eaa6",
+                        "subnet-0544e30702e2e1ad4"
+                    ],
+                    "SecurityGroupId": "sg-08f62b8c3dd4669bc",
+                    "AvailabilityZones": [
+                        "us-east-1b",
+                        "us-east-1a"
+                    ]
+                },
+                "SsoEnabled": false,
+                "DesiredNumberOfDomainControllers": 0
+            },
+            {
+                "DirectoryId": "d-90679603dd",
+                "Name": "test.example.com",
+                "ShortName": "test",
+                "Size": "Small",
+                "Alias": "d-90679603dd",
+                "AccessUrl": "d-90679603dd.awsapps.com",
+                "DnsIpAddrs": [
+                    "10.0.0.177",
+                    "10.0.1.42"
+                ],
+                "Stage": "Deleting",
+                "LaunchTime": {
+                    "__class__": "datetime",
+                    "year": 2023,
+                    "month": 5,
+                    "day": 30,
+                    "hour": 8,
+                    "minute": 45,
+                    "second": 10,
+                    "microsecond": 986000
+                },
+                "StageLastUpdatedDateTime": {
+                    "__class__": "datetime",
+                    "year": 2023,
+                    "month": 5,
+                    "day": 30,
+                    "hour": 8,
+                    "minute": 49,
+                    "second": 26,
+                    "microsecond": 451000
+                },
+                "Type": "SimpleAD",
+                "VpcSettings": {
+                    "VpcId": "vpc-0d4f819d13b730139",
+                    "SubnetIds": [
+                        "subnet-0707ce1b2a6305995",
+                        "subnet-0472841b16adf7476"
+                    ],
+                    "SecurityGroupId": "sg-0ec7770651852c408",
+                    "AvailabilityZones": [
+                        "us-east-1a",
+                        "us-east-1b"
+                    ]
+                },
+                "StageReason": "User initiated directory deletion.",
+                "SsoEnabled": false,
+                "DesiredNumberOfDomainControllers": 0
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_directory_delete/ds.ListTagsForResource_1.json b/tests/data/placebo/test_directory_delete/ds.ListTagsForResource_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_directory_delete/ds.ListTagsForResource_1.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "Tags": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_directory_delete/ds.ListTagsForResource_2.json b/tests/data/placebo/test_directory_delete/ds.ListTagsForResource_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_directory_delete/ds.ListTagsForResource_2.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "Tags": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/test_directory.py b/tests/test_directory.py
--- a/tests/test_directory.py
+++ b/tests/test_directory.py
@@ -3,6 +3,8 @@
 import json
 
 from .common import BaseTest, load_data
+from c7n.resources.directory import CloudDirectoryQueryParser
+from c7n.exceptions import PolicyValidationError
 
 
 class CloudDirectoryTest(BaseTest):
@@ -49,6 +51,51 @@ def test_cloud_directory(self):
         resources = p.run()
         self.assertEqual(len(resources), 1)
 
+    def test_cloud_directory_disable(self):
+        factory = self.replay_flight_data("test_cloud_directory_disable")
+        p = self.load_policy(
+            {
+                "name": "disable-cloud-directory",
+                "resource": "cloud-directory",
+                "filters": [{"Name": "test-cloud"}],
+                "actions": [{"type": "disable"}],
+            },
+            session_factory=factory,
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        self.assertEqual(
+            sorted([r["Name"] for r in resources]),
+            ["test-cloud"],
+        )
+        self.assertEqual(resources[0]["State"], "ENABLED")
+        client = factory().client("clouddirectory")
+        remainder = client.list_directories()["Directories"]
+        self.assertEqual(len(remainder), 1)
+        self.assertEqual(remainder[0]["State"], "DISABLED")
+
+    def test_cloud_directory_delete(self):
+        factory = self.replay_flight_data("test_cloud_directory_delete")
+        p = self.load_policy(
+            {
+                "name": "delete-cloud-directory",
+                "resource": "cloud-directory",
+                "filters": [{"Name": "test-cloud"}],
+                "actions": [{"type": "delete"}],
+            },
+            session_factory=factory,
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        self.assertEqual(
+            sorted([r["Name"] for r in resources]),
+            ["test-cloud"],
+        )
+        client = factory().client("clouddirectory")
+        remainder = client.list_directories()["Directories"]
+        self.assertEqual(len(remainder), 1)
+        self.assertEqual(remainder[0]["State"], "DELETED")
+
 
 class DirectoryTests(BaseTest):
 
@@ -88,3 +135,52 @@ def test_directory_remove_tag(self):
         self.assertEqual(resources[0]["DirectoryId"], "d-90672a7419")
         tags = client.list_tags_for_resource(ResourceId="d-90672a7419")["Tags"]
         self.assertEqual(len(tags), 0)
+
+    def test_directory_delete(self):
+        factory = self.replay_flight_data("test_directory_delete")
+        p = self.load_policy(
+            {
+                "name": "delete-directory",
+                "resource": "directory",
+                "filters": [{"Name": "test.example.com"}],
+                "actions": ["delete"],
+            },
+            session_factory=factory,
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        self.assertEqual(
+            sorted([r["Name"] for r in resources]),
+            ["test.example.com"],
+        )
+        self.assertEqual(resources[0]["Stage"], "Creating")
+        client = factory().client("ds")
+        remainder = client.describe_directories()["DirectoryDescriptions"]
+        self.assertEqual(len(remainder), 2)
+        self.assertEqual(remainder[1]["Stage"], "Deleting")
+
+class CloudDirectoryQueryParse(BaseTest):
+
+    def test_query(self):
+        query_filters = [
+            {'Name': 'tag:Name', 'Values': ['Test']},
+            {'Name': 'state', 'Values': ['DISABLED']}]
+        self.assertEqual(query_filters, CloudDirectoryQueryParser.parse(query_filters))
+
+    def test_invalid_query(self):
+        self.assertRaises(
+            PolicyValidationError, CloudDirectoryQueryParser.parse, {})
+
+        self.assertRaises(
+            PolicyValidationError, CloudDirectoryQueryParser.parse, [None])
+
+        self.assertRaises(
+            PolicyValidationError, CloudDirectoryQueryParser.parse, [{'X': 1}])
+
+        self.assertRaises(
+            PolicyValidationError, CloudDirectoryQueryParser.parse, [
+                {'name': 'state', 'Values': 'disabled'}])
+
+        self.assertRaises(
+            PolicyValidationError, CloudDirectoryQueryParser.parse, [
+                {'name': 'state', 'Values': ['disabled']}])