Repository: getmoto/moto
Pull request #7599 (2024-04-16 12:20:50)

Rekognition: add DetectCustomLabels support
Response data copied from https://docs.aws.amazon.com/rekognition/latest/customlabels-dg/detecting-custom-labels.html#text-response.


Patch:
diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index 116ce7282..ee6d58a46 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -6162,7 +6162,7 @@
 - [ ] describe_project_versions
 - [ ] describe_projects
 - [ ] describe_stream_processor
-- [ ] detect_custom_labels
+- [X] detect_custom_labels
 - [ ] detect_faces
 - [X] detect_labels
 - [ ] detect_moderation_labels
diff --git a/moto/ec2/models/amis.py b/moto/ec2/models/amis.py
index 65385a01a..4e558b6bd 100644
--- a/moto/ec2/models/amis.py
+++ b/moto/ec2/models/amis.py
@@ -145,6 +145,10 @@ class Ami(TaggedEC2Resource):
             return self.owner_id
         elif filter_name == "owner-alias":
             return self.owner_alias
+        elif filter_name == "product-code":
+            return self.product_codes
+        elif filter_name == "product-code.type":
+            return "marketplace"  # devpay is not (yet?) supported
         else:
             return super().get_filter_value(filter_name, "DescribeImages")
 
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-west-3.json b/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-west-3.json
index 5970424e4..fa699a34c 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-west-3.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-west-3.json
@@ -1695,6 +1695,50 @@
   "InstanceType": "c6in.xlarge",
   "Location": "euw3-az2"
  },
+ {
+  "InstanceType": "c7i.12xlarge",
+  "Location": "euw3-az2"
+ },
+ {
+  "InstanceType": "c7i.16xlarge",
+  "Location": "euw3-az2"
+ },
+ {
+  "InstanceType": "c7i.24xlarge",
+  "Location": "euw3-az2"
+ },
+ {
+  "InstanceType": "c7i.2xlarge",
+  "Location": "euw3-az2"
+ },
+ {
+  "InstanceType": "c7i.48xlarge",
+  "Location": "euw3-az2"
+ },
+ {
+  "InstanceType": "c7i.4xlarge",
+  "Location": "euw3-az2"
+ },
+ {
+  "InstanceType": "c7i.8xlarge",
+  "Location": "euw3-az2"
+ },
+ {
+  "InstanceType": "c7i.large",
+  "Location": "euw3-az2"
+ },
+ {
+  "InstanceType": "c7i.metal-24xl",
+  "Location": "euw3-az2"
+ },
+ {
+  "InstanceType": "c7i.metal-48xl",
+  "Location": "euw3-az2"
+ },
+ {
+  "InstanceType": "c7i.xlarge",
+  "Location": "euw3-az2"
+ },
  {
   "InstanceType": "d2.2xlarge",
   "Location": "euw3-az2"
@@ -3103,6 +3147,50 @@
   "InstanceType": "c6in.xlarge",
   "Location": "euw3-az3"
  },
+ {
+  "InstanceType": "c7i.12xlarge",
+  "Location": "euw3-az3"
+ },
+ {
+  "InstanceType": "c7i.16xlarge",
+  "Location": "euw3-az3"
+ },
+ {
+  "InstanceType": "c7i.24xlarge",
+  "Location": "euw3-az3"
+ },
+ {
+  "InstanceType": "c7i.2xlarge",
+  "Location": "euw3-az3"
+ },
+ {
+  "InstanceType": "c7i.48xlarge",
+  "Location": "euw3-az3"
+ },
+ {
+  "InstanceType": "c7i.4xlarge",
+  "Location": "euw3-az3"
+ },
+ {
+  "InstanceType": "c7i.8xlarge",
+  "Location": "euw3-az3"
+ },
+ {
+  "InstanceType": "c7i.large",
+  "Location": "euw3-az3"
+ },
+ {
+  "InstanceType": "c7i.metal-24xl",
+  "Location": "euw3-az3"
+ },
+ {
+  "InstanceType": "c7i.metal-48xl",
+  "Location": "euw3-az3"
+ },
+ {
+  "InstanceType": "c7i.xlarge",
+  "Location": "euw3-az3"
+ },
  {
   "InstanceType": "d2.2xlarge",
   "Location": "euw3-az3"
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone/eu-west-3.json b/moto/ec2/resources/instance_type_offerings/availability-zone/eu-west-3.json
index 4b37de384..7e3a1787f 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone/eu-west-3.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone/eu-west-3.json
@@ -1695,6 +1695,50 @@
   "InstanceType": "c6in.xlarge",
   "Location": "eu-west-3b"
  },
+ {
+  "InstanceType": "c7i.12xlarge",
+  "Location": "eu-west-3b"
+ },
+ {
+  "InstanceType": "c7i.16xlarge",
+  "Location": "eu-west-3b"
+ },
+ {
+  "InstanceType": "c7i.24xlarge",
+  "Location": "eu-west-3b"
+ },
+ {
+  "InstanceType": "c7i.2xlarge",
+  "Location": "eu-west-3b"
+ },
+ {
+  "InstanceType": "c7i.48xlarge",
+  "Location": "eu-west-3b"
+ },
+ {
+  "InstanceType": "c7i.4xlarge",
+  "Location": "eu-west-3b"
+ },
+ {
+  "InstanceType": "c7i.8xlarge",
+  "Location": "eu-west-3b"
+ },
+ {
+  "InstanceType": "c7i.large",
+  "Location": "eu-west-3b"
+ },
+ {
+  "InstanceType": "c7i.metal-24xl",
+  "Location": "eu-west-3b"
+ },
+ {
+  "InstanceType": "c7i.metal-48xl",
+  "Location": "eu-west-3b"
+ },
+ {
+  "InstanceType": "c7i.xlarge",
+  "Location": "eu-west-3b"
+ },
  {
   "InstanceType": "d2.2xlarge",
   "Location": "eu-west-3b"
@@ -3103,6 +3147,50 @@
   "InstanceType": "c6in.xlarge",
   "Location": "eu-west-3c"
  },
