We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Invalid Property Resources/SESConfigurationSetDestination/Properties/EventDestination/SnsDestination
### CloudFormation Lint Version

0.77.8

### What operating system are you using?

Mac/Ubuntu

### Describe the bug

cfn-lint outputs:

Invalid Property Resources/SESConfigurationSetDestination/Properties/EventDestination/SnsDestination

But this is a valid property:

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ses-configurationseteventdestination-eventdestination.html#cfn-ses-configurationseteventdestination-eventdestination-snsdestination

### Expected behavior

cfn-lint doesn't output an error

### Reproduction template

```yaml
Resources:
  SESConfigurationSetDestination:
    Type: AWS::SES::ConfigurationSetEventDestination
    Properties:
      ConfigurationSetName: Name
      EventDestination:
        Name: DestinationName
        Enabled: true
        MatchingEventTypes:
          - send
          - reject
          - bounce
          - complaint
          - delivery
        SnsDestination:
          TopicARN: Arn
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `scripts/update_specs_services_from_ssm.py`
Content:
```
1 #!/usr/bin/env python
2 """
3 Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 SPDX-License-Identifier: MIT-0
5 """
6 import json
7 import logging
8 
9 import boto3
10 
11 from cfnlint.helpers import REGIONS, get_url_content
12 from cfnlint.maintenance import SPEC_REGIONS
13 
14 """
15     Updates our dynamic patches from SSM data
16     This script requires Boto3 and Credentials to call the SSM API
17 """
18 
19 LOGGER = logging.getLogger("cfnlint")
20 
21 service_map = {
22     "acm": ["AWS::CertificateManager::"],
23     "apigateway": ["AWS::ApiGateway::", "AWS::ApiGatewayV2::"],
24     # 'application-autoscaling': ['AWS::ApplicationAutoScaling::'], ## remove because SSM endpoints aren't correct
25     "appstream": ["AWS::AppStream::"],
26     "appsync": ["AWS::AppSync::"],
27     "athena": ["AWS::Athena::"],
28     "autoscaling": ["AWS::AutoScaling::"],
29     "batch": ["AWS::Batch::"],
30     "budgets": ["AWS::Budgets::"],
31     "cloud9": ["AWS::Cloud9::"],
32     "cloudfront": ["AWS::CloudFront::"],
33     "cloudtrail": ["AWS::CloudTrail::"],
34     "cloudwatch": ["AWS::CloudWatch::"],
35     "codebuild": ["AWS::CodeBuild::"],
36     "codecommit": ["AWS::CodeCommit::"],
37     "codedeploy": ["AWS::CodeDeploy::"],
38     "codepipeline": ["AWS::CodePipeline::"],
39     "cognito-identity": ["AWS::Cognito::"],
40     "config": ["AWS::Config::"],
41     "datapipeline": ["AWS::DataPipeline::"],
42     "dax": ["AWS::DAX::"],
43     "dms": ["AWS::DMS::"],
44     "docdb": ["AWS::DocDB::"],
45     "ds": ["AWS::DirectoryService::"],
46     "dynamodb": ["AWS::DynamoDB::"],
47     "ec2": ["AWS::EC2::"],
48     "ecr": ["AWS::ECR::"],
49     "ecs": ["AWS::ECS::"],
50     "efs": ["AWS::EFS::"],
51     "eks": ["AWS::EKS::"],
52     "elasticache": ["AWS::ElastiCache::"],
53     "elasticbeanstalk": ["AWS::ElasticBeanstalk::"],
54     "elb": ["AWS::ElasticLoadBalancing::", "AWS::ElasticLoadBalancingV2::"],
55     "emr": ["AWS::EMR::"],
56     "es": ["AWS::Elasticsearch::"],
57     "events": ["AWS::Events::"],
58     "firehose": ["AWS::KinesisFirehose::"],
59     "fsx": ["AWS::FSx::"],
60     "gamelift": ["AWS::GameLift::"],
61     "glue": ["AWS::Glue::"],
62     "greengrass": ["AWS::Greengrass::"],
63     "guardduty": ["AWS::GuardDuty::"],
64     "inspector": ["AWS::Inspector::"],
65     "iot": ["AWS::IoT::"],
66     "iot1click-projects": ["AWS::IoT1Click::"],
67     "iotanalytics": ["AWS::IoTAnalytics::"],
68     "kinesis": ["AWS::Kinesis::"],
69     "kinesisanalytics": ["AWS::KinesisAnalytics::", "AWS::KinesisAnalyticsV2::"],
70     "kms": ["AWS::KMS::"],
71     "lambda": ["AWS::Lambda::"],
72     "logs": ["AWS::Logs::"],
73     "mq": ["AWS::AmazonMQ::"],
74     "neptune": ["AWS::Neptune::"],
75     "opsworks": ["AWS::OpsWorks::"],
76     "opsworkscm": ["AWS::OpsWorksCM::"],
77     "ram": ["AWS::RAM::"],
78     "rds": ["AWS::RDS::"],
79     "redshift": ["AWS::Redshift::"],
80     "robomaker": ["AWS::RoboMaker::"],
81     "route53": ["AWS::Route53::"],
82     "route53resolver": [
83         "AWS::Route53Resolver::ResolverRule",
84         "AWS::Route53Resolver::ResolverEndpoint",
85     ],
86     "s3": ["AWS::S3::"],
87     "sagemaker": ["AWS::SageMaker::"],
88     "sdb": ["AWS::SDB::"],
89     "secretsmanager": ["AWS::SecretsManager::"],
90     "servicecatalog": ["AWS::ServiceCatalog::"],
91     "servicediscovery": ["AWS::ServiceDiscovery::"],
92     "ses": ["AWS::SES::"],
93     "sns": ["AWS::SNS::"],
94     "sqs": ["AWS::SQS::"],
95     "ssm": ["AWS::SSM::"],
96     "stepfunctions": ["AWS::StepFunctions::"],
97     "waf-regional": ["AWS::WAFRegional::"],
98     "workspaces": ["AWS::WorkSpaces::"],
99 }
100 
101 session = boto3.session.Session()
102 client = session.client("ssm", region_name="us-east-1")
103 
104 
105 def configure_logging():
106     """Setup Logging"""
107     ch = logging.StreamHandler()
108     ch.setLevel(logging.INFO)
109 
110     LOGGER.setLevel(logging.INFO)
111     log_formatter = logging.Formatter(
112         "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
113     )
114     ch.setFormatter(log_formatter)
115 
116     # make sure all other log handlers are removed before adding it back
117     for handler in LOGGER.handlers:
118         LOGGER.removeHandler(handler)
119     LOGGER.addHandler(ch)
120 
121 
122 def update_outputs(region, resource_type, name, outputs):
123     """update outputs with appropriate results"""
124     element = {"op": "remove", "path": "/%s/%s" % (resource_type, name)}
125     outputs[region].append(element)
126 
127     return outputs
128 
129 
130 def get_regions_for_service(service):
131     """get regions for a service"""
132     LOGGER.info("Get the regions for service %s", service)
133     results = []
134     paginator = client.get_paginator("get_parameters_by_path")
135     page_iterator = paginator.paginate(
136         Path="/aws/service/global-infrastructure/services/{}/regions".format(service),
137     )
138 
139     for page in page_iterator:
140         for region in page.get("Parameters"):
141             results.append(region.get("Value"))
142 
143     return results
144 
145 
146 def add_spec_patch(region, services):
147     """Go through spec and determine patching"""
148     LOGGER.info("Create 06_ssm_service_removal patch for region %s", region)
149     spec = json.loads(get_url_content(SPEC_REGIONS.get(region)))
150 
151     patches = []
152 
153     for spec_type in ["ResourceTypes", "PropertyTypes"]:
154         for resource in sorted(spec.get(spec_type).keys()):
155             for service in services:
156                 for spec_name in service_map.get(service):
157                     if resource.startswith(spec_name):
158                         element = {
159                             "op": "remove",
160                             "path": "/%s/%s" % (spec_type, resource),
161                         }
162                         patches.append(element)
163 
164     filename = "src/cfnlint/data/ExtendedSpecs/%s/06_ssm_service_removal.json" % region
165     with open(filename, "w+", encoding="utf-8") as f:
166         json.dump(patches, f, indent=1, sort_keys=True, separators=(",", ": "))
167 
168 
169 def add_spec_missing_services_patch(region, services):
170     """Go through spec and determine patching"""
171     LOGGER.info("Create 07_ssm_service_addition patch for region %s", region)
172     spec_string = get_url_content(SPEC_REGIONS.get(region))
173     spec_string_standard = get_url_content(SPEC_REGIONS.get("us-east-1"))
174 
175     spec = json.loads(spec_string)
176     spec_standard = json.loads(spec_string_standard)
177 
178     patches = []
179 
180     for spec_type in ["ResourceTypes"]:
181         for service in services:
182             found = False
183             for resource in sorted(spec.get(spec_type).keys()):
184                 for spec_name in service_map.get(service):
185                     if resource.startswith(spec_name):
186                         found = True
187             if found is False:
188                 for standard_spec_type in ["ResourceTypes", "PropertyTypes"]:
189                     for resource in sorted(
190                         spec_standard.get(standard_spec_type).keys()
191                     ):
192                         for spec_name in service_map.get(service):
193                             if resource.startswith(spec_name):
194                                 if spec_standard.get(standard_spec_type).get(resource):
195                                     element = {
196                                         "op": "add",
197                                         "path": "/%s/%s"
198                                         % (standard_spec_type, resource),
199                                         "value": spec_standard.get(
200                                             standard_spec_type
201                                         ).get(resource),
202                                     }
203                                     patches.append(element)
204                                 elif standard_spec_type == "ResourceTypes":
205                                     print("patch for %s not found" % service)
206 
207     if patches:
208         filename = (
209             "src/cfnlint/data/ExtendedSpecs/%s/07_ssm_service_addition.json" % region
210         )
211         with open(filename, "w+", encoding="utf-8") as f:
212             json.dump(patches, f, indent=1, sort_keys=True, separators=(",", ": "))
213 
214 
215 def main():
216     """main function"""
217     configure_logging()
218 
219     all_regions = list(set(REGIONS))
220     region_service_removal_map = {}
221     region_service_add_map = {}
222     for region in all_regions:
223         region_service_removal_map[region] = []
224         region_service_add_map[region] = []
225     for service in service_map:
226         regions = get_regions_for_service(service)
227         if regions:
228             for region in list(set(regions)):
229                 region_service_add_map[region].append(service)
230             for region in list(set(all_regions) - set(regions)):
231                 region_service_removal_map[region].append(service)
232 
233     for region, services in region_service_removal_map.items():
234         if services:
235             add_spec_patch(region, services)
236     for region, services in region_service_add_map.items():
237         if services:
238             add_spec_missing_services_patch(region, services)
239 
240 
241 if __name__ == "__main__":
242     try:
243         main()
244     except (ValueError, TypeError):
245         LOGGER.error(ValueError)
246
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/scripts/update_specs_services_from_ssm.py b/scripts/update_specs_services_from_ssm.py
--- a/scripts/update_specs_services_from_ssm.py
+++ b/scripts/update_specs_services_from_ssm.py
@@ -204,12 +204,12 @@
                                 elif standard_spec_type == "ResourceTypes":
                                     print("patch for %s not found" % service)
 
