Repository: cloudtools/troposphere
Pull request #2195 (2023-10-20 13:08:13)

Updates from spec version 145.0.0
Autogenerated by maintenance action

If tests are stuck on https://github.com/peter-evans/create-pull-request/issues/48:
["Manually close pull requests and immediately reopen them. This will enable `on: pull_request` workflows to run and be added as checks."](https://github.com/peter-evans/create-pull-request/blob/master/docs/concepts-guidelines.md#triggering-further-workflow-runs)


Patch:
diff --git a/pyproject.toml b/pyproject.toml
index bf101701..e57a18df 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -3,7 +3,7 @@ include = [
   "**/troposphere",
 ]
 pythonPlatform = "All"
-pythonVersion = "3.7"
+pythonVersion = "3.8"
 reportDuplicateImport = "none"
 reportImportCycles = "none"
 reportIncompatibleMethodOverride = "warning"
diff --git a/setup.cfg b/setup.cfg
index 5c3baf3c..aa9ecb2d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -16,11 +16,11 @@ classifiers =
     Programming Language :: Python
     Programming Language :: Python :: 3 :: Only
     Programming Language :: Python :: 3
-    Programming Language :: Python :: 3.7
     Programming Language :: Python :: 3.8
     Programming Language :: Python :: 3.9
     Programming Language :: Python :: 3.10
     Programming Language :: Python :: 3.11
+    Programming Language :: Python :: 3.12
 project_urls =
     Changelog = https://github.com/cloudtools/troposphere/blob/master/CHANGELOG.md
     Source = https://github.com/cloudtools/troposphere
@@ -32,7 +32,6 @@ tests_require = awacs>=2.0.0
 include_package_data = true
 install_requires =
     cfn_flip >= 1.0.2
-    typing-extensions >= 3.7.4.3; python_version < "3.8"
 packages =
     troposphere
     troposphere.openstack
@@ -42,7 +41,7 @@ packages =
 scripts =
     scripts/cfn
     scripts/cfn2py
-python_requires = >=3.7.0
+python_requires = >=3.8
 zip_safe = false
 
 [options.extras_require]
diff --git a/troposphere/__init__.py b/troposphere/__init__.py
index 05b1592c..db483c11 100644
--- a/troposphere/__init__.py
+++ b/troposphere/__init__.py
@@ -1011,6 +1011,7 @@ class Output(AWSDeclaration):
 class Parameter(AWSDeclaration):
     STRING_PROPERTIES = ["AllowedPattern", "MaxLength", "MinLength"]
     NUMBER_PROPERTIES = ["MaxValue", "MinValue"]
+    COMMA_DELIMITED_LIST = ["AllowedPattern"]
     props = {
         "Type": (str, True),
         "Default": ((str, int, float), False),
@@ -1076,15 +1077,28 @@ class Parameter(AWSDeclaration):
                     if not any(check_type(x, d) for x in allowed):
                         raise ValueError(error_str % (param_type, type(d), dlist))
 
-        if self.properties["Type"] != "String":
-            for p in self.STRING_PROPERTIES:
+        if self.properties["Type"] == "String":
+            not_allowed = [
+                p for p in self.COMMA_DELIMITED_LIST if p not in self.STRING_PROPERTIES
+            ] + self.NUMBER_PROPERTIES
+            for p in not_allowed:
                 if p in self.properties:
                     raise ValueError(
                         "%s can only be used with parameters of " "the String type." % p
                     )
-        if self.properties["Type"] != "Number":
-            for p in self.NUMBER_PROPERTIES:
+        if self.properties["Type"] == "Number":
+            for p in list(set(self.STRING_PROPERTIES + self.COMMA_DELIMITED_LIST)):
                 if p in self.properties:
                     raise ValueError(
                         "%s can only be used with parameters of " "the Number type." % p
                     )
+        if self.properties["Type"] == "CommaDelimitedList":
+            not_allowed = [
+                p for p in self.STRING_PROPERTIES if p not in self.COMMA_DELIMITED_LIST
+            ] + self.NUMBER_PROPERTIES
+            for p in not_allowed:
+                if p in self.properties:
+                    raise ValueError(
+                        "%s can only be used with parameters of "
+                        "the CommaDelimitedList type." % p
+                    )
diff --git a/troposphere/appconfig.py b/troposphere/appconfig.py
index b2a73b15..92c3bb5c 100644
--- a/troposphere/appconfig.py
+++ b/troposphere/appconfig.py
@@ -50,6 +50,7 @@ class ConfigurationProfile(AWSObject):
     props: PropsDictType = {
         "ApplicationId": (str, True),
         "Description": (str, False),
+        "KmsKeyIdentifier": (str, False),
         "LocationUri": (str, True),
         "Name": (str, True),
         "RetrievalRoleArn": (str, False),
diff --git a/troposphere/appintegrations.py b/troposphere/appintegrations.py
index 31075b58..1c3004c4 100644
--- a/troposphere/appintegrations.py
+++ b/troposphere/appintegrations.py
@@ -45,7 +45,7 @@ class DataIntegration(AWSObject):
         "KmsKey": (str, True),
         "Name": (str, True),
         "ObjectConfiguration": (dict, False),
-        "ScheduleConfig": (ScheduleConfig, True),
+        "ScheduleConfig": (ScheduleConfig, False),
         "SourceURI": (str, True),
         "Tags": (Tags, False),
     }
diff --git a/troposphere/autoscaling.py b/troposphere/autoscaling.py
index dca18ee9..af395111 100644
--- a/troposphere/autoscaling.py
+++ b/troposphere/autoscaling.py
@@ -64,7 +64,7 @@ class LifecycleHookSpecification(AWSProperty):
 
 class MetricsCollection(AWSProperty):
     """
-    `MetricsCollection <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-as-metricscollection.html>`__
+    `MetricsCollection <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-autoscaling-autoscalinggroup-metricscollection.html>`__
     """
 
     props: PropsDictType = {
@@ -75,7 +75,7 @@ class MetricsCollection(AWSProperty):
 
 class InstancesDistribution(AWSProperty):
     """
-    `InstancesDistribution <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-as-mixedinstancespolicy-instancesdistribution.html>`__
+    `InstancesDistribution <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-autoscaling-autoscalinggroup-instancesdistribution.html>`__
     """
 
     props: PropsDictType = {
@@ -127,8 +127,8 @@ class MemoryGiBPerVCpuRequest(AWSProperty):
     """
 
     props: PropsDictType = {
-        "Max": (integer, False),
-        "Min": (integer, False),
+        "Max": (double, False),
+        "Min": (double, False),
     }
 
 
@@ -171,8 +171,8 @@ class TotalLocalStorageGBRequest(AWSProperty):
     """
 
     props: PropsDictType = {
-        "Max": (integer, False),
-        "Min": (integer, False),
+        "Max": (double, False),
+        "Min": (double, False),
     }
 
 
@@ -189,7 +189,7 @@ class VCpuCountRequest(AWSProperty):
 
 class InstanceRequirements(AWSProperty):
     """
-    `InstanceRequirements <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-as-mixedinstancespolicy-instancerequirements.html>`__
+    `InstanceRequirements <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-autoscaling-autoscalinggroup-instancerequirements.html>`__
     """
 
     props: PropsDictType = {
@@ -221,7 +221,7 @@ class InstanceRequirements(AWSProperty):
 
 class LaunchTemplateOverrides(AWSProperty):
     """
-    `LaunchTemplateOverrides <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-as-mixedinstancespolicy-launchtemplateoverrides.html>`__
+    `LaunchTemplateOverrides <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-autoscaling-autoscalinggroup-launchtemplateoverrides.html>`__
     """
 
     props: PropsDictType = {
@@ -234,7 +234,7 @@ class LaunchTemplateOverrides(AWSProperty):
 
 class LaunchTemplate(AWSProperty):
     """
-    `LaunchTemplate <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-as-mixedinstancespolicy-launchtemplate.html>`__
+    `LaunchTemplate <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-autoscaling-autoscalinggroup-launchtemplate.html>`__
     """
 
     props: PropsDictType = {
@@ -245,7 +245,7 @@ class LaunchTemplate(AWSProperty):
 
 class MixedInstancesPolicy(AWSProperty):
     """
-    `MixedInstancesPolicy <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-as-group-mixedinstancespolicy.html>`__
+    `MixedInstancesPolicy <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-autoscaling-autoscalinggroup-mixedinstancespolicy.html>`__
     """
 
     props: PropsDictType = {
@@ -256,18 +256,18 @@ class MixedInstancesPolicy(AWSProperty):
 
 class NotificationConfigurations(AWSProperty):
     """
-    `NotificationConfigurations <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-as-notificationconfigurations.html>`__
+    `NotificationConfigurations <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-autoscaling-autoscalinggroup-notificationconfiguration.html>`__
     """
 
     props: PropsDictType = {
         "NotificationTypes": ([str], False),
-        "TopicARN": (str, True),
+        "TopicARN": ([str], True),
     }
 
 
 class AutoScalingGroup(AWSObject):
     """
-    `AutoScalingGroup <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-as-group.html>`__
+    `AutoScalingGroup <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-autoscaling-autoscalinggroup.html>`__
     """
 
     resource_type = "AWS::AutoScaling::AutoScalingGroup"
diff --git a/troposphere/awslambda.py b/troposphere/awslambda.py
index eb8b3eac..7ce054e8 100644
--- a/troposphere/awslambda.py
+++ b/troposphere/awslambda.py
@@ -417,6 +417,7 @@ class Function(AWSObject):
         "Layers": ([str], False),
         "MemorySize": (validate_memory_size, False),
         "PackageType": (validate_package_type, False),
+        "Policy": (dict, False),
         "ReservedConcurrentExecutions": (integer, False),
         "Role": (str, True),
         "Runtime": (str, False),
@@ -523,6 +524,17 @@ class Url(AWSObject):
     }
 
 
+class RuntimePolicy(AWSProperty):
+    """
+    `RuntimePolicy <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-version-runtimepolicy.html>`__
+    """
+
+    props: PropsDictType = {
+        "RuntimeVersionArn": (str, False),
+        "UpdateRuntimeOn": (str, True),
+    }
+
+
 class Version(AWSObject):
     """
     `Version <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html>`__
@@ -535,6 +547,7 @@ class Version(AWSObject):
         "Description": (str, False),
         "FunctionName": (str, True),
         "ProvisionedConcurrencyConfig": (ProvisionedConcurrencyConfiguration, False),
+        "RuntimePolicy": (RuntimePolicy, False),
     }
 
 
diff --git a/troposphere/cognito.py b/troposphere/cognito.py
index 9cd4119c..2f1fdbef 100644
--- a/troposphere/cognito.py
+++ b/troposphere/cognito.py
@@ -7,7 +7,7 @@
 
 
 from . import AWSObject, AWSProperty, PropsDictType
-from .validators import boolean, double, integer
+from .validators import boolean, integer
 from .validators.cognito import validate_recoveryoption_name
 
 
@@ -97,6 +97,41 @@ class IdentityPoolRoleAttachment(AWSObject):
     }
 
 
+class CloudWatchLogsConfiguration(AWSProperty):
+    """
+    `CloudWatchLogsConfiguration <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-logdeliveryconfiguration-cloudwatchlogsconfiguration.html>`__
+    """
+
+    props: PropsDictType = {
+        "LogGroupArn": (str, False),
+    }
+
+
+class LogConfiguration(AWSProperty):
+    """
+    `LogConfiguration <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-logdeliveryconfiguration-logconfiguration.html>`__
+    """
+
+    props: PropsDictType = {
+        "CloudWatchLogsConfiguration": (CloudWatchLogsConfiguration, False),
+        "EventSource": (str, False),
+        "LogLevel": (str, False),
+    }
+
+
+class LogDeliveryConfiguration(AWSObject):
+    """
+    `LogDeliveryConfiguration <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-logdeliveryconfiguration.html>`__
+    """
+
+    resource_type = "AWS::Cognito::LogDeliveryConfiguration"
+
+    props: PropsDictType = {
+        "LogConfigurations": ([LogConfiguration], False),
+        "UserPoolId": (str, True),
+    }
+
+
 class RecoveryOption(AWSProperty):
     """
     `RecoveryOption <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpool-recoveryoption.html>`__
@@ -459,7 +494,7 @@ class UserPoolGroup(AWSObject):
     props: PropsDictType = {
         "Description": (str, False),
         "GroupName": (str, False),
-        "Precedence": (double, False),
+        "Precedence": (integer, False),
         "RoleArn": (str, False),
         "UserPoolId": (str, True),
     }
diff --git a/troposphere/connect.py b/troposphere/connect.py
index 162615a1..324c6713 100644
--- a/troposphere/connect.py
+++ b/troposphere/connect.py
@@ -395,6 +395,7 @@ class RoutingProfile(AWSObject):
     resource_type = "AWS::Connect::RoutingProfile"
 
     props: PropsDictType = {
+        "AgentAvailabilityTimer": (str, False),
         "DefaultOutboundQueueArn": (str, True),
         "Description": (str, True),
         "InstanceArn": (str, True),
@@ -708,6 +709,7 @@ class UserHierarchyGroup(AWSObject):
         "InstanceArn": (str, True),
         "Name": (str, True),
         "ParentGroupArn": (str, False),
+        "Tags": (Tags, False),
     }
 
 
diff --git a/troposphere/ec2.py b/troposphere/ec2.py
index c257a78c..7071ae5c 100644
--- a/troposphere/ec2.py
+++ b/troposphere/ec2.py
@@ -2876,6 +2876,17 @@ class NetworkInterfaceOptions(AWSProperty):
     }
 
 
+class SseSpecification(AWSProperty):
+    """
+    `SseSpecification <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-verifiedaccesstrustprovider-ssespecification.html>`__
+    """
+
+    props: PropsDictType = {
+        "CustomerManagedKeyEnabled": (boolean, False),
+        "KmsKeyArn": (str, False),
+    }
+
+
 class VerifiedAccessEndpoint(AWSObject):
     """
     `VerifiedAccessEndpoint <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-verifiedaccessendpoint.html>`__
@@ -2895,6 +2906,7 @@ class VerifiedAccessEndpoint(AWSObject):
         "PolicyDocument": (str, False),
         "PolicyEnabled": (boolean, False),
         "SecurityGroupIds": ([str], False),
+        "SseSpecification": (SseSpecification, False),
         "Tags": (Tags, False),
         "VerifiedAccessGroupId": (str, True),
     }
@@ -2911,6 +2923,7 @@ class VerifiedAccessGroup(AWSObject):
         "Description": (str, False),
         "PolicyDocument": (str, False),
         "PolicyEnabled": (boolean, False),
+        "SseSpecification": (SseSpecification, False),
         "Tags": (Tags, False),
         "VerifiedAccessInstanceId": (str, True),
     }
@@ -2988,6 +3001,7 @@ class VerifiedAccessInstance(AWSObject):
 
     props: PropsDictType = {
         "Description": (str, False),
+        "FipsEnabled": (boolean, False),
         "LoggingConfigurations": (VerifiedAccessLogs, False),
         "Tags": (Tags, False),
         "VerifiedAccessTrustProviderIds": ([str], False),
@@ -3034,6 +3048,7 @@ class VerifiedAccessTrustProvider(AWSObject):
         "DeviceTrustProviderType": (str, False),
         "OidcOptions": (OidcOptions, False),
         "PolicyReferenceName": (str, True),
+        "SseSpecification": (SseSpecification, False),
         "Tags": (Tags, False),
         "TrustProviderType": (str, True),
         "UserTrustProviderType": (str, False),
diff --git a/troposphere/emr.py b/troposphere/emr.py
index cf00898e..1a6bf9a9 100644
--- a/troposphere/emr.py
+++ b/troposphere/emr.py
@@ -374,7 +374,7 @@ class ManagedScalingPolicy(AWSProperty):
 
 class HadoopJarStepConfig(AWSProperty):
     """
-    `HadoopJarStepConfig <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticmapreduce-step-hadoopjarstepconfig.html>`__
+    `HadoopJarStepConfig <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-emr-step-hadoopjarstepconfig.html>`__
     """
 
     props: PropsDictType = {
diff --git a/troposphere/events.py b/troposphere/events.py
index 1e8bdf22..2fb8dc61 100644
--- a/troposphere/events.py
+++ b/troposphere/events.py
@@ -229,6 +229,7 @@ class EventBus(AWSObject):
     props: PropsDictType = {
         "EventSourceName": (str, False),
         "Name": (str, True),
+        "Policy": (dict, False),
         "Tags": (Tags, False),
     }
 
@@ -427,6 +428,7 @@ class RedshiftDataParameters(AWSProperty):
         "DbUser": (str, False),
         "SecretManagerArn": (str, False),
         "Sql": (str, False),
+        "Sqls": ([str], False),
         "StatementName": (str, False),
         "WithEvent": (boolean, False),
     }
@@ -538,14 +540,3 @@ class Rule(AWSObject):
         "Tags": (Tags, False),
         "Targets": ([Target], False),
     }
-
-
-class TagEntry(AWSProperty):
-    """
-    `TagEntry <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-events-eventbus-tagentry.html>`__
-    """
-
-    props: PropsDictType = {
-        "Key": (str, True),
-        "Value": (str, True),
-    }
diff --git a/troposphere/gamelift.py b/troposphere/gamelift.py
index 12e5dde8..9fc6ee71 100644
--- a/troposphere/gamelift.py
+++ b/troposphere/gamelift.py
@@ -174,6 +174,7 @@ class Fleet(AWSObject):
         "EC2InstanceType": (str, False),
         "FleetType": (str, False),
         "InstanceRoleARN": (str, False),
+        "InstanceRoleCredentialsProvider": (str, False),
         "Locations": ([LocationConfiguration], False),
         "MaxSize": (integer, False),
         "MetricGroups": ([str], False),
diff --git a/troposphere/iam.py b/troposphere/iam.py
index 4c874f50..07bf9fa5 100644
--- a/troposphere/iam.py
+++ b/troposphere/iam.py
@@ -48,7 +48,7 @@ class Policy(AWSProperty):
 
 class Group(AWSObject):
     """
-    `Group <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-group.html>`__
+    `Group <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-group.html>`__
     """
 
     resource_type = "AWS::IAM::Group"
diff --git a/troposphere/iot.py b/troposphere/iot.py
index 4074ef31..dd4aa35c 100644
--- a/troposphere/iot.py
+++ b/troposphere/iot.py
@@ -541,6 +541,7 @@ class Policy(AWSObject):
     props: PropsDictType = {
         "PolicyDocument": (policytypes, True),
         "PolicyName": (str, False),
+        "Tags": (Tags, False),
     }
 
 
@@ -1106,6 +1107,17 @@ class IotSiteWiseAction(AWSProperty):
     }
 
 
+class KafkaActionHeader(AWSProperty):
+    """
+    `KafkaActionHeader <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-topicrule-kafkaactionheader.html>`__
+    """
+
+    props: PropsDictType = {
+        "Key": (str, True),
+        "Value": (str, True),
+    }
+
+
 class KafkaAction(AWSProperty):
     """
     `KafkaAction <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-topicrule-kafkaaction.html>`__
@@ -1114,6 +1126,7 @@ class KafkaAction(AWSProperty):
     props: PropsDictType = {
         "ClientProperties": (dict, True),
         "DestinationArn": (str, True),
+        "Headers": ([KafkaActionHeader], False),
         "Key": (str, False),
         "Partition": (str, False),
         "Topic": (str, True),
diff --git a/troposphere/kendra.py b/troposphere/kendra.py
index 2572e3a7..c4809773 100644
--- a/troposphere/kendra.py
+++ b/troposphere/kendra.py
@@ -548,16 +548,6 @@ class SharePointConfiguration(AWSProperty):
     }
 
 
-class TemplateConfiguration(AWSProperty):
-    """
-    `TemplateConfiguration <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kendra-datasource-templateconfiguration.html>`__
-    """
-
-    props: PropsDictType = {
-        "Template": (str, True),
-    }
-
-
 class ProxyConfiguration(AWSProperty):
     """
     `ProxyConfiguration <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kendra-datasource-proxyconfiguration.html>`__
@@ -671,7 +661,6 @@ class DataSourceConfiguration(AWSProperty):
         "SalesforceConfiguration": (SalesforceConfiguration, False),
         "ServiceNowConfiguration": (ServiceNowConfiguration, False),
         "SharePointConfiguration": (SharePointConfiguration, False),
-        "TemplateConfiguration": (TemplateConfiguration, False),
         "WebCrawlerConfiguration": (WebCrawlerConfiguration, False),
         "WorkDocsConfiguration": (WorkDocsConfiguration, False),
     }
diff --git a/troposphere/quicksight.py b/troposphere/quicksight.py
index 986fef71..8ac1ca8a 100644
--- a/troposphere/quicksight.py
+++ b/troposphere/quicksight.py
@@ -429,6 +429,7 @@ class FilterListConfiguration(AWSProperty):
     props: PropsDictType = {
         "CategoryValues": ([str], False),
         "MatchOperator": (str, True),
+        "NullOption": (str, False),
         "SelectAllOptions": (str, False),
     }
 
@@ -5739,6 +5740,16 @@ class ResourcePermission(AWSProperty):
     }
 
 
+class ValidationStrategy(AWSProperty):
+    """
+    `ValidationStrategy <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-quicksight-template-validationstrategy.html>`__
+    """
+
+    props: PropsDictType = {
+        "Mode": (str, True),
+    }
+
+
 class Analysis(AWSObject):
     """
     `Analysis <http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-quicksight-analysis.html>`__
@@ -5757,6 +5768,7 @@ class Analysis(AWSObject):
         "Status": (str, False),
         "Tags": (Tags, False),
         "ThemeArn": (str, False),
+        "ValidationStrategy": (ValidationStrategy, False),
     }
 
 
@@ -5958,6 +5970,7 @@ class Dashboard(AWSObject):
         "SourceEntity": (DashboardSourceEntity, False),
         "Tags": (Tags, False),
         "ThemeArn": (str, False),
+        "ValidationStrategy": (ValidationStrategy, False),
         "VersionDescription": (str, False),
     }
 
@@ -7003,6 +7016,7 @@ class Template(AWSObject):
         "SourceEntity": (TemplateSourceEntity, False),
         "Tags": (Tags, False),
         "TemplateId": (str, True),
+        "ValidationStrategy": (ValidationStrategy, False),
         "VersionDescription": (str, False),
     }
 
@@ -7269,6 +7283,7 @@ class TopicCalculatedField(AWSProperty):
         "Expression": (str, True),
         "IsIncludedInTopic": (boolean, False),
         "NeverAggregateInFilter": (boolean, False),
+        "NonAdditive": (boolean, False),
         "NotAllowedAggregations": ([str], False),
         "SemanticType": (SemanticType, False),
         "TimeGranularity": (str, False),
@@ -7293,6 +7308,7 @@ class TopicColumn(AWSProperty):
         "DefaultFormatting": (DefaultFormatting, False),
         "IsIncludedInTopic": (boolean, False),
         "NeverAggregateInFilter": (boolean, False),
+        "NonAdditive": (boolean, False),
         "NotAllowedAggregations": ([str], False),
         "SemanticType": (SemanticType, False),
         "TimeGranularity": (str, False),
diff --git a/troposphere/sagemaker.py b/troposphere/sagemaker.py
index 88d68a04..7d49cf9f 100644
--- a/troposphere/sagemaker.py
+++ b/troposphere/sagemaker.py
@@ -200,6 +200,7 @@ class BatchTransformInput(AWSProperty):
     props: PropsDictType = {
         "DataCapturedDestinationS3Uri": (str, True),
         "DatasetFormat": (DatasetFormat, True),
+        "ExcludeFeaturesAttribute": (str, False),
         "LocalPath": (str, True),
         "S3DataDistributionType": (str, False),
         "S3InputMode": (str, False),
@@ -213,6 +214,7 @@ class EndpointInput(AWSProperty):
 
     props: PropsDictType = {
         "EndpointName": (str, True),
+        "ExcludeFeaturesAttribute": (str, False),
         "LocalPath": (str, True),
         "S3DataDistributionType": (str, False),
         "S3InputMode": (str, False),
@@ -2155,6 +2157,8 @@ class ScheduleConfig(AWSProperty):
     """
 
     props: PropsDictType = {
+        "DataAnalysisEndTime": (str, False),
+        "DataAnalysisStartTime": (str, False),
         "ScheduleExpression": (str, True),
     }
 
diff --git a/troposphere/type_defs/compat.py b/troposphere/type_defs/compat.py
index dbc27f20..efffe66f 100644
--- a/troposphere/type_defs/compat.py
+++ b/troposphere/type_defs/compat.py
@@ -2,9 +2,4 @@
 # flake8: noqa
 from __future__ import annotations
 
-import sys
-
-if sys.version_info < (3, 8):  # 3.7
-    from typing_extensions import Final, Literal, Protocol, SupportsIndex
-else:
-    from typing import Final, Literal, Protocol, SupportsIndex
+from typing import Final, Literal, Protocol, SupportsIndex
diff --git a/troposphere/wafv2.py b/troposphere/wafv2.py
index daf26066..c53156c8 100644
--- a/troposphere/wafv2.py
+++ b/troposphere/wafv2.py
@@ -646,6 +646,7 @@ class AWSManagedRulesBotControlRuleSet(AWSProperty):
     """
 
     props: PropsDictType = {
+        "EnableMachineLearning": (boolean, False),
         "InspectionLevel": (str, True),
     }
 


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index c035f835..7fe22cdc 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -15,7 +15,7 @@ jobs:
     name: Python Linting & Tests
     strategy:
       matrix:
-        python-version: ["3.8", "3.9", "3.10", "3.11"]
+        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
         os: [ubuntu-latest]
     runs-on: ${{ matrix.os }}
     steps:
diff --git a/tests/test_parameters.py b/tests/test_parameters.py
index 1c3d98cf..dc28b20f 100644
--- a/tests/test_parameters.py
+++ b/tests/test_parameters.py
@@ -17,5 +17,33 @@ class TestInitArguments(unittest.TestCase):
         self.assertDictEqual(reference.data, {"Ref": "title"})
 
 
+class TestParameterValidator(unittest.TestCase):
+    def test_allowed_pattern_for_number(self):
+        with self.assertRaises(ValueError):
+            Parameter("Foo", Type="Number", AllowedPattern="^[a-zA-Z0-9]*$").validate()
+
+    def test_allowed_pattern_for_comma_delimited_list_and_string(self):
+        Parameter(
+            "Foo",
+            Type="CommaDelimitedList",
+            AllowedPattern="^[A-Z]{2}$",
+            Default="",
+        ).validate()
+
+        Parameter(
+            "Foo",
+            Type="String",
+            AllowedPattern="^[A-Z]{2}$",
+            Default="",
+        ).validate()
+
+    def test_aws_specific_type(self):
+        Parameter(
+            "Foo",
+            Type="AWS::EC2::KeyPair::KeyName",
+            Default="",
+        ).validate()
+
+
 if __name__ == "__main__":
     unittest.main()