kapilt: thanks for the pr, this looks like it was based on the release instead of trunk though (re merge conflicts).
bdwyertech: Yeh I noticed that... I'll try to get this going on master.

On Thu, Mar 19, 2020, 6:33 PM Kapil Thangavelu <notifications@github.com>
wrote:

> thanks for the pr, this looks like it was based on the release instead of
> trunk though (re merge conflicts).
>
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/cloud-custodian/cloud-custodian/pull/5472#issuecomment-601447953>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAWV4WNM2OLAKA5EEYADLQDRIKMUJANCNFSM4LPVTHJA>
> .
>

bdwyertech: @kapilt any insight on why the documentation CI job is failing?  Can't figure it out.
bdwyertech: Nevermind... Figured it out @kapilt 
bdwyertech: Any love @kapilt I have merged master a few times now lmk and I will stop 😂
kapilt: @bdwyertech hey sorry for the delay, adjusting to new schedules post cancelling of academic year in my state. on cursory glance its a little different but seems fine/lgtm, the only question is for non value filter derived if the data in the response is worth considering.
bdwyertech: All good! @kapilt not sure what the use case would be there wrt non-value filter; could you elaborate on scenario?
kapilt: mostly wrt to the evaluation date, ie want to consider that as a part of a policy... tbh. in this context its purely additive as a future addition if its relevant, so i'm fine as is, but.. if you see a reasonable use case for it ootb then its a simple change sans recording to add.

ie specifically this part of the response on https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#SSM.Client.list_resource_compliance_summaries

```
            'ExecutionSummary': {
                'ExecutionTime': datetime(2015, 1, 1),
                'ExecutionId': 'string',
                'ExecutionType': 'string'
}
```
bdwyertech: That makes sense -- can you think of another resource that'd be good to
copy pasta, maybe similar to the eval_filter on the ssm resource?  That one
had me kind of confused.

On Tue, Mar 24, 2020, 8:41 PM Kapil Thangavelu <notifications@github.com>
wrote:

> mostly wrt to the evaluation date, ie want to consider that as a part of a
> policy... tbh. in this context its purely additive as a future addition if
> its relevant, so i'm fine as is, but.. if you see a reasonable use case for
> it ootb then its a simple change sans recording to add.
>
> ie specifically this part of the response on
> https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#SSM.Client.list_resource_compliance_summaries
>
>             'ExecutionSummary': {
>                 'ExecutionTime': datetime(2015, 1, 1),
>                 'ExecutionId': 'string',
>                 'ExecutionType': 'string'
> }
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/cloud-custodian/cloud-custodian/pull/5472#issuecomment-603577294>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAWV4WLHB3XU3HLWL6OOZCTRJFHM3ANCNFSM4LPVTHJA>
> .
>

kapilt: there's are two styles here, deriving from ValueFilter, overriding process to call match(annotation_value) ie

```python
process(self, resources):
    self.annotate_resources(resources)
    results = []
    for r in resources:
         if self.match(r['annotation_key']:
             resources.append(r)
    return results
```

that also wants rinherit=ValueFilter.schema in the type_schema line to get the jsonschema stuff going.

that works for doing a single attribute check on the underlying annotation. 

the other style is for multi attribute checks, there's a baseclass in filters/multiattr.py that helps set that up multiple embedded value filters on named subattributes of the annotation,  i don't know that we really need it for this use case though.

bdwyertech: @kapilt is this what you're looking for?  Lmk and I will squash the commits
kapilt: > @kapilt is this what you're looking for? Lmk and I will squash the commits

yup, that looks pretty good, thanks. no need on squash, the repo is setup to squash all pr merges.
diff --git a/c7n/resources/ec2.py b/c7n/resources/ec2.py
index abd9a5b64..f83ccdb9b 100644
--- a/c7n/resources/ec2.py
+++ b/c7n/resources/ec2.py
@@ -920,6 +920,112 @@ class SsmStatus(ValueFilter):
             r[self.annotation] = info_map.get(r['InstanceId'], {})
 
 