-    if patches:
-        filename = (
-            "src/cfnlint/data/ExtendedSpecs/%s/07_ssm_service_addition.json" % region
-        )
-        with open(filename, "w+", encoding="utf-8") as f:
-            json.dump(patches, f, indent=1, sort_keys=True, separators=(",", ": "))
+    filename = (
+        "src/cfnlint/data/ExtendedSpecs/%s/07_ssm_service_addition.json" % region
+    )
+    with open(filename, "w+", encoding="utf-8") as f:
+        json.dump(patches, f, indent=1, sort_keys=True, separators=(",", ": "))
+            
 
 
 def main():

{"golden_diff": "diff --git a/scripts/update_specs_services_from_ssm.py b/scripts/update_specs_services_from_ssm.py\n--- a/scripts/update_specs_services_from_ssm.py\n+++ b/scripts/update_specs_services_from_ssm.py\n@@ -204,12 +204,12 @@\n                                 elif standard_spec_type == \"ResourceTypes\":\n                                     print(\"patch for %s not found\" % service)\n \n-    if patches:\n-        filename = (\n-            \"src/cfnlint/data/ExtendedSpecs/%s/07_ssm_service_addition.json\" % region\n-        )\n-        with open(filename, \"w+\", encoding=\"utf-8\") as f:\n-            json.dump(patches, f, indent=1, sort_keys=True, separators=(\",\", \": \"))\n+    filename = (\n+        \"src/cfnlint/data/ExtendedSpecs/%s/07_ssm_service_addition.json\" % region\n+    )\n+    with open(filename, \"w+\", encoding=\"utf-8\") as f:\n+        json.dump(patches, f, indent=1, sort_keys=True, separators=(\",\", \": \"))\n+            \n \n \n def main():\n", "issue": "Invalid Property Resources/SESConfigurationSetDestination/Properties/EventDestination/SnsDestination\n### CloudFormation Lint Version\r\n\r\n0.77.8\r\n\r\n### What operating system are you using?\r\n\r\nMac/Ubuntu\r\n\r\n### Describe the bug\r\n\r\ncfn-lint outputs:\r\n\r\nInvalid Property Resources/SESConfigurationSetDestination/Properties/EventDestination/SnsDestination\r\n\r\nBut this is a valid property:\r\n\r\nhttps://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ses-configurationseteventdestination-eventdestination.html#cfn-ses-configurationseteventdestination-eventdestination-snsdestination\r\n\r\n### Expected behavior\r\n\r\ncfn-lint doesn't output an error\r\n\r\n### Reproduction template\r\n\r\n```yaml\r\nResources:\r\n  SESConfigurationSetDestination:\r\n    Type: AWS::SES::ConfigurationSetEventDestination\r\n    Properties:\r\n      ConfigurationSetName: Name\r\n      EventDestination:\r\n        Name: DestinationName\r\n        Enabled: true\r\n        MatchingEventTypes:\r\n          - send\r\n          - reject\r\n          - bounce\r\n          - complaint\r\n          - delivery\r\n        SnsDestination:\r\n          TopicARN: Arn\r\n```\n", "before_files": [{"content": "#!/usr/bin/env python\n\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport json\nimport logging\n\nimport boto3\n\nfrom cfnlint.helpers import REGIONS, get_url_content\nfrom cfnlint.maintenance import SPEC_REGIONS\n\n\"\"\"\n    Updates our dynamic patches from SSM data\n    This script requires Boto3 and Credentials to call the SSM API\n\"\"\"\n\nLOGGER = logging.getLogger(\"cfnlint\")\n\nservice_map = {\n    \"acm\": [\"AWS::CertificateManager::\"],\n    \"apigateway\": [\"AWS::ApiGateway::\", \"AWS::ApiGatewayV2::\"],\n    # 'application-autoscaling': ['AWS::ApplicationAutoScaling::'], ## remove because SSM endpoints aren't correct\n    \"appstream\": [\"AWS::AppStream::\"],\n    \"appsync\": [\"AWS::AppSync::\"],\n    \"athena\": [\"AWS::Athena::\"],\n    \"autoscaling\": [\"AWS::AutoScaling::\"],\n    \"batch\": [\"AWS::Batch::\"],\n    \"budgets\": [\"AWS::Budgets::\"],\n    \"cloud9\": [\"AWS::Cloud9::\"],\n    \"cloudfront\": [\"AWS::CloudFront::\"],\n    \"cloudtrail\": [\"AWS::CloudTrail::\"],\n    \"cloudwatch\": [\"AWS::CloudWatch::\"],\n    \"codebuild\": [\"AWS::CodeBuild::\"],\n    \"codecommit\": [\"AWS::CodeCommit::\"],\n    \"codedeploy\": [\"AWS::CodeDeploy::\"],\n    \"codepipeline\": [\"AWS::CodePipeline::\"],\n    \"cognito-identity\": [\"AWS::Cognito::\"],\n    \"config\": [\"AWS::Config::\"],\n    \"datapipeline\": [\"AWS::DataPipeline::\"],\n    \"dax\": [\"AWS::DAX::\"],\n    \"dms\": [\"AWS::DMS::\"],\n    \"docdb\": [\"AWS::DocDB::\"],\n    \"ds\": [\"AWS::DirectoryService::\"],\n    \"dynamodb\": [\"AWS::DynamoDB::\"],\n    \"ec2\": [\"AWS::EC2::\"],\n    \"ecr\": [\"AWS::ECR::\"],\n    \"ecs\": [\"AWS::ECS::\"],\n    \"efs\": [\"AWS::EFS::\"],\n    \"eks\": [\"AWS::EKS::\"],\n    \"elasticache\": [\"AWS::ElastiCache::\"],\n    \"elasticbeanstalk\": [\"AWS::ElasticBeanstalk::\"],\n    \"elb\": [\"AWS::ElasticLoadBalancing::\", \"AWS::ElasticLoadBalancingV2::\"],\n    \"emr\": [\"AWS::EMR::\"],\n    \"es\": [\"AWS::Elasticsearch::\"],\n    \"events\": [\"AWS::Events::\"],\n    \"firehose\": [\"AWS::KinesisFirehose::\"],\n    \"fsx\": [\"AWS::FSx::\"],\n    \"gamelift\": [\"AWS::GameLift::\"],\n    \"glue\": [\"AWS::Glue::\"],\n    \"greengrass\": [\"AWS::Greengrass::\"],\n    \"guardduty\": [\"AWS::GuardDuty::\"],\n    \"inspector\": [\"AWS::Inspector::\"],\n    \"iot\": [\"AWS::IoT::\"],\n    \"iot1click-projects\": [\"AWS::IoT1Click::\"],\n    \"iotanalytics\": [\"AWS::IoTAnalytics::\"],\n    \"kinesis\": [\"AWS::Kinesis::\"],\n    \"kinesisanalytics\": [\"AWS::KinesisAnalytics::\", \"AWS::KinesisAnalyticsV2::\"],\n    \"kms\": [\"AWS::KMS::\"],\n    \"lambda\": [\"AWS::Lambda::\"],\n    \"logs\": [\"AWS::Logs::\"],\n    \"mq\": [\"AWS::AmazonMQ::\"],\n    \"neptune\": [\"AWS::Neptune::\"],\n    \"opsworks\": [\"AWS::OpsWorks::\"],\n    \"opsworkscm\": [\"AWS::OpsWorksCM::\"],\n    \"ram\": [\"AWS::RAM::\"],\n    \"rds\": [\"AWS::RDS::\"],\n    \"redshift\": [\"AWS::Redshift::\"],\n    \"robomaker\": [\"AWS::RoboMaker::\"],\n    \"route53\": [\"AWS::Route53::\"],\n    \"route53resolver\": [\n        \"AWS::Route53Resolver::ResolverRule\",\n        \"AWS::Route53Resolver::ResolverEndpoint\",\n    ],\n    \"s3\": [\"AWS::S3::\"],\n    \"sagemaker\": [\"AWS::SageMaker::\"],\n    \"sdb\": [\"AWS::SDB::\"],\n    \"secretsmanager\": [\"AWS::SecretsManager::\"],\n    \"servicecatalog\": [\"AWS::ServiceCatalog::\"],\n    \"servicediscovery\": [\"AWS::ServiceDiscovery::\"],\n    \"ses\": [\"AWS::SES::\"],\n    \"sns\": [\"AWS::SNS::\"],\n    \"sqs\": [\"AWS::SQS::\"],\n    \"ssm\": [\"AWS::SSM::\"],\n    \"stepfunctions\": [\"AWS::StepFunctions::\"],\n    \"waf-regional\": [\"AWS::WAFRegional::\"],\n    \"workspaces\": [\"AWS::WorkSpaces::\"],\n}\n\nsession = boto3.session.Session()\nclient = session.client(\"ssm\", region_name=\"us-east-1\")\n\n\ndef configure_logging():\n    \"\"\"Setup Logging\"\"\"\n    ch = logging.StreamHandler()\n    ch.setLevel(logging.INFO)\n\n    LOGGER.setLevel(logging.INFO)\n    log_formatter = logging.Formatter(\n        \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\"\n    )\n    ch.setFormatter(log_formatter)\n\n    # make sure all other log handlers are removed before adding it back\n    for handler in LOGGER.handlers:\n        LOGGER.removeHandler(handler)\n    LOGGER.addHandler(ch)\n\n\ndef update_outputs(region, resource_type, name, outputs):\n    \"\"\"update outputs with appropriate results\"\"\"\n    element = {\"op\": \"remove\", \"path\": \"/%s/%s\" % (resource_type, name)}\n    outputs[region].append(element)\n\n    return outputs\n\n\ndef get_regions_for_service(service):\n    \"\"\"get regions for a service\"\"\"\n    LOGGER.info(\"Get the regions for service %s\", service)\n    results = []\n    paginator = client.get_paginator(\"get_parameters_by_path\")\n    page_iterator = paginator.paginate(\n        Path=\"/aws/service/global-infrastructure/services/{}/regions\".format(service),\n    )\n\n    for page in page_iterator:\n        for region in page.get(\"Parameters\"):\n            results.append(region.get(\"Value\"))\n\n    return results\n\n\ndef add_spec_patch(region, services):\n    \"\"\"Go through spec and determine patching\"\"\"\n    LOGGER.info(\"Create 06_ssm_service_removal patch for region %s\", region)\n    spec = json.loads(get_url_content(SPEC_REGIONS.get(region)))\n\n    patches = []\n\n    for spec_type in [\"ResourceTypes\", \"PropertyTypes\"]:\n        for resource in sorted(spec.get(spec_type).keys()):\n            for service in services:\n                for spec_name in service_map.get(service):\n                    if resource.startswith(spec_name):\n                        element = {\n                            \"op\": \"remove\",\n                            \"path\": \"/%s/%s\" % (spec_type, resource),\n                        }\n                        patches.append(element)\n\n    filename = \"src/cfnlint/data/ExtendedSpecs/%s/06_ssm_service_removal.json\" % region\n    with open(filename, \"w+\", encoding=\"utf-8\") as f:\n        json.dump(patches, f, indent=1, sort_keys=True, separators=(\",\", \": \"))\n\n\ndef add_spec_missing_services_patch(region, services):\n    \"\"\"Go through spec and determine patching\"\"\"\n    LOGGER.info(\"Create 07_ssm_service_addition patch for region %s\", region)\n    spec_string = get_url_content(SPEC_REGIONS.get(region))\n    spec_string_standard = get_url_content(SPEC_REGIONS.get(\"us-east-1\"))\n\n    spec = json.loads(spec_string)\n    spec_standard = json.loads(spec_string_standard)\n\n    patches = []\n\n    for spec_type in [\"ResourceTypes\"]:\n        for service in services:\n            found = False\n            for resource in sorted(spec.get(spec_type).keys()):\n                for spec_name in service_map.get(service):\n                    if resource.startswith(spec_name):\n                        found = True\n            if found is False:\n                for standard_spec_type in [\"ResourceTypes\", \"PropertyTypes\"]:\n                    for resource in sorted(\n                        spec_standard.get(standard_spec_type).keys()\n                    ):\n                        for spec_name in service_map.get(service):\n                            if resource.startswith(spec_name):\n                                if spec_standard.get(standard_spec_type).get(resource):\n                                    element = {\n                                        \"op\": \"add\",\n                                        \"path\": \"/%s/%s\"\n                                        % (standard_spec_type, resource),\n                                        \"value\": spec_standard.get(\n                                            standard_spec_type\n                                        ).get(resource),\n                                    }\n                                    patches.append(element)\n                                elif standard_spec_type == \"ResourceTypes\":\n                                    print(\"patch for %s not found\" % service)\n\n    if patches:\n        filename = (\n            \"src/cfnlint/data/ExtendedSpecs/%s/07_ssm_service_addition.json\" % region\n        )\n        with open(filename, \"w+\", encoding=\"utf-8\") as f:\n            json.dump(patches, f, indent=1, sort_keys=True, separators=(\",\", \": \"))\n\n\ndef main():\n    \"\"\"main function\"\"\"\n    configure_logging()\n\n    all_regions = list(set(REGIONS))\n    region_service_removal_map = {}\n    region_service_add_map = {}\n    for region in all_regions:\n        region_service_removal_map[region] = []\n        region_service_add_map[region] = []\n    for service in service_map:\n        regions = get_regions_for_service(service)\n        if regions:\n            for region in list(set(regions)):\n                region_service_add_map[region].append(service)\n            for region in list(set(all_regions) - set(regions)):\n                region_service_removal_map[region].append(service)\n\n    for region, services in region_service_removal_map.items():\n        if services:\n            add_spec_patch(region, services)\n    for region, services in region_service_add_map.items():\n        if services:\n            add_spec_missing_services_patch(region, services)\n\n\nif __name__ == \"__main__\":\n    try:\n        main()\n    except (ValueError, TypeError):\n        LOGGER.error(ValueError)\n", "path": "scripts/update_specs_services_from_ssm.py"}], "after_files": [{"content": "#!/usr/bin/env python\n\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport json\nimport logging\n\nimport boto3\n\nfrom cfnlint.helpers import REGIONS, get_url_content\nfrom cfnlint.maintenance import SPEC_REGIONS\n\n\"\"\"\n    Updates our dynamic patches from SSM data\n    This script requires Boto3 and Credentials to call the SSM API\n\"\"\"\n\nLOGGER = logging.getLogger(\"cfnlint\")\n\nservice_map = {\n    \"acm\": [\"AWS::CertificateManager::\"],\n    \"apigateway\": [\"AWS::ApiGateway::\", \"AWS::ApiGatewayV2::\"],\n    # 'application-autoscaling': ['AWS::ApplicationAutoScaling::'], ## remove because SSM endpoints aren't correct\n    \"appstream\": [\"AWS::AppStream::\"],\n    \"appsync\": [\"AWS::AppSync::\"],\n    \"athena\": [\"AWS::Athena::\"],\n    \"autoscaling\": [\"AWS::AutoScaling::\"],\n    \"batch\": [\"AWS::Batch::\"],\n    \"budgets\": [\"AWS::Budgets::\"],\n    \"cloud9\": [\"AWS::Cloud9::\"],\n    \"cloudfront\": [\"AWS::CloudFront::\"],\n    \"cloudtrail\": [\"AWS::CloudTrail::\"],\n    \"cloudwatch\": [\"AWS::CloudWatch::\"],\n    \"codebuild\": [\"AWS::CodeBuild::\"],\n    \"codecommit\": [\"AWS::CodeCommit::\"],\n    \"codedeploy\": [\"AWS::CodeDeploy::\"],\n    \"codepipeline\": [\"AWS::CodePipeline::\"],\n    \"cognito-identity\": [\"AWS::Cognito::\"],\n    \"config\": [\"AWS::Config::\"],\n    \"datapipeline\": [\"AWS::DataPipeline::\"],\n    \"dax\": [\"AWS::DAX::\"],\n    \"dms\": [\"AWS::DMS::\"],\n    \"docdb\": [\"AWS::DocDB::\"],\n    \"ds\": [\"AWS::DirectoryService::\"],\n    \"dynamodb\": [\"AWS::DynamoDB::\"],\n    \"ec2\": [\"AWS::EC2::\"],\n    \"ecr\": [\"AWS::ECR::\"],\n    \"ecs\": [\"AWS::ECS::\"],\n    \"efs\": [\"AWS::EFS::\"],\n    \"eks\": [\"AWS::EKS::\"],\n    \"elasticache\": [\"AWS::ElastiCache::\"],\n    \"elasticbeanstalk\": [\"AWS::ElasticBeanstalk::\"],\n    \"elb\": [\"AWS::ElasticLoadBalancing::\", \"AWS::ElasticLoadBalancingV2::\"],\n    \"emr\": [\"AWS::EMR::\"],\n    \"es\": [\"AWS::Elasticsearch::\"],\n    \"events\": [\"AWS::Events::\"],\n    \"firehose\": [\"AWS::KinesisFirehose::\"],\n    \"fsx\": [\"AWS::FSx::\"],\n    \"gamelift\": [\"AWS::GameLift::\"],\n    \"glue\": [\"AWS::Glue::\"],\n    \"greengrass\": [\"AWS::Greengrass::\"],\n    \"guardduty\": [\"AWS::GuardDuty::\"],\n    \"inspector\": [\"AWS::Inspector::\"],\n    \"iot\": [\"AWS::IoT::\"],\n    \"iot1click-projects\": [\"AWS::IoT1Click::\"],\n    \"iotanalytics\": [\"AWS::IoTAnalytics::\"],\n    \"kinesis\": [\"AWS::Kinesis::\"],\n    \"kinesisanalytics\": [\"AWS::KinesisAnalytics::\", \"AWS::KinesisAnalyticsV2::\"],\n    \"kms\": [\"AWS::KMS::\"],\n    \"lambda\": [\"AWS::Lambda::\"],\n    \"logs\": [\"AWS::Logs::\"],\n    \"mq\": [\"AWS::AmazonMQ::\"],\n    \"neptune\": [\"AWS::Neptune::\"],\n    \"opsworks\": [\"AWS::OpsWorks::\"],\n    \"opsworkscm\": [\"AWS::OpsWorksCM::\"],\n    \"ram\": [\"AWS::RAM::\"],\n    \"rds\": [\"AWS::RDS::\"],\n    \"redshift\": [\"AWS::Redshift::\"],\n    \"robomaker\": [\"AWS::RoboMaker::\"],\n    \"route53\": [\"AWS::Route53::\"],\n    \"route53resolver\": [\n        \"AWS::Route53Resolver::ResolverRule\",\n        \"AWS::Route53Resolver::ResolverEndpoint\",\n    ],\n    \"s3\": [\"AWS::S3::\"],\n    \"sagemaker\": [\"AWS::SageMaker::\"],\n    \"sdb\": [\"AWS::SDB::\"],\n    \"secretsmanager\": [\"AWS::SecretsManager::\"],\n    \"servicecatalog\": [\"AWS::ServiceCatalog::\"],\n    \"servicediscovery\": [\"AWS::ServiceDiscovery::\"],\n    \"ses\": [\"AWS::SES::\"],\n    \"sns\": [\"AWS::SNS::\"],\n    \"sqs\": [\"AWS::SQS::\"],\n    \"ssm\": [\"AWS::SSM::\"],\n    \"stepfunctions\": [\"AWS::StepFunctions::\"],\n    \"waf-regional\": [\"AWS::WAFRegional::\"],\n    \"workspaces\": [\"AWS::WorkSpaces::\"],\n}\n\nsession = boto3.session.Session()\nclient = session.client(\"ssm\", region_name=\"us-east-1\")\n\n\ndef configure_logging():\n    \"\"\"Setup Logging\"\"\"\n    ch = logging.StreamHandler()\n    ch.setLevel(logging.INFO)\n\n    LOGGER.setLevel(logging.INFO)\n    log_formatter = logging.Formatter(\n        \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\"\n    )\n    ch.setFormatter(log_formatter)\n\n    # make sure all other log handlers are removed before adding it back\n    for handler in LOGGER.handlers:\n        LOGGER.removeHandler(handler)\n    LOGGER.addHandler(ch)\n\n\ndef update_outputs(region, resource_type, name, outputs):\n    \"\"\"update outputs with appropriate results\"\"\"\n    element = {\"op\": \"remove\", \"path\": \"/%s/%s\" % (resource_type, name)}\n    outputs[region].append(element)\n\n    return outputs\n\n\ndef get_regions_for_service(service):\n    \"\"\"get regions for a service\"\"\"\n    LOGGER.info(\"Get the regions for service %s\", service)\n    results = []\n    paginator = client.get_paginator(\"get_parameters_by_path\")\n    page_iterator = paginator.paginate(\n        Path=\"/aws/service/global-infrastructure/services/{}/regions\".format(service),\n    )\n\n    for page in page_iterator:\n        for region in page.get(\"Parameters\"):\n            results.append(region.get(\"Value\"))\n\n    return results\n\n\ndef add_spec_patch(region, services):\n    \"\"\"Go through spec and determine patching\"\"\"\n    LOGGER.info(\"Create 06_ssm_service_removal patch for region %s\", region)\n    spec = json.loads(get_url_content(SPEC_REGIONS.get(region)))\n\n    patches = []\n\n    for spec_type in [\"ResourceTypes\", \"PropertyTypes\"]:\n        for resource in sorted(spec.get(spec_type).keys()):\n            for service in services:\n                for spec_name in service_map.get(service):\n                    if resource.startswith(spec_name):\n                        element = {\n                            \"op\": \"remove\",\n                            \"path\": \"/%s/%s\" % (spec_type, resource),\n                        }\n                        patches.append(element)\n\n    filename = \"src/cfnlint/data/ExtendedSpecs/%s/06_ssm_service_removal.json\" % region\n    with open(filename, \"w+\", encoding=\"utf-8\") as f:\n        json.dump(patches, f, indent=1, sort_keys=True, separators=(\",\", \": \"))\n\n\ndef add_spec_missing_services_patch(region, services):\n    \"\"\"Go through spec and determine patching\"\"\"\n    LOGGER.info(\"Create 07_ssm_service_addition patch for region %s\", region)\n    spec_string = get_url_content(SPEC_REGIONS.get(region))\n    spec_string_standard = get_url_content(SPEC_REGIONS.get(\"us-east-1\"))\n\n    spec = json.loads(spec_string)\n    spec_standard = json.loads(spec_string_standard)\n\n    patches = []\n\n    for spec_type in [\"ResourceTypes\"]:\n        for service in services:\n            found = False\n            for resource in sorted(spec.get(spec_type).keys()):\n                for spec_name in service_map.get(service):\n                    if resource.startswith(spec_name):\n                        found = True\n            if found is False:\n                for standard_spec_type in [\"ResourceTypes\", \"PropertyTypes\"]:\n                    for resource in sorted(\n                        spec_standard.get(standard_spec_type).keys()\n                    ):\n                        for spec_name in service_map.get(service):\n                            if resource.startswith(spec_name):\n                                if spec_standard.get(standard_spec_type).get(resource):\n                                    element = {\n                                        \"op\": \"add\",\n                                        \"path\": \"/%s/%s\"\n                                        % (standard_spec_type, resource),\n                                        \"value\": spec_standard.get(\n                                            standard_spec_type\n                                        ).get(resource),\n                                    }\n                                    patches.append(element)\n                                elif standard_spec_type == \"ResourceTypes\":\n                                    print(\"patch for %s not found\" % service)\n\n    filename = (\n        \"src/cfnlint/data/ExtendedSpecs/%s/07_ssm_service_addition.json\" % region\n    )\n    with open(filename, \"w+\", encoding=\"utf-8\") as f:\n        json.dump(patches, f, indent=1, sort_keys=True, separators=(\",\", \": \"))\n            \n\n\ndef main():\n    \"\"\"main function\"\"\"\n    configure_logging()\n\n    all_regions = list(set(REGIONS))\n    region_service_removal_map = {}\n    region_service_add_map = {}\n    for region in all_regions:\n        region_service_removal_map[region] = []\n        region_service_add_map[region] = []\n    for service in service_map:\n        regions = get_regions_for_service(service)\n        if regions:\n            for region in list(set(regions)):\n                region_service_add_map[region].append(service)\n            for region in list(set(all_regions) - set(regions)):\n                region_service_removal_map[region].append(service)\n\n    for region, services in region_service_removal_map.items():\n        if services:\n            add_spec_patch(region, services)\n    for region, services in region_service_add_map.items():\n        if services:\n            add_spec_missing_services_patch(region, services)\n\n\nif __name__ == \"__main__\":\n    try:\n        main()\n    except (ValueError, TypeError):\n        LOGGER.error(ValueError)\n", "path": "scripts/update_specs_services_from_ssm.py"}]}