# Buggy code
def apply_patches():
    """Apply patches to make LocalStack seamlessly interact with the moto backend.
    TODO: Eventually, these patches should be contributed to the upstream repo!"""

    # add model mappings to moto
    parsing.MODEL_MAP.update(MODEL_MAP)

    # Patch clean_json in moto
    def clean_json(resource_json, resources_map):
        result = clean_json_orig(resource_json, resources_map)
        if isinstance(result, BaseModel):
            if isinstance(resource_json, dict) and "Ref" in resource_json:
                entity_id = get_entity_id(result, resource_json)
                if entity_id:
                    return entity_id
                LOG.warning(
                    'Unable to resolve "Ref" attribute for: %s - %s - %s',
                    resource_json,
                    result,
                    type(result),
                )
        return result

    clean_json_orig = parsing.clean_json
    parsing.clean_json = clean_json

    # Patch parse_and_create_resource method in moto to deploy resources in LocalStack
    def parse_and_create_resource(
        logical_id, resource_json, resources_map, region_name, force_create=False
    ):
        try:
            return _parse_and_create_resource(
                logical_id,
                resource_json,
                resources_map,
                region_name,
                force_create=force_create,
            )
        except Exception as e:
            LOG.error(
                'Unable to parse and create resource "%s": %s %s'
                % (logical_id, e, traceback.format_exc())
            )
            raise

    def parse_and_update_resource(
        logical_id, resource_json, resources_map, region_name
    ):
        try:
            return _parse_and_create_resource(
                logical_id, resource_json, resources_map, region_name, update=True
            )
        except Exception as e:
            LOG.error(
                'Unable to parse and update resource "%s": %s %s'
                % (logical_id, e, traceback.format_exc())
            )
            raise

    def _parse_and_create_resource(
        logical_id,
        resource_json,
        resources_map,
        region_name,
        update=False,
        force_create=False,
    ):
        stack_name = resources_map.get("AWS::StackName")
        resource_hash_key = (stack_name, logical_id)
        props = resource_json["Properties"] = resource_json.get("Properties") or {}

        # If the current stack is being updated, avoid infinite recursion
        updating = CURRENTLY_UPDATING_RESOURCES.get(resource_hash_key)
        LOG.debug(
            "Currently processing stack resource %s/%s: %s"
            % (stack_name, logical_id, updating)
        )
        if updating:
            return None

        # parse and get final resource JSON
        resource_tuple = parsing.parse_resource(
            logical_id, resource_json, resources_map
        )
        if not resource_tuple:
            return None
        _, resource_json, _ = resource_tuple

        def add_default_props(resource_props):
            """apply some fixes which otherwise cause deployments to fail"""
            res_type = resource_props["Type"]
            props = resource_props.get("Properties", {})
            if res_type == "AWS::Lambda::EventSourceMapping" and not props.get(
                "StartingPosition"
            ):
                props["StartingPosition"] = "LATEST"
            # generate default names for certain resource types
            default_attrs = (
                ("AWS::IAM::Role", "RoleName"),
                ("AWS::Events::Rule", "Name"),
            )
            for entry in default_attrs:
                if res_type == entry[0] and not props.get(entry[1]):
                    props[entry[1]] = "cf-%s-%s" % (
                        stack_name,
                        md5(canonical_json(props)),
                    )

        # add some fixes and default props which otherwise cause deployments to fail
        add_default_props(resource_json)
        for resource in resources_map._resource_json_map.values():
            add_default_props(resource)

        # check if this resource already exists in the resource map
        resource = resources_map._parsed_resources.get(logical_id)
        if resource and not update and not force_create:
            return resource

        # fix resource ARNs, make sure to convert account IDs 000000000000 to 123456789012
        resource_json_arns_fixed = clone(json_safe(convert_objs_to_ids(resource_json)))
        set_moto_account_ids(resource_json_arns_fixed)

        # create resource definition and store CloudFormation metadata in moto
        moto_create_error = None
        if (resource or update) and not force_create:
            parse_and_update_resource_orig(
                logical_id, resource_json_arns_fixed, resources_map, region_name
            )
        elif not resource:
            try:
                resource = parse_and_create_resource_orig(
                    logical_id, resource_json_arns_fixed, resources_map, region_name
                )
                resource.logical_id = logical_id
            except Exception as e:
                moto_create_error = e

        # check whether this resource needs to be deployed
        resource_map_new = dict(resources_map._resource_json_map)
        resource_map_new[logical_id] = resource_json
        should_be_created = template_deployer.should_be_deployed(
            logical_id, resource_map_new, stack_name
        )

        # check for moto creation errors and raise an exception if needed
        if moto_create_error:
            if should_be_created:
                raise moto_create_error
            else:
                LOG.info(
                    "Error on moto CF resource creation. Ignoring, as should_be_created=%s: %s"
                    % (should_be_created, moto_create_error)
                )

        # Fix for moto which sometimes hard-codes region name as 'us-east-1'
        if hasattr(resource, "region_name") and resource.region_name != region_name:
            LOG.debug(
                "Updating incorrect region from %s to %s"
                % (resource.region_name, region_name)
            )
            resource.region_name = region_name

        # check whether this resource needs to be deployed
        is_updateable = False
        if not should_be_created:
            # This resource is either not deployable or already exists. Check if it can be updated
            is_updateable = template_deployer.is_updateable(
                logical_id, resource_map_new, stack_name
            )
            if not update or not is_updateable:
                all_satisfied = template_deployer.all_resource_dependencies_satisfied(
                    logical_id, resource_map_new, stack_name
                )
                if not all_satisfied:
                    LOG.info(
                        "Resource %s cannot be deployed, found unsatisfied dependencies. %s"
                        % (logical_id, resource_json)
                    )
                    details = [logical_id, resource_json, resources_map, region_name]
                    resources_map._unresolved_resources = getattr(
                        resources_map, "_unresolved_resources", {}
                    )
                    resources_map._unresolved_resources[logical_id] = details
                else:
                    LOG.debug(
                        "Resource %s need not be deployed (is_updateable=%s): %s %s"
                        % (logical_id, is_updateable, resource_json, bool(resource))
                    )
                # Return if this resource already exists and can/need not be updated yet
                # NOTE: We should always return the resource here, to avoid duplicate
                #       creation of resources in moto!
                return resource

        # Apply some fixes/patches to the resource names, then deploy resource in LocalStack
        update_resource_name(resource, resource_json)
        LOG.debug(
            "Deploying CloudFormation resource (update=%s, exists=%s, updateable=%s): %s"
            % (update, not should_be_created, is_updateable, resource_json)
        )

        try:
            CURRENTLY_UPDATING_RESOURCES[resource_hash_key] = True
            deploy_func = (
                template_deployer.update_resource
                if update
                else template_deployer.deploy_resource
            )
            result = deploy_func(logical_id, resource_map_new, stack_name=stack_name)
        finally:
            CURRENTLY_UPDATING_RESOURCES[resource_hash_key] = False

        if not should_be_created:
            # skip the parts below for update requests
            return resource

        def find_id(resource):
            """Find ID of the given resource."""
            if not resource:
                return
            for id_attr in (
                "Id",
                "id",
                "ResourceId",
                "RestApiId",
                "DeploymentId",
                "RoleId",
            ):
                if id_attr in resource:
                    return resource[id_attr]

        # update resource IDs to avoid mismatch between CF moto and LocalStack backend resources
        if hasattr(resource, "id") or (
            isinstance(resource, dict) and resource.get("id")
        ):
            existing_id = resource.id if hasattr(resource, "id") else resource["id"]
            new_res_id = find_id(result)
            LOG.debug(
                "Updating resource id: %s - %s, %s - %s"
                % (existing_id, new_res_id, resource, resource_json)
            )
            if new_res_id:
                LOG.info(
                    "Updating resource ID from %s to %s (%s)"
                    % (existing_id, new_res_id, region_name)
                )
                update_resource_id(
                    resource,
                    new_res_id,
                    props,
                    region_name,
                    stack_name,
                    resources_map._resource_json_map,
                )
            else:
                LOG.warning(
                    "Unable to extract id for resource %s: %s" % (logical_id, result)
                )

        # update physical_resource_id field
        update_physical_resource_id(resource)

        return resource

    def update_resource_id(
        resource, new_id, props, region_name, stack_name, resource_map
    ):
        """Update and fix the ID(s) of the given resource."""

        # NOTE: this is a bit of a hack, which is required because
        # of the order of events when CloudFormation resources are created.
        # When we process a request to create a CF resource that's part of a
        # stack, say, an API Gateway Resource, then we (1) create the object
        # in memory in moto, which generates a random ID for the resource, and
        # (2) create the actual resource in the backend service using
        # template_deployer.deploy_resource(..) (see above).
        # The resource created in (2) now has a different ID than the resource
        # created in (1), which leads to downstream problems. Hence, we need
        # the logic below to reconcile the ids, i.e., apply IDs from (2) to (1).

        backend = apigw_models.apigateway_backends[region_name]
        if isinstance(resource, apigw_models.RestAPI):
            backend.apis.pop(resource.id, None)
            backend.apis[new_id] = resource
            # We also need to fetch the resources to replace the root resource
            # that moto automatically adds to newly created RestAPI objects
            client = aws_stack.connect_to_service("apigateway")
            resources = client.get_resources(restApiId=new_id, limit=500)["items"]
            # make sure no resources have been added in addition to the root /
            assert len(resource.resources) == 1
            resource.resources = {}
            for res in resources:
                res_path_part = res.get("pathPart") or res.get("path")
                child = resource.add_child(res_path_part, res.get("parentId"))
                resource.resources.pop(child.id)
                child.id = res["id"]
                child.api_id = new_id
                resource.resources[child.id] = child
            resource.id = new_id
        elif isinstance(resource, apigw_models.Resource):
            api_id = props["RestApiId"]
            api_id = template_deployer.resolve_refs_recursively(
                stack_name, api_id, resource_map
            )
            backend.apis[api_id].resources.pop(resource.id, None)
            backend.apis[api_id].resources[new_id] = resource
            resource.id = new_id
        elif isinstance(resource, apigw_models.Deployment):
            api_id = props["RestApiId"]
            api_id = template_deployer.resolve_refs_recursively(
                stack_name, api_id, resource_map
            )
            backend.apis[api_id].deployments.pop(resource["id"], None)
            backend.apis[api_id].deployments[new_id] = resource
            resource["id"] = new_id
        else:
            LOG.warning(
                "Unexpected resource type when updating ID: %s" % type(resource)
            )

    parse_and_create_resource_orig = parsing.parse_and_create_resource
    parsing.parse_and_create_resource = parse_and_create_resource
    parse_and_update_resource_orig = parsing.parse_and_update_resource
    parsing.parse_and_update_resource = parse_and_update_resource

    # patch CloudFormation parse_output(..) method to fix a bug in moto
    def parse_output(output_logical_id, output_json, resources_map):
        try:
            result = parse_output_orig(output_logical_id, output_json, resources_map)
        except KeyError:
            result = Output()
            result.key = output_logical_id
            result.value = None
            result.description = output_json.get("Description")
        # Make sure output includes export name
        if not hasattr(result, "export_name"):
            result.export_name = output_json.get("Export", {}).get("Name")
        return result

    parse_output_orig = parsing.parse_output
    parsing.parse_output = parse_output

    # Make sure the export name is returned for stack outputs
    if "<ExportName>" not in responses.DESCRIBE_STACKS_TEMPLATE:
        find = "</OutputValue>"
        replace = """</OutputValue>
        {% if output.export_name %}
        <ExportName>{{ output.export_name }}</ExportName>
        {% endif %}
        """
        responses.DESCRIBE_STACKS_TEMPLATE = responses.DESCRIBE_STACKS_TEMPLATE.replace(
            find, replace
        )

    # Patch CloudFormationBackend.update_stack method in moto
    def make_cf_update_stack(cf_backend):
        cf_update_stack_orig = cf_backend.update_stack

        def cf_update_stack(self, *args, **kwargs):
            stack = cf_update_stack_orig(*args, **kwargs)
            # update stack exports
            self._validate_export_uniqueness(stack)
            for export in stack.exports:
                self.exports[export.name] = export
            return stack

        return types.MethodType(cf_update_stack, cf_backend)

    for region, cf_backend in cloudformation_backends.items():
        cf_backend.update_stack = make_cf_update_stack(cf_backend)

    # Patch DynamoDB get_cfn_attribute(..) method in moto
    def DynamoDB_Table_get_cfn_attribute(self, attribute_name):
        try:
            return ddb_table_get_cfn_attribute_orig(self, attribute_name)
        except Exception:
            if attribute_name == "Arn":
                return aws_stack.dynamodb_table_arn(table_name=self.name)
            raise

    ddb_table_get_cfn_attribute_orig = dynamodb_models.Table.get_cfn_attribute
    dynamodb_models.Table.get_cfn_attribute = DynamoDB_Table_get_cfn_attribute

    # Patch generate_stack_id(..) method in moto
    def generate_stack_id(stack_name, region=None, **kwargs):
        region = region or aws_stack.get_region()
        return generate_stack_id_orig(stack_name, region=region, **kwargs)

    generate_stack_id_orig = cloudformation_utils.generate_stack_id
    cloudformation_utils.generate_stack_id = cloudformation_models.generate_stack_id = (
        generate_stack_id
    )

    # Patch DynamoDB get_cfn_attribute(..) method in moto
    def DynamoDB2_Table_get_cfn_attribute(self, attribute_name):
        if attribute_name == "Arn":
            return aws_stack.dynamodb_table_arn(table_name=self.name)
        elif attribute_name == "StreamArn":
            if (self.stream_specification or {}).get("StreamEnabled"):
                return aws_stack.dynamodb_stream_arn(self.name, "latest")
            return None
        raise UnformattedGetAttTemplateException()

    dynamodb2_models.Table.get_cfn_attribute = DynamoDB2_Table_get_cfn_attribute

    # Patch SQS get_cfn_attribute(..) method in moto
    def SQS_Queue_get_cfn_attribute(self, attribute_name):
        if attribute_name in ["Arn", "QueueArn"]:
            return aws_stack.sqs_queue_arn(queue_name=self.name)
        return SQS_Queue_get_cfn_attribute_orig(self, attribute_name)

    SQS_Queue_get_cfn_attribute_orig = sqs_models.Queue.get_cfn_attribute
    sqs_models.Queue.get_cfn_attribute = SQS_Queue_get_cfn_attribute

    # Patch S3 Bucket get_cfn_attribute(..) method in moto
    def S3_Bucket_get_cfn_attribute(self, attribute_name):
        if attribute_name in ["Arn"]:
            return aws_stack.s3_bucket_arn(self.name)
        return S3_Bucket_get_cfn_attribute_orig(self, attribute_name)

    S3_Bucket_get_cfn_attribute_orig = s3_models.FakeBucket.get_cfn_attribute
    s3_models.FakeBucket.get_cfn_attribute = S3_Bucket_get_cfn_attribute

    # Patch SQS physical_resource_id(..) method in moto
    @property
    def SQS_Queue_physical_resource_id(self):
        result = SQS_Queue_physical_resource_id_orig.fget(self)
        if "://" not in result:
            # convert ID to queue URL
            return aws_stack.get_sqs_queue_url(result)
        return result

    SQS_Queue_physical_resource_id_orig = sqs_models.Queue.physical_resource_id
    sqs_models.Queue.physical_resource_id = SQS_Queue_physical_resource_id

    # Patch LogGroup get_cfn_attribute(..) method in moto
    def LogGroup_get_cfn_attribute(self, attribute_name):
        try:
            return LogGroup_get_cfn_attribute_orig(self, attribute_name)
        except Exception:
            if attribute_name == "Arn":
                return aws_stack.log_group_arn(self.name)
            raise

    LogGroup_get_cfn_attribute_orig = getattr(
        cw_models.LogGroup, "get_cfn_attribute", None
    )
    cw_models.LogGroup.get_cfn_attribute = LogGroup_get_cfn_attribute

    # Patch Lambda get_cfn_attribute(..) method in moto
    def Lambda_Function_get_cfn_attribute(self, attribute_name):
        try:
            if attribute_name == "Arn":
                return self.function_arn
            return Lambda_Function_get_cfn_attribute_orig(self, attribute_name)
        except Exception:
            if attribute_name in ("Name", "FunctionName"):
                return self.function_name
            raise

    Lambda_Function_get_cfn_attribute_orig = (
        lambda_models.LambdaFunction.get_cfn_attribute
    )
    lambda_models.LambdaFunction.get_cfn_attribute = Lambda_Function_get_cfn_attribute

    # Patch DynamoDB get_cfn_attribute(..) method in moto
    def DynamoDB_Table_get_cfn_attribute(self, attribute_name):
        try:
            if attribute_name == "StreamArn":
                streams = aws_stack.connect_to_service("dynamodbstreams").list_streams(
                    TableName=self.name
                )["Streams"]
                return streams[0]["StreamArn"] if streams else None
            return DynamoDB_Table_get_cfn_attribute_orig(self, attribute_name)
        except Exception as e:
            LOG.warning(
                'Unable to get attribute "%s" from resource %s: %s'
                % (attribute_name, type(self), e)
            )
            raise

    DynamoDB_Table_get_cfn_attribute_orig = dynamodb_models.Table.get_cfn_attribute
    dynamodb_models.Table.get_cfn_attribute = DynamoDB_Table_get_cfn_attribute

    # Patch IAM get_cfn_attribute(..) method in moto
    def IAM_Role_get_cfn_attribute(self, attribute_name):
        try:
            return IAM_Role_get_cfn_attribute_orig(self, attribute_name)
        except Exception:
            if attribute_name == "Arn":
                return aws_stack.role_arn(self.name)
            raise

    IAM_Role_get_cfn_attribute_orig = iam_models.Role.get_cfn_attribute
    iam_models.Role.get_cfn_attribute = IAM_Role_get_cfn_attribute

    # Patch IAM Role model
    # https://github.com/localstack/localstack/issues/925
    @property
    def IAM_Role_physical_resource_id(self):
        return self.name

    iam_models.Role.physical_resource_id = IAM_Role_physical_resource_id

    # Patch SNS Topic get_cfn_attribute(..) method in moto
    def SNS_Topic_get_cfn_attribute(self, attribute_name):
        result = SNS_Topic_get_cfn_attribute_orig(self, attribute_name)
        if attribute_name.lower() in ["arn", "topicarn"]:
            result = aws_stack.fix_account_id_in_arns(result)
        return result

    SNS_Topic_get_cfn_attribute_orig = sns_models.Topic.get_cfn_attribute
    sns_models.Topic.get_cfn_attribute = SNS_Topic_get_cfn_attribute

    # Patch ES get_cfn_attribute(..) method
    def ES_get_cfn_attribute(self, attribute_name):
        if attribute_name in ["Arn", "DomainArn"]:
            return aws_stack.es_domain_arn(self.params.get("DomainName"))
        if attribute_name == "DomainEndpoint":
            if not hasattr(self, "_domain_endpoint"):
                es_details = aws_stack.connect_to_service(
                    "es"
                ).describe_elasticsearch_domain(
                    DomainName=self.params.get("DomainName")
                )
                self._domain_endpoint = es_details["DomainStatus"]["Endpoint"]
            return self._domain_endpoint
        raise UnformattedGetAttTemplateException()

    service_models.ElasticsearchDomain.get_cfn_attribute = ES_get_cfn_attribute

    # Patch Firehose get_cfn_attribute(..) method
    def Firehose_get_cfn_attribute(self, attribute_name):
        if attribute_name == "Arn":
            return aws_stack.firehose_stream_arn(self.params.get("DeliveryStreamName"))
        raise UnformattedGetAttTemplateException()

    service_models.FirehoseDeliveryStream.get_cfn_attribute = Firehose_get_cfn_attribute

    # Patch LambdaFunction create_from_cloudformation_json(..) method in moto
    @classmethod
    def Lambda_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        resource_name = (
            cloudformation_json.get("Properties", {}).get("FunctionName")
            or resource_name
        )
        return Lambda_create_from_cloudformation_json_orig(
            resource_name, cloudformation_json, region_name
        )

    Lambda_create_from_cloudformation_json_orig = (
        lambda_models.LambdaFunction.create_from_cloudformation_json
    )
    lambda_models.LambdaFunction.create_from_cloudformation_json = (
        Lambda_create_from_cloudformation_json
    )

    # Patch EventSourceMapping create_from_cloudformation_json(..) method in moto
    @classmethod
    def Mapping_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        props = cloudformation_json.get("Properties", {})
        func_name = props.get("FunctionName") or ""
        if ":lambda:" in func_name:
            props["FunctionName"] = aws_stack.lambda_function_name(func_name)
        return Mapping_create_from_cloudformation_json_orig(
            resource_name, cloudformation_json, region_name
        )

    Mapping_create_from_cloudformation_json_orig = (
        lambda_models.EventSourceMapping.create_from_cloudformation_json
    )
    lambda_models.EventSourceMapping.create_from_cloudformation_json = (
        Mapping_create_from_cloudformation_json
    )

    # Patch LambdaFunction update_from_cloudformation_json(..) method in moto
    @classmethod
    def Lambda_update_from_cloudformation_json(
        cls, original_resource, new_resource_name, cloudformation_json, region_name
    ):
        resource_name = (
            cloudformation_json.get("Properties", {}).get("FunctionName")
            or new_resource_name
        )
        return Lambda_create_from_cloudformation_json_orig(
            resource_name, cloudformation_json, region_name
        )

    if not hasattr(lambda_models.LambdaFunction, "update_from_cloudformation_json"):
        lambda_models.LambdaFunction.update_from_cloudformation_json = (
            Lambda_update_from_cloudformation_json
        )

    # Patch Role update_from_cloudformation_json(..) method
    @classmethod
    def Role_update_from_cloudformation_json(
        cls, original_resource, new_resource_name, cloudformation_json, region_name
    ):
        props = cloudformation_json.get("Properties", {})
        original_resource.name = props.get("RoleName") or original_resource.name
        original_resource.assume_role_policy_document = props.get(
            "AssumeRolePolicyDocument"
        )
        return original_resource

    if not hasattr(iam_models.Role, "update_from_cloudformation_json"):
        iam_models.Role.update_from_cloudformation_json = (
            Role_update_from_cloudformation_json
        )

    # patch ApiGateway Deployment
    def depl_delete_from_cloudformation_json(resource_name, resource_json, region_name):
        properties = resource_json["Properties"]
        LOG.info(
            "TODO: apigateway.Deployment.delete_from_cloudformation_json %s"
            % properties
        )

    if not hasattr(apigw_models.Deployment, "delete_from_cloudformation_json"):
        apigw_models.Deployment.delete_from_cloudformation_json = (
            depl_delete_from_cloudformation_json
        )

    # patch Lambda Version
    def vers_delete_from_cloudformation_json(resource_name, resource_json, region_name):
        properties = resource_json["Properties"]
        LOG.info(
            "TODO: apigateway.Deployment.delete_from_cloudformation_json %s"
            % properties
        )

    if not hasattr(lambda_models.LambdaVersion, "delete_from_cloudformation_json"):
        lambda_models.LambdaVersion.delete_from_cloudformation_json = (
            vers_delete_from_cloudformation_json
        )

    # add CloudFormation types
    @classmethod
    def RestAPI_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        props = cloudformation_json["Properties"]
        name = props["Name"]
        region_name = props.get("Region") or aws_stack.get_region()
        description = props.get("Description") or ""
        id = props.get("Id") or short_uid()
        return apigw_models.RestAPI(id, region_name, name, description)

    def RestAPI_get_cfn_attribute(self, attribute_name):
        if attribute_name == "Id":
            return self.id
        if attribute_name == "Region":
            return self.region_name
        if attribute_name == "Name":
            return self.name
        if attribute_name == "Description":
            return self.description
        if attribute_name == "RootResourceId":
            for id, resource in self.resources.items():
                if resource.parent_id is None:
                    return resource.id
            return None
        raise UnformattedGetAttTemplateException()

    @classmethod
    def Deployment_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        props = cloudformation_json["Properties"]
        name = props["StageName"]
        deployment_id = props.get("Id") or short_uid()
        description = props.get("Description") or ""
        return apigw_models.Deployment(deployment_id, name, description)

    @classmethod
    def Resource_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        props = cloudformation_json["Properties"]
        region_name = props.get("Region") or aws_stack.get_region()
        path_part = props.get("PathPart")
        api_id = props.get("RestApiId")
        parent_id = props.get("ParentId")
        id = props.get("Id") or short_uid()
        return apigw_models.Resource(id, region_name, api_id, path_part, parent_id)

    @classmethod
    def Method_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        props = cloudformation_json["Properties"]
        method_type = props.get("HttpMethod")
        authorization_type = props.get("AuthorizationType")
        return apigw_models.Method(method_type, authorization_type)

    apigw_models.RestAPI.create_from_cloudformation_json = (
        RestAPI_create_from_cloudformation_json
    )
    apigw_models.RestAPI.get_cfn_attribute = RestAPI_get_cfn_attribute
    apigw_models.Deployment.create_from_cloudformation_json = (
        Deployment_create_from_cloudformation_json
    )
    apigw_models.Resource.create_from_cloudformation_json = (
        Resource_create_from_cloudformation_json
    )
    apigw_models.Method.create_from_cloudformation_json = (
        Method_create_from_cloudformation_json
    )
    # TODO: add support for AWS::ApiGateway::Model, AWS::ApiGateway::RequestValidator, ...

    # fix AttributeError in moto's CloudFormation describe_stack_resource
    def describe_stack_resource(self):
        stack_name = self._get_param("StackName")
        stack = self.cloudformation_backend.get_stack(stack_name)
        logical_resource_id = self._get_param("LogicalResourceId")
        if not stack:
            msg = 'Unable to find CloudFormation stack "%s" in region %s' % (
                stack_name,
                aws_stack.get_region(),
            )
            if aws_stack.get_region() != self.region:
                msg = "%s/%s" % (msg, self.region)
            LOG.warning(msg)
            response = aws_responses.flask_error_response(
                msg, code=404, error_type="ResourceNotFoundException"
            )
            return 404, response.headers, response.data

        for stack_resource in stack.stack_resources:
            # Note: Line below has been patched
            # if stack_resource.logical_resource_id == logical_resource_id:
            if (
                stack_resource
                and stack_resource.logical_resource_id == logical_resource_id
            ):
                resource = stack_resource
                break
        else:
            raise ValidationError(logical_resource_id)

        template = self.response_template(
            responses.DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE
        )
        return template.render(stack=stack, resource=resource)

    responses.CloudFormationResponse.describe_stack_resource = describe_stack_resource

    # fix moto's describe_stack_events jinja2.exceptions.UndefinedError
    def cf_describe_stack_events(self):
        stack_name = self._get_param("StackName")
        backend = self.cloudformation_backend
        stack = backend.get_stack(stack_name)
        if not stack:
            # Also return stack events for deleted stacks, specified by stack name
            stack = (
                [
                    stk
                    for id, stk in backend.deleted_stacks.items()
                    if stk.name == stack_name
                ]
                or [0]
            )[0]
        if not stack:
            raise ValidationError(
                stack_name,
                message='Unable to find stack "%s" in region %s'
                % (stack_name, aws_stack.get_region()),
            )
        template = self.response_template(responses.DESCRIBE_STACK_EVENTS_RESPONSE)
        return template.render(stack=stack)

    responses.CloudFormationResponse.describe_stack_events = cf_describe_stack_events

    # fix Lambda regions in moto - see https://github.com/localstack/localstack/issues/1961
    for region in boto3.session.Session().get_available_regions("lambda"):
        if region not in lambda_models.lambda_backends:
            lambda_models.lambda_backends[region] = lambda_models.LambdaBackend(region)

    # patch FakeStack.initialize_resources
    def run_dependencies_deployment_loop(stack, action):
        def set_status(status):
            stack._add_stack_event(status)
            stack.status = status

        def run_loop(*args):
            # NOTE: We're adding this additional loop, as it seems that in some cases moto
            #   does not consider resource dependencies (e.g., if a "DependsOn" resource property
            #   is defined). This loop allows us to incrementally resolve such dependencies.
            resource_map = stack.resource_map
            unresolved = {}
            for i in range(MAX_DEPENDENCY_DEPTH):
                LOG.debug(
                    "Running CloudFormation stack deployment loop iteration %s"
                    % (i + 1)
                )
                unresolved = getattr(resource_map, "_unresolved_resources", {})
                if not unresolved:
                    set_status("%s_COMPLETE" % action)
                    return resource_map
                resource_map._unresolved_resources = {}
                for resource_id, resource_details in unresolved.items():
                    # Re-trigger the resource creation
                    parse_and_create_resource(*resource_details, force_create=True)
                if unresolved.keys() == resource_map._unresolved_resources.keys():
                    # looks like no more resources can be resolved -> bail
                    LOG.warning(
                        "Unresolvable dependencies, there may be undeployed stack resources: %s"
                        % unresolved
                    )
                    break
            set_status("%s_FAILED" % action)
            raise Exception(
                "Unable to resolve all CloudFormation resources after traversing "
                + "dependency tree (maximum depth %s reached): %s"
                % (MAX_DEPENDENCY_DEPTH, list(unresolved.keys()))
            )

        # NOTE: We're running the loop in the background, as it might take some time to complete
        FuncThread(run_loop).start()

    def initialize_resources(self):
        self.resource_map._template = self.resource_map._template or self.template_dict
        self.resource_map.load()
        self.resource_map.create(self.template_dict)
        self.output_map.create()
        run_dependencies_deployment_loop(self, "CREATE")

    def update(self, *args, **kwargs):
        stack_update_orig(self, *args, **kwargs)
        run_dependencies_deployment_loop(self, "UPDATE")

    FakeStack.initialize_resources = initialize_resources
    stack_update_orig = FakeStack.update
    FakeStack.update = update

    # patch Kinesis Stream get_cfn_attribute(..) method in moto
    def Kinesis_Stream_get_cfn_attribute(self, attribute_name):
        if attribute_name == "Arn":
            return self.arn

        raise UnformattedGetAttTemplateException()

    kinesis_models.Stream.get_cfn_attribute = Kinesis_Stream_get_cfn_attribute

    # patch cloudformation backend create_change_set(..)
    # #760 cloudformation deploy invalid xml error
    cloudformation_backend_create_change_set_orig = (
        CloudFormationBackend.create_change_set
    )

    def cloudformation_backend_create_change_set(
        self,
        stack_name,
        change_set_name,
        template,
        parameters,
        region_name,
        change_set_type,
        notification_arns=None,
        tags=None,
        role_arn=None,
    ):
        change_set_id, _ = cloudformation_backend_create_change_set_orig(
            self,
            stack_name,
            change_set_name,
            template,
            parameters,
            region_name,
            change_set_type,
            notification_arns,
            tags,
            role_arn,
        )
        change_set = self.change_sets[change_set_id]
        change_set.status = "CREATE_COMPLETE"

        return change_set_id, _

    CloudFormationBackend.create_change_set = cloudformation_backend_create_change_set