+@EC2.filter_registry.register('ssm-compliance')
+class SsmCompliance(Filter):
+    """Filter ec2 instances by their ssm compliance status.
+
+    :Example:
+
+    Find non-compliant ec2 instances.
+
+    .. code-block:: yaml
+
+        policies:
+          - name: ec2-ssm-compliance
+            resource: ec2
+            filters:
+              - type: ssm-compliance
+                compliance_types:
+                  - Association
+                  - Patch
+                severity:
+                  - CRITICAL
+                  - HIGH
+                  - MEDIUM
+                  - LOW
+                  - UNSPECIFIED
+                states:
+                  - NON_COMPLIANT
+                eval_filters:
+                 - type: value
+                   key: ExecutionSummary.ExecutionTime
+                   value_type: age
+                   value: 30
+                   op: less-than
+    """
+    schema = type_schema(
+        'ssm-compliance',
+        **{'required': ['compliance_types'],
+           'compliance_types': {'type': 'array', 'items': {'type': 'string'}},
+           'severity': {'type': 'array', 'items': {'type': 'string'}},
+           'op': {'enum': ['or', 'and']},
+           'eval_filters': {'type': 'array', 'items': {
+                            'oneOf': [
+                                {'$ref': '#/definitions/filters/valuekv'},
+                                {'$ref': '#/definitions/filters/value'}]}},
+           'states': {'type': 'array',
+                      'default': ['NON_COMPLIANT'],
+                      'items': {
+                          'enum': [
+                              'COMPLIANT',
+                              'NON_COMPLIANT'
+                          ]}}})
+    permissions = ('ssm:ListResourceComplianceSummaries',)
+    annotation = 'c7n:ssm-compliance'
+
+    def process(self, resources, event=None):
+        op = self.data.get('op', 'or') == 'or' and any or all
+        eval_filters = []
+        for f in self.data.get('eval_filters', ()):
+            vf = ValueFilter(f)
+            vf.annotate = False
+            eval_filters.append(vf)
+
+        client = utils.local_session(self.manager.session_factory).client('ssm')
+        filters = [
+            {
+                'Key': 'Status',
+                'Values': self.data['states'],
+                'Type': 'EQUAL'
+            },
+            {
+                'Key': 'ComplianceType',
+                'Values': self.data['compliance_types'],
+                'Type': 'EQUAL'
+            }
+        ]
+        severity = self.data.get('severity')
+        if severity:
+            filters.append(
+                {
+                    'Key': 'OverallSeverity',
+                    'Values': severity,
+                    'Type': 'EQUAL'
+                })
+
+        resource_map = {}
+        pager = client.get_paginator('list_resource_compliance_summaries')
+        for page in pager.paginate(Filters=filters):
+            items = page['ResourceComplianceSummaryItems']
+            for i in items:
+                if not eval_filters:
+                    resource_map.setdefault(
+                        i['ResourceId'], []).append(i)
+                    continue
+                if op([f.match(i) for f in eval_filters]):
+                    resource_map.setdefault(
+                        i['ResourceId'], []).append(i)
+
+        results = []
+        for r in resources:
+            result = resource_map.get(r['InstanceId'])
+            if result:
+                r[self.annotation] = result
+                results.append(r)
+
+        return results
+
+
 @actions.register('set-monitoring')
 class MonitorInstances(BaseAction, StateTransitionFilter):
     """Action on EC2 Instances to enable/disable detailed monitoring

AWS.EC2 - SSM Compliance Support via Config-Filter
**Description**
Currently, you cannot leverage config-filter to query SSM Patch Compliance or SSM Association Compliance.  This is because Custodian queries config for `AWS::EC2::Instance` type which does not expose SSM compliance status. The two types for querying SSM Compliance status of an EC2 are `AWS::Config::ResourceCompliance` and `AWS::SSM::ManagedInstanceInventory`.

Amazon pre-packages two great config rules:
  * [Patch Compliance](https://docs.aws.amazon.com/config/latest/developerguide/ec2-managedinstance-patch-compliance-status-check.html)
  * [Association Compliance](https://docs.aws.amazon.com/config/latest/developerguide/ec2-managedinstance-association-compliance-status-check.html)

The objective is to filter EC2 instances which are not compliant with these config rules and take appropriate action (notify resource owner, tag for remediation, etc.)

Ultimately, I am looking to push these two via Organization Config in addition to EC2_INSTANCE_MANAGED_BY_SSM.  By implementing all three of these together, we can effectively evaluate and enforce policy compliance on the EC2 fleet at the organization level, feed this information to Security Hub, and hook into Config & SecHub for remediation via Custodian.

**Solution**
A. Enhance config-filter to support overriding the resource type to query
B. Add a new filter type (maybe a superclass of config-filter) specific to EC2 to achieve the same.

**Alternatives**
If I am not able to do this, I will write a lambda which will tag instances with their compliance status whenever the SSM Patch Compliance or SSM Association Compliance status changes.


**Thoughts**

https://github.com/cloud-custodian/cloud-custodian/blob/cc7a6dc2927ea94e71c313627cab24284bb31b16/c7n/resources/ec2.py#L66


Maybe we can query Config for a specific instance via resource ID or if gathering an array in the return, check for where resourceId ends with the instance-id instead of resourceId == instance-id

#### Example Resource ID:
```
AWS::SSM::PatchCompliance/AWS::SSM::ManagedInstanceInventory/i-0050852ea15a9ea0f
```

#### Example Query:
```
SELECT
  configuration,
  *
WHERE
  resourceType = 'AWS::Config::ResourceCompliance'
  AND configuration.targetResourceType = 'AWS::SSM::PatchCompliance'
  AND configuration.complianceType = 'NON_COMPLIANT'

# Can also filter for an instance id
AND resourceId LIKE 'AWS::SSM::PatchCompliance%i-0012345ab23a2da0f'
```
diff --git a/tests/data/placebo/test_ec2_ssm_compliance_filter/ec2.DescribeInstances_1.json b/tests/data/placebo/test_ec2_ssm_compliance_filter/ec2.DescribeInstances_1.json
new file mode 100644
index 000000000..5ee1da1b1
--- /dev/null
+++ b/tests/data/placebo/test_ec2_ssm_compliance_filter/ec2.DescribeInstances_1.json
@@ -0,0 +1,326 @@
+{
+    "status_code": 200,
+    "data": {
+        "Reservations": [
+            {
+                "Groups": [],
+                "Instances": [
+                    {
+                        "AmiLaunchIndex": 0,
+                        "ImageId": "ami-0f65671a86f061fcd",
+                        "InstanceId": "i-0dea82d960d56dc1d",
+                        "InstanceType": "t2.medium",
+                        "KeyName": "ssm-dev",
+                        "LaunchTime": {
+                            "__class__": "datetime",
+                            "year": 2019,
+                            "month": 1,
+                            "day": 30,
+                            "hour": 13,
+                            "minute": 14,
+                            "second": 36,
+                            "microsecond": 0
+                        },
+                        "Monitoring": {
+                            "State": "disabled"
+                        },
+                        "Placement": {
+                            "AvailabilityZone": "us-east-2a",
+                            "GroupName": "",
+                            "Tenancy": "default"
+                        },
+                        "PrivateDnsName": "ip-172-31-11-50.us-east-2.compute.internal",
+                        "PrivateIpAddress": "172.31.11.50",
+                        "ProductCodes": [],
+                        "PublicDnsName": "ec2-18-191-215-59.us-east-2.compute.amazonaws.com",
+                        "PublicIpAddress": "18.191.215.59",
+                        "State": {
+                            "Code": 64,
+                            "Name": "stopping"
+                        },
+                        "StateTransitionReason": "User initiated (2019-01-30 13:33:45 GMT)",
+                        "SubnetId": "subnet-db9f62b2",
+                        "VpcId": "vpc-5f6c9936",
+                        "Architecture": "x86_64",
+                        "BlockDeviceMappings": [
+                            {
+                                "DeviceName": "/dev/sda1",
+                                "Ebs": {
+                                    "AttachTime": {
+                                        "__class__": "datetime",
+                                        "year": 2019,
+                                        "month": 1,
+                                        "day": 30,
+                                        "hour": 13,
+                                        "minute": 14,
+                                        "second": 36,
+                                        "microsecond": 0
+                                    },
+                                    "DeleteOnTermination": true,
+                                    "Status": "attached",
+                                    "VolumeId": "vol-08d1573a7cd3fd1f2"
+                                }
+                            }
+                        ],
+                        "ClientToken": "",
+                        "EbsOptimized": false,
+                        "EnaSupport": true,
+                        "Hypervisor": "xen",
+                        "IamInstanceProfile": {
+                            "Arn": "arn:aws:iam::644160558196:instance-profile/CloudCustodianRole",
+                            "Id": "AIPAJO5EMJ3YEMNUF5ZWM"
+                        },
+                        "NetworkInterfaces": [
+                            {
+                                "Association": {
+                                    "IpOwnerId": "amazon",
+                                    "PublicDnsName": "ec2-18-191-215-59.us-east-2.compute.amazonaws.com",
+                                    "PublicIp": "18.191.215.59"
+                                },
+                                "Attachment": {
+                                    "AttachTime": {
+                                        "__class__": "datetime",
+                                        "year": 2019,
+                                        "month": 1,
+                                        "day": 30,
+                                        "hour": 13,
+                                        "minute": 14,
+                                        "second": 36,
+                                        "microsecond": 0
+                                    },
+                                    "AttachmentId": "eni-attach-0bc8d36721d13f0a6",
+                                    "DeleteOnTermination": true,
+                                    "DeviceIndex": 0,
+                                    "Status": "attached"
+                                },
+                                "Description": "Primary network interface",
+                                "Groups": [
+                                    {
+                                        "GroupName": "launch-wizard-1",
+                                        "GroupId": "sg-bebafdd5"
+                                    }
+                                ],
+                                "Ipv6Addresses": [],
+                                "MacAddress": "02:27:4e:43:cb:e6",
+                                "NetworkInterfaceId": "eni-01075318d08f25bf5",
+                                "OwnerId": "644160558196",
+                                "PrivateDnsName": "ip-172-31-11-50.us-east-2.compute.internal",
+                                "PrivateIpAddress": "172.31.11.50",
+                                "PrivateIpAddresses": [
+                                    {
+                                        "Association": {
+                                            "IpOwnerId": "amazon",
+                                            "PublicDnsName": "ec2-18-191-215-59.us-east-2.compute.amazonaws.com",
+                                            "PublicIp": "18.191.215.59"
+                                        },
+                                        "Primary": true,
+                                        "PrivateDnsName": "ip-172-31-11-50.us-east-2.compute.internal",
+                                        "PrivateIpAddress": "172.31.11.50"
+                                    }
+                                ],
+                                "SourceDestCheck": true,
+                                "Status": "in-use",
+                                "SubnetId": "subnet-db9f62b2",
+                                "VpcId": "vpc-5f6c9936"
+                            }
+                        ],
+                        "RootDeviceName": "/dev/sda1",
+                        "RootDeviceType": "ebs",
+                        "SecurityGroups": [
+                            {
+                                "GroupName": "launch-wizard-1",
+                                "GroupId": "sg-bebafdd5"
+                            }
+                        ],
+                        "SourceDestCheck": true,
+                        "StateReason": {
+                            "Code": "Client.UserInitiatedShutdown",
+                            "Message": "Client.UserInitiatedShutdown: User initiated shutdown"
+                        },
+                        "Tags": [
+                            {
+                                "Key": "Name",
+                                "Value": "kapil-ssm-dev"
+                            }
+                        ],
+                        "VirtualizationType": "hvm",
+                        "CpuOptions": {
+                            "CoreCount": 2,
+                            "ThreadsPerCore": 1
+                        },
+                        "HibernationOptions": {
+                            "Configured": false
+                        }
+                    }
+                ],
+                "OwnerId": "644160558196",
+                "ReservationId": "r-07103c7504d70d128"
+            },
+            {
+                "Groups": [],
+                "Instances": [
+                    {
+                        "AmiLaunchIndex": 0,
+                        "ImageId": "ami-0f65671a86f061fcd",
+                        "InstanceId": "i-0ba3874e85bb97244",
+                        "InstanceType": "t2.medium",
+                        "KeyName": "ssm-dev",
+                        "LaunchTime": {
+                            "__class__": "datetime",
+                            "year": 2019,
+                            "month": 1,
+                            "day": 30,
+                            "hour": 11,
+                            "minute": 58,
+                            "second": 37,
+                            "microsecond": 0
+                        },
+                        "Monitoring": {
+                            "State": "disabled"
+                        },
+                        "Placement": {
+                            "AvailabilityZone": "us-east-2a",
+                            "GroupName": "",
+                            "Tenancy": "default"
+                        },
+                        "PrivateDnsName": "ip-172-31-10-93.us-east-2.compute.internal",
+                        "PrivateIpAddress": "172.31.10.93",
+                        "ProductCodes": [],
+                        "PublicDnsName": "ec2-18-222-166-169.us-east-2.compute.amazonaws.com",
+                        "PublicIpAddress": "18.222.166.169",
+                        "State": {
+                            "Code": 16,
+                            "Name": "running"
+                        },
+                        "StateTransitionReason": "",
+                        "SubnetId": "subnet-db9f62b2",
+                        "VpcId": "vpc-5f6c9936",
+                        "Architecture": "x86_64",
+                        "BlockDeviceMappings": [
+                            {
+                                "DeviceName": "/dev/sda1",
+                                "Ebs": {
+                                    "AttachTime": {
+                                        "__class__": "datetime",
+                                        "year": 2019,
+                                        "month": 1,
+                                        "day": 30,
+                                        "hour": 11,
+                                        "minute": 58,
+                                        "second": 37,
+                                        "microsecond": 0
+                                    },
+                                    "DeleteOnTermination": true,
+                                    "Status": "attached",
+                                    "VolumeId": "vol-0fcfccae49c73e29a"
+                                }
+                            }
+                        ],
+                        "ClientToken": "",
+                        "EbsOptimized": false,
+                        "EnaSupport": true,
+                        "Hypervisor": "xen",
+                        "IamInstanceProfile": {
+                            "Arn": "arn:aws:iam::644160558196:instance-profile/CloudCustodianRole",
+                            "Id": "AIPAJO5EMJ3YEMNUF5ZWM"
+                        },
+                        "NetworkInterfaces": [
+                            {
+                                "Association": {
+                                    "IpOwnerId": "amazon",
+                                    "PublicDnsName": "ec2-18-222-166-169.us-east-2.compute.amazonaws.com",
+                                    "PublicIp": "18.222.166.169"
+                                },
+                                "Attachment": {
+                                    "AttachTime": {
+                                        "__class__": "datetime",
+                                        "year": 2019,
+                                        "month": 1,
+                                        "day": 30,
+                                        "hour": 11,
+                                        "minute": 58,
+                                        "second": 37,
+                                        "microsecond": 0
+                                    },
+                                    "AttachmentId": "eni-attach-0dbd4eeabb5c3ffa0",
+                                    "DeleteOnTermination": true,
+                                    "DeviceIndex": 0,
+                                    "Status": "attached"
+                                },
+                                "Description": "",
+                                "Groups": [
+                                    {
+                                        "GroupName": "launch-wizard-1",
+                                        "GroupId": "sg-bebafdd5"
+                                    }
+                                ],
+                                "Ipv6Addresses": [],
+                                "MacAddress": "02:ac:dd:b6:54:8a",
+                                "NetworkInterfaceId": "eni-0fe02396b03961784",
+                                "OwnerId": "644160558196",
+                                "PrivateDnsName": "ip-172-31-10-93.us-east-2.compute.internal",
+                                "PrivateIpAddress": "172.31.10.93",
+                                "PrivateIpAddresses": [
+                                    {
+                                        "Association": {
+                                            "IpOwnerId": "amazon",
+                                            "PublicDnsName": "ec2-18-222-166-169.us-east-2.compute.amazonaws.com",
+                                            "PublicIp": "18.222.166.169"
+                                        },
+                                        "Primary": true,
+                                        "PrivateDnsName": "ip-172-31-10-93.us-east-2.compute.internal",
+                                        "PrivateIpAddress": "172.31.10.93"
+                                    }
+                                ],
+                                "SourceDestCheck": true,
+                                "Status": "in-use",
+                                "SubnetId": "subnet-db9f62b2",
+                                "VpcId": "vpc-5f6c9936"
+                            }
+                        ],
+                        "RootDeviceName": "/dev/sda1",
+                        "RootDeviceType": "ebs",
+                        "SecurityGroups": [
+                            {
+                                "GroupName": "launch-wizard-1",
+                                "GroupId": "sg-bebafdd5"
+                            }
+                        ],
+                        "SourceDestCheck": true,
+                        "Tags": [
+                            {
+                                "Key": "Name",
+                                "Value": "kapil-ssm-dev"
+                            }
+                        ],
+                        "VirtualizationType": "hvm",
+                        "CpuOptions": {
+                            "CoreCount": 2,
+                            "ThreadsPerCore": 1
+                        },
+                        "CapacityReservationSpecification": {
+                            "CapacityReservationPreference": "open"
+                        },
+                        "HibernationOptions": {
+                            "Configured": false
+                        }
+                    }
+                ],
+                "OwnerId": "644160558196",
+                "ReservationId": "r-074f65542bb72fd56"
+            }
+        ],
+        "ResponseMetadata": {
+            "RequestId": "8151c8c3-afeb-4a62-b10d-b801d72a7458",
+            "HTTPStatusCode": 200,
+            "HTTPHeaders": {
+                "content-type": "text/xml;charset=UTF-8",
+                "transfer-encoding": "chunked",
+                "vary": "Accept-Encoding",
+                "date": "Wed, 30 Jan 2019 13:34:16 GMT",
+                "server": "AmazonEC2"
+            },
+            "RetryAttempts": 0
+        }
+    }
+}
diff --git a/tests/data/placebo/test_ec2_ssm_compliance_filter/ssm.ListResourceComplianceSummaries_1.json b/tests/data/placebo/test_ec2_ssm_compliance_filter/ssm.ListResourceComplianceSummaries_1.json
new file mode 100644
index 000000000..cddb0b11a
--- /dev/null
+++ b/tests/data/placebo/test_ec2_ssm_compliance_filter/ssm.ListResourceComplianceSummaries_1.json
@@ -0,0 +1,150 @@
+{
+  "status_code": 200,
+  "data": {
+    "ResourceComplianceSummaryItems": [
+      {
+        "ComplianceType": "Association",
+        "CompliantSummary": {
+          "CompliantCount": 1,
+          "SeveritySummary": {
+            "CriticalCount": 0,
+            "HighCount": 0,
+            "InformationalCount": 0,
+            "LowCount": 0,
+            "MediumCount": 0,
+            "UnspecifiedCount": 1
+          }
+        },
+        "ExecutionSummary": {
+          "ExecutionTime": "2020-03-20 10:52:50-04:00"
+        },
+        "NonCompliantSummary": {
+          "NonCompliantCount": 3,
+          "SeveritySummary": {
+            "CriticalCount": 0,
+            "HighCount": 0,
+            "InformationalCount": 0,
+            "LowCount": 0,
+            "MediumCount": 0,
+            "UnspecifiedCount": 3
+          }
+        },
+        "OverallSeverity": "UNSPECIFIED",
+        "ResourceId": "i-0dea82d960d56dc1d",
+        "ResourceType": "ManagedInstance",
+        "Status": "NON_COMPLIANT"
+      },
+      {
+        "ComplianceType": "Patch",
+        "CompliantSummary": {
+          "CompliantCount": 310,
+          "SeveritySummary": {
+            "CriticalCount": 0,
+            "HighCount": 0,
+            "InformationalCount": 0,
+            "LowCount": 0,
+            "MediumCount": 0,
+            "UnspecifiedCount": 310
+          }
+        },
+        "ExecutionSummary": {
+          "ExecutionId": "e44bffd7-4017-44ec-80f9-a6fbf932672f",
+          "ExecutionTime": "2020-03-19 18:50:59-04:00",
+          "ExecutionType": "Command"
+        },
+        "NonCompliantSummary": {
+          "NonCompliantCount": 36,
+          "SeveritySummary": {
+            "CriticalCount": 0,
+            "HighCount": 0,
+            "InformationalCount": 0,
+            "LowCount": 0,
+            "MediumCount": 0,
+            "UnspecifiedCount": 36
+          }
+        },
+        "OverallSeverity": "UNSPECIFIED",
+        "ResourceId": "i-0dea82d960d56dc1d",
+        "ResourceType": "ManagedInstance",
+        "Status": "NON_COMPLIANT"
+      },
+      {
+        "ComplianceType": "Association",
+        "CompliantSummary": {
+          "CompliantCount": 1,
+          "SeveritySummary": {
+            "CriticalCount": 0,
+            "HighCount": 0,
+            "InformationalCount": 0,
+            "LowCount": 0,
+            "MediumCount": 0,
+            "UnspecifiedCount": 1
+          }
+        },
+        "ExecutionSummary": {
+          "ExecutionTime": "2020-03-20 10:52:50-04:00"
+        },
+        "NonCompliantSummary": {
+          "NonCompliantCount": 3,
+          "SeveritySummary": {
+            "CriticalCount": 0,
+            "HighCount": 0,
+            "InformationalCount": 0,
+            "LowCount": 0,
+            "MediumCount": 0,
+            "UnspecifiedCount": 3
+          }
+        },
+        "OverallSeverity": "UNSPECIFIED",
+        "ResourceId": "i-0ba3874e85bb97244",
+        "ResourceType": "ManagedInstance",
+        "Status": "NON_COMPLIANT"
+      },
+      {
+        "ComplianceType": "Patch",
+        "CompliantSummary": {
+          "CompliantCount": 310,
+          "SeveritySummary": {
+            "CriticalCount": 0,
+            "HighCount": 0,
+            "InformationalCount": 0,
+            "LowCount": 0,
+            "MediumCount": 0,
+            "UnspecifiedCount": 310
+          }
+        },
+        "ExecutionSummary": {
+          "ExecutionId": "e44bffd7-4017-44ec-80f9-a6fbf932672f",
+          "ExecutionTime": "2020-03-19 18:50:59-04:00",
+          "ExecutionType": "Command"
+        },
+        "NonCompliantSummary": {
+          "NonCompliantCount": 36,
+          "SeveritySummary": {
+            "CriticalCount": 0,
+            "HighCount": 0,
+            "InformationalCount": 0,
+            "LowCount": 0,
+            "MediumCount": 0,
+            "UnspecifiedCount": 36
+          }
+        },
+        "OverallSeverity": "UNSPECIFIED",
+        "ResourceId": "i-0ba3874e85bb97244",
+        "ResourceType": "ManagedInstance",
+        "Status": "NON_COMPLIANT"
+      }
+    ],
+    "ResponseMetadata": {
+      "HTTPHeaders": {
+        "content-length": "12345",
+        "content-type": "application/x-amz-json-1.1",
+        "date": "Fri, 20 Mar 2020 15:09:20 GMT",
+        "x-amzn-requestid": "88383b45-36f4-44bb-b8a6-942dd5ffca3b"
+      },
+      "HTTPStatusCode": 200,
+      "RequestId": "88383b45-36f4-44bb-b8a6-942dd5ffca3b",
+      "RetryAttempts": 0
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/test_ec2.py b/tests/test_ec2.py
index 2b0eb8497..e949d90ff 100644
--- a/tests/test_ec2.py
+++ b/tests/test_ec2.py
@@ -263,6 +263,34 @@ class TestSsm(BaseTest):
             [r['InstanceId'] for r in resources],
             ['i-0dea82d960d56dc1d', 'i-0ba3874e85bb97244'])
 
+    def test_ssm_compliance(self):
+        session_factory = self.replay_flight_data('test_ec2_ssm_compliance_filter')
+        policy = self.load_policy({
+            'name': 'ec2-ssm-compliance',
+            'resource': 'aws.ec2',
+            'filters': [
+                {'type': 'ssm-compliance',
+                 'compliance_types': [
+                     'Association',
+                     'Patch'
+                 ],
+                 'severity': [
+                     'CRITICAL',
+                     'HIGH',
+                     'MEDIUM',
+                     'LOW',
+                     'UNSPECIFIED'
+                 ],
+                 'states': ['NON_COMPLIANT']}]},
+            session_factory=session_factory,
+            config={'region': 'us-east-2'})
+        resources = policy.run()
+        self.assertEqual(len(resources), 2)
+        self.assertTrue('c7n:ssm-compliance' in resources[0])
+        self.assertEqual(
+            [r['InstanceId'] for r in resources],
+            ['i-0dea82d960d56dc1d', 'i-0ba3874e85bb97244'])
+
 
 class TestHealthEventsFilter(BaseTest):
 

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio pytest
apt-get update apt-get install -y gcc
3.8
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
apipkg==1.5
appdirs==1.4.3
argcomplete==1.11.1
attrs==19.3.0
aws-xray-sdk==2.4.3
bleach==3.1.3
boto3==1.12.25
botocore==1.15.25
-e git+https://github.com/cloud-custodian/cloud-custodian.git@d3596445a82b76a722dc3244bb18770258109a8a#egg=c7n
certifi==2019.11.28
cffi==1.14.0
chardet==3.0.4
coverage==5.0.4
cryptography==2.8
distlib==0.3.0
docutils==0.15.2
entrypoints==0.3
exceptiongroup==1.2.2
execnet==1.7.1
filelock==3.0.12
flake8==3.7.9
future==0.18.2
idna==2.9
importlib-metadata==1.5.0
iniconfig==2.1.0
jeepney==0.4.3
jmespath==0.9.5
jsonpatch==1.25
jsonpickle==1.3
jsonpointer==2.0
jsonschema==3.2.0
keyring==21.2.0
mccabe==0.6.1
mock==4.0.2
more-itertools==8.2.0
multidict==4.7.5
packaging==20.3
pkginfo==1.5.0.1
placebo==0.9.0
pluggy==1.5.0
psutil==5.7.0
py==1.8.1
pycodestyle==2.5.0
pycparser==2.20
pyflakes==2.1.1
Pygments==2.6.1
pyparsing==2.4.6
pyrsistent==0.15.7
pytest==8.3.5
pytest-asyncio==0.24.0
pytest-cov==2.8.1
pytest-forked==1.1.3
pytest-mock==3.14.0
pytest-xdist==1.31.0
python-dateutil==2.8.1
PyYAML==5.3.1
readme-renderer==25.0
requests==2.23.0
requests-toolbelt==0.9.1
s3transfer==0.3.3
SecretStorage==3.1.2
six==1.14.0
tabulate==0.8.6
termcolor==1.1.0
toml==0.10.0
tomli==2.2.1
tox==3.14.5
tqdm==4.43.0
twine==3.1.1
urllib3==1.25.8
vcrpy==4.0.2
virtualenv==20.0.13
wcwidth==0.1.8
webencodings==0.5.1
wrapt==1.12.1
yarl==1.4.2
zipp==3.1.0

name: cloud-custodian
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=24.2=py38h06a4308_0
  - python=3.8.20=he870216_0
  - readline=8.2=h5eee18b_0
  - setuptools=75.1.0=py38h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.44.0=py38h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - apipkg==1.5
      - appdirs==1.4.3
      - argcomplete==1.11.1
      - attrs==19.3.0
      - aws-xray-sdk==2.4.3
      - bleach==3.1.3
      - boto3==1.12.25
      - botocore==1.15.25
      - c7n==0.9.0
      - certifi==2019.11.28
      - cffi==1.14.0
      - chardet==3.0.4
      - coverage==5.0.4
      - cryptography==2.8
      - distlib==0.3.0
      - docutils==0.15.2
      - entrypoints==0.3
      - exceptiongroup==1.2.2
      - execnet==1.7.1
      - filelock==3.0.12
      - flake8==3.7.9
      - future==0.18.2
      - idna==2.9
      - importlib-metadata==1.5.0
      - iniconfig==2.1.0
      - jeepney==0.4.3
      - jmespath==0.9.5
      - jsonpatch==1.25
      - jsonpickle==1.3
      - jsonpointer==2.0
      - jsonschema==3.2.0
      - keyring==21.2.0
      - mccabe==0.6.1
      - mock==4.0.2
      - more-itertools==8.2.0
      - multidict==4.7.5
      - packaging==20.3
      - pkginfo==1.5.0.1
      - placebo==0.9.0
      - pluggy==1.5.0
      - psutil==5.7.0
      - py==1.8.1
      - pycodestyle==2.5.0
      - pycparser==2.20
      - pyflakes==2.1.1
      - pygments==2.6.1
      - pyparsing==2.4.6
      - pyrsistent==0.15.7
      - pytest==8.3.5
      - pytest-asyncio==0.24.0
      - pytest-cov==2.8.1
      - pytest-forked==1.1.3
      - pytest-mock==3.14.0
      - pytest-xdist==1.31.0
      - python-dateutil==2.8.1
      - pyyaml==5.3.1
      - readme-renderer==25.0
      - requests==2.23.0
      - requests-toolbelt==0.9.1
      - s3transfer==0.3.3
      - secretstorage==3.1.2
      - six==1.14.0
      - tabulate==0.8.6
      - termcolor==1.1.0
      - toml==0.10.0
      - tomli==2.2.1
      - tox==3.14.5
      - tqdm==4.43.0
      - twine==3.1.1
      - urllib3==1.25.8
      - vcrpy==4.0.2
      - virtualenv==20.0.13
      - wcwidth==0.1.8
      - webencodings==0.5.1
      - wrapt==1.12.1
      - yarl==1.4.2
      - zipp==3.1.0
prefix: /opt/conda/envs/cloud-custodian

tests/test_ec2.py::TestSsm::test_ssm_compliance
tests/test_ec2.py::TestEc2NetworkLocation::test_ec2_network_location_terminated tests/test_ec2.py::TestTagAugmentation::test_tag_augment tests/test_ec2.py::TestTagAugmentation::test_tag_augment_empty tests/test_ec2.py::TestInstanceAttrFilter::test_attr_filter tests/test_ec2.py::TestMetricFilter::test_metric_filter tests/test_ec2.py::TestPropagateSpotTags::test_propagate_spot tests/test_ec2.py::TestDisableApiTermination::test_policy_permissions tests/test_ec2.py::TestDisableApiTermination::test_term_prot_enabled tests/test_ec2.py::TestDisableApiTermination::test_term_prot_not_enabled tests/test_ec2.py::TestEc2Permissions::test_ec2_permissions tests/test_ec2.py::TestSsm::test_ssm_status tests/test_ec2.py::TestHealthEventsFilter::test_ec2_health_events_filter tests/test_ec2.py::TestTagTrim::test_ec2_tag_trim tests/test_ec2.py::TestVolumeFilter::test_ec2_attached_ebs_filter tests/test_ec2.py::TestVolumeFilter::test_ec2_attached_volume_skip_block tests/test_ec2.py::TestResizeInstance::test_ec2_resize tests/test_ec2.py::TestStateTransitionAgeFilter::test_date_parsing tests/test_ec2.py::TestStateTransitionAgeFilter::test_ec2_state_transition_age tests/test_ec2.py::TestImageAgeFilter::test_ec2_image_age tests/test_ec2.py::TestImageFilter::test_ec2_image tests/test_ec2.py::TestInstanceAge::test_ec2_instance_age tests/test_ec2.py::TestTag::test_ec2_mark_hours tests/test_ec2.py::TestTag::test_ec2_mark_zero tests/test_ec2.py::TestTag::test_ec2_marked_hours tests/test_ec2.py::TestTag::test_ec2_normalize_tag tests/test_ec2.py::TestTag::test_ec2_rename_tag tests/test_ec2.py::TestTag::test_ec2_tag tests/test_ec2.py::TestTag::test_ec2_tag_errors tests/test_ec2.py::TestTag::test_ec2_untag tests/test_ec2.py::TestTag::test_ec2_untag_array tests/test_ec2.py::TestStop::test_ec2_stop tests/test_ec2.py::TestStop::test_ec2_stop_hibernate tests/test_ec2.py::TestReboot::test_ec2_reboot tests/test_ec2.py::TestStart::test_ec2_start tests/test_ec2.py::TestStart::test_ec2_start_fails tests/test_ec2.py::TestStart::test_ec2_start_handle_invalid_state tests/test_ec2.py::TestStart::test_invalid_state_extract tests/test_ec2.py::TestOr::test_ec2_or_condition tests/test_ec2.py::TestSnapshot::test_ec2_snapshot_copy_instance_tags_default tests/test_ec2.py::TestSnapshot::test_ec2_snapshot_copy_tags tests/test_ec2.py::TestSnapshot::test_ec2_snapshot_error_process_once tests/test_ec2.py::TestSnapshot::test_ec2_snapshot_error_process_set tests/test_ec2.py::TestSnapshot::test_ec2_snapshot_validate tests/test_ec2.py::TestSetInstanceProfile::test_ec2_set_instance_profile_disassocation tests/test_ec2.py::TestSetInstanceProfile::test_ec2_set_instance_profile_existing tests/test_ec2.py::TestSetInstanceProfile::test_ec2_set_instance_profile_missing tests/test_ec2.py::TestEC2QueryFilter::test_parse tests/test_ec2.py::TestTerminate::test_ec2_terminate tests/test_ec2.py::TestDefaultVpc::test_ec2_default_vpc tests/test_ec2.py::TestSingletonFilter::test_ec2_singleton_filter tests/test_ec2.py::TestOffHoursFilter::test_ec2_offhours_filter tests/test_ec2.py::TestOffHoursFilter::test_ec2_offhours_no_filter tests/test_ec2.py::TestOnHoursFilter::test_ec2_onhours_filter tests/test_ec2.py::TestOnHoursFilter::test_ec2_onhours_no_filter tests/test_ec2.py::TestActions::test_action_construction tests/test_ec2.py::TestModifySecurityGroupsActionSchema::test_invalid_isolation_group_params tests/test_ec2.py::TestModifySecurityGroupsActionSchema::test_invalid_remove_params tests/test_ec2.py::TestModifySecurityGroupsActionSchema::test_remove_dependencies tests/test_ec2.py::TestModifySecurityGroupsActionSchema::test_valid_add_params tests/test_ec2.py::TestModifySecurityGroupAction::test_add_remove_with_name tests/test_ec2.py::TestModifySecurityGroupAction::test_ec2_add_security_groups tests/test_ec2.py::TestModifySecurityGroupAction::test_invalid_modify_groups_schema tests/test_ec2.py::TestModifySecurityGroupAction::test_security_group_modify_groups_action tests/test_ec2.py::TestModifySecurityGroupAction::test_security_group_type tests/test_ec2.py::TestAutoRecoverAlarmAction::test_autorecover_alarm tests/test_ec2.py::TestFilter::test_not_filter tests/test_ec2.py::TestUserData::test_regex_filter tests/test_ec2.py::TestLaunchTemplate::test_launch_template_id_not_found tests/test_ec2.py::TestLaunchTemplate::test_launch_template_versions tests/test_ec2.py::TestLaunchTemplate::test_template_get_resources tests/test_ec2.py::TestReservedInstance::test_reserved_instance_query tests/test_ec2.py::TestMonitoringInstance::test_monitor_instance tests/test_ec2.py::TestMonitoringInstance::test_unmonitor_instance
c7n/resources/ec2.py
c7n/resources/ec2.py