+ {
+  "InstanceType": "c7i.12xlarge",
+  "Location": "eu-west-3c"
+ },
+ {
+  "InstanceType": "c7i.16xlarge",
+  "Location": "eu-west-3c"
+ },
+ {
+  "InstanceType": "c7i.24xlarge",
+  "Location": "eu-west-3c"
+ },
+ {
+  "InstanceType": "c7i.2xlarge",
+  "Location": "eu-west-3c"
+ },
+ {
+  "InstanceType": "c7i.48xlarge",
+  "Location": "eu-west-3c"
+ },
+ {
+  "InstanceType": "c7i.4xlarge",
+  "Location": "eu-west-3c"
+ },
+ {
+  "InstanceType": "c7i.8xlarge",
+  "Location": "eu-west-3c"
+ },
+ {
+  "InstanceType": "c7i.large",
+  "Location": "eu-west-3c"
+ },
+ {
+  "InstanceType": "c7i.metal-24xl",
+  "Location": "eu-west-3c"
+ },
+ {
+  "InstanceType": "c7i.metal-48xl",
+  "Location": "eu-west-3c"
+ },
+ {
+  "InstanceType": "c7i.xlarge",
+  "Location": "eu-west-3c"
+ },
  {
   "InstanceType": "d2.2xlarge",
   "Location": "eu-west-3c"
diff --git a/moto/ec2/resources/instance_type_offerings/region/eu-west-3.json b/moto/ec2/resources/instance_type_offerings/region/eu-west-3.json
index 743117d03..7da76fd33 100644
--- a/moto/ec2/resources/instance_type_offerings/region/eu-west-3.json
+++ b/moto/ec2/resources/instance_type_offerings/region/eu-west-3.json
@@ -303,6 +303,50 @@
   "InstanceType": "c6in.xlarge",
   "Location": "eu-west-3"
  },
+ {
+  "InstanceType": "c7i.12xlarge",
+  "Location": "eu-west-3"
+ },
+ {
+  "InstanceType": "c7i.16xlarge",
+  "Location": "eu-west-3"
+ },
+ {
+  "InstanceType": "c7i.24xlarge",
+  "Location": "eu-west-3"
+ },
+ {
+  "InstanceType": "c7i.2xlarge",
+  "Location": "eu-west-3"
+ },
+ {
+  "InstanceType": "c7i.48xlarge",
+  "Location": "eu-west-3"
+ },
+ {
+  "InstanceType": "c7i.4xlarge",
+  "Location": "eu-west-3"
+ },
+ {
+  "InstanceType": "c7i.8xlarge",
+  "Location": "eu-west-3"
+ },
+ {
+  "InstanceType": "c7i.large",
+  "Location": "eu-west-3"
+ },
+ {
+  "InstanceType": "c7i.metal-24xl",
+  "Location": "eu-west-3"
+ },
+ {
+  "InstanceType": "c7i.metal-48xl",
+  "Location": "eu-west-3"
+ },
+ {
+  "InstanceType": "c7i.xlarge",
+  "Location": "eu-west-3"
+ },
  {
   "InstanceType": "d2.2xlarge",
   "Location": "eu-west-3"
diff --git a/moto/ec2/responses/amis.py b/moto/ec2/responses/amis.py
index 7238c32e5..9b9d06877 100644
--- a/moto/ec2/responses/amis.py
+++ b/moto/ec2/responses/amis.py
@@ -61,7 +61,7 @@ class AmisResponse(EC2BaseResponse):
 
         # only valid attributes as per
         # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2/client/describe_image_attribute.html
-        valid_atrributes_list = {
+        valid_attributes_list = {
             "description": "description",
             "kernel": "kernel_id",
             "ramdisk": "ramdisk",
@@ -75,7 +75,7 @@ class AmisResponse(EC2BaseResponse):
             "lastLaunchedTime": "lld",
             "imdsSupport": "imds",
         }
-        if attribute_name not in valid_atrributes_list:
+        if attribute_name not in valid_attributes_list:
             raise InvalidRequest
         elif attribute_name == "blockDeviceMapping":
             # replicate real aws behaviour and throw and error
@@ -86,11 +86,11 @@ class AmisResponse(EC2BaseResponse):
         launch_permissions = None
         if attribute_name == "launchPermission":
             launch_permissions = self.ec2_backend.describe_image_attribute(
-                ami_id, valid_atrributes_list[attribute_name]
+                ami_id, valid_attributes_list[attribute_name]
             )
         else:
             attribute_value = self.ec2_backend.describe_image_attribute(
-                ami_id, valid_atrributes_list[attribute_name]
+                ami_id, valid_attributes_list[attribute_name]
             )
 
         template = self.response_template(DESCRIBE_IMAGE_ATTRIBUTES_RESPONSE)
diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py
index 2e0f47da2..a162df8d2 100644
--- a/moto/ec2/utils.py
+++ b/moto/ec2/utils.py
@@ -431,6 +431,7 @@ filter_dict_attribute_mapping = {
     "subnet-id": "subnet_id",
     "dns-name": "public_dns",
     "key-name": "key_name",
+    "product-code": "product_codes",
 }
 
 
diff --git a/moto/ecs/models.py b/moto/ecs/models.py
index 72acea49f..adfc7b5ec 100644
--- a/moto/ecs/models.py
+++ b/moto/ecs/models.py
@@ -703,7 +703,12 @@ class Service(BaseObject, CloudFormationModel):
             )
         else:
             return ecs_backend.update_service(
-                cluster_name, service_name, task_definition, desired_count
+                {
+                    "cluster": cluster_name,
+                    "service": service_name,
+                    "task_definition": task_definition,
+                    "desired_count": desired_count,
+                }
             )
 
     @classmethod
@@ -1677,26 +1682,22 @@ class EC2ContainerServiceBackend(BaseBackend):
 
         return result, failures
 
-    def update_service(
-        self,
-        cluster_str: str,
-        service_str: str,
-        task_definition_str: str,
-        desired_count: Optional[int],
-    ) -> Service:
+    def update_service(self, service_properties: Dict[str, Any]) -> Service:
+        cluster_str = service_properties.pop("cluster", "default")
+        task_definition_str = service_properties.pop("task_definition", None)
         cluster = self._get_cluster(cluster_str)
-
-        service_name = service_str.split("/")[-1]
+        service_name = service_properties.pop("service").split("/")[-1]
         cluster_service_pair = f"{cluster.name}:{service_name}"
+
         if cluster_service_pair in self.services:
-            if task_definition_str is not None:
+            current_service = self.services[cluster_service_pair]
+            for prop_name, prop_val in service_properties.items():
+                if prop_val is not None:
+                    current_service.__setattr__(prop_name, prop_val)
+            if task_definition_str:
                 self.describe_task_definition(task_definition_str)
-                self.services[
-                    cluster_service_pair
-                ].task_definition = task_definition_str
-            if desired_count is not None:
-                self.services[cluster_service_pair].desired_count = desired_count
-            return self.services[cluster_service_pair]
+                current_service.task_definition = task_definition_str
+            return current_service
         else:
             raise ServiceNotFoundException
 
diff --git a/moto/ecs/responses.py b/moto/ecs/responses.py
index dd40d60cf..f388ad16e 100644
--- a/moto/ecs/responses.py
+++ b/moto/ecs/responses.py
@@ -2,6 +2,7 @@ import json
 from typing import Any, Dict
 
 from moto.core.responses import BaseResponse
+from moto.core.utils import camelcase_to_underscores
 
 from .models import EC2ContainerServiceBackend, ecs_backends
 
@@ -306,13 +307,12 @@ class EC2ContainerServiceResponse(BaseResponse):
         return json.dumps(resp)
 
     def update_service(self) -> str:
-        cluster_str = self._get_param("cluster", "default")
-        service_name = self._get_param("service")
-        task_definition = self._get_param("taskDefinition")
-        desired_count = self._get_int_param("desiredCount")
-        service = self.ecs_backend.update_service(
-            cluster_str, service_name, task_definition, desired_count
-        )
+        service_properties = json.loads(self.body)
+        parsed_props = {}
+        for k, v in service_properties.items():
+            parsed_props[camelcase_to_underscores(k)] = v
+
+        service = self.ecs_backend.update_service(parsed_props)
         return json.dumps({"service": service.response_object})
 
     def delete_service(self) -> str:
diff --git a/moto/rekognition/models.py b/moto/rekognition/models.py
index d7f05d7ba..9445832ce 100644
--- a/moto/rekognition/models.py
+++ b/moto/rekognition/models.py
@@ -76,6 +76,9 @@ class RekognitionBackend(BaseBackend):
             "3.0",
         )
 
