Repository: cloud-custodian/cloud-custodian

Issue:
Support for AWS Systems Manager - Session Manager
### Describe the feature

There is no support for ssm-session (AWS Systems Manager - Session Manager). I would like to have a custodian policy to ensure the session preferences configurations is set per policy and any violation is flagged and remediated by the custodian policy. Currently only below SSM are supported.

- aws.ssm-activation
- aws.ssm-data-sync
- aws.ssm-document
- aws.ssm-managed-instance
- aws.ssm-parameter

Steps in AWS Console
1. Login to AWS console
2. Systems Manager --> Session Manager --> Preference
3. Check if S3 logging is enabled. If not enabled, flag the session as non-compliant. 

### Extra information or context

_No response_


Discussion:
thanks sounds good and contributions welcome

Resolving patch:
diff --git a/c7n/resources/resource_map.py b/c7n/resources/resource_map.py
--- a/c7n/resources/resource_map.py
+++ b/c7n/resources/resource_map.py
@@ -236,6 +236,7 @@
   "aws.ssm-managed-instance": "c7n.resources.ssm.ManagedInstance",
   "aws.ssm-parameter": "c7n.resources.ssm.SSMParameter",
   "aws.ssm-patch-group": "c7n.resources.ssm.SsmPatchGroup",
+  "aws.ssm-session-manager": "c7n.resources.ssm.SSMSessionManager",
   "aws.step-machine": "c7n.resources.sfn.StepFunction",
   "aws.storage-gateway": "c7n.resources.storagegw.StorageGateway",
   "aws.streaming-distribution": "c7n.resources.cloudfront.StreamingDistribution",
diff --git a/c7n/resources/ssm.py b/c7n/resources/ssm.py
--- a/c7n/resources/ssm.py
+++ b/c7n/resources/ssm.py
@@ -842,3 +842,52 @@ class resource_type(TypeInfo):
         id = "PatchGroup"
         name = "PatchGroup"
 
+
+@resources.register('ssm-session-manager')
+class SSMSessionManager(QueryResourceManager):
+
+    class resource_type(TypeInfo):
+        service = 'ssm'
+        enum_spec = ('describe_sessions', 'Sessions', None)
+        name = "SessionId"
+        id = "SessionId"
+        arn_type = 'session'
+
+    retry = staticmethod(get_retry(('Throttled',)))
+    permissions = ('ssm:DescribeSessions', 'ssm:TerminateSession', )
+
+    augment = universal_augment
+
+    def resources(self, query=None):
+        if query is None:
+            query = {}
+        if 'State' not in query:
+            # Default to Active if not given
+            query['State'] = 'Active'
+        return super(SSMSessionManager, self).resources(query=query)
+
+@SSMSessionManager.action_registry.register('terminate')
+class TerminateSession(Action):
+    """ Terminate a session.
+
+    This call will permanently end a session's connection to an instance.
+
+    :Example:
+
+    .. code-block:: yaml
+
+        policies:
+          - name: ssm-session-termination
+            resource: ssm-session-manager
+            actions:
+              - terminate
+    """
+    schema = type_schema('terminate')
+
+    def get_permissions(self):
+        return ('ssm:TerminateSession',)
+
+    def process(self, resources):
+        client = local_session(self.manager.session_factory).client('ssm')
+        for r in resources:
+            client.terminate_session(SessionId=r['SessionId'])


Test patch:
diff --git a/tests/data/placebo/test_ssm_session_manager_terminate/ssm.DescribeSessions_1.json b/tests/data/placebo/test_ssm_session_manager_terminate/ssm.DescribeSessions_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ssm_session_manager_terminate/ssm.DescribeSessions_1.json
@@ -0,0 +1,29 @@
+{
+    "status_code": 200,
+    "data": {
+        "Sessions": [
+            {
+                "SessionId": "timmy-01cd54a0c8322733a",
+                "Target": "i-04ddf14c18a4d4995",
+                "Status": "Connected",
+                "StartDate": {
+                    "__class__": "datetime",
+                    "year": 2023,
+                    "month": 8,
+                    "day": 14,
+                    "hour": 15,
+                    "minute": 24,
+                    "second": 58,
+                    "microsecond": 207000
+                },
+                "Owner": "arn:aws:iam::644160558196:user/timmy",
+                "Details": "",
+                "OutputUrl": {
+                    "S3OutputUrl": "",
+                    "CloudWatchOutputUrl": ""
+                }
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ssm_session_manager_terminate/ssm.DescribeSessions_2.json b/tests/data/placebo/test_ssm_session_manager_terminate/ssm.DescribeSessions_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ssm_session_manager_terminate/ssm.DescribeSessions_2.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "Sessions": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ssm_session_manager_terminate/ssm.TerminateSession_1.json b/tests/data/placebo/test_ssm_session_manager_terminate/ssm.TerminateSession_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ssm_session_manager_terminate/ssm.TerminateSession_1.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "SessionId": "timmy-01cd54a0c8322733a",
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ssm_session_manager_terminate/tagging.GetResources_1.json b/tests/data/placebo/test_ssm_session_manager_terminate/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ssm_session_manager_terminate/tagging.GetResources_1.json
@@ -0,0 +1,22 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [
+            {
+                "ResourceARN": "arn:aws:ssm:us-east-1:644160558196:session/timmy-01cd54a0c8322733a",
+                "Tags": [
+                    {
+                        "Key": "aws:ssmmessages:session-id",
+                        "Value": "AIDAZL6XWCR2FXBVQTGH4"
+                    },
+                    {
+                        "Key": "aws:ssmmessages:target-id",
+                        "Value": "i-04ddf14c18a4d4995"
+                    }
+                ]
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/test_ssm.py b/tests/test_ssm.py
--- a/tests/test_ssm.py
+++ b/tests/test_ssm.py
@@ -502,6 +502,23 @@ def test_data_sync_delete_error(self):
         data_syncs = p.run()
         self.assertEqual(len(data_syncs), 3)
 
+    def test_ssm_session_manager_terminate(self):
+        session_factory = self.replay_flight_data("test_ssm_session_manager_terminate")
+        p = self.load_policy({
+            'name': 'ssm-session-manager',
+            'resource': 'ssm-session-manager',
+            'actions': [
+                    {
+                        'type': 'terminate'
+                    }
+                ]},
+            session_factory=session_factory)
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        client = session_factory().client('ssm', region_name='us-east-1')
+        sessions = client.describe_sessions(State="Active")
+        self.assertEqual(len(sessions["Sessions"]), 0)
+
 
 @terraform("ssm_patch_group")
 def test_ssm_patch_group_query(test, ssm_patch_group):