# Traceback (ParamValidationError)
localstack_1            | 2020-02-24T14:57:32:ERROR:localstack.services.cloudformation.cloudformation_starter: Unable to parse and create resource "MyTestSubscription": Parameter validation failed:
localstack_1            | Missing required parameter in input: "TopicArn" Traceback (most recent call last):
localstack_1            |   File "/opt/code/localstack/localstack/services/cloudformation/cloudformation_starter.py", line 185, in parse_and_create_resource
localstack_1            |     return _parse_and_create_resource(logical_id, resource_json,
localstack_1            |   File "/opt/code/localstack/localstack/services/cloudformation/cloudformation_starter.py", line 287, in _parse_and_create_resource
localstack_1            |     result = deploy_func(logical_id, resource_map_new, stack_name=stack_name)
localstack_1            |   File "/opt/code/localstack/localstack/utils/cloudformation/template_deployer.py", line 823, in deploy_resource
localstack_1            |     return execute_resource_action(resource_id, resources, stack_name, ACTION_CREATE)
localstack_1            |   File "/opt/code/localstack/localstack/utils/cloudformation/template_deployer.py", line 849, in execute_resource_action
localstack_1            |     result = configure_resource_via_sdk(resource_id, resources, resource_type, func, stack_name)
localstack_1            |   File "/opt/code/localstack/localstack/utils/cloudformation/template_deployer.py", line 922, in configure_resource_via_sdk
localstack_1            |     raise e
localstack_1            |   File "/opt/code/localstack/localstack/utils/cloudformation/template_deployer.py", line 919, in configure_resource_via_sdk
localstack_1            |     result = function(**params)
localstack_1            |   File "/opt/code/localstack/.venv/lib/python3.8/site-packages/botocore/client.py", line 316, in _api_call
localstack_1            |     return self._make_api_call(operation_name, kwargs)
localstack_1            |   File "/opt/code/localstack/.venv/lib/python3.8/site-packages/botocore/client.py", line 598, in _make_api_call
localstack_1            |     request_dict = self._convert_to_request_dict(
localstack_1            |   File "/opt/code/localstack/.venv/lib/python3.8/site-packages/botocore/client.py", line 646, in _convert_to_request_dict
localstack_1            |     request_dict = self._serializer.serialize_to_request(
localstack_1            |   File "/opt/code/localstack/.venv/lib/python3.8/site-packages/botocore/validate.py", line 297, in serialize_to_request
localstack_1            |     raise ParamValidationError(report=report.generate_report())
localstack_1            | botocore.exceptions.ParamValidationError: Parameter validation failed:
localstack_1            | Missing required parameter in input: "TopicArn"

# Fixed code
def apply_patches():
    """Apply patches to make LocalStack seamlessly interact with the moto backend.
    TODO: Eventually, these patches should be contributed to the upstream repo!"""

    # add model mappings to moto
    parsing.MODEL_MAP.update(MODEL_MAP)

    # fix account ID
    parsing.ACCOUNT_ID = TEST_AWS_ACCOUNT_ID

    # Patch clean_json in moto
    def clean_json(resource_json, resources_map):
        result = clean_json_orig(resource_json, resources_map)
        if isinstance(result, BaseModel):
            if isinstance(resource_json, dict) and "Ref" in resource_json:
                entity_id = get_entity_id(result, resource_json)
                if entity_id:
                    return entity_id
                LOG.warning(
                    'Unable to resolve "Ref" attribute for: %s - %s - %s',
                    resource_json,
                    result,
                    type(result),
                )
        return result

    clean_json_orig = parsing.clean_json
    parsing.clean_json = clean_json

    # Patch parse_and_create_resource method in moto to deploy resources in LocalStack
    def parse_and_create_resource(
        logical_id, resource_json, resources_map, region_name, force_create=False
    ):
        try:
            return _parse_and_create_resource(
                logical_id,
                resource_json,
                resources_map,
                region_name,
                force_create=force_create,
            )
        except Exception as e:
            LOG.error(
                'Unable to parse and create resource "%s": %s %s'
                % (logical_id, e, traceback.format_exc())
            )
            raise

    def parse_and_update_resource(
        logical_id, resource_json, resources_map, region_name
    ):
        try:
            return _parse_and_create_resource(
                logical_id, resource_json, resources_map, region_name, update=True
            )
        except Exception as e:
            LOG.error(
                'Unable to parse and update resource "%s": %s %s'
                % (logical_id, e, traceback.format_exc())
            )
            raise

    def _parse_and_create_resource(
        logical_id,
        resource_json,
        resources_map,
        region_name,
        update=False,
        force_create=False,
    ):
        stack_name = resources_map.get("AWS::StackName")
        resource_hash_key = (stack_name, logical_id)
        props = resource_json["Properties"] = resource_json.get("Properties") or {}

        # If the current stack is being updated, avoid infinite recursion
        updating = CURRENTLY_UPDATING_RESOURCES.get(resource_hash_key)
        LOG.debug(
            "Currently processing stack resource %s/%s: %s"
            % (stack_name, logical_id, updating)
        )
        if updating:
            return None

        # parse and get final resource JSON
        resource_tuple = parsing.parse_resource(
            logical_id, resource_json, resources_map
        )
        if not resource_tuple:
            return None
        _, resource_json, _ = resource_tuple

        def add_default_props(resource_props):
            """apply some fixes which otherwise cause deployments to fail"""
            res_type = resource_props["Type"]
            props = resource_props.get("Properties", {})
            if res_type == "AWS::Lambda::EventSourceMapping" and not props.get(
                "StartingPosition"
            ):
                props["StartingPosition"] = "LATEST"
            # generate default names for certain resource types
            default_attrs = (
                ("AWS::IAM::Role", "RoleName"),
                ("AWS::Events::Rule", "Name"),
            )
            for entry in default_attrs:
                if res_type == entry[0] and not props.get(entry[1]):
                    props[entry[1]] = "cf-%s-%s" % (
                        stack_name,
                        md5(canonical_json(props)),
                    )

        # add some fixes and default props which otherwise cause deployments to fail
        add_default_props(resource_json)
        for resource in resources_map._resource_json_map.values():
            add_default_props(resource)

        # check if this resource already exists in the resource map
        resource = resources_map._parsed_resources.get(logical_id)
        if resource and not update and not force_create:
            return resource

        # fix resource ARNs, make sure to convert account IDs 000000000000 to 123456789012
        resource_json_arns_fixed = clone(json_safe(convert_objs_to_ids(resource_json)))
        set_moto_account_ids(resource_json_arns_fixed)

        # create resource definition and store CloudFormation metadata in moto
        moto_create_error = None
        if (resource or update) and not force_create:
            parse_and_update_resource_orig(
                logical_id, resource_json_arns_fixed, resources_map, region_name
            )
        elif not resource:
            try:
                resource = parse_and_create_resource_orig(
                    logical_id, resource_json_arns_fixed, resources_map, region_name
                )
                if not resource:
                    # this can happen if the resource has an associated Condition which evaluates to false
                    return resource
                resource.logical_id = logical_id
            except Exception as e:
                moto_create_error = e

        # check whether this resource needs to be deployed
        resource_map_new = dict(resources_map._resource_json_map)
        resource_map_new[logical_id] = resource_json
        should_be_created = template_deployer.should_be_deployed(
            logical_id, resource_map_new, stack_name
        )

        # check for moto creation errors and raise an exception if needed
        if moto_create_error:
            if should_be_created:
                raise moto_create_error
            else:
                LOG.info(
                    "Error on moto CF resource creation. Ignoring, as should_be_created=%s: %s"
                    % (should_be_created, moto_create_error)
                )

        # Fix for moto which sometimes hard-codes region name as 'us-east-1'
        if hasattr(resource, "region_name") and resource.region_name != region_name:
            LOG.debug(
                "Updating incorrect region from %s to %s"
                % (resource.region_name, region_name)
            )
            resource.region_name = region_name

        # check whether this resource needs to be deployed
        is_updateable = False
        if not should_be_created:
            # This resource is either not deployable or already exists. Check if it can be updated
            is_updateable = template_deployer.is_updateable(
                logical_id, resource_map_new, stack_name
            )
            if not update or not is_updateable:
                all_satisfied = template_deployer.all_resource_dependencies_satisfied(
                    logical_id, resource_map_new, stack_name
                )
                if not all_satisfied:
                    LOG.info(
                        "Resource %s cannot be deployed, found unsatisfied dependencies. %s"
                        % (logical_id, resource_json)
                    )
                    details = [logical_id, resource_json, resources_map, region_name]
                    resources_map._unresolved_resources = getattr(
                        resources_map, "_unresolved_resources", {}
                    )
                    resources_map._unresolved_resources[logical_id] = details
                else:
                    LOG.debug(
                        "Resource %s need not be deployed (is_updateable=%s): %s %s"
                        % (logical_id, is_updateable, resource_json, bool(resource))
                    )
                # Return if this resource already exists and can/need not be updated yet
                # NOTE: We should always return the resource here, to avoid duplicate
                #       creation of resources in moto!
                return resource

        # Apply some fixes/patches to the resource names, then deploy resource in LocalStack
        update_resource_name(resource, resource_json)
        LOG.debug(
            "Deploying CloudFormation resource (update=%s, exists=%s, updateable=%s): %s"
            % (update, not should_be_created, is_updateable, resource_json)
        )

        try:
            CURRENTLY_UPDATING_RESOURCES[resource_hash_key] = True
            deploy_func = (
                template_deployer.update_resource
                if update
                else template_deployer.deploy_resource
            )
            result = deploy_func(logical_id, resource_map_new, stack_name=stack_name)
        finally:
            CURRENTLY_UPDATING_RESOURCES[resource_hash_key] = False

        if not should_be_created:
            # skip the parts below for update requests
            return resource

        def find_id(resource):
            """Find ID of the given resource."""
            if not resource:
                return
            for id_attr in (
                "Id",
                "id",
                "ResourceId",
                "RestApiId",
                "DeploymentId",
                "RoleId",
            ):
                if id_attr in resource:
                    return resource[id_attr]

        # update resource IDs to avoid mismatch between CF moto and LocalStack backend resources
        if hasattr(resource, "id") or (
            isinstance(resource, dict) and resource.get("id")
        ):
            existing_id = resource.id if hasattr(resource, "id") else resource["id"]
            new_res_id = find_id(result)
            LOG.debug(
                "Updating resource id: %s - %s, %s - %s"
                % (existing_id, new_res_id, resource, resource_json)
            )
            if new_res_id:
                LOG.info(
                    "Updating resource ID from %s to %s (%s)"
                    % (existing_id, new_res_id, region_name)
                )
                update_resource_id(
                    resource,
                    new_res_id,
                    props,
                    region_name,
                    stack_name,
                    resources_map._resource_json_map,
                )
            else:
                LOG.warning(
                    "Unable to extract id for resource %s: %s" % (logical_id, result)
                )

        # update physical_resource_id field
        update_physical_resource_id(resource)

        return resource

    def update_resource_id(
        resource, new_id, props, region_name, stack_name, resource_map
    ):
        """Update and fix the ID(s) of the given resource."""

        # NOTE: this is a bit of a hack, which is required because
        # of the order of events when CloudFormation resources are created.
        # When we process a request to create a CF resource that's part of a
        # stack, say, an API Gateway Resource, then we (1) create the object
        # in memory in moto, which generates a random ID for the resource, and
        # (2) create the actual resource in the backend service using
        # template_deployer.deploy_resource(..) (see above).
        # The resource created in (2) now has a different ID than the resource
        # created in (1), which leads to downstream problems. Hence, we need
        # the logic below to reconcile the ids, i.e., apply IDs from (2) to (1).

        backend = apigw_models.apigateway_backends[region_name]
        if isinstance(resource, apigw_models.RestAPI):
            backend.apis.pop(resource.id, None)
            backend.apis[new_id] = resource
            # We also need to fetch the resources to replace the root resource
            # that moto automatically adds to newly created RestAPI objects
            client = aws_stack.connect_to_service("apigateway")
            resources = client.get_resources(restApiId=new_id, limit=500)["items"]
            # make sure no resources have been added in addition to the root /
            assert len(resource.resources) == 1
            resource.resources = {}
            for res in resources:
                res_path_part = res.get("pathPart") or res.get("path")
                child = resource.add_child(res_path_part, res.get("parentId"))
                resource.resources.pop(child.id)
                child.id = res["id"]
                child.api_id = new_id
                resource.resources[child.id] = child
            resource.id = new_id
        elif isinstance(resource, apigw_models.Resource):
            api_id = props["RestApiId"]
            api_id = template_deployer.resolve_refs_recursively(
                stack_name, api_id, resource_map
            )
            backend.apis[api_id].resources.pop(resource.id, None)
            backend.apis[api_id].resources[new_id] = resource
            resource.id = new_id
        elif isinstance(resource, apigw_models.Deployment):
            api_id = props["RestApiId"]
            api_id = template_deployer.resolve_refs_recursively(
                stack_name, api_id, resource_map
            )
            backend.apis[api_id].deployments.pop(resource["id"], None)
            backend.apis[api_id].deployments[new_id] = resource
            resource["id"] = new_id
        else:
            LOG.warning(
                "Unexpected resource type when updating ID: %s" % type(resource)
            )

    parse_and_create_resource_orig = parsing.parse_and_create_resource
    parsing.parse_and_create_resource = parse_and_create_resource
    parse_and_update_resource_orig = parsing.parse_and_update_resource
    parsing.parse_and_update_resource = parse_and_update_resource

    # patch CloudFormation parse_output(..) method to fix a bug in moto
    def parse_output(output_logical_id, output_json, resources_map):
        try:
            result = parse_output_orig(output_logical_id, output_json, resources_map)
        except KeyError:
            result = Output()
            result.key = output_logical_id
            result.value = None
            result.description = output_json.get("Description")
        # Make sure output includes export name
        if not hasattr(result, "export_name"):
            result.export_name = output_json.get("Export", {}).get("Name")
        return result

    parse_output_orig = parsing.parse_output
    parsing.parse_output = parse_output

    # Make sure the export name is returned for stack outputs
    if "<ExportName>" not in responses.DESCRIBE_STACKS_TEMPLATE:
        find = "</OutputValue>"
        replace = """</OutputValue>
        {% if output.export_name %}
        <ExportName>{{ output.export_name }}</ExportName>
        {% endif %}
        """
        responses.DESCRIBE_STACKS_TEMPLATE = responses.DESCRIBE_STACKS_TEMPLATE.replace(
            find, replace
        )

    # Patch CloudFormationBackend.update_stack method in moto
    def make_cf_update_stack(cf_backend):
        cf_update_stack_orig = cf_backend.update_stack

        def cf_update_stack(self, *args, **kwargs):
            stack = cf_update_stack_orig(*args, **kwargs)
            # update stack exports
            self._validate_export_uniqueness(stack)
            for export in stack.exports:
                self.exports[export.name] = export
            return stack

        return types.MethodType(cf_update_stack, cf_backend)

    for region, cf_backend in cloudformation_backends.items():
        cf_backend.update_stack = make_cf_update_stack(cf_backend)

    # Patch DynamoDB get_cfn_attribute(..) method in moto
    def DynamoDB_Table_get_cfn_attribute(self, attribute_name):
        try:
            return ddb_table_get_cfn_attribute_orig(self, attribute_name)
        except Exception:
            if attribute_name == "Arn":
                return aws_stack.dynamodb_table_arn(table_name=self.name)
            raise

    ddb_table_get_cfn_attribute_orig = dynamodb_models.Table.get_cfn_attribute
    dynamodb_models.Table.get_cfn_attribute = DynamoDB_Table_get_cfn_attribute

    # Patch generate_stack_id(..) method in moto
    def generate_stack_id(stack_name, region=None, **kwargs):
        region = region or aws_stack.get_region()
        return generate_stack_id_orig(stack_name, region=region, **kwargs)

    generate_stack_id_orig = cloudformation_utils.generate_stack_id
    cloudformation_utils.generate_stack_id = cloudformation_models.generate_stack_id = (
        generate_stack_id
    )

    # Patch DynamoDB get_cfn_attribute(..) method in moto
    def DynamoDB2_Table_get_cfn_attribute(self, attribute_name):
        if attribute_name == "Arn":
            return aws_stack.dynamodb_table_arn(table_name=self.name)
        elif attribute_name == "StreamArn":
            if (self.stream_specification or {}).get("StreamEnabled"):
                return aws_stack.dynamodb_stream_arn(self.name, "latest")
            return None
        raise UnformattedGetAttTemplateException()

    dynamodb2_models.Table.get_cfn_attribute = DynamoDB2_Table_get_cfn_attribute

    # Patch SQS get_cfn_attribute(..) method in moto
    def SQS_Queue_get_cfn_attribute(self, attribute_name):
        if attribute_name in ["Arn", "QueueArn"]:
            return aws_stack.sqs_queue_arn(queue_name=self.name)
        return SQS_Queue_get_cfn_attribute_orig(self, attribute_name)

    SQS_Queue_get_cfn_attribute_orig = sqs_models.Queue.get_cfn_attribute
    sqs_models.Queue.get_cfn_attribute = SQS_Queue_get_cfn_attribute

    # Patch S3 Bucket get_cfn_attribute(..) method in moto
    def S3_Bucket_get_cfn_attribute(self, attribute_name):
        if attribute_name in ["Arn"]:
            return aws_stack.s3_bucket_arn(self.name)
        return S3_Bucket_get_cfn_attribute_orig(self, attribute_name)

    S3_Bucket_get_cfn_attribute_orig = s3_models.FakeBucket.get_cfn_attribute
    s3_models.FakeBucket.get_cfn_attribute = S3_Bucket_get_cfn_attribute

    # Patch SQS physical_resource_id(..) method in moto
    @property
    def SQS_Queue_physical_resource_id(self):
        result = SQS_Queue_physical_resource_id_orig.fget(self)
        if "://" not in result:
            # convert ID to queue URL
            return aws_stack.get_sqs_queue_url(result)
        return result

    SQS_Queue_physical_resource_id_orig = sqs_models.Queue.physical_resource_id
    sqs_models.Queue.physical_resource_id = SQS_Queue_physical_resource_id

    # Patch LogGroup get_cfn_attribute(..) method in moto
    def LogGroup_get_cfn_attribute(self, attribute_name):
        try:
            return LogGroup_get_cfn_attribute_orig(self, attribute_name)
        except Exception:
            if attribute_name == "Arn":
                return aws_stack.log_group_arn(self.name)
            raise

    LogGroup_get_cfn_attribute_orig = getattr(
        cw_models.LogGroup, "get_cfn_attribute", None
    )
    cw_models.LogGroup.get_cfn_attribute = LogGroup_get_cfn_attribute

    # Patch Lambda get_cfn_attribute(..) method in moto
    def Lambda_Function_get_cfn_attribute(self, attribute_name):
        try:
            if attribute_name == "Arn":
                return self.function_arn
            return Lambda_Function_get_cfn_attribute_orig(self, attribute_name)
        except Exception:
            if attribute_name in ("Name", "FunctionName"):
                return self.function_name
            raise

    Lambda_Function_get_cfn_attribute_orig = (
        lambda_models.LambdaFunction.get_cfn_attribute
    )
    lambda_models.LambdaFunction.get_cfn_attribute = Lambda_Function_get_cfn_attribute

    # Patch DynamoDB get_cfn_attribute(..) method in moto
    def DynamoDB_Table_get_cfn_attribute(self, attribute_name):
        try:
            if attribute_name == "StreamArn":
                streams = aws_stack.connect_to_service("dynamodbstreams").list_streams(
                    TableName=self.name
                )["Streams"]
                return streams[0]["StreamArn"] if streams else None
            return DynamoDB_Table_get_cfn_attribute_orig(self, attribute_name)
        except Exception as e:
            LOG.warning(
                'Unable to get attribute "%s" from resource %s: %s'
                % (attribute_name, type(self), e)
            )
            raise

    DynamoDB_Table_get_cfn_attribute_orig = dynamodb_models.Table.get_cfn_attribute
    dynamodb_models.Table.get_cfn_attribute = DynamoDB_Table_get_cfn_attribute

    # Patch IAM get_cfn_attribute(..) method in moto
    def IAM_Role_get_cfn_attribute(self, attribute_name):
        try:
            return IAM_Role_get_cfn_attribute_orig(self, attribute_name)
        except Exception:
            if attribute_name == "Arn":
                return aws_stack.role_arn(self.name)
            raise

    IAM_Role_get_cfn_attribute_orig = iam_models.Role.get_cfn_attribute
    iam_models.Role.get_cfn_attribute = IAM_Role_get_cfn_attribute

    # Patch IAM Role model
    # https://github.com/localstack/localstack/issues/925
    @property
    def IAM_Role_physical_resource_id(self):
        return self.name

    iam_models.Role.physical_resource_id = IAM_Role_physical_resource_id

    # Patch SNS Topic get_cfn_attribute(..) method in moto
    def SNS_Topic_get_cfn_attribute(self, attribute_name):
        result = SNS_Topic_get_cfn_attribute_orig(self, attribute_name)
        if attribute_name.lower() in ["arn", "topicarn"]:
            result = aws_stack.fix_account_id_in_arns(result)
        return result

    SNS_Topic_get_cfn_attribute_orig = sns_models.Topic.get_cfn_attribute
    sns_models.Topic.get_cfn_attribute = SNS_Topic_get_cfn_attribute

    # Patch ES get_cfn_attribute(..) method
    def ES_get_cfn_attribute(self, attribute_name):
        if attribute_name in ["Arn", "DomainArn"]:
            return aws_stack.es_domain_arn(self.params.get("DomainName"))
        if attribute_name == "DomainEndpoint":
            if not hasattr(self, "_domain_endpoint"):
                es_details = aws_stack.connect_to_service(
                    "es"
                ).describe_elasticsearch_domain(
                    DomainName=self.params.get("DomainName")
                )
                self._domain_endpoint = es_details["DomainStatus"]["Endpoint"]
            return self._domain_endpoint
        raise UnformattedGetAttTemplateException()

    service_models.ElasticsearchDomain.get_cfn_attribute = ES_get_cfn_attribute

    # Patch Firehose get_cfn_attribute(..) method
    def Firehose_get_cfn_attribute(self, attribute_name):
        if attribute_name == "Arn":
            return aws_stack.firehose_stream_arn(self.params.get("DeliveryStreamName"))
        raise UnformattedGetAttTemplateException()

    service_models.FirehoseDeliveryStream.get_cfn_attribute = Firehose_get_cfn_attribute

    # Patch LambdaFunction create_from_cloudformation_json(..) method in moto
    @classmethod
    def Lambda_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        resource_name = (
            cloudformation_json.get("Properties", {}).get("FunctionName")
            or resource_name
        )
        return Lambda_create_from_cloudformation_json_orig(
            resource_name, cloudformation_json, region_name
        )

    Lambda_create_from_cloudformation_json_orig = (
        lambda_models.LambdaFunction.create_from_cloudformation_json
    )
    lambda_models.LambdaFunction.create_from_cloudformation_json = (
        Lambda_create_from_cloudformation_json
    )

    # Patch EventSourceMapping create_from_cloudformation_json(..) method in moto
    @classmethod
    def Mapping_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        props = cloudformation_json.get("Properties", {})
        func_name = props.get("FunctionName") or ""
        if ":lambda:" in func_name:
            props["FunctionName"] = aws_stack.lambda_function_name(func_name)
        return Mapping_create_from_cloudformation_json_orig(
            resource_name, cloudformation_json, region_name
        )

    Mapping_create_from_cloudformation_json_orig = (
        lambda_models.EventSourceMapping.create_from_cloudformation_json
    )
    lambda_models.EventSourceMapping.create_from_cloudformation_json = (
        Mapping_create_from_cloudformation_json
    )

    # Patch LambdaFunction update_from_cloudformation_json(..) method in moto
    @classmethod
    def Lambda_update_from_cloudformation_json(
        cls, original_resource, new_resource_name, cloudformation_json, region_name
    ):
        resource_name = (
            cloudformation_json.get("Properties", {}).get("FunctionName")
            or new_resource_name
        )
        return Lambda_create_from_cloudformation_json_orig(
            resource_name, cloudformation_json, region_name
        )

    if not hasattr(lambda_models.LambdaFunction, "update_from_cloudformation_json"):
        lambda_models.LambdaFunction.update_from_cloudformation_json = (
            Lambda_update_from_cloudformation_json
        )

    # Patch Role update_from_cloudformation_json(..) method
    @classmethod
    def Role_update_from_cloudformation_json(
        cls, original_resource, new_resource_name, cloudformation_json, region_name
    ):
        props = cloudformation_json.get("Properties", {})
        original_resource.name = props.get("RoleName") or original_resource.name
        original_resource.assume_role_policy_document = props.get(
            "AssumeRolePolicyDocument"
        )
        return original_resource

    if not hasattr(iam_models.Role, "update_from_cloudformation_json"):
        iam_models.Role.update_from_cloudformation_json = (
            Role_update_from_cloudformation_json
        )

    # patch ApiGateway Deployment
    def depl_delete_from_cloudformation_json(resource_name, resource_json, region_name):
        properties = resource_json["Properties"]
        LOG.info(
            "TODO: apigateway.Deployment.delete_from_cloudformation_json %s"
            % properties
        )

    if not hasattr(apigw_models.Deployment, "delete_from_cloudformation_json"):
        apigw_models.Deployment.delete_from_cloudformation_json = (
            depl_delete_from_cloudformation_json
        )

    # patch Lambda Version
    def vers_delete_from_cloudformation_json(resource_name, resource_json, region_name):
        properties = resource_json["Properties"]
        LOG.info(
            "TODO: apigateway.Deployment.delete_from_cloudformation_json %s"
            % properties
        )

    if not hasattr(lambda_models.LambdaVersion, "delete_from_cloudformation_json"):
        lambda_models.LambdaVersion.delete_from_cloudformation_json = (
            vers_delete_from_cloudformation_json
        )

    # add CloudFormation types
    @classmethod
    def RestAPI_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        props = cloudformation_json["Properties"]
        name = props["Name"]
        region_name = props.get("Region") or aws_stack.get_region()
        description = props.get("Description") or ""
        id = props.get("Id") or short_uid()
        return apigw_models.RestAPI(id, region_name, name, description)

    def RestAPI_get_cfn_attribute(self, attribute_name):
        if attribute_name == "Id":
            return self.id
        if attribute_name == "Region":
            return self.region_name
        if attribute_name == "Name":
            return self.name
        if attribute_name == "Description":
            return self.description
        if attribute_name == "RootResourceId":
            for id, resource in self.resources.items():
                if resource.parent_id is None:
                    return resource.id
            return None
        raise UnformattedGetAttTemplateException()

    @classmethod
    def Deployment_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        props = cloudformation_json["Properties"]
        name = props["StageName"]
        deployment_id = props.get("Id") or short_uid()
        description = props.get("Description") or ""
        return apigw_models.Deployment(deployment_id, name, description)

    @classmethod
    def Resource_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        props = cloudformation_json["Properties"]
        region_name = props.get("Region") or aws_stack.get_region()
        path_part = props.get("PathPart")
        api_id = props.get("RestApiId")
        parent_id = props.get("ParentId")
        id = props.get("Id") or short_uid()
        return apigw_models.Resource(id, region_name, api_id, path_part, parent_id)

    @classmethod
    def Method_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        props = cloudformation_json["Properties"]
        method_type = props.get("HttpMethod")
        authorization_type = props.get("AuthorizationType")
        return apigw_models.Method(method_type, authorization_type)

    apigw_models.RestAPI.create_from_cloudformation_json = (
        RestAPI_create_from_cloudformation_json
    )
    apigw_models.RestAPI.get_cfn_attribute = RestAPI_get_cfn_attribute
    apigw_models.Deployment.create_from_cloudformation_json = (
        Deployment_create_from_cloudformation_json
    )
    apigw_models.Resource.create_from_cloudformation_json = (
        Resource_create_from_cloudformation_json
    )
    apigw_models.Method.create_from_cloudformation_json = (
        Method_create_from_cloudformation_json
    )
    # TODO: add support for AWS::ApiGateway::Model, AWS::ApiGateway::RequestValidator, ...

    # fix AttributeError in moto's CloudFormation describe_stack_resource
    def describe_stack_resource(self):
        stack_name = self._get_param("StackName")
        stack = self.cloudformation_backend.get_stack(stack_name)
        logical_resource_id = self._get_param("LogicalResourceId")
        if not stack:
            msg = 'Unable to find CloudFormation stack "%s" in region %s' % (
                stack_name,
                aws_stack.get_region(),
            )
            if aws_stack.get_region() != self.region:
                msg = "%s/%s" % (msg, self.region)
            LOG.warning(msg)
            response = aws_responses.flask_error_response(
                msg, code=404, error_type="ResourceNotFoundException"
            )
            return 404, response.headers, response.data

        for stack_resource in stack.stack_resources:
            # Note: Line below has been patched
            # if stack_resource.logical_resource_id == logical_resource_id:
            if (
                stack_resource
                and stack_resource.logical_resource_id == logical_resource_id
            ):
                resource = stack_resource
                break
        else:
            raise ValidationError(logical_resource_id)

        template = self.response_template(
            responses.DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE
        )
        return template.render(stack=stack, resource=resource)

    responses.CloudFormationResponse.describe_stack_resource = describe_stack_resource

    # fix moto's describe_stack_events jinja2.exceptions.UndefinedError
    def cf_describe_stack_events(self):
        stack_name = self._get_param("StackName")
        backend = self.cloudformation_backend
        stack = backend.get_stack(stack_name)
        if not stack:
            # Also return stack events for deleted stacks, specified by stack name
            stack = (
                [
                    stk
                    for id, stk in backend.deleted_stacks.items()
                    if stk.name == stack_name
                ]
                or [0]
            )[0]
        if not stack:
            raise ValidationError(
                stack_name,
                message='Unable to find stack "%s" in region %s'
                % (stack_name, aws_stack.get_region()),
            )
        template = self.response_template(responses.DESCRIBE_STACK_EVENTS_RESPONSE)
        return template.render(stack=stack)

    responses.CloudFormationResponse.describe_stack_events = cf_describe_stack_events

    # fix Lambda regions in moto - see https://github.com/localstack/localstack/issues/1961
    for region in boto3.session.Session().get_available_regions("lambda"):
        if region not in lambda_models.lambda_backends:
            lambda_models.lambda_backends[region] = lambda_models.LambdaBackend(region)

    # patch FakeStack.initialize_resources
    def run_dependencies_deployment_loop(stack, action):
        def set_status(status):
            stack._add_stack_event(status)
            stack.status = status

        def run_loop(*args):
            # NOTE: We're adding this additional loop, as it seems that in some cases moto
            #   does not consider resource dependencies (e.g., if a "DependsOn" resource property
            #   is defined). This loop allows us to incrementally resolve such dependencies.
            resource_map = stack.resource_map
            unresolved = {}
            for i in range(MAX_DEPENDENCY_DEPTH):
                LOG.debug(
                    "Running CloudFormation stack deployment loop iteration %s"
                    % (i + 1)
                )
                unresolved = getattr(resource_map, "_unresolved_resources", {})
                if not unresolved:
                    set_status("%s_COMPLETE" % action)
                    return resource_map
                resource_map._unresolved_resources = {}
                for resource_id, resource_details in unresolved.items():
                    # Re-trigger the resource creation
                    parse_and_create_resource(*resource_details, force_create=True)
                if unresolved.keys() == resource_map._unresolved_resources.keys():
                    # looks like no more resources can be resolved -> bail
                    LOG.warning(
                        "Unresolvable dependencies, there may be undeployed stack resources: %s"
                        % unresolved
                    )
                    break
            set_status("%s_FAILED" % action)
            raise Exception(
                "Unable to resolve all CloudFormation resources after traversing "
                + "dependency tree (maximum depth %s reached): %s"
                % (MAX_DEPENDENCY_DEPTH, list(unresolved.keys()))
            )

        # NOTE: We're running the loop in the background, as it might take some time to complete
        FuncThread(run_loop).start()

    def initialize_resources(self):
        self.resource_map._template = self.resource_map._template or self.template_dict
        self.resource_map.load()
        self.resource_map.create(self.template_dict)
        self.output_map.create()
        run_dependencies_deployment_loop(self, "CREATE")

    def update(self, *args, **kwargs):
        stack_update_orig(self, *args, **kwargs)
        run_dependencies_deployment_loop(self, "UPDATE")

    FakeStack.initialize_resources = initialize_resources
    stack_update_orig = FakeStack.update
    FakeStack.update = update

    # patch Kinesis Stream get_cfn_attribute(..) method in moto
    def Kinesis_Stream_get_cfn_attribute(self, attribute_name):
        if attribute_name == "Arn":
            return self.arn

        raise UnformattedGetAttTemplateException()

    kinesis_models.Stream.get_cfn_attribute = Kinesis_Stream_get_cfn_attribute

    # patch cloudformation backend create_change_set(..)
    # #760 cloudformation deploy invalid xml error
    cloudformation_backend_create_change_set_orig = (
        CloudFormationBackend.create_change_set
    )

    def cloudformation_backend_create_change_set(
        self,
        stack_name,
        change_set_name,
        template,
        parameters,
        region_name,
        change_set_type,
        notification_arns=None,
        tags=None,
        role_arn=None,
    ):
        change_set_id, _ = cloudformation_backend_create_change_set_orig(
            self,
            stack_name,
            change_set_name,
            template,
            parameters,
            region_name,
            change_set_type,
            notification_arns,
            tags,
            role_arn,
        )
        change_set = self.change_sets[change_set_id]
        change_set.status = "CREATE_COMPLETE"

        return change_set_id, _

    CloudFormationBackend.create_change_set = cloudformation_backend_create_change_set