+    def detect_custom_labels(self) -> Tuple[List[Dict[str, Any]]]:
+        return (self._detect_custom_labels_detections(),)
+
     # private
 
     def _job_id(self) -> str:
@@ -755,5 +758,21 @@ class RekognitionBackend(BaseBackend):
             },
         ]
 
+    def _detect_custom_labels_detections(self) -> List[Dict[str, Any]]:
+        return [
+            {
+                "Name": "MyLogo",
+                "Confidence": 77.7729721069336,
+                "Geometry": {
+                    "BoundingBox": {
+                        "Width": 0.198987677693367,
+                        "Height": 0.31296101212501526,
+                        "Left": 0.07924537360668182,
+                        "Top": 0.4037395715713501,
+                    }
+                },
+            }
+        ]
+
 
 rekognition_backends = BackendDict(RekognitionBackend, "rekognition")
diff --git a/moto/rekognition/responses.py b/moto/rekognition/responses.py
index a18e912c7..abf56694b 100644
--- a/moto/rekognition/responses.py
+++ b/moto/rekognition/responses.py
@@ -103,6 +103,14 @@ class RekognitionResponse(BaseResponse):
             )
         )
 
+    def detect_custom_labels(self) -> str:
+        (custom_labels,) = self.rekognition_backend.detect_custom_labels()
+        return json.dumps(
+            dict(
+                CustomLabels=custom_labels,
+            )
+        )
+
     def start_face_search(self) -> TYPE_RESPONSE:
         headers = {"Content-Type": "application/x-amz-json-1.1"}
         job_id = self.rekognition_backend.start_face_search()
diff --git a/moto/ssm/resources/regions.json b/moto/ssm/resources/regions.json
index 90a07ae72..87ccd9ca8 100644
--- a/moto/ssm/resources/regions.json
+++ b/moto/ssm/resources/regions.json
@@ -1243,6 +1243,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "resource-explorer-2": {
+                "Value": "resource-explorer-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.af-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "resource-groups": {
                 "Value": "resource-groups",
                 "endpoint": {
@@ -1318,6 +1327,9 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "s3control": {
+                "Value": "s3control"
+              },
               "s3outposts": {
                 "Value": "s3outposts",
                 "endpoint": {
@@ -2894,6 +2906,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "resource-explorer-2": {
+                "Value": "resource-explorer-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.ap-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "resource-groups": {
                 "Value": "resource-groups",
                 "endpoint": {
@@ -2960,6 +2981,9 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "s3control": {
+                "Value": "s3control"
+              },
               "s3outposts": {
                 "Value": "s3outposts",
                 "endpoint": {
@@ -12690,6 +12714,24 @@
                   "Value": "HTTPS"
                 }
               },
+              "cognito-identity": {
+                "Value": "cognito-identity",
+                "endpoint": {
+                  "Value": "cognito-identity.ap-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
+              "cognito-idp": {
+                "Value": "cognito-idp",
+                "endpoint": {
+                  "Value": "cognito-idp.ap-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "compute-optimizer": {
                 "Value": "compute-optimizer",
                 "endpoint": {
@@ -13323,6 +13365,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "resource-explorer-2": {
+                "Value": "resource-explorer-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.ap-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "resource-groups": {
                 "Value": "resource-groups",
                 "endpoint": {
@@ -13380,6 +13431,9 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "s3control": {
+                "Value": "s3control"
+              },
               "sagemaker": {
                 "Value": "sagemaker",
                 "endpoint": {
@@ -13677,6 +13731,9 @@
             "local-zones": {
               "apse1-bkk1-az1": {
                 "Value": "apse1-bkk1-az1"
+              },
+              "apse1-mnl1-az1": {
+                "Value": "apse1-mnl1-az1"
               }
             },
             "longName": {
@@ -13974,15 +14031,6 @@
               "bedrock": {
                 "Value": "bedrock"
               },
-              "budgets": {
-                "Value": "budgets",
-                "endpoint": {
-                  "Value": "budgets.amazonaws.com"
-                },
-                "protocols": {
-                  "Value": "HTTPS"
-                }
-              },
               "chatbot": {
                 "Value": "chatbot"
               },
@@ -16611,6 +16659,9 @@
                   "Value": "HTTPS"
                 }
               },
+              "bedrock": {
+                "Value": "bedrock"
+              },
               "budgets": {
                 "Value": "budgets",
                 "endpoint": {
@@ -20149,6 +20200,9 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "s3control": {
+                "Value": "s3control"
+              },
               "s3outposts": {
                 "Value": "s3outposts",
                 "endpoint": {
@@ -21392,6 +21446,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "resource-explorer-2": {
+                "Value": "resource-explorer-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.ap-southeast-4.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "resource-groups": {
                 "Value": "resource-groups",
                 "endpoint": {
@@ -21449,6 +21512,9 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "s3control": {
+                "Value": "s3control"
+              },
               "sagemaker": {
                 "Value": "sagemaker",
                 "endpoint": {
@@ -24540,6 +24606,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "kafka": {
+                "Value": "kafka",
+                "endpoint": {
+                  "Value": "kafka.ca-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "kinesis": {
                 "Value": "kinesis",
                 "endpoint": {
@@ -24690,6 +24765,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "resource-explorer-2": {
+                "Value": "resource-explorer-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.ca-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "resource-groups": {
                 "Value": "resource-groups",
                 "endpoint": {
@@ -24918,6 +25002,15 @@
               "trustedadvisor": {
                 "Value": "trustedadvisor"
               },
+              "verifiedpermissions": {
+                "Value": "verifiedpermissions",
+                "endpoint": {
+                  "Value": "verifiedpermissions.ca-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "vpc": {
                 "Value": "vpc"
               },
@@ -29048,15 +29141,6 @@
                   "Value": "HTTPS"
                 }
               },
-              "iot-roborunner": {
-                "Value": "iot-roborunner",
-                "endpoint": {
-                  "Value": "iotroborunner.eu-central-1.amazonaws.com"
-                },
-                "protocols": {
-                  "Value": "HTTPS"
-                }
-              },
               "iot1click-projects": {
                 "Value": "iot1click-projects",
                 "endpoint": {
@@ -31457,6 +31541,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "resource-explorer-2": {
+                "Value": "resource-explorer-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.eu-central-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "resource-groups": {
                 "Value": "resource-groups",
                 "endpoint": {
@@ -31511,6 +31604,9 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "s3control": {
+                "Value": "s3control"
+              },
               "sagemaker": {
                 "Value": "sagemaker",
                 "endpoint": {
@@ -35026,6 +35122,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "resource-explorer-2": {
+                "Value": "resource-explorer-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.eu-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "resource-groups": {
                 "Value": "resource-groups",
                 "endpoint": {
@@ -35101,6 +35206,9 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "s3control": {
+                "Value": "s3control"
+              },
               "s3outposts": {
                 "Value": "s3outposts",
                 "endpoint": {
@@ -35702,6 +35810,24 @@
                   "Value": "HTTPS"
                 }
               },
+              "cognito-identity": {
+                "Value": "cognito-identity",
+                "endpoint": {
+                  "Value": "cognito-identity.eu-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
+              "cognito-idp": {
+                "Value": "cognito-idp",
+                "endpoint": {
+                  "Value": "cognito-idp.eu-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "compute-optimizer": {
                 "Value": "compute-optimizer",
                 "endpoint": {
@@ -36329,6 +36455,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "resource-explorer-2": {
+                "Value": "resource-explorer-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.eu-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "resource-groups": {
                 "Value": "resource-groups",
                 "endpoint": {
@@ -36386,6 +36521,9 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "s3control": {
+                "Value": "s3control"
+              },
               "sagemaker": {
                 "Value": "sagemaker",
                 "endpoint": {
@@ -44969,6 +45107,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "resource-explorer-2": {
+                "Value": "resource-explorer-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.il-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "resource-groups": {
                 "Value": "resource-groups",
                 "endpoint": {
@@ -45023,6 +45170,9 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "s3control": {
+                "Value": "s3control"
+              },
               "s3outposts": {
                 "Value": "s3outposts",
                 "endpoint": {
@@ -45272,6 +45422,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "verifiedpermissions": {
+                "Value": "verifiedpermissions",
+                "endpoint": {
+                  "Value": "verifiedpermissions.il-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "vpc": {
                 "Value": "vpc"
               },
@@ -46368,6 +46527,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "resource-explorer-2": {
+                "Value": "resource-explorer-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.me-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "resource-groups": {
                 "Value": "resource-groups",
                 "endpoint": {
@@ -46425,6 +46593,9 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "s3control": {
+                "Value": "s3control"
+              },
               "sagemaker": {
                 "Value": "sagemaker",
                 "endpoint": {
@@ -48028,6 +48199,9 @@
                   "Value": "HTTPS, HTTP"
                 }
               },
+              "s3control": {
+                "Value": "s3control"
+              },
               "s3outposts": {
                 "Value": "s3outposts",
                 "endpoint": {
@@ -50430,15 +50604,30 @@
               "Value": "US-VA"
             },
             "local-zones": {
+              "use1-atl2-az1": {
+                "Value": "use1-atl2-az1"
+              },
               "use1-bos1-az1": {
                 "Value": "use1-bos1-az1"
               },
+              "use1-chi2-az1": {
+                "Value": "use1-chi2-az1"
+              },
+              "use1-dfw2-az1": {
+                "Value": "use1-dfw2-az1"
+              },
+              "use1-iah2-az1": {
+                "Value": "use1-iah2-az1"
+              },
               "use1-lim1-az1": {
                 "Value": "use1-lim1-az1"
               },
               "use1-mci1-az1": {
                 "Value": "use1-mci1-az1"
               },
+              "use1-mia2-az1": {
+                "Value": "use1-mia2-az1"
+              },
               "use1-msp1-az1": {
                 "Value": "use1-msp1-az1"
               },
@@ -51917,15 +52106,6 @@
                   "Value": "HTTPS"
                 }
               },
-              "iot-roborunner": {
-                "Value": "iot-roborunner",
-                "endpoint": {
-                  "Value": "iotroborunner.us-east-1.amazonaws.com"
-                },
-                "protocols": {
-                  "Value": "HTTPS"
-                }
-              },
               "iot1click-projects": {
                 "Value": "iot1click-projects",
                 "endpoint": {
@@ -57582,6 +57762,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "verifiedpermissions": {
+                "Value": "verifiedpermissions",
+                "endpoint": {
+                  "Value": "verifiedpermissions.us-gov-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "vmwarecloudonaws": {
                 "Value": "vmwarecloudonaws"
               },
@@ -59278,6 +59467,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "verifiedpermissions": {
+                "Value": "verifiedpermissions",
+                "endpoint": {
+                  "Value": "verifiedpermissions.us-gov-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "vmwarecloudonaws": {
                 "Value": "vmwarecloudonaws"
               },
diff --git a/moto/ssm/resources/services.json b/moto/ssm/resources/services.json
index f26614d84..86b4d1dd0 100644
--- a/moto/ssm/resources/services.json
+++ b/moto/ssm/resources/services.json
@@ -7845,6 +7845,9 @@
               "ap-southeast-1": {
                 "Value": "ap-southeast-1"
               },
+              "ap-southeast-2": {
+                "Value": "ap-southeast-2"
+              },
               "eu-central-1": {
                 "Value": "eu-central-1"
               },
@@ -7962,15 +7965,6 @@
                   "Value": "HTTPS"
                 }
               },
-              "ap-southeast-1": {
-                "Value": "ap-southeast-1",
-                "endpoint": {
-                  "Value": "budgets.amazonaws.com"
-                },
-                "protocols": {
-                  "Value": "HTTPS"
-                }
-              },
               "ap-southeast-2": {
                 "Value": "ap-southeast-2",
                 "endpoint": {
@@ -13767,6 +13761,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-south-2": {
+                "Value": "ap-south-2",
+                "endpoint": {
+                  "Value": "cognito-identity.ap-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ap-southeast-1": {
                 "Value": "ap-southeast-1",
                 "endpoint": {
@@ -13857,6 +13860,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "eu-south-2": {
+                "Value": "eu-south-2",
+                "endpoint": {
+                  "Value": "cognito-identity.eu-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "eu-west-1": {
                 "Value": "eu-west-1",
                 "endpoint": {
@@ -14018,6 +14030,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-south-2": {
+                "Value": "ap-south-2",
+                "endpoint": {
+                  "Value": "cognito-idp.ap-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ap-southeast-1": {
                 "Value": "ap-southeast-1",
                 "endpoint": {
@@ -14099,6 +14120,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "eu-south-2": {
+                "Value": "eu-south-2",
+                "endpoint": {
+                  "Value": "cognito-idp.eu-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "eu-west-1": {
                 "Value": "eu-west-1",
                 "endpoint": {
@@ -16849,6 +16879,9 @@
             "longName": {
               "Value": "Amazon DynamoDB Accelerator"
             },
+            "marketingHomeURL": {
+              "Value": "https://aws.amazon.com/dynamodb/dax/"
+            },
             "regions": {
               "ap-northeast-1": {
                 "Value": "ap-northeast-1",
@@ -32606,7 +32639,6 @@
             }
           },
           "iot-roborunner": {
-            "Value": "iot-roborunner",
             "longName": {
               "Value": "AWS IoT RoboRunner"
             },
@@ -34498,6 +34530,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ca-west-1": {
+                "Value": "ca-west-1",
+                "endpoint": {
+                  "Value": "kafka.ca-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "cn-north-1": {
                 "Value": "cn-north-1",
                 "endpoint": {
@@ -50504,6 +50545,24 @@
               "Value": "https://aws.amazon.com/resourceexplorer/"
             },
             "regions": {
+              "af-south-1": {
+                "Value": "af-south-1",
+                "endpoint": {
+                  "Value": "resource-explorer-2.af-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
+              "ap-east-1": {
+                "Value": "ap-east-1",
+                "endpoint": {
+                  "Value": "resource-explorer-2.ap-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ap-northeast-1": {
                 "Value": "ap-northeast-1",
                 "endpoint": {
@@ -50540,6 +50599,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-south-2": {
+                "Value": "ap-south-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.ap-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ap-southeast-1": {
                 "Value": "ap-southeast-1",
                 "endpoint": {
@@ -50567,6 +50635,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ap-southeast-4": {
+                "Value": "ap-southeast-4",
+                "endpoint": {
+                  "Value": "resource-explorer-2.ap-southeast-4.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "ca-central-1": {
                 "Value": "ca-central-1",
                 "endpoint": {
@@ -50576,6 +50653,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ca-west-1": {
+                "Value": "ca-west-1",
+                "endpoint": {
+                  "Value": "resource-explorer-2.ca-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "eu-central-1": {
                 "Value": "eu-central-1",
                 "endpoint": {
@@ -50585,6 +50671,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "eu-central-2": {
+                "Value": "eu-central-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.eu-central-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "eu-north-1": {
                 "Value": "eu-north-1",
                 "endpoint": {
@@ -50594,6 +50689,24 @@
                   "Value": "HTTPS"
                 }
               },
+              "eu-south-1": {
+                "Value": "eu-south-1",
+                "endpoint": {
+                  "Value": "resource-explorer-2.eu-south-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
+              "eu-south-2": {
+                "Value": "eu-south-2",
+                "endpoint": {
+                  "Value": "resource-explorer-2.eu-south-2.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "eu-west-1": {
                 "Value": "eu-west-1",
                 "endpoint": {
@@ -50621,6 +50734,24 @@
                   "Value": "HTTPS"
                 }
               },
+              "il-central-1": {
+                "Value": "il-central-1",
+                "endpoint": {
+                  "Value": "resource-explorer-2.il-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
+              "me-central-1": {
+                "Value": "me-central-1",
+                "endpoint": {
+                  "Value": "resource-explorer-2.me-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "me-south-1": {
                 "Value": "me-south-1",
                 "endpoint": {
@@ -53115,6 +53246,12 @@
               "Value": "AWS S3 Control"
             },
             "regions": {
+              "af-south-1": {
+                "Value": "af-south-1"
+              },
+              "ap-east-1": {
+                "Value": "ap-east-1"
+              },
               "ap-northeast-1": {
                 "Value": "ap-northeast-1"
               },
@@ -53127,12 +53264,21 @@
               "ap-south-1": {
                 "Value": "ap-south-1"
               },
+              "ap-south-2": {
+                "Value": "ap-south-2"
+              },
               "ap-southeast-1": {
                 "Value": "ap-southeast-1"
               },
               "ap-southeast-2": {
                 "Value": "ap-southeast-2"
               },
+              "ap-southeast-3": {
+                "Value": "ap-southeast-3"
+              },
+              "ap-southeast-4": {
+                "Value": "ap-southeast-4"
+              },
               "ca-central-1": {
                 "Value": "ca-central-1"
               },
@@ -53145,9 +53291,18 @@
               "eu-central-1": {
                 "Value": "eu-central-1"
               },
+              "eu-central-2": {
+                "Value": "eu-central-2"
+              },
               "eu-north-1": {
                 "Value": "eu-north-1"
               },
+              "eu-south-1": {
+                "Value": "eu-south-1"
+              },
+              "eu-south-2": {
+                "Value": "eu-south-2"
+              },
               "eu-west-1": {
                 "Value": "eu-west-1"
               },
@@ -53157,6 +53312,15 @@
               "eu-west-3": {
                 "Value": "eu-west-3"
               },
+              "il-central-1": {
+                "Value": "il-central-1"
+              },
+              "me-central-1": {
+                "Value": "me-central-1"
+              },
+              "me-south-1": {
+                "Value": "me-south-1"
+              },
               "sa-east-1": {
                 "Value": "sa-east-1"
               },
@@ -64521,6 +64685,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "ca-west-1": {
+                "Value": "ca-west-1",
+                "endpoint": {
+                  "Value": "verifiedpermissions.ca-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "eu-central-1": {
                 "Value": "eu-central-1",
                 "endpoint": {
@@ -64593,6 +64766,15 @@
                   "Value": "HTTPS"
                 }
               },
+              "il-central-1": {
+                "Value": "il-central-1",
+                "endpoint": {
+                  "Value": "verifiedpermissions.il-central-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "me-central-1": {
                 "Value": "me-central-1",
                 "endpoint": {
@@ -64638,6 +64820,24 @@
                   "Value": "HTTPS"
                 }
               },
+              "us-gov-east-1": {
+                "Value": "us-gov-east-1",
+                "endpoint": {
+                  "Value": "verifiedpermissions.us-gov-east-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
+              "us-gov-west-1": {
+                "Value": "us-gov-west-1",
+                "endpoint": {
+                  "Value": "verifiedpermissions.us-gov-west-1.amazonaws.com"
+                },
+                "protocols": {
+                  "Value": "HTTPS"
+                }
+              },
               "us-west-1": {
                 "Value": "us-west-1",
                 "endpoint": {
diff --git a/moto/stepfunctions/models.py b/moto/stepfunctions/models.py
index ab20597db..d66100532 100644
--- a/moto/stepfunctions/models.py
+++ b/moto/stepfunctions/models.py
@@ -638,7 +638,7 @@ class StepFunctionBackend(BaseBackend):
         except StateMachineDoesNotExist:
             raise ResourceNotFound(resource_arn)
 
-    def send_task_failure(self, task_token: str) -> None:
+    def send_task_failure(self, task_token: str, error: Optional[str] = None) -> None:
         pass
 
     def send_task_heartbeat(self, task_token: str) -> None:
diff --git a/moto/stepfunctions/parser/asl/component/common/error_name/custom_error_name.py b/moto/stepfunctions/parser/asl/component/common/error_name/custom_error_name.py
index bb30b8f7e..252f6e5e5 100644
--- a/moto/stepfunctions/parser/asl/component/common/error_name/custom_error_name.py
+++ b/moto/stepfunctions/parser/asl/component/common/error_name/custom_error_name.py
@@ -13,7 +13,7 @@ class CustomErrorName(ErrorName):
     _ILLEGAL_PREFIX: Final[str] = "States."
 
     def __init__(self, error_name: str):
-        if error_name.startswith(CustomErrorName._ILLEGAL_PREFIX):
+        if error_name and error_name.startswith(CustomErrorName._ILLEGAL_PREFIX):
             raise ValueError(
                 f"Custom Error Names MUST NOT begin with the prefix 'States.', got '{error_name}'."
             )
diff --git a/moto/stepfunctions/parser/asl/component/state/exec/state_task/service/state_task_service_callback.py b/moto/stepfunctions/parser/asl/component/state/exec/state_task/service/state_task_service_callback.py
index f1eb7af9b..7772bd581 100644
--- a/moto/stepfunctions/parser/asl/component/state/exec/state_task/service/state_task_service_callback.py
+++ b/moto/stepfunctions/parser/asl/component/state/exec/state_task/service/state_task_service_callback.py
@@ -1,5 +1,6 @@
 import abc
 import json
+import time
 
 from moto.stepfunctions.parser.api import (
     HistoryEventExecutionDataDetails,
@@ -49,7 +50,20 @@ class StateTaskServiceCallback(StateTaskService, abc.ABC):
         # Discard the state evaluation output.
         env.stack.pop()
 
-        callback_id = env.context_object_manager.context_object["Task"]["Token"]
+        callback_id = env.context_object_manager.context_object.get("Task", {}).get(
+            "Token"
+        )
+        if callback_id is None:
+            # We never generated a TaskToken, so there's no point in waiting until we receive it
+            # AWS seems to just wait forever  in this scenario- so let's do the same
+            timeout_seconds = self.timeout.timeout_seconds
+            mustend_at = time.time() + timeout_seconds
+            while time.time() < mustend_at:
+                if not env.is_running():
+                    return
+                time.sleep(1)
+            return
+
         callback_endpoint = env.callback_pool_manager.get(callback_id)
 
         # With Timeouts-only definition:
diff --git a/moto/stepfunctions/parser/models.py b/moto/stepfunctions/parser/models.py
index 48a95b68b..8e8bb03f3 100644
--- a/moto/stepfunctions/parser/models.py
+++ b/moto/stepfunctions/parser/models.py
@@ -106,7 +106,7 @@ class StepFunctionsParserBackend(StepFunctionBackend):
         raise InvalidToken()
 
     def send_task_success(
-        self, task_token: TaskToken, outcome: SensitiveData
+        self, task_token: TaskToken, outcome: str
     ) -> SendTaskSuccessOutput:
         outcome = CallbackOutcomeSuccess(callback_id=task_token, output=outcome)
         running_executions = self._get_executions(ExecutionStatus.RUNNING)
diff --git a/moto/stepfunctions/responses.py b/moto/stepfunctions/responses.py
index 4ad3aa1be..70633c164 100644
--- a/moto/stepfunctions/responses.py
+++ b/moto/stepfunctions/responses.py
@@ -192,7 +192,8 @@ class StepFunctionResponse(BaseResponse):
 
     def send_task_failure(self) -> TYPE_RESPONSE:
         task_token = self._get_param("taskToken")
-        self.stepfunction_backend.send_task_failure(task_token)
+        error = self._get_param("error")
+        self.stepfunction_backend.send_task_failure(task_token, error=error)
         return 200, {}, "{}"
 
     def send_task_heartbeat(self) -> TYPE_RESPONSE:
@@ -202,8 +203,8 @@ class StepFunctionResponse(BaseResponse):
 
     def send_task_success(self) -> TYPE_RESPONSE:
         task_token = self._get_param("taskToken")
-        outcome = self._get_param("outcome")
-        self.stepfunction_backend.send_task_success(task_token, outcome)
+        output = self._get_param("output")
+        self.stepfunction_backend.send_task_success(task_token, output)
         return 200, {}, "{}"
 
     def list_map_runs(self) -> TYPE_RESPONSE:


Test patch:
diff --git a/tests/test_ec2/test_amis.py b/tests/test_ec2/test_amis.py
index 87905a3ce..885ab4c2e 100644
--- a/tests/test_ec2/test_amis.py
+++ b/tests/test_ec2/test_amis.py
@@ -483,6 +483,14 @@ def test_ami_filters():
     )["Images"]
     assert imageA.id in [ami["ImageId"] for ami in amis_by_nonpublic]
 
+    amis_by_product_code = ec2.describe_images(
+        Filters=[
+            {"Name": "product-code", "Values": ["code123"]},
+            {"Name": "product-code.type", "Values": ["marketplace"]},
+        ]
+    )["Images"]
+    assert "ami-0b301ce3ce3475r4f" in [ami["ImageId"] for ami in amis_by_product_code]
+
 
 @mock_aws
 def test_ami_filtering_via_tag():
diff --git a/tests/test_ecs/test_ecs_boto3.py b/tests/test_ecs/test_ecs_boto3.py
index 29f3848d1..dd9295645 100644
--- a/tests/test_ecs/test_ecs_boto3.py
+++ b/tests/test_ecs/test_ecs_boto3.py
@@ -3650,6 +3650,84 @@ def test_list_tasks_with_filters():
     assert len(resp["taskArns"]) == 1
 
 
+@pytest.mark.parametrize(
+    "update_params",
+    [
+        {
+            "loadBalancers": [
+                {
+                    "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/mock-service-http",
+                    "loadBalancerName": "mock-service1",
+                    "containerName": "mock-container1",
+                    "containerPort": 80,
+                }
+            ]
+        },
+        {
+            "serviceRegistries": [
+                {
+                    "registryArn": "string",
+                    "port": 123,
+                    "containerName": "string",
+                    "containerPort": 123,
+                },
+                {
+                    "registryArn": "string2",
+                    "port": 1234,
+                    "containerName": "string2",
+                    "containerPort": 1234,
+                },
+            ],
+        },
+        {
+            "placementStrategy": [
+                {"type": "random", "field": "string"},
+            ],
+        },
+        {
+            "deploymentConfiguration": {
+                "deploymentCircuitBreaker": {"enable": True, "rollback": True},
+                "maximumPercent": 10,
+                "minimumHealthyPercent": 1,
+                "alarms": {
+                    "alarmNames": [
+                        "string",
+                    ],
+                    "enable": True,
+                    "rollback": True,
+                },
+            },
+        },
+    ],
+)
+@mock_aws
+def test_remove_tg(update_params):
+    # Setup
+    ecs_client = boto3.client("ecs", region_name=ECS_REGION)
+    cluster = "mock-cluster"
+    service = "mock-service"
+    ecs_client.create_cluster(clusterName=cluster)
+    ecs_client.create_service(
+        cluster=cluster,
+        serviceName=service,
+    )
+
+    # Execute
+    ecs_client.update_service(
+        cluster="arn:aws:ecs:us-east-1:123456789012:cluster/" + cluster,
+        service=service,
+        **update_params,
+    )
+    response = ecs_client.describe_services(cluster=cluster, services=[service])
+    response_svc = response["services"][0]
+    property_key = next(iter(update_params))
+    property_val = next(iter(update_params.values()))
+
+    # Verify
+    assert len(response_svc[property_key]) == len(property_val)
+    assert response_svc[property_key] == update_params[property_key]
+
+
 def setup_ecs(client, ec2):
     """test helper"""
     vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
diff --git a/tests/test_rekognition/test_rekognition.py b/tests/test_rekognition/test_rekognition.py
index 94239fcd5..6df5cc999 100644
--- a/tests/test_rekognition/test_rekognition.py
+++ b/tests/test_rekognition/test_rekognition.py
@@ -6,6 +6,7 @@ import string
 import boto3
 
 from moto import mock_aws
+from moto.moto_api._internal import mock_random
 
 # See our Development Tips on writing tests for hints on how to write good tests:
 # http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
@@ -116,3 +117,16 @@ def test_get_text_detection():
 
     assert resp["JobStatus"] == "SUCCEEDED"
     assert resp["TextDetections"][0]["TextDetection"]["DetectedText"] == "Hello world"
+
+
+@mock_aws
+def test_detect_custom_labels():
+    client = boto3.client("rekognition", region_name="us-east-2")
+    resp = client.detect_custom_labels(
+        Image={"S3Object": {"Bucket": "string", "Name": "name.jpg"}},
+        MaxResults=10,
+        MinConfidence=80,
+        ProjectVersionArn=mock_random.get_random_string(),
+    )
+    assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+    assert resp["CustomLabels"][0]["Name"] == "MyLogo"
diff --git a/tests/test_stepfunctions/parser/__init__.py b/tests/test_stepfunctions/parser/__init__.py
index 34a513126..11d3e17d9 100644
--- a/tests/test_stepfunctions/parser/__init__.py
+++ b/tests/test_stepfunctions/parser/__init__.py
@@ -76,7 +76,7 @@ def verify_execution_result(
     )
     for _ in range(10):
         execution = client.describe_execution(executionArn=execution_arn)
-        if execution["status"] == expected_status:
+        if expected_status is None or execution["status"] == expected_status:
             result = _verify_result(client, execution, execution_arn)
             if result is not False:
                 client.delete_state_machine(stateMachineArn=state_machine_arn)
diff --git a/tests/test_stepfunctions/parser/templates/services/dynamodb_invalid_task_token.json b/tests/test_stepfunctions/parser/templates/services/dynamodb_invalid_task_token.json
new file mode 100644
index 000000000..a9e05c7d2
--- /dev/null
+++ b/tests/test_stepfunctions/parser/templates/services/dynamodb_invalid_task_token.json
@@ -0,0 +1,18 @@
+{
+"StartAt": "WaitForTransferFinished",
+"States": {
+    "WaitForTransferFinished": {
+        "Type": "Task",
+        "Resource": "arn:aws:states:::aws-sdk:dynamodb:updateItem.waitForTaskToken",
+        "Parameters": {
+            "TableName.$": "$.TableName",
+            "Key": {"id": {"S": "1"}},
+            "UpdateExpression": "SET StepFunctionTaskToken = :taskToken",
+            "ExpressionAttributeValues": {
+                ":taskToken": {"S": "$$.Task.Token"}
+            }
+        },
+        "End": true
+    }
+}
+}
\ No newline at end of file
diff --git a/tests/test_stepfunctions/parser/templates/services/dynamodb_task_token.json b/tests/test_stepfunctions/parser/templates/services/dynamodb_task_token.json
new file mode 100644
index 000000000..6e660d078
--- /dev/null
+++ b/tests/test_stepfunctions/parser/templates/services/dynamodb_task_token.json
@@ -0,0 +1,18 @@
+{
+"StartAt": "WaitForTransferFinished",
+"States": {
+    "WaitForTransferFinished": {
+        "Type": "Task",
+        "Resource": "arn:aws:states:::aws-sdk:dynamodb:updateItem.waitForTaskToken",
+        "Parameters": {
+            "TableName.$": "$.TableName",
+            "Key": {"id": {"S": "1"}},
+            "UpdateExpression": "SET StepFunctionTaskToken = :taskToken",
+            "ExpressionAttributeValues": {
+                ":taskToken": {"S.$": "$$.Task.Token"}
+            }
+        },
+        "End": true
+    }
+}
+}
\ No newline at end of file
diff --git a/tests/test_stepfunctions/parser/test_stepfunctions_dynamodb_integration.py b/tests/test_stepfunctions/parser/test_stepfunctions_dynamodb_integration.py
index eceb0dc28..1fbd708d0 100644
--- a/tests/test_stepfunctions/parser/test_stepfunctions_dynamodb_integration.py
+++ b/tests/test_stepfunctions/parser/test_stepfunctions_dynamodb_integration.py
@@ -100,6 +100,118 @@ def test_state_machine_calling_dynamodb_put(table_name=None, sleep_time=0):
     )
 
 
+@aws_verified
+@pytest.mark.aws_verified
+def test_state_machine_calling_dynamodb_put_wait_for_invalid_task_token(
+    table_name=None, sleep_time=0
+):
+    dynamodb = boto3.client("dynamodb", "us-east-1")
+    exec_input = {
+        "TableName": table_name,
+        "Item": {"data": {"S": "HelloWorld"}, "id": {"S": "id1"}},
+    }
+
+    tmpl_name = "services/dynamodb_invalid_task_token"
+
+    def _verify_result(client, execution, execution_arn):
+        if execution["status"] == "RUNNING":
+            items = dynamodb.scan(TableName=table_name)["Items"]
+            if len(items) > 0:
+                assert len(items) == 1
+                assert items[0]["id"] == {"S": "1"}
+                assert items[0]["StepFunctionTaskToken"] == {"S": "$$.Task.Token"}
+                # Because the TaskToken is not returned, we can't signal to SFN to finish the execution
+                # So let's just stop the execution manually
+                client.stop_execution(
+                    executionArn=execution_arn,
+                    error="Bad Example - no TaskToken available to continue this execution",
+                )
+                return True
+
+        return False
+
+    verify_execution_result(
+        _verify_result,
+        expected_status=None,
+        tmpl_name=tmpl_name,
+        exec_input=json.dumps(exec_input),
+        sleep_time=sleep_time,
+    )
+
+
+@aws_verified
+@pytest.mark.aws_verified
+def test_state_machine_calling_dynamodb_put_wait_for_task_token(
+    table_name=None, sleep_time=0
+):
+    dynamodb = boto3.client("dynamodb", "us-east-1")
+    exec_input = {"TableName": table_name, "Item": {"id": {"S": "id1"}}}
+    output = {"a": "b"}
+
+    tmpl_name = "services/dynamodb_task_token"
+
+    def _verify_result(client, execution, execution_arn):
+        if execution["status"] == "RUNNING":
+            items = dynamodb.scan(TableName=table_name)["Items"]
+            if len(items) > 0:
+                assert len(items) == 1
+                assert items[0]["id"] == {"S": "1"}
+                # Some random token
+                assert len(items[0]["StepFunctionTaskToken"]["S"]) > 25
+                token = items[0]["StepFunctionTaskToken"]["S"]
+                client.send_task_success(taskToken=token, output=json.dumps(output))
+
+        if execution["status"] == "SUCCEEDED":
+            assert json.loads(execution["output"]) == output
+            return True
+
+        return False
+
+    verify_execution_result(
+        _verify_result,
+        expected_status=None,
+        tmpl_name=tmpl_name,
+        exec_input=json.dumps(exec_input),
+        sleep_time=sleep_time,
+    )
+
+
+@aws_verified
+@pytest.mark.aws_verified
+def test_state_machine_calling_dynamodb_put_fail_task_token(
+    table_name=None, sleep_time=0
+):
+    dynamodb = boto3.client("dynamodb", "us-east-1")
+    exec_input = {"TableName": table_name, "Item": {"id": {"S": "id1"}}}
+
+    tmpl_name = "services/dynamodb_task_token"
+
+    def _verify_result(client, execution, execution_arn):
+        if execution["status"] == "RUNNING":
+            items = dynamodb.scan(TableName=table_name)["Items"]
+            if len(items) > 0:
+                assert len(items) == 1
+                assert items[0]["id"] == {"S": "1"}
+                # Some random token
+                assert len(items[0]["StepFunctionTaskToken"]["S"]) > 25
+                token = items[0]["StepFunctionTaskToken"]["S"]
+                client.send_task_failure(taskToken=token, error="test error")
+
+        if execution["status"] == "FAILED":
+            assert execution["error"] == "test error"
+            return True
+
+        return False
+
+    verify_execution_result(
+        _verify_result,
+        expected_status=None,
+        tmpl_name=tmpl_name,
+        exec_input=json.dumps(exec_input),
+        sleep_time=sleep_time,
+    )
+
+
 @aws_verified
 @pytest.mark.aws_verified
 def test_state_machine_calling_dynamodb_put_and_delete(table_name=None, sleep_time=0):