diff --git a/.github/CLA.md b/.github/CLA.md index fb829b3434191..840ddc9fdb05c 100644 --- a/.github/CLA.md +++ b/.github/CLA.md @@ -2,7 +2,7 @@ This license is for your protection as a Contributor as well as the protection of the maintainers of the LocalStack software; it does not change your rights to use your own Contributions for any other purpose. In the following, the maintainers of LocalStack are referred to as "LocalStack". -You accept and agree to the following terms and conditions for Your present and future Contributions submitted to "LocalStack". Except for the license granted herein to LocalSack and recipients of software distributed by "LocalStack", You reserve all right, title, and interest in and to Your Contributions. +You accept and agree to the following terms and conditions for Your present and future Contributions submitted to "LocalStack". Except for the license granted herein to LocalStack and recipients of software distributed by "LocalStack", You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. diff --git a/.github/workflows/asf-updates.yml b/.github/workflows/asf-updates.yml index 2e69486228ddb..45d20e220dab0 100644 --- a/.github/workflows/asf-updates.yml +++ b/.github/workflows/asf-updates.yml @@ -19,11 +19,9 @@ jobs: sudo apt-get update sudo apt-get install jq - - name: Set up Python 3.11 + - name: Set up Python id: setup-python uses: actions/setup-python@v6 - with: - python-version: '3.11' - name: Install release helper dependencies run: pip install --upgrade setuptools setuptools_scm uv @@ -127,6 +125,6 @@ jobs: author: "LocalStack Bot " committer: "LocalStack Bot " commit-message: "update generated ASF APIs to latest version" - labels: "area: asf, area: dependencies, semver: patch, skip-docs" + labels: "area: asf, area: dependencies, semver: patch, docs: skip, notes: skip" token: ${{ secrets.PRO_ACCESS_TOKEN }} reviewers: silv-io,alexrashed diff --git a/.github/workflows/aws-main.yml b/.github/workflows/aws-main.yml index 38fa32bf8c0e1..f948790bdb46b 100644 --- a/.github/workflows/aws-main.yml +++ b/.github/workflows/aws-main.yml @@ -105,7 +105,6 @@ jobs: - name: Set up Python uses: actions/setup-python@v6 with: - python-version-file: '.python-version' cache: 'pip' cache-dependency-path: 'requirements-dev.txt' diff --git a/.github/workflows/aws-tests-s3-image.yml b/.github/workflows/aws-tests-s3-image.yml index 4831c2828b809..1adc43f049685 100644 --- a/.github/workflows/aws-tests-s3-image.yml +++ b/.github/workflows/aws-tests-s3-image.yml @@ -115,8 +115,6 @@ jobs: - name: Set up Python uses: actions/setup-python@v6 - with: - python-version: '3.11' - name: Install docker build dependencies run: pip install --upgrade setuptools setuptools_scm diff --git a/.github/workflows/aws-tests.yml b/.github/workflows/aws-tests.yml index e94323b79fd95..98cf8fc61c84f 100644 --- a/.github/workflows/aws-tests.yml +++ b/.github/workflows/aws-tests.yml @@ -281,7 +281,7 @@ jobs: - 'tests/aws/services/sns/**' # todo: potentially add more locations (lambda/sqs tests?) - name: Run Unit Tests - timeout-minutes: 8 + timeout-minutes: 20 env: # add the GitHub API token to avoid rate limit issues GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/marker-report.yml b/.github/workflows/marker-report.yml index 3b5bc73173cd1..d978bb31f966d 100644 --- a/.github/workflows/marker-report.yml +++ b/.github/workflows/marker-report.yml @@ -45,8 +45,6 @@ jobs: - name: Set up Python id: setup-python uses: actions/setup-python@v6 - with: - python-version: "3.11" - name: Cache LocalStack community dependencies (venv) uses: actions/cache@v4 diff --git a/.github/workflows/pr-enforce-docs-labels.yml b/.github/workflows/pr-enforce-docs-labels.yml deleted file mode 100644 index 5a8de216c6255..0000000000000 --- a/.github/workflows/pr-enforce-docs-labels.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: Enforce Docs Labels - -on: - pull_request_target: - types: [labeled, unlabeled, opened, edited, synchronize] - -jobs: - enforce-docs-labels: - uses: localstack/meta/.github/workflows/pr-enforce-docs-labels.yml@main - secrets: - github-token: ${{ secrets.PRO_ACCESS_TOKEN }} diff --git a/.github/workflows/pr-enforce-pr-labels.yml b/.github/workflows/pr-enforce-pr-labels.yml index b316c45845360..8ccbf1e8321ab 100644 --- a/.github/workflows/pr-enforce-pr-labels.yml +++ b/.github/workflows/pr-enforce-pr-labels.yml @@ -1,11 +1,10 @@ -name: Enforce PR Labels - +name: Enforce Labels on: pull_request_target: - types: [labeled, unlabeled, opened, edited, synchronize] + types: [labeled, unlabeled, opened] jobs: - enforce-semver-labels: - uses: localstack/meta/.github/workflows/pr-enforce-semver-labels.yml@main + labels: + uses: localstack/meta/.github/workflows/pr-enforce-labels.yml@main secrets: github-token: ${{ secrets.PRO_ACCESS_TOKEN }} diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml index cbfa316871909..ab2562ad0adbd 100644 --- a/.github/workflows/sync-labels.yml +++ b/.github/workflows/sync-labels.yml @@ -10,6 +10,6 @@ jobs: sync-labels: uses: localstack/meta/.github/workflows/sync-labels.yml@main with: - categories: status,aws,semver,docs + categories: status,aws,semver,docs,notes secrets: github-token: ${{ secrets.PRO_ACCESS_TOKEN }} diff --git a/.github/workflows/tests-bin.yml b/.github/workflows/tests-bin.yml index 863e15989f24d..1e207b372dca6 100644 --- a/.github/workflows/tests-bin.yml +++ b/.github/workflows/tests-bin.yml @@ -30,8 +30,6 @@ jobs: - name: Set up Python uses: actions/setup-python@v6 - with: - python-version: '3.11' - name: Install helper script dependencies run: pip install --upgrade setuptools setuptools_scm diff --git a/.github/workflows/tests-podman.yml b/.github/workflows/tests-podman.yml index f16529a79a780..a12f6f11493e1 100644 --- a/.github/workflows/tests-podman.yml +++ b/.github/workflows/tests-podman.yml @@ -41,8 +41,6 @@ jobs: - name: Set up Python uses: actions/setup-python@v6 - with: - python-version: "3.11" - name: Install podman and test dependencies run: | diff --git a/.github/workflows/tests-pro-integration.yml b/.github/workflows/tests-pro-integration.yml index af1758a652b24..a428f76cd8ad2 100644 --- a/.github/workflows/tests-pro-integration.yml +++ b/.github/workflows/tests-pro-integration.yml @@ -209,24 +209,19 @@ jobs: id: setup-python uses: actions/setup-python@v6 with: - python-version: '3.11' + python-version-file: 'localstack/.python-version' - name: Set up Node 22.x uses: actions/setup-node@v5 with: node-version: 22.x - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '21' distribution: 'temurin' - - name: Set up Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: 0.13.7 - - name: Install OS packages run: | sudo apt-get update diff --git a/.github/workflows/update-cfn-resources.yml b/.github/workflows/update-cfn-resources.yml new file mode 100644 index 0000000000000..91957da968339 --- /dev/null +++ b/.github/workflows/update-cfn-resources.yml @@ -0,0 +1,60 @@ +name: Update CloudFormation Resources + +on: + schedule: + - cron: '0 6 * * TUE' + workflow_dispatch: + +jobs: + update-cfn-resources: + name: Update CloudFormation resources metadata + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version-file: '.python-version' + + - name: Install uv + uses: astral-sh/setup-uv@v6 + + - name: Determine boto dependency pins + id: boto_versions + run: | + BOTO3_VERSION=$(grep '^boto3==' requirements-base-runtime.txt | head -n1 | sed 's/^boto3==//') + BOTOCORE_VERSION=$(grep '^botocore==' requirements-base-runtime.txt | head -n1 | sed 's/^botocore==//') + echo "boto3_version=$BOTO3_VERSION" >> "$GITHUB_OUTPUT" + echo "botocore_version=$BOTOCORE_VERSION" >> "$GITHUB_OUTPUT" + + - name: Run CloudFormation resource updater + id: run-script + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: us-east-1 + run: | + uv run --no-project --no-config \ + --with boto3==${{ steps.boto_versions.outputs.boto3_version }} \ + --with botocore==${{ steps.boto_versions.outputs.botocore_version }} \ + python scripts/update_cfn_resources.py --resource-file localstack-core/localstack/services/cloudformation/resources.py + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7 + with: + title: "Update CloudFormation resource availability" + body: "Automated update of CloudFormation resource metadata." + branch: "bot/update-cfn-resources" + commit-message: "chore: update CloudFormation resource metadata" + author: "LocalStack Bot " + committer: "LocalStack Bot " + labels: "area: cloudformation, semver: patch, docs: skip, notes: skip" + token: ${{ secrets.PRO_ACCESS_TOKEN }} + signoff: true diff --git a/.github/workflows/update-test-durations.yml b/.github/workflows/update-test-durations.yml index 93e6d1d64c372..e8a4a52b06508 100644 --- a/.github/workflows/update-test-durations.yml +++ b/.github/workflows/update-test-durations.yml @@ -72,5 +72,5 @@ jobs: commit-message: "CI: update .test_durations to latest version" path: localstack add-paths: .test_durations - labels: "semver: patch, area: testing, area: ci, skip-docs" + labels: "semver: patch, area: testing, area: ci, docs: skip, notes: skip" token: ${{ secrets.PRO_ACCESS_TOKEN }} diff --git a/.github/workflows/upgrade-python-dependencies.yml b/.github/workflows/upgrade-python-dependencies.yml index d4407d5513cf5..7d2a1ba8b5c87 100644 --- a/.github/workflows/upgrade-python-dependencies.yml +++ b/.github/workflows/upgrade-python-dependencies.yml @@ -12,4 +12,4 @@ jobs: secrets: github-token: ${{ secrets.PRO_ACCESS_TOKEN }} with: - labels: "area: dependencies, semver: patch, skip-docs" + labels: "area: dependencies, semver: patch, docs: skip, notes: skip" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 04450ca5e96e1..b2c5d9d556d3c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.13.0 + rev: v0.13.2 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] @@ -11,7 +11,7 @@ repos: - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.18.1 + rev: v1.18.2 hooks: - id: mypy entry: bash -c 'cd localstack-core && mypy --install-types --non-interactive' diff --git a/.python-version b/.python-version index 2c0733315e415..24ee5b1be9961 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.11 +3.13 diff --git a/CODEOWNERS b/CODEOWNERS index 47d263c3a2bd9..d82443a8c02f6 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -235,9 +235,9 @@ /tests/unit/services/sqs/ @thrau @baermat @gregfurman # ssm -/localstack-core/localstack/aws/api/ssm/ @dominikschubert -/localstack-core/localstack/services/ssm/ @dominikschubert -/tests/aws/services/ssm/ @dominikschubert +/localstack-core/localstack/aws/api/ssm/ @viren-nadkarni @dominikschubert +/localstack-core/localstack/services/ssm/ @viren-nadkarni @dominikschubert +/tests/aws/services/ssm/ @viren-nadkarni @dominikschubert # stepfunctions /localstack-core/localstack/aws/api/stepfunctions/ @dominikschubert @joe4dev @gregfurman diff --git a/Dockerfile b/Dockerfile index 953f5bb77e4b0..58520b4fd97ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # # base: Stage which installs necessary runtime dependencies (OS packages, etc.) # -FROM python:3.11.13-slim-bookworm@sha256:873f91540d53b36327ed4fb018c9669107a4e2a676719720edb4209c4b15d029 AS base +FROM python:3.13.7-slim-trixie@sha256:58c30f5bfaa718b5803a53393190b9c68bd517c44c6c94c1b6c8c172bcfad040 AS base ARG TARGETARCH # Install runtime OS package dependencies @@ -161,9 +161,9 @@ RUN --mount=type=cache,target=/root/.cache \ chmod -R 777 /usr/lib/localstack # link the python package installer virtual environments into the localstack venv -RUN echo /var/lib/localstack/lib/python-packages/lib/python3.11/site-packages > localstack-var-python-packages-venv.pth && \ +RUN echo /var/lib/localstack/lib/python-packages/lib/python3.13/site-packages > localstack-var-python-packages-venv.pth && \ mv localstack-var-python-packages-venv.pth .venv/lib/python*/site-packages/ -RUN echo /usr/lib/localstack/python-packages/lib/python3.11/site-packages > localstack-static-python-packages-venv.pth && \ +RUN echo /usr/lib/localstack/python-packages/lib/python3.13/site-packages > localstack-static-python-packages-venv.pth && \ mv localstack-static-python-packages-venv.pth .venv/lib/python*/site-packages/ # expose edge service, external service ports, and debugpy diff --git a/Dockerfile.s3 b/Dockerfile.s3 index ba236ebd88837..7292f13942686 100644 --- a/Dockerfile.s3 +++ b/Dockerfile.s3 @@ -1,5 +1,5 @@ # base: Stage which installs necessary runtime dependencies (OS packages, filesystem...) -FROM python:3.11.13-slim-bookworm@sha256:873f91540d53b36327ed4fb018c9669107a4e2a676719720edb4209c4b15d029 AS base +FROM python:3.13.7-slim-trixie@sha256:58c30f5bfaa718b5803a53393190b9c68bd517c44c6c94c1b6c8c172bcfad040 AS base ARG TARGETARCH # set workdir @@ -66,7 +66,7 @@ RUN --mount=type=cache,target=/root/.cache \ # delete the botocore specs for other services (>80mb) # TODO: well now it's compressed and it's much lighter: 20mb maybe not worth it -RUN find .venv/lib/python3.11/site-packages/botocore/data/ -mindepth 1 -maxdepth 1 -type d -not -name s3 -exec rm -rf '{}' \; +RUN find .venv/lib/python3.13/site-packages/botocore/data/ -mindepth 1 -maxdepth 1 -type d -not -name s3 -exec rm -rf '{}' \; # final stage: Builds upon base stage and copies resources from builder stages @@ -97,9 +97,9 @@ RUN SETUPTOOLS_SCM_PRETEND_VERSION_FOR_LOCALSTACK_CORE=${LOCALSTACK_BUILD_VERSIO RUN . .venv/bin/activate && python3 -m localstack.aws.spec # link the python package installer virtual environments into the localstack venv -RUN echo /var/lib/localstack/lib/python-packages/lib/python3.11/site-packages > localstack-var-python-packages-venv.pth && \ +RUN echo /var/lib/localstack/lib/python-packages/lib/python3.13/site-packages > localstack-var-python-packages-venv.pth && \ mv localstack-var-python-packages-venv.pth .venv/lib/python*/site-packages/ -RUN echo /usr/lib/localstack/python-packages/lib/python3.11/site-packages > localstack-static-python-packages-venv.pth && \ +RUN echo /usr/lib/localstack/python-packages/lib/python3.13/site-packages > localstack-static-python-packages-venv.pth && \ mv localstack-static-python-packages-venv.pth .venv/lib/python*/site-packages/ # expose edge service and debugpy diff --git a/localstack-core/localstack/aws/api/cloudwatch/__init__.py b/localstack-core/localstack/aws/api/cloudwatch/__init__.py index f038e8af7ede0..5739823f29805 100644 --- a/localstack-core/localstack/aws/api/cloudwatch/__init__.py +++ b/localstack-core/localstack/aws/api/cloudwatch/__init__.py @@ -218,7 +218,7 @@ class ConcurrentModificationException(ServiceException): class ConflictException(ServiceException): code: str = "ConflictException" sender_fault: bool = False - status_code: int = 400 + status_code: int = 409 class DashboardValidationMessage(TypedDict, total=False): diff --git a/localstack-core/localstack/aws/api/config/__init__.py b/localstack-core/localstack/aws/api/config/__init__.py index 13e5026cd3aba..c4befd59cdab4 100644 --- a/localstack-core/localstack/aws/api/config/__init__.py +++ b/localstack-core/localstack/aws/api/config/__init__.py @@ -291,12 +291,16 @@ class RemediationExecutionState(StrEnum): IN_PROGRESS = "IN_PROGRESS" SUCCEEDED = "SUCCEEDED" FAILED = "FAILED" + UNKNOWN = "UNKNOWN" class RemediationExecutionStepState(StrEnum): SUCCEEDED = "SUCCEEDED" PENDING = "PENDING" FAILED = "FAILED" + IN_PROGRESS = "IN_PROGRESS" + EXITED = "EXITED" + UNKNOWN = "UNKNOWN" class RemediationTargetType(StrEnum): diff --git a/localstack-core/localstack/aws/api/core.py b/localstack-core/localstack/aws/api/core.py index 0498ebaf7473f..d05ef5125b586 100644 --- a/localstack-core/localstack/aws/api/core.py +++ b/localstack-core/localstack/aws/api/core.py @@ -13,6 +13,7 @@ from rolo.gateway import RequestContext as RoloRequestContext from localstack.aws.connect import InternalRequestParameters +from localstack.aws.spec import ProtocolName from localstack.http import Request, Response from localstack.utils.strings import long_uid @@ -88,6 +89,8 @@ class RequestContext(RoloRequestContext): """The underlying incoming HTTP request.""" service: ServiceModel | None """The botocore ServiceModel of the service the request is made to.""" + protocol: ProtocolName | None + """The botocore Protocol for the service the request is made to.""" operation: OperationModel | None """The botocore OperationModel of the AWS operation being invoked.""" region: str @@ -112,6 +115,7 @@ class RequestContext(RoloRequestContext): def __init__(self, request: Request): super().__init__(request) self.service = None + self.protocol = None self.operation = None self.region = None # type: ignore[assignment] # type=str, because we know it will always be set downstream self.partition = "aws" # Sensible default - will be overwritten by region-handler diff --git a/localstack-core/localstack/aws/api/ec2/__init__.py b/localstack-core/localstack/aws/api/ec2/__init__.py index 6476f278b3cbf..6d4e1980f8ddb 100644 --- a/localstack-core/localstack/aws/api/ec2/__init__.py +++ b/localstack-core/localstack/aws/api/ec2/__init__.py @@ -171,6 +171,8 @@ Hour = int IamInstanceProfileAssociationId = str ImageId = str +ImageName = str +ImageNameRequest = str ImageProvider = str ImageProviderRequest = str ImageUsageReportId = str @@ -237,12 +239,16 @@ LocalGatewayVirtualInterfaceId = str Location = str MacModificationTaskId = str +MarketplaceProductCode = str +MarketplaceProductCodeRequest = str MaxIpv4AddrPerInterface = int MaxIpv6AddrPerInterface = int MaxNetworkInterfaces = int MaxResults = int MaxResultsParam = int MaximumBandwidthInMbps = int +MaximumDaysSinceCreatedValue = int +MaximumDaysSinceDeprecatedValue = int MaximumEbsAttachments = int MaximumEfaInterfaces = int MaximumEnaQueueCount = int @@ -2439,6 +2445,20 @@ class InstanceType(StrEnum): i8ge_48xlarge = "i8ge.48xlarge" i8ge_metal_24xl = "i8ge.metal-24xl" i8ge_metal_48xl = "i8ge.metal-48xl" + mac_m4_metal = "mac-m4.metal" + mac_m4pro_metal = "mac-m4pro.metal" + r8gn_medium = "r8gn.medium" + r8gn_large = "r8gn.large" + r8gn_xlarge = "r8gn.xlarge" + r8gn_2xlarge = "r8gn.2xlarge" + r8gn_4xlarge = "r8gn.4xlarge" + r8gn_8xlarge = "r8gn.8xlarge" + r8gn_12xlarge = "r8gn.12xlarge" + r8gn_16xlarge = "r8gn.16xlarge" + r8gn_24xlarge = "r8gn.24xlarge" + r8gn_48xlarge = "r8gn.48xlarge" + r8gn_metal_24xl = "r8gn.metal-24xl" + r8gn_metal_48xl = "r8gn.metal-48xl" class InstanceTypeHypervisor(StrEnum): @@ -10692,6 +10712,14 @@ class CreateVpnGatewayResult(TypedDict, total=False): VpnGateway: Optional[VpnGateway] +class CreationDateCondition(TypedDict, total=False): + MaximumDaysSinceCreated: Optional[MaximumDaysSinceCreatedValue] + + +class CreationDateConditionRequest(TypedDict, total=False): + MaximumDaysSinceCreated: Optional[MaximumDaysSinceCreatedValue] + + CustomerGatewayIdStringList = List[CustomerGatewayId] CustomerGatewayList = List[CustomerGateway] @@ -11545,6 +11573,14 @@ class DeleteVpnGatewayRequest(ServiceRequest): DryRun: Optional[Boolean] +class DeprecationTimeCondition(TypedDict, total=False): + MaximumDaysSinceDeprecated: Optional[MaximumDaysSinceDeprecatedValue] + + +class DeprecationTimeConditionRequest(TypedDict, total=False): + MaximumDaysSinceDeprecated: Optional[MaximumDaysSinceDeprecatedValue] + + class DeprovisionByoipCidrRequest(ServiceRequest): Cidr: String DryRun: Optional[Boolean] @@ -17391,11 +17427,17 @@ class GetAllowedImagesSettingsRequest(ServiceRequest): DryRun: Optional[Boolean] +ImageNameList = List[ImageName] +MarketplaceProductCodeList = List[MarketplaceProductCode] ImageProviderList = List[ImageProvider] class ImageCriterion(TypedDict, total=False): ImageProviders: Optional[ImageProviderList] + MarketplaceProductCodes: Optional[MarketplaceProductCodeList] + ImageNames: Optional[ImageNameList] + DeprecationTimeCondition: Optional[DeprecationTimeCondition] + CreationDateCondition: Optional[CreationDateCondition] ImageCriterionList = List[ImageCriterion] @@ -18495,11 +18537,17 @@ class ImageAttribute(TypedDict, total=False): BlockDeviceMappings: Optional[BlockDeviceMappingList] +ImageNameRequestList = List[ImageNameRequest] +MarketplaceProductCodeRequestList = List[MarketplaceProductCodeRequest] ImageProviderRequestList = List[ImageProviderRequest] class ImageCriterionRequest(TypedDict, total=False): ImageProviders: Optional[ImageProviderRequestList] + MarketplaceProductCodes: Optional[MarketplaceProductCodeRequestList] + ImageNames: Optional[ImageNameRequestList] + DeprecationTimeCondition: Optional[DeprecationTimeConditionRequest] + CreationDateCondition: Optional[CreationDateConditionRequest] ImageCriterionRequestList = List[ImageCriterionRequest] diff --git a/localstack-core/localstack/aws/api/logs/__init__.py b/localstack-core/localstack/aws/api/logs/__init__.py index a47bc36320bfd..bbc3a8d608d6d 100644 --- a/localstack-core/localstack/aws/api/logs/__init__.py +++ b/localstack-core/localstack/aws/api/logs/__init__.py @@ -57,6 +57,7 @@ FieldDelimiter = str FieldHeader = str FieldIndexName = str +FieldSelectionCriteria = str FilterCount = int FilterName = str FilterPattern = str @@ -137,6 +138,7 @@ StartFromHead = bool StatsValue = float Success = bool +SystemField = str TagKey = str TagValue = str Target = str @@ -1139,6 +1141,7 @@ class DescribeMetricFiltersRequest(ServiceRequest): metricNamespace: Optional[MetricNamespace] +EmitSystemFields = List[SystemField] Dimensions = Dict[DimensionsKey, DimensionsValue] @@ -1161,6 +1164,8 @@ class MetricFilter(TypedDict, total=False): creationTime: Optional[Timestamp] logGroupName: Optional[LogGroupName] applyOnTransformedLogs: Optional[ApplyOnTransformedLogs] + fieldSelectionCriteria: Optional[FieldSelectionCriteria] + emitSystemFieldDimensions: Optional[EmitSystemFields] MetricFilters = List[MetricFilter] @@ -1263,6 +1268,8 @@ class SubscriptionFilter(TypedDict, total=False): distribution: Optional[Distribution] applyOnTransformedLogs: Optional[ApplyOnTransformedLogs] creationTime: Optional[Timestamp] + fieldSelectionCriteria: Optional[FieldSelectionCriteria] + emitSystemFields: Optional[EmitSystemFields] SubscriptionFilters = List[SubscriptionFilter] @@ -2032,6 +2039,8 @@ class PutMetricFilterRequest(ServiceRequest): filterPattern: FilterPattern metricTransformations: MetricTransformations applyOnTransformedLogs: Optional[ApplyOnTransformedLogs] + fieldSelectionCriteria: Optional[FieldSelectionCriteria] + emitSystemFieldDimensions: Optional[EmitSystemFields] class PutQueryDefinitionRequest(ServiceRequest): @@ -2072,6 +2081,8 @@ class PutSubscriptionFilterRequest(ServiceRequest): roleArn: Optional[RoleArn] distribution: Optional[Distribution] applyOnTransformedLogs: Optional[ApplyOnTransformedLogs] + fieldSelectionCriteria: Optional[FieldSelectionCriteria] + emitSystemFields: Optional[EmitSystemFields] class PutTransformerRequest(ServiceRequest): @@ -2929,6 +2940,8 @@ def put_metric_filter( filter_pattern: FilterPattern, metric_transformations: MetricTransformations, apply_on_transformed_logs: ApplyOnTransformedLogs | None = None, + field_selection_criteria: FieldSelectionCriteria | None = None, + emit_system_field_dimensions: EmitSystemFields | None = None, **kwargs, ) -> None: raise NotImplementedError @@ -2980,6 +2993,8 @@ def put_subscription_filter( role_arn: RoleArn | None = None, distribution: Distribution | None = None, apply_on_transformed_logs: ApplyOnTransformedLogs | None = None, + field_selection_criteria: FieldSelectionCriteria | None = None, + emit_system_fields: EmitSystemFields | None = None, **kwargs, ) -> None: raise NotImplementedError diff --git a/localstack-core/localstack/aws/api/redshift/__init__.py b/localstack-core/localstack/aws/api/redshift/__init__.py index 1bcc3ad7816ad..a2d440b99b8e9 100644 --- a/localstack-core/localstack/aws/api/redshift/__init__.py +++ b/localstack-core/localstack/aws/api/redshift/__init__.py @@ -1985,6 +1985,9 @@ class CreateIntegrationMessage(ServiceRequest): Description: Optional[IntegrationDescription] +TagKeyList = List[String] + + class ReadWriteAccess(TypedDict, total=False): Authorization: ServiceAuthorization @@ -2023,6 +2026,8 @@ class CreateRedshiftIdcApplicationMessage(ServiceRequest): IamRoleArn: String AuthorizedTokenIssuerList: Optional[AuthorizedTokenIssuerList] ServiceIntegrations: Optional[ServiceIntegrationList] + Tags: Optional[TagList] + SsoTagKeys: Optional[TagKeyList] class RedshiftIdcApplication(TypedDict, total=False): @@ -2036,6 +2041,8 @@ class RedshiftIdcApplication(TypedDict, total=False): IdcOnboardStatus: Optional[String] AuthorizedTokenIssuerList: Optional[AuthorizedTokenIssuerList] ServiceIntegrations: Optional[ServiceIntegrationList] + Tags: Optional[TagList] + SsoTagKeys: Optional[TagKeyList] class CreateRedshiftIdcApplicationResult(TypedDict, total=False): @@ -2247,9 +2254,6 @@ class DeleteSnapshotScheduleMessage(ServiceRequest): ScheduleIdentifier: String -TagKeyList = List[String] - - class DeleteTagsMessage(ServiceRequest): ResourceName: String TagKeys: TagKeyList @@ -3914,6 +3918,8 @@ def create_redshift_idc_application( identity_namespace: IdentityNamespaceString | None = None, authorized_token_issuer_list: AuthorizedTokenIssuerList | None = None, service_integrations: ServiceIntegrationList | None = None, + tags: TagList | None = None, + sso_tag_keys: TagKeyList | None = None, **kwargs, ) -> CreateRedshiftIdcApplicationResult: raise NotImplementedError diff --git a/localstack-core/localstack/aws/api/s3control/__init__.py b/localstack-core/localstack/aws/api/s3control/__init__.py index c6142822bef7b..84490dc574503 100644 --- a/localstack-core/localstack/aws/api/s3control/__init__.py +++ b/localstack-core/localstack/aws/api/s3control/__init__.py @@ -65,6 +65,7 @@ MultiRegionAccessPointId = str MultiRegionAccessPointName = str NoSuchPublicAccessBlockConfigurationMessage = str +NonEmptyKmsKeyArnString = str NonEmptyMaxLength1024String = str NonEmptyMaxLength2048String = str NonEmptyMaxLength256String = str @@ -902,6 +903,36 @@ class CreateBucketResult(TypedDict, total=False): BucketArn: Optional[S3RegionalBucketArn] +class NotSSEFilter(TypedDict, total=False): + pass + + +class SSECFilter(TypedDict, total=False): + pass + + +class DSSEKMSFilter(TypedDict, total=False): + KmsKeyArn: Optional[NonEmptyKmsKeyArnString] + + +class SSEKMSFilter(TypedDict, total=False): + KmsKeyArn: Optional[NonEmptyKmsKeyArnString] + BucketKeyEnabled: Optional[Boolean] + + +class SSES3Filter(TypedDict, total=False): + pass + + +class ObjectEncryptionFilter(TypedDict, total=False): + SSES3: Optional[SSES3Filter] + SSEKMS: Optional[SSEKMSFilter] + DSSEKMS: Optional[DSSEKMSFilter] + SSEC: Optional[SSECFilter] + NOTSSE: Optional[NotSSEFilter] + + +ObjectEncryptionFilterList = List[ObjectEncryptionFilter] StorageClassList = List[S3StorageClass] ObjectSizeLessThanBytes = int ObjectSizeGreaterThanBytes = int @@ -927,6 +958,7 @@ class JobManifestGeneratorFilter(TypedDict, total=False): ObjectSizeGreaterThanBytes: Optional[ObjectSizeGreaterThanBytes] ObjectSizeLessThanBytes: Optional[ObjectSizeLessThanBytes] MatchAnyStorageClass: Optional[StorageClassList] + MatchAnyObjectEncryption: Optional[ObjectEncryptionFilterList] class SSEKMSEncryption(TypedDict, total=False): diff --git a/localstack-core/localstack/aws/api/ssm/__init__.py b/localstack-core/localstack/aws/api/ssm/__init__.py index bf32cd2834bc2..5955484431472 100644 --- a/localstack-core/localstack/aws/api/ssm/__init__.py +++ b/localstack-core/localstack/aws/api/ssm/__init__.py @@ -4426,6 +4426,7 @@ class GetDeployablePatchSnapshotForInstanceRequest(ServiceRequest): InstanceId: InstanceId SnapshotId: SnapshotId BaselineOverride: Optional[BaselineOverride] + UseS3DualStackEndpoint: Optional[Boolean] class GetDeployablePatchSnapshotForInstanceResult(TypedDict, total=False): @@ -6749,6 +6750,7 @@ def get_deployable_patch_snapshot_for_instance( instance_id: InstanceId, snapshot_id: SnapshotId, baseline_override: BaselineOverride | None = None, + use_s3_dual_stack_endpoint: Boolean | None = None, **kwargs, ) -> GetDeployablePatchSnapshotForInstanceResult: raise NotImplementedError diff --git a/localstack-core/localstack/aws/client.py b/localstack-core/localstack/aws/client.py index b372d8c2f5914..3077592ef11cb 100644 --- a/localstack-core/localstack/aws/client.py +++ b/localstack-core/localstack/aws/client.py @@ -21,6 +21,7 @@ from .api import CommonServiceException, RequestContext, ServiceException, ServiceResponse from .connect import get_service_endpoint from .gateway import Gateway +from .spec import ProtocolName LOG = logging.getLogger(__name__) @@ -284,13 +285,17 @@ def _patch_botocore_endpoint_in_memory(): def parse_response( - operation: OperationModel, response: Response, include_response_metadata: bool = True + operation: OperationModel, + protocol: ProtocolName, + response: Response, + include_response_metadata: bool = True, ) -> ServiceResponse: """ Parses an HTTP Response object into an AWS response object using botocore. It does this by adapting the procedure of ``botocore.endpoint.convert_to_response_dict`` to work with Werkzeug's server-side response object. :param operation: the operation of the original request + :param protocol: the protocol of the original request :param response: the HTTP response object containing the response of the operation :param include_response_metadata: True if the ResponseMetadata (typical for boto response dicts) should be included :return: a parsed dictionary as it is returned by botocore @@ -322,7 +327,7 @@ def parse_response( timestamp_parser=_cbor_timestamp_parser, blob_parser=_cbor_blob_parser ) - parser = factory.create_parser(operation.service_model.protocol) + parser = factory.create_parser(protocol) parsed_response = parser.parse(response_dict, operation.output_shape) if response.status_code >= 301: diff --git a/localstack-core/localstack/aws/forwarder.py b/localstack-core/localstack/aws/forwarder.py index f368f3f1ebce9..b0f64817587da 100644 --- a/localstack-core/localstack/aws/forwarder.py +++ b/localstack-core/localstack/aws/forwarder.py @@ -8,6 +8,8 @@ from botocore.awsrequest import AWSPreparedRequest, prepare_request_dict from botocore.config import Config as BotoConfig +from botocore.model import OperationModel +from botocore.serialize import create_serializer from werkzeug.datastructures import Headers from localstack.aws.api.core import ( @@ -19,7 +21,7 @@ from localstack.aws.client import create_http_request, parse_response, raise_service_exception from localstack.aws.connect import connect_to from localstack.aws.skeleton import DispatchTable, create_dispatch_table -from localstack.aws.spec import load_service +from localstack.aws.spec import ProtocolName, load_service from localstack.constants import AWS_REGION_US_EAST_1 from localstack.http import Response from localstack.http.proxy import Proxy @@ -79,7 +81,7 @@ def forward( if not self.parse_response: return http_response parsed_response = parse_response( - context.operation, http_response, self.include_response_metadata + context.operation, context.protocol, http_response, self.include_response_metadata ) raise_service_exception(http_response, parsed_response) return parsed_response @@ -90,6 +92,7 @@ def new_request_context(self, original: RequestContext, service_request: Service action=original.operation.name, parameters=service_request, region=original.region, + protocol=original.protocol, ) # update the newly created context with non-payload specific request headers (the payload can differ from # the original request, f.e. it could be JSON encoded now while the initial request was CBOR encoded) @@ -184,7 +187,9 @@ def dispatch_to_backend( :raises ServiceException: if the dispatcher returned an error response """ http_response = http_request_dispatcher(context) - parsed_response = parse_response(context.operation, http_response, include_response_metadata) + parsed_response = parse_response( + context.operation, context.protocol, http_response, include_response_metadata + ) raise_service_exception(http_response, parsed_response) return parsed_response @@ -196,6 +201,7 @@ def dispatch_to_backend( def create_aws_request_context( service_name: str, action: str, + protocol: ProtocolName = None, parameters: Mapping[str, Any] = None, region: str = None, endpoint_url: str | None = None, @@ -210,6 +216,7 @@ def create_aws_request_context( :param service_name: the AWS service :param action: the action to invoke + :param protocol: the protocol to use :param parameters: the invocation parameters :param region: the region name (default is us-east-1) :param endpoint_url: the endpoint to call (defaults to localstack) @@ -222,6 +229,8 @@ def create_aws_request_context( service = load_service(service_name) operation = service.operation_model(action) + # TODO: remove this once every usage upstream has been removed + protocol = protocol or service.resolved_protocol # we re-use botocore internals here to serialize the HTTP request, # but deactivate validation (validation errors should be handled by the backend) @@ -243,8 +252,14 @@ def create_aws_request_context( endpoint_url = "http://localhost.localstack.cloud" # pre-process the request args (some params are modified using botocore event handlers) parameters = client._emit_api_params(parameters, operation, request_context) - request_dict = client._convert_to_request_dict( - parameters, operation, endpoint_url, context=request_context + + request_dict = _convert_to_request_dict_with_protocol( + client=client, + protocol=protocol, + api_params=parameters, + operation_model=operation, + endpoint_url=endpoint_url, + context=request_context, ) if auth_path := request_dict.get("auth_path"): @@ -266,7 +281,39 @@ def create_aws_request_context( context = RequestContext(request=create_http_request(aws_request)) context.service = service context.operation = operation + context.protocol = protocol context.region = region context.service_request = parameters return context + + +def _convert_to_request_dict_with_protocol( + client, + protocol: ProtocolName, + api_params: dict, + operation_model: OperationModel, + endpoint_url: str, + context: dict, + set_user_agent_header: bool = True, +) -> dict: + """ + This function is taken from botocore Client._convert_to_request_dict, but we are overriding the serializer + Botocore does not expose a way to create a client with a specific protocol, but we need this functionality + to support multi-protocols. + """ + serializer = create_serializer(protocol, include_validation=False) + request_dict = serializer.serialize_to_request(api_params, operation_model) + if not client._client_config.inject_host_prefix: + request_dict.pop("host_prefix", None) + if set_user_agent_header: + user_agent = client._user_agent_creator.to_string() + else: + user_agent = None + prepare_request_dict( + request_dict, + endpoint_url=endpoint_url, + user_agent=user_agent, + context=context, + ) + return request_dict diff --git a/localstack-core/localstack/aws/handlers/analytics.py b/localstack-core/localstack/aws/handlers/analytics.py index b0cbada8b1e17..a824b4a9b24d2 100644 --- a/localstack-core/localstack/aws/handlers/analytics.py +++ b/localstack-core/localstack/aws/handlers/analytics.py @@ -60,7 +60,7 @@ def _get_err_type(self, context: RequestContext, response: Response) -> str | No if context.service_exception: return context.service_exception.code - response = parse_response(context.operation, response) + response = parse_response(context.operation, context.protocol, response) return response["Error"]["Code"] except Exception: if config.DEBUG_ANALYTICS: diff --git a/localstack-core/localstack/aws/handlers/service.py b/localstack-core/localstack/aws/handlers/service.py index 40919c9d45a8e..4009f720386c7 100644 --- a/localstack-core/localstack/aws/handlers/service.py +++ b/localstack-core/localstack/aws/handlers/service.py @@ -9,15 +9,15 @@ from localstack import config from localstack.http import Response -from localstack.utils.coverage_docs import get_coverage_link_for_service +from ...utils.coverage_docs import get_coverage_link_for_service from ..api import CommonServiceException, RequestContext, ServiceException from ..api.core import ServiceOperation from ..chain import CompositeResponseHandler, ExceptionHandler, Handler, HandlerChain from ..client import parse_response, parse_service_exception from ..protocol.parser import RequestParser, create_parser from ..protocol.serializer import create_serializer -from ..protocol.service_router import determine_aws_service_model +from ..protocol.service_router import determine_aws_protocol, determine_aws_service_model from ..skeleton import Skeleton, create_skeleton LOG = logging.getLogger(__name__) @@ -33,6 +33,8 @@ def __call__(self, chain: HandlerChain, context: RequestContext, response: Respo # example). If it is already set, we can skip the parsing of the request. It is very important for S3, because # parsing the request will consume the data stream and prevent streaming. if context.service: + if not context.protocol: + context.protocol = determine_aws_protocol(context.request, context.service) return service_model = determine_aws_service_model(context.request) @@ -41,6 +43,7 @@ def __call__(self, chain: HandlerChain, context: RequestContext, response: Respo return context.service = service_model + context.protocol = determine_aws_protocol(context.request, service_model) class ServiceRequestParser(Handler): @@ -63,7 +66,7 @@ def __call__(self, chain: HandlerChain, context: RequestContext, response: Respo return self.parse_and_enrich(context) def parse_and_enrich(self, context: RequestContext): - parser = create_parser(context.service) + parser = create_parser(context.service, context.protocol) operation, instance = parser.parse(context.request) # enrich context @@ -137,7 +140,7 @@ def create_not_implemented_response(self, context): operation_name = operation.name message = f"no handler for operation '{operation_name}' on service '{service_name}'" error = CommonServiceException("InternalFailure", message, status_code=501) - serializer = create_serializer(context.service) + serializer = create_serializer(context.service, context.protocol) return serializer.serialize_error_to_response( error, operation, context.request.headers, context.request_id ) @@ -176,8 +179,8 @@ def create_exception_response(self, exception: Exception, context: RequestContex action_name = operation.name exception_message: str | None = exception.args[0] if exception.args else None message = exception_message or get_coverage_link_for_service(service_name, action_name) - LOG.info(message) error = CommonServiceException("InternalFailure", message, status_code=501) + LOG.info(message) context.service_exception = error elif not isinstance(exception, ServiceException): @@ -209,7 +212,7 @@ def create_exception_response(self, exception: Exception, context: RequestContex ).with_traceback(exception.__traceback__) context.service_exception = error - serializer = create_serializer(context.service) # TODO: serializer cache + serializer = create_serializer(context.service, context.protocol) return serializer.serialize_error_to_response( error, operation, context.request.headers, context.request_id ) @@ -252,7 +255,9 @@ def __call__(self, chain: HandlerChain, context: RequestContext, response: Respo return # in this case we need to parse the raw response - parsed = parse_response(context.operation, response, include_response_metadata=False) + parsed = parse_response( + context.operation, context.protocol, response, include_response_metadata=False + ) if service_exception := parse_service_exception(response, parsed): context.service_exception = service_exception else: diff --git a/localstack-core/localstack/aws/protocol/parser.py b/localstack-core/localstack/aws/protocol/parser.py index 3259f30784094..a2c7482898a88 100644 --- a/localstack-core/localstack/aws/protocol/parser.py +++ b/localstack-core/localstack/aws/protocol/parser.py @@ -19,22 +19,23 @@ │RequestParser│ └─────────────┘ ▲ ▲ ▲ - ┌─────────────────┘ │ └────────────────────┐ - ┌────────┴─────────┐ ┌─────────┴───────────┐ ┌──────────┴──────────┐ - │QueryRequestParser│ │BaseRestRequestParser│ │BaseJSONRequestParser│ - └──────────────────┘ └─────────────────────┘ └─────────────────────┘ - ▲ ▲ ▲ ▲ ▲ - ┌───────┴────────┐ ┌─────────┴──────────┐ │ │ │ - │EC2RequestParser│ │RestXMLRequestParser│ │ │ │ - └────────────────┘ └────────────────────┘ │ │ │ - ┌────────────────┴───┴┐ ┌────────┴────────┐ - │RestJSONRequestParser│ │JSONRequestParser│ - └─────────────────────┘ └─────────────────┘ + ┌─────────────────┘ │ └────────────────────┬───────────────────────┬───────────────────────┐ + ┌────────┴─────────┐ ┌─────────┴───────────┐ ┌──────────┴──────────┐ ┌──────────┴──────────┐ ┌──────────┴───────────┐ + │QueryRequestParser│ │BaseRestRequestParser│ │BaseJSONRequestParser│ │BaseCBORRequestParser│ │BaseRpcV2RequestParser│ + └──────────────────┘ └─────────────────────┘ └─────────────────────┘ └─────────────────────┘ └──────────────────────┘ + ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ + ┌───────┴────────┐ ┌─────────┴──────────┐ │ │ ┌────────┴────────┐ │ ┌───┴─────────────┴────┐ + │EC2RequestParser│ │RestXMLRequestParser│ │ │ │JSONRequestParser│ │ │RpcV2CBORRequestParser│ + └────────────────┘ └────────────────────┘ │ │ └─────────────────┘ │ └──────────────────────┘ + ┌────────────────┴───┴┐ ▲ │ + │RestJSONRequestParser│ ┌───┴──────┴──────┐ + └─────────────────────┘ │CBORRequestParser│ + └─────────────────┘ :: The ``RequestParser`` contains the logic that is used among all the different protocols (``query``, ``json``, ``rest-json``, ``rest-xml``, -and ``ec2``). +``cbor`` and ``ec2``). The relation between the different protocols is described in the ``serializer``. @@ -44,13 +45,21 @@ which is shared among all different protocols. * The ``BaseRestRequestParser`` contains the logic for the REST protocol specifics (i.e. specific HTTP metadata parsing). +* The ``BaseRpcV2RequestParser`` contains the logic for the RPC v2 + protocol specifics (special path routing, no logic about body decoding) * The ``BaseJSONRequestParser`` contains the logic for the JSON body parsing. +* The ``BaseCBORRequestParser`` contains the logic for the CBOR body + parsing. * The ``RestJSONRequestParser`` inherits the ReST specific logic from the ``BaseRestRequestParser`` and the JSON body parsing from the ``BaseJSONRequestParser``. -* The ``QueryRequestParser``, ``RestXMLRequestParser``, and the - ``JSONRequestParser`` have a conventional inheritance structure. +* The ``CBORRequestParser`` inherits the ``json``-protocol specific + logic from the ``JSONRequestParser`` and the CBOR body parsing + from the ``BaseCBORRequestParser``. +* The ``QueryRequestParser``, ``RestXMLRequestParser``, + ``RpcV2CBORRequestParser`` and ``JSONRequestParser`` have a + conventional inheritance structure. The services and their protocols are defined by using AWS's Smithy (a language to define services in a - somewhat - protocol-agnostic @@ -66,7 +75,10 @@ import base64 import datetime import functools +import io +import os import re +import struct from abc import ABC from collections.abc import Mapping from email.utils import parsedate_to_datetime @@ -89,6 +101,7 @@ from werkzeug.exceptions import BadRequest, NotFound from localstack.aws.protocol.op_router import RestServiceOperationRouter +from localstack.aws.spec import ProtocolName from localstack.http import Request @@ -332,7 +345,7 @@ def _noop_parser(self, _, __, node: Any, ___): _parse_double = _parse_float _parse_long = _parse_integer - def _convert_str_to_timestamp(self, value: str, timestamp_format=None): + def _convert_str_to_timestamp(self, value: str, timestamp_format=None) -> datetime.datetime: if timestamp_format is None: timestamp_format = self.TIMESTAMP_FORMAT timestamp_format = timestamp_format.lower() @@ -346,11 +359,11 @@ def _timestamp_iso8601(date_string: str) -> datetime.datetime: @staticmethod def _timestamp_unixtimestamp(timestamp_string: str) -> datetime.datetime: - return datetime.datetime.utcfromtimestamp(int(timestamp_string)) + return datetime.datetime.fromtimestamp(int(timestamp_string), tz=datetime.UTC) @staticmethod def _timestamp_unixtimestampmillis(timestamp_string: str) -> datetime.datetime: - return datetime.datetime.utcfromtimestamp(float(timestamp_string) / 1000) + return datetime.datetime.fromtimestamp(float(timestamp_string) / 1000, tz=datetime.UTC) @staticmethod def _timestamp_rfc822(datetime_string: str) -> datetime.datetime: @@ -976,6 +989,405 @@ def _create_event_stream(self, request: Request, shape: Shape) -> Any: raise NotImplementedError +class BaseCBORRequestParser(RequestParser, ABC): + """ + The ``BaseCBORRequestParser`` is the base class for all CBOR-based AWS service protocols. + This base-class handles parsing the payload / body as CBOR. + """ + + INDEFINITE_ITEM_ADDITIONAL_INFO = 31 + BREAK_CODE = 0xFF + # timestamp format for requests with CBOR content type + TIMESTAMP_FORMAT = "unixtimestamp" + + @functools.cached_property + def major_type_to_parsing_method_map(self): + return { + 0: self._parse_type_unsigned_integer, + 1: self._parse_type_negative_integer, + 2: self._parse_type_byte_string, + 3: self._parse_type_text_string, + 4: self._parse_type_array, + 5: self._parse_type_map, + 6: self._parse_type_tag, + 7: self._parse_type_simple_and_float, + } + + @staticmethod + def get_peekable_stream_from_bytes(_bytes: bytes) -> io.BufferedReader: + return io.BufferedReader(io.BytesIO(_bytes)) + + def parse_data_item(self, stream: io.BufferedReader) -> Any: + # CBOR data is divided into "data items", and each data item starts + # with an initial byte that describes how the following bytes should be parsed + initial_byte = self._read_bytes_as_int(stream, 1) + # The highest order three bits of the initial byte describe the CBOR major type + major_type = initial_byte >> 5 + # The lowest order 5 bits of the initial byte tells us more information about + # how the bytes should be parsed that will be used + additional_info: int = initial_byte & 0b00011111 + + if major_type in self.major_type_to_parsing_method_map: + method = self.major_type_to_parsing_method_map[major_type] + return method(stream, additional_info) + else: + raise ProtocolParserError( + f"Unsupported inital byte found for data item- " + f"Major type:{major_type}, Additional info: " + f"{additional_info}" + ) + + # Major type 0 - unsigned integers + def _parse_type_unsigned_integer(self, stream: io.BufferedReader, additional_info: int) -> int: + additional_info_to_num_bytes = { + 24: 1, + 25: 2, + 26: 4, + 27: 8, + } + # Values under 24 don't need a full byte to be stored; their values are + # instead stored as the "additional info" in the initial byte + if additional_info < 24: + return additional_info + elif additional_info in additional_info_to_num_bytes: + num_bytes = additional_info_to_num_bytes[additional_info] + return self._read_bytes_as_int(stream, num_bytes) + else: + raise ProtocolParserError( + "Invalid CBOR integer returned from the service; unparsable " + f"additional info found for major type 0 or 1: {additional_info}" + ) + + # Major type 1 - negative integers + def _parse_type_negative_integer(self, stream: io.BufferedReader, additional_info: int) -> int: + return -1 - self._parse_type_unsigned_integer(stream, additional_info) + + # Major type 2 - byte string + def _parse_type_byte_string(self, stream: io.BufferedReader, additional_info: int) -> bytes: + if additional_info != self.INDEFINITE_ITEM_ADDITIONAL_INFO: + length = self._parse_type_unsigned_integer(stream, additional_info) + return self._read_from_stream(stream, length) + else: + chunks = [] + while True: + if self._handle_break_code(stream): + break + initial_byte = self._read_bytes_as_int(stream, 1) + additional_info = initial_byte & 0b00011111 + length = self._parse_type_unsigned_integer(stream, additional_info) + chunks.append(self._read_from_stream(stream, length)) + return b"".join(chunks) + + # Major type 3 - text string + def _parse_type_text_string(self, stream: io.BufferedReader, additional_info: int) -> str: + return self._parse_type_byte_string(stream, additional_info).decode("utf-8") + + # Major type 4 - lists + def _parse_type_array(self, stream: io.BufferedReader, additional_info: int) -> list: + if additional_info != self.INDEFINITE_ITEM_ADDITIONAL_INFO: + length = self._parse_type_unsigned_integer(stream, additional_info) + return [self.parse_data_item(stream) for _ in range(length)] + else: + items = [] + while not self._handle_break_code(stream): + items.append(self.parse_data_item(stream)) + return items + + # Major type 5 - maps + def _parse_type_map(self, stream: io.BufferedReader, additional_info: int) -> dict: + items = {} + if additional_info != self.INDEFINITE_ITEM_ADDITIONAL_INFO: + length = self._parse_type_unsigned_integer(stream, additional_info) + for _ in range(length): + self._parse_type_key_value_pair(stream, items) + return items + + else: + while not self._handle_break_code(stream): + self._parse_type_key_value_pair(stream, items) + return items + + def _parse_type_key_value_pair(self, stream: io.BufferedReader, items: dict) -> None: + key = self.parse_data_item(stream) + value = self.parse_data_item(stream) + if value is not None: + items[key] = value + + # Major type 6 is tags. The only tag we currently support is tag 1 for unix + # timestamps + def _parse_type_tag(self, stream: io.BufferedReader, additional_info: int): + tag = self._parse_type_unsigned_integer(stream, additional_info) + value = self.parse_data_item(stream) + if tag == 1: # Epoch-based date/time in milliseconds + return self._parse_type_datetime(value) + else: + raise ProtocolParserError(f"Found CBOR tag not supported by botocore: {tag}") + + def _parse_type_datetime(self, value: int | float) -> datetime.datetime: + if isinstance(value, (int, float)): + return self._convert_str_to_timestamp(str(value)) + else: + raise ProtocolParserError(f"Unable to parse datetime value: {value}") + + # Major type 7 includes floats and "simple" types. Supported simple types are + # currently boolean values, CBOR's null, and CBOR's undefined type. All other + # values are either floats or invalid. + def _parse_type_simple_and_float( + self, stream: io.BufferedReader, additional_info: int + ) -> bool | float | None: + # For major type 7, values 20-23 correspond to CBOR "simple" values + additional_info_simple_values = { + 20: False, # CBOR false + 21: True, # CBOR true + 22: None, # CBOR null + 23: None, # CBOR undefined + } + # First we check if the additional info corresponds to a supported simple value + if additional_info in additional_info_simple_values: + return additional_info_simple_values[additional_info] + + # If it's not a simple value, we need to parse it into the correct format and + # number fo bytes + float_formats = { + 25: (">e", 2), + 26: (">f", 4), + 27: (">d", 8), + } + + if additional_info in float_formats: + float_format, num_bytes = float_formats[additional_info] + return struct.unpack(float_format, self._read_from_stream(stream, num_bytes))[0] + raise ProtocolParserError( + f"Invalid additional info found for major type 7: {additional_info}. " + f"This indicates an unsupported simple type or an indefinite float value" + ) + + @_text_content + def _parse_blob(self, _, __, node: bytes, ___) -> bytes: + return node + + @_text_content + def _parse_timestamp( + self, _, shape: Shape, node: datetime.datetime | str, ___ + ) -> datetime.datetime: + if isinstance(node, datetime.datetime): + return node + return super()._parse_timestamp(_, shape, node, ___) + + @_text_content + def _parse_boolean(self, _, __, node: str | bool, ___) -> bool: + if isinstance(node, str): + value = node.lower() + if value == "true": + return True + if value == "false": + return False + raise ValueError(f"cannot parse boolean value {node}") + return node + + # This helper method is intended for use when parsing indefinite length items. + # It does nothing if the next byte is not the break code. If the next byte is + # the break code, it advances past that byte and returns True so the calling + # method knows to stop parsing that data item. + def _handle_break_code(self, stream: io.BufferedReader) -> bool | None: + if int.from_bytes(stream.peek(1)[:1], "big") == self.BREAK_CODE: + stream.seek(1, os.SEEK_CUR) + return True + + def _read_bytes_as_int(self, stream: IO[bytes], num_bytes: int) -> int: + byte = self._read_from_stream(stream, num_bytes) + return int.from_bytes(byte, "big") + + @staticmethod + def _read_from_stream(stream: IO[bytes], num_bytes: int) -> bytes: + value = stream.read(num_bytes) + if len(value) != num_bytes: + raise ProtocolParserError( + "End of stream reached; this indicates a " + "malformed CBOR response from the server or an " + "issue in botocore" + ) + return value + + +class CBORRequestParser(BaseCBORRequestParser, JSONRequestParser): + """ + The ``CBORRequestParser`` is responsible for parsing incoming requests for services which use the ``cbor`` + protocol. + The requests for these services encode the majority of their parameters as CBOR in the request body. + The operation is defined in an HTTP header field. + This protocol is not properly defined in the specs, but it is derived from the ``json`` protocol. Only Kinesis uses + it for now. + """ + + # timestamp format is different from traditional CBOR, and is encoded as a milliseconds integer + TIMESTAMP_FORMAT = "unixtimestampmillis" + + def _do_parse( + self, request: Request, shape: Shape, uri_params: Mapping[str, Any] = None + ) -> dict: + parsed = {} + if shape is not None: + event_name = shape.event_stream_name + if event_name: + parsed = self._handle_event_stream(request, shape, event_name) + else: + self._parse_payload(request, shape, parsed, uri_params) + return parsed + + def _handle_event_stream(self, request: Request, shape: Shape, event_name: str): + # TODO handle event streams + raise NotImplementedError + + def _parse_payload( + self, + request: Request, + shape: Shape, + final_parsed: dict, + uri_params: Mapping[str, Any] = None, + ) -> None: + original_parsed = self._initial_body_parse(request) + body_parsed = self._parse_shape(request, shape, original_parsed, uri_params) + final_parsed.update(body_parsed) + + def _initial_body_parse(self, request: Request) -> Any: + body_contents = request.data + if body_contents == b"": + return body_contents + body_contents_stream = self.get_peekable_stream_from_bytes(body_contents) + return self.parse_data_item(body_contents_stream) + + def _parse_timestamp( + self, request: Request, shape: Shape, node: str, uri_params: Mapping[str, Any] = None + ) -> datetime.datetime: + # TODO: remove once CBOR support has been removed from `JSONRequestParser` + return super()._parse_timestamp(request, shape, node, uri_params) + + +class BaseRpcV2RequestParser(RequestParser): + """ + The ``BaseRpcV2RequestParser`` is the base class for all RPC V2-based AWS service protocols. + This base class handles the routing of the request, which is specific based on the path. + The body decoding is done in the respective subclasses. + """ + + @_handle_exceptions + def parse(self, request: Request) -> tuple[OperationModel, Any]: + # see https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html + if request.method != "POST": + raise ProtocolParserError("RPC v2 only accepts POST requests.") + + headers = request.headers + if "X-Amz-Target" in headers or "X-Amzn-Target" in headers: + raise ProtocolParserError( + "RPC v2 does not accept 'X-Amz-Target' or 'X-Amzn-Target'. " + "Such requests are rejected for security reasons." + ) + # The Smithy RPCv2 CBOR protocol will only use the last four segments of the URL when routing requests. + rpc_v2_params = request.path.lstrip("/").split("/") + if len(rpc_v2_params) < 4 or not ( + operation := self.service.operation_model(rpc_v2_params[-1]) + ): + raise OperationNotFoundParserError( + f"Unable to find operation for request to service " + f"{self.service.service_name}: {request.method} {request.path}" + ) + + # there are no URI params in RPC v2 + uri_params = {} + shape: StructureShape = operation.input_shape + final_parsed = self._do_parse(request, shape, uri_params) + return operation, final_parsed + + @_handle_exceptions + def _do_parse( + self, request: Request, shape: Shape, uri_params: Mapping[str, Any] = None + ) -> dict[str, Any]: + parsed = {} + if shape is not None: + event_stream_name = shape.event_stream_name + if event_stream_name: + parsed = self._handle_event_stream(request, shape, event_stream_name) + else: + parsed = {} + self._parse_payload(request, shape, parsed, uri_params) + + return parsed + + def _handle_event_stream(self, request: Request, shape: Shape, event_name: str): + # TODO handle event streams + raise NotImplementedError + + def _parse_structure( + self, + request: Request, + shape: StructureShape, + node: dict | None, + uri_params: Mapping[str, Any] = None, + ): + if shape.is_document_type: + final_parsed = node + else: + if node is None: + # If the comes across the wire as "null" (None in python), + # we should be returning this unchanged, instead of as an + # empty dict. + return None + final_parsed = {} + members = shape.members + if shape.is_tagged_union: + cleaned_value = node.copy() + cleaned_value.pop("__type", None) + cleaned_value = {k: v for k, v in cleaned_value.items() if v is not None} + if len(cleaned_value) != 1: + raise ProtocolParserError( + f"Invalid service response: {shape.name} must have one and only one member set." + ) + + for member_name, member_shape in members.items(): + member_value = node.get(member_name) + if member_value is not None: + final_parsed[member_name] = self._parse_shape( + request, member_shape, member_value, uri_params + ) + + return final_parsed + + def _parse_payload( + self, + request: Request, + shape: Shape, + final_parsed: dict, + uri_params: Mapping[str, Any] = None, + ) -> None: + original_parsed = self._initial_body_parse(request) + body_parsed = self._parse_shape(request, shape, original_parsed, uri_params) + final_parsed.update(body_parsed) + + def _initial_body_parse(self, request: Request): + # This method should do the initial parsing of the + # body. We still need to walk the parsed body in order + # to convert types, but this method will do the first round + # of parsing. + raise NotImplementedError("_initial_body_parse") + + +class RpcV2CBORRequestParser(BaseRpcV2RequestParser, BaseCBORRequestParser): + """ + The ``RpcV2CBORRequestParser`` is responsible for parsing incoming requests for services which use the + ``rpc-v2-cbor`` protocol. The requests for these services encode all of their parameters as CBOR in the + request body. + """ + + # TODO: investigate datetime format for RpcV2CBOR protocol, which might be different than Kinesis CBOR + def _initial_body_parse(self, request: Request): + body_contents = request.data + if body_contents == b"": + return body_contents + body_contents_stream = self.get_peekable_stream_from_bytes(body_contents) + return self.parse_data_item(body_contents_stream) + + class EC2RequestParser(QueryRequestParser): """ The ``EC2RequestParser`` is responsible for parsing incoming requests for services which use the ``ec2`` @@ -1154,11 +1566,12 @@ def _get_serialized_name(self, shape: Shape, default_name: str, node: dict) -> s @functools.cache -def create_parser(service: ServiceModel) -> RequestParser: +def create_parser(service: ServiceModel, protocol: ProtocolName | None = None) -> RequestParser: """ Creates the right parser for the given service model. :param service: to create the parser for + :param protocol: the protocol for the parser. If not provided, fallback to the service's default protocol :return: RequestParser which can handle the protocol of the service """ # Unfortunately, some services show subtle differences in their parsing or operation detection behavior, even though @@ -1176,14 +1589,20 @@ def create_parser(service: ServiceModel) -> RequestParser: "rest-json": RestJSONRequestParser, "rest-xml": RestXMLRequestParser, "ec2": EC2RequestParser, + "smithy-rpc-v2-cbor": RpcV2CBORRequestParser, + # TODO: implement multi-protocol support for Kinesis, so that it can uses the `cbor` protocol and remove + # CBOR handling from JSONRequestParser + # this is not an "official" protocol defined from the spec, but is derived from ``json`` } + service_protocol = protocol or service.protocol + # Try to select a service- and protocol-specific parser implementation if ( service.service_name in service_specific_parsers - and service.protocol in service_specific_parsers[service.service_name] + and service_protocol in service_specific_parsers[service.service_name] ): - return service_specific_parsers[service.service_name][service.protocol](service) + return service_specific_parsers[service.service_name][service_protocol](service) else: # Otherwise, pick the protocol-specific parser for the protocol of the service - return protocol_specific_parsers[service.protocol](service) + return protocol_specific_parsers[service_protocol](service) diff --git a/localstack-core/localstack/aws/protocol/serializer.py b/localstack-core/localstack/aws/protocol/serializer.py index 871481cc4bb1f..5816f0395522d 100644 --- a/localstack-core/localstack/aws/protocol/serializer.py +++ b/localstack-core/localstack/aws/protocol/serializer.py @@ -14,27 +14,34 @@ designed such that the serializers share as much logic as possible. The class hierarchy looks as follows: :: - ┌───────────────────┐ - │ResponseSerializer │ - └───────────────────┘ - ▲ ▲ ▲ - ┌──────────────────────┘ │ └──────────────────┐ - ┌────────────┴────────────┐ ┌────────────┴─────────────┐ ┌─────────┴────────────┐ - │BaseXMLResponseSerializer│ │BaseRestResponseSerializer│ │JSONResponseSerializer│ - └─────────────────────────┘ └──────────────────────────┘ └──────────────────────┘ - ▲ ▲ ▲ ▲ ▲ - ┌──────────────────────┴─┐ ┌┴─────────────┴──────────┐ ┌┴──────────────┴──────────┐ - │QueryResponseSerializer │ │RestXMLResponseSerializer│ │RestJSONResponseSerializer│ - └────────────────────────┘ └─────────────────────────┘ └──────────────────────────┘ - ▲ - ┌──────────┴──────────┐ + ┌────────────────────┐ + │ ResponseSerializer │ + └────────────────────┘ + ▲ ▲ ▲ + ┌─────────────────┬───────┘ │ └──────────────┬──────────────────────┐ + ┌────────────┴────────────┐ │ ┌───────┴──────────────┐ │ ┌────────────┴─────────────┐ + │BaseXMLResponseSerializer│ │ │JSONResponseSerializer│ │ │BaseCBORResponseSerializer│ + └─────────────────────────┘ │ └──────────────────────┘ │ └──────────────────────────┘ + ▲ ▲ ┌─────────────┴────────────┐ ▲ ┌─────┴─────────────────────┐ ▲ ▲ + │ │ │BaseRestResponseSerializer│ │ │BaseRpcV2ResponseSerializer│ │ │ + │ │ └──────────────────────────┘ │ └───────────────────────────┘ │ │ + │ │ ▲ ▲ │ ▲ │ │ + │ │ │ │ │ │ │ │ + │ ┌─┴──────────────┴────────┐ ┌──┴───────────┴───────────┐ ┌──────────┴───────────┴────┐ │ + │ │RestXMLResponseSerializer│ │RestJSONResponseSerializer│ │RpcV2CBORResponseSerializer│ │ + │ └─────────────────────────┘ └──────────────────────────┘ └───────────────────────────┘ │ + ┌─────┴──────────────────┐ ┌──────────┴─────────────┐ + │QueryResponseSerializer │ │ CBORResponseSerializer │ + └────────────────────────┘ └────────────────────────┘ + ▲ + ┌─────────┴───────────┐ │EC2ResponseSerializer│ └─────────────────────┘ :: The ``ResponseSerializer`` contains the logic that is used among all the -different protocols (``query``, ``json``, ``rest-json``, ``rest-xml``, and -``ec2``). +different protocols (``query``, ``json``, ``rest-json``, ``rest-xml``, ``cbor`` +and ``ec2``). The protocols relate to each other in the following ways: * The ``query`` and the ``rest-xml`` protocols both have XML bodies in their @@ -42,10 +49,14 @@ type). * The ``json`` and the ``rest-json`` protocols both have JSON bodies in their responses which are serialized the same way. +* The ``cbor`` protocol is not properly defined in the spec, but mirrors the + ``json`` protocol. * The ``rest-json`` and ``rest-xml`` protocols serialize some metadata in - the HTTP response's header fields + the HTTP response's header fields. * The ``ec2`` protocol is basically similar to the ``query`` protocol with a specific error response formatting. +* The ``smithy-rpc-v2-cbor`` protocol defines a specific way to route request + to services via the RPC v2 trait, and encodes its body with the CBOR format. The serializer classes in this module correspond directly to the different protocols. ``#create_serializer`` shows the explicit mapping between the @@ -54,13 +65,23 @@ * The ``ResponseSerializer`` contains all the basic logic for the serialization which is shared among all different protocols. -* The ``BaseXMLResponseSerializer`` and the ``JSONResponseSerializer`` - contain the logic for the XML and the JSON serialization respectively. +* The ``BaseXMLResponseSerializer``, ``JSONResponseSerializer`` and + ``BaseCBORResponseSerializer`` contain the logic for the XML, JSON + and the CBOR serialization respectively. * The ``BaseRestResponseSerializer`` contains the logic for the REST protocol specifics (i.e. specific HTTP header serializations). +* The ``BaseRpcV2ResponseSerializer`` contains the logic for the RPC v2 + protocol specifics (i.e. pretty bare, does not has any specific + about body serialization). * The ``RestXMLResponseSerializer`` and the ``RestJSONResponseSerializer`` inherit the ReST specific logic from the ``BaseRestResponseSerializer`` and the XML / JSON body serialization from their second super class. +* The ``RpcV2CBORResponseSerializer`` inherits the RPC v2 specific logic + from the ``BaseRpcV2ResponseSerializer`` and the CBOR body serialization + from its second super class. +* The ``CBORResponseSerializer`` contains the logic specific to the + non-official ``cbor`` protocol, mirroring the ``json`` protocol but + with CBOR encoded body The services and their protocols are defined by using AWS's Smithy (a language to define services in a - somewhat - protocol-agnostic @@ -73,21 +94,32 @@ import abc import base64 +import datetime import functools import json import logging +import math import string +import struct from abc import ABC from binascii import crc32 from collections.abc import Iterable, Iterator -from datetime import datetime from email.utils import formatdate from struct import pack -from typing import Any +from typing import IO, Any from xml.etree import ElementTree as ETree import xmltodict -from botocore.model import ListShape, MapShape, OperationModel, ServiceModel, Shape, StructureShape +from botocore.model import ( + ListShape, + MapShape, + OperationModel, + ServiceModel, + Shape, + ShapeResolver, + StringShape, + StructureShape, +) from botocore.serialize import ISO8601, ISO8601_MICRO from botocore.utils import calculate_md5, is_json_value_header, parse_to_aware_datetime @@ -261,7 +293,11 @@ def serialize_error_to_response( f"Error to serialize ({error.__class__.__name__ if error else None}) is not a ServiceException." ) shape = operation_model.service_model.shape_for_error_code(error.code) - serialized_response.status_code = error.status_code + serialized_response.status_code = self._get_error_status_code( + error=error, + headers=headers, + service_model=operation_model.service_model, + ) self._serialize_error( error, serialized_response, shape, operation_model, mime_type, request_id @@ -506,7 +542,7 @@ def _get_mime_type(self, headers: dict | Headers | None) -> str: # Some extra utility methods subclasses can use. @staticmethod - def _timestamp_iso8601(value: datetime) -> str: + def _timestamp_iso8601(value: datetime.datetime) -> str: if value.microsecond > 0: timestamp_format = ISO8601_MICRO else: @@ -514,15 +550,17 @@ def _timestamp_iso8601(value: datetime) -> str: return value.strftime(timestamp_format) @staticmethod - def _timestamp_unixtimestamp(value: datetime) -> float: + def _timestamp_unixtimestamp(value: datetime.datetime) -> float: return value.timestamp() - def _timestamp_rfc822(self, value: datetime) -> str: - if isinstance(value, datetime): + def _timestamp_rfc822(self, value: datetime.datetime) -> str: + if isinstance(value, datetime.datetime): value = self._timestamp_unixtimestamp(value) return formatdate(value, usegmt=True) - def _convert_timestamp_to_str(self, value: int | str | datetime, timestamp_format=None) -> str: + def _convert_timestamp_to_str( + self, value: int | str | datetime.datetime, timestamp_format=None + ) -> str: if timestamp_format is None: timestamp_format = self.TIMESTAMP_FORMAT timestamp_format = timestamp_format.lower() @@ -580,6 +618,60 @@ def _add_md5_header(self, response: Response): def _get_error_message(self, error: Exception) -> str | None: return str(error) if error is not None and str(error) != "None" else None + def _get_error_status_code( + self, error: ServiceException, headers: Headers, service_model: ServiceModel + ) -> int: + return error.status_code + + +class QueryCompatibleProtocolMixin: + def _get_error_status_code( + self, error: ServiceException, headers: dict | Headers | None, service_model: ServiceModel + ) -> int: + # by default, some protocols (namely `json` and `smithy-rpc-v2-cbor`) might not define exception status code in + # their specs, so they are not defined in the `ServiceException` object and will use the default value of `400` + # But Query compatible service always do define them, so we get the wrong code for service that are + # multi-protocols like CloudWatch + # we need to verify if the service is compatible, and if the client has requested the query compatible error + # code to return the right value + if not service_model.is_query_compatible: + return error.status_code + + if headers and headers.get("x-amzn-query-mode") == "true": + return error.status_code + + # we only want to override status code 4XX + if 400 < error.status_code <= 499: + return 400 + + return error.status_code + + def _add_query_compatible_error_header(self, response: Response, error: ServiceException): + """ + Add an `x-amzn-query-error` header for client to translate errors codes from former `query` services + into other protocols. + """ + + sender_fault = "Sender" if error.sender_fault else "Receiver" + response.headers["x-amzn-query-error"] = f"{error.code};{sender_fault}" + + def _get_error_code( + self, is_query_compatible: bool, error: ServiceException, shape: Shape | None = None + ): + # if the operation is query compatible, we need to add to use shape name + if is_query_compatible: + if shape: + code = shape.name + else: + # if the shape is not defined, we are using the Exception named to derive the `Code`, like you would + # from the shape. This allows us to have Exception that are valid in multi-protocols by defining its + # code and its name to be different + code = error.__class__.__name__ + else: + code = error.code + + return code + class BaseXMLResponseSerializer(ResponseSerializer): """ @@ -1193,7 +1285,7 @@ def _prepare_additional_traits_in_xml(self, root: ETree.Element | None, request_ request_id_element.text = request_id -class JSONResponseSerializer(ResponseSerializer): +class JSONResponseSerializer(QueryCompatibleProtocolMixin, ResponseSerializer): """ The ``JSONResponseSerializer`` is responsible for the serialization of responses from services with the ``json`` protocol. It implements the JSON response body serialization, which is also used by the @@ -1220,25 +1312,48 @@ def _serialize_error( # TODO implement different service-specific serializer configurations # - currently we set both, the `__type` member as well as the `X-Amzn-Errortype` header # - the specification defines that it's either the __type field OR the header - response.headers["X-Amzn-Errortype"] = error.code - body["__type"] = error.code + # this depends on the JSON protocol version as well. If json-1.0 the Error should be the full shape ID, like + # com.amazon.coral.service#ExceptionName + # if json-1.1, it should only be the name + + is_query_compatible = operation_model.service_model.is_query_compatible + code = self._get_error_code(is_query_compatible, error, shape) + + response.headers["X-Amzn-Errortype"] = code + + # the `__type` field is not defined in default botocore error shapes + body["__type"] = code if shape: remaining_params = {} # TODO add a possibility to serialize simple non-modelled errors (like S3 NoSuchBucket#BucketName) for member in shape.members: if hasattr(error, member): - remaining_params[member] = getattr(error, member) + value = getattr(error, member) + # Default error message fields can sometimes have different casing in the specs elif member.lower() in ["code", "message"] and hasattr(error, member.lower()): - remaining_params[member] = getattr(error, member.lower()) + value = getattr(error, member.lower()) + + else: + continue + + if value is None: + # do not serialize a value that is set to `None` + continue + + # if the value is falsy (empty string, empty list) and not in the Shape required members, AWS will + # not serialize it, and it will not be part of the response body. + if value or member in shape.required_members: + remaining_params[member] = value + self._serialize(body, remaining_params, shape, None, mime_type) - # Only set the message if it has not been set with the shape members + # this is a workaround, some Error Shape do not define a `Message` field, but it is always returned + # this could be solved at the same time as the `__type` field if "message" not in body and "Message" not in body: - message = self._get_error_message(error) - if message is not None: - body["message"] = message + if error_message := self._get_error_message(error): + body["message"] = error_message if mime_type in self.CBOR_TYPES: response.set_response(cbor2_dumps(body, datetime_as_timestamp=True)) @@ -1246,6 +1361,9 @@ def _serialize_error( else: response.set_json(body) + if is_query_compatible: + self._add_query_compatible_error_header(response, error) + def _serialize_response( self, parameters: dict, @@ -1407,6 +1525,504 @@ def _serialize_content_type( serialized.headers["Content-Type"] = mime_type +class BaseCBORResponseSerializer(ResponseSerializer): + """ + The ``BaseCBORResponseSerializer`` performs the basic logic for the CBOR response serialization. + + There are two types of map/list in CBOR, indefinite length types and "defined" ones: + You can use the `\xbf` byte marker to indicate a map with indefinite length, then `\xff` to indicate the end + of the map. + You can also use, for example, `\xa4` to indicate a map with exactly 4 things in it, so `\xff` is not + required at the end. + AWS, for both Kinesis and `smithy-rpc-v2-cbor` services, is using indefinite data structures when returning + responses. + + The CBOR serializer cannot serialize an exception if it is not defined in our specs. + LocalStack defines a way to have user-defined exception by subclassing `CommonServiceException`, so it needs to be + able to encode those, as well as InternalError + We are creating a default botocore structure shape (`_DEFAULT_ERROR_STRUCTURE_SHAPE`) to be used in such cases. + """ + + SUPPORTED_MIME_TYPES = [APPLICATION_CBOR, APPLICATION_AMZ_CBOR_1_1] + + UNSIGNED_INT_MAJOR_TYPE = 0 + NEGATIVE_INT_MAJOR_TYPE = 1 + BLOB_MAJOR_TYPE = 2 + STRING_MAJOR_TYPE = 3 + LIST_MAJOR_TYPE = 4 + MAP_MAJOR_TYPE = 5 + TAG_MAJOR_TYPE = 6 + FLOAT_AND_SIMPLE_MAJOR_TYPE = 7 + + INDEFINITE_ITEM_ADDITIONAL_INFO = 31 + BREAK_CODE = b"\xff" + USE_INDEFINITE_DATA_STRUCTURE = True + + _ERROR_TYPE_SHAPE = StringShape(shape_name="__type", shape_model={"type": "string"}) + + _DEFAULT_ERROR_STRUCTURE_SHAPE = StructureShape( + shape_name="DefaultErrorStructure", + shape_model={ + "type": "structure", + "members": { + "message": {"shape": "ErrorMessage"}, + "__type": {"shape": "ErrorType"}, + }, + "error": {"code": "DefaultErrorStructure", "httpStatusCode": 400, "senderFault": True}, + "exception": True, + }, + shape_resolver=ShapeResolver( + shape_map={ + "ErrorMessage": {"type": "string"}, + "ErrorType": {"type": "string"}, + }, + ), + ) + + def _serialize_data_item( + self, serialized: bytearray, value: Any, shape: Shape | None, name: str | None = None + ) -> None: + method = getattr(self, f"_serialize_type_{shape.type_name}") + if method is None: + raise ValueError( + f"Unrecognized C2J type: {shape.type_name}, unable to serialize request" + ) + method(serialized, value, shape, name) + + def _serialize_type_integer( + self, serialized: bytearray, value: int, shape: Shape | None, name: str | None = None + ) -> None: + if value >= 0: + major_type = self.UNSIGNED_INT_MAJOR_TYPE + else: + major_type = self.NEGATIVE_INT_MAJOR_TYPE + # The only differences in serializing negative and positive integers is + # that for negative, we set the major type to 1 and set the value to -1 + # minus the value + value = -1 - value + additional_info, num_bytes = self._get_additional_info_and_num_bytes(value) + initial_byte = self._get_initial_byte(major_type, additional_info) + if num_bytes == 0: + serialized.extend(initial_byte) + else: + serialized.extend(initial_byte + value.to_bytes(num_bytes, "big")) + + def _serialize_type_long( + self, serialized: bytearray, value: int, shape: Shape, name: str | None = None + ) -> None: + self._serialize_type_integer(serialized, value, shape, name) + + def _serialize_type_blob( + self, + serialized: bytearray, + value: str | bytes | IO[bytes], + shape: Shape | None, + name: str | None = None, + ) -> None: + if isinstance(value, str): + value = value.encode("utf-8") + elif not isinstance(value, (bytes, bytearray)): + # We support file-like objects for blobs; these already have been + # validated to ensure they have a read method + value = value.read() + length = len(value) + additional_info, num_bytes = self._get_additional_info_and_num_bytes(length) + initial_byte = self._get_initial_byte(self.BLOB_MAJOR_TYPE, additional_info) + if num_bytes == 0: + serialized.extend(initial_byte) + else: + serialized.extend(initial_byte + length.to_bytes(num_bytes, "big")) + serialized.extend(value) + + def _serialize_type_string( + self, serialized: bytearray, value: str, shape: Shape | None, name: str | None = None + ) -> None: + encoded = value.encode("utf-8") + length = len(encoded) + additional_info, num_bytes = self._get_additional_info_and_num_bytes(length) + initial_byte = self._get_initial_byte(self.STRING_MAJOR_TYPE, additional_info) + if num_bytes == 0: + serialized.extend(initial_byte + encoded) + else: + serialized.extend(initial_byte + length.to_bytes(num_bytes, "big") + encoded) + + def _serialize_type_list( + self, serialized: bytearray, value: list, shape: Shape | None, name: str | None = None + ) -> None: + initial_bytes, closing_bytes = self._get_bytes_for_data_structure( + value, self.LIST_MAJOR_TYPE + ) + serialized.extend(initial_bytes) + + for item in value: + self._serialize_data_item(serialized, item, shape.member) + + if closing_bytes is not None: + serialized.extend(closing_bytes) + + def _serialize_type_map( + self, serialized: bytearray, value: dict, shape: Shape | None, name: str | None = None + ) -> None: + initial_bytes, closing_bytes = self._get_bytes_for_data_structure( + value, self.MAP_MAJOR_TYPE + ) + serialized.extend(initial_bytes) + + for key_item, item in value.items(): + self._serialize_data_item(serialized, key_item, shape.key) + self._serialize_data_item(serialized, item, shape.value) + + if closing_bytes is not None: + serialized.extend(closing_bytes) + + def _serialize_type_structure( + self, + serialized: bytearray, + value: dict, + shape: Shape | None, + name: str | None = None, + shape_members: dict[str, Shape] | None = None, + ) -> None: + # `_serialize_type_structure` has a different signature other `_serialize_type_*` methods as it accepts + # `shape_members`. This is because sometimes, the `StructureShape` does not have some members defined in the + # specs, and we want to be able to pass arbitrary members to serialize undocumented members. + # see `_serialize_error_structure` for its specific usage + + if name is not None: + # For nested structures, we need to serialize the key first + self._serialize_data_item(serialized, name, shape.key_shape) + + # Remove `None` values from the dictionary + value = {k: v for k, v in value.items() if v is not None} + + initial_bytes, closing_bytes = self._get_bytes_for_data_structure( + value, self.MAP_MAJOR_TYPE + ) + serialized.extend(initial_bytes) + members = shape_members or shape.members + for member_key, member_value in value.items(): + member_shape = members[member_key] + if "name" in member_shape.serialization: + member_key = member_shape.serialization["name"] + if member_value is not None: + self._serialize_type_string(serialized, member_key, None, None) + self._serialize_data_item(serialized, member_value, member_shape) + + if closing_bytes is not None: + serialized.extend(closing_bytes) + + def _serialize_type_timestamp( + self, + serialized: bytearray, + value: int | str | datetime.datetime, + shape: Shape | None, + name: str | None = None, + ) -> None: + # https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html#timestamp-type-serialization + tag = 1 # Use tag 1 for unix timestamp + initial_byte = self._get_initial_byte(self.TAG_MAJOR_TYPE, tag) + serialized.extend(initial_byte) # Tagging the timestamp + + # we encode the timestamp as a double, like the Go SDK + # https://github.com/aws/aws-sdk-go-v2/blob/5d7c17325a2581afae4455c150549174ebfd9428/internal/protocoltest/smithyrpcv2cbor/serializers.go#L664-L669 + # Currently, the Botocore serializer using unsigned integers, but it does not conform to the Smithy specs: + # > This protocol uses epoch-seconds, also known as Unix timestamps, with millisecond + # > (1/1000th of a second) resolution. + timestamp = float(self._convert_timestamp_to_str(value)) + initial_byte = self._get_initial_byte(self.FLOAT_AND_SIMPLE_MAJOR_TYPE, 27) + serialized.extend(initial_byte + struct.pack(">d", timestamp)) + + def _serialize_type_float( + self, serialized: bytearray, value: float, shape: Shape | None, name: str | None = None + ) -> None: + if self._is_special_number(value): + serialized.extend( + self._get_bytes_for_special_numbers(value) + ) # Handle special values like NaN or Infinity + else: + initial_byte = self._get_initial_byte(self.FLOAT_AND_SIMPLE_MAJOR_TYPE, 26) + serialized.extend(initial_byte + struct.pack(">f", value)) + + def _serialize_type_double( + self, serialized: bytearray, value: float, shape: Shape | None, name: str | None = None + ) -> None: + if self._is_special_number(value): + serialized.extend( + self._get_bytes_for_special_numbers(value) + ) # Handle special values like NaN or Infinity + else: + initial_byte = self._get_initial_byte(self.FLOAT_AND_SIMPLE_MAJOR_TYPE, 27) + serialized.extend(initial_byte + struct.pack(">d", value)) + + def _serialize_type_boolean( + self, serialized: bytearray, value: bool, shape: Shape | None, name: str | None = None + ) -> None: + additional_info = 21 if value else 20 + serialized.extend(self._get_initial_byte(self.FLOAT_AND_SIMPLE_MAJOR_TYPE, additional_info)) + + @staticmethod + def _get_additional_info_and_num_bytes(value: int) -> tuple[int, int]: + # Values under 24 can be stored in the initial byte and don't need further + # encoding + if value < 24: + return value, 0 + # Values between 24 and 255 (inclusive) can be stored in 1 byte and + # correspond to additional info 24 + elif value < 256: + return 24, 1 + # Values up to 65535 can be stored in two bytes and correspond to additional + # info 25 + elif value < 65536: + return 25, 2 + # Values up to 4294967296 can be stored in four bytes and correspond to + # additional info 26 + elif value < 4294967296: + return 26, 4 + # The maximum number of bytes in a definite length data items is 8 which + # to additional info 27 + else: + return 27, 8 + + def _get_initial_byte(self, major_type: int, additional_info: int) -> bytes: + # The highest order three bits are the major type, so we need to bitshift the + # major type by 5 + major_type_bytes = major_type << 5 + return (major_type_bytes | additional_info).to_bytes(1, "big") + + @staticmethod + def _is_special_number(value: int | float) -> bool: + return any( + [ + value == float("inf"), + value == float("-inf"), + math.isnan(value), + ] + ) + + def _get_bytes_for_special_numbers(self, value: int | float) -> bytes: + additional_info = 25 + initial_byte = self._get_initial_byte(self.FLOAT_AND_SIMPLE_MAJOR_TYPE, additional_info) + if value == float("inf"): + return initial_byte + struct.pack(">H", 0x7C00) + elif value == float("-inf"): + return initial_byte + struct.pack(">H", 0xFC00) + elif math.isnan(value): + return initial_byte + struct.pack(">H", 0x7E00) + + def _get_bytes_for_data_structure( + self, value: list | dict, major_type: int + ) -> tuple[bytes, bytes | None]: + if self.USE_INDEFINITE_DATA_STRUCTURE: + additional_info = self.INDEFINITE_ITEM_ADDITIONAL_INFO + return self._get_initial_byte(major_type, additional_info), self.BREAK_CODE + else: + length = len(value) + additional_info, num_bytes = self._get_additional_info_and_num_bytes(length) + initial_byte = self._get_initial_byte(major_type, additional_info) + if num_bytes != 0: + initial_byte = initial_byte + length.to_bytes(num_bytes, "big") + + return initial_byte, None + + def _serialize_error_structure( + self, body: bytearray, shape: Shape | None, error: ServiceException, code: str + ): + if not shape: + shape = self._DEFAULT_ERROR_STRUCTURE_SHAPE + shape_members = shape.members + else: + # we need to manually add the `__type` field to the shape members as it is not part of the specs + # we do a shallow copy of the shape members + shape_members = shape.members.copy() + shape_members["__type"] = self._ERROR_TYPE_SHAPE + + # Error responses in the rpcv2Cbor protocol MUST be serialized identically to standard responses with one + # additional component to distinguish which error is contained: a body field named __type. + params = {"__type": code} + + for member in shape_members: + if hasattr(error, member): + value = getattr(error, member) + + # Default error message fields can sometimes have different casing in the specs + elif member.lower() in ["code", "message"] and hasattr(error, member.lower()): + value = getattr(error, member.lower()) + + else: + continue + + if value is None: + # do not serialize a value that is set to `None` + continue + + # if the value is falsy (empty string, empty list) and not in the Shape required members, AWS will + # not serialize it, and it will not be part of the response body. + if value or member in shape.required_members: + params[member] = value + + self._serialize_type_structure(body, params, shape, None, shape_members=shape_members) + + +class CBORResponseSerializer(BaseCBORResponseSerializer): + """ + The ``CBORResponseSerializer`` is responsible for the serialization of responses from services with the ``cbor`` + protocol. It implements the CBOR response body serialization, which is only currently used by Kinesis and is derived + conceptually from the ``JSONResponseSerializer`` + """ + + TIMESTAMP_FORMAT = "unixtimestamp" + + def _serialize_error( + self, + error: ServiceException, + response: Response, + shape: StructureShape, + operation_model: OperationModel, + mime_type: str, + request_id: str, + ) -> None: + body = bytearray() + response.content_type = mime_type + response.headers["X-Amzn-Errortype"] = error.code + + self._serialize_error_structure(body, shape, error, code=error.code) + + response.set_response(bytes(body)) + + def _serialize_response( + self, + parameters: dict, + response: Response, + shape: Shape | None, + shape_members: dict, + operation_model: OperationModel, + mime_type: str, + request_id: str, + ) -> None: + response.content_type = mime_type + response.set_response( + self._serialize_body_params(parameters, shape, operation_model, mime_type, request_id) + ) + + def _serialize_body_params( + self, + params: dict, + shape: Shape, + operation_model: OperationModel, + mime_type: str, + request_id: str, + ) -> bytes | None: + if shape is None: + return b"" + body = bytearray() + self._serialize_data_item(body, params, shape) + return bytes(body) + + def _prepare_additional_traits_in_response( + self, response: Response, operation_model: OperationModel, request_id: str + ) -> Response: + response.headers["x-amzn-requestid"] = request_id + response = super()._prepare_additional_traits_in_response( + response, operation_model, request_id + ) + return response + + +class BaseRpcV2ResponseSerializer(ResponseSerializer): + """ + The BaseRpcV2ResponseSerializer performs the basic logic for the RPC V2 response serialization. + The only variance between the various RPCv2 protocols is the way the body is serialized for regular responses, + and the way they will encode exceptions. + """ + + def _serialize_response( + self, + parameters: dict, + response: Response, + shape: Shape | None, + shape_members: dict, + operation_model: OperationModel, + mime_type: str, + request_id: str, + ) -> None: + response.content_type = mime_type + response.set_response( + self._serialize_body_params(parameters, shape, operation_model, mime_type, request_id) + ) + + def _serialize_body_params( + self, + params: dict, + shape: Shape, + operation_model: OperationModel, + mime_type: str, + request_id: str, + ) -> bytes | None: + raise NotImplementedError + + +class RpcV2CBORResponseSerializer( + QueryCompatibleProtocolMixin, BaseRpcV2ResponseSerializer, BaseCBORResponseSerializer +): + """ + The RpcV2CBORResponseSerializer implements the CBOR body serialization part for the RPC v2 protocol, and implements the + specific exception serialization. + https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html + """ + + # the Smithy spec defines that only `application/cbor` is supported for RPC v2 CBOR + SUPPORTED_MIME_TYPES = [APPLICATION_CBOR] + TIMESTAMP_FORMAT = "unixtimestamp" + + def _serialize_body_params( + self, + params: dict, + shape: Shape, + operation_model: OperationModel, + mime_type: str, + request_id: str, + ) -> bytes | None: + if shape is None: + return b"" + body = bytearray() + self._serialize_data_item(body, params, shape) + return bytes(body) + + def _serialize_error( + self, + error: ServiceException, + response: Response, + shape: StructureShape, + operation_model: OperationModel, + mime_type: str, + request_id: str, + ) -> None: + body = bytearray() + response.content_type = mime_type # can only be 'application/cbor' + + # Responses for the rpcv2Cbor protocol SHOULD NOT contain the X-Amzn-ErrorType header. + # Type information is always serialized in the payload. This is different from the `json` protocol + is_query_compatible = operation_model.service_model.is_query_compatible + code = self._get_error_code(is_query_compatible, error, shape) + + self._serialize_error_structure(body, shape, error, code=code) + + response.set_response(bytes(body)) + + if is_query_compatible: + self._add_query_compatible_error_header(response, error) + + def _prepare_additional_traits_in_response( + self, response: Response, operation_model: OperationModel, request_id: str + ): + response.headers["x-amzn-requestid"] = request_id + response.headers["Smithy-Protocol"] = "rpc-v2-cbor" + response = super()._prepare_additional_traits_in_response( + response, operation_model, request_id + ) + return response + + class S3ResponseSerializer(RestXMLResponseSerializer): """ The ``S3ResponseSerializer`` adds some minor logic to handle S3 specific peculiarities with the error response @@ -1573,7 +2189,7 @@ def _prepare_additional_traits_in_xml(self, root: ETree.Element | None, request_ root.attrib["xmlns"] = self.XML_NAMESPACE @staticmethod - def _timestamp_iso8601(value: datetime) -> str: + def _timestamp_iso8601(value: datetime.datetime) -> str: """ This is very specific to S3, S3 returns an ISO8601 timestamp but with milliseconds always set to 000 Some SDKs are very picky about the length @@ -1700,26 +2316,12 @@ class SqsJsonResponseSerializer(JSONResponseSerializer): "QueueNameExists": "QueueAlreadyExists", } - def _serialize_error( - self, - error: ServiceException, - response: Response, - shape: StructureShape, - operation_model: OperationModel, - mime_type: str, - request_id: str, - ) -> None: - """ - Overrides _serialize_error as SQS has a special header for query API legacy reason: 'x-amzn-query-error', - which contained the exception code as well as a Sender field. - Ex: 'x-amzn-query-error': 'InvalidParameterValue;Sender' - """ - # TODO: for body["__type"] = error.code, it seems AWS differs from what we send for SQS - # AWS: "com.amazon.coral.service#InvalidParameterValueException" - # or AWS: "com.amazonaws.sqs#BatchRequestTooLong" - # LocalStack: "InvalidParameterValue" - super()._serialize_error(error, response, shape, operation_model, mime_type, request_id) - # We need to add a prefix to certain errors, as they have been deleted in the specs. These will not change + # TODO: on body error serialization (body["__type"]),it seems AWS differs from what we send for SQS + # AWS: "com.amazon.coral.service#InvalidParameterValueException" + # or AWS: "com.amazonaws.sqs#BatchRequestTooLong" + # LocalStack: "InvalidParameterValue" + + def _add_query_compatible_error_header(self, response: Response, error: ServiceException): if error.code in self.JSON_TO_QUERY_ERROR_CODES: code = self.JSON_TO_QUERY_ERROR_CODES[error.code] elif error.code in self.QUERY_PREFIXED_ERRORS: @@ -1727,6 +2329,7 @@ def _serialize_error( else: code = error.code + # SQS exceptions all have sender fault set to False, so we hardcode it to `Sender` response.headers["x-amzn-query-error"] = f"{code};Sender" @@ -1744,11 +2347,14 @@ def gen_amzn_requestid(): @functools.cache -def create_serializer(service: ServiceModel) -> ResponseSerializer: +def create_serializer( + service: ServiceModel, protocol: ProtocolName | None = None +) -> ResponseSerializer: """ Creates the right serializer for the given service model. :param service: to create the serializer for + :param protocol: the protocol for the serializer. If not provided, fallback to the service's default protocol :return: ResponseSerializer which can handle the protocol of the service """ @@ -1768,17 +2374,22 @@ def create_serializer(service: ServiceModel) -> ResponseSerializer: "rest-json": RestJSONResponseSerializer, "rest-xml": RestXMLResponseSerializer, "ec2": EC2ResponseSerializer, + "smithy-rpc-v2-cbor": RpcV2CBORResponseSerializer, + # TODO: implement multi-protocol support for Kinesis, so that it can uses the `cbor` protocol and remove + # CBOR handling from JSONResponseParser + # this is not an "official" protocol defined from the spec, but is derived from ``json`` } + service_protocol = protocol or service.protocol # Try to select a service- and protocol-specific serializer implementation if ( service.service_name in service_specific_serializers - and service.protocol in service_specific_serializers[service.service_name] + and service_protocol in service_specific_serializers[service.service_name] ): - return service_specific_serializers[service.service_name][service.protocol]() + return service_specific_serializers[service.service_name][service_protocol]() else: # Otherwise, pick the protocol-specific serializer for the protocol of the service - return protocol_specific_serializers[service.protocol]() + return protocol_specific_serializers[service_protocol]() def aws_response_serializer( @@ -1809,7 +2420,7 @@ def my_route(request: Request): def _decorate(fn): service_model = load_service(service_name, protocol=protocol) operation_model = service_model.operation_model(operation) - serializer = create_serializer(service_model) + serializer = create_serializer(service_model, protocol=protocol) def _proxy(*args, **kwargs) -> WerkzeugResponse: # extract request from function invocation (decorator can be used for methods as well as for functions). diff --git a/localstack-core/localstack/aws/protocol/service_router.py b/localstack-core/localstack/aws/protocol/service_router.py index 5b3768fa882d6..dae3d3101e62c 100644 --- a/localstack-core/localstack/aws/protocol/service_router.py +++ b/localstack-core/localstack/aws/protocol/service_router.py @@ -6,9 +6,11 @@ from werkzeug.http import parse_dict_header from localstack.aws.spec import ( + ProtocolName, ServiceCatalog, ServiceModelIdentifier, get_service_catalog, + is_protocol_in_service_model_identifier, ) from localstack.http import Request from localstack.services.s3.utils import uses_host_addressing @@ -17,6 +19,23 @@ LOG = logging.getLogger(__name__) +_PROTOCOL_DETECTION_PRIORITY: list[ProtocolName] = [ + "smithy-rpc-v2-cbor", + "json", + "query", + "ec2", + "rest-json", + "rest-xml", +] + + +class ProtocolError(Exception): + """ + Error which is thrown if we cannot detect the protocol for the request. + """ + + pass + class _ServiceIndicators(NamedTuple): """ @@ -43,6 +62,7 @@ def _extract_service_indicators(request: Request) -> _ServiceIndicators: """Extracts all different fields that might indicate which service a request is targeting.""" x_amz_target = request.headers.get("x-amz-target") authorization = request.headers.get("authorization") + is_rpc_v2 = "rpc-v2-cbor" in request.headers.get("Smithy-Protocol", "") signing_name = None if authorization: @@ -55,7 +75,15 @@ def _extract_service_indicators(request: Request) -> _ServiceIndicators: except (ValueError, KeyError): LOG.debug("auth header could not be parsed for service routing: %s", authorization) pass - if x_amz_target: + if is_rpc_v2: + # https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html#requests + rpc_v2_params = request.path.lstrip("/").split("/") + if len(rpc_v2_params) >= 4: + *_, service_shape_name, __, operation = rpc_v2_params + target_prefix = service_shape_name.split("#")[-1] + else: + target_prefix, operation = None, None + elif x_amz_target: if "." in x_amz_target: target_prefix, operation = x_amz_target.split(".", 1) else: @@ -67,6 +95,48 @@ def _extract_service_indicators(request: Request) -> _ServiceIndicators: return _ServiceIndicators(signing_name, target_prefix, operation, request.host, request.path) +def _matches_protocol(request: Request, protocol: ProtocolName) -> bool: + headers = request.headers + mimetype = request.mimetype.lower() + match protocol: + case "smithy-rpc-v2-cbor": + # Every request for the rpcv2Cbor protocol MUST contain a `Smithy-Protocol` header with the value + # of `rpc-v2-cbor`. + # https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html + return headers.get("Smithy-Protocol", "") == "rpc-v2-cbor" + case "json": + return mimetype.startswith("application/x-amz-json") + case "query" | "ec2": + # https://smithy.io/2.0/aws/protocols/aws-query-protocol.html#request-serialization + return ( + mimetype.startswith("application/x-www-form-urlencoded") or "Action" in request.args + ) + case "rest-xml" | "rest-json": + # `rest-json` and `rest-xml` can accept any kind of Content-Type, and it can be configured on the operation + # level. + # https://smithy.io/2.0/aws/protocols/aws-restjson1-protocol.html + return True + case _: + return False + + +def match_available_protocols( + request: Request, available_protocols: list[ProtocolName] +) -> ProtocolName | None: + """ + Tries to match the current request and determine the protocol used amongst the available protocols given. + We use a priority order to try to determine the protocol, as some protocols are more permissive that others. + :param request: the incoming request + :param available_protocols: the available protocols of the Service the request is directed to + :return: the protocol matched, if any + """ + for protocol in _PROTOCOL_DETECTION_PRIORITY: + if protocol in available_protocols and _matches_protocol(request, protocol): + return protocol + + return None + + signing_name_path_prefix_rules = { # custom rules based on URI path prefixes that are not easily generalizable "apigateway": { @@ -248,10 +318,10 @@ def resolve_conflicts( # The `application/x-amz-json-1.0` header is mandatory for requests targeting SQS with the `json` protocol. We # can safely route them to the `sqs` JSON parser/serializer. If not present, route the request to the # sqs-query protocol. - content_type = request.headers.get("Content-Type") + protocol = match_available_protocols(request, available_protocols=["json", "query"]) return ( ServiceModelIdentifier("sqs") - if content_type == "application/x-amz-json-1.0" + if protocol == "json" else ServiceModelIdentifier("sqs", "query") ) @@ -266,7 +336,25 @@ def determine_aws_service_model_for_data_plane( custom_host_match = custom_host_addressing_rules(request.host) if custom_host_match: services = services or get_service_catalog() - return services.get(*custom_host_match) + return services.get(custom_host_match.name, custom_host_match.protocol) + + +def determine_aws_protocol(request: Request, service_model: ServiceModel) -> ProtocolName: + if not (protocols := service_model.metadata.get("protocols")): + # if the service does not define multiple protocols, return the `protocol` defined for the service + return service_model.protocol + + if len(protocols) == 1: + return protocols[0] + + if protocol := match_available_protocols(request, available_protocols=protocols): + return protocol + + raise ProtocolError( + f"Could not determine the protocol for the request: " + f"{request.method} {request.path} for the service '{service_model.service_name}' " + f"(available protocols: {protocols})" + ) def determine_aws_service_model( @@ -287,12 +375,13 @@ def determine_aws_service_model( signing_name_candidates = services.by_signing_name(signing_name) if len(signing_name_candidates) == 1: # a unique signing-name -> service name mapping is the case for ~75% of service operations - return services.get(*signing_name_candidates[0]) + candidate = signing_name_candidates[0] + return services.get(candidate.name, candidate.protocol) # try to find a match with the custom signing name rules custom_match = custom_signing_name_rules(signing_name, path) if custom_match: - return services.get(*custom_match) + return services.get(custom_match.name, custom_match.protocol) # still ambiguous - add the services to the list of candidates candidates.update(signing_name_candidates) @@ -302,33 +391,34 @@ def determine_aws_service_model( target_candidates = services.by_target_prefix(target_prefix) if len(target_candidates) == 1: # a unique target prefix - return services.get(*target_candidates[0]) + candidate = target_candidates[0] + return services.get(candidate.name, candidate.protocol) # still ambiguous - add the services to the list of candidates candidates.update(target_candidates) # exclude services where the operation is not contained in the service spec for service_identifier in list(candidates): - service = services.get(*service_identifier) + service = services.get(service_identifier.name, service_identifier.protocol) if operation not in service.operation_names: candidates.remove(service_identifier) else: # exclude services which have a target prefix (the current request does not have one) for service_identifier in list(candidates): - service = services.get(*service_identifier) + service = services.get(service_identifier.name, service_identifier.protocol) if service.metadata.get("targetPrefix") is not None: candidates.remove(service_identifier) if len(candidates) == 1: service_identifier = candidates.pop() - return services.get(*service_identifier) + return services.get(service_identifier.name, service_identifier.protocol) # 3. check the path if it is set and not a trivial root path if path and path != "/": # try to find a match with the custom path rules custom_path_match = custom_path_addressing_rules(path) if custom_path_match: - return services.get(*custom_path_match) + return services.get(custom_path_match.name, custom_path_match.protocol) # 4. check the host (custom host addressing rules) if host: @@ -337,12 +427,14 @@ def determine_aws_service_model( # this prevents a virtual host addressed bucket to be wrongly recognized if host.startswith(f"{prefix}.") and ".s3." not in host: if len(services_per_prefix) == 1: - return services.get(*services_per_prefix[0]) + candidate = services_per_prefix[0] + return services.get(candidate.name, candidate.protocol) candidates.update(services_per_prefix) custom_host_match = custom_host_addressing_rules(host) if custom_host_match: - return services.get(*custom_host_match) + candidate = custom_host_match[0] + return services.get(candidate.name, candidate.protocol) if request.shallow: # from here on we would need access to the request body, which doesn't exist for shallow requests like @@ -357,21 +449,28 @@ def determine_aws_service_model( query_candidates = [ service for service in services.by_operation(values["Action"]) - if service.protocol in ("ec2", "query") + if any( + is_protocol_in_service_model_identifier(protocol, service) + for protocol in ("ec2", "query") + ) ] if len(query_candidates) == 1: - return services.get(*query_candidates[0]) + candidate = query_candidates[0] + return services.get(candidate.name, candidate.protocol) if "Version" in values: for service_identifier in list(query_candidates): - service_model = services.get(*service_identifier) + service_model = services.get( + service_identifier.name, service_identifier.protocol + ) if values["Version"] != service_model.api_version: # the combination of Version and Action is not unique, add matches to the candidates query_candidates.remove(service_identifier) if len(query_candidates) == 1: - return services.get(*query_candidates[0]) + candidate = query_candidates[0] + return services.get(candidate.name, candidate.protocol) candidates.update(query_candidates) @@ -387,15 +486,16 @@ def determine_aws_service_model( # 6. resolve service spec conflicts resolved_conflict = resolve_conflicts(candidates, request) if resolved_conflict: - return services.get(*resolved_conflict) + return services.get(resolved_conflict.name, resolved_conflict.protocol) # 7. check the legacy S3 rules in the end legacy_match = legacy_s3_rules(request) if legacy_match: - return services.get(*legacy_match) + return services.get(legacy_match.name, legacy_match.protocol) if signing_name: return services.get(name=signing_name) if candidates: - return services.get(*candidates.pop()) + candidate = candidates.pop() + return services.get(candidate.name, candidate.protocol) return None diff --git a/localstack-core/localstack/aws/skeleton.py b/localstack-core/localstack/aws/skeleton.py index e72c87cea77e8..2e15595787cb7 100644 --- a/localstack-core/localstack/aws/skeleton.py +++ b/localstack-core/localstack/aws/skeleton.py @@ -130,14 +130,16 @@ def __init__(self, service: ServiceModel, implementation: Any | DispatchTable): self.dispatch_table = create_dispatch_table(implementation) def invoke(self, context: RequestContext) -> Response: - serializer = create_serializer(context.service) + serializer = create_serializer(context.service, context.protocol) if context.operation and context.service_request: # if the parsed request is already set in the context, re-use them operation, instance = context.operation, context.service_request else: # otherwise, parse the incoming HTTPRequest - operation, instance = create_parser(context.service).parse(context.request) + operation, instance = create_parser(context.service, context.protocol).parse( + context.request + ) context.operation = operation try: diff --git a/localstack-core/localstack/aws/spec-patches.json b/localstack-core/localstack/aws/spec-patches.json index 37cc8a5c27001..c1ebb922cfb38 100644 --- a/localstack-core/localstack/aws/spec-patches.json +++ b/localstack-core/localstack/aws/spec-patches.json @@ -1352,5 +1352,43 @@ "path": "/operations/CreateApiMapping/http/responseCode", "value": 200 } + ], + "cloudwatch/2010-08-01/service-2": [ + { + "op": "add", + "path": "/metadata/awsQueryCompatible", + "value": {} + }, + { + "op": "add", + "path": "/metadata/jsonVersion", + "value": "1.0" + }, + { + "op": "add", + "path": "/metadata/targetPrefix", + "value": "GraniteServiceVersion20100801" + }, + { + "op": "replace", + "path": "/metadata/protocol", + "value": "smithy-rpc-v2-cbor" + }, + { + "op": "replace", + "path": "/metadata/protocols", + "value": [ + "smithy-rpc-v2-cbor", + "json", + "query" + ] + }, + { + "op": "add", + "path": "/shapes/ConflictException/error", + "value": { + "httpStatusCode": 409 + } + } ] } diff --git a/localstack-core/localstack/aws/spec.py b/localstack-core/localstack/aws/spec.py index 3de44f66d1a23..f6d60dbe5de42 100644 --- a/localstack-core/localstack/aws/spec.py +++ b/localstack-core/localstack/aws/spec.py @@ -21,7 +21,7 @@ LOG = logging.getLogger(__name__) ServiceName = str -ProtocolName = Literal["query", "json", "rest-json", "rest-xml", "ec2"] +ProtocolName = Literal["query", "json", "rest-json", "rest-xml", "ec2", "smithy-rpc-v2-cbor"] class ServiceModelIdentifier(NamedTuple): @@ -33,6 +33,7 @@ class ServiceModelIdentifier(NamedTuple): name: ServiceName protocol: ProtocolName | None = None + protocols: tuple[ProtocolName] | None = None spec_patches_json = os.path.join(os.path.dirname(__file__), "spec-patches.json") @@ -114,9 +115,13 @@ def load_service( :raises: UnknownServiceProtocolError if the specific protocol of the service cannot be found """ service_description = loader.load_service_model(service, "service-2", version) + service_metadata = service_description.get("metadata", {}) + service_protocols = {service_metadata.get("protocol")} + if protocols := service_metadata.get("protocols"): + service_protocols.update(protocols) # check if the protocol is defined, and if so, if the loaded service defines this protocol - if protocol is not None and protocol != service_description.get("metadata", {}).get("protocol"): + if protocol is not None and protocol not in service_protocols: # if the protocol is defined, but not the one of the currently loaded service, # check if we already loaded the custom spec based on the naming convention (-), # f.e. "sqs-query" @@ -132,7 +137,7 @@ def load_service( # remove potential protocol names from the service name # FIXME add more protocols here if we have to internalize more than just sqs-query - # TODO this should not contain specific internalized serivce names + # TODO this should not contain specific internalized service names service = {"sqs-query": "sqs"}.get(service, service) return ServiceModel(service_description, service) @@ -149,6 +154,27 @@ def iterate_service_operations() -> Generator[tuple[ServiceModel, OperationModel yield service, service.operation_model(op_name) +def is_protocol_in_service_model_identifier( + protocol: ProtocolName, service_model_identifier: ServiceModelIdentifier +) -> bool: + """ + :param protocol: the protocol name to check + :param service_model_identifier: + :return: boolean to indicate if the protocol is available for that service + """ + protocols = service_model_identifier.protocols or [] + return protocol in protocols or protocol == service_model_identifier.protocol + + +def get_service_model_identifier(service_model: ServiceModel) -> ServiceModelIdentifier: + protocols = service_model.metadata.get("protocols") + return ServiceModelIdentifier( + name=service_model.service_name, + protocol=service_model.protocol, + protocols=tuple(protocols) if protocols else None, + ) + + @dataclasses.dataclass class ServiceCatalogIndex: """ @@ -178,9 +204,7 @@ def target_prefix_index(self) -> dict[str, list[ServiceModelIdentifier]]: for service_model in service_models: target_prefix = service_model.metadata.get("targetPrefix") if target_prefix: - result[target_prefix].append( - ServiceModelIdentifier(service_model.service_name, service_model.protocol) - ) + result[target_prefix].append(get_service_model_identifier(service_model)) return dict(result) @cached_property @@ -189,7 +213,7 @@ def signing_name_index(self) -> dict[str, list[ServiceModelIdentifier]]: for service_models in self._services.values(): for service_model in service_models: result[service_model.signing_name].append( - ServiceModelIdentifier(service_model.service_name, service_model.protocol) + get_service_model_identifier(service_model) ) return dict(result) @@ -201,11 +225,7 @@ def operations_index(self) -> dict[str, list[ServiceModelIdentifier]]: operations = service_model.operation_names if operations: for operation in operations: - result[operation].append( - ServiceModelIdentifier( - service_model.service_name, service_model.protocol - ) - ) + result[operation].append(get_service_model_identifier(service_model)) return dict(result) @cached_property @@ -214,7 +234,7 @@ def endpoint_prefix_index(self) -> dict[str, list[ServiceModelIdentifier]]: for service_models in self._services.values(): for service_model in service_models: result[service_model.endpoint_prefix].append( - ServiceModelIdentifier(service_model.service_name, service_model.protocol) + get_service_model_identifier(service_model) ) return dict(result) diff --git a/localstack-core/localstack/constants.py b/localstack-core/localstack/constants.py index c8833e557fced..27e3ace83c083 100644 --- a/localstack-core/localstack/constants.py +++ b/localstack-core/localstack/constants.py @@ -102,32 +102,6 @@ # strings with valid log levels for LS_LOG LOG_LEVELS = ("trace-internal", "trace", "debug", "info", "warn", "error", "warning") -# the version of elasticsearch that is pre-seeded into the base image (sync with Dockerfile.base) -ELASTICSEARCH_DEFAULT_VERSION = "Elasticsearch_7.10" -# See https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/aes-supported-plugins.html -ELASTICSEARCH_PLUGIN_LIST = [ - "analysis-icu", - "ingest-attachment", - "analysis-kuromoji", - "mapper-murmur3", - "mapper-size", - "analysis-phonetic", - "analysis-smartcn", - "analysis-stempel", - "analysis-ukrainian", -] -# Default ES modules to exclude (save apprx 66MB in the final image) -ELASTICSEARCH_DELETE_MODULES = ["ingest-geoip"] - -# the version of opensearch which is used by default -OPENSEARCH_DEFAULT_VERSION = "OpenSearch_2.11" - -# See https://docs.aws.amazon.com/opensearch-service/latest/developerguide/supported-plugins.html -OPENSEARCH_PLUGIN_LIST = [ - "ingest-attachment", - "analysis-kuromoji", -] - # API endpoint for analytics events API_ENDPOINT = os.environ.get("API_ENDPOINT") or "https://api.localstack.cloud/v1" # new analytics API endpoint @@ -171,9 +145,6 @@ DEFAULT_BUCKET_MARKER_LOCAL = "hot-reload" LEGACY_DEFAULT_BUCKET_MARKER_LOCAL = "__local__" -# user that starts the opensearch process if the current user is root -OS_USER_OPENSEARCH = "localstack" - # output string that indicates that the stack is ready READY_MARKER_OUTPUT = "Ready." diff --git a/localstack-core/localstack/dev/kubernetes/__main__.py b/localstack-core/localstack/dev/kubernetes/__main__.py index 28f9048388b3d..d8ecdcdda4bc3 100644 --- a/localstack-core/localstack/dev/kubernetes/__main__.py +++ b/localstack-core/localstack/dev/kubernetes/__main__.py @@ -32,7 +32,7 @@ def generate_mount_points( # container paths target_path = "/opt/code/localstack/" - venv_path = os.path.join(target_path, ".venv", "lib", "python3.11", "site-packages") + venv_path = os.path.join(target_path, ".venv", "lib", "python3.13", "site-packages") # Community code if pro: diff --git a/localstack-core/localstack/dev/run/paths.py b/localstack-core/localstack/dev/run/paths.py index 00c7c647fc424..1f7ec8b63ee0f 100644 --- a/localstack-core/localstack/dev/run/paths.py +++ b/localstack-core/localstack/dev/run/paths.py @@ -68,7 +68,7 @@ class ContainerPaths: """Important paths in the container""" project_dir: str = "/opt/code/localstack" - site_packages_target_dir: str = "/opt/code/localstack/.venv/lib/python3.11/site-packages" + site_packages_target_dir: str = "/opt/code/localstack/.venv/lib/python3.13/site-packages" docker_entrypoint: str = "/usr/local/bin/docker-entrypoint.sh" localstack_supervisor: str = "/usr/local/bin/localstack-supervisor" localstack_source_dir: str diff --git a/localstack-core/localstack/dns/server.py b/localstack-core/localstack/dns/server.py index 847f0233853ce..78e43b2bfc7ef 100644 --- a/localstack-core/localstack/dns/server.py +++ b/localstack-core/localstack/dns/server.py @@ -445,13 +445,22 @@ def _skip_local_resolution(self, request) -> bool: return True return False + def _find_matching_aliases(self, question: DNSQuestion) -> list[AliasTarget] | None: + """ + Find aliases matching the question, supporting wildcards. + """ + qlabel = DNSLabel(to_bytes(question.qname)) + qtype = RecordType[QTYPE[question.qtype]] + for (label, rtype), targets in self.aliases.items(): + if rtype == qtype and qlabel.matchWildcard(label): + return targets + return None + def _resolve_alias( self, request: DNSRecord, reply: DNSRecord, client_address: ClientAddress ) -> bool: if request.q.qtype in (QTYPE.A, QTYPE.AAAA, QTYPE.CNAME): - key = (DNSLabel(to_bytes(request.q.qname)), RecordType[QTYPE[request.q.qtype]]) - # check if we have aliases defined for our given qname/qtype pair - if aliases := self.aliases.get(key): + if aliases := self._find_matching_aliases(request.q): for alias in aliases: # if there is no health check, or the healthcheck is successful, we will consider this alias # take the first alias passing this check diff --git a/localstack-core/localstack/packages/plugins.py b/localstack-core/localstack/packages/plugins.py index fdeba86a04204..94d0cc62f09ee 100644 --- a/localstack-core/localstack/packages/plugins.py +++ b/localstack-core/localstack/packages/plugins.py @@ -5,14 +5,6 @@ if TYPE_CHECKING: from localstack.packages.ffmpeg import FfmpegPackageInstaller from localstack.packages.java import JavaPackageInstaller - from localstack.packages.terraform import TerraformPackageInstaller - - -@package(name="terraform") -def terraform_package() -> Package["TerraformPackageInstaller"]: - from .terraform import terraform_package - - return terraform_package @package(name="ffmpeg") diff --git a/localstack-core/localstack/packages/terraform.py b/localstack-core/localstack/packages/terraform.py deleted file mode 100644 index 5d5ad321e0a61..0000000000000 --- a/localstack-core/localstack/packages/terraform.py +++ /dev/null @@ -1,46 +0,0 @@ -import os -import platform - -from localstack.packages import InstallTarget, Package -from localstack.packages.core import ArchiveDownloadAndExtractInstaller -from localstack.utils.files import chmod_r -from localstack.utils.platform import get_arch - -TERRAFORM_VERSION = os.getenv("TERRAFORM_VERSION", "1.5.7") -TERRAFORM_URL_TEMPLATE = ( - "https://releases.hashicorp.com/terraform/{version}/terraform_{version}_{os}_{arch}.zip" -) -TERRAFORM_CHECKSUM_URL_TEMPLATE = ( - "https://releases.hashicorp.com/terraform/{version}/terraform_{version}_SHA256SUMS" -) - - -class TerraformPackage(Package["TerraformPackageInstaller"]): - def __init__(self) -> None: - super().__init__("Terraform", TERRAFORM_VERSION) - - def get_versions(self) -> list[str]: - return [TERRAFORM_VERSION] - - def _get_installer(self, version: str) -> "TerraformPackageInstaller": - return TerraformPackageInstaller("terraform", version) - - -class TerraformPackageInstaller(ArchiveDownloadAndExtractInstaller): - def _get_install_marker_path(self, install_dir: str) -> str: - return os.path.join(install_dir, "terraform") - - def _get_download_url(self) -> str: - system = platform.system().lower() - arch = get_arch() - return TERRAFORM_URL_TEMPLATE.format(version=TERRAFORM_VERSION, os=system, arch=arch) - - def _install(self, target: InstallTarget) -> None: - super()._install(target) - chmod_r(self.get_executable_path(), 0o777) # type: ignore[arg-type] - - def _get_checksum_url(self) -> str | None: - return TERRAFORM_CHECKSUM_URL_TEMPLATE.format(version=TERRAFORM_VERSION) - - -terraform_package = TerraformPackage() diff --git a/localstack-core/localstack/runtime/init.py b/localstack-core/localstack/runtime/init.py index 92a3f49840feb..52682bc5b87f9 100644 --- a/localstack-core/localstack/runtime/init.py +++ b/localstack-core/localstack/runtime/init.py @@ -89,7 +89,7 @@ class ShellScriptRunner(ScriptRunner): suffixes = [".sh"] def run(self, path: str) -> None: - exit_code = subprocess.call(args=[], executable=path) + exit_code = subprocess.call(args=[path]) if exit_code != 0: raise OSError(f"Script {path} returned a non-zero exit code {exit_code}") diff --git a/localstack-core/localstack/services/apigateway/legacy/provider.py b/localstack-core/localstack/services/apigateway/legacy/provider.py index da5a2dd29623d..31b42e6e171fe 100644 --- a/localstack-core/localstack/services/apigateway/legacy/provider.py +++ b/localstack-core/localstack/services/apigateway/legacy/provider.py @@ -2977,6 +2977,7 @@ def create_custom_context( ctx = create_aws_request_context( service_name=context.service.service_name, action=action, + protocol=context.service.protocol, parameters=parameters, region=context.region, ) diff --git a/localstack-core/localstack/services/apigateway/next_gen/execute_api/integrations/aws.py b/localstack-core/localstack/services/apigateway/next_gen/execute_api/integrations/aws.py index 6ad97a1a067bf..16a79b7e077af 100644 --- a/localstack-core/localstack/services/apigateway/next_gen/execute_api/integrations/aws.py +++ b/localstack-core/localstack/services/apigateway/next_gen/execute_api/integrations/aws.py @@ -211,6 +211,9 @@ def invoke(self, context: RestApiInvocationContext) -> EndpointResponse: action = parsed_uri["path"] if target := self.get_action_service_target(service_name, action): + # TODO: properly implement the auto-`Content-Type` headers depending on the service protocol + # e.g. `x-amz-json-1.0` for DynamoDB + # this is needed to properly support multi-protocol headers["X-Amz-Target"] = target query_params["Action"] = action diff --git a/localstack-core/localstack/services/apigateway/next_gen/execute_api/integrations/http.py b/localstack-core/localstack/services/apigateway/next_gen/execute_api/integrations/http.py index da60b9d1fa33a..6bc9d0f0fb163 100644 --- a/localstack-core/localstack/services/apigateway/next_gen/execute_api/integrations/http.py +++ b/localstack-core/localstack/services/apigateway/next_gen/execute_api/integrations/http.py @@ -8,7 +8,7 @@ from localstack.aws.api.apigateway import Integration from ..context import EndpointResponse, IntegrationRequest, RestApiInvocationContext -from ..gateway_response import ApiConfigurationError, IntegrationFailureError +from ..gateway_response import ApiConfigurationError, IntegrationFailureError, InternalServerError from ..header_utils import build_multi_value_headers from .core import RestApiIntegration @@ -72,7 +72,7 @@ def invoke(self, context: RestApiInvocationContext) -> EndpointResponse: except (requests.exceptions.InvalidURL, requests.exceptions.InvalidSchema) as e: LOG.warning("Execution failed due to configuration error: Invalid endpoint address") LOG.debug("The URI specified for the HTTP/HTTP_PROXY integration is invalid: %s", uri) - raise ApiConfigurationError("Internal server error") from e + raise InternalServerError("Internal server error") from e except (requests.exceptions.Timeout, requests.exceptions.SSLError) as e: # TODO make the exception catching more fine grained @@ -127,7 +127,7 @@ def invoke(self, context: RestApiInvocationContext) -> EndpointResponse: except (requests.exceptions.InvalidURL, requests.exceptions.InvalidSchema) as e: LOG.warning("Execution failed due to configuration error: Invalid endpoint address") LOG.debug("The URI specified for the HTTP/HTTP_PROXY integration is invalid: %s", uri) - raise ApiConfigurationError("Internal server error") from e + raise InternalServerError("Internal server error") from e except (requests.exceptions.Timeout, requests.exceptions.SSLError): # TODO make the exception catching more fine grained diff --git a/localstack-core/localstack/services/apigateway/next_gen/execute_api/test_invoke.py b/localstack-core/localstack/services/apigateway/next_gen/execute_api/test_invoke.py index 979779fe52f4c..b2a6f86ea892d 100644 --- a/localstack-core/localstack/services/apigateway/next_gen/execute_api/test_invoke.py +++ b/localstack-core/localstack/services/apigateway/next_gen/execute_api/test_invoke.py @@ -62,6 +62,17 @@ {formatted_date} : Method completed with status: {method_response_status} """ +TEST_INVOKE_TEMPLATE_FAILED = """Execution log for request {request_id} +{formatted_date} : Starting execution for request: {request_id} +{formatted_date} : HTTP Method: {request_method}, Resource Path: {resource_path} +{formatted_date} : Method request path: {method_request_path_parameters} +{formatted_date} : Method request query string: {method_request_query_string} +{formatted_date} : Method request headers: {method_request_headers} +{formatted_date} : Method request body before transformations: {method_request_body} +{formatted_date} : Execution failed due to {error_type}: {error_message} +{formatted_date} : Method completed with status: {method_response_status} +""" + def _dump_headers(headers: Headers) -> str: if not headers: @@ -80,9 +91,9 @@ def log_template(invocation_context: RestApiInvocationContext, response_headers: formatted_date = datetime.datetime.now(tz=datetime.UTC).strftime("%a %b %d %H:%M:%S %Z %Y") request = invocation_context.invocation_request context_var = invocation_context.context_variables - integration_req = invocation_context.integration_request - endpoint_resp = invocation_context.endpoint_response - method_resp = invocation_context.invocation_response + integration_req = invocation_context.integration_request or {} + endpoint_resp = invocation_context.endpoint_response or {} + method_resp = invocation_context.invocation_response or {} # TODO: if endpoint_uri is an ARN, it means it's an AWS_PROXY integration # this should be transformed to the true URL of a lambda invoke call endpoint_uri = integration_req.get("uri", "") @@ -116,7 +127,7 @@ def log_mock_template( formatted_date = datetime.datetime.now(tz=datetime.UTC).strftime("%a %b %d %H:%M:%S %Z %Y") request = invocation_context.invocation_request context_var = invocation_context.context_variables - method_resp = invocation_context.invocation_response + method_resp = invocation_context.invocation_response or {} return TEST_INVOKE_TEMPLATE_MOCK.format( formatted_date=formatted_date, @@ -133,6 +144,29 @@ def log_mock_template( ) +def log_failed_template( + invocation_context: RestApiInvocationContext, response_status_code: int +) -> str: + formatted_date = datetime.datetime.now(tz=datetime.UTC).strftime("%a %b %d %H:%M:%S %Z %Y") + request = invocation_context.invocation_request + context_var = invocation_context.context_variables + + return TEST_INVOKE_TEMPLATE_FAILED.format( + formatted_date=formatted_date, + request_id=context_var["requestId"], + resource_path=request["path"], + request_method=request["http_method"], + method_request_path_parameters=dict_to_string(request["path_parameters"]), + method_request_query_string=dict_to_string(request["query_string_parameters"]), + method_request_headers=_dump_headers(request.get("headers")), + method_request_body=to_str(request.get("body", "")), + method_response_status=response_status_code, + # TODO: fix the error message + error_type="", + error_message="", + ) + + def create_test_chain() -> HandlerChain[RestApiInvocationContext]: return HandlerChain( request_handlers=[ @@ -216,7 +250,9 @@ def create_test_invocation_context( responseOverride=ContextVarsResponseOverride(header={}, status=0), ) invocation_context.trace_id = parse_handler.populate_trace_id({}) - resource_method = resource["resourceMethods"][http_method] + resource_method = ( + resource["resourceMethods"].get(http_method) or resource["resourceMethods"]["ANY"] + ) invocation_context.resource = resource invocation_context.resource_method = resource_method invocation_context.integration = resource_method["methodIntegration"] @@ -256,7 +292,15 @@ def run_test_invocation( # AWS does not return the Content-Length for TestInvokeMethod response_headers.remove("Content-Length") - if is_mock_integration: + if not invocation_context.invocation_response: + # TODO: this is an heuristic to guess if we encounter an exception in the call + # in the future, we should attach the exception to the context so we could act on it and properly + # log as we go through the invocation, so that if we have an error we stop logging at the right moment + for header in ("Content-Type", "X-Amzn-Trace-Id"): + response_headers.remove(header) + log = log_failed_template(invocation_context, test_response.status_code) + + elif is_mock_integration: # TODO: revisit how we're building the logs log = log_mock_template(invocation_context, response_headers) else: diff --git a/localstack-core/localstack/services/apigateway/next_gen/provider.py b/localstack-core/localstack/services/apigateway/next_gen/provider.py index 5153463c60a4c..ac1c0029fee67 100644 --- a/localstack-core/localstack/services/apigateway/next_gen/provider.py +++ b/localstack-core/localstack/services/apigateway/next_gen/provider.py @@ -429,6 +429,11 @@ def test_invoke_method( if not resource: raise NotFoundException("Invalid Resource identifier specified") + resource_methods = resource.resource_methods + + if request["httpMethod"] not in resource_methods and "ANY" not in resource_methods: + raise NotFoundException("Invalid Method identifier specified") + # test httpMethod rest_api_container = get_rest_api_container(context, rest_api_id=rest_api_id) diff --git a/localstack-core/localstack/services/cloudformation/engine/entities.py b/localstack-core/localstack/services/cloudformation/engine/entities.py index 0e56e940f5cd1..633bf3be9b86d 100644 --- a/localstack-core/localstack/services/cloudformation/engine/entities.py +++ b/localstack-core/localstack/services/cloudformation/engine/entities.py @@ -14,7 +14,13 @@ ) from localstack.utils.aws import arns from localstack.utils.collections import select_attributes -from localstack.utils.id_generator import ExistingIds, ResourceIdentifier, Tags, generate_short_uid +from localstack.utils.id_generator import ( + ExistingIds, + ResourceIdentifier, + Tags, + generate_short_uid, + generate_uid, +) from localstack.utils.json import clone_safe from localstack.utils.objects import recurse_object from localstack.utils.strings import long_uid, short_uid @@ -75,6 +81,11 @@ def generate(self, existing_ids: ExistingIds = None, tags: Tags = None) -> str: return generate_short_uid(resource_identifier=self, existing_ids=existing_ids, tags=tags) +class StackIdentifierV2(StackIdentifier): + def generate(self, existing_ids: ExistingIds = None, tags: Tags = None) -> str: + return generate_uid(resource_identifier=self, existing_ids=existing_ids, tags=tags) + + # TODO: remove metadata (flatten into individual fields) class Stack: change_sets: list["StackChangeSet"] diff --git a/localstack-core/localstack/services/cloudformation/engine/v2/change_set_model_preproc.py b/localstack-core/localstack/services/cloudformation/engine/v2/change_set_model_preproc.py index b0abaf91d9a02..8cfc0993485f4 100644 --- a/localstack-core/localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +++ b/localstack-core/localstack/services/cloudformation/engine/v2/change_set_model_preproc.py @@ -45,6 +45,7 @@ ChangeSetModelVisitor, ) from localstack.services.cloudformation.engine.v2.resolving import ( + REGEX_DYNAMIC_REF, extract_dynamic_reference, perform_dynamic_reference_lookup, ) @@ -432,6 +433,7 @@ def _maybe_perform_on_delta( def _perform_dynamic_replacements(self, value: _T) -> _T: if not isinstance(value, str): return value + if dynamic_ref := extract_dynamic_reference(value): new_value = perform_dynamic_reference_lookup( reference=dynamic_ref, @@ -439,7 +441,11 @@ def _perform_dynamic_replacements(self, value: _T) -> _T: region_name=self._change_set.region_name, ) if new_value: - return new_value + # We need to use a function here, to avoid backslash processing by regex. + # From the regex sub documentation: + # repl can be a string or a function; if it is a string, any backslash escapes in it are processed. + # Using a function, we can avoid this processing. + return REGEX_DYNAMIC_REF.sub(lambda _: new_value, value) return value diff --git a/localstack-core/localstack/services/cloudformation/engine/v2/resolving.py b/localstack-core/localstack/services/cloudformation/engine/v2/resolving.py index b99630c54a7a7..0be0bffd8a8de 100644 --- a/localstack-core/localstack/services/cloudformation/engine/v2/resolving.py +++ b/localstack-core/localstack/services/cloudformation/engine/v2/resolving.py @@ -10,7 +10,9 @@ LOG = logging.getLogger(__name__) -REGEX_DYNAMIC_REF = re.compile(r"{{resolve:([^:]+):(.+)}}") +# CloudFormation allows using dynamic references in `Fn::Sub` expressions, so we must make sure +# we don't capture the parameter usage by excluding ${} characters +REGEX_DYNAMIC_REF = re.compile(r"{{resolve:([^:]+):([^${}]+)}}") @dataclass @@ -21,7 +23,7 @@ class DynamicReference: def extract_dynamic_reference(value: Any) -> DynamicReference | None: if isinstance(value, str): - if dynamic_ref_match := REGEX_DYNAMIC_REF.match(value): + if dynamic_ref_match := REGEX_DYNAMIC_REF.search(value): return DynamicReference(dynamic_ref_match[1], dynamic_ref_match[2]) return None @@ -90,9 +92,9 @@ def perform_dynamic_reference_lookup( raise RuntimeError( f"JSON value for {reference.service_name}.{reference.reference_key} not present" ) - return json_secret[json_key] + return str(json_secret[json_key]) else: - return secret_value + return str(secret_value) LOG.warning( "Unsupported service for dynamic parameter: service_name=%s", reference.service_name diff --git a/localstack-core/localstack/services/cloudformation/resources.py b/localstack-core/localstack/services/cloudformation/resources.py new file mode 100644 index 0000000000000..5c338d20bb0ff --- /dev/null +++ b/localstack-core/localstack/services/cloudformation/resources.py @@ -0,0 +1,24149 @@ +"""Generated by scripts/update_cfn_resources.py – do not edit manually.""" + +AWS_AVAILABLE_CFN_RESOURCES = { + "AMZN::SDC::Deployment": [ + "ap-northeast-2", + ], + "AWS::ACMPCA::Certificate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ACMPCA::CertificateAuthority": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ACMPCA::CertificateAuthorityActivation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ACMPCA::Permission": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AIOps::InvestigationGroup": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::APS::ResourcePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::APS::RuleGroupsNamespace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::APS::Scraper": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::APS::Workspace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ARCRegionSwitch::Plan": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ARCZonalShift::AutoshiftObserverNotificationStatus": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ARCZonalShift::ZonalAutoshiftConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AccessAnalyzer::Analyzer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AmazonMQ::Broker": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AmazonMQ::Configuration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AmazonMQ::ConfigurationAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Amplify::App": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Amplify::Branch": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Amplify::Domain": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AmplifyUIBuilder::Component": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AmplifyUIBuilder::Form": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AmplifyUIBuilder::Theme": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::Account": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::ApiKey": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::Authorizer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::BasePathMapping": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::BasePathMappingV2": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::ClientCertificate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::Deployment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::DocumentationPart": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::DocumentationVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::DomainName": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::DomainNameAccessAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::DomainNameV2": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::GatewayResponse": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::Method": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::Model": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::RequestValidator": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::Resource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::RestApi": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::Stage": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::UsagePlan": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::UsagePlanKey": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGateway::VpcLink": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::Api": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::ApiGatewayManagedOverrides": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::ApiMapping": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::Authorizer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::Deployment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::DomainName": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::Integration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::IntegrationResponse": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::Model": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::Route": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::RouteResponse": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::RoutingRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::Stage": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApiGatewayV2::VpcLink": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppConfig::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppConfig::ConfigurationProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppConfig::Deployment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppConfig::DeploymentStrategy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppConfig::Environment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppConfig::Extension": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppConfig::ExtensionAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppConfig::HostedConfigurationVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppFlow::Connector": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppFlow::ConnectorProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppFlow::Flow": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppIntegrations::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::AppIntegrations::DataIntegration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::AppIntegrations::EventIntegration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::AppMesh::GatewayRoute": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppMesh::Mesh": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppMesh::Route": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppMesh::VirtualGateway": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppMesh::VirtualNode": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppMesh::VirtualRouter": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppMesh::VirtualService": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppRunner::AutoScalingConfiguration": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::AppRunner::ObservabilityConfiguration": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::AppRunner::Service": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::AppRunner::VpcConnector": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::AppRunner::VpcIngressConnection": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::AppStream::AppBlock": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::AppStream::AppBlockBuilder": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::AppStream::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::AppStream::ApplicationEntitlementAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::AppStream::ApplicationFleetAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::AppStream::DirectoryConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppStream::Entitlement": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::AppStream::Fleet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppStream::ImageBuilder": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppStream::Stack": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppStream::StackFleetAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppStream::StackUserAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppStream::User": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppSync::Api": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppSync::ApiCache": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppSync::ApiKey": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppSync::ChannelNamespace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppSync::DataSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppSync::DomainName": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppSync::DomainNameApiAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppSync::FunctionConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppSync::GraphQLApi": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppSync::GraphQLSchema": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppSync::Resolver": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppSync::SourceApiAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AppTest::TestCase": [ + "ap-southeast-2", + "eu-central-1", + "sa-east-1", + "us-east-1", + ], + "AWS::ApplicationAutoScaling::ScalableTarget": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApplicationAutoScaling::ScalingPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApplicationInsights::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApplicationSignals::Discovery": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ApplicationSignals::ServiceLevelObjective": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Athena::CapacityReservation": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-north-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Athena::DataCatalog": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Athena::NamedQuery": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Athena::PreparedStatement": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Athena::WorkGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AuditManager::Assessment": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AutoScaling::AutoScalingGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AutoScaling::LaunchConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AutoScaling::LifecycleHook": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AutoScaling::ScalingPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AutoScaling::ScheduledAction": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AutoScaling::WarmPool": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::AutoScalingPlans::ScalingPlan": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::B2BI::Capability": [ + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::B2BI::Partnership": [ + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::B2BI::Profile": [ + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::B2BI::Transformer": [ + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::BCMDataExports::Export": [ + "eu-west-2", + "us-east-1", + ], + "AWS::Backup::BackupPlan": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Backup::BackupSelection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Backup::BackupVault": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Backup::Framework": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Backup::LogicallyAirGappedBackupVault": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Backup::ReportPlan": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Backup::RestoreTestingPlan": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Backup::RestoreTestingSelection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::BackupGateway::Hypervisor": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Batch::ComputeEnvironment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Batch::ConsumableResource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Batch::JobDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Batch::JobQueue": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Batch::SchedulingPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Batch::ServiceEnvironment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Bedrock::Agent": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Bedrock::AgentAlias": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Bedrock::ApplicationInferenceProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Bedrock::AutomatedReasoningPolicy": [ + "eu-central-1", + "eu-west-1", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Bedrock::AutomatedReasoningPolicyVersion": [ + "eu-central-1", + "eu-west-1", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Bedrock::Blueprint": [ + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Bedrock::DataAutomationProject": [ + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Bedrock::DataSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Bedrock::Flow": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Bedrock::FlowAlias": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Bedrock::FlowVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Bedrock::Guardrail": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Bedrock::GuardrailVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Bedrock::IntelligentPromptRouter": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Bedrock::KnowledgeBase": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Bedrock::Prompt": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Bedrock::PromptVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Billing::BillingView": [ + "us-east-1", + "us-west-2", + ], + "AWS::BillingConductor::BillingGroup": [ + "us-east-1", + ], + "AWS::BillingConductor::CustomLineItem": [ + "us-east-1", + ], + "AWS::BillingConductor::PricingPlan": [ + "us-east-1", + ], + "AWS::BillingConductor::PricingRule": [ + "us-east-1", + ], + "AWS::Budgets::Budget": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Budgets::BudgetsAction": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CE::AnomalyMonitor": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CE::AnomalySubscription": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CE::CostCategory": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CUR::ReportDefinition": [ + "us-east-1", + ], + "AWS::Cassandra::Keyspace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cassandra::Table": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cassandra::Type": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CertificateManager::Account": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CertificateManager::Certificate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Chatbot::CustomAction": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Chatbot::MicrosoftTeamsChannelConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Chatbot::SlackChannelConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CleanRooms::AnalysisTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CleanRooms::Collaboration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CleanRooms::ConfiguredTable": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CleanRooms::ConfiguredTableAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CleanRooms::IdMappingTable": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CleanRooms::IdNamespaceAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CleanRooms::Membership": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CleanRooms::PrivacyBudgetTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CleanRoomsML::TrainingDataset": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Cloud9::EnvironmentEC2": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::CustomResource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::GuardHook": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::HookDefaultVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::HookTypeConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::HookVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::LambdaHook": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::Macro": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::ModuleDefaultVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::ModuleVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::PublicTypeVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::Publisher": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::ResourceDefaultVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::ResourceVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::Stack": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::StackSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::TypeActivation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::WaitCondition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFormation::WaitConditionHandle": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::AnycastIpList": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::CachePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::CloudFrontOriginAccessIdentity": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::ConnectionGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::ContinuousDeploymentPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::Distribution": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::DistributionTenant": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::Function": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::KeyGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::KeyValueStore": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::MonitoringSubscription": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::OriginAccessControl": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::OriginRequestPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::PublicKey": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::RealtimeLogConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::ResponseHeadersPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::StreamingDistribution": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudFront::VpcOrigin": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudTrail::Channel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudTrail::Dashboard": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudTrail::EventDataStore": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudTrail::ResourcePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudTrail::Trail": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudWatch::Alarm": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudWatch::AnomalyDetector": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudWatch::CompositeAlarm": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudWatch::Dashboard": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudWatch::InsightRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CloudWatch::MetricStream": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeArtifact::Domain": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CodeArtifact::PackageGroup": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CodeArtifact::Repository": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CodeBuild::Fleet": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CodeBuild::Project": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeBuild::ReportGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeBuild::SourceCredential": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeCommit::Repository": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeConnections::Connection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeDeploy::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeDeploy::DeploymentConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeDeploy::DeploymentGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeGuruProfiler::ProfilingGroup": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CodeGuruReviewer::RepositoryAssociation": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::CodePipeline::CustomActionType": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodePipeline::Pipeline": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodePipeline::Webhook": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeStar::GitHubRepository": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeStarConnections::Connection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeStarConnections::RepositoryLink": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeStarConnections::SyncConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CodeStarNotifications::NotificationRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::IdentityPool": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::IdentityPoolPrincipalTag": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::IdentityPoolRoleAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::LogDeliveryConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::ManagedLoginBranding": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::UserPool": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::UserPoolClient": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::UserPoolDomain": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::UserPoolGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::UserPoolIdentityProvider": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::UserPoolResourceServer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::UserPoolRiskConfigurationAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::UserPoolUICustomizationAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::UserPoolUser": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Cognito::UserPoolUserToGroupAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Comprehend::DocumentClassifier": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Comprehend::Flywheel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Config::AggregationAuthorization": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Config::ConfigRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Config::ConfigurationAggregator": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Config::ConfigurationRecorder": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Config::ConformancePack": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Config::DeliveryChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Config::OrganizationConfigRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Config::OrganizationConformancePack": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Config::RemediationConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Config::StoredQuery": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Connect::AgentStatus": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::ApprovedOrigin": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::ContactFlow": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::ContactFlowModule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::ContactFlowVersion": [ + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::EmailAddress": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::EvaluationForm": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::HoursOfOperation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::Instance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::InstanceStorageConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::IntegrationAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::PhoneNumber": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::PredefinedAttribute": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::Prompt": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::Queue": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::QuickConnect": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::RoutingProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::Rule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::SecurityKey": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::SecurityProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::TaskTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::TrafficDistributionGroup": [ + "ap-northeast-1", + "ap-northeast-3", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::User": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::UserHierarchyGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::UserHierarchyStructure": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::View": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Connect::ViewVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::ConnectCampaigns::Campaign": [ + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::ConnectCampaignsV2::Campaign": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::ControlTower::EnabledBaseline": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ControlTower::EnabledControl": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ControlTower::LandingZone": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::CustomerProfiles::CalculatedAttributeDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::CustomerProfiles::Domain": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::CustomerProfiles::EventStream": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::CustomerProfiles::EventTrigger": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::CustomerProfiles::Integration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::CustomerProfiles::ObjectType": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::CustomerProfiles::SegmentDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::DAX::Cluster": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DAX::ParameterGroup": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DAX::SubnetGroup": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DLM::LifecyclePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DMS::Certificate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DMS::DataMigration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DMS::DataProvider": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DMS::Endpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DMS::EventSubscription": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DMS::InstanceProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DMS::MigrationProject": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DMS::ReplicationConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DMS::ReplicationInstance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DMS::ReplicationSubnetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DMS::ReplicationTask": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DSQL::Cluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataBrew::Dataset": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataBrew::Job": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataBrew::Project": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataBrew::Recipe": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataBrew::Ruleset": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataBrew::Schedule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataPipeline::Pipeline": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataSync::Agent": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataSync::LocationAzureBlob": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataSync::LocationEFS": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataSync::LocationFSxLustre": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataSync::LocationFSxONTAP": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataSync::LocationFSxOpenZFS": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataSync::LocationFSxWindows": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataSync::LocationHDFS": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataSync::LocationNFS": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataSync::LocationObjectStorage": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataSync::LocationS3": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataSync::LocationSMB": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataSync::Task": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DataZone::Connection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::DataSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::Domain": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::DomainUnit": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::Environment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::EnvironmentActions": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::EnvironmentBlueprintConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::EnvironmentProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::GroupProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::Owner": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::PolicyGrant": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::Project": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::ProjectMembership": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::ProjectProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::SubscriptionTarget": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DataZone::UserProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Deadline::Farm": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Deadline::Fleet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Deadline::LicenseEndpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Deadline::Limit": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Deadline::MeteredProduct": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Deadline::Monitor": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Deadline::Queue": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Deadline::QueueEnvironment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Deadline::QueueFleetAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Deadline::QueueLimitAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Deadline::StorageProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Detective::Graph": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Detective::MemberInvitation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Detective::OrganizationAdmin": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DevOpsGuru::LogAnomalyDetectionIntegration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DevOpsGuru::NotificationChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DevOpsGuru::ResourceCollection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DeviceFarm::DevicePool": [ + "us-west-2", + ], + "AWS::DeviceFarm::InstanceProfile": [ + "us-west-2", + ], + "AWS::DeviceFarm::NetworkProfile": [ + "us-west-2", + ], + "AWS::DeviceFarm::Project": [ + "us-west-2", + ], + "AWS::DeviceFarm::TestGridProject": [ + "us-west-2", + ], + "AWS::DeviceFarm::VPCEConfiguration": [ + "us-west-2", + ], + "AWS::DirectoryService::MicrosoftAD": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DirectoryService::SimpleAD": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DocDB::DBCluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DocDB::DBClusterParameterGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DocDB::DBInstance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DocDB::DBSubnetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DocDB::EventSubscription": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DocDBElastic::Cluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::DynamoDB::GlobalTable": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::DynamoDB::Table": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::CapacityReservation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::CapacityReservationFleet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::CarrierGateway": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::EC2::ClientVpnAuthorizationRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::ClientVpnEndpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::ClientVpnRoute": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::ClientVpnTargetNetworkAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::CustomerGateway": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::DHCPOptions": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::EC2Fleet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::EIP": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::EIPAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::EgressOnlyInternetGateway": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::EnclaveCertificateIamRoleAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::FlowLog": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::GatewayRouteTableAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::Host": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::IPAM": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::IPAMAllocation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::IPAMPool": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::IPAMPoolCidr": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::IPAMResourceDiscovery": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::IPAMResourceDiscoveryAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::IPAMScope": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::Instance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::InstanceConnectEndpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::InternetGateway": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::IpPoolRouteTableAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::KeyPair": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::LaunchTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::LocalGatewayRoute": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::LocalGatewayRouteTable": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::LocalGatewayRouteTableVPCAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::LocalGatewayRouteTableVirtualInterfaceGroupAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::NatGateway": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::NetworkAcl": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::NetworkAclEntry": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::NetworkInsightsAccessScope": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::NetworkInsightsAccessScopeAnalysis": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::NetworkInsightsAnalysis": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::NetworkInsightsPath": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::NetworkInterface": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::NetworkInterfaceAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::NetworkInterfacePermission": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::NetworkPerformanceMetricSubscription": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::PlacementGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::PrefixList": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::Route": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::RouteServer": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::RouteServerAssociation": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::RouteServerEndpoint": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::RouteServerPeer": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::RouteServerPropagation": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::RouteTable": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::SecurityGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::SecurityGroupEgress": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::SecurityGroupIngress": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::SecurityGroupVpcAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::SnapshotBlockPublicAccess": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::SpotFleet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::Subnet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::SubnetCidrBlock": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::SubnetNetworkAclAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::SubnetRouteTableAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TrafficMirrorFilter": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TrafficMirrorFilterRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TrafficMirrorSession": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TrafficMirrorTarget": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGateway": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayConnect": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayConnectPeer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayMulticastDomain": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayMulticastDomainAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayMulticastGroupMember": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayMulticastGroupSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayPeeringAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayRoute": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayRouteTable": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayRouteTableAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayRouteTablePropagation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::TransitGatewayVpcAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPC": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPCBlockPublicAccessExclusion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPCBlockPublicAccessOptions": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPCCidrBlock": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPCDHCPOptionsAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPCEndpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPCEndpointConnectionNotification": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPCEndpointService": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPCEndpointServicePermissions": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPCGatewayAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPCPeeringConnection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPNConnection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPNConnectionRoute": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPNGateway": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VPNGatewayRoutePropagation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VerifiedAccessEndpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VerifiedAccessGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VerifiedAccessInstance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VerifiedAccessTrustProvider": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::Volume": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EC2::VolumeAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECR::PublicRepository": [ + "us-east-1", + ], + "AWS::ECR::PullThroughCacheRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECR::RegistryPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECR::RegistryScanningConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECR::ReplicationConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECR::Repository": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECR::RepositoryCreationTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECS::CapacityProvider": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECS::Cluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECS::ClusterCapacityProviderAssociations": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECS::PrimaryTaskSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECS::Service": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECS::TaskDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ECS::TaskSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EFS::AccessPoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EFS::FileSystem": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EFS::MountTarget": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EKS::AccessEntry": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EKS::Addon": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EKS::Cluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EKS::FargateProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EKS::IdentityProviderConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EKS::Nodegroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EKS::PodIdentityAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EMR::Cluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EMR::InstanceFleetConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EMR::InstanceGroupConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EMR::SecurityConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EMR::Step": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EMR::Studio": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EMR::StudioSessionMapping": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EMR::WALWorkspace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::EMRContainers::VirtualCluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EMRServerless::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EVS::Environment": [ + "ap-northeast-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::ElastiCache::CacheCluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElastiCache::GlobalReplicationGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElastiCache::ParameterGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElastiCache::ReplicationGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElastiCache::SecurityGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElastiCache::SecurityGroupIngress": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElastiCache::ServerlessCache": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElastiCache::SubnetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElastiCache::User": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElastiCache::UserGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElasticBeanstalk::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElasticBeanstalk::ApplicationVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElasticBeanstalk::ConfigurationTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElasticBeanstalk::Environment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElasticLoadBalancing::LoadBalancer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElasticLoadBalancingV2::Listener": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElasticLoadBalancingV2::ListenerCertificate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElasticLoadBalancingV2::ListenerRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElasticLoadBalancingV2::LoadBalancer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElasticLoadBalancingV2::TargetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElasticLoadBalancingV2::TrustStore": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ElasticLoadBalancingV2::TrustStoreRevocation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Elasticsearch::Domain": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EntityResolution::IdMappingWorkflow": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::EntityResolution::IdNamespace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::EntityResolution::MatchingWorkflow": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::EntityResolution::PolicyStatement": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::EntityResolution::SchemaMapping": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::EventSchemas::Discoverer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EventSchemas::Registry": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EventSchemas::RegistryPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::EventSchemas::Schema": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Events::ApiDestination": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Events::Archive": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Events::Connection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Events::Endpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Events::EventBus": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Events::EventBusPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Events::Rule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Evidently::Experiment": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Evidently::Feature": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Evidently::Launch": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Evidently::Project": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Evidently::Segment": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::FIS::ExperimentTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::FIS::TargetAccountConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::FMS::NotificationChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::FMS::Policy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::FMS::ResourceSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::FSx::DataRepositoryAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::FSx::FileSystem": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::FSx::S3AccessPointAttachment": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::FSx::Snapshot": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::FSx::StorageVirtualMachine": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::FSx::Volume": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::FinSpace::Environment": [ + "ca-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Forecast::Dataset": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Forecast::DatasetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::FraudDetector::Detector": [ + "ap-southeast-1", + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::FraudDetector::EntityType": [ + "ap-southeast-1", + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::FraudDetector::EventType": [ + "ap-southeast-1", + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::FraudDetector::Label": [ + "ap-southeast-1", + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::FraudDetector::List": [ + "ap-southeast-1", + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::FraudDetector::Outcome": [ + "ap-southeast-1", + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::FraudDetector::Variable": [ + "ap-southeast-1", + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::GameLift::Alias": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GameLift::Build": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GameLift::ContainerFleet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::GameLift::ContainerGroupDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::GameLift::Fleet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GameLift::GameServerGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GameLift::GameSessionQueue": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GameLift::Location": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GameLift::MatchmakingConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GameLift::MatchmakingRuleSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GameLift::Script": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GameLiftStreams::Application": [ + "ap-northeast-1", + "eu-central-1", + "us-east-2", + "us-west-2", + ], + "AWS::GameLiftStreams::StreamGroup": [ + "ap-northeast-1", + "eu-central-1", + "us-east-2", + "us-west-2", + ], + "AWS::GammaDilithium::JobDefinition": [ + "ap-southeast-1", + "us-west-1", + ], + "AWS::GlobalAccelerator::Accelerator": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GlobalAccelerator::CrossAccountAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GlobalAccelerator::EndpointGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GlobalAccelerator::Listener": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::Classifier": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::Connection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::Crawler": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::CustomEntityType": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::DataCatalogEncryptionSettings": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::DataQualityRuleset": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::Database": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::DevEndpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::Job": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::MLTransform": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::Partition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::Registry": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::Schema": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::SchemaVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::SchemaVersionMetadata": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::SecurityConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::Table": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::TableOptimizer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Glue::Trigger": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::UsageProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Glue::Workflow": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Grafana::Workspace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Greengrass::ConnectorDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::ConnectorDefinitionVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::CoreDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::CoreDefinitionVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::DeviceDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::DeviceDefinitionVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::FunctionDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::FunctionDefinitionVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::Group": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::GroupVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::LoggerDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::LoggerDefinitionVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::ResourceDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::ResourceDefinitionVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::SubscriptionDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Greengrass::SubscriptionDefinitionVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GreengrassV2::ComponentVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::GreengrassV2::Deployment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::GroundStation::Config": [ + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::GroundStation::DataflowEndpointGroup": [ + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::GroundStation::MissionProfile": [ + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::GuardDuty::Detector": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GuardDuty::Filter": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GuardDuty::IPSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GuardDuty::MalwareProtectionPlan": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GuardDuty::Master": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GuardDuty::Member": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GuardDuty::PublishingDestination": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GuardDuty::ThreatEntitySet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GuardDuty::ThreatIntelSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::GuardDuty::TrustedEntitySet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::HealthImaging::Datastore": [ + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::HealthLake::FHIRDatastore": [ + "ap-south-1", + "ap-southeast-2", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IAM::AccessKey": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::Group": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::GroupPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::InstanceProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::ManagedPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::OIDCProvider": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::Policy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::Role": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::RolePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::SAMLProvider": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::ServerCertificate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::ServiceLinkedRole": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::User": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::UserPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::UserToGroupAddition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IAM::VirtualMFADevice": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IVS::Channel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IVS::EncoderConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IVS::IngestConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IVS::PlaybackKeyPair": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IVS::PlaybackRestrictionPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IVS::PublicKey": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IVS::RecordingConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IVS::Stage": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IVS::StorageConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IVS::StreamKey": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IVSChat::LoggingConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IVSChat::Room": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IdentityStore::Group": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IdentityStore::GroupMembership": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ImageBuilder::Component": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ImageBuilder::ContainerRecipe": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ImageBuilder::DistributionConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ImageBuilder::Image": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ImageBuilder::ImagePipeline": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ImageBuilder::ImageRecipe": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ImageBuilder::InfrastructureConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ImageBuilder::LifecyclePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ImageBuilder::Workflow": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Inspector::AssessmentTarget": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Inspector::AssessmentTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Inspector::ResourceGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::InspectorV2::CisScanConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::InspectorV2::CodeSecurityIntegration": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::InspectorV2::CodeSecurityScanConfiguration": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::InspectorV2::Filter": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::InternetMonitor::Monitor": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Invoicing::InvoiceUnit": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::AccountAuditConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::Authorizer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::BillingGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::CACertificate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::Certificate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::CertificateProvider": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::Command": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::CustomMetric": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::Dimension": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::DomainConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::EncryptionConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::FleetMetric": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::JobTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::Logging": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::MitigationAction": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::Policy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::PolicyPrincipalAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::ProvisioningTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::ResourceSpecificLogging": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::RoleAlias": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::ScheduledAudit": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::SecurityProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::SoftwarePackage": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::SoftwarePackageVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::Thing": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::ThingGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::ThingPrincipalAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::ThingType": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::TopicRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoT::TopicRuleDestination": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::IoTAnalytics::Channel": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTAnalytics::Dataset": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTAnalytics::Datastore": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTAnalytics::Pipeline": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTCoreDeviceAdvisor::SuiteDefinition": [ + "ap-northeast-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTEvents::AlarmModel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTEvents::DetectorModel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTEvents::Input": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTFleetHub::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTFleetWise::Campaign": [ + "ap-south-1", + "eu-central-1", + "us-east-1", + ], + "AWS::IoTFleetWise::DecoderManifest": [ + "ap-south-1", + "eu-central-1", + "us-east-1", + ], + "AWS::IoTFleetWise::Fleet": [ + "ap-south-1", + "eu-central-1", + "us-east-1", + ], + "AWS::IoTFleetWise::ModelManifest": [ + "ap-south-1", + "eu-central-1", + "us-east-1", + ], + "AWS::IoTFleetWise::SignalCatalog": [ + "ap-south-1", + "eu-central-1", + "us-east-1", + ], + "AWS::IoTFleetWise::StateTemplate": [ + "ap-south-1", + "eu-central-1", + "us-east-1", + ], + "AWS::IoTFleetWise::Vehicle": [ + "ap-south-1", + "eu-central-1", + "us-east-1", + ], + "AWS::IoTManagedIntegrations::CredentialLocker": [ + "ca-central-1", + "eu-west-1", + ], + "AWS::IoTManagedIntegrations::ManagedThing": [ + "ca-central-1", + "eu-west-1", + ], + "AWS::IoTManagedIntegrations::ProvisioningProfile": [ + "ca-central-1", + "eu-west-1", + ], + "AWS::IoTSiteWise::AccessPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTSiteWise::Asset": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTSiteWise::AssetModel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTSiteWise::ComputationModel": [ + "ap-southeast-2", + "eu-west-1", + "us-east-1", + ], + "AWS::IoTSiteWise::Dashboard": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTSiteWise::Dataset": [ + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTSiteWise::Gateway": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTSiteWise::Portal": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTSiteWise::Project": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::IoTThingsGraph::FlowTemplate": [ + "ap-northeast-1", + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTTwinMaker::ComponentType": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTTwinMaker::Entity": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTTwinMaker::Scene": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTTwinMaker::SyncJob": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTTwinMaker::Workspace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTWireless::Destination": [ + "ap-northeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTWireless::DeviceProfile": [ + "ap-northeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTWireless::FuotaTask": [ + "ap-northeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTWireless::MulticastGroup": [ + "ap-northeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTWireless::NetworkAnalyzerConfiguration": [ + "ap-northeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTWireless::PartnerAccount": [ + "us-east-1", + ], + "AWS::IoTWireless::ServiceProfile": [ + "ap-northeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTWireless::TaskDefinition": [ + "ap-northeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTWireless::WirelessDevice": [ + "ap-northeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::IoTWireless::WirelessDeviceImportTask": [ + "us-east-1", + ], + "AWS::IoTWireless::WirelessGateway": [ + "ap-northeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::KMS::Alias": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KMS::Key": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KMS::ReplicaKey": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KafkaConnect::Connector": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KafkaConnect::CustomPlugin": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KafkaConnect::WorkerConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Kendra::DataSource": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Kendra::Faq": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Kendra::Index": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::KendraRanking::ExecutionPlan": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Kinesis::ResourcePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Kinesis::Stream": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Kinesis::StreamConsumer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KinesisAnalytics::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KinesisAnalytics::ApplicationOutput": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KinesisAnalytics::ApplicationReferenceDataSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KinesisAnalyticsV2::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KinesisAnalyticsV2::ApplicationCloudWatchLoggingOption": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KinesisAnalyticsV2::ApplicationOutput": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KinesisAnalyticsV2::ApplicationReferenceDataSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KinesisFirehose::DeliveryStream": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::KinesisVideo::SignalingChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::KinesisVideo::Stream": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::LakeFormation::DataCellsFilter": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::LakeFormation::DataLakeSettings": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::LakeFormation::Permissions": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::LakeFormation::PrincipalPermissions": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::LakeFormation::Resource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::LakeFormation::Tag": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::LakeFormation::TagAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Lambda::Alias": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Lambda::CodeSigningConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Lambda::EventInvokeConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Lambda::EventSourceMapping": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Lambda::Function": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Lambda::LayerVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Lambda::LayerVersionPermission": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Lambda::Permission": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Lambda::ResourcePolicy": [ + "eu-west-1", + ], + "AWS::Lambda::Url": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Lambda::Version": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::LaunchWizard::Deployment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Lex::Bot": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Lex::BotAlias": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Lex::BotVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Lex::ResourcePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::LicenseManager::Grant": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::LicenseManager::License": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Lightsail::Alarm": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Lightsail::Bucket": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Lightsail::Certificate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Lightsail::Container": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Lightsail::Database": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Lightsail::Disk": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Lightsail::Distribution": [ + "us-east-1", + ], + "AWS::Lightsail::Domain": [ + "us-east-1", + ], + "AWS::Lightsail::Instance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Lightsail::InstanceSnapshot": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Lightsail::LoadBalancer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Lightsail::LoadBalancerTlsCertificate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Lightsail::StaticIp": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Location::APIKey": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Location::GeofenceCollection": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Location::Map": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Location::PlaceIndex": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Location::RouteCalculator": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Location::Tracker": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Location::TrackerConsumer": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Logs::AccountPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::Delivery": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::DeliveryDestination": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::DeliverySource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::Destination": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::Integration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::LogAnomalyDetector": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::LogGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::LogStream": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::MetricFilter": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::QueryDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::ResourcePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::SubscriptionFilter": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Logs::Transformer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::LookoutEquipment::InferenceScheduler": [ + "ap-northeast-2", + "eu-west-1", + "us-east-1", + ], + "AWS::LookoutMetrics::Alert": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::LookoutMetrics::AnomalyDetector": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::LookoutVision::Project": [ + "ap-northeast-1", + "ap-northeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::M2::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::M2::Deployment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::M2::Environment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MPA::ApprovalTeam": [ + "us-east-1", + ], + "AWS::MPA::IdentitySource": [ + "us-east-1", + ], + "AWS::MSK::BatchScramSecret": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MSK::Cluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MSK::ClusterPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MSK::Configuration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MSK::Replicator": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MSK::ServerlessCluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MSK::VpcConnection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MWAA::Environment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Macie::AllowList": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Macie::CustomDataIdentifier": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Macie::FindingsFilter": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Macie::Session": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ManagedBlockchain::Accessor": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + ], + "AWS::ManagedBlockchain::Member": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::ManagedBlockchain::Node": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + ], + "AWS::MediaConnect::Bridge": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaConnect::BridgeOutput": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaConnect::BridgeSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaConnect::Flow": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaConnect::FlowEntitlement": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaConnect::FlowOutput": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaConnect::FlowSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaConnect::FlowVpcInterface": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaConnect::Gateway": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaConvert::JobTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaConvert::Preset": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaConvert::Queue": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaLive::Channel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaLive::ChannelPlacementGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaLive::CloudWatchAlarmTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaLive::CloudWatchAlarmTemplateGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaLive::Cluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaLive::EventBridgeRuleTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaLive::EventBridgeRuleTemplateGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaLive::Input": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::MediaLive::InputSecurityGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaLive::Multiplex": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaLive::Multiplexprogram": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaLive::Network": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaLive::SdiSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaLive::SignalMap": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaPackage::Asset": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaPackage::Channel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaPackage::OriginEndpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaPackage::PackagingConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaPackage::PackagingGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaPackageV2::Channel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaPackageV2::ChannelGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaPackageV2::ChannelPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaPackageV2::OriginEndpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaPackageV2::OriginEndpointPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MediaStore::Container": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::MediaTailor::Channel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaTailor::ChannelPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaTailor::LiveSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaTailor::PlaybackConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaTailor::SourceLocation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MediaTailor::VodSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::MemoryDB::ACL": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MemoryDB::Cluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MemoryDB::MultiRegionCluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MemoryDB::ParameterGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MemoryDB::SubnetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::MemoryDB::User": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Neptune::DBCluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Neptune::DBClusterParameterGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Neptune::DBInstance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Neptune::DBParameterGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Neptune::DBSubnetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Neptune::EventSubscription": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NeptuneGraph::Graph": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::NeptuneGraph::PrivateGraphEndpoint": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::NetworkFirewall::Firewall": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkFirewall::FirewallPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkFirewall::LoggingConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkFirewall::RuleGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkFirewall::TLSInspectionConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkFirewall::VpcEndpointAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::ConnectAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::ConnectPeer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::CoreNetwork": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::CustomerGatewayAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::Device": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::DirectConnectGatewayAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::GlobalNetwork": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::Link": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::LinkAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::Site": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::SiteToSiteVpnAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::TransitGatewayPeering": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::TransitGatewayRegistration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::TransitGatewayRouteTableAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NetworkManager::VpcAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::NimbleStudio::Studio": [ + "ap-southeast-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "us-east-2", + ], + "AWS::Notifications::ChannelAssociation": [ + "us-east-1", + ], + "AWS::Notifications::EventRule": [ + "us-east-1", + ], + "AWS::Notifications::ManagedNotificationAccountContactAssociation": [ + "us-east-1", + ], + "AWS::Notifications::ManagedNotificationAdditionalChannelAssociation": [ + "us-east-1", + ], + "AWS::Notifications::NotificationConfiguration": [ + "us-east-1", + ], + "AWS::Notifications::NotificationHub": [ + "us-east-1", + ], + "AWS::Notifications::OrganizationalUnitAssociation": [ + "us-east-1", + ], + "AWS::NotificationsContacts::EmailContact": [ + "us-east-1", + ], + "AWS::ODB::CloudAutonomousVmCluster": [ + "us-east-1", + "us-west-2", + ], + "AWS::ODB::CloudExadataInfrastructure": [ + "us-east-1", + "us-west-2", + ], + "AWS::ODB::CloudVmCluster": [ + "us-east-1", + "us-west-2", + ], + "AWS::ODB::OdbNetwork": [ + "us-east-1", + "us-west-2", + ], + "AWS::ODB::OdbPeeringConnection": [ + "us-east-1", + "us-west-2", + ], + "AWS::OSIS::Pipeline": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Oam::Link": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Oam::Sink": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ObservabilityAdmin::OrganizationTelemetryRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ObservabilityAdmin::TelemetryRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Omics::AnnotationStore": [ + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Omics::ReferenceStore": [ + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Omics::RunGroup": [ + "ap-northeast-2", + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Omics::SequenceStore": [ + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Omics::VariantStore": [ + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Omics::Workflow": [ + "ap-northeast-2", + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Omics::WorkflowVersion": [ + "ap-northeast-2", + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::OpenSearchServerless::AccessPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpenSearchServerless::Collection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpenSearchServerless::Index": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpenSearchServerless::LifecyclePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpenSearchServerless::SecurityConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpenSearchServerless::SecurityPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpenSearchServerless::VpcEndpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpenSearchService::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpenSearchService::Domain": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpsWorks::App": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpsWorks::ElasticLoadBalancerAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpsWorks::Instance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpsWorks::Layer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpsWorks::Stack": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpsWorks::UserProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpsWorks::Volume": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::OpsWorksCM::Server": [ + "ap-northeast-1", + ], + "AWS::Organizations::Account": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Organizations::Organization": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Organizations::OrganizationalUnit": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Organizations::Policy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Organizations::ResourcePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::PCAConnectorAD::Connector": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::PCAConnectorAD::DirectoryRegistration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::PCAConnectorAD::ServicePrincipalName": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::PCAConnectorAD::Template": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::PCAConnectorAD::TemplateGroupAccessControlEntry": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::PCAConnectorSCEP::Challenge": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::PCAConnectorSCEP::Connector": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::PCS::Cluster": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::PCS::ComputeNodeGroup": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::PCS::Queue": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Panorama::ApplicationInstance": [ + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::Panorama::Package": [ + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::Panorama::PackageVersion": [ + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::PaymentCryptography::Alias": [ + "ap-northeast-1", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::PaymentCryptography::Key": [ + "ap-northeast-1", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Personalize::Dataset": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Personalize::DatasetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Personalize::Schema": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Personalize::Solution": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Pinpoint::ADMChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::APNSChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::APNSSandboxChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::APNSVoipChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::APNSVoipSandboxChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::App": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::ApplicationSettings": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::BaiduChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::Campaign": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::EmailChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::EmailTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::EventStream": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::GCMChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::InAppTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Pinpoint::PushTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::SMSChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::Segment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::SmsTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Pinpoint::VoiceChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::PinpointEmail::ConfigurationSet": [ + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::PinpointEmail::ConfigurationSetEventDestination": [ + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::PinpointEmail::DedicatedIpPool": [ + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::PinpointEmail::Identity": [ + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::Pipes::Pipe": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Proton::EnvironmentAccountConnection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Proton::EnvironmentTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Proton::ServiceTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QBusiness::Application": [ + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::QBusiness::DataAccessor": [ + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::QBusiness::DataSource": [ + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::QBusiness::Index": [ + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::QBusiness::Permission": [ + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::QBusiness::Plugin": [ + "us-east-1", + "us-west-2", + ], + "AWS::QBusiness::Retriever": [ + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::QBusiness::WebExperience": [ + "ap-southeast-2", + "eu-west-1", + "us-east-1", + "us-west-2", + ], + "AWS::QLDB::Ledger": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QLDB::Stream": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QuickSight::Analysis": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QuickSight::CustomPermissions": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QuickSight::Dashboard": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QuickSight::DataSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QuickSight::DataSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QuickSight::Folder": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QuickSight::RefreshSchedule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QuickSight::Template": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QuickSight::Theme": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QuickSight::Topic": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::QuickSight::VPCConnection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::RAM::Permission": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RAM::ResourceShare": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::CustomDBEngineVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::RDS::DBCluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::DBClusterParameterGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::DBInstance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::DBParameterGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::DBProxy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::DBProxyEndpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::DBProxyTargetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::DBSecurityGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::DBSecurityGroupIngress": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::DBShardGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::DBSubnetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::EventSubscription": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::GlobalCluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::Integration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RDS::OptionGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RUM::AppMonitor": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Rbin::Rule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Redshift::Cluster": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Redshift::ClusterParameterGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Redshift::ClusterSecurityGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Redshift::ClusterSecurityGroupIngress": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Redshift::ClusterSubnetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Redshift::EndpointAccess": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Redshift::EndpointAuthorization": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Redshift::EventSubscription": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Redshift::Integration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Redshift::ScheduledAction": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RedshiftServerless::Namespace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RedshiftServerless::Snapshot": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RedshiftServerless::Workgroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RefactorSpaces::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RefactorSpaces::Environment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RefactorSpaces::Route": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RefactorSpaces::Service": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Rekognition::Collection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Rekognition::Project": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Rekognition::StreamProcessor": [ + "ap-northeast-1", + "ap-south-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::ResilienceHub::App": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ResilienceHub::ResiliencyPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ResourceExplorer2::DefaultViewAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ResourceExplorer2::Index": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ResourceExplorer2::View": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ResourceGroups::Group": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ResourceGroups::TagSyncTask": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RoboMaker::Fleet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::RoboMaker::Robot": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::RoboMaker::RobotApplication": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::RoboMaker::RobotApplicationVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::RoboMaker::SimulationApplication": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::RoboMaker::SimulationApplicationVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::RolesAnywhere::CRL": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RolesAnywhere::Profile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::RolesAnywhere::TrustAnchor": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53::CidrCollection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53::DNSSEC": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53::HealthCheck": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53::HostedZone": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53::KeySigningKey": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53::RecordSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53::RecordSetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Profiles::Profile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Profiles::ProfileAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Profiles::ProfileResourceAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53RecoveryControl::Cluster": [ + "us-east-1", + "us-west-2", + ], + "AWS::Route53RecoveryControl::ControlPanel": [ + "us-east-1", + "us-west-2", + ], + "AWS::Route53RecoveryControl::RoutingControl": [ + "us-east-1", + "us-west-2", + ], + "AWS::Route53RecoveryControl::SafetyRule": [ + "us-east-1", + "us-west-2", + ], + "AWS::Route53RecoveryReadiness::Cell": [ + "us-east-1", + "us-west-2", + ], + "AWS::Route53RecoveryReadiness::ReadinessCheck": [ + "us-east-1", + "us-west-2", + ], + "AWS::Route53RecoveryReadiness::RecoveryGroup": [ + "us-east-1", + "us-west-2", + ], + "AWS::Route53RecoveryReadiness::ResourceSet": [ + "us-east-1", + "us-west-2", + ], + "AWS::Route53Resolver::FirewallDomainList": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Resolver::FirewallRuleGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Resolver::FirewallRuleGroupAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Resolver::OutpostResolver": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Resolver::ResolverConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Resolver::ResolverDNSSECConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Resolver::ResolverEndpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Resolver::ResolverQueryLoggingConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Resolver::ResolverQueryLoggingConfigAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Resolver::ResolverRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Route53Resolver::ResolverRuleAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3::AccessGrant": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3::AccessGrantsInstance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3::AccessGrantsLocation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3::AccessPoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3::Bucket": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3::BucketPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3::MultiRegionAccessPoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3::MultiRegionAccessPointPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3::StorageLens": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3::StorageLensGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3Express::AccessPoint": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::S3Express::BucketPolicy": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::S3Express::DirectoryBucket": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::S3ObjectLambda::AccessPoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3ObjectLambda::AccessPointPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3Outposts::AccessPoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3Outposts::Bucket": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3Outposts::BucketPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3Outposts::Endpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3Tables::Namespace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3Tables::Table": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3Tables::TableBucket": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3Tables::TableBucketPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::S3Tables::TablePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SDB::Domain": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::ConfigurationSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::ConfigurationSetEventDestination": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::ContactList": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::DedicatedIpPool": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::EmailIdentity": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::MailManagerAddonInstance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::MailManagerAddonSubscription": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::MailManagerAddressList": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::MailManagerArchive": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::MailManagerIngressPoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::MailManagerRelay": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::MailManagerRuleSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::MailManagerTrafficPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::ReceiptFilter": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::ReceiptRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::ReceiptRuleSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::Template": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SES::VdmAttributes": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SMSVOICE::ConfigurationSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SMSVOICE::OptOutList": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SMSVOICE::PhoneNumber": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SMSVOICE::Pool": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SMSVOICE::ProtectConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SMSVOICE::ResourcePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SMSVOICE::SenderId": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SNS::Subscription": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SNS::Topic": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SNS::TopicInlinePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SNS::TopicPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SQS::Queue": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SQS::QueueInlinePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SQS::QueuePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSM::Association": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSM::Document": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSM::MaintenanceWindow": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSM::MaintenanceWindowTarget": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSM::MaintenanceWindowTask": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSM::Parameter": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSM::PatchBaseline": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSM::ResourceDataSync": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSM::ResourcePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSMContacts::Contact": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSMContacts::ContactChannel": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSMContacts::Plan": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSMContacts::Rotation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSMGuiConnect::Preferences": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSMIncidents::ReplicationSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSMIncidents::ResponsePlan": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSMQuickSetup::ConfigurationManager": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSO::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSO::ApplicationAssignment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSO::Assignment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSO::Instance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSO::InstanceAccessControlAttributeConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SSO::PermissionSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::App": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::AppImageConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::Cluster": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::CodeRepository": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::DataQualityJobDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::Device": [ + "ap-northeast-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::SageMaker::DeviceFleet": [ + "ap-northeast-1", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::SageMaker::Domain": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::Endpoint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::EndpointConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::FeatureGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::Image": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::ImageVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::InferenceComponent": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::SageMaker::InferenceExperiment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::MlflowTrackingServer": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::Model": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::ModelBiasJobDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::ModelCard": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::ModelExplainabilityJobDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::ModelPackage": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::ModelPackageGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::ModelQualityJobDefinition": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::MonitoringSchedule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::NotebookInstance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::NotebookInstanceLifecycleConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::PartnerApp": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::Pipeline": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::ProcessingJob": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::Project": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::Space": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::StudioLifecycleConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::UserProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SageMaker::Workteam": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Scheduler::Schedule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Scheduler::ScheduleGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecretsManager::ResourcePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecretsManager::RotationSchedule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecretsManager::Secret": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecretsManager::SecretTargetAttachment": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::AggregatorV2": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::AutomationRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::AutomationRuleV2": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::ConfigurationPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::DelegatedAdmin": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::FindingAggregator": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::Hub": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::HubV2": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::Insight": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::OrganizationConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::PolicyAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::ProductSubscription": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::SecurityControl": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityHub::Standard": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityLake::AwsLogSource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityLake::DataLake": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityLake::Subscriber": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SecurityLake::SubscriberNotification": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::AcceptedPortfolioShare": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::CloudFormationProduct": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::CloudFormationProvisionedProduct": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::LaunchNotificationConstraint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::LaunchRoleConstraint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::LaunchTemplateConstraint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::Portfolio": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::PortfolioPrincipalAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::PortfolioProductAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::PortfolioShare": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::ResourceUpdateConstraint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::ServiceAction": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::ServiceActionAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::StackSetConstraint": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::TagOption": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalog::TagOptionAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalogAppRegistry::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalogAppRegistry::AttributeGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalogAppRegistry::AttributeGroupAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceCatalogAppRegistry::ResourceAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceDiscovery::HttpNamespace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceDiscovery::Instance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceDiscovery::PrivateDnsNamespace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceDiscovery::PublicDnsNamespace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::ServiceDiscovery::Service": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Shield::DRTAccess": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Shield::ProactiveEngagement": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Shield::Protection": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Shield::ProtectionGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Signer::ProfilePermission": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Signer::SigningProfile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SimSpaceWeaver::Simulation": [ + "ap-southeast-1", + "ap-southeast-2", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::StepFunctions::Activity": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::StepFunctions::StateMachine": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::StepFunctions::StateMachineAlias": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::StepFunctions::StateMachineVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SupportApp::AccountAlias": [ + "ap-northeast-1", + "ap-southeast-1", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::SupportApp::SlackChannelConfiguration": [ + "ap-northeast-1", + "ap-southeast-1", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::SupportApp::SlackWorkspaceConfiguration": [ + "ap-northeast-1", + "ap-southeast-1", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Synthetics::Canary": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Synthetics::Group": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::SystemsManagerSAP::Application": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Timestream::Database": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Timestream::InfluxDBInstance": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Timestream::ScheduledQuery": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Timestream::Table": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-2", + "eu-central-1", + "eu-west-1", + "us-east-1", + "us-east-2", + "us-west-2", + ], + "AWS::Transfer::Agreement": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Transfer::Certificate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Transfer::Connector": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Transfer::Profile": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Transfer::Server": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Transfer::User": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Transfer::WebApp": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Transfer::Workflow": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VerifiedPermissions::IdentitySource": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VerifiedPermissions::Policy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VerifiedPermissions::PolicyStore": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VerifiedPermissions::PolicyTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VoiceID::Domain": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::VpcLattice::AccessLogSubscription": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VpcLattice::AuthPolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VpcLattice::Listener": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VpcLattice::ResourceConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VpcLattice::ResourceGateway": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VpcLattice::ResourcePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VpcLattice::Rule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VpcLattice::Service": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VpcLattice::ServiceNetwork": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VpcLattice::ServiceNetworkResourceAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VpcLattice::ServiceNetworkServiceAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VpcLattice::ServiceNetworkVpcAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::VpcLattice::TargetGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAF::ByteMatchSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAF::IPSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAF::Rule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAF::SizeConstraintSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAF::SqlInjectionMatchSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAF::WebACL": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAF::XssMatchSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFRegional::ByteMatchSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFRegional::GeoMatchSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFRegional::IPSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFRegional::RateBasedRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFRegional::RegexPatternSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFRegional::Rule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFRegional::SizeConstraintSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFRegional::SqlInjectionMatchSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFRegional::WebACL": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFRegional::WebACLAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFRegional::XssMatchSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFv2::IPSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFv2::LoggingConfiguration": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFv2::RegexPatternSet": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFv2::RuleGroup": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFv2::WebACL": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WAFv2::WebACLAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::Wisdom::AIAgent": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Wisdom::AIAgentVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Wisdom::AIGuardrail": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Wisdom::AIGuardrailVersion": [ + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Wisdom::AIPrompt": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Wisdom::AIPromptVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Wisdom::Assistant": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Wisdom::AssistantAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Wisdom::KnowledgeBase": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Wisdom::MessageTemplate": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Wisdom::MessageTemplateVersion": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::Wisdom::QuickResponse": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpaces::ConnectionAlias": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpaces::Workspace": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::WorkSpaces::WorkspacesPool": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpacesThinClient::Environment": [ + "ap-south-1", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpacesWeb::BrowserSettings": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpacesWeb::DataProtectionSettings": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpacesWeb::IdentityProvider": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpacesWeb::IpAccessSettings": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpacesWeb::NetworkSettings": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpacesWeb::Portal": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpacesWeb::SessionLogger": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpacesWeb::TrustStore": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpacesWeb::UserAccessLoggingSettings": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::WorkSpacesWeb::UserSettings": [ + "ap-northeast-1", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "us-east-1", + "us-west-2", + ], + "AWS::WorkspacesInstances::Volume": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::WorkspacesInstances::VolumeAssociation": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::WorkspacesInstances::WorkspaceInstance": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-west-2", + ], + "AWS::XRay::Group": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::XRay::ResourcePolicy": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::XRay::SamplingRule": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "AWS::XRay::TransactionSearchConfig": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], + "Alexa::ASK::Skill": [ + "ap-northeast-1", + "ap-northeast-2", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + ], +} + +AWS_CFN_REGIONS_SCANNED = { + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ca-central-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", +} diff --git a/localstack-core/localstack/services/cloudformation/v2/entities.py b/localstack-core/localstack/services/cloudformation/v2/entities.py index 111f35ae9312f..bb6101a426c8f 100644 --- a/localstack-core/localstack/services/cloudformation/v2/entities.py +++ b/localstack-core/localstack/services/cloudformation/v2/entities.py @@ -25,7 +25,7 @@ Parameter as ApiParameter, ) from localstack.services.cloudformation.engine.entities import ( - StackIdentifier, + StackIdentifierV2, ) from localstack.services.cloudformation.engine.v2.change_set_model import ( ChangeType, @@ -88,7 +88,7 @@ def __init__( self.parameters = request_payload.get("Parameters", []) self.stack_id = arns.cloudformation_stack_arn( self.stack_name, - stack_id=StackIdentifier( + stack_id=StackIdentifierV2( account_id=self.account_id, region=self.region_name, stack_name=self.stack_name ).generate(tags=request_payload.get("Tags")), account_id=self.account_id, diff --git a/localstack-core/localstack/services/cloudformation/v2/provider.py b/localstack-core/localstack/services/cloudformation/v2/provider.py index 0fbc519628367..d5e437b5d13ad 100644 --- a/localstack-core/localstack/services/cloudformation/v2/provider.py +++ b/localstack-core/localstack/services/cloudformation/v2/provider.py @@ -15,6 +15,7 @@ ChangeSetNameOrId, ChangeSetNotFoundException, ChangeSetStatus, + ChangeSetSummary, ChangeSetType, ClientRequestToken, CreateChangeSetInput, @@ -46,6 +47,7 @@ IncludePropertyValues, InsufficientCapabilitiesException, InvalidChangeSetStatusException, + ListChangeSetsOutput, ListExportsOutput, ListStackResourcesOutput, ListStacksOutput, @@ -234,14 +236,18 @@ def on_before_start(self): issue_url = "?".join([base, urlencode(query_args)]) LOG.info( "You have opted in to the new CloudFormation deployment engine. " - "You can opt in to using the old engine by setting PROVIDER_OVERRIDE_CLOUDFORMATION=legacy. " + "You can opt in to using the old engine by setting PROVIDER_OVERRIDE_CLOUDFORMATION=engine-legacy. " "If you experience issues, please submit a bug report at this URL: %s", issue_url, ) @staticmethod def _resolve_parameters( - template: dict | None, parameters: dict | None, account_id: str, region_name: str + template: dict | None, + parameters: dict | None, + account_id: str, + region_name: str, + before_parameters: dict | None, ) -> dict[str, EngineParameter]: template_parameters = template.get("Parameters", {}) resolved_parameters = {} @@ -276,10 +282,25 @@ def _resolve_parameters( resolved_parameter["resolved_value"] = resolve_ssm_parameter( account_id, region_name, given_value or default_value ) - except Exception: - raise ValidationError( - f"Parameter {name} should either have input value or default value" - ) + except Exception as e: + # we could not find the parameter however CDK provides the resolved value rather than the + # parameter name again so try to look up the value in the previous parameters + if ( + before_parameters + and (before_param := before_parameters.get(name)) + and isinstance(before_param, dict) + and (resolved_value := before_param.get("resolved_value")) + ): + LOG.debug( + "Parameter %s could not be resolved, using previous value of %s", + name, + resolved_value, + ) + resolved_parameter["resolved_value"] = resolved_value + else: + raise ValidationError( + f"Parameter {name} should either have input value or default value" + ) from e elif given_value is None and default_value is None: invalid_parameters.append(name) continue @@ -318,6 +339,7 @@ def _setup_change_set_model( after_parameters, change_set.stack.account_id, change_set.stack.region_name, + before_parameters, ) change_set.resolved_parameters = resolved_parameters @@ -724,6 +746,44 @@ def describe_change_set( result["Parameters"] = self._render_resolved_parameters(change_set.resolved_parameters) return result + @handler("ListChangeSets") + def list_change_sets( + self, + context: RequestContext, + stack_name: StackNameOrId, + next_token: NextToken = None, + **kwargs, + ) -> ListChangeSetsOutput: + store = get_cloudformation_store(account_id=context.account_id, region_name=context.region) + stack = find_stack_v2(store, stack_name) + if not stack: + raise StackNotFoundError(stack_name) + summaries = [] + for change_set_id in stack.change_set_ids: + change_set = store.change_sets[change_set_id] + if ( + change_set.status != ChangeSetStatus.CREATE_COMPLETE + or change_set.execution_status != ExecutionStatus.AVAILABLE + ): + continue + + summaries.append( + ChangeSetSummary( + StackId=change_set.stack.stack_id, + StackName=change_set.stack.stack_name, + ChangeSetId=change_set_id, + ChangeSetName=change_set.change_set_name, + ExecutionStatus=change_set.execution_status, + Status=change_set.status, + StatusReason=change_set.status_reason, + CreationTime=change_set.creation_time, + # mocked information + IncludeNestedStacks=False, + ) + ) + + return ListChangeSetsOutput(Summaries=summaries) + @handler("DeleteChangeSet") def delete_change_set( self, diff --git a/localstack-core/localstack/services/cloudwatch/provider_v2.py b/localstack-core/localstack/services/cloudwatch/provider_v2.py index b571f950786bc..2a41252cefb37 100644 --- a/localstack-core/localstack/services/cloudwatch/provider_v2.py +++ b/localstack-core/localstack/services/cloudwatch/provider_v2.py @@ -108,17 +108,11 @@ AWS_MAX_DATAPOINTS_ACCEPTED: int = 1440 -class ValidationError(CommonServiceException): - # TODO: check this error against AWS (doesn't exist in the API) +class ValidationException(CommonServiceException): def __init__(self, message: str): super().__init__("ValidationError", message, 400, True) -class InvalidParameterCombination(CommonServiceException): - def __init__(self, message: str): - super().__init__("InvalidParameterCombination", message, 400, True) - - def _validate_parameters_for_put_metric_data(metric_data: MetricData) -> None: for index, metric_item in enumerate(metric_data): indexplusone = index + 1 @@ -246,7 +240,7 @@ def get_metric_data( results: list[MetricDataResult] = [] limit = max_datapoints or 100_800 messages: MetricDataResultMessages = [] - nxt = None + nxt: str | None = None label_additions = [] for diff in LABEL_DIFFERENTIATORS: @@ -280,14 +274,14 @@ def get_metric_data( timestamp_value_dicts = [ { "Timestamp": timestamp, - "Value": value, + "Value": float(value), } for timestamp, value in zip(timestamps, values, strict=False) ] pagination = PaginatedList(timestamp_value_dicts) timestamp_page, nxt = pagination.get_page( - lambda item: item.get("Timestamp"), + lambda item: str(item.get("Timestamp")), next_token=next_token, page_size=limit, ) @@ -315,6 +309,11 @@ def set_alarm_state( state_reason_data: StateReasonData = None, **kwargs, ) -> None: + if state_value not in ("OK", "ALARM", "INSUFFICIENT_DATA"): + raise ValidationException( + f"1 validation error detected: Value '{state_value}' at 'stateValue' failed to satisfy constraint: Member must satisfy enum value set: [INSUFFICIENT_DATA, ALARM, OK]" + ) + try: if state_reason_data: state_reason_data = json.loads(state_reason_data) @@ -333,10 +332,6 @@ def set_alarm_state( raise ResourceNotFound() old_state = alarm.alarm["StateValue"] - if state_value not in ("OK", "ALARM", "INSUFFICIENT_DATA"): - raise ValidationError( - f"1 validation error detected: Value '{state_value}' at 'stateValue' failed to satisfy constraint: Member must satisfy enum value set: [INSUFFICIENT_DATA, ALARM, OK]" - ) old_state_reason = alarm.alarm["StateReason"] old_state_update_timestamp = alarm.alarm["StateUpdatedTimestamp"] @@ -416,7 +411,7 @@ def put_metric_alarm(self, context: RequestContext, request: PutMetricAlarmInput "ignore", "missing", ]: - raise ValidationError( + raise ValidationException( f"The value {request['TreatMissingData']} is not supported for TreatMissingData parameter. Supported values are [breaching, notBreaching, ignore, missing]." ) # do some sanity checks: @@ -425,7 +420,7 @@ def put_metric_alarm(self, context: RequestContext, request: PutMetricAlarmInput value = request.get("Period") if value not in (10, 30): if value % 60 != 0: - raise ValidationError("Period must be 10, 30 or a multiple of 60") + raise ValidationException("Period must be 10, 30 or a multiple of 60") if request.get("Statistic"): if request.get("Statistic") not in [ "SampleCount", @@ -434,7 +429,7 @@ def put_metric_alarm(self, context: RequestContext, request: PutMetricAlarmInput "Minimum", "Maximum", ]: - raise ValidationError( + raise ValidationException( f"Value '{request.get('Statistic')}' at 'statistic' failed to satisfy constraint: Member must satisfy enum value set: [Maximum, SampleCount, Sum, Minimum, Average]" ) @@ -448,7 +443,7 @@ def put_metric_alarm(self, context: RequestContext, request: PutMetricAlarmInput "evaluate", "ignore", ): - raise ValidationError( + raise ValidationException( f"Option {evaluate_low_sample_count_percentile} is not supported. " "Supported options for parameter EvaluateLowSampleCountPercentile are evaluate and ignore." ) @@ -691,7 +686,7 @@ def get_metric_statistics( expected_datapoints = (end_time_unix - start_time_unix) / period if expected_datapoints > AWS_MAX_DATAPOINTS_ACCEPTED: - raise InvalidParameterCombination( + raise InvalidParameterCombinationException( f"You have requested up to {int(expected_datapoints)} datapoints, which exceeds the limit of {AWS_MAX_DATAPOINTS_ACCEPTED}. " f"You may reduce the datapoints requested by increasing Period, or decreasing the time range." ) @@ -738,7 +733,7 @@ def get_metric_statistics( for i, timestamp in enumerate(timestamps): stat_datapoints.setdefault(selected_unit, {}) stat_datapoints[selected_unit].setdefault(timestamp, {}) - stat_datapoints[selected_unit][timestamp][stat] = values[i] + stat_datapoints[selected_unit][timestamp][stat] = float(values[i]) stat_datapoints[selected_unit][timestamp]["Unit"] = selected_unit datapoints: list[Datapoint] = [] diff --git a/localstack-core/localstack/services/dynamodb/packages.py b/localstack-core/localstack/services/dynamodb/packages.py index f80175b5df183..47d52471262a6 100644 --- a/localstack-core/localstack/services/dynamodb/packages.py +++ b/localstack-core/localstack/services/dynamodb/packages.py @@ -17,7 +17,8 @@ DDB_AGENT_JAR_URL = f"{ARTIFACTS_REPO}/raw/e4e8c8e294b1fcda90c678ff6af5d5ebe1f091eb/dynamodb-local-patch/target/ddb-local-loader-0.2.jar" JAVASSIST_JAR_URL = f"{MAVEN_REPO_URL}/org/javassist/javassist/3.30.2-GA/javassist-3.30.2-GA.jar" -DDBLOCAL_URL = "https://d1ni2b6xgvw0s0.cloudfront.net/v3.x/dynamodb_local_latest.zip" +# URL points to 2.x here - however the latest 3.x builds are available under this URL +DDBLOCAL_URL = "https://d1ni2b6xgvw0s0.cloudfront.net/v2.x/dynamodb_local_latest.zip" class DynamoDBLocalPackage(Package): diff --git a/localstack-core/localstack/services/ecr/resource_providers/aws_ecr_repository.py b/localstack-core/localstack/services/ecr/resource_providers/aws_ecr_repository.py index a5475880ada6a..a94f089335e50 100644 --- a/localstack-core/localstack/services/ecr/resource_providers/aws_ecr_repository.py +++ b/localstack-core/localstack/services/ecr/resource_providers/aws_ecr_repository.py @@ -6,7 +6,6 @@ from typing import TypedDict import localstack.services.cloudformation.provider_utils as util -from localstack.constants import AWS_REGION_US_EAST_1, DEFAULT_AWS_ACCOUNT_ID from localstack.services.cloudformation.resource_provider import ( OperationStatus, ProgressEvent, @@ -102,7 +101,7 @@ def create( model.update( { "Arn": arns.ecr_repository_arn( - model["RepositoryName"], DEFAULT_AWS_ACCOUNT_ID, AWS_REGION_US_EAST_1 + model["RepositoryName"], request.account_id, request.region_name ), "RepositoryUri": "http://localhost:4566", "ImageTagMutability": "MUTABLE", diff --git a/localstack-core/localstack/services/es/provider.py b/localstack-core/localstack/services/es/provider.py index 0188b19ca8ba3..0551b956d8621 100644 --- a/localstack-core/localstack/services/es/provider.py +++ b/localstack-core/localstack/services/es/provider.py @@ -3,7 +3,6 @@ from botocore.exceptions import ClientError -from localstack import constants from localstack.aws.api import RequestContext, handler from localstack.aws.api.es import ( ARN, @@ -68,6 +67,7 @@ VersionString, ) from localstack.aws.connect import connect_to +from localstack.services.opensearch.packages import ELASTICSEARCH_DEFAULT_VERSION def _version_to_opensearch( @@ -236,7 +236,7 @@ def create_elasticsearch_domain( engine_version = ( _version_to_opensearch(elasticsearch_version) if elasticsearch_version - else constants.ELASTICSEARCH_DEFAULT_VERSION + else ELASTICSEARCH_DEFAULT_VERSION ) kwargs = { "DomainName": domain_name, diff --git a/localstack-core/localstack/services/events/models.py b/localstack-core/localstack/services/events/models.py index 4b1198b9673e5..6d62e5f05045a 100644 --- a/localstack-core/localstack/services/events/models.py +++ b/localstack-core/localstack/services/events/models.py @@ -4,7 +4,7 @@ from enum import Enum from typing import Literal, TypeAlias, TypedDict -from localstack.aws.api.core import ServiceException +from localstack.aws.api import CommonServiceException from localstack.aws.api.events import ( ApiDestinationDescription, ApiDestinationHttpMethod, @@ -66,10 +66,9 @@ TargetDict = dict[TargetId, Target] -class ValidationException(ServiceException): - code: str = "ValidationException" - sender_fault: bool = True - status_code: int = 400 +class ValidationException(CommonServiceException): + def __init__(self, message: str): + super().__init__("ValidationException", message, 400, True) class InvalidEventPatternException(Exception): diff --git a/localstack-core/localstack/services/kinesis/models.py b/localstack-core/localstack/services/kinesis/models.py index 20207a702844a..f601fda0ab1ca 100644 --- a/localstack-core/localstack/services/kinesis/models.py +++ b/localstack-core/localstack/services/kinesis/models.py @@ -1,7 +1,18 @@ from collections import defaultdict -from localstack.aws.api.kinesis import ConsumerDescription, MetricsName, StreamName -from localstack.services.stores import AccountRegionBundle, BaseStore, LocalAttribute +from localstack.aws.api.kinesis import ( + ConsumerDescription, + MetricsName, + Policy, + ResourceARN, + StreamName, +) +from localstack.services.stores import ( + AccountRegionBundle, + BaseStore, + CrossAccountAttribute, + LocalAttribute, +) class KinesisStore(BaseStore): @@ -13,5 +24,7 @@ class KinesisStore(BaseStore): default=lambda: defaultdict(set) ) + resource_policies: dict[ResourceARN, Policy] = CrossAccountAttribute(default=dict) + kinesis_stores = AccountRegionBundle("kinesis", KinesisStore) diff --git a/localstack-core/localstack/services/kinesis/provider.py b/localstack-core/localstack/services/kinesis/provider.py index 216ff316ec397..6c4f19f2f09da 100644 --- a/localstack-core/localstack/services/kinesis/provider.py +++ b/localstack-core/localstack/services/kinesis/provider.py @@ -1,5 +1,7 @@ +import json import logging import os +import re import time from random import random @@ -8,14 +10,18 @@ from localstack.aws.api.kinesis import ( ConsumerARN, Data, + GetResourcePolicyOutput, HashKey, KinesisApi, PartitionKey, + Policy, ProvisionedThroughputExceededException, PutRecordOutput, PutRecordsOutput, PutRecordsRequestEntryList, PutRecordsResultEntry, + ResourceARN, + ResourceNotFoundException, SequenceNumber, ShardId, StartingPosition, @@ -24,6 +30,7 @@ SubscribeToShardEvent, SubscribeToShardEventStream, SubscribeToShardOutput, + ValidationException, ) from localstack.aws.connect import connect_to from localstack.constants import LOCALHOST @@ -39,6 +46,13 @@ MAX_SUBSCRIPTION_SECONDS = 300 SERVER_STARTUP_TIMEOUT = 120 +DATA_STREAM_ARN_REGEX = re.compile( + r"^arn:aws(?:-[a-z]+)*:kinesis:[a-z0-9-]+:\d{12}:stream\/[a-zA-Z0-9_.\-]+$" +) +CONSUMER_ARN_REGEX = re.compile( + r"^arn:aws(?:-[a-z]+)*:kinesis:[a-z0-9-]+:\d{12}:stream\/[a-zA-Z0-9_.\-]+\/consumer\/[a-zA-Z0-9_.\-]+:\d+$" +) + def find_stream_for_consumer(consumer_arn): account_id = extract_account_id_from_arn(consumer_arn) @@ -52,6 +66,11 @@ def find_stream_for_consumer(consumer_arn): raise Exception(f"Unable to find stream for stream consumer {consumer_arn}") +def is_valid_kinesis_arn(resource_arn: ResourceARN) -> bool: + """Check if the provided ARN is a valid Kinesis ARN.""" + return bool(CONSUMER_ARN_REGEX.match(resource_arn) or DATA_STREAM_ARN_REGEX.match(resource_arn)) + + class KinesisProvider(KinesisApi, ServiceLifecycleHook): server_manager: KinesisServerManager @@ -81,6 +100,64 @@ def get_forward_url(self, account_id: str, region_name: str) -> str: def get_store(account_id: str, region_name: str) -> KinesisStore: return kinesis_stores[account_id][region_name] + def put_resource_policy( + self, + context: RequestContext, + resource_arn: ResourceARN, + policy: Policy, + **kwargs, + ) -> None: + if not is_valid_kinesis_arn(resource_arn): + raise ValidationException(f"invalid kinesis arn {resource_arn}") + + kinesis = connect_to( + aws_access_key_id=context.account_id, region_name=context.region + ).kinesis + try: + kinesis.describe_stream_summary(StreamARN=resource_arn) + except kinesis.exceptions.ResourceNotFoundException: + raise ResourceNotFoundException(f"Stream with ARN {resource_arn} not found") + + store = self.get_store(context.account_id, context.region) + store.resource_policies[resource_arn] = policy + + def get_resource_policy( + self, + context: RequestContext, + resource_arn: ResourceARN, + **kwargs, + ) -> GetResourcePolicyOutput: + if not is_valid_kinesis_arn(resource_arn): + raise ValidationException(f"invalid kinesis arn {resource_arn}") + + kinesis = connect_to( + aws_access_key_id=context.account_id, region_name=context.region + ).kinesis + try: + kinesis.describe_stream_summary(StreamARN=resource_arn) + except kinesis.exceptions.ResourceNotFoundException: + raise ResourceNotFoundException(f"Stream with ARN {resource_arn} not found") + + store = self.get_store(context.account_id, context.region) + policy = store.resource_policies.get(resource_arn, json.dumps({})) + return GetResourcePolicyOutput(Policy=policy) + + def delete_resource_policy( + self, + context: RequestContext, + resource_arn: ResourceARN, + **kwargs, + ) -> None: + if not is_valid_kinesis_arn(resource_arn): + raise ValidationException(f"invalid kinesis arn {resource_arn}") + + store = self.get_store(context.account_id, context.region) + if resource_arn not in store.resource_policies: + raise ResourceNotFoundException( + f"No resource policy found for resource ARN {resource_arn}" + ) + del store.resource_policies[resource_arn] + def subscribe_to_shard( self, context: RequestContext, diff --git a/localstack-core/localstack/services/lambda_/invocation/internal_sqs_queue.py b/localstack-core/localstack/services/lambda_/invocation/internal_sqs_queue.py index 45fddadf3155d..742a1f3c83089 100644 --- a/localstack-core/localstack/services/lambda_/invocation/internal_sqs_queue.py +++ b/localstack-core/localstack/services/lambda_/invocation/internal_sqs_queue.py @@ -19,13 +19,9 @@ String, TagMap, ) -from localstack.services.sqs.models import SqsQueue, StandardQueue -from localstack.services.sqs.provider import ( - QueueUpdateWorker, - _create_message_attribute_hash, - to_sqs_api_message, -) -from localstack.services.sqs.utils import generate_message_id +from localstack.services.sqs.models import SqsQueue, StandardQueue, to_sqs_api_message +from localstack.services.sqs.provider import QueueUpdateWorker +from localstack.services.sqs.utils import create_message_attribute_hash, generate_message_id from localstack.utils.objects import singleton_factory from localstack.utils.strings import md5 from localstack.utils.time import now @@ -189,7 +185,7 @@ def send_message( MD5OfBody=md5(MessageBody), Body=MessageBody, Attributes=self._create_message_attributes(MessageSystemAttributes), - MD5OfMessageAttributes=_create_message_attribute_hash(MessageAttributes), + MD5OfMessageAttributes=create_message_attribute_hash(MessageAttributes), MessageAttributes=MessageAttributes, ) queue_item = queue.put( @@ -204,7 +200,7 @@ def send_message( "MD5OfMessageBody": message["MD5OfBody"], "MD5OfMessageAttributes": message.get("MD5OfMessageAttributes"), "SequenceNumber": queue_item.sequence_number, - "MD5OfMessageSystemAttributes": _create_message_attribute_hash(MessageSystemAttributes), + "MD5OfMessageSystemAttributes": create_message_attribute_hash(MessageSystemAttributes), } diff --git a/localstack-core/localstack/services/moto.py b/localstack-core/localstack/services/moto.py index 130c3e7857459..ebc21b7684459 100644 --- a/localstack-core/localstack/services/moto.py +++ b/localstack-core/localstack/services/moto.py @@ -66,6 +66,7 @@ def call_moto_with_request( action=context.operation.name, parameters=service_request, region=context.region, + protocol=context.protocol, ) # we keep the headers from the original request, but override them with the ones created from the `service_request` headers = copy.deepcopy(context.request.headers) diff --git a/localstack-core/localstack/services/opensearch/cluster.py b/localstack-core/localstack/services/opensearch/cluster.py index f8f4c54d8cd76..e19c92bccb086 100644 --- a/localstack-core/localstack/services/opensearch/cluster.py +++ b/localstack-core/localstack/services/opensearch/cluster.py @@ -18,7 +18,12 @@ from localstack.http.proxy import ProxyHandler from localstack.services.edge import ROUTER from localstack.services.opensearch import versions -from localstack.services.opensearch.packages import elasticsearch_package, opensearch_package +from localstack.services.opensearch.packages import ( + ELASTICSEARCH_DEFAULT_VERSION, + OPENSEARCH_DEFAULT_VERSION, + elasticsearch_package, + opensearch_package, +) from localstack.utils.aws.arns import parse_arn from localstack.utils.common import ( ShellCommandThread, @@ -37,6 +42,9 @@ INTERNAL_USER_AUTH = ("localstack-internal", "localstack-internal") DEFAULT_BACKEND_HOST = "127.0.0.1" +# user that starts the opensearch process if the current user is root +OS_USER_OPENSEARCH = "localstack" + CommandSettings = dict[str, str] @@ -314,7 +322,7 @@ def __init__( @property def default_version(self) -> str: - return constants.OPENSEARCH_DEFAULT_VERSION + return OPENSEARCH_DEFAULT_VERSION @property def version(self) -> str: @@ -336,7 +344,7 @@ def bin_name(self) -> str: @property def os_user(self): - return constants.OS_USER_OPENSEARCH + return OS_USER_OPENSEARCH def health(self) -> str | None: return get_cluster_health_status(self.url, auth=self.auth) @@ -580,7 +588,7 @@ def version(self): @property def default_version(self): - return constants.OPENSEARCH_DEFAULT_VERSION + return OPENSEARCH_DEFAULT_VERSION @property def url(self) -> str: @@ -658,7 +666,7 @@ def __init__( @property def default_version(self) -> str: - return constants.ELASTICSEARCH_DEFAULT_VERSION + return ELASTICSEARCH_DEFAULT_VERSION @property def bin_name(self) -> str: @@ -666,7 +674,7 @@ def bin_name(self) -> str: @property def os_user(self): - return constants.OS_USER_OPENSEARCH + return OS_USER_OPENSEARCH def _ensure_installed(self): elasticsearch_package.install(self.version) @@ -710,7 +718,7 @@ def _create_env_vars(self, directories: Directories) -> dict: class EdgeProxiedElasticsearchCluster(EdgeProxiedOpensearchCluster): @property def default_version(self): - return constants.ELASTICSEARCH_DEFAULT_VERSION + return ELASTICSEARCH_DEFAULT_VERSION def _backend_cluster(self) -> OpensearchCluster: return ElasticsearchCluster( diff --git a/localstack-core/localstack/services/opensearch/packages.py b/localstack-core/localstack/services/opensearch/packages.py index 8b0e91368b256..b5c266512ba43 100644 --- a/localstack-core/localstack/services/opensearch/packages.py +++ b/localstack-core/localstack/services/opensearch/packages.py @@ -9,13 +9,6 @@ import semver from localstack import config -from localstack.constants import ( - ELASTICSEARCH_DEFAULT_VERSION, - ELASTICSEARCH_DELETE_MODULES, - ELASTICSEARCH_PLUGIN_LIST, - OPENSEARCH_DEFAULT_VERSION, - OPENSEARCH_PLUGIN_LIST, -) from localstack.packages import InstallTarget, Package, PackageInstaller from localstack.packages.java import java_package from localstack.services.opensearch import versions @@ -32,6 +25,32 @@ LOG = logging.getLogger(__name__) +# the version of opensearch which is used by default +OPENSEARCH_DEFAULT_VERSION = "OpenSearch_3.1" + +# See https://docs.aws.amazon.com/opensearch-service/latest/developerguide/supported-plugins.html +OPENSEARCH_PLUGIN_LIST = [ + "ingest-attachment", + "analysis-kuromoji", +] + +# the version of elasticsearch that is pre-seeded into the base image (sync with Dockerfile.base) +ELASTICSEARCH_DEFAULT_VERSION = "Elasticsearch_7.10" + +# See https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/aes-supported-plugins.html +ELASTICSEARCH_PLUGIN_LIST = [ + "analysis-icu", + "ingest-attachment", + "analysis-kuromoji", + "mapper-murmur3", + "mapper-size", + "analysis-phonetic", + "analysis-smartcn", + "analysis-stempel", + "analysis-ukrainian", +] +# Default ES modules to exclude (save apprx 66MB in the final image) +ELASTICSEARCH_DELETE_MODULES = ["ingest-geoip"] _OPENSEARCH_INSTALL_LOCKS = SynchronizedDefaultDict(threading.RLock) diff --git a/localstack-core/localstack/services/opensearch/provider.py b/localstack-core/localstack/services/opensearch/provider.py index c3ac8e4331a35..0818e1700c20f 100644 --- a/localstack-core/localstack/services/opensearch/provider.py +++ b/localstack-core/localstack/services/opensearch/provider.py @@ -26,6 +26,7 @@ CognitoOptions, CognitoOptionsStatus, ColdStorageOptions, + CompatibleVersionsMap, CreateDomainRequest, CreateDomainResponse, DeleteDomainResponse, @@ -75,7 +76,6 @@ VolumeType, VPCDerivedInfoStatus, ) -from localstack.constants import OPENSEARCH_DEFAULT_VERSION from localstack.services.opensearch import versions from localstack.services.opensearch.cluster import SecurityOptions from localstack.services.opensearch.cluster_manager import ( @@ -84,6 +84,7 @@ create_cluster_manager, ) from localstack.services.opensearch.models import OpenSearchStore, opensearch_stores +from localstack.services.opensearch.packages import OPENSEARCH_DEFAULT_VERSION from localstack.services.plugins import ServiceLifecycleHook from localstack.state import AssetDirectory, StateVisitor from localstack.utils.aws.arns import parse_arn @@ -650,6 +651,10 @@ def get_compatible_versions( for comp in versions.compatible_versions if comp["SourceVersion"] == version_filter ] + if not compatible_versions: + compatible_versions = [ + CompatibleVersionsMap(SourceVersion=version_filter, TargetVersions=[]) + ] return GetCompatibleVersionsResponse(CompatibleVersions=compatible_versions) def describe_domain_config( diff --git a/localstack-core/localstack/services/opensearch/versions.py b/localstack-core/localstack/services/opensearch/versions.py index a5f8a3d5a7179..44332006510bd 100644 --- a/localstack-core/localstack/services/opensearch/versions.py +++ b/localstack-core/localstack/services/opensearch/versions.py @@ -13,20 +13,24 @@ # Internal representation of the OpenSearch versions (without the "OpenSearch_" prefix) _opensearch_install_versions = { + "3.1": "3.1.0", + "2.19": "2.19.3", + "2.17": "2.17.1", + "2.15": "2.15.0", "2.13": "2.13.0", "2.11": "2.11.1", "2.9": "2.9.0", "2.7": "2.7.0", "2.5": "2.5.0", "2.3": "2.3.0", - "1.3": "1.3.12", + "1.3": "1.3.20", "1.2": "1.2.4", "1.1": "1.1.0", "1.0": "1.0.0", } # Internal representation of the Elasticsearch versions (without the "Elasticsearch_" prefix) _elasticsearch_install_versions = { - "7.10": "7.10.0", + "7.10": "7.10.2", "7.9": "7.9.3", "7.8": "7.8.1", "7.7": "7.7.1", @@ -221,6 +225,9 @@ "OpenSearch_2.9", "OpenSearch_2.11", "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19", ], ), CompatibleVersionsMap( @@ -231,28 +238,68 @@ "OpenSearch_2.9", "OpenSearch_2.11", "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19", ], ), CompatibleVersionsMap( SourceVersion="OpenSearch_2.5", - TargetVersions=["OpenSearch_2.7", "OpenSearch_2.9", "OpenSearch_2.11", "OpenSearch_2.13"], + TargetVersions=[ + "OpenSearch_2.7", + "OpenSearch_2.9", + "OpenSearch_2.11", + "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19", + ], ), CompatibleVersionsMap( SourceVersion="OpenSearch_2.7", - TargetVersions=["OpenSearch_2.9", "OpenSearch_2.11", "OpenSearch_2.13"], + TargetVersions=[ + "OpenSearch_2.9", + "OpenSearch_2.11", + "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19", + ], ), CompatibleVersionsMap( SourceVersion="OpenSearch_2.9", - TargetVersions=["OpenSearch_2.11", "OpenSearch_2.13"], + TargetVersions=[ + "OpenSearch_2.11", + "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19", + ], ), CompatibleVersionsMap( SourceVersion="OpenSearch_2.11", - TargetVersions=["OpenSearch_2.13"], + TargetVersions=["OpenSearch_2.13", "OpenSearch_2.15", "OpenSearch_2.17", "OpenSearch_2.19"], + ), + CompatibleVersionsMap( + SourceVersion="OpenSearch_2.13", + TargetVersions=["OpenSearch_2.15", "OpenSearch_2.17", "OpenSearch_2.19"], + ), + CompatibleVersionsMap( + SourceVersion="OpenSearch_2.15", + TargetVersions=["OpenSearch_2.17", "OpenSearch_2.19"], + ), + CompatibleVersionsMap( + SourceVersion="OpenSearch_2.17", + TargetVersions=["OpenSearch_2.19"], + ), + CompatibleVersionsMap( + SourceVersion="OpenSearch_2.19", + TargetVersions=["OpenSearch_3.1"], ), ] -def get_install_type_and_version(version: str) -> (EngineType, str): +def get_install_type_and_version(version: str) -> tuple[EngineType, str]: engine_type = EngineType(version.split("_")[0]) if version not in install_versions: @@ -297,6 +344,8 @@ def get_download_url(install_version: str, engine_type: EngineType) -> str: return _opensearch_url(install_version) elif engine_type == EngineType.Elasticsearch: return _es_url(install_version) + else: + raise ValueError(f"Unknown OpenSearch engine type: {engine_type}") def fetch_latest_versions() -> dict[str, str]: # pragma: no cover diff --git a/localstack-core/localstack/services/s3/provider.py b/localstack-core/localstack/services/s3/provider.py index b1d9a125c1f72..78dcd3b02a2c3 100644 --- a/localstack-core/localstack/services/s3/provider.py +++ b/localstack-core/localstack/services/s3/provider.py @@ -3298,7 +3298,7 @@ def delete_object_tagging( s3_object = s3_bucket.get_object(key=key, version_id=version_id, http_method="DELETE") - store.TAGS.tags.pop(get_unique_key_id(bucket, key, version_id), None) + store.TAGS.tags.pop(get_unique_key_id(bucket, key, s3_object.version_id), None) response = DeleteObjectTaggingOutput() if s3_object.version_id: response["VersionId"] = s3_object.version_id @@ -3859,7 +3859,9 @@ def put_object_retention( if retention and retention["RetainUntilDate"] < datetime.datetime.now(datetime.UTC): # weirdly, this date is format as following: Tue Dec 31 16:00:00 PST 2019 # it contains the timezone as PST, even if you target a bucket in Europe or Asia - pst_datetime = retention["RetainUntilDate"].astimezone(tz=ZoneInfo("US/Pacific")) + pst_datetime = retention["RetainUntilDate"].astimezone( + tz=ZoneInfo("America/Los_Angeles") + ) raise InvalidArgument( "The retain until date must be in the future!", ArgumentName="RetainUntilDate", diff --git a/localstack-core/localstack/services/ses/provider.py b/localstack-core/localstack/services/ses/provider.py index 85445c23e4fb6..daa133ddec32a 100644 --- a/localstack-core/localstack/services/ses/provider.py +++ b/localstack-core/localstack/services/ses/provider.py @@ -183,6 +183,8 @@ class SesProvider(SesApi, ServiceLifecycleHook): # def on_after_init(self): + self._apply_patches() + # Allow sent emails to be retrieved from the SES emails endpoint register_ses_api_resource() @@ -198,6 +200,12 @@ def get_source_from_raw(self, raw_data: str) -> str | None: return entity.replace("From:", "").strip() return None + def _apply_patches(self) -> None: + # Suppress Moto's validation of receipt rule actions. These validations use Moto's implementation of S3, Lambda + # and SQS, which fail because these services have been internalised in LocalStack. + # Besides, AWS does not run the same validations as evidenced by our AWS-validated tests. + SESBackend._validate_receipt_rule_actions = lambda *_: None + # # Implementations for SES operations # @@ -519,8 +527,10 @@ def clone_receipt_rule_set( backend.create_receipt_rule_set(rule_set_name) original_rule_set = backend.describe_receipt_rule_set(original_rule_set_name) + after = None for rule in original_rule_set.rules: - backend.create_receipt_rule(rule_set_name, rule) + backend.create_receipt_rule(rule_set_name, rule, after) + after = rule["Name"] return CloneReceiptRuleSetResponse() diff --git a/localstack-core/localstack/services/sqs/developer_api.py b/localstack-core/localstack/services/sqs/developer_api.py new file mode 100644 index 0000000000000..d9571192b8d5c --- /dev/null +++ b/localstack-core/localstack/services/sqs/developer_api.py @@ -0,0 +1,205 @@ +import logging +from typing import Literal + +from werkzeug import Request as WerkzeugRequest + +from localstack.aws.api import CommonServiceException, ServiceException +from localstack.aws.api.sqs import ( + Message, + QueueAttributeName, + QueueDoesNotExist, + ReceiveMessageResult, +) +from localstack.aws.protocol.parser import create_parser +from localstack.aws.protocol.serializer import aws_response_serializer +from localstack.aws.spec import load_service +from localstack.http import Request, route +from localstack.services.sqs.models import ( + FifoQueue, + SqsMessage, + SqsQueue, + StandardQueue, + sqs_stores, + to_sqs_api_message, +) +from localstack.services.sqs.utils import ( + parse_queue_url, +) +from localstack.utils.aws.request_context import extract_region_from_headers + +LOG = logging.getLogger(__name__) + + +class InvalidAddress(ServiceException): + code = "InvalidAddress" + message = "The address https://queue.amazonaws.com/ is not valid for this endpoint." + sender_fault = True + status_code = 404 + + +def get_sqs_protocol(request: Request) -> Literal["query", "json"]: + content_type = request.headers.get("Content-Type") + return "json" if content_type == "application/x-amz-json-1.0" else "query" + + +def sqs_auto_protocol_aws_response_serializer(service_name: str, operation: str): + def _decorate(fn): + def _proxy(*args, **kwargs): + # extract request from function invocation (decorator can be used for methods as well as for functions). + if len(args) > 0 and isinstance(args[0], WerkzeugRequest): + # function + request = args[0] + elif len(args) > 1 and isinstance(args[1], WerkzeugRequest): + # method (arg[0] == self) + request = args[1] + elif "request" in kwargs: + request = kwargs["request"] + else: + raise ValueError(f"could not find Request in signature of function {fn}") + + protocol = get_sqs_protocol(request) + return aws_response_serializer(service_name, operation, protocol)(fn)(*args, **kwargs) + + return _proxy + + return _decorate + + +class SqsDeveloperApi: + """ + A set of SQS developer tool endpoints: + + - ``/_aws/sqs/messages``: list SQS messages without side effects, compatible with ``ReceiveMessage``. + """ + + def __init__(self, stores=None): + self.stores = stores or sqs_stores + + @route("/_aws/sqs/messages", methods=["GET", "POST"]) + @sqs_auto_protocol_aws_response_serializer("sqs", "ReceiveMessage") + def list_messages(self, request: Request) -> ReceiveMessageResult: + """ + This endpoint expects a ``QueueUrl`` request parameter (either as query arg or form parameter), similar to + the ``ReceiveMessage`` operation. It will parse the Queue URL generated by one of the SQS endpoint strategies. + """ + + if "x-amz-" in request.mimetype or "x-www-form-urlencoded" in request.mimetype: + # only parse the request using a parser if it comes from an AWS client + protocol = get_sqs_protocol(request) + operation, service_request = create_parser( + load_service("sqs", protocol=protocol) + ).parse(request) + if operation.name != "ReceiveMessage": + raise CommonServiceException( + "InvalidRequest", "This endpoint only accepts ReceiveMessage calls" + ) + else: + service_request = dict(request.values) + + if not service_request.get("QueueUrl"): + raise QueueDoesNotExist() + + try: + account_id, region, queue_name = parse_queue_url(service_request.get("QueueUrl")) + except ValueError: + LOG.error( + "Error while parsing Queue URL from request values: %s", + service_request.get, + exc_info=LOG.isEnabledFor(logging.DEBUG), + ) + raise InvalidAddress() + + if not region: + region = extract_region_from_headers(request.headers) + + return self._get_and_serialize_messages(request, region, account_id, queue_name) + + @route("/_aws/sqs/messages///") + @sqs_auto_protocol_aws_response_serializer("sqs", "ReceiveMessage") + def list_messages_for_queue_url( + self, request: Request, region: str, account_id: str, queue_name: str + ) -> ReceiveMessageResult: + """ + This endpoint extracts the region, account_id, and queue_name directly from the URL rather than requiring the + QueueUrl as parameter. + """ + return self._get_and_serialize_messages(request, region, account_id, queue_name) + + def _get_and_serialize_messages( + self, + request: Request, + region: str, + account_id: str, + queue_name: str, + ) -> ReceiveMessageResult: + show_invisible = request.values.get("ShowInvisible", "").lower() in ["true", "1"] + show_delayed = request.values.get("ShowDelayed", "").lower() in ["true", "1"] + + try: + store = self.stores[account_id][region] + queue = store.queues[queue_name] + except KeyError: + LOG.info( + "no queue named %s in region %s and account %s", queue_name, region, account_id + ) + raise QueueDoesNotExist() + + messages = self._collect_messages( + queue, show_invisible=show_invisible, show_delayed=show_delayed + ) + + return ReceiveMessageResult(Messages=messages) + + def _collect_messages( + self, queue: SqsQueue, show_invisible: bool = False, show_delayed: bool = False + ) -> list[Message]: + """ + Retrieves from a given SqsQueue all visible messages without causing any side effects (not setting any + receive timestamps, receive counts, or visibility state). + + :param queue: the queue + :param show_invisible: show invisible messages as well + :param show_delayed: show delayed messages as well + :return: a list of messages + """ + receipt_handle = "SQS/BACKDOOR/ACCESS" # dummy receipt handle + + sqs_messages: list[SqsMessage] = [] + + if show_invisible: + sqs_messages.extend(queue.inflight) + + if isinstance(queue, StandardQueue): + sqs_messages.extend(queue.visible.queue) + elif isinstance(queue, FifoQueue): + if show_invisible: + for inflight_group in queue.inflight_groups: + # messages that have been received are held in ``queue.inflight``, even for FIFO queues. however, + # for fifo queues, messages that are in the same message group as messages that have been + # received, are also considered invisible, and are held here in ``inflight_group.messages``. + for sqs_message in inflight_group.messages: + sqs_messages.append(sqs_message) + + for message_group in queue.message_group_queue.queue: + # these are all messages of message groups that are visible + for sqs_message in message_group.messages: + sqs_messages.append(sqs_message) + else: + raise ValueError(f"unknown queue type {type(queue)}") + + if show_delayed: + sqs_messages.extend(queue.delayed) + + messages = [] + + for sqs_message in sqs_messages: + message: Message = to_sqs_api_message(sqs_message, [QueueAttributeName.All], ["All"]) + # these are all non-standard fields so we squelch the linter + if show_invisible: + message["Attributes"]["IsVisible"] = str(sqs_message.is_visible).lower() # noqa + if show_delayed: + message["Attributes"]["IsDelayed"] = str(sqs_message.is_delayed).lower() # noqa + messages.append(message) + message["ReceiptHandle"] = receipt_handle + + return messages diff --git a/localstack-core/localstack/services/sqs/models.py b/localstack-core/localstack/services/sqs/models.py index a9357b75d1249..15c2bafb1e2b8 100644 --- a/localstack-core/localstack/services/sqs/models.py +++ b/localstack-core/localstack/services/sqs/models.py @@ -1,3 +1,4 @@ +import copy import hashlib import heapq import inspect @@ -15,6 +16,7 @@ AttributeNameList, InvalidAttributeName, Message, + MessageAttributeNameList, MessageSystemAttributeName, QueueAttributeMap, QueueAttributeName, @@ -29,12 +31,15 @@ ) from localstack.services.sqs.queue import InterruptiblePriorityQueue, InterruptibleQueue from localstack.services.sqs.utils import ( + create_message_attribute_hash, encode_move_task_handle, encode_receipt_handle, extract_receipt_handle_info, global_message_sequence, guess_endpoint_strategy_and_host, is_message_deduplication_id_required, + message_filter_attributes, + message_filter_message_attributes, ) from localstack.services.stores import AccountRegionBundle, BaseStore, LocalAttribute from localstack.utils.aws.arns import get_partition @@ -153,7 +158,7 @@ def is_visible(self) -> bool: """ Returns false if the message has a visibility deadline that is in the future. - :return: whether the message is visibile or not. + :return: whether the message is visible or not. """ if self.visibility_deadline is None: return True @@ -190,6 +195,41 @@ def __repr__(self): return f"SqsMessage(id={self.message_id},group={self.message_group_id})" +def to_sqs_api_message( + standard_message: SqsMessage, + attribute_names: AttributeNameList = None, + message_attribute_names: MessageAttributeNameList = None, +) -> Message: + """ + Utility function to convert an SQS message from LocalStack's internal representation to the AWS API + concept 'Message', which is the format returned by the ``ReceiveMessage`` operation. + + :param standard_message: A LocalStack SQS message + :param attribute_names: the attribute name list to filter + :param message_attribute_names: the message attribute names to filter + :return: a copy of the original Message with updated message attributes and MD5 attribute hash sums + """ + # prepare message for receiver + message = copy.deepcopy(standard_message.message) + + # update system attributes of the message copy + message["Attributes"][MessageSystemAttributeName.ApproximateFirstReceiveTimestamp] = str( + int((standard_message.first_received or 0) * 1000) + ) + + # filter attributes for receiver + message_filter_attributes(message, attribute_names) + message_filter_message_attributes(message, message_attribute_names) + if message.get("MessageAttributes"): + message["MD5OfMessageAttributes"] = create_message_attribute_hash( + message["MessageAttributes"] + ) + else: + # delete the value that was computed when creating the message + message.pop("MD5OfMessageAttributes", None) + return message + + class ReceiveMessageResult: """ Object to communicate the result of a "receive messages" operation between the SqsProvider and diff --git a/localstack-core/localstack/services/sqs/provider.py b/localstack-core/localstack/services/sqs/provider.py index 6049e336b8bb0..49e177682a75a 100644 --- a/localstack-core/localstack/services/sqs/provider.py +++ b/localstack-core/localstack/services/sqs/provider.py @@ -1,5 +1,3 @@ -import copy -import hashlib import json import logging import re @@ -8,15 +6,12 @@ from collections.abc import Iterable from concurrent.futures.thread import ThreadPoolExecutor from itertools import islice -from typing import Literal from botocore.utils import InvalidArnException -from moto.sqs.models import BINARY_TYPE_FIELD_INDEX, STRING_TYPE_FIELD_INDEX -from moto.sqs.models import Message as MotoMessage from werkzeug import Request as WerkzeugRequest from localstack import config -from localstack.aws.api import CommonServiceException, RequestContext, ServiceException +from localstack.aws.api import RequestContext, ServiceException from localstack.aws.api.sqs import ( ActionNameList, AttributeNameList, @@ -70,11 +65,8 @@ Token, TooManyEntriesInBatchRequest, ) -from localstack.aws.protocol.parser import create_parser -from localstack.aws.protocol.serializer import aws_response_serializer from localstack.aws.spec import load_service from localstack.config import SQS_DISABLE_MAX_NUMBER_OF_MESSAGE_LIMIT -from localstack.http import Request, route from localstack.services.edge import ROUTER from localstack.services.plugins import ServiceLifecycleHook from localstack.services.sqs import constants as sqs_constants @@ -84,6 +76,7 @@ HEADER_LOCALSTACK_SQS_OVERRIDE_WAIT_TIME_SECONDS, MAX_RESULT_LIMIT, ) +from localstack.services.sqs.developer_api import SqsDeveloperApi from localstack.services.sqs.exceptions import ( InvalidParameterValueException, MissingRequiredParameterException, @@ -97,8 +90,10 @@ SqsStore, StandardQueue, sqs_stores, + to_sqs_api_message, ) from localstack.services.sqs.utils import ( + create_message_attribute_hash, decode_move_task_handle, generate_message_id, is_fifo_queue, @@ -107,7 +102,6 @@ ) from localstack.services.stores import AccountRegionBundle from localstack.utils.aws.arns import parse_arn -from localstack.utils.aws.request_context import extract_region_from_headers from localstack.utils.bootstrap import is_api_enabled from localstack.utils.cloudwatch.cloudwatch_util import ( SqsMetricBatchData, @@ -127,13 +121,6 @@ _STORE_LOCK = threading.RLock() -class InvalidAddress(ServiceException): - code = "InvalidAddress" - message = "The address https://queue.amazonaws.com/ is not valid for this endpoint." - sender_fault = True - status_code = 404 - - def assert_queue_name(queue_name: str, fifo: bool = False): if queue_name.endswith(".fifo"): if not fifo: @@ -640,165 +627,6 @@ def check_fifo_id(fifo_id: str | None, parameter: str): ) -def get_sqs_protocol(request: Request) -> Literal["query", "json"]: - content_type = request.headers.get("Content-Type") - return "json" if content_type == "application/x-amz-json-1.0" else "query" - - -def sqs_auto_protocol_aws_response_serializer(service_name: str, operation: str): - def _decorate(fn): - def _proxy(*args, **kwargs): - # extract request from function invocation (decorator can be used for methods as well as for functions). - if len(args) > 0 and isinstance(args[0], WerkzeugRequest): - # function - request = args[0] - elif len(args) > 1 and isinstance(args[1], WerkzeugRequest): - # method (arg[0] == self) - request = args[1] - elif "request" in kwargs: - request = kwargs["request"] - else: - raise ValueError(f"could not find Request in signature of function {fn}") - - protocol = get_sqs_protocol(request) - return aws_response_serializer(service_name, operation, protocol)(fn)(*args, **kwargs) - - return _proxy - - return _decorate - - -class SqsDeveloperEndpoints: - """ - A set of SQS developer tool endpoints: - - - ``/_aws/sqs/messages``: list SQS messages without side effects, compatible with ``ReceiveMessage``. - """ - - def __init__(self, stores=None): - self.stores = stores or sqs_stores - - @route("/_aws/sqs/messages", methods=["GET", "POST"]) - @sqs_auto_protocol_aws_response_serializer("sqs", "ReceiveMessage") - def list_messages(self, request: Request) -> ReceiveMessageResult: - """ - This endpoint expects a ``QueueUrl`` request parameter (either as query arg or form parameter), similar to - the ``ReceiveMessage`` operation. It will parse the Queue URL generated by one of the SQS endpoint strategies. - """ - - if "x-amz-" in request.mimetype or "x-www-form-urlencoded" in request.mimetype: - # only parse the request using a parser if it comes from an AWS client - protocol = get_sqs_protocol(request) - operation, service_request = create_parser( - load_service("sqs", protocol=protocol) - ).parse(request) - if operation.name != "ReceiveMessage": - raise CommonServiceException( - "InvalidRequest", "This endpoint only accepts ReceiveMessage calls" - ) - else: - service_request = dict(request.values) - - if not service_request.get("QueueUrl"): - raise QueueDoesNotExist() - - try: - account_id, region, queue_name = parse_queue_url(service_request.get("QueueUrl")) - except ValueError: - LOG.error( - "Error while parsing Queue URL from request values: %s", - service_request.get, - exc_info=LOG.isEnabledFor(logging.DEBUG), - ) - raise InvalidAddress() - - if not region: - region = extract_region_from_headers(request.headers) - - return self._get_and_serialize_messages(request, region, account_id, queue_name) - - @route("/_aws/sqs/messages///") - @sqs_auto_protocol_aws_response_serializer("sqs", "ReceiveMessage") - def list_messages_for_queue_url( - self, request: Request, region: str, account_id: str, queue_name: str - ) -> ReceiveMessageResult: - """ - This endpoint extracts the region, account_id, and queue_name directly from the URL rather than requiring the - QueueUrl as parameter. - """ - return self._get_and_serialize_messages(request, region, account_id, queue_name) - - def _get_and_serialize_messages( - self, - request: Request, - region: str, - account_id: str, - queue_name: str, - ) -> ReceiveMessageResult: - show_invisible = request.values.get("ShowInvisible", "").lower() in ["true", "1"] - show_delayed = request.values.get("ShowDelayed", "").lower() in ["true", "1"] - - try: - store = SqsProvider.get_store(account_id, region) - queue = store.queues[queue_name] - except KeyError: - LOG.info( - "no queue named %s in region %s and account %s", queue_name, region, account_id - ) - raise QueueDoesNotExist() - - messages = self._collect_messages( - queue, show_invisible=show_invisible, show_delayed=show_delayed - ) - - return ReceiveMessageResult(Messages=messages) - - def _collect_messages( - self, queue: SqsQueue, show_invisible: bool = False, show_delayed: bool = False - ) -> list[Message]: - """ - Retrieves from a given SqsQueue all visible messages without causing any side effects (not setting any - receive timestamps, receive counts, or visibility state). - - :param queue: the queue - :param show_invisible: show invisible messages as well - :param show_delayed: show delayed messages as well - :return: a list of messages - """ - receipt_handle = "SQS/BACKDOOR/ACCESS" # dummy receipt handle - - sqs_messages: list[SqsMessage] = [] - - if show_invisible: - sqs_messages.extend(queue.inflight) - - if isinstance(queue, StandardQueue): - sqs_messages.extend(queue.visible.queue) - elif isinstance(queue, FifoQueue): - for message_group in queue.message_groups.values(): - for sqs_message in message_group.messages: - sqs_messages.append(sqs_message) - else: - raise ValueError(f"unknown queue type {type(queue)}") - - if show_delayed: - sqs_messages.extend(queue.delayed) - - messages = [] - - for sqs_message in sqs_messages: - message: Message = to_sqs_api_message(sqs_message, [QueueAttributeName.All], ["All"]) - # these are all non-standard fields so we squelch the linter - if show_invisible: - message["Attributes"]["IsVisible"] = str(sqs_message.is_visible).lower() # noqa - if show_delayed: - message["Attributes"]["IsDelayed"] = str(sqs_message.is_delayed).lower() # noqa - messages.append(message) - message["ReceiptHandle"] = receipt_handle - - return messages - - class SqsProvider(SqsApi, ServiceLifecycleHook): """ LocalStack SQS Provider. @@ -840,7 +668,6 @@ def on_after_init(self): # default are limited to 500kb payload size by Werkzeug. we make sure we only *increase* the limit if it's # already set, and if it's already set to unlimited we leave it. from rolo import Request as RoloRequest - from werkzeug import Request as WerkzeugRequest # needed for the webserver integration (webservers create Werkzeug request objects) if WerkzeugRequest.max_form_memory_size is not None: @@ -855,7 +682,7 @@ def on_after_init(self): def on_before_start(self): query_api.register(ROUTER) - self._router_rules = ROUTER.add(SqsDeveloperEndpoints()) + self._router_rules = ROUTER.add(SqsDeveloperApi()) self._queue_update_worker.start() self._start_cloudwatch_metrics_reporting() @@ -1150,7 +977,7 @@ def send_message( MD5OfMessageBody=message["MD5OfBody"], MD5OfMessageAttributes=message.get("MD5OfMessageAttributes"), SequenceNumber=queue_item.sequence_number, - MD5OfMessageSystemAttributes=_create_message_attribute_hash(message_system_attributes), + MD5OfMessageSystemAttributes=create_message_attribute_hash(message_system_attributes), ) def send_message_batch( @@ -1196,7 +1023,7 @@ def send_message_batch( MessageId=message.get("MessageId"), MD5OfMessageBody=message.get("MD5OfBody"), MD5OfMessageAttributes=message.get("MD5OfMessageAttributes"), - MD5OfMessageSystemAttributes=_create_message_attribute_hash( + MD5OfMessageSystemAttributes=create_message_attribute_hash( message.get("message_system_attributes") ), SequenceNumber=queue_item.sequence_number, @@ -1250,7 +1077,7 @@ def _put_message( MD5OfBody=md5(message_body), Body=message_body, Attributes=self._create_message_attributes(context, message_system_attributes), - MD5OfMessageAttributes=_create_message_attribute_hash(message_attributes), + MD5OfMessageAttributes=create_message_attribute_hash(message_attributes), MessageAttributes=message_attributes, ) if self._cloudwatch_dispatcher: @@ -1810,34 +1637,6 @@ def _stop_cloudwatch_metrics_reporting(self): self._cloudwatch_dispatcher.shutdown() -# Method from moto's attribute_md5 of moto/sqs/models.py, separated from the Message Object -def _create_message_attribute_hash(message_attributes) -> str | None: - # To avoid the need to check for dict conformity everytime we invoke this function - if not isinstance(message_attributes, dict): - return - hash = hashlib.md5() - - for attrName in sorted(message_attributes.keys()): - attr_value = message_attributes[attrName] - # Encode name - MotoMessage.update_binary_length_and_value(hash, MotoMessage.utf8(attrName)) - # Encode data type - MotoMessage.update_binary_length_and_value(hash, MotoMessage.utf8(attr_value["DataType"])) - # Encode transport type and value - if attr_value.get("StringValue"): - hash.update(bytearray([STRING_TYPE_FIELD_INDEX])) - MotoMessage.update_binary_length_and_value( - hash, MotoMessage.utf8(attr_value.get("StringValue")) - ) - elif attr_value.get("BinaryValue"): - hash.update(bytearray([BINARY_TYPE_FIELD_INDEX])) - decoded_binary_value = attr_value.get("BinaryValue") - MotoMessage.update_binary_length_and_value(hash, decoded_binary_value) - # string_list_value, binary_list_value type is not implemented, reserved for the future use. - # See https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_MessageAttributeValue.html - return hash.hexdigest() - - def resolve_queue_location( context: RequestContext, queue_name: str | None = None, queue_url: str | None = None ) -> tuple[str, str | None, str]: @@ -1862,106 +1661,6 @@ def resolve_queue_location( return context.account_id, context.region, queue_name -def to_sqs_api_message( - standard_message: SqsMessage, - attribute_names: AttributeNameList = None, - message_attribute_names: MessageAttributeNameList = None, -) -> Message: - """ - Utility function to convert an SQS message from LocalStack's internal representation to the AWS API - concept 'Message', which is the format returned by the ``ReceiveMessage`` operation. - - :param standard_message: A LocalStack SQS message - :param attribute_names: the attribute name list to filter - :param message_attribute_names: the message attribute names to filter - :return: a copy of the original Message with updated message attributes and MD5 attribute hash sums - """ - # prepare message for receiver - message = copy.deepcopy(standard_message.message) - - # update system attributes of the message copy - message["Attributes"][MessageSystemAttributeName.ApproximateFirstReceiveTimestamp] = str( - int((standard_message.first_received or 0) * 1000) - ) - - # filter attributes for receiver - message_filter_attributes(message, attribute_names) - message_filter_message_attributes(message, message_attribute_names) - if message.get("MessageAttributes"): - message["MD5OfMessageAttributes"] = _create_message_attribute_hash( - message["MessageAttributes"] - ) - else: - # delete the value that was computed when creating the message - message.pop("MD5OfMessageAttributes", None) - return message - - -def message_filter_attributes(message: Message, names: AttributeNameList | None): - """ - Utility function filter from the given message (in-place) the system attributes from the given list. It will - apply all rules according to: - https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sqs.html#SQS.Client.receive_message. - - :param message: The message to filter (it will be modified) - :param names: the attributes names/filters - """ - if "Attributes" not in message: - return - - if not names: - del message["Attributes"] - return - - if QueueAttributeName.All in names: - return - - for k in list(message["Attributes"].keys()): - if k not in names: - del message["Attributes"][k] - - -def message_filter_message_attributes(message: Message, names: MessageAttributeNameList | None): - """ - Utility function filter from the given message (in-place) the message attributes from the given list. It will - apply all rules according to: - https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sqs.html#SQS.Client.receive_message. - - :param message: The message to filter (it will be modified) - :param names: the attributes names/filters (can be 'All', '.*', '*' or prefix filters like 'Foo.*') - """ - if not message.get("MessageAttributes"): - return - - if not names: - del message["MessageAttributes"] - return - - if "All" in names or ".*" in names or "*" in names: - return - - attributes = message["MessageAttributes"] - matched = [] - - keys = [name for name in names if ".*" not in name] - prefixes = [name.split(".*")[0] for name in names if ".*" in name] - - # match prefix filters - for k in attributes: - if k in keys: - matched.append(k) - continue - - for prefix in prefixes: - if k.startswith(prefix): - matched.append(k) - break - if matched: - message["MessageAttributes"] = {k: attributes[k] for k in matched} - else: - message.pop("MessageAttributes") - - def extract_message_count_from_headers(context: RequestContext) -> int | None: if override := context.request.headers.get( HEADER_LOCALSTACK_SQS_OVERRIDE_MESSAGE_COUNT, default=None, type=int diff --git a/localstack-core/localstack/services/sqs/query_api.py b/localstack-core/localstack/services/sqs/query_api.py index 0b76176c1294b..83bd0532ab547 100644 --- a/localstack-core/localstack/services/sqs/query_api.py +++ b/localstack-core/localstack/services/sqs/query_api.py @@ -35,7 +35,7 @@ service = load_service("sqs-query") parser = create_parser(service) -serializer = create_serializer(service) +serializer = create_serializer(service, "query") @route( diff --git a/localstack-core/localstack/services/sqs/utils.py b/localstack-core/localstack/services/sqs/utils.py index a67fd4c65c472..ff78955f0ce46 100644 --- a/localstack-core/localstack/services/sqs/utils.py +++ b/localstack-core/localstack/services/sqs/utils.py @@ -1,12 +1,20 @@ import base64 +import hashlib import itertools import json import re +import struct import time -from typing import Literal, NamedTuple +from typing import Any, Literal, NamedTuple from urllib.parse import urlparse -from localstack.aws.api.sqs import QueueAttributeName, ReceiptHandleIsInvalid +from localstack.aws.api.sqs import ( + AttributeNameList, + Message, + MessageAttributeNameList, + QueueAttributeName, + ReceiptHandleIsInvalid, +) from localstack.services.sqs.constants import ( DOMAIN_STRATEGY_URL_REGEX, LEGACY_STRATEGY_URL_REGEX, @@ -22,6 +30,11 @@ PATH_ENDPOINT = re.compile(PATH_STRATEGY_URL_REGEX) LEGACY_ENDPOINT = re.compile(LEGACY_STRATEGY_URL_REGEX) +STRING_TYPE_FIELD_INDEX = 1 +BINARY_TYPE_FIELD_INDEX = 2 +STRING_LIST_TYPE_FIELD_INDEX = 3 +BINARY_LIST_TYPE_FIELD_INDEX = 4 + def is_sqs_queue_url(url: str) -> bool: return any( @@ -184,3 +197,109 @@ def global_message_sequence(): def generate_message_id(): return long_uid() + + +def message_filter_attributes(message: Message, names: AttributeNameList | None): + """ + Utility function filter from the given message (in-place) the system attributes from the given list. It will + apply all rules according to: + https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sqs.html#SQS.Client.receive_message. + + :param message: The message to filter (it will be modified) + :param names: the attributes names/filters + """ + if "Attributes" not in message: + return + + if not names: + del message["Attributes"] + return + + if QueueAttributeName.All in names: + return + + for k in list(message["Attributes"].keys()): + if k not in names: + del message["Attributes"][k] + + +def message_filter_message_attributes(message: Message, names: MessageAttributeNameList | None): + """ + Utility function filter from the given message (in-place) the message attributes from the given list. It will + apply all rules according to: + https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sqs.html#SQS.Client.receive_message. + + :param message: The message to filter (it will be modified) + :param names: the attributes names/filters (can be 'All', '.*', '*' or prefix filters like 'Foo.*') + """ + if not message.get("MessageAttributes"): + return + + if not names: + del message["MessageAttributes"] + return + + if "All" in names or ".*" in names or "*" in names: + return + + attributes = message["MessageAttributes"] + matched = [] + + keys = [name for name in names if ".*" not in name] + prefixes = [name.split(".*")[0] for name in names if ".*" in name] + + # match prefix filters + for k in attributes: + if k in keys: + matched.append(k) + continue + + for prefix in prefixes: + if k.startswith(prefix): + matched.append(k) + break + if matched: + message["MessageAttributes"] = {k: attributes[k] for k in matched} + else: + message.pop("MessageAttributes") + + +def _utf8(value: Any) -> bytes: # type: ignore[misc] + if isinstance(value, str): + return value.encode("utf-8") + return value + + +def _update_binary_length_and_value(md5: Any, value: bytes) -> None: # type: ignore[misc] + length_bytes = struct.pack("!I".encode("ascii"), len(value)) + md5.update(length_bytes) + md5.update(value) + + +def create_message_attribute_hash(message_attributes) -> str | None: + """ + Method from moto's attribute_md5 of moto/sqs/models.py, separated from the Message Object. + """ + # To avoid the need to check for dict conformity everytime we invoke this function + if not isinstance(message_attributes, dict): + return + + hash = hashlib.md5() + + for attrName in sorted(message_attributes.keys()): + attr_value = message_attributes[attrName] + # Encode name + _update_binary_length_and_value(hash, _utf8(attrName)) + # Encode data type + _update_binary_length_and_value(hash, _utf8(attr_value["DataType"])) + # Encode transport type and value + if attr_value.get("StringValue"): + hash.update(bytearray([STRING_TYPE_FIELD_INDEX])) + _update_binary_length_and_value(hash, _utf8(attr_value.get("StringValue"))) + elif attr_value.get("BinaryValue"): + hash.update(bytearray([BINARY_TYPE_FIELD_INDEX])) + decoded_binary_value = attr_value.get("BinaryValue") + _update_binary_length_and_value(hash, decoded_binary_value) + # string_list_value, binary_list_value type is not implemented, reserved for the future use. + # See https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_MessageAttributeValue.html + return hash.hexdigest() diff --git a/localstack-core/localstack/testing/pytest/marking.py b/localstack-core/localstack/testing/pytest/marking.py index 76c0f170c05bf..f677d64e0f249 100644 --- a/localstack-core/localstack/testing/pytest/marking.py +++ b/localstack-core/localstack/testing/pytest/marking.py @@ -75,6 +75,8 @@ class Markers: """The test requires docker or a compatible container engine - will not work on kubernetes""" lambda_runtime_update = pytest.mark.lambda_runtime_update """Tests to execute when updating snapshots for a new Lambda runtime""" + k8s_always_run = pytest.mark.k8s_always_run + """This tests will always run against k8s environment""" # pytest plugin @@ -226,3 +228,7 @@ def pytest_configure(config): "markers", "requires_in_process: mark the test as requiring the test to run inside the same process as LocalStack - will not work if tests are run against a running LS container.", ) + config.addinivalue_line( + "markers", + "k8s_always_run: mark the test to always run in k8s environment. This allows us to run tests that would otherwise be skipped, such as localstack_only tests.", + ) diff --git a/localstack-core/localstack/utils/catalog/catalog.py b/localstack-core/localstack/utils/catalog/catalog.py new file mode 100644 index 0000000000000..ca5e7e8bcbf1e --- /dev/null +++ b/localstack-core/localstack/utils/catalog/catalog.py @@ -0,0 +1,135 @@ +import logging +from abc import abstractmethod + +from plux import Plugin + +from localstack.services.cloudformation.resource_provider import ( + plugin_manager as cfn_plugin_manager, +) +from localstack.utils.catalog.catalog_loader import RemoteCatalogLoader +from localstack.utils.catalog.common import ( + AwsServiceOperationsSupportInLatest, + AwsServicesSupportInLatest, + AwsServiceSupportAtRuntime, + CloudFormationResourcesSupportAtRuntime, + CloudFormationResourcesSupportInLatest, + LocalstackEmulatorType, +) + +ServiceName = str +ServiceOperations = set[str] +ProviderName = str +CfnResourceName = str +CfnResourceMethodName = str +AwsServicesSupportStatus = ( + AwsServiceSupportAtRuntime | AwsServicesSupportInLatest | AwsServiceOperationsSupportInLatest +) +CfnResourceSupportStatus = ( + CloudFormationResourcesSupportInLatest | CloudFormationResourcesSupportAtRuntime +) + +LOG = logging.getLogger(__name__) + + +class CatalogPlugin(Plugin): + namespace = "localstack.utils.catalog" + + @staticmethod + def get_cfn_resources_catalog(cloudformation_resources: dict): + cfn_resources_catalog = {} + for emulator_type, resources in cloudformation_resources.items(): + for resource_name, resource in resources.items(): + cfn_resources_catalog[emulator_type] = {resource_name: set(resource.methods)} + return cfn_resources_catalog + + @staticmethod + def get_aws_services_at_runtime(): + from localstack.services.plugins import SERVICE_PLUGINS + + return SERVICE_PLUGINS.list_available() + + @abstractmethod + def get_aws_service_status( + self, service_name: str, operation_name: str | None = None + ) -> AwsServicesSupportStatus | None: + pass + + @abstractmethod + def get_cloudformation_resource_status( + self, resource_name: str, service_name: str + ) -> CfnResourceSupportStatus | AwsServicesSupportInLatest | None: + pass + + +class AwsCatalogRuntimePlugin(CatalogPlugin): + name = "aws-catalog-runtime-only" + + def get_aws_service_status( + self, service_name: str, operation_name: str | None = None + ) -> AwsServicesSupportStatus | None: + return None + + def get_cloudformation_resource_status( + self, resource_name: str, service_name: str + ) -> CfnResourceSupportStatus | AwsServicesSupportInLatest | None: + return None + + +class AwsCatalogRemoteStatePlugin(CatalogPlugin): + name = "aws-catalog-remote-state" + current_emulator_type: LocalstackEmulatorType = LocalstackEmulatorType.COMMUNITY + services_in_latest: dict[ServiceName, dict[LocalstackEmulatorType, ServiceOperations]] = {} + services_at_runtime: set[ServiceName] = set() + cfn_resources_in_latest: dict[ + LocalstackEmulatorType, dict[CfnResourceName, set[CfnResourceMethodName]] + ] = {} + cfn_resources_at_runtime: set[CfnResourceName] = set() + + def __init__(self, remote_catalog_loader: RemoteCatalogLoader | None = None) -> None: + catalog_loader = remote_catalog_loader or RemoteCatalogLoader() + remote_catalog = catalog_loader.get_remote_catalog() + for service_name, emulators in remote_catalog.services.items(): + for emulator_type, service_provider in emulators.items(): + self.services_in_latest.setdefault(service_name, {})[emulator_type] = set( + service_provider.operations + ) + + self.cfn_resources_in_latest = self.get_cfn_resources_catalog( + remote_catalog.cloudformation_resources + ) + self.cfn_resources_at_runtime = set(cfn_plugin_manager.list_names()) + self.services_at_runtime = self.get_aws_services_at_runtime() + + def get_aws_service_status( + self, service_name: str, operation_name: str | None = None + ) -> AwsServicesSupportStatus | None: + if not self.services_in_latest: + return None + if service_name not in self.services_in_latest: + return AwsServicesSupportInLatest.NOT_SUPPORTED + if self.current_emulator_type not in self.services_in_latest[service_name]: + return AwsServicesSupportInLatest.SUPPORTED_WITH_LICENSE_UPGRADE + if not operation_name: + return AwsServicesSupportInLatest.SUPPORTED + if operation_name in self.services_in_latest[service_name][self.current_emulator_type]: + return AwsServiceOperationsSupportInLatest.SUPPORTED + for emulator_type in self.services_in_latest[service_name]: + if emulator_type is self.current_emulator_type: + continue + if operation_name in self.services_in_latest[service_name][emulator_type]: + return AwsServiceOperationsSupportInLatest.SUPPORTED_WITH_LICENSE_UPGRADE + return AwsServiceOperationsSupportInLatest.NOT_SUPPORTED + + def get_cloudformation_resource_status( + self, resource_name: str, service_name: str + ) -> CfnResourceSupportStatus | AwsServicesSupportInLatest | None: + if resource_name in self.cfn_resources_at_runtime: + return CloudFormationResourcesSupportAtRuntime.AVAILABLE + if service_name in self.services_at_runtime: + if resource_name in self.cfn_resources_in_latest[self.current_emulator_type]: + return CloudFormationResourcesSupportInLatest.SUPPORTED + else: + return CloudFormationResourcesSupportInLatest.NOT_SUPPORTED + if service_name in self.services_in_latest: + return self.get_aws_service_status(service_name, operation_name=None) + return AwsServicesSupportInLatest.NOT_SUPPORTED diff --git a/localstack-core/localstack/utils/catalog/catalog_loader.py b/localstack-core/localstack/utils/catalog/catalog_loader.py new file mode 100644 index 0000000000000..885e2d9978a79 --- /dev/null +++ b/localstack-core/localstack/utils/catalog/catalog_loader.py @@ -0,0 +1,11 @@ +import json + +from localstack.utils.catalog.common import AwsRemoteCatalog + +LICENSE_CATALOG_PATH = "" + + +class RemoteCatalogLoader: + def get_remote_catalog(self) -> AwsRemoteCatalog: + with open(LICENSE_CATALOG_PATH) as f: + return AwsRemoteCatalog(**json.load(f)) diff --git a/localstack-core/localstack/utils/catalog/common.py b/localstack-core/localstack/utils/catalog/common.py new file mode 100644 index 0000000000000..df4d129b37d77 --- /dev/null +++ b/localstack-core/localstack/utils/catalog/common.py @@ -0,0 +1,57 @@ +from enum import StrEnum + +from pydantic import BaseModel + + +class CloudFormationResource(BaseModel): + methods: list[str] + + +class AwsServiceCatalog(BaseModel): + provider: str + operations: list[str] + plans: list[str] + + +class LocalStackMetadata(BaseModel): + version: str + + +class AwsRemoteCatalog(BaseModel): + schema_version: str + localstack: LocalStackMetadata + services: dict[str, dict[str, AwsServiceCatalog]] + cloudformation_resources: dict[str, dict[str, CloudFormationResource]] + + +class LocalstackEmulatorType(StrEnum): + COMMUNITY = "community" + PRO = "pro" + + +class AwsServiceSupportAtRuntime(StrEnum): + AVAILABLE = "AVAILABLE" + AVAILABLE_WITH_LICENSE_UPGRADE = "AVAILABLE_WITH_LICENSE_UPGRADE" + NOT_IMPLEMENTED = "NOT_IMPLEMENTED" + + +class AwsServicesSupportInLatest(StrEnum): + SUPPORTED = "SUPPORTED" + SUPPORTED_WITH_LICENSE_UPGRADE = "SUPPORTED_WITH_LICENSE_UPGRADE" + NOT_SUPPORTED = "NOT_SUPPORTED" + NON_DEFAULT_PROVIDER = "NON_DEFAULT_PROVIDER" + + +class AwsServiceOperationsSupportInLatest(StrEnum): + SUPPORTED = "SUPPORTED" + SUPPORTED_WITH_LICENSE_UPGRADE = "SUPPORTED_WITH_LICENSE_UPGRADE" + NOT_SUPPORTED = "NOT_SUPPORTED" + + +class CloudFormationResourcesSupportInLatest(StrEnum): + SUPPORTED = "SUPPORTED" + NOT_SUPPORTED = "NOT_SUPPORTED" + + +class CloudFormationResourcesSupportAtRuntime(StrEnum): + AVAILABLE = "AVAILABLE" diff --git a/localstack-core/localstack/utils/catalog/plugins.py b/localstack-core/localstack/utils/catalog/plugins.py new file mode 100644 index 0000000000000..8324268ba5340 --- /dev/null +++ b/localstack-core/localstack/utils/catalog/plugins.py @@ -0,0 +1,28 @@ +import logging + +from plux import PluginManager + +from localstack.utils.catalog.catalog import CatalogPlugin +from localstack.utils.objects import singleton_factory + +LOG = logging.getLogger(__name__) + + +@singleton_factory +def get_aws_catalog() -> CatalogPlugin: + plugin_manager = PluginManager(CatalogPlugin.namespace) + try: + plugin_name = "aws-catalog-remote-state-with-license" + if not plugin_manager.exists(plugin_name): + plugin_name = "aws-catalog-remote-state" + return plugin_manager.load(plugin_name) + except Exception as e: + LOG.debug( + "Failed to load catalog plugin with the latest LocalStack services support data, falling back to catalog without remote state: %s", + e, + ) + # Try to load runtime catalog from pro version first + fallback_plugin_name = "aws-catalog-runtime-only-with-license" + if not plugin_manager.exists(fallback_plugin_name): + fallback_plugin_name = "aws-catalog-runtime-only" + return plugin_manager.load(fallback_plugin_name) diff --git a/localstack-core/localstack/utils/time.py b/localstack-core/localstack/utils/time.py index 3ab8804febd90..ce0d7f402770d 100644 --- a/localstack-core/localstack/utils/time.py +++ b/localstack-core/localstack/utils/time.py @@ -1,6 +1,7 @@ import time from datetime import date, datetime, timezone, tzinfo from typing import Optional +from zoneinfo import ZoneInfo TIMESTAMP_FORMAT = "%Y-%m-%dT%H:%M:%S" TIMESTAMP_FORMAT_TZ = "%Y-%m-%dT%H:%M:%SZ" @@ -42,6 +43,11 @@ def epoch_timestamp() -> float: def parse_timestamp(ts_str: str) -> datetime: + """ + Parse the incoming date string into a timezone aware datetime object + :param ts_str: + :return: + """ for ts_format in [ TIMESTAMP_FORMAT, TIMESTAMP_FORMAT_TZ, @@ -49,7 +55,10 @@ def parse_timestamp(ts_str: str) -> datetime: TIMESTAMP_READABLE_FORMAT, ]: try: - return datetime.strptime(ts_str, ts_format) + value = datetime.strptime(ts_str, ts_format) + if value.tzinfo is None: + value = value.replace(tzinfo=ZoneInfo("UTC")) + return value except ValueError: pass raise Exception(f"Unable to parse timestamp string with any known formats: {ts_str}") diff --git a/pyproject.toml b/pyproject.toml index b69f7e59efe05..400e439bde8df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ dependencies = [ dynamic = ["version"] classifiers = [ "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.13", "Topic :: Internet", "Topic :: Software Development :: Testing", "Topic :: System :: Emulators", @@ -53,9 +53,9 @@ Issues = "https://github.com/localstack/localstack/issues" # minimal required to actually run localstack on the host for services natively implemented in python base-runtime = [ # pinned / updated by ASF update action - "boto3==1.40.30", + "boto3==1.40.40", # pinned / updated by ASF update action - "botocore==1.40.30", + "botocore==1.40.40", "awscrt>=0.13.14,!=0.27.1", "cbor2>=5.5.0", "dnspython>=1.16.0", @@ -78,7 +78,7 @@ base-runtime = [ runtime = [ "localstack-core[base-runtime]", # pinned / updated by ASF update action - "awscli==1.42.30", + "awscli==1.42.40", "airspeed-ext>=0.6.3", # version that has a built wheel "kclpy-ext>=3.0.0", @@ -93,7 +93,7 @@ runtime = [ "json5>=0.9.11", "jsonpath-ng>=1.6.1", "jsonpath-rw>=1.4.0", - "moto-ext[all]==5.1.11.post1", + "moto-ext[all]>=5.1.12.post22", "opensearch-py>=2.4.1", "pymongo>=4.2.0", "pyopenssl>=23.0.0", diff --git a/requirements-base-runtime.txt b/requirements-base-runtime.txt index 8dde73a917631..a38115222821d 100644 --- a/requirements-base-runtime.txt +++ b/requirements-base-runtime.txt @@ -1,19 +1,19 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.13 # by the following command: # -# pip-compile --extra=base-runtime --output-file=requirements-base-runtime.txt --strip-extras --unsafe-package=distribute --unsafe-package=localstack-core --unsafe-package=pip --unsafe-package=setuptools pyproject.toml +# pip-compile --cert=None --client-cert=None --extra=base-runtime --index-url=None --output-file=requirements-base-runtime.txt --pip-args=None --strip-extras --unsafe-package=distribute --unsafe-package=localstack-core --unsafe-package=pip --unsafe-package=setuptools pyproject.toml # attrs==25.3.0 # via # jsonschema # localstack-twisted # referencing -awscrt==0.27.6 +awscrt==0.28.1 # via localstack-core (pyproject.toml) -boto3==1.40.30 +boto3==1.40.40 # via localstack-core (pyproject.toml) -botocore==1.40.30 +botocore==1.40.40 # via # boto3 # localstack-core (pyproject.toml) @@ -30,11 +30,11 @@ cffi==2.0.0 # via cryptography charset-normalizer==3.4.3 # via requests -click==8.2.1 +click==8.3.0 # via localstack-core (pyproject.toml) constantly==23.10.4 # via localstack-twisted -cryptography==45.0.7 +cryptography==46.0.1 # via # localstack-core (pyproject.toml) # pyopenssl @@ -98,7 +98,7 @@ localstack-twisted==24.3.0 # via localstack-core (pyproject.toml) markdown-it-py==4.0.0 # via rich -markupsafe==3.0.2 +markupsafe==3.0.3 # via werkzeug mdurl==0.1.2 # via markdown-it-py @@ -124,13 +124,13 @@ priority==1.3.0 # via # hypercorn # localstack-twisted -psutil==7.0.0 +psutil==7.1.0 # via localstack-core (pyproject.toml) pycparser==2.23 # via cffi pygments==2.19.2 # via rich -pyopenssl==25.2.0 +pyopenssl==25.3.0 # via # localstack-core (pyproject.toml) # localstack-twisted @@ -140,7 +140,7 @@ python-dateutil==2.9.0.post0 # via botocore python-dotenv==1.1.1 # via localstack-core (pyproject.toml) -pyyaml==6.0.2 +pyyaml==6.0.3 # via # jsonschema-path # localstack-core (pyproject.toml) @@ -183,9 +183,7 @@ tailer==0.4.1 typing-extensions==4.15.0 # via # localstack-twisted - # pyopenssl # readerwriterlock - # referencing urllib3==2.5.0 # via # botocore @@ -199,9 +197,9 @@ werkzeug==3.1.3 # rolo wsproto==1.2.0 # via hypercorn -xmltodict==1.0.0 +xmltodict==1.0.2 # via localstack-core (pyproject.toml) -zope-interface==8.0 +zope-interface==8.0.1 # via localstack-twisted # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements-basic.txt b/requirements-basic.txt index 31b092b618469..bb3f799d08277 100644 --- a/requirements-basic.txt +++ b/requirements-basic.txt @@ -1,8 +1,8 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.13 # by the following command: # -# pip-compile --output-file=requirements-basic.txt --strip-extras --unsafe-package=distribute --unsafe-package=localstack-core --unsafe-package=pip --unsafe-package=setuptools pyproject.toml +# pip-compile --cert=None --client-cert=None --index-url=None --output-file=requirements-basic.txt --pip-args=None --strip-extras --unsafe-package=distribute --unsafe-package=localstack-core --unsafe-package=pip --unsafe-package=setuptools pyproject.toml # build==1.3.0 # via localstack-core (pyproject.toml) @@ -14,9 +14,9 @@ cffi==2.0.0 # via cryptography charset-normalizer==3.4.3 # via requests -click==8.2.1 +click==8.3.0 # via localstack-core (pyproject.toml) -cryptography==45.0.7 +cryptography==46.0.1 # via localstack-core (pyproject.toml) dill==0.3.6 # via localstack-core (pyproject.toml) @@ -34,7 +34,7 @@ packaging==25.0 # via build plux==1.13.0 # via localstack-core (pyproject.toml) -psutil==7.0.0 +psutil==7.1.0 # via localstack-core (pyproject.toml) pycparser==2.23 # via cffi @@ -44,7 +44,7 @@ pyproject-hooks==1.2.0 # via build python-dotenv==1.1.1 # via localstack-core (pyproject.toml) -pyyaml==6.0.2 +pyyaml==6.0.3 # via localstack-core (pyproject.toml) requests==2.32.5 # via localstack-core (pyproject.toml) diff --git a/requirements-dev.txt b/requirements-dev.txt index bc929a132c03c..51847db0fbec4 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,8 +1,8 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.13 # by the following command: # -# pip-compile --extra=dev --output-file=requirements-dev.txt --strip-extras --unsafe-package=distribute --unsafe-package=localstack-core --unsafe-package=pip --unsafe-package=setuptools pyproject.toml +# pip-compile --cert=None --client-cert=None --extra=dev --index-url=None --output-file=requirements-dev.txt --pip-args=None --strip-extras --unsafe-package=distribute --unsafe-package=localstack-core --unsafe-package=pip --unsafe-package=setuptools pyproject.toml # airspeed-ext==0.6.9 # via localstack-core @@ -12,9 +12,9 @@ antlr4-python3-runtime==4.13.2 # via # localstack-core # moto-ext -anyio==4.10.0 +anyio==4.11.0 # via httpx -apispec==6.8.3 +apispec==6.8.4 # via localstack-core argparse==1.4.0 # via kclpy-ext @@ -29,9 +29,9 @@ aws-cdk-asset-awscli-v1==2.2.242 # via aws-cdk-lib aws-cdk-asset-node-proxy-agent-v6==2.1.0 # via aws-cdk-lib -aws-cdk-cloud-assembly-schema==48.9.0 +aws-cdk-cloud-assembly-schema==48.11.0 # via aws-cdk-lib -aws-cdk-lib==2.215.0 +aws-cdk-lib==2.218.0 # via localstack-core aws-sam-translator==1.100.0 # via @@ -39,17 +39,17 @@ aws-sam-translator==1.100.0 # localstack-core aws-xray-sdk==2.14.0 # via moto-ext -awscli==1.42.30 +awscli==1.42.40 # via localstack-core -awscrt==0.27.6 +awscrt==0.28.1 # via localstack-core -boto3==1.40.30 +boto3==1.40.40 # via # aws-sam-translator # kclpy-ext # localstack-core # moto-ext -botocore==1.40.30 +botocore==1.40.40 # via # aws-xray-sdk # awscli @@ -80,11 +80,11 @@ cffi==2.0.0 # via cryptography cfgv==3.4.0 # via pre-commit -cfn-lint==1.39.1 +cfn-lint==1.40.0 # via moto-ext charset-normalizer==3.4.3 # via requests -click==8.2.1 +click==8.3.0 # via # localstack-core # localstack-core (pyproject.toml) @@ -94,22 +94,22 @@ constantly==23.10.4 # via localstack-twisted constructs==10.4.2 # via aws-cdk-lib -coverage==7.10.6 +coverage==6.5.0 # via # coveralls # localstack-core -coveralls==4.0.1 +coveralls==3.3.1 # via localstack-core (pyproject.toml) crontab==1.0.5 # via localstack-core -cryptography==45.0.7 +cryptography==46.0.1 # via # joserfc # localstack-core # localstack-core (pyproject.toml) # moto-ext # pyopenssl -cython==3.1.3 +cython==3.1.4 # via localstack-core (pyproject.toml) decorator==5.2.1 # via jsonpath-rw @@ -191,11 +191,11 @@ jmespath==1.0.1 # via # boto3 # botocore -joserfc==1.3.3 +joserfc==1.3.4 # via moto-ext jpype1==1.6.0 # via localstack-core -jsii==1.114.1 +jsii==1.115.0 # via # aws-cdk-asset-awscli-v1 # aws-cdk-asset-node-proxy-agent-v6 @@ -242,7 +242,7 @@ localstack-twisted==24.3.0 # via localstack-core markdown-it-py==4.0.0 # via rich -markupsafe==3.0.2 +markupsafe==3.0.3 # via # jinja2 # werkzeug @@ -250,13 +250,13 @@ mdurl==0.1.2 # via markdown-it-py more-itertools==10.8.0 # via openapi-core -moto-ext==5.1.11.post1 +moto-ext==5.1.13.post18 # via localstack-core mpmath==1.3.0 # via sympy multipart==1.3.0 # via moto-ext -mypy==1.18.1 +mypy==1.18.2 # via localstack-core (pyproject.toml) mypy-extensions==1.1.0 # via mypy @@ -319,7 +319,7 @@ priority==1.3.0 # via # hypercorn # localstack-twisted -psutil==7.0.0 +psutil==7.1.0 # via # localstack-core # localstack-core (pyproject.toml) @@ -345,15 +345,15 @@ pygments==2.19.2 # via # pytest # rich -pymongo==4.15.0 +pymongo==4.15.1 # via localstack-core -pyopenssl==25.2.0 +pyopenssl==25.3.0 # via # localstack-core # localstack-twisted pypandoc==1.15 # via localstack-core (pyproject.toml) -pyparsing==3.2.4 +pyparsing==3.2.5 # via moto-ext pyproject-hooks==1.2.0 # via build @@ -381,7 +381,7 @@ python-dotenv==1.1.1 # via # localstack-core # localstack-core (pyproject.toml) -pyyaml==6.0.2 +pyyaml==6.0.3 # via # awscli # cfn-lint @@ -398,7 +398,7 @@ referencing==0.36.2 # jsonschema # jsonschema-path # jsonschema-specifications -regex==2025.9.1 +regex==2025.9.18 # via cfn-lint requests==2.32.5 # via @@ -433,7 +433,7 @@ rsa==4.7.2 # via awscli rstr==3.2.2 # via localstack-core (pyproject.toml) -ruff==0.13.0 +ruff==0.13.2 # via localstack-core (pyproject.toml) s3transfer==0.14.0 # via @@ -467,7 +467,6 @@ typeguard==2.13.3 # jsii typing-extensions==4.15.0 # via - # anyio # aws-sam-translator # cattrs # cfn-lint @@ -476,9 +475,7 @@ typing-extensions==4.15.0 # mypy # pydantic # pydantic-core - # pyopenssl # readerwriterlock - # referencing # typing-inspection typing-inspection==0.4.1 # via pydantic @@ -505,11 +502,11 @@ wrapt==1.17.3 # via aws-xray-sdk wsproto==1.2.0 # via hypercorn -xmltodict==1.0.0 +xmltodict==1.0.2 # via # localstack-core # moto-ext -zope-interface==8.0 +zope-interface==8.0.1 # via localstack-twisted # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements-runtime.txt b/requirements-runtime.txt index c2effb7bc86c0..af8460d90a512 100644 --- a/requirements-runtime.txt +++ b/requirements-runtime.txt @@ -1,8 +1,8 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.13 # by the following command: # -# pip-compile --extra=runtime --output-file=requirements-runtime.txt --strip-extras --unsafe-package=distribute --unsafe-package=localstack-core --unsafe-package=pip --unsafe-package=setuptools pyproject.toml +# pip-compile --cert=None --client-cert=None --extra=runtime --index-url=None --output-file=requirements-runtime.txt --pip-args=None --strip-extras --unsafe-package=distribute --unsafe-package=localstack-core --unsafe-package=pip --unsafe-package=setuptools pyproject.toml # airspeed-ext==0.6.9 # via localstack-core (pyproject.toml) @@ -12,7 +12,7 @@ antlr4-python3-runtime==4.13.2 # via # localstack-core (pyproject.toml) # moto-ext -apispec==6.8.3 +apispec==6.8.4 # via localstack-core (pyproject.toml) argparse==1.4.0 # via kclpy-ext @@ -27,17 +27,17 @@ aws-sam-translator==1.100.0 # localstack-core (pyproject.toml) aws-xray-sdk==2.14.0 # via moto-ext -awscli==1.42.30 +awscli==1.42.40 # via localstack-core (pyproject.toml) -awscrt==0.27.6 +awscrt==0.28.1 # via localstack-core -boto3==1.40.30 +boto3==1.40.40 # via # aws-sam-translator # kclpy-ext # localstack-core # moto-ext -botocore==1.40.30 +botocore==1.40.40 # via # aws-xray-sdk # awscli @@ -62,11 +62,11 @@ certifi==2025.8.3 # requests cffi==2.0.0 # via cryptography -cfn-lint==1.39.1 +cfn-lint==1.40.0 # via moto-ext charset-normalizer==3.4.3 # via requests -click==8.2.1 +click==8.3.0 # via # localstack-core # localstack-core (pyproject.toml) @@ -76,7 +76,7 @@ constantly==23.10.4 # via localstack-twisted crontab==1.0.5 # via localstack-core (pyproject.toml) -cryptography==45.0.7 +cryptography==46.0.1 # via # joserfc # localstack-core @@ -139,7 +139,7 @@ jmespath==1.0.1 # via # boto3 # botocore -joserfc==1.3.3 +joserfc==1.3.4 # via moto-ext jpype1==1.6.0 # via localstack-core (pyproject.toml) @@ -180,7 +180,7 @@ localstack-twisted==24.3.0 # via localstack-core markdown-it-py==4.0.0 # via rich -markupsafe==3.0.2 +markupsafe==3.0.3 # via # jinja2 # werkzeug @@ -188,7 +188,7 @@ mdurl==0.1.2 # via markdown-it-py more-itertools==10.8.0 # via openapi-core -moto-ext==5.1.11.post1 +moto-ext==5.1.13.post18 # via localstack-core (pyproject.toml) mpmath==1.3.0 # via sympy @@ -229,7 +229,7 @@ priority==1.3.0 # via # hypercorn # localstack-twisted -psutil==7.0.0 +psutil==7.1.0 # via # localstack-core # localstack-core (pyproject.toml) @@ -245,14 +245,14 @@ pydantic-core==2.33.2 # via pydantic pygments==2.19.2 # via rich -pymongo==4.15.0 +pymongo==4.15.1 # via localstack-core (pyproject.toml) -pyopenssl==25.2.0 +pyopenssl==25.3.0 # via # localstack-core # localstack-core (pyproject.toml) # localstack-twisted -pyparsing==3.2.4 +pyparsing==3.2.5 # via moto-ext pyproject-hooks==1.2.0 # via build @@ -265,7 +265,7 @@ python-dotenv==1.1.1 # via # localstack-core # localstack-core (pyproject.toml) -pyyaml==6.0.2 +pyyaml==6.0.3 # via # awscli # cfn-lint @@ -281,7 +281,7 @@ referencing==0.36.2 # jsonschema # jsonschema-path # jsonschema-specifications -regex==2025.9.1 +regex==2025.9.18 # via cfn-lint requests==2.32.5 # via @@ -339,9 +339,7 @@ typing-extensions==4.15.0 # localstack-twisted # pydantic # pydantic-core - # pyopenssl # readerwriterlock - # referencing # typing-inspection typing-inspection==0.4.1 # via pydantic @@ -363,11 +361,11 @@ wrapt==1.17.3 # via aws-xray-sdk wsproto==1.2.0 # via hypercorn -xmltodict==1.0.0 +xmltodict==1.0.2 # via # localstack-core # moto-ext -zope-interface==8.0 +zope-interface==8.0.1 # via localstack-twisted # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements-test.txt b/requirements-test.txt index d559277b08506..0cb48f0be4181 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,8 +1,8 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.13 # by the following command: # -# pip-compile --extra=test --output-file=requirements-test.txt --strip-extras --unsafe-package=distribute --unsafe-package=localstack-core --unsafe-package=pip --unsafe-package=setuptools pyproject.toml +# pip-compile --cert=None --client-cert=None --extra=test --index-url=None --output-file=requirements-test.txt --pip-args=None --strip-extras --unsafe-package=distribute --unsafe-package=localstack-core --unsafe-package=pip --unsafe-package=setuptools pyproject.toml # airspeed-ext==0.6.9 # via localstack-core @@ -12,9 +12,9 @@ antlr4-python3-runtime==4.13.2 # via # localstack-core # moto-ext -anyio==4.10.0 +anyio==4.11.0 # via httpx -apispec==6.8.3 +apispec==6.8.4 # via localstack-core argparse==1.4.0 # via kclpy-ext @@ -29,9 +29,9 @@ aws-cdk-asset-awscli-v1==2.2.242 # via aws-cdk-lib aws-cdk-asset-node-proxy-agent-v6==2.1.0 # via aws-cdk-lib -aws-cdk-cloud-assembly-schema==48.9.0 +aws-cdk-cloud-assembly-schema==48.11.0 # via aws-cdk-lib -aws-cdk-lib==2.215.0 +aws-cdk-lib==2.218.0 # via localstack-core (pyproject.toml) aws-sam-translator==1.100.0 # via @@ -39,17 +39,17 @@ aws-sam-translator==1.100.0 # localstack-core aws-xray-sdk==2.14.0 # via moto-ext -awscli==1.42.30 +awscli==1.42.40 # via localstack-core -awscrt==0.27.6 +awscrt==0.28.1 # via localstack-core -boto3==1.40.30 +boto3==1.40.40 # via # aws-sam-translator # kclpy-ext # localstack-core # moto-ext -botocore==1.40.30 +botocore==1.40.40 # via # aws-xray-sdk # awscli @@ -78,11 +78,11 @@ certifi==2025.8.3 # requests cffi==2.0.0 # via cryptography -cfn-lint==1.39.1 +cfn-lint==1.40.0 # via moto-ext charset-normalizer==3.4.3 # via requests -click==8.2.1 +click==8.3.0 # via # localstack-core # localstack-core (pyproject.toml) @@ -92,11 +92,11 @@ constantly==23.10.4 # via localstack-twisted constructs==10.4.2 # via aws-cdk-lib -coverage==7.10.6 +coverage==7.10.7 # via localstack-core (pyproject.toml) crontab==1.0.5 # via localstack-core -cryptography==45.0.7 +cryptography==46.0.1 # via # joserfc # localstack-core @@ -175,11 +175,11 @@ jmespath==1.0.1 # via # boto3 # botocore -joserfc==1.3.3 +joserfc==1.3.4 # via moto-ext jpype1==1.6.0 # via localstack-core -jsii==1.114.1 +jsii==1.115.0 # via # aws-cdk-asset-awscli-v1 # aws-cdk-asset-node-proxy-agent-v6 @@ -226,7 +226,7 @@ localstack-twisted==24.3.0 # via localstack-core markdown-it-py==4.0.0 # via rich -markupsafe==3.0.2 +markupsafe==3.0.3 # via # jinja2 # werkzeug @@ -234,7 +234,7 @@ mdurl==0.1.2 # via markdown-it-py more-itertools==10.8.0 # via openapi-core -moto-ext==5.1.11.post1 +moto-ext==5.1.13.post18 # via localstack-core mpmath==1.3.0 # via sympy @@ -283,7 +283,7 @@ priority==1.3.0 # via # hypercorn # localstack-twisted -psutil==7.0.0 +psutil==7.1.0 # via # localstack-core # localstack-core (pyproject.toml) @@ -309,13 +309,13 @@ pygments==2.19.2 # via # pytest # rich -pymongo==4.15.0 +pymongo==4.15.1 # via localstack-core -pyopenssl==25.2.0 +pyopenssl==25.3.0 # via # localstack-core # localstack-twisted -pyparsing==3.2.4 +pyparsing==3.2.5 # via moto-ext pyproject-hooks==1.2.0 # via build @@ -343,7 +343,7 @@ python-dotenv==1.1.1 # via # localstack-core # localstack-core (pyproject.toml) -pyyaml==6.0.2 +pyyaml==6.0.3 # via # awscli # cfn-lint @@ -359,7 +359,7 @@ referencing==0.36.2 # jsonschema # jsonschema-path # jsonschema-specifications -regex==2025.9.1 +regex==2025.9.18 # via cfn-lint requests==2.32.5 # via @@ -423,7 +423,6 @@ typeguard==2.13.3 # jsii typing-extensions==4.15.0 # via - # anyio # aws-sam-translator # cattrs # cfn-lint @@ -431,9 +430,7 @@ typing-extensions==4.15.0 # localstack-twisted # pydantic # pydantic-core - # pyopenssl # readerwriterlock - # referencing # typing-inspection typing-inspection==0.4.1 # via pydantic @@ -458,11 +455,11 @@ wrapt==1.17.3 # via aws-xray-sdk wsproto==1.2.0 # via hypercorn -xmltodict==1.0.0 +xmltodict==1.0.2 # via # localstack-core # moto-ext -zope-interface==8.0 +zope-interface==8.0.1 # via localstack-twisted # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements-typehint.txt b/requirements-typehint.txt index 301cc05305a58..8e525795fbc39 100644 --- a/requirements-typehint.txt +++ b/requirements-typehint.txt @@ -1,8 +1,8 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.13 # by the following command: # -# pip-compile --extra=typehint --output-file=requirements-typehint.txt --strip-extras --unsafe-package=distribute --unsafe-package=localstack-core --unsafe-package=pip --unsafe-package=setuptools pyproject.toml +# pip-compile --cert=None --client-cert=None --extra=typehint --index-url=None --output-file=requirements-typehint.txt --pip-args=None --strip-extras --unsafe-package=distribute --unsafe-package=localstack-core --unsafe-package=pip --unsafe-package=setuptools pyproject.toml # airspeed-ext==0.6.9 # via localstack-core @@ -12,9 +12,9 @@ antlr4-python3-runtime==4.13.2 # via # localstack-core # moto-ext -anyio==4.10.0 +anyio==4.11.0 # via httpx -apispec==6.8.3 +apispec==6.8.4 # via localstack-core argparse==1.4.0 # via kclpy-ext @@ -29,9 +29,9 @@ aws-cdk-asset-awscli-v1==2.2.242 # via aws-cdk-lib aws-cdk-asset-node-proxy-agent-v6==2.1.0 # via aws-cdk-lib -aws-cdk-cloud-assembly-schema==48.9.0 +aws-cdk-cloud-assembly-schema==48.11.0 # via aws-cdk-lib -aws-cdk-lib==2.215.0 +aws-cdk-lib==2.218.0 # via localstack-core aws-sam-translator==1.100.0 # via @@ -39,19 +39,19 @@ aws-sam-translator==1.100.0 # localstack-core aws-xray-sdk==2.14.0 # via moto-ext -awscli==1.42.30 +awscli==1.42.40 # via localstack-core -awscrt==0.27.6 +awscrt==0.28.1 # via localstack-core -boto3==1.40.30 +boto3==1.40.40 # via # aws-sam-translator # kclpy-ext # localstack-core # moto-ext -boto3-stubs==1.40.31 +boto3-stubs==1.40.41 # via localstack-core (pyproject.toml) -botocore==1.40.30 +botocore==1.40.40 # via # aws-xray-sdk # awscli @@ -59,7 +59,7 @@ botocore==1.40.30 # localstack-core # moto-ext # s3transfer -botocore-stubs==1.40.31 +botocore-stubs==1.40.33 # via boto3-stubs build==1.3.0 # via @@ -84,11 +84,11 @@ cffi==2.0.0 # via cryptography cfgv==3.4.0 # via pre-commit -cfn-lint==1.39.1 +cfn-lint==1.40.0 # via moto-ext charset-normalizer==3.4.3 # via requests -click==8.2.1 +click==8.3.0 # via # localstack-core # localstack-core (pyproject.toml) @@ -98,22 +98,22 @@ constantly==23.10.4 # via localstack-twisted constructs==10.4.2 # via aws-cdk-lib -coverage==7.10.6 +coverage==6.5.0 # via # coveralls # localstack-core -coveralls==4.0.1 +coveralls==3.3.1 # via localstack-core crontab==1.0.5 # via localstack-core -cryptography==45.0.7 +cryptography==46.0.1 # via # joserfc # localstack-core # localstack-core (pyproject.toml) # moto-ext # pyopenssl -cython==3.1.3 +cython==3.1.4 # via localstack-core decorator==5.2.1 # via jsonpath-rw @@ -195,11 +195,11 @@ jmespath==1.0.1 # via # boto3 # botocore -joserfc==1.3.3 +joserfc==1.3.4 # via moto-ext jpype1==1.6.0 # via localstack-core -jsii==1.114.1 +jsii==1.115.0 # via # aws-cdk-asset-awscli-v1 # aws-cdk-asset-node-proxy-agent-v6 @@ -246,7 +246,7 @@ localstack-twisted==24.3.0 # via localstack-core markdown-it-py==4.0.0 # via rich -markupsafe==3.0.2 +markupsafe==3.0.3 # via # jinja2 # werkzeug @@ -254,13 +254,13 @@ mdurl==0.1.2 # via markdown-it-py more-itertools==10.8.0 # via openapi-core -moto-ext==5.1.11.post1 +moto-ext==5.1.13.post18 # via localstack-core mpmath==1.3.0 # via sympy multipart==1.3.0 # via moto-ext -mypy==1.18.1 +mypy==1.18.2 # via localstack-core mypy-boto3-acm==1.40.0 # via boto3-stubs @@ -286,9 +286,9 @@ mypy-boto3-autoscaling==1.40.27 # via boto3-stubs mypy-boto3-backup==1.40.0 # via boto3-stubs -mypy-boto3-batch==1.40.19 +mypy-boto3-batch==1.40.36 # via boto3-stubs -mypy-boto3-ce==1.40.31 +mypy-boto3-ce==1.40.40 # via boto3-stubs mypy-boto3-cloudcontrol==1.40.0 # via boto3-stubs @@ -298,7 +298,7 @@ mypy-boto3-cloudfront==1.40.23 # via boto3-stubs mypy-boto3-cloudtrail==1.40.0 # via boto3-stubs -mypy-boto3-cloudwatch==1.40.27 +mypy-boto3-cloudwatch==1.40.38 # via boto3-stubs mypy-boto3-codebuild==1.40.8 # via boto3-stubs @@ -322,9 +322,9 @@ mypy-boto3-docdb==1.40.16 # via boto3-stubs mypy-boto3-dynamodb==1.40.20 # via boto3-stubs -mypy-boto3-dynamodbstreams==1.40.0 +mypy-boto3-dynamodbstreams==1.40.40 # via boto3-stubs -mypy-boto3-ec2==1.40.24 +mypy-boto3-ec2==1.40.40 # via boto3-stubs mypy-boto3-ecr==1.40.0 # via boto3-stubs @@ -332,7 +332,7 @@ mypy-boto3-ecs==1.40.29 # via boto3-stubs mypy-boto3-efs==1.40.0 # via boto3-stubs -mypy-boto3-eks==1.40.19 +mypy-boto3-eks==1.40.36 # via boto3-stubs mypy-boto3-elasticache==1.40.19 # via boto3-stubs @@ -354,7 +354,7 @@ mypy-boto3-fis==1.40.20 # via boto3-stubs mypy-boto3-glacier==1.40.18 # via boto3-stubs -mypy-boto3-glue==1.40.20 +mypy-boto3-glue==1.40.39 # via boto3-stubs mypy-boto3-iam==1.40.0 # via boto3-stubs @@ -376,13 +376,13 @@ mypy-boto3-kinesisanalytics==1.40.17 # via boto3-stubs mypy-boto3-kinesisanalyticsv2==1.40.14 # via boto3-stubs -mypy-boto3-kms==1.40.0 +mypy-boto3-kms==1.40.38 # via boto3-stubs mypy-boto3-lakeformation==1.40.19 # via boto3-stubs mypy-boto3-lambda==1.40.7 # via boto3-stubs -mypy-boto3-logs==1.40.0 +mypy-boto3-logs==1.40.32 # via boto3-stubs mypy-boto3-managedblockchain==1.40.15 # via boto3-stubs @@ -394,7 +394,7 @@ mypy-boto3-mq==1.40.23 # via boto3-stubs mypy-boto3-mwaa==1.40.0 # via boto3-stubs -mypy-boto3-neptune==1.40.22 +mypy-boto3-neptune==1.40.38 # via boto3-stubs mypy-boto3-opensearch==1.40.0 # via boto3-stubs @@ -414,7 +414,7 @@ mypy-boto3-rds==1.40.29 # via boto3-stubs mypy-boto3-rds-data==1.40.0 # via boto3-stubs -mypy-boto3-redshift==1.40.19 +mypy-boto3-redshift==1.40.40 # via boto3-stubs mypy-boto3-redshift-data==1.40.0 # via boto3-stubs @@ -446,11 +446,11 @@ mypy-boto3-sesv2==1.40.0 # via boto3-stubs mypy-boto3-sns==1.40.1 # via boto3-stubs -mypy-boto3-sqs==1.40.17 +mypy-boto3-sqs==1.40.35 # via boto3-stubs -mypy-boto3-ssm==1.40.0 +mypy-boto3-ssm==1.40.37 # via boto3-stubs -mypy-boto3-sso-admin==1.40.7 +mypy-boto3-sso-admin==1.40.37 # via boto3-stubs mypy-boto3-stepfunctions==1.40.0 # via boto3-stubs @@ -529,7 +529,7 @@ priority==1.3.0 # via # hypercorn # localstack-twisted -psutil==7.0.0 +psutil==7.1.0 # via # localstack-core # localstack-core (pyproject.toml) @@ -555,15 +555,15 @@ pygments==2.19.2 # via # pytest # rich -pymongo==4.15.0 +pymongo==4.15.1 # via localstack-core -pyopenssl==25.2.0 +pyopenssl==25.3.0 # via # localstack-core # localstack-twisted pypandoc==1.15 # via localstack-core -pyparsing==3.2.4 +pyparsing==3.2.5 # via moto-ext pyproject-hooks==1.2.0 # via build @@ -591,7 +591,7 @@ python-dotenv==1.1.1 # via # localstack-core # localstack-core (pyproject.toml) -pyyaml==6.0.2 +pyyaml==6.0.3 # via # awscli # cfn-lint @@ -608,7 +608,7 @@ referencing==0.36.2 # jsonschema # jsonschema-path # jsonschema-specifications -regex==2025.9.1 +regex==2025.9.18 # via cfn-lint requests==2.32.5 # via @@ -643,7 +643,7 @@ rsa==4.7.2 # via awscli rstr==3.2.2 # via localstack-core -ruff==0.13.0 +ruff==0.13.2 # via localstack-core s3transfer==0.14.0 # via @@ -681,122 +681,15 @@ types-s3transfer==0.13.1 # via boto3-stubs typing-extensions==4.15.0 # via - # anyio # aws-sam-translator - # boto3-stubs # cattrs # cfn-lint # jsii # localstack-twisted # mypy - # mypy-boto3-acm - # mypy-boto3-acm-pca - # mypy-boto3-amplify - # mypy-boto3-apigateway - # mypy-boto3-apigatewayv2 - # mypy-boto3-appconfig - # mypy-boto3-appconfigdata - # mypy-boto3-application-autoscaling - # mypy-boto3-appsync - # mypy-boto3-athena - # mypy-boto3-autoscaling - # mypy-boto3-backup - # mypy-boto3-batch - # mypy-boto3-ce - # mypy-boto3-cloudcontrol - # mypy-boto3-cloudformation - # mypy-boto3-cloudfront - # mypy-boto3-cloudtrail - # mypy-boto3-cloudwatch - # mypy-boto3-codebuild - # mypy-boto3-codecommit - # mypy-boto3-codeconnections - # mypy-boto3-codedeploy - # mypy-boto3-codepipeline - # mypy-boto3-codestar-connections - # mypy-boto3-cognito-identity - # mypy-boto3-cognito-idp - # mypy-boto3-dms - # mypy-boto3-docdb - # mypy-boto3-dynamodb - # mypy-boto3-dynamodbstreams - # mypy-boto3-ec2 - # mypy-boto3-ecr - # mypy-boto3-ecs - # mypy-boto3-efs - # mypy-boto3-eks - # mypy-boto3-elasticache - # mypy-boto3-elasticbeanstalk - # mypy-boto3-elbv2 - # mypy-boto3-emr - # mypy-boto3-emr-serverless - # mypy-boto3-es - # mypy-boto3-events - # mypy-boto3-firehose - # mypy-boto3-fis - # mypy-boto3-glacier - # mypy-boto3-glue - # mypy-boto3-iam - # mypy-boto3-identitystore - # mypy-boto3-iot - # mypy-boto3-iot-data - # mypy-boto3-iotanalytics - # mypy-boto3-iotwireless - # mypy-boto3-kafka - # mypy-boto3-kinesis - # mypy-boto3-kinesisanalytics - # mypy-boto3-kinesisanalyticsv2 - # mypy-boto3-kms - # mypy-boto3-lakeformation - # mypy-boto3-lambda - # mypy-boto3-logs - # mypy-boto3-managedblockchain - # mypy-boto3-mediaconvert - # mypy-boto3-mediastore - # mypy-boto3-mq - # mypy-boto3-mwaa - # mypy-boto3-neptune - # mypy-boto3-opensearch - # mypy-boto3-organizations - # mypy-boto3-pi - # mypy-boto3-pinpoint - # mypy-boto3-pipes - # mypy-boto3-qldb - # mypy-boto3-qldb-session - # mypy-boto3-rds - # mypy-boto3-rds-data - # mypy-boto3-redshift - # mypy-boto3-redshift-data - # mypy-boto3-resource-groups - # mypy-boto3-resourcegroupstaggingapi - # mypy-boto3-route53 - # mypy-boto3-route53resolver - # mypy-boto3-s3 - # mypy-boto3-s3control - # mypy-boto3-sagemaker - # mypy-boto3-sagemaker-runtime - # mypy-boto3-secretsmanager - # mypy-boto3-serverlessrepo - # mypy-boto3-servicediscovery - # mypy-boto3-ses - # mypy-boto3-sesv2 - # mypy-boto3-sns - # mypy-boto3-sqs - # mypy-boto3-ssm - # mypy-boto3-sso-admin - # mypy-boto3-stepfunctions - # mypy-boto3-sts - # mypy-boto3-timestream-query - # mypy-boto3-timestream-write - # mypy-boto3-transcribe - # mypy-boto3-verifiedpermissions - # mypy-boto3-wafv2 - # mypy-boto3-xray # pydantic # pydantic-core - # pyopenssl # readerwriterlock - # referencing # typing-inspection typing-inspection==0.4.1 # via pydantic @@ -823,11 +716,11 @@ wrapt==1.17.3 # via aws-xray-sdk wsproto==1.2.0 # via hypercorn -xmltodict==1.0.0 +xmltodict==1.0.2 # via # localstack-core # moto-ext -zope-interface==8.0 +zope-interface==8.0.1 # via localstack-twisted # The following packages are considered to be unsafe in a requirements file: diff --git a/scripts/update_cfn_resources.py b/scripts/update_cfn_resources.py new file mode 100644 index 0000000000000..ff318384fd682 --- /dev/null +++ b/scripts/update_cfn_resources.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +""" +Script to gather all CloudFormation resource types available across AWS regions. + +The collected data is written to `localstack/services/cloudformation/resources.py` as + +* `AWS_AVAILABLE_CFN_RESOURCES`: a dict, mapping resource names to the regions they are available in. +* `AWS_CFN_REGIONS_SCANNED`: a set of regions for which listing CloudFormation types succeeded. + +The script expects valid AWS credentials either via the environment or configured profiles (for local execution). + +Note: +We evaluated scraping the public documentation/zip schema endpoints, but they do not have resources like +``AWS::OpsWorksCM::Server`` that are still returned by the CloudFormation API. +To make sure we cover all resources, we rely on the list of available types from the API. +""" + +import argparse +import logging +import sys +from collections.abc import Iterable +from pathlib import Path + +import boto3 +from botocore.exceptions import ClientError + +LOG = logging.getLogger(__name__) + +REPO_ROOT = Path(__file__).resolve().parents[1] +DEFAULT_RESOURCE_FILE = ( + REPO_ROOT / "localstack-core" / "localstack" / "services" / "cloudformation" / "resources.py" +) + + +def get_regions( + session: boto3.session.Session, explicit_regions: Iterable[str] | None = None +) -> list[str]: + if explicit_regions: + return sorted(set(explicit_regions)) + + return sorted(session.get_available_regions("cloudformation")) + + +def collect_region_resource_types( + session: boto3.session.Session, region: str +) -> tuple[set[str], bool]: + client = session.client("cloudformation", region_name=region) + resources: set[str] = set() + token: str | None = None + succeeded = True + + while True: + try: + params = { + "Visibility": "PUBLIC", + "Type": "RESOURCE", + "Filters": {"Category": "AWS_TYPES"}, + } + if token: + params["NextToken"] = token + response = client.list_types(**params) + except ClientError as exc: + LOG.warning("Skipping region %s due to error while listing types: %s", region, exc) + succeeded = False + break + + for summary in response.get("TypeSummaries", []): + type_name = summary.get("TypeName") + if type_name: + resources.add(type_name) + + token = response.get("NextToken") + if not token: + break + + return resources, succeeded + + +def collect_all_resource_types( + session: boto3.session.Session, regions: Iterable[str] +) -> tuple[dict[str, set[str]], set[str]]: + aggregated: dict[str, set[str]] = {} + successful_regions: set[str] = set() + for region in regions: + print(f"Collecting CloudFormation resource types in region {region}") + region_resources, succeeded = collect_region_resource_types(session, region) + if not succeeded: + continue + + successful_regions.add(region) + for resource in region_resources: + aggregated.setdefault(resource, set()).add(region) + return aggregated, successful_regions + + +def render_resource_file(resources: dict[str, set[str]], successful_regions: set[str]) -> str: + lines: list[str] = [ + '"""Generated by scripts/update_cfn_resources.py – do not edit manually."""', + "", + ] + + if resources: + lines.append("AWS_AVAILABLE_CFN_RESOURCES = {") + for resource in sorted(resources): + regions = sorted(resources[resource]) + if regions: + lines.append(f' "{resource}": [') + for region_name in regions: + lines.append(f' "{region_name}",') + lines.append(" ],") + else: + lines.append(f' "{resource}": [],') + lines.append("}") + else: + lines.append("AWS_AVAILABLE_CFN_RESOURCES = {}") + + lines.append("") + + if successful_regions: + lines.append("AWS_CFN_REGIONS_SCANNED = {") + for region in sorted(successful_regions): + lines.append(f' "{region}",') + lines.append("}") + else: + lines.append("AWS_CFN_REGIONS_SCANNED = set()") + + lines.append("") + + return "\n".join(lines) + + +def write_resource_file(path: Path, content: str) -> None: + path.write_text(content, encoding="utf-8") + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Update the AWS_AVAILABLE_CFN_RESOURCES constant.") + parser.add_argument("--profile", help="AWS profile name to use") + parser.add_argument( + "--regions", + nargs="*", + help="Optional list of AWS regions to scan. Defaults to all regions supporting CloudFormation.", + ) + parser.add_argument( + "--resource-file", + default=str(DEFAULT_RESOURCE_FILE), + help="Path to the resources.py file that should be updated.", + ) + parser.add_argument( + "--dry-run", action="store_true", help="Do not write the file, only print the resources." + ) + return parser.parse_args() + + +def main() -> int: + args = parse_args() + + session_kwargs = {} + if args.profile: + session_kwargs["profile_name"] = args.profile + + try: + session = boto3.session.Session(**session_kwargs) + except Exception as exc: + LOG.error("Failed to create boto3 session: %s", exc) + return 1 + + regions = get_regions(session, args.regions) + if not regions: + LOG.error("Could not determine any regions to scan.") + return 1 + + resources, successful_regions = collect_all_resource_types(session, regions) + if not resources: + LOG.error("No CloudFormation resources were discovered.") + return 1 + + content = render_resource_file(resources, successful_regions) + + if args.dry_run: + sys.stdout.write(content) + return 0 + + resource_file = Path(args.resource_file) + write_resource_file(resource_file, content) + print( + f"Updated {resource_file} with {len(resources)} CloudFormation resource types " + f"across {len(successful_regions)} regions." + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/aws/cdk_templates/Bookstore/BookstoreStack.json b/tests/aws/cdk_templates/Bookstore/BookstoreStack.json index e1486e5fcd76c..058c14eba1271 100644 --- a/tests/aws/cdk_templates/Bookstore/BookstoreStack.json +++ b/tests/aws/cdk_templates/Bookstore/BookstoreStack.json @@ -202,7 +202,7 @@ "EncryptionAtRestOptions": { "Enabled": false }, - "EngineVersion": "OpenSearch_2.11", + "EngineVersion": "OpenSearch_3.1", "LogPublishingOptions": {}, "NodeToNodeEncryptionOptions": { "Enabled": false diff --git a/tests/aws/conftest.py b/tests/aws/conftest.py index 0f762b6155042..94a19b8a2dd65 100644 --- a/tests/aws/conftest.py +++ b/tests/aws/conftest.py @@ -13,7 +13,6 @@ TransformerUtility, ) from localstack.utils.aws.arns import get_partition -from tests.aws.test_terraform import TestTerraform def pytest_configure(config: Config): @@ -57,13 +56,6 @@ def pytest_runtestloop(session): test_init_functions.add(transcribe_install_async) - # add init functions for certain tests that download/install things - for test_class in test_classes: - # set flag that terraform will be used - if TestTerraform is test_class: - test_init_functions.add(TestTerraform.init_async) - continue - if not session.items: return diff --git a/tests/aws/scenario/bookstore/test_bookstore.py b/tests/aws/scenario/bookstore/test_bookstore.py index 149a7872a1fee..e707c91f509fa 100644 --- a/tests/aws/scenario/bookstore/test_bookstore.py +++ b/tests/aws/scenario/bookstore/test_bookstore.py @@ -287,7 +287,9 @@ def _verify_search(category: str, expected_amount: int): "$..ClusterConfig.DedicatedMasterType", # added in LS "$..ClusterConfig.Options.DedicatedMasterCount", # added in LS "$..ClusterConfig.Options.DedicatedMasterType", # added in LS + "$..DomainStatusList..AIMLOptions", # missing "$..DomainStatusList..EBSOptions.Iops", # added in LS + "$..DomainStatusList..IdentityCenterOptions", # missing "$..DomainStatusList..IPAddressType", # missing "$..DomainStatusList..DomainProcessingStatus", # missing "$..DomainStatusList..ModifyingProperties", # missing @@ -299,7 +301,9 @@ def _verify_search(category: str, expected_amount: int): "$..ClusterConfig.MultiAZWithStandbyEnabled", # missing "$..AdvancedSecurityOptions.AnonymousAuthEnabled", # missing "$..AdvancedSecurityOptions.Options.AnonymousAuthEnabled", # missing + "$..DomainConfig.AIMLOptions", # missing "$..DomainConfig.ClusterConfig.Options.WarmEnabled", # missing + "$..DomainConfig.IdentityCenterOptions", # missing "$..DomainConfig.IPAddressType", # missing "$..DomainConfig.ModifyingProperties", # missing "$..ClusterConfig.Options.ColdStorageOptions", # missing diff --git a/tests/aws/scenario/bookstore/test_bookstore.snapshot.json b/tests/aws/scenario/bookstore/test_bookstore.snapshot.json index 4fdc081fcaf06..e042de8f6645c 100644 --- a/tests/aws/scenario/bookstore/test_bookstore.snapshot.json +++ b/tests/aws/scenario/bookstore/test_bookstore.snapshot.json @@ -549,11 +549,20 @@ } }, "tests/aws/scenario/bookstore/test_bookstore.py::TestBookstoreApplication::test_opensearch_crud": { - "recorded-date": "15-07-2024, 12:55:29", + "recorded-date": "12-09-2025, 09:51:48", "recorded-content": { "describe_domains": { "DomainStatusList": [ { + "AIMLOptions": { + "NaturalLanguageQueryGenerationOptions": { + "CurrentState": "NOT_ENABLED", + "DesiredState": "DISABLED" + }, + "S3VectorsEngine": { + "Enabled": false + } + }, "ARN": "arn::es::111111111111:domain/", "AccessPolicies": "", "AdvancedOptions": { @@ -609,8 +618,9 @@ "Enabled": false }, "Endpoint": "", - "EngineVersion": "OpenSearch_2.11", + "EngineVersion": "OpenSearch_3.1", "IPAddressType": "ipv4", + "IdentityCenterOptions": {}, "ModifyingProperties": [], "NodeToNodeEncryptionOptions": { "Enabled": false @@ -628,7 +638,7 @@ "ServiceSoftwareOptions": { "AutomatedUpdateDate": "datetime", "Cancellable": false, - "CurrentVersion": "OpenSearch_2_11_R20240502-P2", + "CurrentVersion": "OpenSearch_3_1_R20250904-P1", "Description": "There is no software update available for this domain.", "NewVersion": "", "OptionalDeployment": true, @@ -663,6 +673,24 @@ }, "describe_domain_config": { "DomainConfig": { + "AIMLOptions": { + "Options": { + "NaturalLanguageQueryGenerationOptions": { + "CurrentState": "NOT_ENABLED", + "DesiredState": "DISABLED" + }, + "S3VectorsEngine": { + "Enabled": false + } + }, + "Status": { + "CreationDate": "datetime", + "PendingDeletion": false, + "State": "Active", + "UpdateDate": "datetime", + "UpdateVersion": "update-version" + } + }, "AccessPolicies": { "Options": "", "Status": { @@ -795,7 +823,7 @@ } }, "EngineVersion": { - "Options": "OpenSearch_2.11", + "Options": "OpenSearch_3.1", "Status": { "CreationDate": "datetime", "PendingDeletion": false, @@ -814,6 +842,16 @@ "UpdateVersion": "update-version" } }, + "IdentityCenterOptions": { + "Options": {}, + "Status": { + "CreationDate": "datetime", + "PendingDeletion": false, + "State": "Active", + "UpdateDate": "datetime", + "UpdateVersion": "update-version" + } + }, "LogPublishingOptions": { "Options": {}, "Status": { @@ -926,10 +964,8 @@ "get_compatible_versions": { "CompatibleVersions": [ { - "SourceVersion": "OpenSearch_2.11", - "TargetVersions": [ - "OpenSearch_2.13" - ] + "SourceVersion": "OpenSearch_3.1", + "TargetVersions": [] } ], "ResponseMetadata": { @@ -939,6 +975,10 @@ }, "list_versions": { "Versions": [ + "OpenSearch_3.1", + "OpenSearch_2.19", + "OpenSearch_2.17", + "OpenSearch_2.15", "OpenSearch_2.13", "OpenSearch_2.11", "OpenSearch_2.9", diff --git a/tests/aws/scenario/bookstore/test_bookstore.validation.json b/tests/aws/scenario/bookstore/test_bookstore.validation.json index 3b64f0fc25b2f..ddcc18749085b 100644 --- a/tests/aws/scenario/bookstore/test_bookstore.validation.json +++ b/tests/aws/scenario/bookstore/test_bookstore.validation.json @@ -3,7 +3,13 @@ "last_validated_date": "2024-07-15T12:54:17+00:00" }, "tests/aws/scenario/bookstore/test_bookstore.py::TestBookstoreApplication::test_opensearch_crud": { - "last_validated_date": "2024-07-15T12:55:29+00:00" + "last_validated_date": "2025-09-12T10:07:07+00:00", + "durations_in_seconds": { + "setup": 1157.65, + "call": 3.36, + "teardown": 919.26, + "total": 2080.27 + } }, "tests/aws/scenario/bookstore/test_bookstore.py::TestBookstoreApplication::test_search_books": { "last_validated_date": "2024-07-15T12:55:25+00:00" diff --git a/tests/aws/services/apigateway/test_apigateway_api.py b/tests/aws/services/apigateway/test_apigateway_api.py index c33614b2cb89c..3a7887df4080e 100644 --- a/tests/aws/services/apigateway/test_apigateway_api.py +++ b/tests/aws/services/apigateway/test_apigateway_api.py @@ -2884,7 +2884,18 @@ def invoke_method( apigw_test_invoke_response_formatter(response), ) - # assert resource and rest api doesn't exist + # assert method doesn't exist + with pytest.raises(ClientError) as ex: + aws_client.apigateway.test_invoke_method( + restApiId=rest_api_id, + resourceId=resource_id, + httpMethod="HEAD", + pathWithQueryString="/pets/123", + body=json.dumps({"foo": "bar"}), + ) + snapshot.match("resource-method-not-found", ex.value.response) + + # assert resource doesn't exist with pytest.raises(ClientError) as ex: aws_client.apigateway.test_invoke_method( restApiId=rest_api_id, @@ -2895,6 +2906,7 @@ def invoke_method( ) snapshot.match("resource-id-not-found", ex.value.response) + # assert API doesn't exist with pytest.raises(ClientError) as ex: aws_client.apigateway.test_invoke_method( restApiId=rest_api_id, @@ -2905,6 +2917,69 @@ def invoke_method( ) snapshot.match("rest-api-not-found", ex.value.response) + @markers.aws.validated + @markers.snapshot.skip_snapshot_verify( + # TODO: our way of handling logs for TestInvokeMethod is too naive to properly handle all + # type of exceptions (we'll need to build logs as we progress through the invocation) + paths=[ + "$..log.line07", + ] + ) + def test_failed_invoke_test_method( + self, create_rest_apigw, snapshot, aws_client, apigw_test_invoke_response_formatter + ): + rest_api_id, _, root_resource_id = create_rest_apigw(name="test failed invoke") + + aws_client.apigateway.put_method( + restApiId=rest_api_id, + resourceId=root_resource_id, + httpMethod="ANY", + authorizationType="NONE", + ) + + aws_client.apigateway.put_integration( + restApiId=rest_api_id, + resourceId=root_resource_id, + httpMethod="ANY", + type="HTTP_PROXY", + uri="https://${stageVariables.testHost}", + integrationHttpMethod="ANY", + ) + # we are going to not declare this stage variable on purpose to make the call fail + + def invoke_method( + api_id: str, + target_resource_id: str, + method: str, + path_with_query_string: str | None = None, + body: str = "", + ): + kwargs = {} + if path_with_query_string is not None: + kwargs["pathWithQueryString"] = path_with_query_string + res = aws_client.apigateway.test_invoke_method( + restApiId=api_id, + resourceId=target_resource_id, + httpMethod=method, + body=body, + **kwargs, + ) + assert res.get("status") == 500 + return res + + response = retry( + invoke_method, + retries=10, + sleep=5, + api_id=rest_api_id, + target_resource_id=root_resource_id, + method="GET", + ) + snapshot.match( + "test-invoke-failure", + apigw_test_invoke_response_formatter(response), + ) + class TestApigatewayIntegration: @markers.aws.validated diff --git a/tests/aws/services/apigateway/test_apigateway_api.snapshot.json b/tests/aws/services/apigateway/test_apigateway_api.snapshot.json index fab0502213d49..84b358853287f 100644 --- a/tests/aws/services/apigateway/test_apigateway_api.snapshot.json +++ b/tests/aws/services/apigateway/test_apigateway_api.snapshot.json @@ -2813,7 +2813,7 @@ } }, "tests/aws/services/apigateway/test_apigateway_api.py::TestApigatewayTestInvoke::test_invoke_test_method": { - "recorded-date": "19-08-2025, 17:20:41", + "recorded-date": "29-09-2025, 19:28:50", "recorded-content": { "test-invoke-method-get-pets": { "body": { @@ -3046,6 +3046,17 @@ "HTTPStatusCode": 200 } }, + "resource-method-not-found": { + "Error": { + "Code": "NotFoundException", + "Message": "Invalid Method identifier specified" + }, + "message": "Invalid Method identifier specified", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 404 + } + }, "resource-id-not-found": { "Error": { "Code": "NotFoundException", @@ -4861,5 +4872,41 @@ } } } + }, + "tests/aws/services/apigateway/test_apigateway_api.py::TestApigatewayTestInvoke::test_failed_invoke_test_method": { + "recorded-date": "29-09-2025, 19:44:14", + "recorded-content": { + "test-invoke-failure": { + "body": { + "message": "Internal server error" + }, + "headers": { + "x-amzn-ErrorType": "InternalServerErrorException" + }, + "latency": "", + "log": { + "line00": "Execution log for request ", + "line01": "DDD MMM dd hh:mm:ss UTC yyyy : Starting execution for request: ", + "line02": "DDD MMM dd hh:mm:ss UTC yyyy : HTTP Method: GET, Resource Path: /", + "line03": "DDD MMM dd hh:mm:ss UTC yyyy : Method request path: {}", + "line04": "DDD MMM dd hh:mm:ss UTC yyyy : Method request query string: {}", + "line05": "DDD MMM dd hh:mm:ss UTC yyyy : Method request headers: {}", + "line06": "DDD MMM dd hh:mm:ss UTC yyyy : Method request body before transformations: ", + "line07": "DDD MMM dd hh:mm:ss UTC yyyy : Execution failed due to configuration error: Invalid endpoint address", + "line08": "DDD MMM dd hh:mm:ss UTC yyyy : Method completed with status: 500", + "line09": "" + }, + "multiValueHeaders": { + "x-amzn-ErrorType": [ + "InternalServerErrorException" + ] + }, + "status": 500, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } } } diff --git a/tests/aws/services/apigateway/test_apigateway_api.validation.json b/tests/aws/services/apigateway/test_apigateway_api.validation.json index 05f58ffb55c21..a21557799c53f 100644 --- a/tests/aws/services/apigateway/test_apigateway_api.validation.json +++ b/tests/aws/services/apigateway/test_apigateway_api.validation.json @@ -389,13 +389,22 @@ "total": 3.98 } }, + "tests/aws/services/apigateway/test_apigateway_api.py::TestApigatewayTestInvoke::test_failed_invoke_test_method": { + "last_validated_date": "2025-09-29T19:44:14+00:00", + "durations_in_seconds": { + "setup": 0.81, + "call": 1.14, + "teardown": 0.67, + "total": 2.62 + } + }, "tests/aws/services/apigateway/test_apigateway_api.py::TestApigatewayTestInvoke::test_invoke_test_method": { - "last_validated_date": "2025-08-19T17:20:41+00:00", + "last_validated_date": "2025-09-29T19:28:50+00:00", "durations_in_seconds": { - "setup": 0.83, - "call": 4.12, + "setup": 0.86, + "call": 4.31, "teardown": 0.67, - "total": 5.62 + "total": 5.84 } } } diff --git a/tests/aws/services/apigateway/test_apigateway_import.py b/tests/aws/services/apigateway/test_apigateway_import.py index 6ed23dcc3c1ba..3b3a9f83d6c12 100644 --- a/tests/aws/services/apigateway/test_apigateway_import.py +++ b/tests/aws/services/apigateway/test_apigateway_import.py @@ -802,15 +802,14 @@ def call_api(): url_error = api_invoke_url(api_id=rest_api_id, stage="v2", path="/path1") - def call_api_error(): + def call_api_error() -> requests.Response: res = requests.get(url_error) assert res.status_code == 500 - return res.json() + return res resp = retry(call_api_error, retries=5, sleep=2) - # we remove the headers from the response, not really needed for this test - resp.pop("headers", None) - snapshot.match("get-error-resp-from-http", resp) + error = {"body": resp.json(), "errorType": resp.headers.get("x-amzn-ErrorType")} + snapshot.match("get-error-resp-from-http", error) @markers.aws.validated @markers.snapshot.skip_snapshot_verify( diff --git a/tests/aws/services/apigateway/test_apigateway_import.snapshot.json b/tests/aws/services/apigateway/test_apigateway_import.snapshot.json index b1b91697846da..4fe5b2643b022 100644 --- a/tests/aws/services/apigateway/test_apigateway_import.snapshot.json +++ b/tests/aws/services/apigateway/test_apigateway_import.snapshot.json @@ -4823,7 +4823,7 @@ } }, "tests/aws/services/apigateway/test_apigateway_import.py::TestApiGatewayImportRestApi::test_import_with_stage_variables": { - "recorded-date": "02-07-2025, 16:31:50", + "recorded-date": "29-09-2025, 20:07:21", "recorded-content": { "get-resp-from-http": { "args": { @@ -4836,7 +4836,10 @@ "path": "/test-path" }, "get-error-resp-from-http": { - "message": "Internal server error" + "body": { + "message": "Internal server error" + }, + "errorType": "InternalServerErrorException" } } }, diff --git a/tests/aws/services/apigateway/test_apigateway_import.validation.json b/tests/aws/services/apigateway/test_apigateway_import.validation.json index 5f21b963b11b7..15d0d347311eb 100644 --- a/tests/aws/services/apigateway/test_apigateway_import.validation.json +++ b/tests/aws/services/apigateway/test_apigateway_import.validation.json @@ -144,12 +144,12 @@ } }, "tests/aws/services/apigateway/test_apigateway_import.py::TestApiGatewayImportRestApi::test_import_with_stage_variables": { - "last_validated_date": "2025-07-02T16:31:50+00:00", + "last_validated_date": "2025-09-29T20:07:21+00:00", "durations_in_seconds": { - "setup": 0.11, - "call": 5.83, - "teardown": 69.73, - "total": 75.67 + "setup": 11.64, + "call": 11.14, + "teardown": 2.26, + "total": 25.04 } }, "tests/aws/services/apigateway/test_apigateway_import.py::TestApiGatewayImportRestApi::test_put_rest_api_mode_binary_media_types[merge]": { diff --git a/tests/aws/services/cloudformation/api/test_reference_resolving.py b/tests/aws/services/cloudformation/api/test_reference_resolving.py index a5a8fbf10b129..efcb8deb8f375 100644 --- a/tests/aws/services/cloudformation/api/test_reference_resolving.py +++ b/tests/aws/services/cloudformation/api/test_reference_resolving.py @@ -1,9 +1,11 @@ import os import pytest +from tests.aws.services.cloudformation.conftest import skip_if_legacy_engine from localstack.services.cloudformation.engine.template_deployer import MOCK_REFERENCE from localstack.testing.pytest import markers +from localstack.utils import testutil from localstack.utils.strings import short_uid @@ -103,3 +105,110 @@ def test_reference_unsupported_resource(deploy_cfn_template, aws_client): value_of_unsupported = deployment.outputs["parameter"] assert ref_of_unsupported == MOCK_REFERENCE assert value_of_unsupported == f"The value of the attribute is: {MOCK_REFERENCE}" + + +@markers.aws.validated +@skip_if_legacy_engine() +def test_redeploy_cdk_with_reference( + aws_client, account_id, create_lambda_function, deploy_cfn_template, snapshot, cleanups +): + """ + Test a user scenario with a lambda function that fails to redeploy + + """ + # perform cdk bootstrap + template_file = os.path.join( + os.path.dirname(__file__), "../../../templates/cdk_bootstrap_v28.yaml" + ) + qualifier = short_uid() + bootstrap_stack = deploy_cfn_template( + template_path=template_file, + parameters={ + "CloudFormationExecutionPolicies": "", + "FileAssetsBucketKmsKeyId": "AWS_MANAGED_KEY", + "PublicAccessBlockConfiguration": "true", + "TrustedAccounts": "", + "TrustedAccountsForLookup": "", + "Qualifier": qualifier, + }, + ) + + lambda_bucket = bootstrap_stack.outputs["BucketName"] + + # upload the lambda function + lambda_src_1 = """ + def handler(event, context): + return {"status": "ok"} + """ + lambda_src_2 = """ + def handler(event, context): + return {"status": "foo"} + """ + + function_name = f"function-{short_uid()}" + cleanups.append(lambda: aws_client.lambda_.delete_function(FunctionName=function_name)) + + def deploy_or_update_lambda(content: str, lambda_key: str): + archive = testutil.create_lambda_archive(content) + with open(archive, "rb") as infile: + aws_client.s3.put_object(Bucket=lambda_bucket, Key=lambda_key, Body=infile) + + lambda_exists = False + try: + aws_client.lambda_.get_function(FunctionName=function_name) + lambda_exists = True + except Exception: + # TODO: work out the proper exception + pass + + if lambda_exists: + aws_client.lambda_.update_function_code( + FunctionName=function_name, + S3Bucket=lambda_bucket, + S3Key=lambda_key, + ) + else: + aws_client.lambda_.create_function( + FunctionName=function_name, + Runtime="python3.12", + Handler="handler", + Code={ + "S3Bucket": lambda_bucket, + "S3Key": lambda_key, + }, + # The role does not matter + Role=f"arn:aws:iam::{account_id}:role/LambdaExecutionRole", + ) + aws_client.lambda_.get_waiter("function_active_v2").wait(FunctionName=function_name) + + lambda_key_1 = f"{short_uid()}.zip" + deploy_or_update_lambda(lambda_src_1, lambda_key_1) + + # deploy the template the first time + stack = deploy_cfn_template( + template_path=os.path.join( + os.path.dirname(__file__), "../../../templates/cdk-lambda-redeploy.json" + ), + parameters={ + "DeployBucket": lambda_bucket, + "DeployKey": lambda_key_1, + "BootstrapVersion": f"/cdk-bootstrap/{qualifier}/version", + }, + ) + + lambda_key_2 = f"{short_uid()}.zip" + deploy_or_update_lambda(lambda_src_2, lambda_key_2) + + # deploy the template the second time + deploy_cfn_template( + stack_name=stack.stack_id, + template_path=os.path.join( + os.path.dirname(__file__), "../../../templates/cdk-lambda-redeploy.json" + ), + is_update=True, + parameters={ + "DeployBucket": lambda_bucket, + "DeployKey": lambda_key_2, + "BootstrapVersion": "28", + }, + ) diff --git a/tests/aws/services/cloudformation/api/test_reference_resolving.snapshot.json b/tests/aws/services/cloudformation/api/test_reference_resolving.snapshot.json index 2aebe631514be..488fba1a50d6c 100644 --- a/tests/aws/services/cloudformation/api/test_reference_resolving.snapshot.json +++ b/tests/aws/services/cloudformation/api/test_reference_resolving.snapshot.json @@ -32,5 +32,9 @@ "MyTopicSubWithMap": "|arn::sns::111111111111:|something" } } + }, + "tests/aws/services/cloudformation/api/test_reference_resolving.py::test_redeploy_cdk_with_reference": { + "recorded-date": "24-09-2025, 19:40:59", + "recorded-content": {} } } diff --git a/tests/aws/services/cloudformation/api/test_reference_resolving.validation.json b/tests/aws/services/cloudformation/api/test_reference_resolving.validation.json index 5422a01c739f0..ec17cf50bd2bd 100644 --- a/tests/aws/services/cloudformation/api/test_reference_resolving.validation.json +++ b/tests/aws/services/cloudformation/api/test_reference_resolving.validation.json @@ -5,6 +5,15 @@ "tests/aws/services/cloudformation/api/test_reference_resolving.py::test_nested_getatt_ref[TopicName]": { "last_validated_date": "2023-05-11T11:43:51+00:00" }, + "tests/aws/services/cloudformation/api/test_reference_resolving.py::test_redeploy_cdk_with_reference": { + "last_validated_date": "2025-09-24T19:41:46+00:00", + "durations_in_seconds": { + "setup": 12.48, + "call": 102.32, + "teardown": 47.28, + "total": 162.08 + } + }, "tests/aws/services/cloudformation/api/test_reference_resolving.py::test_sub_resolving": { "last_validated_date": "2023-05-12T05:51:06+00:00" } diff --git a/tests/aws/services/cloudformation/engine/test_references.py b/tests/aws/services/cloudformation/engine/test_references.py index 7eaf0db5254f4..05643055fdddb 100644 --- a/tests/aws/services/cloudformation/engine/test_references.py +++ b/tests/aws/services/cloudformation/engine/test_references.py @@ -3,6 +3,7 @@ import pytest from botocore.exceptions import ClientError +from tests.aws.services.cloudformation.conftest import skip_if_legacy_engine from localstack.testing.aws.util import is_aws_cloud from localstack.testing.pytest import markers @@ -147,3 +148,20 @@ def test_aws_novalue(deploy_cfn_template, parameter_value): assert outputs["BucketName"] != fallback_bucket_name case other: pytest.fail(f"Test setup error, unexpected parameter value: {other}") + + +@markers.aws.validated +@skip_if_legacy_engine() +class TestPseudoParameters: + def test_stack_id(self, deploy_cfn_template, snapshot): + template_path = os.path.join( + os.path.dirname(__file__), + "../../../templates/stack-id-validation.yaml", + ) + stack = deploy_cfn_template(template_path=template_path) + + random_component = stack.stack_id.split("-")[-1] + snapshot.add_transformer(snapshot.transform.regex(random_component, "")) + snapshot.add_transformer(snapshot.transform.regex(stack.stack_name, "")) + + snapshot.match("parameter-value", stack.outputs["ParameterValue"]) diff --git a/tests/aws/services/cloudformation/engine/test_references.snapshot.json b/tests/aws/services/cloudformation/engine/test_references.snapshot.json index 9e6600aaaa00f..d92850a19536d 100644 --- a/tests/aws/services/cloudformation/engine/test_references.snapshot.json +++ b/tests/aws/services/cloudformation/engine/test_references.snapshot.json @@ -80,5 +80,12 @@ "Version": 1 } } + }, + "tests/aws/services/cloudformation/engine/test_references.py::TestPseudoParameters::test_stack_id": { + "recorded-date": "24-09-2025, 09:53:55", + "recorded-content": { + "parameter-value": "-s3logs-", + "stack-id": "arn::cloudformation::111111111111:stack//5dd694c0-992c-11f0-9d8f-" + } } } diff --git a/tests/aws/services/cloudformation/engine/test_references.validation.json b/tests/aws/services/cloudformation/engine/test_references.validation.json index a244e45674c07..460521c7a4ed6 100644 --- a/tests/aws/services/cloudformation/engine/test_references.validation.json +++ b/tests/aws/services/cloudformation/engine/test_references.validation.json @@ -8,6 +8,15 @@ "tests/aws/services/cloudformation/engine/test_references.py::TestFnSub::test_non_string_parameter_in_sub": { "last_validated_date": "2024-10-17T22:49:56+00:00" }, + "tests/aws/services/cloudformation/engine/test_references.py::TestPseudoParameters::test_stack_id": { + "last_validated_date": "2025-09-24T09:54:00+00:00", + "durations_in_seconds": { + "setup": 1.3, + "call": 8.18, + "teardown": 4.36, + "total": 13.84 + } + }, "tests/aws/services/cloudformation/engine/test_references.py::test_aws_novalue[no]": { "last_validated_date": "2025-08-12T21:47:08+00:00", "durations_in_seconds": { diff --git a/tests/aws/services/cloudformation/test_change_sets.py b/tests/aws/services/cloudformation/test_change_sets.py index 4d00fab89249b..81df75f995049 100644 --- a/tests/aws/services/cloudformation/test_change_sets.py +++ b/tests/aws/services/cloudformation/test_change_sets.py @@ -979,3 +979,59 @@ def test_describe_failed_change_set(aws_client: ServiceLevelClientFactory, snaps describe = aws_client.cloudformation.describe_change_set(ChangeSetName=res["Id"]) snapshot.match("describe", describe) + + +@skip_if_legacy_engine() +@markers.aws.validated +def test_list_change_sets(deploy_cfn_template, aws_client, snapshot): + snapshot.add_transformer(snapshot.transform.cloudformation_api()) + template = { + "Resources": { + "MyParameter": { + "Type": "AWS::SSM::Parameter", + "Properties": { + "Type": "String", + "Value": short_uid(), + }, + }, + }, + } + + # first create an executed change set + stack = deploy_cfn_template(template=json.dumps(template)) + stack_id = stack.stack_id + + # now create a non-executed change set + template2 = copy.deepcopy(template) + template2["Resources"]["MyParameter"]["Properties"]["Value"] = short_uid() + + non_executed_change_set_name = f"cs-{short_uid()}" + non_executed_change_set_id = aws_client.cloudformation.create_change_set( + ChangeSetName=non_executed_change_set_name, + StackName=stack.stack_id, + ChangeSetType="UPDATE", + TemplateBody=json.dumps(template2), + )["Id"] + aws_client.cloudformation.get_waiter("change_set_create_complete").wait( + ChangeSetName=non_executed_change_set_id + ) + + # now create and delete a change set + template3 = copy.deepcopy(template) + template3["Resources"]["MyParameter"]["Properties"]["Value"] = short_uid() + + deleted_change_set_name = f"cs-{short_uid()}" + deleted_change_set_id = aws_client.cloudformation.create_change_set( + ChangeSetName=deleted_change_set_name, + StackName=stack.stack_id, + ChangeSetType="UPDATE", + TemplateBody=json.dumps(template3), + )["Id"] + aws_client.cloudformation.get_waiter("change_set_create_complete").wait( + ChangeSetName=deleted_change_set_id + ) + + aws_client.cloudformation.delete_change_set(ChangeSetName=deleted_change_set_id) + + change_sets = aws_client.cloudformation.list_change_sets(StackName=stack_id) + snapshot.match("change-sets", change_sets) diff --git a/tests/aws/services/cloudformation/test_change_sets.snapshot.json b/tests/aws/services/cloudformation/test_change_sets.snapshot.json index 4b687d6aa2473..308f620d46395 100644 --- a/tests/aws/services/cloudformation/test_change_sets.snapshot.json +++ b/tests/aws/services/cloudformation/test_change_sets.snapshot.json @@ -5927,5 +5927,28 @@ } } } + }, + "tests/aws/services/cloudformation/test_change_sets.py::test_list_change_sets": { + "recorded-date": "16-09-2025, 14:49:32", + "recorded-content": { + "change-sets": { + "Summaries": [ + { + "ChangeSetId": "arn::cloudformation::111111111111:changeSet/", + "ChangeSetName": "", + "CreationTime": "datetime", + "ExecutionStatus": "AVAILABLE", + "IncludeNestedStacks": false, + "StackId": "arn::cloudformation::111111111111:stack//", + "StackName": "", + "Status": "CREATE_COMPLETE" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } } } diff --git a/tests/aws/services/cloudformation/test_change_sets.validation.json b/tests/aws/services/cloudformation/test_change_sets.validation.json index eee32ce776852..5f4ecab525235 100644 --- a/tests/aws/services/cloudformation/test_change_sets.validation.json +++ b/tests/aws/services/cloudformation/test_change_sets.validation.json @@ -125,6 +125,15 @@ "total": 23.45 } }, + "tests/aws/services/cloudformation/test_change_sets.py::test_list_change_sets": { + "last_validated_date": "2025-09-16T14:49:36+00:00", + "durations_in_seconds": { + "setup": 0.95, + "call": 15.36, + "teardown": 4.4, + "total": 20.71 + } + }, "tests/aws/services/cloudformation/test_change_sets.py::test_single_resource_static_update": { "last_validated_date": "2025-03-18T16:52:35+00:00" }, diff --git a/tests/aws/services/cloudformation/test_template_engine.py b/tests/aws/services/cloudformation/test_template_engine.py index cb2fe8413c9c1..52e4876cea994 100644 --- a/tests/aws/services/cloudformation/test_template_engine.py +++ b/tests/aws/services/cloudformation/test_template_engine.py @@ -349,20 +349,21 @@ def test_create_stack_with_ssm_parameters( ) @markers.aws.validated - def test_resolve_ssm(self, create_parameter, deploy_cfn_template): + @skip_if_legacy_engine() + def test_resolve_ssm(self, create_parameter, deploy_cfn_template, snapshot): parameter_key = f"param-key-{short_uid()}" parameter_value = f"param-value-{short_uid()}" + snapshot.add_transformer(snapshot.transform.regex(parameter_value, "")) create_parameter(Name=parameter_key, Value=parameter_value, Type="String") result = deploy_cfn_template( - parameters={"DynamicParameter": parameter_key}, + parameters={"DynamicParameter": parameter_key, "ParameterName": parameter_key}, template_path=os.path.join( os.path.dirname(__file__), "../../templates/resolve_ssm.yaml" ), ) - topic_name = result.outputs["TopicName"] - assert topic_name == parameter_value + snapshot.match("results", result.outputs) @markers.aws.validated def test_resolve_ssm_with_version(self, create_parameter, deploy_cfn_template, aws_client): @@ -380,8 +381,12 @@ def test_resolve_ssm_with_version(self, create_parameter, deploy_cfn_template, a Name=parameter_key, Overwrite=True, Type="String", Value=parameter_value_v2 ) + versioned_parameter_reference = f"{parameter_key}:{v1['Version']}" result = deploy_cfn_template( - parameters={"DynamicParameter": f"{parameter_key}:{v1['Version']}"}, + parameters={ + "DynamicParameter": versioned_parameter_reference, + "ParameterName": versioned_parameter_reference, + }, template_path=os.path.join( os.path.dirname(__file__), "../../templates/resolve_ssm.yaml" ), @@ -515,7 +520,7 @@ def test_resolve_secretsmanager(self, create_secret, deploy_cfn_template, templa create_secret(Name=parameter_key, SecretString=parameter_value) result = deploy_cfn_template( - parameters={"DynamicParameter": f"{parameter_key}"}, + parameters={"DynamicParameter": parameter_key}, template_path=os.path.join( os.path.dirname(__file__), "../../templates", @@ -526,6 +531,23 @@ def test_resolve_secretsmanager(self, create_secret, deploy_cfn_template, templa topic_name = result.outputs["TopicName"] assert topic_name == parameter_value + @markers.aws.validated + def test_resolve_secretsmanager_with_backslashes(self, create_secret, deploy_cfn_template): + parameter_key = f"param-key-{short_uid()}" + secret_value = json.dumps({"password": r"p\\30\asw\\\ord"}) + + create_secret(Name=parameter_key, SecretString=secret_value) + + result = deploy_cfn_template( + parameters={"DynamicParameter": parameter_key}, + template_path=os.path.join( + os.path.dirname(__file__), + "../../templates/resolve_secretsmanager_with_backslashes.yaml", + ), + ) + + assert secret_value == result.outputs["ParameterValue"] + class TestPreviousValues: @pytest.mark.skip(reason="outputs don't behave well in combination with conditions") diff --git a/tests/aws/services/cloudformation/test_template_engine.snapshot.json b/tests/aws/services/cloudformation/test_template_engine.snapshot.json index 9113f0143cfc8..bc433c691614c 100644 --- a/tests/aws/services/cloudformation/test_template_engine.snapshot.json +++ b/tests/aws/services/cloudformation/test_template_engine.snapshot.json @@ -691,5 +691,14 @@ "recorded-content": { "error-response": "An error occurred (ValidationError) when calling the CreateChangeSet operation: Parameter InputValue should either have input value or default value" } + }, + "tests/aws/services/cloudformation/test_template_engine.py::TestSsmParameters::test_resolve_ssm": { + "recorded-date": "24-09-2025, 22:06:32", + "recorded-content": { + "results": { + "ParameterValue": "abc:", + "TopicName": "" + } + } } } diff --git a/tests/aws/services/cloudformation/test_template_engine.validation.json b/tests/aws/services/cloudformation/test_template_engine.validation.json index dac358fb03f57..79bece0983398 100644 --- a/tests/aws/services/cloudformation/test_template_engine.validation.json +++ b/tests/aws/services/cloudformation/test_template_engine.validation.json @@ -98,6 +98,15 @@ "tests/aws/services/cloudformation/test_template_engine.py::TestPseudoParameters::test_stack_id": { "last_validated_date": "2024-07-18T08:56:47+00:00" }, + "tests/aws/services/cloudformation/test_template_engine.py::TestSecretsManagerParameters::test_resolve_secretsmanager_with_backslashes": { + "last_validated_date": "2025-09-30T15:36:21+00:00", + "durations_in_seconds": { + "setup": 0.01, + "call": 11.22, + "teardown": 4.49, + "total": 15.72 + } + }, "tests/aws/services/cloudformation/test_template_engine.py::TestSsmParameters::test_create_change_set_with_ssm_parameter_list": { "last_validated_date": "2024-08-08T21:21:23+00:00" }, @@ -110,6 +119,15 @@ "total": 61.74 } }, + "tests/aws/services/cloudformation/test_template_engine.py::TestSsmParameters::test_resolve_ssm": { + "last_validated_date": "2025-09-24T22:07:22+00:00", + "durations_in_seconds": { + "setup": 0.95, + "call": 10.67, + "teardown": 50.0, + "total": 61.62 + } + }, "tests/aws/services/cloudformation/test_template_engine.py::TestSsmParameters::test_resolve_ssm_missing_parameter": { "last_validated_date": "2025-08-06T09:34:07+00:00", "durations_in_seconds": { diff --git a/tests/aws/services/cloudwatch/conftest.py b/tests/aws/services/cloudwatch/conftest.py new file mode 100644 index 0000000000000..e685952dd22df --- /dev/null +++ b/tests/aws/services/cloudwatch/conftest.py @@ -0,0 +1,52 @@ +import os +from typing import TYPE_CHECKING + +import pytest +from botocore.parsers import create_parser +from botocore.serialize import create_serializer + +from localstack.aws.spec import load_service +from localstack.testing.aws.util import is_aws_cloud + +if TYPE_CHECKING: + from mypy_boto3_cloudwatch import CloudWatchClient + + +def is_old_provider(): + return os.environ.get("PROVIDER_OVERRIDE_CLOUDWATCH") == "v1" and not is_aws_cloud() + + +@pytest.fixture(params=["query", "json", "smithy-rpc-v2-cbor"]) +def aws_cloudwatch_client(aws_client, monkeypatch, request) -> "CloudWatchClient": + protocol = request.param + if is_old_provider() and protocol in ("json", "smithy-rpc-v2-cbor"): + pytest.skip(f"Protocol '{protocol}' not supported in Moto") + """ + Currently, there are no way to select which protocol to use when creating a Boto3 client for a service that supports + multiple protocols, like CloudWatch. + To avoid mutating clients by patching the client initialization logic, we can hardcode the parser and serializer + used by the client instead. + """ + # TODO: remove once Botocore countains the new CloudWatch spec + # for now, we need to also patch the botocore client to be sure it contains the updated service model via the + # json patch + service_model = load_service("cloudwatch") + + # instantiate a client via our ExternalAwsClientFactory exposed via `aws_client` fixture + cloudwatch_client_wrapper = aws_client.cloudwatch + # this instance above is the `MetadataRequestInjector`, which wraps the actual client + cloudwatch_client = cloudwatch_client_wrapper._client + + # the default client behavior is to include validation + protocol_serializer = create_serializer(protocol) + protocol_parser = create_parser(protocol) + + monkeypatch.setattr(cloudwatch_client.meta, "_service_model", service_model) + monkeypatch.setattr(cloudwatch_client, "_serializer", protocol_serializer) + monkeypatch.setattr(cloudwatch_client, "_response_parser", protocol_parser) + monkeypatch.setattr(cloudwatch_client.meta.service_model, "resolved_protocol", protocol) + + # this is useful to know from the test itself which protocol is currently used + monkeypatch.setattr(cloudwatch_client, "test_client_protocol", protocol, raising=False) + + yield cloudwatch_client diff --git a/tests/aws/services/cloudwatch/test_cloudwatch.py b/tests/aws/services/cloudwatch/test_cloudwatch.py index 5bb37ac24e57c..b95c6b6742baa 100644 --- a/tests/aws/services/cloudwatch/test_cloudwatch.py +++ b/tests/aws/services/cloudwatch/test_cloudwatch.py @@ -1,8 +1,6 @@ -import copy import gzip import json import logging -import os import threading import time from datetime import UTC, datetime, timedelta, timezone @@ -24,28 +22,28 @@ from localstack.utils.common import retry, short_uid, to_str from localstack.utils.sync import poll_condition, wait_until +from .conftest import is_old_provider +from .utils import get_cloudwatch_client + if TYPE_CHECKING: from mypy_boto3_logs import CloudWatchLogsClient + PUBLICATION_RETRIES = 5 LOG = logging.getLogger(__name__) -def is_old_provider(): - return os.environ.get("PROVIDER_OVERRIDE_CLOUDWATCH") == "v1" and not is_aws_cloud() - - class TestCloudwatch: @markers.aws.validated - def test_put_metric_data_values_list(self, snapshot, aws_client): + def test_put_metric_data_values_list(self, snapshot, aws_cloudwatch_client): metric_name = "test-metric" namespace = f"ns-{short_uid()}" - utc_now = datetime.utcnow().replace(tzinfo=UTC) + utc_now = datetime.now(tz=UTC) snapshot.add_transformer( snapshot.transform.key_value("Timestamp", reference_replacement=False) ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -58,9 +56,11 @@ def test_put_metric_data_values_list(self, snapshot, aws_client): ], ) + stats = {} + def get_stats() -> int: - global stats - stats = aws_client.cloudwatch.get_metric_statistics( + nonlocal stats + stats = aws_cloudwatch_client.get_metric_statistics( Namespace=namespace, MetricName=metric_name, StartTime=utc_now - timedelta(seconds=60), @@ -75,7 +75,7 @@ def get_stats() -> int: snapshot.match("get_metric_statistics", stats) @markers.aws.only_localstack - def test_put_metric_data_gzip(self, aws_client, region_name): + def test_put_metric_data_gzip_with_query_protocol(self, aws_client, region_name): metric_name = "test-metric" namespace = "namespace" data = ( @@ -115,13 +115,13 @@ def test_put_metric_data_gzip(self, aws_client, region_name): @markers.aws.validated @pytest.mark.skipif(is_old_provider(), reason="not supported by the old provider") - def test_put_metric_data_validation(self, aws_client): + def test_put_metric_data_validation(self, aws_cloudwatch_client, snapshot): namespace = f"ns-{short_uid()}" - utc_now = datetime.utcnow().replace(tzinfo=UTC) + utc_now = datetime.now(tz=UTC) # test invalid due to having both Values and Value - with pytest.raises(Exception) as ex: - aws_client.cloudwatch.put_metric_data( + with pytest.raises(ClientError) as ex: + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -133,16 +133,11 @@ def test_put_metric_data_validation(self, aws_client): } ], ) - err = ex.value.response["Error"] - assert err["Code"] == "InvalidParameterCombination" - assert ( - err["Message"] - == "The parameters MetricData.member.1.Value and MetricData.member.1.Values are mutually exclusive and you have specified both." - ) + snapshot.match("invalid-param-combination", ex.value.response) # test invalid due to data can not have and values mismatched_counts - with pytest.raises(Exception) as ex: - aws_client.cloudwatch.put_metric_data( + with pytest.raises(ClientError) as ex: + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -154,16 +149,11 @@ def test_put_metric_data_validation(self, aws_client): } ], ) - err = ex.value.response["Error"] - assert err["Code"] == "InvalidParameterValue" - assert ( - err["Message"] - == "The parameters MetricData.member.1.Values and MetricData.member.1.Counts must be of the same size." - ) + snapshot.match("invalid-param-value", ex.value.response) # test invalid due to inserting both value and statistic values - with pytest.raises(Exception) as ex: - aws_client.cloudwatch.put_metric_data( + with pytest.raises(ClientError) as ex: + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -180,15 +170,10 @@ def test_put_metric_data_validation(self, aws_client): } ], ) - err = ex.value.response["Error"] - assert err["Code"] == "InvalidParameterCombination" - assert ( - err["Message"] - == "The parameters MetricData.member.1.Value and MetricData.member.1.StatisticValues are mutually exclusive and you have specified both." - ) + snapshot.match("invalid-param-combination-2", ex.value.response) # For some strange reason the AWS implementation allows this - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -207,27 +192,27 @@ def test_put_metric_data_validation(self, aws_client): ) @markers.aws.validated - def test_get_metric_data(self, aws_client): + def test_get_metric_data(self, aws_cloudwatch_client): namespace1 = f"test/{short_uid()}" namespace2 = f"test/{short_uid()}" - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace1, MetricData=[{"MetricName": "someMetric", "Value": 23}] ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace1, MetricData=[{"MetricName": "someMetric", "Value": 18}] ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace2, MetricData=[{"MetricName": "ug", "Value": 23}] ) - now = datetime.utcnow().replace(microsecond=0) + now = datetime.now(tz=UTC).replace(microsecond=0) start_time = now - timedelta(minutes=10) end_time = now + timedelta(minutes=5) def _get_metric_data_sum(): # filtering metric data with current time interval - response = aws_client.cloudwatch.get_metric_data( + response = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "some", @@ -254,21 +239,21 @@ def _get_metric_data_sum(): ) assert 2 == len(response["MetricDataResults"]) - for data_metric in response["MetricDataResults"]: + for _data_metric in response["MetricDataResults"]: # TODO: there's an issue in the implementation of the service here. # The returned timestamps should have the seconds set to 0 - if data_metric["Id"] == "some": + if _data_metric["Id"] == "some": assert 41.0 == sum( - data_metric["Values"] + _data_metric["Values"] ) # might fall under different 60s "buckets" - if data_metric["Id"] == "part": - assert 23.0 == sum(data_metric["Values"]) + if _data_metric["Id"] == "part": + assert 23.0 == sum(_data_metric["Values"]) # need to retry because the might most likely not be ingested immediately (it's fairly quick though) retry(_get_metric_data_sum, retries=10, sleep_before=2) # filtering metric data with current time interval - response = aws_client.cloudwatch.get_metric_data( + get_metric_data = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "some", @@ -290,23 +275,23 @@ def _get_metric_data_sum(): }, }, ], - StartTime=datetime.utcnow() + timedelta(hours=1), - EndTime=datetime.utcnow() + timedelta(hours=2), + StartTime=datetime.now(tz=UTC) + timedelta(hours=1), + EndTime=datetime.now(tz=UTC) + timedelta(hours=2), ) - for data_metric in response["MetricDataResults"]: + for data_metric in get_metric_data["MetricDataResults"]: if data_metric["Id"] == "some": assert len(data_metric["Values"]) == 0 if data_metric["Id"] == "part": assert len(data_metric["Values"]) == 0 @markers.aws.validated - def test_get_metric_data_for_multiple_metrics(self, aws_client, snapshot): + def test_get_metric_data_for_multiple_metrics(self, aws_cloudwatch_client, snapshot): snapshot.add_transformer(snapshot.transform.cloudwatch_api()) utc_now = datetime.now(tz=UTC) namespace = f"test/{short_uid()}" - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -317,7 +302,7 @@ def test_get_metric_data_for_multiple_metrics(self, aws_client, snapshot): } ], ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -329,7 +314,7 @@ def test_get_metric_data_for_multiple_metrics(self, aws_client, snapshot): ], ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -347,7 +332,7 @@ def test_get_metric_data_for_multiple_metrics(self, aws_client, snapshot): ) def assert_results(): - response = aws_client.cloudwatch.get_metric_data( + response = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "result1", @@ -378,7 +363,7 @@ def assert_results(): EndTime=utc_now + timedelta(seconds=60), ) - assert len(response["MetricDataResults"][0]["Values"]) > 0 + assert len(response["MetricDataResults"][2]["Values"]) > 0 snapshot.match("get_metric_data", response) retry(assert_results, retries=10, sleep_before=1) @@ -388,11 +373,11 @@ def assert_results(): "stat", ["Sum", "SampleCount", "Minimum", "Maximum", "Average"], ) - def test_get_metric_data_stats(self, aws_client, snapshot, stat): + def test_get_metric_data_stats(self, aws_cloudwatch_client, snapshot, stat): utc_now = datetime.now(tz=UTC) namespace = f"test/{short_uid()}" - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -404,7 +389,7 @@ def test_get_metric_data_stats(self, aws_client, snapshot, stat): ], ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -422,7 +407,7 @@ def test_get_metric_data_stats(self, aws_client, snapshot, stat): ) def assert_results(): - response = aws_client.cloudwatch.get_metric_data( + response = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "result1", @@ -444,11 +429,11 @@ def assert_results(): retry(assert_results, retries=10, sleep_before=sleep_before) @markers.aws.validated - def test_get_metric_data_with_dimensions(self, aws_client, snapshot): + def test_get_metric_data_with_dimensions(self, aws_cloudwatch_client, snapshot): utc_now = datetime.now(tz=UTC) namespace = f"test/{short_uid()}" - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -461,7 +446,7 @@ def test_get_metric_data_with_dimensions(self, aws_client, snapshot): ], ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -474,7 +459,7 @@ def test_get_metric_data_with_dimensions(self, aws_client, snapshot): ], ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -492,7 +477,7 @@ def test_get_metric_data_with_dimensions(self, aws_client, snapshot): ) def assert_results(): - response = aws_client.cloudwatch.get_metric_data( + response = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "result1", @@ -522,7 +507,7 @@ def assert_results(): @markers.aws.only_localstack # this feature was a customer request and added with https://github.com/localstack/localstack/pull/3535 - def test_raw_metric_data(self, aws_client, region_name): + def test_raw_metric_data_internal_endpoint(self, aws_client, region_name): """ tests internal endpoint at "/_aws/cloudwatch/metrics/raw" """ @@ -543,7 +528,7 @@ def test_raw_metric_data(self, aws_client, region_name): assert len(metrics_with_ns) == 1 @markers.aws.validated - def test_multiple_dimensions(self, aws_client): + def test_multiple_dimensions(self, aws_cloudwatch_client): namespaces = [ f"ns1-{short_uid()}", f"ns2-{short_uid()}", @@ -552,7 +537,7 @@ def test_multiple_dimensions(self, aws_client): num_dimensions = 2 for ns in namespaces: for i in range(3): - rs = aws_client.cloudwatch.put_metric_data( + put_metric_data = aws_cloudwatch_client.put_metric_data( Namespace=ns, MetricData=[ { @@ -567,10 +552,10 @@ def test_multiple_dimensions(self, aws_client): } ], ) - assert 200 == rs["ResponseMetadata"]["HTTPStatusCode"] + assert put_metric_data["ResponseMetadata"]["HTTPStatusCode"] == 200 def _check_metrics(): - rs = aws_client.cloudwatch.get_paginator("list_metrics").paginate().build_full_result() + rs = aws_cloudwatch_client.get_paginator("list_metrics").paginate().build_full_result() metrics = [m for m in rs["Metrics"] if m.get("Namespace") in namespaces] assert metrics assert len(metrics) == len(namespaces) * num_dimensions @@ -578,11 +563,23 @@ def _check_metrics(): retry(_check_metrics, sleep=2, retries=10, sleep_before=2) @markers.aws.validated - def test_describe_alarms_converts_date_format_correctly(self, aws_client, cleanups): + @markers.snapshot.skip_snapshot_verify( + condition=is_old_provider, paths=["$..MetricAlarms..StateTransitionedTimestamp"] + ) + def test_describe_alarms_converts_date_format_correctly( + self, aws_cloudwatch_client, cleanups, snapshot + ): + snapshot.add_transformers_list( + [ + snapshot.transform.key_value("AlarmName"), + snapshot.transform.key_value("MetricName"), + snapshot.transform.key_value("Namespace"), + ] + ) alarm_name = f"a-{short_uid()}:test" metric_name = f"test-metric-{short_uid()}" namespace = f"test-ns-{short_uid()}" - aws_client.cloudwatch.put_metric_alarm( + aws_cloudwatch_client.put_metric_alarm( AlarmName=alarm_name, Namespace=namespace, MetricName=metric_name, @@ -592,20 +589,21 @@ def test_describe_alarms_converts_date_format_correctly(self, aws_client, cleanu Statistic="Sum", Threshold=30, ) - cleanups.append(lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=[alarm_name])) - result = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + cleanups.append(lambda: aws_cloudwatch_client.delete_alarms(AlarmNames=[alarm_name])) + result = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) alarm = result["MetricAlarms"][0] assert isinstance(alarm["AlarmConfigurationUpdatedTimestamp"], datetime) assert isinstance(alarm["StateUpdatedTimestamp"], datetime) + snapshot.match("describe-alarms", result) @markers.aws.validated - def test_put_composite_alarm_describe_alarms(self, aws_client, cleanups): + def test_put_composite_alarm_describe_alarms(self, aws_cloudwatch_client, cleanups): composite_alarm_name = f"composite-a-{short_uid()}" alarm_name = f"a-{short_uid()}" metric_name = "something" namespace = f"test-ns-{short_uid()}" alarm_rule = f'ALARM("{alarm_name}")' - aws_client.cloudwatch.put_metric_alarm( + aws_cloudwatch_client.put_metric_alarm( AlarmName=alarm_name, Namespace=namespace, MetricName=metric_name, @@ -615,15 +613,15 @@ def test_put_composite_alarm_describe_alarms(self, aws_client, cleanups): Statistic="Sum", Threshold=30, ) - cleanups.append(lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=[alarm_name])) - aws_client.cloudwatch.put_composite_alarm( + cleanups.append(lambda: aws_cloudwatch_client.delete_alarms(AlarmNames=[alarm_name])) + aws_cloudwatch_client.put_composite_alarm( AlarmName=composite_alarm_name, AlarmRule=alarm_rule, ) cleanups.append( - lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=[composite_alarm_name]) + lambda: aws_cloudwatch_client.delete_alarms(AlarmNames=[composite_alarm_name]) ) - result = aws_client.cloudwatch.describe_alarms( + result = aws_cloudwatch_client.describe_alarms( AlarmNames=[composite_alarm_name], AlarmTypes=["CompositeAlarm"] ) alarm = result["CompositeAlarms"][0] @@ -635,12 +633,12 @@ def test_put_composite_alarm_describe_alarms(self, aws_client, cleanups): condition=is_old_provider, paths=["$..MetricAlarms..AlarmDescription", "$..MetricAlarms..StateTransitionedTimestamp"], ) - def test_store_tags(self, aws_client, cleanups, snapshot): + def test_store_tags(self, aws_cloudwatch_client, cleanups, snapshot): alarm_name = f"a-{short_uid()}" metric_name = "store_tags" namespace = f"test-ns-{short_uid()}" snapshot.add_transformer(snapshot.transform.cloudwatch_api()) - put_metric_alarm = aws_client.cloudwatch.put_metric_alarm( + put_metric_alarm = aws_cloudwatch_client.put_metric_alarm( AlarmName=alarm_name, Namespace=namespace, MetricName=metric_name, @@ -650,31 +648,31 @@ def test_store_tags(self, aws_client, cleanups, snapshot): Statistic="Sum", Threshold=30, ) - cleanups.append(lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=[alarm_name])) + cleanups.append(lambda: aws_cloudwatch_client.delete_alarms(AlarmNames=[alarm_name])) snapshot.match("put_metric_alarm", put_metric_alarm) - describe_alarms = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + describe_alarms = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) snapshot.match("describe_alarms", describe_alarms) alarm = describe_alarms["MetricAlarms"][0] alarm_arn = alarm["AlarmArn"] - list_tags_for_resource = aws_client.cloudwatch.list_tags_for_resource(ResourceARN=alarm_arn) + list_tags_for_resource = aws_cloudwatch_client.list_tags_for_resource(ResourceARN=alarm_arn) snapshot.match("list_tags_for_resource_empty ", list_tags_for_resource) # add tags tags = [{"Key": "tag1", "Value": "foo"}, {"Key": "tag2", "Value": "bar"}] - response = aws_client.cloudwatch.tag_resource(ResourceARN=alarm_arn, Tags=tags) - assert 200 == response["ResponseMetadata"]["HTTPStatusCode"] - list_tags_for_resource = aws_client.cloudwatch.list_tags_for_resource(ResourceARN=alarm_arn) + response = aws_cloudwatch_client.tag_resource(ResourceARN=alarm_arn, Tags=tags) + assert response["ResponseMetadata"]["HTTPStatusCode"] == 200 + list_tags_for_resource = aws_cloudwatch_client.list_tags_for_resource(ResourceARN=alarm_arn) snapshot.match("list_tags_for_resource", list_tags_for_resource) - response = aws_client.cloudwatch.untag_resource(ResourceARN=alarm_arn, TagKeys=["tag1"]) - assert 200 == response["ResponseMetadata"]["HTTPStatusCode"] - list_tags_for_resource_post_untag = aws_client.cloudwatch.list_tags_for_resource( + response = aws_cloudwatch_client.untag_resource(ResourceARN=alarm_arn, TagKeys=["tag1"]) + assert response["ResponseMetadata"]["HTTPStatusCode"] == 200 + list_tags_for_resource_post_untag = aws_cloudwatch_client.list_tags_for_resource( ResourceARN=alarm_arn ) snapshot.match("list_tags_for_resource_post_untag", list_tags_for_resource_post_untag) @markers.aws.validated - def test_list_metrics_uniqueness(self, aws_client): + def test_list_metrics_uniqueness(self, aws_cloudwatch_client): """ This can take quite a while on AWS unfortunately From the AWS docs: @@ -685,7 +683,7 @@ def test_list_metrics_uniqueness(self, aws_client): namespace = f"test/{short_uid()}" sleep_seconds = 10 if is_aws_cloud() else 1 retries = 100 if is_aws_cloud() else 10 - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -695,7 +693,7 @@ def test_list_metrics_uniqueness(self, aws_client): } ], ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -707,7 +705,7 @@ def test_list_metrics_uniqueness(self, aws_client): ) # duplicating existing metric - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -719,13 +717,13 @@ def test_list_metrics_uniqueness(self, aws_client): ) def _count_single_metrics(): - results = aws_client.cloudwatch.list_metrics(Namespace=namespace)["Metrics"] + results = aws_cloudwatch_client.list_metrics(Namespace=namespace)["Metrics"] assert len(results) == 2 # asserting only unique values are returned retry(_count_single_metrics, retries=retries, sleep_before=sleep_seconds) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -742,17 +740,17 @@ def _count_single_metrics(): ) def _count_aggregated_metrics(): - results = aws_client.cloudwatch.list_metrics(Namespace=namespace)["Metrics"] + results = aws_cloudwatch_client.list_metrics(Namespace=namespace)["Metrics"] assert len(results) == 3 retry(_count_aggregated_metrics, retries=retries, sleep_before=sleep_seconds) @markers.aws.validated - def test_list_metrics_with_filters(self, aws_client): + def test_list_metrics_with_filters(self, aws_cloudwatch_client): namespace = f"test/{short_uid()}" sleep_seconds = 10 if is_aws_cloud() else 1 retries = 100 if is_aws_cloud() else 10 - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -761,7 +759,7 @@ def test_list_metrics_with_filters(self, aws_client): } ], ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -772,7 +770,7 @@ def test_list_metrics_with_filters(self, aws_client): ], ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -783,7 +781,7 @@ def test_list_metrics_with_filters(self, aws_client): ], ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -800,13 +798,13 @@ def test_list_metrics_with_filters(self, aws_client): ) def _count_all_metrics_in_namespace(): - results = aws_client.cloudwatch.list_metrics(Namespace=namespace)["Metrics"] + results = aws_cloudwatch_client.list_metrics(Namespace=namespace)["Metrics"] assert len(results) == 4 retry(_count_all_metrics_in_namespace, retries=retries, sleep_before=sleep_seconds) def _count_specific_metric_in_namespace(): - results = aws_client.cloudwatch.list_metrics( + results = aws_cloudwatch_client.list_metrics( Namespace=namespace, MetricName="CPUUtilization" )["Metrics"] assert len(results) == 1 @@ -814,7 +812,7 @@ def _count_specific_metric_in_namespace(): retry(_count_specific_metric_in_namespace, retries=retries, sleep_before=sleep_seconds) def _count_metrics_in_namespace_with_dimension(): - results = aws_client.cloudwatch.list_metrics( + results = aws_cloudwatch_client.list_metrics( Namespace=namespace, Dimensions=[{"Name": "InstanceId"}] )["Metrics"] assert len(results) == 3 @@ -824,7 +822,7 @@ def _count_metrics_in_namespace_with_dimension(): ) def _count_metrics_in_namespace_with_dimension_value(): - results = aws_client.cloudwatch.list_metrics( + results = aws_cloudwatch_client.list_metrics( Namespace=namespace, Dimensions=[{"Name": "InstanceId", "Value": "two"}] )["Metrics"] assert len(results) == 2 @@ -836,8 +834,21 @@ def _count_metrics_in_namespace_with_dimension_value(): ) @markers.aws.validated - def test_put_metric_alarm_escape_character(self, cleanups, aws_client): - aws_client.cloudwatch.put_metric_alarm( + @markers.snapshot.skip_snapshot_verify( + condition=is_old_provider, paths=["$..MetricAlarms..StateTransitionedTimestamp"] + ) + def test_put_metric_alarm_escape_character( + self, cleanups, aws_cloudwatch_client, snapshot, region_name + ): + snapshot.add_transformers_list( + [ + snapshot.transform.key_value("AlarmName"), + snapshot.transform.key_value("MetricName"), + snapshot.transform.key_value("Namespace"), + ] + ) + + aws_cloudwatch_client.put_metric_alarm( AlarmName="cpu-mon", AlarmDescription="<", MetricName="CPUUtilization-2", @@ -847,18 +858,28 @@ def test_put_metric_alarm_escape_character(self, cleanups, aws_client): Threshold=1, ComparisonOperator="GreaterThanThreshold", EvaluationPeriods=1, - AlarmActions=["arn:aws:sns:us-east-1:111122223333:MyTopic"], + AlarmActions=[f"arn:aws:sns:{region_name}:111122223333:MyTopic"], ) - cleanups.append(lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=["cpu-mon"])) + cleanups.append(lambda: aws_cloudwatch_client.delete_alarms(AlarmNames=["cpu-mon"])) - result = aws_client.cloudwatch.describe_alarms(AlarmNames=["cpu-mon"]) + result = aws_cloudwatch_client.describe_alarms(AlarmNames=["cpu-mon"]) assert result.get("MetricAlarms")[0]["AlarmDescription"] == "<" + snapshot.match("describe-alarms-escaped-character", result) @markers.aws.validated @markers.snapshot.skip_snapshot_verify( condition=is_old_provider, paths=["$..MetricAlarms..StateTransitionedTimestamp"] ) - def test_set_alarm(self, sns_create_topic, sqs_create_queue, aws_client, cleanups, snapshot): + def test_set_alarm( + self, + sns_create_topic, + sqs_create_queue, + aws_client, + aws_cloudwatch_client, + cleanups, + snapshot, + sns_create_sqs_subscription, + ): snapshot.add_transformer(snapshot.transform.cloudwatch_api()) # create topics for state 'ALARM' and 'OK' topic_name_alarm = f"topic-{short_uid()}" @@ -876,20 +897,6 @@ def test_set_alarm(self, sns_create_topic, sqs_create_queue, aws_client, cleanup queue_url_alarm = sqs_create_queue(QueueName=f"AlarmQueue-{uid}") queue_url_ok = sqs_create_queue(QueueName=f"OKQueue-{uid}") - arn_queue_alarm = aws_client.sqs.get_queue_attributes( - QueueUrl=queue_url_alarm, AttributeNames=["QueueArn"] - )["Attributes"]["QueueArn"] - arn_queue_ok = aws_client.sqs.get_queue_attributes( - QueueUrl=queue_url_ok, AttributeNames=["QueueArn"] - )["Attributes"]["QueueArn"] - aws_client.sqs.set_queue_attributes( - QueueUrl=queue_url_alarm, - Attributes={"Policy": get_sqs_policy(arn_queue_alarm, topic_arn_alarm)}, - ) - aws_client.sqs.set_queue_attributes( - QueueUrl=queue_url_ok, Attributes={"Policy": get_sqs_policy(arn_queue_ok, topic_arn_ok)} - ) - alarm_name = "test-alarm" alarm_description = "Test Alarm when CPU exceeds 50 percent" @@ -908,23 +915,17 @@ def test_set_alarm(self, sns_create_topic, sqs_create_queue, aws_client, cleanup "Statistic": "AVERAGE", } # subscribe to SQS - subscription_alarm = aws_client.sns.subscribe( - TopicArn=topic_arn_alarm, Protocol="sqs", Endpoint=arn_queue_alarm - ) - cleanups.append( - lambda: aws_client.sns.unsubscribe( - SubscriptionArn=subscription_alarm["SubscriptionArn"] - ) + sns_create_sqs_subscription( + topic_arn=topic_arn_alarm, + queue_url=queue_url_alarm, ) - subscription_ok = aws_client.sns.subscribe( - TopicArn=topic_arn_ok, Protocol="sqs", Endpoint=arn_queue_ok - ) - cleanups.append( - lambda: aws_client.sns.unsubscribe(SubscriptionArn=subscription_ok["SubscriptionArn"]) + sns_create_sqs_subscription( + topic_arn=topic_arn_ok, + queue_url=queue_url_ok, ) # create alarm with actions for "OK" and "ALARM" - aws_client.cloudwatch.put_metric_alarm( + aws_cloudwatch_client.put_metric_alarm( AlarmName=alarm_name, AlarmDescription=alarm_description, MetricName=expected_trigger["MetricName"], @@ -934,20 +935,19 @@ def test_set_alarm(self, sns_create_topic, sqs_create_queue, aws_client, cleanup Threshold=expected_trigger["Threshold"], Dimensions=[{"Name": "InstanceId", "Value": "i-0317828c84edbe100"}], Unit=expected_trigger["Unit"], - Statistic=expected_trigger["Statistic"].capitalize(), + Statistic="Average", OKActions=[topic_arn_ok], AlarmActions=[topic_arn_alarm], EvaluationPeriods=expected_trigger["EvaluationPeriods"], ComparisonOperator=expected_trigger["ComparisonOperator"], TreatMissingData=expected_trigger["TreatMissingData"], ) - cleanups.append(lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=[alarm_name])) + cleanups.append(lambda: aws_cloudwatch_client.delete_alarms(AlarmNames=[alarm_name])) # trigger alarm - state_value = "ALARM" state_reason = "testing alarm" - aws_client.cloudwatch.set_alarm_state( - AlarmName=alarm_name, StateReason=state_reason, StateValue=state_value + aws_cloudwatch_client.set_alarm_state( + AlarmName=alarm_name, StateReason=state_reason, StateValue="ALARM" ) retry( @@ -957,19 +957,18 @@ def test_set_alarm(self, sns_create_topic, sqs_create_queue, aws_client, cleanup sqs_client=aws_client.sqs, expected_queue_url=queue_url_alarm, expected_topic_arn=topic_arn_alarm, - expected_new=state_value, + expected_new="ALARM", expected_reason=state_reason, alarm_name=alarm_name, alarm_description=alarm_description, expected_trigger=expected_trigger, ) - describe_alarm = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + describe_alarm = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) snapshot.match("triggered-alarm", describe_alarm) # trigger OK - state_value = "OK" state_reason = "resetting alarm" - aws_client.cloudwatch.set_alarm_state( - AlarmName=alarm_name, StateReason=state_reason, StateValue=state_value + aws_cloudwatch_client.set_alarm_state( + AlarmName=alarm_name, StateReason=state_reason, StateValue="OK" ) retry( @@ -979,19 +978,26 @@ def test_set_alarm(self, sns_create_topic, sqs_create_queue, aws_client, cleanup sqs_client=aws_client.sqs, expected_queue_url=queue_url_ok, expected_topic_arn=topic_arn_ok, - expected_new=state_value, + expected_new="OK", expected_reason=state_reason, alarm_name=alarm_name, alarm_description=alarm_description, expected_trigger=expected_trigger, ) - describe_alarm = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + describe_alarm = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) snapshot.match("reset-alarm", describe_alarm) @markers.aws.validated @pytest.mark.skipif(is_old_provider(), reason="New test for v2 provider") def test_trigger_composite_alarm( - self, sns_create_topic, sqs_create_queue, aws_client, cleanups, snapshot + self, + sns_create_topic, + sqs_create_queue, + aws_client, + cleanups, + snapshot, + sns_create_sqs_subscription, + aws_cloudwatch_client, ): # create topics for state 'ALARM' and 'OK' of the composite alarm topic_name_alarm = f"topic-alarm-{short_uid()}" @@ -1002,45 +1008,23 @@ def test_trigger_composite_alarm( sns_topic_ok = sns_create_topic(Name=topic_name_ok) topic_arn_ok = sns_topic_ok["TopicArn"] - # TODO extract SNS-to-SQS into a fixture # create queues for 'ALARM' and 'OK' of the composite alarm (will receive sns messages) queue_url_alarm = sqs_create_queue(QueueName=f"AlarmQueue-{short_uid()}") queue_url_ok = sqs_create_queue(QueueName=f"OKQueue-{short_uid()}") - arn_queue_alarm = aws_client.sqs.get_queue_attributes( - QueueUrl=queue_url_alarm, AttributeNames=["QueueArn"] - )["Attributes"]["QueueArn"] - arn_queue_ok = aws_client.sqs.get_queue_attributes( - QueueUrl=queue_url_ok, AttributeNames=["QueueArn"] - )["Attributes"]["QueueArn"] - aws_client.sqs.set_queue_attributes( - QueueUrl=queue_url_alarm, - Attributes={"Policy": get_sqs_policy(arn_queue_alarm, topic_arn_alarm)}, + sns_create_sqs_subscription( + topic_arn=topic_arn_alarm, + queue_url=queue_url_alarm, ) - aws_client.sqs.set_queue_attributes( - QueueUrl=queue_url_ok, Attributes={"Policy": get_sqs_policy(arn_queue_ok, topic_arn_ok)} - ) - - # subscribe to SQS - subscription_alarm = aws_client.sns.subscribe( - TopicArn=topic_arn_alarm, Protocol="sqs", Endpoint=arn_queue_alarm - ) - cleanups.append( - lambda: aws_client.sns.unsubscribe( - SubscriptionArn=subscription_alarm["SubscriptionArn"] - ) - ) - subscription_ok = aws_client.sns.subscribe( - TopicArn=topic_arn_ok, Protocol="sqs", Endpoint=arn_queue_ok - ) - cleanups.append( - lambda: aws_client.sns.unsubscribe(SubscriptionArn=subscription_ok["SubscriptionArn"]) + sns_create_sqs_subscription( + topic_arn=topic_arn_ok, + queue_url=queue_url_ok, ) # put metric alarms that would be parts of a composite one # TODO extract put metric alarm and associated cleanups into a fixture def _put_metric_alarm(alarm_name: str): - aws_client.cloudwatch.put_metric_alarm( + aws_cloudwatch_client.put_metric_alarm( AlarmName=alarm_name, MetricName="CPUUtilization", Namespace="AWS/EC2", @@ -1050,7 +1034,7 @@ def _put_metric_alarm(alarm_name: str): ComparisonOperator="GreaterThanThreshold", Threshold=30, ) - cleanups.append(lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=[alarm_name])) + cleanups.append(lambda: aws_cloudwatch_client.delete_alarms(AlarmNames=[alarm_name])) alarm_1_name = f"simple-alarm-1-{short_uid()}" alarm_2_name = f"simple-alarm-2-{short_uid()}" @@ -1058,10 +1042,10 @@ def _put_metric_alarm(alarm_name: str): _put_metric_alarm(alarm_1_name) _put_metric_alarm(alarm_2_name) - alarm_1_arn = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_1_name])[ + alarm_1_arn = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_1_name])[ "MetricAlarms" ][0]["AlarmArn"] - alarm_2_arn = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_2_name])[ + alarm_2_arn = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_2_name])[ "MetricAlarms" ][0]["AlarmArn"] @@ -1071,7 +1055,7 @@ def _put_metric_alarm(alarm_name: str): composite_alarm_rule = f'ALARM("{alarm_1_arn}") OR ALARM("{alarm_2_arn}")' - put_composite_alarm_response = aws_client.cloudwatch.put_composite_alarm( + put_composite_alarm_response = aws_cloudwatch_client.put_composite_alarm( AlarmName=composite_alarm_name, AlarmDescription=composite_alarm_description, AlarmRule=composite_alarm_rule, @@ -1079,11 +1063,11 @@ def _put_metric_alarm(alarm_name: str): AlarmActions=[topic_arn_alarm], ) cleanups.append( - lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=[composite_alarm_name]) + lambda: aws_cloudwatch_client.delete_alarms(AlarmNames=[composite_alarm_name]) ) snapshot.match("put-composite-alarm", put_composite_alarm_response) - composite_alarms_list = aws_client.cloudwatch.describe_alarms( + composite_alarms_list = aws_cloudwatch_client.describe_alarms( AlarmNames=[composite_alarm_name], AlarmTypes=["CompositeAlarm"] ) composite_alarm = composite_alarms_list["CompositeAlarms"][0] @@ -1148,7 +1132,7 @@ def _check_composite_alarm_ok_message( ) # trigger alarm 1 - composite one should also go into ALARM state - aws_client.cloudwatch.set_alarm_state( + aws_cloudwatch_client.set_alarm_state( AlarmName=alarm_1_name, StateValue="ALARM", StateReason="trigger alarm 1" ) @@ -1157,7 +1141,7 @@ def _check_composite_alarm_ok_message( expected_triggering_child_state="ALARM", ) - composite_alarms_list = aws_client.cloudwatch.describe_alarms( + composite_alarms_list = aws_cloudwatch_client.describe_alarms( AlarmNames=[composite_alarm_name], AlarmTypes=["CompositeAlarm"] ) composite_alarm_in_alarm_when_alarm_1_in_alarm = composite_alarms_list["CompositeAlarms"][0] @@ -1167,7 +1151,7 @@ def _check_composite_alarm_ok_message( ) # trigger OK for alarm 1 - composite one should also go back to OK - aws_client.cloudwatch.set_alarm_state( + aws_cloudwatch_client.set_alarm_state( AlarmName=alarm_1_name, StateValue="OK", StateReason="resetting alarm 1" ) @@ -1176,7 +1160,7 @@ def _check_composite_alarm_ok_message( expected_triggering_child_state="OK", ) - composite_alarms_list = aws_client.cloudwatch.describe_alarms( + composite_alarms_list = aws_cloudwatch_client.describe_alarms( AlarmNames=[composite_alarm_name], AlarmTypes=["CompositeAlarm"] ) composite_alarm_in_ok_when_alarm_1_back_to_ok = composite_alarms_list["CompositeAlarms"][0] @@ -1186,7 +1170,7 @@ def _check_composite_alarm_ok_message( ) # trigger alarm 2 - composite one should go again into ALARM state - aws_client.cloudwatch.set_alarm_state( + aws_cloudwatch_client.set_alarm_state( AlarmName=alarm_2_name, StateValue="ALARM", StateReason="trigger alarm 2" ) @@ -1195,7 +1179,7 @@ def _check_composite_alarm_ok_message( expected_triggering_child_state="ALARM", ) - composite_alarms_list = aws_client.cloudwatch.describe_alarms( + composite_alarms_list = aws_cloudwatch_client.describe_alarms( AlarmNames=[composite_alarm_name], AlarmTypes=["CompositeAlarm"] ) composite_alarm_in_alarm_when_alarm_2_in_alarm = composite_alarms_list["CompositeAlarms"][0] @@ -1205,7 +1189,7 @@ def _check_composite_alarm_ok_message( ) # trigger OK for alarm 2 - composite one should also go back to OK - aws_client.cloudwatch.set_alarm_state( + aws_cloudwatch_client.set_alarm_state( AlarmName=alarm_2_name, StateValue="OK", StateReason="resetting alarm 2" ) @@ -1214,7 +1198,7 @@ def _check_composite_alarm_ok_message( expected_triggering_child_state="OK", ) - composite_alarms_list = aws_client.cloudwatch.describe_alarms( + composite_alarms_list = aws_cloudwatch_client.describe_alarms( AlarmNames=[composite_alarm_name], AlarmTypes=["CompositeAlarm"] ) composite_alarm_in_ok_when_alarm_2_back_to_ok = composite_alarms_list["CompositeAlarms"][0] @@ -1224,14 +1208,14 @@ def _check_composite_alarm_ok_message( ) # trigger alarm 2 while alarm 1 is triggered - composite one shouldn't change - aws_client.cloudwatch.set_alarm_state( + aws_cloudwatch_client.set_alarm_state( AlarmName=alarm_1_name, StateValue="ALARM", StateReason="trigger alarm 1" ) - aws_client.cloudwatch.set_alarm_state( + aws_cloudwatch_client.set_alarm_state( AlarmName=alarm_2_name, StateValue="ALARM", StateReason="trigger alarm 2" ) - composite_alarms_list = aws_client.cloudwatch.describe_alarms( + composite_alarms_list = aws_cloudwatch_client.describe_alarms( AlarmNames=[composite_alarm_name], AlarmTypes=["CompositeAlarm"] ) composite_alarm_is_triggered_by_alarm_1_and_then_not_changed_by_alarm_2 = ( @@ -1256,10 +1240,16 @@ def _check_composite_alarm_ok_message( condition=is_old_provider(), reason="DescribeAlarmHistory is not implemented" ) def test_put_metric_alarm( - self, sns_create_topic, sqs_create_queue, snapshot, aws_client, cleanups + self, + sns_create_topic, + sqs_queue, + snapshot, + aws_client, + cleanups, + sns_create_sqs_subscription, + aws_cloudwatch_client, ): - sns_topic_alarm = sns_create_topic() - topic_arn_alarm = sns_topic_alarm["TopicArn"] + topic_arn_alarm = sns_create_topic()["TopicArn"] snapshot.add_transformer(snapshot.transform.cloudwatch_api()) snapshot.add_transformer( @@ -1275,44 +1265,34 @@ def test_put_metric_alarm( namespace = f"test-nsp-{short_uid()}" snapshot.add_transformer(snapshot.transform.regex(namespace, "")) - sqs_queue = sqs_create_queue() - arn_queue = aws_client.sqs.get_queue_attributes( - QueueUrl=sqs_queue, AttributeNames=["QueueArn"] - )["Attributes"]["QueueArn"] - # required for AWS: - aws_client.sqs.set_queue_attributes( - QueueUrl=sqs_queue, - Attributes={"Policy": get_sqs_policy(arn_queue, topic_arn_alarm)}, - ) metric_name = "my-metric1" dimension = [{"Name": "InstanceId", "Value": "abc"}] alarm_name = f"test-alarm-{short_uid()}" - subscription = aws_client.sns.subscribe( - TopicArn=topic_arn_alarm, Protocol="sqs", Endpoint=arn_queue - ) - cleanups.append( - lambda: aws_client.sns.unsubscribe(SubscriptionArn=subscription["SubscriptionArn"]) + sns_create_sqs_subscription( + topic_arn=topic_arn_alarm, + queue_url=sqs_queue, ) + data = [ { "MetricName": metric_name, "Dimensions": dimension, "Value": 21, - "Timestamp": datetime.utcnow().replace(tzinfo=UTC), + "Timestamp": datetime.now(tz=UTC), "Unit": "Seconds", }, { "MetricName": metric_name, "Dimensions": dimension, "Value": 22, - "Timestamp": datetime.utcnow().replace(tzinfo=UTC), + "Timestamp": datetime.now(tz=UTC), "Unit": "Seconds", }, ] # create alarm with action for "ALARM" - aws_client.cloudwatch.put_metric_alarm( + aws_cloudwatch_client.put_metric_alarm( AlarmName=alarm_name, AlarmDescription="testing cloudwatch alarms", MetricName=metric_name, @@ -1330,11 +1310,11 @@ def test_put_metric_alarm( TreatMissingData="ignore", # notBreaching had some downsides, as depending on the alarm evaluation interval it would first go into OK ) - cleanups.append(lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=[alarm_name])) - response = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + cleanups.append(lambda: aws_cloudwatch_client.delete_alarms(AlarmNames=[alarm_name])) + response = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) snapshot.match("describe-alarm", response) - aws_client.cloudwatch.put_metric_data(Namespace=namespace, MetricData=data) + aws_cloudwatch_client.put_metric_data(Namespace=namespace, MetricData=data) retry( _sqs_messages_snapshot, retries=60, @@ -1348,13 +1328,13 @@ def test_put_metric_alarm( ) # describe alarm history - history = aws_client.cloudwatch.describe_alarm_history( + history = aws_cloudwatch_client.describe_alarm_history( AlarmName=alarm_name, HistoryItemType="StateUpdate" ) snapshot.match("describe-alarm-history", history) # describe alarms for metric - alarms = aws_client.cloudwatch.describe_alarms_for_metric( + alarms = aws_cloudwatch_client.describe_alarms_for_metric( MetricName=metric_name, Namespace=namespace, Dimensions=dimension, @@ -1376,38 +1356,32 @@ def test_put_metric_alarm( ] ) def test_breaching_alarm_actions( - self, sns_create_topic, sqs_create_queue, snapshot, aws_client, cleanups + self, + sns_create_topic, + sqs_queue, + snapshot, + aws_client, + cleanups, + sns_create_sqs_subscription, + aws_cloudwatch_client, ): - sns_topic_alarm = sns_create_topic() - topic_arn_alarm = sns_topic_alarm["TopicArn"] + topic_arn_alarm = sns_create_topic()["TopicArn"] snapshot.add_transformer(snapshot.transform.cloudwatch_api()) snapshot.add_transformer( snapshot.transform.regex(topic_arn_alarm.split(":")[-1], ""), priority=2 ) - sqs_queue = sqs_create_queue() - arn_queue = aws_client.sqs.get_queue_attributes( - QueueUrl=sqs_queue, AttributeNames=["QueueArn"] - )["Attributes"]["QueueArn"] - # required for AWS: - aws_client.sqs.set_queue_attributes( - QueueUrl=sqs_queue, - Attributes={"Policy": get_sqs_policy(arn_queue, topic_arn_alarm)}, - ) metric_name = "my-metric101" dimension = [{"Name": "InstanceId", "Value": "abc"}] namespace = "test/breaching-alarm" alarm_name = f"test-alarm-{short_uid()}" - subscription = aws_client.sns.subscribe( - TopicArn=topic_arn_alarm, Protocol="sqs", Endpoint=arn_queue - ) - cleanups.append( - lambda: aws_client.sns.unsubscribe(SubscriptionArn=subscription["SubscriptionArn"]) + sns_create_sqs_subscription( + topic_arn=topic_arn_alarm, + queue_url=sqs_queue, ) - snapshot.match("cloudwatch_sns_subscription", subscription) - aws_client.cloudwatch.put_metric_alarm( + aws_cloudwatch_client.put_metric_alarm( AlarmName=alarm_name, AlarmDescription="testing cloudwatch alarms", MetricName=metric_name, @@ -1423,22 +1397,22 @@ def test_breaching_alarm_actions( ComparisonOperator="GreaterThanThreshold", TreatMissingData="breaching", ) - cleanups.append(lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=[alarm_name])) - response = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + cleanups.append(lambda: aws_cloudwatch_client.delete_alarms(AlarmNames=[alarm_name])) + response = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) assert response["MetricAlarms"][0]["ActionsEnabled"] retry( _sqs_messages_snapshot, retries=80, sleep=3.0, - sleep_before=5, + sleep_before=5 if is_aws_cloud() else 0.5, expected_state="ALARM", sqs_client=aws_client.sqs, sqs_queue=sqs_queue, snapshot=snapshot, identifier="alarm-1", ) - response = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + response = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) snapshot.match("alarm-1-describe", response) @markers.aws.validated @@ -1446,7 +1420,14 @@ def test_breaching_alarm_actions( condition=is_old_provider, paths=["$..MetricAlarms..StateTransitionedTimestamp"] ) def test_enable_disable_alarm_actions( - self, sns_create_topic, sqs_create_queue, snapshot, aws_client, cleanups + self, + sns_create_topic, + sqs_create_queue, + snapshot, + aws_client, + cleanups, + sns_create_sqs_subscription, + aws_cloudwatch_client, ): sns_topic_alarm = sns_create_topic() topic_arn_alarm = sns_topic_alarm["TopicArn"] @@ -1455,28 +1436,18 @@ def test_enable_disable_alarm_actions( snapshot.transform.regex(topic_arn_alarm.split(":")[-1], ""), priority=2 ) - sqs_queue = sqs_create_queue() - arn_queue = aws_client.sqs.get_queue_attributes( - QueueUrl=sqs_queue, AttributeNames=["QueueArn"] - )["Attributes"]["QueueArn"] - # required for AWS: - aws_client.sqs.set_queue_attributes( - QueueUrl=sqs_queue, - Attributes={"Policy": get_sqs_policy(arn_queue, topic_arn_alarm)}, - ) + queue_url = sqs_create_queue() metric_name = "my-metric101" dimension = [{"Name": "InstanceId", "Value": "abc"}] namespace = f"test/enable-{short_uid()}" alarm_name = f"test-alarm-{short_uid()}" - subscription = aws_client.sns.subscribe( - TopicArn=topic_arn_alarm, Protocol="sqs", Endpoint=arn_queue - ) - cleanups.append( - lambda: aws_client.sns.unsubscribe(SubscriptionArn=subscription["SubscriptionArn"]) + sns_create_sqs_subscription( + topic_arn=topic_arn_alarm, + queue_url=queue_url, ) - snapshot.match("cloudwatch_sns_subscription", subscription) - aws_client.cloudwatch.put_metric_alarm( + + aws_cloudwatch_client.put_metric_alarm( AlarmName=alarm_name, AlarmDescription="testing cloudwatch alarms", MetricName=metric_name, @@ -1492,35 +1463,36 @@ def test_enable_disable_alarm_actions( ComparisonOperator="GreaterThanThreshold", TreatMissingData="ignore", ) - cleanups.append(lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=[alarm_name])) - response = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + cleanups.append(lambda: aws_cloudwatch_client.delete_alarms(AlarmNames=[alarm_name])) + response = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) assert response["MetricAlarms"][0]["ActionsEnabled"] snapshot.match("describe_alarm", response) - aws_client.cloudwatch.set_alarm_state( + aws_cloudwatch_client.set_alarm_state( AlarmName=alarm_name, StateValue="ALARM", StateReason="testing alarm" ) + sleep_before = 5 if is_aws_cloud() else 0.5 retry( _sqs_messages_snapshot, retries=80, sleep=3.0, - sleep_before=5, + sleep_before=sleep_before, expected_state="ALARM", sqs_client=aws_client.sqs, - sqs_queue=sqs_queue, + sqs_queue=queue_url, snapshot=snapshot, identifier="alarm-state", ) - describe_alarm = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + describe_alarm = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) snapshot.match("alarm-state-describe", describe_alarm) # disable alarm action - aws_client.cloudwatch.disable_alarm_actions(AlarmNames=[alarm_name]) - aws_client.cloudwatch.set_alarm_state( + aws_cloudwatch_client.disable_alarm_actions(AlarmNames=[alarm_name]) + aws_cloudwatch_client.set_alarm_state( AlarmName=alarm_name, StateValue="OK", StateReason="testing OK state" ) - response = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + response = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) snapshot.match("describe_alarm_disabled", response) assert response["MetricAlarms"][0]["StateValue"] == "OK" assert not response["MetricAlarms"][0]["ActionsEnabled"] @@ -1528,22 +1500,24 @@ def test_enable_disable_alarm_actions( _check_alarm_triggered, retries=80, sleep=3.0, - sleep_before=5, + sleep_before=sleep_before, expected_state="OK", alarm_name=alarm_name, - cloudwatch_client=aws_client.cloudwatch, + cloudwatch_client=aws_cloudwatch_client, snapshot=snapshot, identifier="ok-state-action-disabled", ) # enable alarm action - aws_client.cloudwatch.enable_alarm_actions(AlarmNames=[alarm_name]) - response = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + aws_cloudwatch_client.enable_alarm_actions(AlarmNames=[alarm_name]) + response = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) snapshot.match("describe_alarm_enabled", response) assert response["MetricAlarms"][0]["ActionsEnabled"] @markers.aws.validated - def test_aws_sqs_metrics_created(self, sqs_create_queue, snapshot, aws_client): + def test_aws_sqs_metrics_created( + self, sqs_create_queue, snapshot, aws_client, aws_cloudwatch_client + ): snapshot.add_transformer(snapshot.transform.cloudwatch_api()) sqs_url = sqs_create_queue() sqs_arn = aws_client.sqs.get_queue_attributes( @@ -1556,39 +1530,39 @@ def test_aws_sqs_metrics_created(self, sqs_create_queue, snapshot, aws_client): aws_client.sqs.send_message(QueueUrl=sqs_url, MessageBody="Hello") dimensions = [{"Name": "QueueName", "Value": queue_name}] - metric_default = { - "MetricStat": { - "Metric": { - "Namespace": "AWS/SQS", - "Dimensions": dimensions, + def _create_metric(metric_id: str) -> dict: + return { + "Id": metric_id, + "MetricStat": { + "Metric": { + "Namespace": "AWS/SQS", + "Dimensions": dimensions, + }, + "Period": 60, + "Stat": "Sum", }, - "Period": 60, - "Stat": "Sum", - }, - } - sent = {"Id": "sent"} - sent.update(copy.deepcopy(metric_default)) + } + + sent = _create_metric("sent") sent["MetricStat"]["Metric"]["MetricName"] = "NumberOfMessagesSent" - sent_size = {"Id": "sent_size"} - sent_size.update(copy.deepcopy(metric_default)) + sent_size = _create_metric("sent_size") sent_size["MetricStat"]["Metric"]["MetricName"] = "SentMessageSize" - empty = {"Id": "empty_receives"} - empty.update(copy.deepcopy(metric_default)) + empty = _create_metric("empty_receives") empty["MetricStat"]["Metric"]["MetricName"] = "NumberOfEmptyReceives" def contains_sent_messages_metrics() -> int: - res = aws_client.cloudwatch.list_metrics(Dimensions=dimensions) + res = aws_cloudwatch_client.list_metrics(Dimensions=dimensions) metrics = [metric["MetricName"] for metric in res["Metrics"]] if all( m in metrics for m in ["NumberOfMessagesSent", "SentMessageSize", "NumberOfEmptyReceives"] ): - res = aws_client.cloudwatch.get_metric_data( + res = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[sent, sent_size, empty], - StartTime=datetime.utcnow() - timedelta(hours=1), - EndTime=datetime.utcnow(), + StartTime=datetime.now(tz=UTC) - timedelta(hours=1), + EndTime=datetime.now(tz=UTC), ) # add check for values, because AWS is sometimes a bit slower to fill those values up... if ( @@ -1601,10 +1575,10 @@ def contains_sent_messages_metrics() -> int: assert poll_condition(lambda: contains_sent_messages_metrics(), interval=1, timeout=120) - response = aws_client.cloudwatch.get_metric_data( + response = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[sent, sent_size, empty], - StartTime=datetime.utcnow() - timedelta(hours=1), - EndTime=datetime.utcnow(), + StartTime=datetime.now(tz=UTC) - timedelta(hours=1), + EndTime=datetime.now(tz=UTC), ) snapshot.match("get_metric_data", response) @@ -1615,22 +1589,20 @@ def contains_sent_messages_metrics() -> int: receipt_handle = sqs_messages[0]["ReceiptHandle"] aws_client.sqs.delete_message(QueueUrl=sqs_url, ReceiptHandle=receipt_handle) - msg_received = {"Id": "num_msg_received"} - msg_received.update(copy.deepcopy(metric_default)) + msg_received = _create_metric("num_msg_received") msg_received["MetricStat"]["Metric"]["MetricName"] = "NumberOfMessagesReceived" - msg_deleted = {"Id": "num_msg_deleted"} - msg_deleted.update(copy.deepcopy(metric_default)) + msg_deleted = _create_metric("num_msg_deleted") msg_deleted["MetricStat"]["Metric"]["MetricName"] = "NumberOfMessagesDeleted" def contains_receive_delete_metrics() -> int: - res = aws_client.cloudwatch.list_metrics(Dimensions=dimensions) + res = aws_cloudwatch_client.list_metrics(Dimensions=dimensions) metrics = [metric["MetricName"] for metric in res["Metrics"]] if all(m in metrics for m in ["NumberOfMessagesReceived", "NumberOfMessagesDeleted"]): - res = aws_client.cloudwatch.get_metric_data( + res = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[msg_received, msg_deleted], - StartTime=datetime.utcnow() - timedelta(hours=1), - EndTime=datetime.utcnow(), + StartTime=datetime.now(tz=UTC) - timedelta(hours=1), + EndTime=datetime.now(tz=UTC), ) # add check for values, because AWS is sometimes a bit slower to fill those values up... if res["MetricDataResults"][0]["Values"] and res["MetricDataResults"][1]["Values"]: @@ -1639,17 +1611,17 @@ def contains_receive_delete_metrics() -> int: assert poll_condition(lambda: contains_receive_delete_metrics(), interval=1, timeout=120) - response = aws_client.cloudwatch.get_metric_data( + response = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[msg_received, msg_deleted], - StartTime=datetime.utcnow() - timedelta(hours=1), - EndTime=datetime.utcnow(), + StartTime=datetime.now(tz=UTC) - timedelta(hours=1), + EndTime=datetime.now(tz=UTC), ) snapshot.match("get_metric_data_2", response) @markers.aws.validated @pytest.mark.skipif(condition=is_old_provider(), reason="Old provider is not raising exception") - def test_invalid_dashboard_name(self, aws_client, region_name, snapshot): + def test_invalid_dashboard_name(self, aws_cloudwatch_client, region_name, snapshot): dashboard_name = f"test-{short_uid()}:invalid" dashboard_body = { "widgets": [ @@ -1669,8 +1641,8 @@ def test_invalid_dashboard_name(self, aws_client, region_name, snapshot): ] } - with pytest.raises(Exception) as ex: - aws_client.cloudwatch.put_dashboard( + with pytest.raises(ClientError) as ex: + aws_cloudwatch_client.put_dashboard( DashboardName=dashboard_name, DashboardBody=json.dumps(dashboard_body) ) @@ -1688,7 +1660,7 @@ def test_invalid_dashboard_name(self, aws_client, region_name, snapshot): "$..DashboardEntries..Size", # need to be skipped because size changes if the region name length is longer ] ) - def test_dashboard_lifecycle(self, aws_client, region_name, snapshot): + def test_dashboard_lifecycle(self, aws_cloudwatch_client, region_name, snapshot): dashboard_name = f"test-{short_uid()}" dashboard_body = { "widgets": [ @@ -1707,31 +1679,31 @@ def test_dashboard_lifecycle(self, aws_client, region_name, snapshot): } ] } - aws_client.cloudwatch.put_dashboard( + aws_cloudwatch_client.put_dashboard( DashboardName=dashboard_name, DashboardBody=json.dumps(dashboard_body) ) - response = aws_client.cloudwatch.get_dashboard(DashboardName=dashboard_name) + response = aws_cloudwatch_client.get_dashboard(DashboardName=dashboard_name) snapshot.add_transformer(snapshot.transform.key_value("DashboardName")) snapshot.match("get_dashboard", response) - dashboards_list = aws_client.cloudwatch.list_dashboards() + dashboards_list = aws_cloudwatch_client.list_dashboards() snapshot.match("list_dashboards", dashboards_list) # assert prefix filtering working - dashboards_list = aws_client.cloudwatch.list_dashboards(DashboardNamePrefix="no-valid") + dashboards_list = aws_cloudwatch_client.list_dashboards(DashboardNamePrefix="no-valid") snapshot.match("list_dashboards_prefix_empty", dashboards_list) - dashboards_list = aws_client.cloudwatch.list_dashboards(DashboardNamePrefix="test") + dashboards_list = aws_cloudwatch_client.list_dashboards(DashboardNamePrefix="test") snapshot.match("list_dashboards_prefix", dashboards_list) - aws_client.cloudwatch.delete_dashboards(DashboardNames=[dashboard_name]) - dashboards_list = aws_client.cloudwatch.list_dashboards() + aws_cloudwatch_client.delete_dashboards(DashboardNames=[dashboard_name]) + dashboards_list = aws_cloudwatch_client.list_dashboards() snapshot.match("list_dashboards_empty", dashboards_list) @markers.aws.validated @pytest.mark.skipif(condition=not is_aws_cloud(), reason="Operations not supported") def test_create_metric_stream( self, - aws_client, + aws_cloudwatch_client, firehose_create_delivery_stream, s3_create_bucket, create_role_with_policy, @@ -1790,7 +1762,7 @@ def test_create_metric_stream( time.sleep(15) metric_stream_name = f"MyMetricStream-{short_uid()}" - response_create = aws_client.cloudwatch.put_metric_stream( + response_create = aws_cloudwatch_client.put_metric_stream( Name=metric_stream_name, FirehoseArn=stream_arn, RoleArn=role_arn, @@ -1802,32 +1774,32 @@ def test_create_metric_stream( snapshot.match("create_metric_stream", response_create) - get_response = aws_client.cloudwatch.get_metric_stream(Name=metric_stream_name) + get_response = aws_cloudwatch_client.get_metric_stream(Name=metric_stream_name) snapshot.match("get_metric_stream", get_response) - response_list = aws_client.cloudwatch.list_metric_streams() + response_list = aws_cloudwatch_client.list_metric_streams() metric_streams = response_list.get("Entries", []) metric_streams_names = [metric_stream["Name"] for metric_stream in metric_streams] assert metric_stream_name in metric_streams_names - start_response = aws_client.cloudwatch.start_metric_streams(Names=[metric_stream_name]) + start_response = aws_cloudwatch_client.start_metric_streams(Names=[metric_stream_name]) snapshot.match("start_metric_stream", start_response) - stop_response = aws_client.cloudwatch.stop_metric_streams(Names=[metric_stream_name]) + stop_response = aws_cloudwatch_client.stop_metric_streams(Names=[metric_stream_name]) snapshot.match("stop_metric_stream", stop_response) - response_delete = aws_client.cloudwatch.delete_metric_stream(Name=metric_stream_name) + response_delete = aws_cloudwatch_client.delete_metric_stream(Name=metric_stream_name) snapshot.match("delete_metric_stream", response_delete) - response_list = aws_client.cloudwatch.list_metric_streams() + response_list = aws_cloudwatch_client.list_metric_streams() metric_streams = response_list.get("Entries", []) metric_streams_names = [metric_stream["Name"] for metric_stream in metric_streams] assert metric_stream_name not in metric_streams_names @markers.aws.validated @pytest.mark.skipif(condition=not is_aws_cloud(), reason="Operations not supported") - def test_insight_rule(self, aws_client, snapshot): + def test_insight_rule(self, aws_cloudwatch_client, snapshot): insight_rule_name = f"MyInsightRule-{short_uid()}" - response_create = aws_client.cloudwatch.put_insight_rule( + response_create = aws_cloudwatch_client.put_insight_rule( RuleName=insight_rule_name, RuleState="ENABLED", RuleDefinition=json.dumps( @@ -1847,43 +1819,43 @@ def test_insight_rule(self, aws_client, snapshot): snapshot.add_transformer(snapshot.transform.key_value("Name")) snapshot.match("create_insight_rule", response_create) - response_describe = aws_client.cloudwatch.describe_insight_rules() + response_describe = aws_cloudwatch_client.describe_insight_rules() snapshot.match("describe_insight_rule", response_describe) - response_disable = aws_client.cloudwatch.disable_insight_rules( + response_disable = aws_cloudwatch_client.disable_insight_rules( RuleNames=[insight_rule_name] ) snapshot.match("disable_insight_rule", response_disable) - response_enable = aws_client.cloudwatch.enable_insight_rules(RuleNames=[insight_rule_name]) + response_enable = aws_cloudwatch_client.enable_insight_rules(RuleNames=[insight_rule_name]) snapshot.match("enable_insight_rule", response_enable) - insight_rule_report = aws_client.cloudwatch.get_insight_rule_report( + insight_rule_report = aws_cloudwatch_client.get_insight_rule_report( RuleName=insight_rule_name, - StartTime=datetime.utcnow() - timedelta(hours=1), - EndTime=datetime.utcnow(), + StartTime=datetime.now(tz=UTC) - timedelta(hours=1), + EndTime=datetime.now(tz=UTC), Period=300, MaxContributorCount=10, Metrics=["UniqueContributors"], ) snapshot.match("get_insight_rule_report", insight_rule_report) - response_list = aws_client.cloudwatch.describe_insight_rules() + response_list = aws_cloudwatch_client.describe_insight_rules() insight_rules_names = [ insight_rule["Name"] for insight_rule in response_list["InsightRules"] ] assert insight_rule_name in insight_rules_names - response_delete = aws_client.cloudwatch.delete_insight_rules(RuleNames=[insight_rule_name]) + response_delete = aws_cloudwatch_client.delete_insight_rules(RuleNames=[insight_rule_name]) snapshot.match("delete_insight_rule", response_delete) @markers.aws.validated @pytest.mark.skipif(condition=not is_aws_cloud(), reason="Operations not supported") - def test_anomaly_detector_lifecycle(self, aws_client, snapshot): + def test_anomaly_detector_lifecycle(self, aws_cloudwatch_client, snapshot): namespace = "MyNamespace" metric_name = "MyMetric" - response_create = aws_client.cloudwatch.put_anomaly_detector( + response_create = aws_cloudwatch_client.put_anomaly_detector( MetricName=metric_name, Namespace=namespace, Stat="Sum", @@ -1892,10 +1864,10 @@ def test_anomaly_detector_lifecycle(self, aws_client, snapshot): ) snapshot.match("create_anomaly_detector", response_create) - response_list = aws_client.cloudwatch.describe_anomaly_detectors() + response_list = aws_cloudwatch_client.describe_anomaly_detectors() snapshot.match("describe_anomaly_detector", response_list) - response_delete = aws_client.cloudwatch.delete_anomaly_detector( + response_delete = aws_cloudwatch_client.delete_anomaly_detector( MetricName=metric_name, Namespace=namespace, Stat="Sum", @@ -1905,16 +1877,16 @@ def test_anomaly_detector_lifecycle(self, aws_client, snapshot): @markers.aws.validated @pytest.mark.skipif(condition=not is_aws_cloud(), reason="Operations not supported") - def test_metric_widget(self, aws_client): + def test_metric_widget(self, aws_cloudwatch_client): metric_name = f"test-metric-{short_uid()}" namespace = f"ns-{short_uid()}" - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { "MetricName": metric_name, - "Timestamp": datetime.utcnow().replace(tzinfo=UTC), + "Timestamp": datetime.now(tz=UTC), "Values": [1.0, 10.0], "Counts": [2, 4], "Unit": "Count", @@ -1922,7 +1894,7 @@ def test_metric_widget(self, aws_client): ], ) - response = aws_client.cloudwatch.get_metric_widget_image( + response = aws_cloudwatch_client.get_metric_widget_image( MetricWidget=json.dumps( { "metrics": [ @@ -1948,14 +1920,14 @@ def test_metric_widget(self, aws_client): @markers.aws.validated @pytest.mark.skipif(is_old_provider(), reason="New test for v2 provider") - def test_describe_minimal_metric_alarm(self, snapshot, aws_client, cleanups): + def test_describe_minimal_metric_alarm(self, snapshot, aws_cloudwatch_client, cleanups): snapshot.add_transformer(snapshot.transform.cloudwatch_api()) alarm_name = f"a-{short_uid()}" metric_name = f"m-{short_uid()}" name_space = f"n-sp-{short_uid()}" snapshot.add_transformer(TransformerUtility.key_value("MetricName")) - aws_client.cloudwatch.put_metric_alarm( + aws_cloudwatch_client.put_metric_alarm( AlarmName=alarm_name, MetricName=metric_name, Namespace=name_space, @@ -1965,39 +1937,27 @@ def test_describe_minimal_metric_alarm(self, snapshot, aws_client, cleanups): ComparisonOperator="GreaterThanThreshold", Threshold=30, ) - cleanups.append(lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=[alarm_name])) - response = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + cleanups.append(lambda: aws_cloudwatch_client.delete_alarms(AlarmNames=[alarm_name])) + response = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) snapshot.match("describe_minimal_metric_alarm", response) @markers.aws.validated @pytest.mark.skipif(is_old_provider(), reason="New test for v2 provider") - def test_set_alarm_invalid_input(self, aws_client, snapshot, cleanups): + def test_set_alarm_invalid_input(self, aws_cloudwatch_client, snapshot): snapshot.add_transformer(snapshot.transform.cloudwatch_api()) alarm_name = f"a-{short_uid()}" - metric_name = f"m-{short_uid()}" - name_space = f"n-sp-{short_uid()}" snapshot.add_transformer(TransformerUtility.key_value("MetricName")) - aws_client.cloudwatch.put_metric_alarm( - AlarmName=alarm_name, - MetricName=metric_name, - Namespace=name_space, - EvaluationPeriods=1, - Period=10, - Statistic="Sum", - ComparisonOperator="GreaterThanThreshold", - Threshold=30, - ) - cleanups.append(lambda: aws_client.cloudwatch.delete_alarms(AlarmNames=[alarm_name])) - with pytest.raises(Exception) as ex: - aws_client.cloudwatch.set_alarm_state( + + with pytest.raises(ClientError) as ex: + aws_cloudwatch_client.set_alarm_state( AlarmName=alarm_name, StateValue="INVALID", StateReason="test" ) snapshot.match("error-invalid-state", ex.value.response) - with pytest.raises(Exception) as ex: - aws_client.cloudwatch.set_alarm_state( + with pytest.raises(ClientError) as ex: + aws_cloudwatch_client.set_alarm_state( AlarmName=f"{alarm_name}-nonexistent", StateValue="OK", StateReason="test" ) @@ -2005,13 +1965,13 @@ def test_set_alarm_invalid_input(self, aws_client, snapshot, cleanups): @markers.aws.validated @pytest.mark.skipif(is_old_provider(), reason="not supported by the old provider") - def test_get_metric_data_with_zero_and_labels(self, aws_client, snapshot): + def test_get_metric_data_with_zero_and_labels(self, aws_cloudwatch_client, snapshot): utc_now = datetime.now(tz=UTC) namespace1 = f"test/{short_uid()}" # put metric data values = [0, 2, 4, 3.5, 7, 100] - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace1, MetricData=[ {"MetricName": "metric1", "Value": val, "Unit": "Seconds"} for val in values @@ -2021,7 +1981,7 @@ def test_get_metric_data_with_zero_and_labels(self, aws_client, snapshot): stats = ["Average", "Sum", "Minimum", "Maximum"] def _get_metric_data(): - return aws_client.cloudwatch.get_metric_data( + return aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "result_" + stat, @@ -2048,13 +2008,13 @@ def _match_results(): @markers.aws.validated @markers.snapshot.skip_snapshot_verify(paths=["$..Datapoints..Unit"]) - def test_get_metric_statistics(self, aws_client, snapshot): + def test_get_metric_statistics(self, aws_cloudwatch_client, snapshot): snapshot.add_transformer(snapshot.transform.cloudwatch_api()) utc_now = datetime.now(tz=UTC) namespace = f"test/{short_uid()}" for i in range(10): - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -2066,7 +2026,7 @@ def test_get_metric_statistics(self, aws_client, snapshot): ) def assert_results(): - stats_responce = aws_client.cloudwatch.get_metric_statistics( + stats_responce = aws_cloudwatch_client.get_metric_statistics( Namespace=namespace, MetricName="metric", StartTime=utc_now - timedelta(seconds=60), @@ -2082,12 +2042,12 @@ def assert_results(): retry(assert_results, retries=10, sleep=1.0, sleep_before=sleep_before) @markers.aws.validated - def test_list_metrics_pagination(self, aws_client): + def test_list_metrics_pagination(self, aws_cloudwatch_client): namespace = f"n-sp-{short_uid()}" metric_name = f"m-{short_uid()}" max_metrics = 500 # max metrics per page according to AWS docs for i in range(0, max_metrics + 1): - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -2099,20 +2059,20 @@ def test_list_metrics_pagination(self, aws_client): ) def assert_metrics_count(): - response = aws_client.cloudwatch.list_metrics(Namespace=namespace) + response = aws_cloudwatch_client.list_metrics(Namespace=namespace) assert len(response["Metrics"]) == max_metrics and response.get("NextToken") is not None retry(assert_metrics_count, retries=10, sleep=1.0, sleep_before=1.0) @markers.aws.validated @pytest.mark.skipif(condition=is_old_provider(), reason="not supported by the old provider") - def test_get_metric_data_pagination(self, aws_client): + def test_get_metric_data_pagination(self, aws_cloudwatch_client): namespace = f"n-sp-{short_uid()}" metric_name = f"m-{short_uid()}" max_data_points = 10 # default is 100,800 according to AWS docs - now = datetime.utcnow().replace(tzinfo=UTC) + now = datetime.now(tz=UTC) for i in range(0, max_data_points * 2): - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -2125,7 +2085,7 @@ def test_get_metric_data_pagination(self, aws_client): ) def assert_data_points_count(): - response = aws_client.cloudwatch.get_metric_data( + response = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "m1", @@ -2150,14 +2110,14 @@ def assert_data_points_count(): retry(assert_data_points_count, retries=10, sleep=1.0, sleep_before=2.0) @markers.aws.validated - def test_put_metric_uses_utc(self, aws_client): + def test_put_metric_uses_utc(self, aws_cloudwatch_client): namespace = f"n-sp-{short_uid()}" metric_name = f"m-{short_uid()}" now_local = datetime.now(timezone(timedelta(hours=-5), "America/Cancun")).replace( tzinfo=None ) # Remove the tz info to avoid boto converting it to UTC - now_utc = datetime.utcnow() - aws_client.cloudwatch.put_metric_data( + now_utc = datetime.now(tz=UTC) + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -2169,7 +2129,7 @@ def test_put_metric_uses_utc(self, aws_client): ) def assert_found_in_utc(): - response = aws_client.cloudwatch.get_metric_statistics( + response = aws_cloudwatch_client.get_metric_statistics( Namespace=namespace, MetricName=metric_name, StartTime=now_local - timedelta(seconds=60), @@ -2179,7 +2139,7 @@ def assert_found_in_utc(): ) assert len(response["Datapoints"]) == 0 - response = aws_client.cloudwatch.get_metric_statistics( + response = aws_cloudwatch_client.get_metric_statistics( Namespace=namespace, MetricName=metric_name, StartTime=now_utc - timedelta(seconds=60), @@ -2192,12 +2152,12 @@ def assert_found_in_utc(): retry(assert_found_in_utc, retries=10, sleep=1.0) @markers.aws.validated - def test_default_ordering(self, aws_client): + def test_default_ordering(self, aws_cloudwatch_client): namespace = f"n-sp-{short_uid()}" metric_name = f"m-{short_uid()}" - now = datetime.utcnow().replace(tzinfo=UTC) + now = datetime.now(tz=UTC) for i in range(0, 10): - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -2210,7 +2170,7 @@ def test_default_ordering(self, aws_client): ) def assert_ordering(): - default_ordering = aws_client.cloudwatch.get_metric_data( + default_ordering = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "m1", @@ -2229,7 +2189,7 @@ def assert_ordering(): MaxDatapoints=10, ) - ascending_ordering = aws_client.cloudwatch.get_metric_data( + ascending_ordering = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "m1", @@ -2249,7 +2209,7 @@ def assert_ordering(): ScanBy="TimestampAscending", ) - descening_ordering = aws_client.cloudwatch.get_metric_data( + descening_ordering = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "m1", @@ -2281,11 +2241,11 @@ def assert_ordering(): @markers.aws.validated @pytest.mark.skipif(is_old_provider(), reason="not supported by the old provider") - def test_handle_different_units(self, aws_client, snapshot): + def test_handle_different_units(self, aws_cloudwatch_client, snapshot): namespace = f"n-sp-{short_uid()}" metric_name = "m-test" - now = datetime.utcnow().replace(tzinfo=UTC) - aws_client.cloudwatch.put_metric_data( + now = datetime.now(tz=UTC) + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -2309,7 +2269,7 @@ def test_handle_different_units(self, aws_client, snapshot): ) def assert_results(): - response = aws_client.cloudwatch.get_metric_statistics( + response = aws_cloudwatch_client.get_metric_statistics( Namespace=namespace, MetricName=metric_name, StartTime=now - timedelta(seconds=60), @@ -2326,11 +2286,11 @@ def assert_results(): retry(assert_results, retries=retries, sleep=1.0, sleep_before=sleep_before) @markers.aws.validated - def test_get_metric_data_with_different_units(self, aws_client, snapshot): + def test_get_metric_data_with_different_units(self, aws_cloudwatch_client, snapshot): namespace = f"n-sp-{short_uid()}" metric_name = "m-test" - now = datetime.utcnow().replace(tzinfo=UTC) - aws_client.cloudwatch.put_metric_data( + now = datetime.now(tz=UTC) + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -2349,7 +2309,7 @@ def test_get_metric_data_with_different_units(self, aws_client, snapshot): ) def assert_results(): - response = aws_client.cloudwatch.get_metric_data( + response = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "m1", @@ -2404,9 +2364,12 @@ def assert_results(): ], ) @markers.aws.needs_fixing - @pytest.mark.skip(reason="Not supported in either provider, needs to be fixed in new one") + @pytest.mark.skipif( + condition=not is_aws_cloud(), + reason="Not supported in either provider, needs to be fixed in new one", + ) def test_get_metric_data_different_units_no_unit_in_query( - self, aws_client, snapshot, metric_data + self, aws_cloudwatch_client, snapshot, metric_data ): # From the docs: """ @@ -2427,15 +2390,15 @@ def test_get_metric_data_different_units_no_unit_in_query( namespace = f"n-sp-{short_uid()}" metric_name = "m-test" - now = datetime.utcnow().replace(tzinfo=UTC) + now = datetime.now(tz=UTC) for m in metric_data: m["MetricName"] = metric_name m["Timestamp"] = now - aws_client.cloudwatch.put_metric_data(Namespace=namespace, MetricData=metric_data) + aws_cloudwatch_client.put_metric_data(Namespace=namespace, MetricData=metric_data) def assert_results(): - response = aws_client.cloudwatch.get_metric_data( + response = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "m1", @@ -2473,14 +2436,14 @@ def assert_results(): ) @markers.aws.validated @pytest.mark.skipif(is_old_provider(), reason="not supported by the old provider") - def test_label_generation(self, aws_client, snapshot, input_pairs): + def test_label_generation(self, aws_cloudwatch_client, snapshot, input_pairs): # Whenever values differ for a statistic type or period, that value is added to the label utc_now = datetime.now(tz=UTC) namespace1 = f"test/{short_uid()}" # put metric data values = [0, 2, 7, 100] - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace1, MetricData=[ {"MetricName": "metric1", "Value": val, "Unit": "Seconds"} for val in values @@ -2490,7 +2453,7 @@ def test_label_generation(self, aws_client, snapshot, input_pairs): # get_metric_data def _get_metric_data(): - return aws_client.cloudwatch.get_metric_data( + return aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": f"result_{stat}_{str(period)}_{unit}", @@ -2510,16 +2473,16 @@ def _get_metric_data(): def _match_results(): response = _get_metric_data() # keep one assert to avoid storing incorrect values - sum = [ + _sum = [ res for res in response["MetricDataResults"] if res["Id"].startswith("result_Sum") ][0] - assert [int(val) for val in sum["Values"]] == [109] + assert [int(val) for val in _sum["Values"]] == [109] snapshot.match("label_generation", response) retry(_match_results, retries=10, sleep=1.0) @markers.aws.validated - def test_get_metric_with_null_dimensions(self, aws_client, snapshot): + def test_get_metric_with_null_dimensions(self, aws_cloudwatch_client, snapshot): """ This test validates the behaviour when there is metric data with dimensions and the get_metric_data call has no dimensions specified. The expected behaviour is that the call should return the metric data with @@ -2530,7 +2493,7 @@ def test_get_metric_with_null_dimensions(self, aws_client, snapshot): snapshot.add_transformer(snapshot.transform.key_value("Label")) namespace = f"n-{short_uid()}" metric_name = "m-test" - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -2548,7 +2511,7 @@ def test_get_metric_with_null_dimensions(self, aws_client, snapshot): ) def assert_results(): - response = aws_client.cloudwatch.get_metric_data( + response = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "m1", @@ -2563,8 +2526,8 @@ def assert_results(): }, } ], - StartTime=datetime.utcnow() - timedelta(hours=1), - EndTime=datetime.utcnow(), + StartTime=datetime.now(tz=UTC) - timedelta(hours=1), + EndTime=datetime.now(tz=UTC), ) assert len(response["MetricDataResults"][0]["Values"]) == 0 snapshot.match("get_metric_with_null_dimensions", response) @@ -2572,6 +2535,8 @@ def assert_results(): retry(assert_results, retries=10, sleep=1.0, sleep_before=2 if is_aws_cloud() else 0.0) @markers.aws.validated + # we're not testing multi protocols for this test because we're not snapshotting any CloudWatch response + # and are only testing Lambda logic def test_alarm_lambda_target( self, aws_client, create_lambda_function, cleanups, account_id, snapshot ): @@ -2639,12 +2604,12 @@ def log_group_exists(): snapshot.match("lambda-alarm-invocations", invocation_res) @markers.aws.validated - def test_get_metric_with_no_results(self, snapshot, aws_client): + def test_get_metric_with_no_results(self, snapshot, aws_cloudwatch_client): utc_now = datetime.now(tz=UTC) namespace = f"n-{short_uid()}" metric = f"m-{short_uid()}" - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -2655,14 +2620,14 @@ def test_get_metric_with_no_results(self, snapshot, aws_client): ) def assert_metric_ready(): - list_of_metrics = aws_client.cloudwatch.list_metrics( + list_of_metrics = aws_cloudwatch_client.list_metrics( Namespace=namespace, MetricName=metric ) assert len(list_of_metrics["Metrics"]) == 1 retry(assert_metric_ready, sleep=1, retries=10) - data = aws_client.cloudwatch.get_metric_data( + data = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "result", @@ -2691,6 +2656,7 @@ def assert_metric_ready(): @markers.aws.only_localstack @pytest.mark.skipif(is_old_provider(), reason="old provider has known concurrency issues") # test some basic concurrency tasks + # this test is not enabled for multi-protocols as there is nothing to gain from it def test_parallel_put_metric_data_list_metrics(self, aws_client): num_threads = 20 create_barrier = threading.Barrier(num_threads) @@ -2720,7 +2686,7 @@ def _put_metric_get_metric_data(runner: int): ], ) else: - now = datetime.utcnow().replace(microsecond=0) + now = datetime.now(tz=UTC).replace(microsecond=0) start_time = now - timedelta(minutes=10) end_time = now + timedelta(minutes=5) aws_client.cloudwatch.get_metric_data( @@ -2776,11 +2742,11 @@ def _put_metric_get_metric_data(runner: int): "$..describe-alarm.MetricAlarms..StateTransitionedTimestamp", ], ) - def test_delete_alarm(self, aws_client, snapshot): + def test_delete_alarm(self, aws_cloudwatch_client, snapshot): snapshot.add_transformer(snapshot.transform.cloudwatch_api()) alarm_name = "test-alarm" - aws_client.cloudwatch.put_metric_alarm( + aws_cloudwatch_client.put_metric_alarm( AlarmName="test-alarm", Namespace=f"my-namespace-{short_uid()}", MetricName="metric1", @@ -2790,13 +2756,13 @@ def test_delete_alarm(self, aws_client, snapshot): Statistic="Sum", Threshold=30, ) - result = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + result = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) snapshot.match("describe-alarm", result) - delete_result = aws_client.cloudwatch.delete_alarms(AlarmNames=[alarm_name]) + delete_result = aws_cloudwatch_client.delete_alarms(AlarmNames=[alarm_name]) snapshot.match("delete-alarm", delete_result) - describe_alarm = aws_client.cloudwatch.describe_alarms(AlarmNames=[alarm_name]) + describe_alarm = aws_cloudwatch_client.describe_alarms(AlarmNames=[alarm_name]) snapshot.match("describe-after-delete", describe_alarm) @markers.aws.validated @@ -2806,7 +2772,7 @@ def test_delete_alarm(self, aws_client, snapshot): "$..list-metrics..Metrics", ], ) - def test_multiple_dimensions_statistics(self, aws_client, snapshot): + def test_multiple_dimensions_statistics(self, aws_cloudwatch_client, snapshot): snapshot.add_transformer(snapshot.transform.cloudwatch_api()) utc_now = datetime.now(tz=UTC) @@ -2820,7 +2786,7 @@ def test_multiple_dimensions_statistics(self, aws_client, snapshot): {"Name": "uri", "Value": "/greetings"}, {"Name": "status", "Value": "200"}, ] - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -2833,7 +2799,7 @@ def test_multiple_dimensions_statistics(self, aws_client, snapshot): } ], ) - aws_client.cloudwatch.put_metric_data( + aws_cloudwatch_client.put_metric_data( Namespace=namespace, MetricData=[ { @@ -2848,7 +2814,7 @@ def test_multiple_dimensions_statistics(self, aws_client, snapshot): ) def assert_results(): - response = aws_client.cloudwatch.get_metric_data( + response = aws_cloudwatch_client.get_metric_data( MetricDataQueries=[ { "Id": "result1", @@ -2876,7 +2842,7 @@ def assert_results(): retry(assert_results, retries=retries, sleep_before=sleep_before) def list_metrics(): - res = aws_client.cloudwatch.list_metrics( + res = aws_cloudwatch_client.list_metrics( Namespace=namespace, MetricName=metric_name, Dimensions=dimensions ) assert len(res["Metrics"]) > 0 @@ -2896,11 +2862,11 @@ def sort_dimensions(data: dict): @markers.aws.validated @pytest.mark.skipif(is_old_provider(), reason="New test for v2 provider") - def test_invalid_amount_of_datapoints(self, aws_client, snapshot): + def test_invalid_amount_of_datapoints(self, aws_cloudwatch_client, snapshot): snapshot.add_transformer(snapshot.transform.cloudwatch_api()) utc_now = datetime.now(tz=UTC) with pytest.raises(ClientError) as ex: - aws_client.cloudwatch.get_metric_statistics( + aws_cloudwatch_client.get_metric_statistics( Namespace="namespace", MetricName="metric_name", StartTime=utc_now, @@ -2911,7 +2877,7 @@ def test_invalid_amount_of_datapoints(self, aws_client, snapshot): snapshot.match("error-invalid-amount-datapoints", ex.value.response) with pytest.raises(ClientError) as ex: - aws_client.cloudwatch.get_metric_statistics( + aws_cloudwatch_client.get_metric_statistics( Namespace="namespace", MetricName="metric_name", StartTime=utc_now, @@ -2922,7 +2888,7 @@ def test_invalid_amount_of_datapoints(self, aws_client, snapshot): snapshot.match("error-invalid-time-frame", ex.value.response) - response = aws_client.cloudwatch.get_metric_statistics( + response = aws_cloudwatch_client.get_metric_statistics( Namespace=f"namespace_{short_uid()}", MetricName="metric_name", StartTime=utc_now, @@ -2934,6 +2900,179 @@ def test_invalid_amount_of_datapoints(self, aws_client, snapshot): snapshot.match("get-metric-statitics", response) +class TestCloudWatchMultiProtocol: + @pytest.fixture + def cloudwatch_http_client(self, region_name, aws_http_client_factory): + def _create_client(protocol: str): + return get_cloudwatch_client( + client_factory=aws_http_client_factory, region=region_name, protocol=protocol + ) + + return _create_client + + @markers.aws.validated + def test_multi_protocol_client_fixture(self, aws_cloudwatch_client): + """ + Smoke test to validate that the client is indeed using the right protocol + """ + response = aws_cloudwatch_client.describe_alarms() + response_headers = response["ResponseMetadata"]["HTTPHeaders"] + content_type = response_headers["content-type"] + if aws_cloudwatch_client.test_client_protocol == "query": + assert content_type in ("text/xml", "application/xml") + elif aws_cloudwatch_client.test_client_protocol == "json": + assert content_type == "application/x-amz-json-1.0" + elif aws_cloudwatch_client.test_client_protocol == "smithy-rpc-v2-cbor": + assert content_type == "application/cbor" + assert response_headers["smithy-protocol"] == "rpc-v2-cbor" + + @markers.aws.validated + @pytest.mark.parametrize("protocol", ["json", "smithy-rpc-v2-cbor", "query"]) + @markers.snapshot.skip_snapshot_verify( + paths=[ + # LogAlarms is not defined in the Boto specs anywhere, but is returned in raw responses (deprecated?) + "$.describe-alarms..LogAlarms", + ] + ) + def test_basic_operations_multiple_protocols( + self, cloudwatch_http_client, aws_client, snapshot, protocol + ): + if is_old_provider() and protocol != "query": + pytest.skip( + "Skipping as Moto does not support any other protocol than `query` for CloudWatch for now" + ) + snapshot.add_transformer(snapshot.transform.key_value("Label")) + http_client = cloudwatch_http_client(protocol) + response = http_client.post( + "DescribeAlarms", + payload={}, + ) + snapshot.match("describe-alarms", response) + + namespace1 = f"test/{short_uid()}" + namespace2 = f"test/{short_uid()}" + now = datetime.now(tz=UTC).replace(microsecond=0) + start_time = now - timedelta(minutes=1) + end_time = now + timedelta(minutes=5) + + parameters = [ + { + "Namespace": namespace1, + "MetricData": [{"MetricName": "someMetric", "Value": 23}], + }, + { + "Namespace": namespace1, + "MetricData": [{"MetricName": "someMetric", "Value": 18}], + }, + { + "Namespace": namespace2, + "MetricData": [{"MetricName": "ug", "Value": 23, "Timestamp": now}], + }, + ] + for index, input_values in enumerate(parameters): + response = http_client.post_raw( + operation="PutMetricData", + payload=input_values, + ) + assert response.status_code == 200 + + get_metric_input = { + "MetricDataQueries": [ + { + "Id": "some", + "MetricStat": { + "Metric": { + "Namespace": namespace1, + "MetricName": "someMetric", + }, + "Period": 60, + "Stat": "Sum", + }, + }, + { + "Id": "part", + "MetricStat": { + "Metric": {"Namespace": namespace2, "MetricName": "ug"}, + "Period": 60, + "Stat": "Sum", + }, + }, + ], + "StartTime": start_time, + "EndTime": end_time, + } + + def _get_metric_data_sum(): + # we can use the default AWS Client here, it is for the retries + _response = aws_client.cloudwatch.get_metric_data(**get_metric_input) + assert len(_response["MetricDataResults"]) == 2 + + for _data_metric in _response["MetricDataResults"]: + # TODO: there's an issue in the implementation of the service here. + # The returned timestamps should have the seconds set to 0 + if _data_metric["Id"] == "some": + assert sum(_data_metric["Values"]) == 41.0 + if _data_metric["Id"] == "part": + assert 23.0 == sum(_data_metric["Values"]) == 23.0 + + # need to retry because the might most likely not be ingested immediately (it's fairly quick though) + retry(_get_metric_data_sum, retries=10, sleep_before=2) + + response = http_client.post( + operation="GetMetricData", + payload=get_metric_input, + ) + snapshot.match("get-metric-data", response) + + @markers.aws.validated + @pytest.mark.skipif(is_old_provider(), reason="Wrong behavior in v1 in SetAlarmState") + @pytest.mark.parametrize("protocol", ["json", "smithy-rpc-v2-cbor", "query"]) + def test_exception_serializing_with_no_shape_in_spec( + self, cloudwatch_http_client, snapshot, protocol + ): + alarm_name = f"a-{short_uid()}" + http_client = cloudwatch_http_client(protocol) + + # ValidationError is not defined in Botocore specs + invalid_value_response = http_client.post( + "SetAlarmState", + payload={ + "AlarmName": alarm_name, + "StateValue": "INVALID", + "StateReason": "test", + }, + status_code=400, + ) + snapshot.match("invalid-value-response", invalid_value_response) + + # RPC v2, by default, should always return 400. It only returns a different status code if Query Mode is enabled + status_code = 404 if protocol == "query" else 400 + + not_found_response = http_client.post( + "SetAlarmState", + payload={ + "AlarmName": alarm_name, + "StateValue": "OK", + "StateReason": "test", + }, + status_code=status_code, + ) + snapshot.match("not-found-response", not_found_response) + + # if the client sends `x-amzn-query-mode: true`, AWS responds with a different status code for RPC v2 + not_found_query_mode = http_client.post( + "SetAlarmState", + payload={ + "AlarmName": alarm_name, + "StateValue": "OK", + "StateReason": "test", + }, + status_code=404, + query_mode=True, + ) + snapshot.match("not-found-response-query-mode-true", not_found_query_mode) + + def _get_lambda_logs(logs_client: "CloudWatchLogsClient", fn_name: str): log_events = logs_client.filter_log_events(logGroupName=f"/aws/lambda/{fn_name}")["events"] filtered_logs = [event for event in log_events if event["message"].startswith("{")] diff --git a/tests/aws/services/cloudwatch/test_cloudwatch.snapshot.json b/tests/aws/services/cloudwatch/test_cloudwatch.snapshot.json index 87abfc826b4a8..93b4c21669080 100644 --- a/tests/aws/services/cloudwatch/test_cloudwatch.snapshot.json +++ b/tests/aws/services/cloudwatch/test_cloudwatch.snapshot.json @@ -1,255 +1,4965 @@ { - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_enable_disable_alarm_actions": { - "recorded-date": "12-09-2023, 12:00:45", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_alarm_lambda_target": { + "recorded-date": "03-01-2024, 17:30:00", + "recorded-content": { + "lambda-alarm-invocations": { + "source": "aws.cloudwatch", + "alarmArn": "arn::cloudwatch::111111111111:alarm:", + "accountId": "111111111111", + "time": "date", + "region": "", + "alarmData": { + "alarmName": "", + "state": { + "value": "ALARM", + "reason": "testing alarm", + "timestamp": "date" + }, + "previousState": { + "value": "INSUFFICIENT_DATA", + "reason": "Unchecked: Initial alarm creation", + "timestamp": "date" + }, + "configuration": { + "description": "testing lambda alarm action", + "metrics": [ + { + "id": "", + "metricStat": { + "metric": { + "namespace": "namespace", + "name": "metric1", + "dimensions": {} + }, + "period": 10, + "stat": "Average" + }, + "returnData": true + } + ] + } + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_basic_operations_multiple_protocols[json]": { + "recorded-date": "18-09-2025, 13:56:03", + "recorded-content": { + "describe-alarms": { + "CompositeAlarms": [], + "LogAlarms": [], + "MetricAlarms": [] + }, + "get-metric-data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "some", + "Label": "", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 41.0 + ] + }, + { + "Id": "part", + "Label": "", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 23.0 + ] + } + ] + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_basic_operations_multiple_protocols[smithy-rpc-v2-cbor]": { + "recorded-date": "18-09-2025, 13:56:06", + "recorded-content": { + "describe-alarms": { + "CompositeAlarms": [], + "LogAlarms": [], + "MetricAlarms": [] + }, + "get-metric-data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "some", + "Label": "", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 41.0 + ] + }, + { + "Id": "part", + "Label": "", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 23.0 + ] + } + ] + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_basic_operations_multiple_protocols[query]": { + "recorded-date": "18-09-2025, 13:56:09", + "recorded-content": { + "describe-alarms": { + "DescribeAlarmsResponse": { + "@xmlns": "http://monitoring.amazonaws.com/doc/2010-08-01/", + "DescribeAlarmsResult": { + "CompositeAlarms": null, + "LogAlarms": null, + "MetricAlarms": null + } + } + }, + "get-metric-data": { + "GetMetricDataResponse": { + "@xmlns": "http://monitoring.amazonaws.com/doc/2010-08-01/", + "GetMetricDataResult": { + "Messages": null, + "MetricDataResults": { + "member": [ + { + "Id": "some", + "Label": "", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": { + "member": "41.0" + } + }, + { + "Id": "part", + "Label": "", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": { + "member": "23.0" + } + } + ] + } + } + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_exception_serializing_with_no_shape_in_spec[json]": { + "recorded-date": "22-09-2025, 19:17:52", + "recorded-content": { + "invalid-value-response": { + "__type": "com.amazon.coral.validate#ValidationException", + "message": "1 validation error detected: Value 'INVALID' at 'stateValue' failed to satisfy constraint: Member must satisfy enum value set: [INSUFFICIENT_DATA, ALARM, OK]" + }, + "not-found-response": { + "__type": "com.amazonaws.cloudwatch.v2010_08_01#ResourceNotFound" + }, + "not-found-response-query-mode-true": { + "__type": "com.amazonaws.cloudwatch.v2010_08_01#ResourceNotFound" + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_exception_serializing_with_no_shape_in_spec[smithy-rpc-v2-cbor]": { + "recorded-date": "22-09-2025, 19:17:53", + "recorded-content": { + "invalid-value-response": { + "__type": "com.amazon.coral.validate#ValidationException", + "message": "1 validation error detected: Value 'INVALID' at 'stateValue' failed to satisfy constraint: Member must satisfy enum value set: [INSUFFICIENT_DATA, ALARM, OK]" + }, + "not-found-response": { + "__type": "com.amazonaws.cloudwatch.v2010_08_01#ResourceNotFound" + }, + "not-found-response-query-mode-true": { + "__type": "com.amazonaws.cloudwatch.v2010_08_01#ResourceNotFound" + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_exception_serializing_with_no_shape_in_spec[query]": { + "recorded-date": "22-09-2025, 19:17:54", + "recorded-content": { + "invalid-value-response": { + "ErrorResponse": { + "@xmlns": "http://monitoring.amazonaws.com/doc/2010-08-01/", + "Error": { + "Code": "ValidationError", + "Message": "1 validation error detected: Value 'INVALID' at 'stateValue' failed to satisfy constraint: Member must satisfy enum value set: [INSUFFICIENT_DATA, ALARM, OK]", + "Type": "Sender" + }, + "RequestId": "" + } + }, + "not-found-response": { + "ErrorResponse": { + "@xmlns": "http://monitoring.amazonaws.com/doc/2010-08-01/", + "Error": { + "Code": "ResourceNotFound", + "Type": "Sender" + }, + "RequestId": "" + } + }, + "not-found-response-query-mode-true": { + "ErrorResponse": { + "@xmlns": "http://monitoring.amazonaws.com/doc/2010-08-01/", + "Error": { + "Code": "ResourceNotFound", + "Type": "Sender" + }, + "RequestId": "" + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_values_list[query]": { + "recorded-date": "19-09-2025, 17:40:30", + "recorded-content": { + "get_metric_statistics": { + "Datapoints": [ + { + "Maximum": 10.0, + "SampleCount": 6.0, + "Sum": 42.0, + "Timestamp": "timestamp", + "Unit": "Count" + } + ], + "Label": "test-metric", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_values_list[json]": { + "recorded-date": "19-09-2025, 17:40:31", + "recorded-content": { + "get_metric_statistics": { + "Datapoints": [ + { + "Maximum": 10.0, + "SampleCount": 6.0, + "Sum": 42.0, + "Timestamp": "timestamp", + "Unit": "Count" + } + ], + "Label": "test-metric", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_values_list[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 17:40:32", + "recorded-content": { + "get_metric_statistics": { + "Datapoints": [ + { + "Maximum": 10.0, + "SampleCount": 6.0, + "Sum": 42.0, + "Timestamp": "timestamp", + "Unit": "Count" + } + ], + "Label": "test-metric", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_validation[query]": { + "recorded-date": "19-09-2025, 18:08:03", + "recorded-content": { + "invalid-param-combination": { + "Error": { + "Code": "InvalidParameterCombination", + "Message": "The parameters MetricData.member.1.Value and MetricData.member.1.Values are mutually exclusive and you have specified both.", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "invalid-param-value": { + "Error": { + "Code": "InvalidParameterValue", + "Message": "The parameters MetricData.member.1.Values and MetricData.member.1.Counts must be of the same size.", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "invalid-param-combination-2": { + "Error": { + "Code": "InvalidParameterCombination", + "Message": "The parameters MetricData.member.1.Value and MetricData.member.1.StatisticValues are mutually exclusive and you have specified both.", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_validation[json]": { + "recorded-date": "19-09-2025, 18:08:04", + "recorded-content": { + "invalid-param-combination": { + "Error": { + "Code": "InvalidParameterCombination", + "Message": "The parameters MetricData.member.1.Value and MetricData.member.1.Values are mutually exclusive and you have specified both.", + "QueryErrorCode": "InvalidParameterCombinationException", + "Type": "Sender" + }, + "message": "The parameters MetricData.member.1.Value and MetricData.member.1.Values are mutually exclusive and you have specified both.", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "invalid-param-value": { + "Error": { + "Code": "InvalidParameterValue", + "Message": "The parameters MetricData.member.1.Values and MetricData.member.1.Counts must be of the same size.", + "QueryErrorCode": "InvalidParameterValueException", + "Type": "Sender" + }, + "message": "The parameters MetricData.member.1.Values and MetricData.member.1.Counts must be of the same size.", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "invalid-param-combination-2": { + "Error": { + "Code": "InvalidParameterCombination", + "Message": "The parameters MetricData.member.1.Value and MetricData.member.1.StatisticValues are mutually exclusive and you have specified both.", + "QueryErrorCode": "InvalidParameterCombinationException", + "Type": "Sender" + }, + "message": "The parameters MetricData.member.1.Value and MetricData.member.1.StatisticValues are mutually exclusive and you have specified both.", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_validation[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 18:08:05", + "recorded-content": { + "invalid-param-combination": { + "Error": { + "Code": "InvalidParameterCombination", + "Message": "The parameters MetricData.member.1.Value and MetricData.member.1.Values are mutually exclusive and you have specified both.", + "QueryErrorCode": "InvalidParameterCombinationException", + "Type": "Sender" + }, + "message": "The parameters MetricData.member.1.Value and MetricData.member.1.Values are mutually exclusive and you have specified both.", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "invalid-param-value": { + "Error": { + "Code": "InvalidParameterValue", + "Message": "The parameters MetricData.member.1.Values and MetricData.member.1.Counts must be of the same size.", + "QueryErrorCode": "InvalidParameterValueException", + "Type": "Sender" + }, + "message": "The parameters MetricData.member.1.Values and MetricData.member.1.Counts must be of the same size.", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "invalid-param-combination-2": { + "Error": { + "Code": "InvalidParameterCombination", + "Message": "The parameters MetricData.member.1.Value and MetricData.member.1.StatisticValues are mutually exclusive and you have specified both.", + "QueryErrorCode": "InvalidParameterCombinationException", + "Type": "Sender" + }, + "message": "The parameters MetricData.member.1.Value and MetricData.member.1.StatisticValues are mutually exclusive and you have specified both.", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_for_multiple_metrics[query]": { + "recorded-date": "19-09-2025, 20:29:17", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 50.0 + ] + }, + { + "Id": "result2", + "Label": "metric2", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 25.0 + ] + }, + { + "Id": "result3", + "Label": "metric3", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 55.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_for_multiple_metrics[json]": { + "recorded-date": "19-09-2025, 20:29:18", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 50.0 + ] + }, + { + "Id": "result2", + "Label": "metric2", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 25.0 + ] + }, + { + "Id": "result3", + "Label": "metric3", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 55.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_for_multiple_metrics[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 20:29:20", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 50.0 + ] + }, + { + "Id": "result2", + "Label": "metric2", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 25.0 + ] + }, + { + "Id": "result3", + "Label": "metric3", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 55.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[query-Sum]": { + "recorded-date": "19-09-2025, 20:31:51", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 66.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[query-SampleCount]": { + "recorded-date": "19-09-2025, 20:31:54", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 11.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[query-Minimum]": { + "recorded-date": "19-09-2025, 20:31:56", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[query-Maximum]": { + "recorded-date": "19-09-2025, 20:31:59", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 11.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[query-Average]": { + "recorded-date": "19-09-2025, 20:32:03", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 6.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[json-Sum]": { + "recorded-date": "19-09-2025, 20:32:05", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 66.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[json-SampleCount]": { + "recorded-date": "19-09-2025, 20:32:08", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 11.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[json-Minimum]": { + "recorded-date": "19-09-2025, 20:32:10", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[json-Maximum]": { + "recorded-date": "19-09-2025, 20:32:13", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 11.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[json-Average]": { + "recorded-date": "19-09-2025, 20:32:15", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 6.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[smithy-rpc-v2-cbor-Sum]": { + "recorded-date": "19-09-2025, 20:32:18", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 66.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[smithy-rpc-v2-cbor-SampleCount]": { + "recorded-date": "19-09-2025, 20:32:20", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 11.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[smithy-rpc-v2-cbor-Minimum]": { + "recorded-date": "19-09-2025, 20:32:22", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[smithy-rpc-v2-cbor-Maximum]": { + "recorded-date": "19-09-2025, 20:32:25", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 11.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[smithy-rpc-v2-cbor-Average]": { + "recorded-date": "19-09-2025, 20:32:27", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 6.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_dimensions[query]": { + "recorded-date": "19-09-2025, 20:33:39", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 11.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_dimensions[json]": { + "recorded-date": "19-09-2025, 20:33:41", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 11.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_dimensions[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 20:33:44", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result1", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 11.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_alarms_converts_date_format_correctly[query]": { + "recorded-date": "19-09-2025, 20:38:27", + "recorded-content": { + "describe-alarms": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "", + "Namespace": "", + "OKActions": [], + "Period": 60, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 30.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_alarms_converts_date_format_correctly[json]": { + "recorded-date": "19-09-2025, 20:38:27", + "recorded-content": { + "describe-alarms": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "", + "Namespace": "", + "OKActions": [], + "Period": 60, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 30.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_alarms_converts_date_format_correctly[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 20:38:28", + "recorded-content": { + "describe-alarms": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "", + "Namespace": "", + "OKActions": [], + "Period": 60, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 30.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_store_tags[query]": { + "recorded-date": "19-09-2025, 20:42:24", + "recorded-content": { + "put_metric_alarm": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_alarms": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "store_tags", + "Namespace": "", + "OKActions": [], + "Period": 60, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 30.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_tags_for_resource_empty ": { + "Tags": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_tags_for_resource": { + "Tags": [ + { + "Key": "tag1", + "Value": "foo" + }, + { + "Key": "tag2", + "Value": "bar" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_tags_for_resource_post_untag": { + "Tags": [ + { + "Key": "tag2", + "Value": "bar" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_store_tags[json]": { + "recorded-date": "19-09-2025, 20:42:26", + "recorded-content": { + "put_metric_alarm": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_alarms": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "store_tags", + "Namespace": "", + "OKActions": [], + "Period": 60, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 30.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_tags_for_resource_empty ": { + "Tags": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_tags_for_resource": { + "Tags": [ + { + "Key": "tag1", + "Value": "foo" + }, + { + "Key": "tag2", + "Value": "bar" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_tags_for_resource_post_untag": { + "Tags": [ + { + "Key": "tag2", + "Value": "bar" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_store_tags[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 20:42:27", + "recorded-content": { + "put_metric_alarm": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_alarms": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "store_tags", + "Namespace": "", + "OKActions": [], + "Period": 60, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 30.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_tags_for_resource_empty ": { + "Tags": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_tags_for_resource": { + "Tags": [ + { + "Key": "tag1", + "Value": "foo" + }, + { + "Key": "tag2", + "Value": "bar" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_tags_for_resource_post_untag": { + "Tags": [ + { + "Key": "tag2", + "Value": "bar" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm_escape_character[query]": { + "recorded-date": "24-09-2025, 15:16:44", + "recorded-content": { + "describe-alarms-escaped-character": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111122223333:MyTopic" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "<", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "", + "Namespace": "", + "OKActions": [], + "Period": 600, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 1.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm_escape_character[json]": { + "recorded-date": "24-09-2025, 15:16:45", + "recorded-content": { + "describe-alarms-escaped-character": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111122223333:MyTopic" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "<", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "", + "Namespace": "", + "OKActions": [], + "Period": 600, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 1.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm_escape_character[smithy-rpc-v2-cbor]": { + "recorded-date": "24-09-2025, 15:16:46", + "recorded-content": { + "describe-alarms-escaped-character": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111122223333:MyTopic" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "<", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "", + "Namespace": "", + "OKActions": [], + "Period": 600, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 1.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_trigger_composite_alarm[query]": { + "recorded-date": "19-09-2025, 21:03:22", + "recorded-content": { + "put-composite-alarm": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "composite-alarm-in-alarm-when-alarm-1-is-in-alarm": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "ALARM", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM" + }, + "composite-alarm-in-ok-when-alarm-1-is-back-to-ok": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "OK", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK" + }, + "composite-alarm-in-alarm-when-alarm-2-is-in-alarm": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "ALARM", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM" + }, + "composite-alarm-in-ok-when-alarm-2-is-back-to-ok": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "OK", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK" + }, + "composite-alarm-is-triggered-by-alarm-1-and-then-unchanged-by-alarm-2": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "ALARM", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM" + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_trigger_composite_alarm[json]": { + "recorded-date": "19-09-2025, 21:03:34", + "recorded-content": { + "put-composite-alarm": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "composite-alarm-in-alarm-when-alarm-1-is-in-alarm": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "ALARM", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM" + }, + "composite-alarm-in-ok-when-alarm-1-is-back-to-ok": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "OK", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK" + }, + "composite-alarm-in-alarm-when-alarm-2-is-in-alarm": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "ALARM", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM" + }, + "composite-alarm-in-ok-when-alarm-2-is-back-to-ok": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "OK", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK" + }, + "composite-alarm-is-triggered-by-alarm-1-and-then-unchanged-by-alarm-2": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "ALARM", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM" + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_trigger_composite_alarm[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 21:03:49", + "recorded-content": { + "put-composite-alarm": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "composite-alarm-in-alarm-when-alarm-1-is-in-alarm": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "ALARM", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM" + }, + "composite-alarm-in-ok-when-alarm-1-is-back-to-ok": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "OK", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK" + }, + "composite-alarm-in-alarm-when-alarm-2-is-in-alarm": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "ALARM", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM" + }, + "composite-alarm-in-ok-when-alarm-2-is-back-to-ok": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "OK", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK" + }, + "composite-alarm-is-triggered-by-alarm-1-and-then-unchanged-by-alarm-2": { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "composite alarm description", + "AlarmName": "", + "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", + "InsufficientDataActions": [], + "OKActions": [ + "arn::sns::111111111111:" + ], + "StateReason": "", + "StateReasonData": { + "triggeringAlarms": [ + { + "arn": "arn::cloudwatch::111111111111:alarm:", + "state": { + "value": "ALARM", + "timestamp": "date" + } + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM" + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm[query]": { + "recorded-date": "19-09-2025, 21:08:24", + "recorded-content": { + "describe-alarm": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "my-metric1", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Average", + "Threshold": 21.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "alarm-triggered-sqs-msg": { + "AWSAccountId": "111111111111", + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "date", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "InsufficientDataActions": [], + "NewStateReason": "Threshold Crossed: 1 datapoint [21.5 (MM/DD/YY HH:MM:SS)] was greater than the threshold (21.0).", + "NewStateValue": "ALARM", + "OKActions": [ + "arn::sns::111111111111:" + ], + "OldStateValue": "INSUFFICIENT_DATA", + "Region": "", + "StateChangeTime": "date", + "Trigger": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "name": "InstanceId", + "value": "abc" + } + ], + "EvaluateLowSampleCountPercentile": "", + "EvaluationPeriods": 1, + "MetricName": "my-metric1", + "Namespace": "", + "Period": 10, + "Statistic": "AVERAGE", + "StatisticType": "Statistic", + "Threshold": 21.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + }, + "describe-alarm-history": { + "AlarmHistoryItems": [ + { + "AlarmName": "", + "AlarmType": "MetricAlarm", + "HistoryData": { + "version": "1.0", + "oldState": { + "stateValue": "INSUFFICIENT_DATA", + "stateReason": "Unchecked: Initial alarm creation" + }, + "newState": { + "stateValue": "ALARM", + "stateReason": "Threshold Crossed: 1 datapoint [21.5 (MM/DD/YY HH:MM:SS)] was greater than the threshold (21.0).", + "stateReasonData": { + "version": "1.0", + "queryDate": "date", + "startDate": "date", + "unit": "Seconds", + "statistic": "Average", + "period": 10, + "recentDatapoints": [ + 21.5 + ], + "threshold": 21.0, + "evaluatedDatapoints": [ + { + "timestamp": "date", + "sampleCount": 2.0, + "value": 21.5 + } + ] + } + } + }, + "HistoryItemType": "StateUpdate", + "HistorySummary": "Alarm updated from INSUFFICIENT_DATA to ALARM", + "Timestamp": "timestamp" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe-alarms-for-metric": { + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "my-metric1", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "Threshold Crossed: 1 datapoint [21.5 (MM/DD/YY HH:MM:SS)] was greater than the threshold (21.0).", + "StateReasonData": { + "version": "1.0", + "queryDate": "date", + "startDate": "date", + "unit": "Seconds", + "statistic": "Average", + "period": 10, + "recentDatapoints": [ + 21.5 + ], + "threshold": 21.0, + "evaluatedDatapoints": [ + { + "timestamp": "date", + "sampleCount": 2.0, + "value": 21.5 + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM", + "Statistic": "Average", + "Threshold": 21.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm[json]": { + "recorded-date": "19-09-2025, 21:08:43", + "recorded-content": { + "describe-alarm": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "my-metric1", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Average", + "Threshold": 21.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "alarm-triggered-sqs-msg": { + "AWSAccountId": "111111111111", + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "date", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "InsufficientDataActions": [], + "NewStateReason": "Threshold Crossed: 1 datapoint [21.5 (MM/DD/YY HH:MM:SS)] was greater than the threshold (21.0).", + "NewStateValue": "ALARM", + "OKActions": [ + "arn::sns::111111111111:" + ], + "OldStateValue": "INSUFFICIENT_DATA", + "Region": "", + "StateChangeTime": "date", + "Trigger": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "name": "InstanceId", + "value": "abc" + } + ], + "EvaluateLowSampleCountPercentile": "", + "EvaluationPeriods": 1, + "MetricName": "my-metric1", + "Namespace": "", + "Period": 10, + "Statistic": "AVERAGE", + "StatisticType": "Statistic", + "Threshold": 21.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + }, + "describe-alarm-history": { + "AlarmHistoryItems": [ + { + "AlarmName": "", + "AlarmType": "MetricAlarm", + "HistoryData": { + "version": "1.0", + "oldState": { + "stateValue": "INSUFFICIENT_DATA", + "stateReason": "Unchecked: Initial alarm creation" + }, + "newState": { + "stateValue": "ALARM", + "stateReason": "Threshold Crossed: 1 datapoint [21.5 (MM/DD/YY HH:MM:SS)] was greater than the threshold (21.0).", + "stateReasonData": { + "version": "1.0", + "queryDate": "date", + "startDate": "date", + "unit": "Seconds", + "statistic": "Average", + "period": 10, + "recentDatapoints": [ + 21.5 + ], + "threshold": 21.0, + "evaluatedDatapoints": [ + { + "timestamp": "date", + "sampleCount": 2.0, + "value": 21.5 + } + ] + } + } + }, + "HistoryItemType": "StateUpdate", + "HistorySummary": "Alarm updated from INSUFFICIENT_DATA to ALARM", + "Timestamp": "timestamp" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe-alarms-for-metric": { + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "my-metric1", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "Threshold Crossed: 1 datapoint [21.5 (MM/DD/YY HH:MM:SS)] was greater than the threshold (21.0).", + "StateReasonData": { + "version": "1.0", + "queryDate": "date", + "startDate": "date", + "unit": "Seconds", + "statistic": "Average", + "period": 10, + "recentDatapoints": [ + 21.5 + ], + "threshold": 21.0, + "evaluatedDatapoints": [ + { + "timestamp": "date", + "sampleCount": 2.0, + "value": 21.5 + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM", + "Statistic": "Average", + "Threshold": 21.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 21:09:02", + "recorded-content": { + "describe-alarm": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "my-metric1", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Average", + "Threshold": 21.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "alarm-triggered-sqs-msg": { + "AWSAccountId": "111111111111", + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "date", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "InsufficientDataActions": [], + "NewStateReason": "Threshold Crossed: 1 datapoint [21.5 (MM/DD/YY HH:MM:SS)] was greater than the threshold (21.0).", + "NewStateValue": "ALARM", + "OKActions": [ + "arn::sns::111111111111:" + ], + "OldStateValue": "INSUFFICIENT_DATA", + "Region": "", + "StateChangeTime": "date", + "Trigger": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "name": "InstanceId", + "value": "abc" + } + ], + "EvaluateLowSampleCountPercentile": "", + "EvaluationPeriods": 1, + "MetricName": "my-metric1", + "Namespace": "", + "Period": 10, + "Statistic": "AVERAGE", + "StatisticType": "Statistic", + "Threshold": 21.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + }, + "describe-alarm-history": { + "AlarmHistoryItems": [ + { + "AlarmName": "", + "AlarmType": "MetricAlarm", + "HistoryData": { + "version": "1.0", + "oldState": { + "stateValue": "INSUFFICIENT_DATA", + "stateReason": "Unchecked: Initial alarm creation" + }, + "newState": { + "stateValue": "ALARM", + "stateReason": "Threshold Crossed: 1 datapoint [21.5 (MM/DD/YY HH:MM:SS)] was greater than the threshold (21.0).", + "stateReasonData": { + "version": "1.0", + "queryDate": "date", + "startDate": "date", + "unit": "Seconds", + "statistic": "Average", + "period": 10, + "recentDatapoints": [ + 21.5 + ], + "threshold": 21.0, + "evaluatedDatapoints": [ + { + "timestamp": "date", + "sampleCount": 2.0, + "value": 21.5 + } + ] + } + } + }, + "HistoryItemType": "StateUpdate", + "HistorySummary": "Alarm updated from INSUFFICIENT_DATA to ALARM", + "Timestamp": "timestamp" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe-alarms-for-metric": { + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "my-metric1", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "Threshold Crossed: 1 datapoint [21.5 (MM/DD/YY HH:MM:SS)] was greater than the threshold (21.0).", + "StateReasonData": { + "version": "1.0", + "queryDate": "date", + "startDate": "date", + "unit": "Seconds", + "statistic": "Average", + "period": 10, + "recentDatapoints": [ + 21.5 + ], + "threshold": 21.0, + "evaluatedDatapoints": [ + { + "timestamp": "date", + "sampleCount": 2.0, + "value": 21.5 + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM", + "Statistic": "Average", + "Threshold": 21.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_breaching_alarm_actions[query]": { + "recorded-date": "19-09-2025, 21:11:41", + "recorded-content": { + "alarm-1-sqs-msg": { + "AWSAccountId": "111111111111", + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "date", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "InsufficientDataActions": [], + "NewStateReason": "Threshold Crossed: no datapoints were received for 2 periods and 2 missing datapoints were treated as [Breaching].", + "NewStateValue": "ALARM", + "OKActions": [ + "arn::sns::111111111111:" + ], + "OldStateValue": "INSUFFICIENT_DATA", + "Region": "", + "StateChangeTime": "date", + "Trigger": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "name": "InstanceId", + "value": "abc" + } + ], + "EvaluateLowSampleCountPercentile": "", + "EvaluationPeriods": 2, + "MetricName": "my-metric101", + "Namespace": "", + "Period": 10, + "Statistic": "AVERAGE", + "StatisticType": "Statistic", + "Threshold": 2.0, + "TreatMissingData": "breaching", + "Unit": "Seconds" + } + }, + "alarm-1-describe": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "Threshold Crossed: no datapoints were received for 2 periods and 2 missing datapoints were treated as [Breaching].", + "StateReasonData": { + "version": "1.0", + "queryDate": "date", + "unit": "Seconds", + "statistic": "Average", + "period": 10, + "recentDatapoints": [], + "threshold": 2.0, + "evaluatedDatapoints": [ + { + "timestamp": "date" + }, + { + "timestamp": "date" + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "breaching", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_breaching_alarm_actions[json]": { + "recorded-date": "19-09-2025, 21:12:42", + "recorded-content": { + "alarm-1-sqs-msg": { + "AWSAccountId": "111111111111", + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "date", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "InsufficientDataActions": [], + "NewStateReason": "Threshold Crossed: no datapoints were received for 2 periods and 2 missing datapoints were treated as [Breaching].", + "NewStateValue": "ALARM", + "OKActions": [ + "arn::sns::111111111111:" + ], + "OldStateValue": "INSUFFICIENT_DATA", + "Region": "", + "StateChangeTime": "date", + "Trigger": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "name": "InstanceId", + "value": "abc" + } + ], + "EvaluateLowSampleCountPercentile": "", + "EvaluationPeriods": 2, + "MetricName": "my-metric101", + "Namespace": "", + "Period": 10, + "Statistic": "AVERAGE", + "StatisticType": "Statistic", + "Threshold": 2.0, + "TreatMissingData": "breaching", + "Unit": "Seconds" + } + }, + "alarm-1-describe": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "Threshold Crossed: no datapoints were received for 2 periods and 2 missing datapoints were treated as [Breaching].", + "StateReasonData": { + "version": "1.0", + "queryDate": "date", + "unit": "Seconds", + "statistic": "Average", + "period": 10, + "recentDatapoints": [], + "threshold": 2.0, + "evaluatedDatapoints": [ + { + "timestamp": "date" + }, + { + "timestamp": "date" + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "breaching", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_breaching_alarm_actions[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 21:13:17", + "recorded-content": { + "alarm-1-sqs-msg": { + "AWSAccountId": "111111111111", + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "date", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "InsufficientDataActions": [], + "NewStateReason": "Threshold Crossed: no datapoints were received for 2 periods and 2 missing datapoints were treated as [Breaching].", + "NewStateValue": "ALARM", + "OKActions": [ + "arn::sns::111111111111:" + ], + "OldStateValue": "INSUFFICIENT_DATA", + "Region": "", + "StateChangeTime": "date", + "Trigger": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "name": "InstanceId", + "value": "abc" + } + ], + "EvaluateLowSampleCountPercentile": "", + "EvaluationPeriods": 2, + "MetricName": "my-metric101", + "Namespace": "", + "Period": 10, + "Statistic": "AVERAGE", + "StatisticType": "Statistic", + "Threshold": 2.0, + "TreatMissingData": "breaching", + "Unit": "Seconds" + } + }, + "alarm-1-describe": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "Threshold Crossed: no datapoints were received for 2 periods and 2 missing datapoints were treated as [Breaching].", + "StateReasonData": { + "version": "1.0", + "queryDate": "date", + "unit": "Seconds", + "statistic": "Average", + "period": 10, + "recentDatapoints": [], + "threshold": 2.0, + "evaluatedDatapoints": [ + { + "timestamp": "date" + }, + { + "timestamp": "date" + } + ] + }, + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "breaching", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_enable_disable_alarm_actions[query]": { + "recorded-date": "19-09-2025, 21:17:31", + "recorded-content": { + "describe_alarm": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "alarm-state-sqs-msg": { + "AWSAccountId": "111111111111", + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "date", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "InsufficientDataActions": [], + "NewStateReason": "testing alarm", + "NewStateValue": "ALARM", + "OKActions": [ + "arn::sns::111111111111:" + ], + "OldStateValue": "INSUFFICIENT_DATA", + "Region": "", + "StateChangeTime": "date", + "Trigger": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "name": "InstanceId", + "value": "abc" + } + ], + "EvaluateLowSampleCountPercentile": "", + "EvaluationPeriods": 2, + "MetricName": "my-metric101", + "Namespace": "", + "Period": 10, + "Statistic": "AVERAGE", + "StatisticType": "Statistic", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + }, + "alarm-state-describe": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "testing alarm", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_alarm_disabled": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": false, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "testing OK state", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "ok-state-action-disabled-describe": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": false, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "testing OK state", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_alarm_enabled": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "testing OK state", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_enable_disable_alarm_actions[json]": { + "recorded-date": "19-09-2025, 21:17:46", + "recorded-content": { + "describe_alarm": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "alarm-state-sqs-msg": { + "AWSAccountId": "111111111111", + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "date", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "InsufficientDataActions": [], + "NewStateReason": "testing alarm", + "NewStateValue": "ALARM", + "OKActions": [ + "arn::sns::111111111111:" + ], + "OldStateValue": "INSUFFICIENT_DATA", + "Region": "", + "StateChangeTime": "date", + "Trigger": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "name": "InstanceId", + "value": "abc" + } + ], + "EvaluateLowSampleCountPercentile": "", + "EvaluationPeriods": 2, + "MetricName": "my-metric101", + "Namespace": "", + "Period": 10, + "Statistic": "AVERAGE", + "StatisticType": "Statistic", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + }, + "alarm-state-describe": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "testing alarm", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_alarm_disabled": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": false, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "testing OK state", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "ok-state-action-disabled-describe": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": false, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "testing OK state", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_alarm_enabled": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "testing OK state", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_enable_disable_alarm_actions[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 21:18:00", + "recorded-content": { + "describe_alarm": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "alarm-state-sqs-msg": { + "AWSAccountId": "111111111111", + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "date", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "InsufficientDataActions": [], + "NewStateReason": "testing alarm", + "NewStateValue": "ALARM", + "OKActions": [ + "arn::sns::111111111111:" + ], + "OldStateValue": "INSUFFICIENT_DATA", + "Region": "", + "StateChangeTime": "date", + "Trigger": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "name": "InstanceId", + "value": "abc" + } + ], + "EvaluateLowSampleCountPercentile": "", + "EvaluationPeriods": 2, + "MetricName": "my-metric101", + "Namespace": "", + "Period": 10, + "Statistic": "AVERAGE", + "StatisticType": "Statistic", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + }, + "alarm-state-describe": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "testing alarm", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_alarm_disabled": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": false, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "testing OK state", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "ok-state-action-disabled-describe": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": false, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "testing OK state", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_alarm_enabled": { + "CompositeAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "testing cloudwatch alarms", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "abc" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "my-metric101", + "Namespace": "", + "OKActions": [ + "arn::sns::111111111111:" + ], + "Period": 10, + "StateReason": "testing OK state", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK", + "Statistic": "Average", + "Threshold": 2.0, + "TreatMissingData": "ignore", + "Unit": "Seconds" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_aws_sqs_metrics_created[query]": { + "recorded-date": "19-09-2025, 21:27:01", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "sent", + "Label": "NumberOfMessagesSent", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + }, + { + "Id": "sent_size", + "Label": "SentMessageSize", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 5.0 + ] + }, + { + "Id": "empty_receives", + "Label": "NumberOfEmptyReceives", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get_metric_data_2": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "num_msg_received", + "Label": "NumberOfMessagesReceived", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + }, + { + "Id": "num_msg_deleted", + "Label": "NumberOfMessagesDeleted", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_aws_sqs_metrics_created[json]": { + "recorded-date": "19-09-2025, 21:29:00", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "sent", + "Label": "NumberOfMessagesSent", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + }, + { + "Id": "sent_size", + "Label": "SentMessageSize", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 5.0 + ] + }, + { + "Id": "empty_receives", + "Label": "NumberOfEmptyReceives", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get_metric_data_2": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "num_msg_received", + "Label": "NumberOfMessagesReceived", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + }, + { + "Id": "num_msg_deleted", + "Label": "NumberOfMessagesDeleted", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_aws_sqs_metrics_created[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 21:31:00", + "recorded-content": { + "get_metric_data": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "sent", + "Label": "NumberOfMessagesSent", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + }, + { + "Id": "sent_size", + "Label": "SentMessageSize", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 5.0 + ] + }, + { + "Id": "empty_receives", + "Label": "NumberOfEmptyReceives", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get_metric_data_2": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "num_msg_received", + "Label": "NumberOfMessagesReceived", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + }, + { + "Id": "num_msg_deleted", + "Label": "NumberOfMessagesDeleted", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_dashboard_name[query]": { + "recorded-date": "19-09-2025, 21:32:13", + "recorded-content": { + "error-invalid-dashboardname": { + "Error": { + "Code": "InvalidParameterValue", + "Message": "The value for field DashboardName contains invalid characters. It can only contain alphanumerics, dash (-) and underscore (_).\n", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_dashboard_name[json]": { + "recorded-date": "19-09-2025, 21:32:13", + "recorded-content": { + "error-invalid-dashboardname": { + "Error": { + "Code": "InvalidParameterValue", + "Message": "The value for field DashboardName contains invalid characters. It can only contain alphanumerics, dash (-) and underscore (_).\n", + "QueryErrorCode": "InvalidParameterValueException", + "Type": "Sender" + }, + "message": "The value for field DashboardName contains invalid characters. It can only contain alphanumerics, dash (-) and underscore (_).\n", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_dashboard_name[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 21:32:13", + "recorded-content": { + "error-invalid-dashboardname": { + "Error": { + "Code": "InvalidParameterValue", + "Message": "The value for field DashboardName contains invalid characters. It can only contain alphanumerics, dash (-) and underscore (_).\n", + "QueryErrorCode": "InvalidParameterValueException", + "Type": "Sender" + }, + "message": "The value for field DashboardName contains invalid characters. It can only contain alphanumerics, dash (-) and underscore (_).\n", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_dashboard_lifecycle[query]": { + "recorded-date": "19-09-2025, 21:32:55", + "recorded-content": { + "get_dashboard": { + "DashboardArn": "arn::cloudwatch::111111111111:dashboard/", + "DashboardBody": { + "widgets": [ + { + "type": "metric", + "x": 0, + "y": 0, + "width": 6, + "height": 6, + "properties": { + "metrics": [ + [ + "AWS/EC2", + "CPUUtilization", + "InstanceId", + "i-12345678" + ] + ], + "region": "", + "view": "timeSeries", + "stacked": false + } + } + ] + }, + "DashboardName": "", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_dashboards": { + "DashboardEntries": [ + { + "DashboardArn": "arn::cloudwatch::111111111111:dashboard/", + "DashboardName": "", + "LastModified": "datetime", + "Size": 240 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_dashboards_prefix_empty": { + "DashboardEntries": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_dashboards_prefix": { + "DashboardEntries": [ + { + "DashboardArn": "arn::cloudwatch::111111111111:dashboard/", + "DashboardName": "", + "LastModified": "datetime", + "Size": 240 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_dashboards_empty": { + "DashboardEntries": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_dashboard_lifecycle[json]": { + "recorded-date": "19-09-2025, 21:32:56", + "recorded-content": { + "get_dashboard": { + "DashboardArn": "arn::cloudwatch::111111111111:dashboard/", + "DashboardBody": { + "widgets": [ + { + "type": "metric", + "x": 0, + "y": 0, + "width": 6, + "height": 6, + "properties": { + "metrics": [ + [ + "AWS/EC2", + "CPUUtilization", + "InstanceId", + "i-12345678" + ] + ], + "region": "", + "view": "timeSeries", + "stacked": false + } + } + ] + }, + "DashboardName": "", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_dashboards": { + "DashboardEntries": [ + { + "DashboardArn": "arn::cloudwatch::111111111111:dashboard/", + "DashboardName": "", + "LastModified": "datetime", + "Size": 240 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_dashboards_prefix_empty": { + "DashboardEntries": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_dashboards_prefix": { + "DashboardEntries": [ + { + "DashboardArn": "arn::cloudwatch::111111111111:dashboard/", + "DashboardName": "", + "LastModified": "datetime", + "Size": 240 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_dashboards_empty": { + "DashboardEntries": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_dashboard_lifecycle[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 21:32:58", + "recorded-content": { + "get_dashboard": { + "DashboardArn": "arn::cloudwatch::111111111111:dashboard/", + "DashboardBody": { + "widgets": [ + { + "type": "metric", + "x": 0, + "y": 0, + "width": 6, + "height": 6, + "properties": { + "metrics": [ + [ + "AWS/EC2", + "CPUUtilization", + "InstanceId", + "i-12345678" + ] + ], + "region": "", + "view": "timeSeries", + "stacked": false + } + } + ] + }, + "DashboardName": "", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_dashboards": { + "DashboardEntries": [ + { + "DashboardArn": "arn::cloudwatch::111111111111:dashboard/", + "DashboardName": "", + "LastModified": "datetime", + "Size": 240 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_dashboards_prefix_empty": { + "DashboardEntries": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_dashboards_prefix": { + "DashboardEntries": [ + { + "DashboardArn": "arn::cloudwatch::111111111111:dashboard/", + "DashboardName": "", + "LastModified": "datetime", + "Size": 240 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list_dashboards_empty": { + "DashboardEntries": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_create_metric_stream[query]": { + "recorded-date": "19-09-2025, 21:35:54", + "recorded-content": { + "create_metric_stream": { + "Arn": "arn::cloudwatch::111111111111:metric-stream/", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get_metric_stream": { + "Arn": "arn::cloudwatch::111111111111:metric-stream/", + "CreationDate": "datetime", + "FirehoseArn": "", + "IncludeLinkedAccountsMetrics": false, + "LastUpdateDate": "datetime", + "Name": "", + "OutputFormat": "json", + "RoleArn": "", + "State": "running", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "start_metric_stream": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "stop_metric_stream": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "delete_metric_stream": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_create_metric_stream[json]": { + "recorded-date": "19-09-2025, 21:39:06", + "recorded-content": { + "create_metric_stream": { + "Arn": "arn::cloudwatch::111111111111:metric-stream/", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get_metric_stream": { + "Arn": "arn::cloudwatch::111111111111:metric-stream/", + "CreationDate": "datetime", + "FirehoseArn": "", + "IncludeLinkedAccountsMetrics": false, + "LastUpdateDate": "datetime", + "Name": "", + "OutputFormat": "json", + "RoleArn": "", + "State": "running", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "start_metric_stream": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "stop_metric_stream": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "delete_metric_stream": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_create_metric_stream[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 21:41:37", + "recorded-content": { + "create_metric_stream": { + "Arn": "arn::cloudwatch::111111111111:metric-stream/", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get_metric_stream": { + "Arn": "arn::cloudwatch::111111111111:metric-stream/", + "CreationDate": "datetime", + "FirehoseArn": "", + "IncludeLinkedAccountsMetrics": false, + "LastUpdateDate": "datetime", + "Name": "", + "OutputFormat": "json", + "RoleArn": "", + "State": "running", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "start_metric_stream": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "stop_metric_stream": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "delete_metric_stream": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_insight_rule[query]": { + "recorded-date": "19-09-2025, 21:45:12", + "recorded-content": { + "create_insight_rule": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_insight_rule": { + "InsightRules": [ + { + "ApplyOnTransformedLogs": false, + "Definition": { + "Schema": { + "Name": "", + "Version": 1 + }, + "LogGroupNames": [ + "API-Gateway-Access-Logs*" + ], + "LogFormat": "CLF", + "Fields": { + "4": "IpAddress", + "7": "StatusCode" + }, + "Contribution": { + "Keys": [ + "IpAddress" + ], + "Filters": [ + { + "Match": "StatusCode", + "EqualTo": 200 + } + ] + }, + "AggregateOn": "Count" + }, + "ManagedRule": false, + "Name": "", + "Schema": "/1", + "State": "ENABLED" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "disable_insight_rule": { + "Failures": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "enable_insight_rule": { + "Failures": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get_insight_rule_report": { + "AggregateValue": 0.0, + "AggregationStatistic": "SampleCount", + "ApproximateUniqueCount": 0, + "Contributors": [], + "KeyLabels": [ + "IpAddress" + ], + "MetricDatapoints": [ + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "delete_insight_rule": { + "Failures": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_insight_rule[json]": { + "recorded-date": "19-09-2025, 21:45:14", + "recorded-content": { + "create_insight_rule": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_insight_rule": { + "InsightRules": [ + { + "ApplyOnTransformedLogs": false, + "Definition": { + "Schema": { + "Name": "", + "Version": 1 + }, + "LogGroupNames": [ + "API-Gateway-Access-Logs*" + ], + "LogFormat": "CLF", + "Fields": { + "4": "IpAddress", + "7": "StatusCode" + }, + "Contribution": { + "Keys": [ + "IpAddress" + ], + "Filters": [ + { + "Match": "StatusCode", + "EqualTo": 200 + } + ] + }, + "AggregateOn": "Count" + }, + "ManagedRule": false, + "Name": "", + "Schema": "/1", + "State": "ENABLED" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "disable_insight_rule": { + "Failures": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "enable_insight_rule": { + "Failures": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get_insight_rule_report": { + "AggregateValue": 0.0, + "AggregationStatistic": "SampleCount", + "ApproximateUniqueCount": 0, + "Contributors": [], + "KeyLabels": [ + "IpAddress" + ], + "MetricDatapoints": [ + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "delete_insight_rule": { + "Failures": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_insight_rule[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 21:45:15", + "recorded-content": { + "create_insight_rule": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_insight_rule": { + "InsightRules": [ + { + "ApplyOnTransformedLogs": false, + "Definition": { + "Schema": { + "Name": "", + "Version": 1 + }, + "LogGroupNames": [ + "API-Gateway-Access-Logs*" + ], + "LogFormat": "CLF", + "Fields": { + "4": "IpAddress", + "7": "StatusCode" + }, + "Contribution": { + "Keys": [ + "IpAddress" + ], + "Filters": [ + { + "Match": "StatusCode", + "EqualTo": 200 + } + ] + }, + "AggregateOn": "Count" + }, + "ManagedRule": false, + "Name": "", + "Schema": "/1", + "State": "ENABLED" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "disable_insight_rule": { + "Failures": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "enable_insight_rule": { + "Failures": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get_insight_rule_report": { + "AggregateValue": 0.0, + "AggregationStatistic": "SampleCount", + "ApproximateUniqueCount": 0, + "Contributors": [], + "KeyLabels": [ + "IpAddress" + ], + "MetricDatapoints": [ + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + }, + { + "Timestamp": "timestamp", + "UniqueContributors": 0.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "delete_insight_rule": { + "Failures": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_anomaly_detector_lifecycle[query]": { + "recorded-date": "19-09-2025, 21:45:48", + "recorded-content": { + "create_anomaly_detector": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_anomaly_detector": { + "AnomalyDetectors": [ + { + "Configuration": { + "ExcludedTimeRanges": [] + }, + "Dimensions": [ + { + "Name": "DimensionName", + "Value": "DimensionValue" + } + ], + "MetricName": "MyMetric", + "Namespace": "MyNamespace", + "SingleMetricAnomalyDetector": { + "AccountId": "111111111111", + "Dimensions": [ + { + "Name": "DimensionName", + "Value": "DimensionValue" + } + ], + "MetricName": "MyMetric", + "Namespace": "MyNamespace", + "Stat": "Sum" + }, + "Stat": "Sum", + "StateValue": "PENDING_TRAINING" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "delete_anomaly_detector": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_anomaly_detector_lifecycle[json]": { + "recorded-date": "19-09-2025, 21:45:48", + "recorded-content": { + "create_anomaly_detector": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe_anomaly_detector": { + "AnomalyDetectors": [ + { + "Configuration": { + "ExcludedTimeRanges": [] + }, + "Dimensions": [ + { + "Name": "DimensionName", + "Value": "DimensionValue" + } + ], + "MetricName": "MyMetric", + "Namespace": "MyNamespace", + "SingleMetricAnomalyDetector": { + "AccountId": "111111111111", + "Dimensions": [ + { + "Name": "DimensionName", + "Value": "DimensionValue" + } + ], + "MetricName": "MyMetric", + "Namespace": "MyNamespace", + "Stat": "Sum" + }, + "Stat": "Sum", + "StateValue": "PENDING_TRAINING" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "delete_anomaly_detector": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_anomaly_detector_lifecycle[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 21:45:48", "recorded-content": { - "cloudwatch_sns_subscription": { - "SubscriptionArn": "arn::sns::111111111111::", + "create_anomaly_detector": { "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "describe_alarm": { + "describe_anomaly_detector": { + "AnomalyDetectors": [ + { + "Configuration": { + "ExcludedTimeRanges": [] + }, + "Dimensions": [ + { + "Name": "DimensionName", + "Value": "DimensionValue" + } + ], + "MetricName": "MyMetric", + "Namespace": "MyNamespace", + "SingleMetricAnomalyDetector": { + "AccountId": "111111111111", + "Dimensions": [ + { + "Name": "DimensionName", + "Value": "DimensionValue" + } + ], + "MetricName": "MyMetric", + "Namespace": "MyNamespace", + "Stat": "Sum" + }, + "Stat": "Sum", + "StateValue": "PENDING_TRAINING" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "delete_anomaly_detector": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_minimal_metric_alarm[query]": { + "recorded-date": "19-09-2025, 21:47:20", + "recorded-content": { + "describe_minimal_metric_alarm": { "CompositeAlarms": [], "MetricAlarms": [ { "ActionsEnabled": true, - "AlarmActions": [ - "arn::sns::111111111111:" - ], + "AlarmActions": [], "AlarmArn": "arn::cloudwatch::111111111111:alarm:", "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "testing cloudwatch alarms", "AlarmName": "", "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [ - { - "Name": "InstanceId", - "Value": "abc" - } - ], - "EvaluationPeriods": 2, + "Dimensions": [], + "EvaluationPeriods": 1, "InsufficientDataActions": [], - "MetricName": "my-metric101", + "MetricName": "", "Namespace": "", - "OKActions": [ - "arn::sns::111111111111:" - ], + "OKActions": [], "Period": 10, "StateReason": "Unchecked: Initial alarm creation", "StateTransitionedTimestamp": "timestamp", "StateUpdatedTimestamp": "timestamp", "StateValue": "INSUFFICIENT_DATA", - "Statistic": "Average", - "Threshold": 2.0, - "TreatMissingData": "ignore", - "Unit": "Seconds" + "Statistic": "Sum", + "Threshold": 30.0 } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "alarm-state-describe": { + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_minimal_metric_alarm[json]": { + "recorded-date": "19-09-2025, 21:47:20", + "recorded-content": { + "describe_minimal_metric_alarm": { "CompositeAlarms": [], "MetricAlarms": [ { "ActionsEnabled": true, - "AlarmActions": [ - "arn::sns::111111111111:" - ], + "AlarmActions": [], "AlarmArn": "arn::cloudwatch::111111111111:alarm:", "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "testing cloudwatch alarms", "AlarmName": "", "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [ - { - "Name": "InstanceId", - "Value": "abc" - } - ], - "EvaluationPeriods": 2, + "Dimensions": [], + "EvaluationPeriods": 1, "InsufficientDataActions": [], - "MetricName": "my-metric101", + "MetricName": "", "Namespace": "", - "OKActions": [ - "arn::sns::111111111111:" - ], + "OKActions": [], "Period": 10, - "StateReason": "testing alarm", + "StateReason": "Unchecked: Initial alarm creation", "StateTransitionedTimestamp": "timestamp", "StateUpdatedTimestamp": "timestamp", - "StateValue": "ALARM", - "Statistic": "Average", - "Threshold": 2.0, - "TreatMissingData": "ignore", - "Unit": "Seconds" + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 30.0 } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "alarm-state-sqs-msg": { - "AWSAccountId": "111111111111", - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "date", - "AlarmDescription": "testing cloudwatch alarms", - "AlarmName": "", - "InsufficientDataActions": [], - "NewStateReason": "testing alarm", - "NewStateValue": "ALARM", - "OKActions": [ - "arn::sns::111111111111:" - ], - "OldStateValue": "INSUFFICIENT_DATA", - "Region": "", - "StateChangeTime": "date", - "Trigger": { - "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [ - { - "name": "InstanceId", - "value": "abc" - } - ], - "EvaluateLowSampleCountPercentile": "", - "EvaluationPeriods": 2, - "MetricName": "my-metric101", - "Namespace": "", - "Period": 10, - "Statistic": "AVERAGE", - "StatisticType": "Statistic", - "Threshold": 2.0, - "TreatMissingData": "ignore", - "Unit": "Seconds" - } - }, - "describe_alarm_disabled": { + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_minimal_metric_alarm[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 21:47:21", + "recorded-content": { + "describe_minimal_metric_alarm": { "CompositeAlarms": [], "MetricAlarms": [ { - "ActionsEnabled": false, - "AlarmActions": [ - "arn::sns::111111111111:" - ], + "ActionsEnabled": true, + "AlarmActions": [], "AlarmArn": "arn::cloudwatch::111111111111:alarm:", "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "testing cloudwatch alarms", "AlarmName": "", "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [ - { - "Name": "InstanceId", - "Value": "abc" - } - ], - "EvaluationPeriods": 2, + "Dimensions": [], + "EvaluationPeriods": 1, "InsufficientDataActions": [], - "MetricName": "my-metric101", + "MetricName": "", "Namespace": "", - "OKActions": [ - "arn::sns::111111111111:" - ], + "OKActions": [], "Period": 10, - "StateReason": "testing OK state", + "StateReason": "Unchecked: Initial alarm creation", "StateTransitionedTimestamp": "timestamp", "StateUpdatedTimestamp": "timestamp", - "StateValue": "OK", - "Statistic": "Average", - "Threshold": 2.0, - "TreatMissingData": "ignore", - "Unit": "Seconds" + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 30.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_zero_and_labels[query]": { + "recorded-date": "19-09-2025, 22:46:59", + "recorded-content": { + "get_metric_data_with_zero_and_labels": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result_Average", + "Label": "metric1 Average", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 19.416666666666668 + ] + }, + { + "Id": "result_Sum", + "Label": "metric1 Sum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 116.5 + ] + }, + { + "Id": "result_Minimum", + "Label": "metric1 Minimum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 0.0 + ] + }, + { + "Id": "result_Maximum", + "Label": "metric1 Maximum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 100.0 + ] } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "ok-state-action-disabled-describe": { - "CompositeAlarms": [], - "MetricAlarms": [ + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_zero_and_labels[json]": { + "recorded-date": "19-09-2025, 22:47:00", + "recorded-content": { + "get_metric_data_with_zero_and_labels": { + "Messages": [], + "MetricDataResults": [ { - "ActionsEnabled": false, - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "testing cloudwatch alarms", - "AlarmName": "", - "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [ - { - "Name": "InstanceId", - "Value": "abc" - } - ], - "EvaluationPeriods": 2, - "InsufficientDataActions": [], - "MetricName": "my-metric101", - "Namespace": "", - "OKActions": [ - "arn::sns::111111111111:" - ], - "Period": 10, - "StateReason": "testing OK state", - "StateTransitionedTimestamp": "timestamp", - "StateUpdatedTimestamp": "timestamp", - "StateValue": "OK", - "Statistic": "Average", - "Threshold": 2.0, - "TreatMissingData": "ignore", - "Unit": "Seconds" + "Id": "result_Average", + "Label": "metric1 Average", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 19.416666666666668 + ] + }, + { + "Id": "result_Sum", + "Label": "metric1 Sum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 116.5 + ] + }, + { + "Id": "result_Minimum", + "Label": "metric1 Minimum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 0.0 + ] + }, + { + "Id": "result_Maximum", + "Label": "metric1 Maximum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 100.0 + ] } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "describe_alarm_enabled": { - "CompositeAlarms": [], - "MetricAlarms": [ + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_zero_and_labels[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 22:47:02", + "recorded-content": { + "get_metric_data_with_zero_and_labels": { + "Messages": [], + "MetricDataResults": [ { - "ActionsEnabled": true, - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "testing cloudwatch alarms", - "AlarmName": "", - "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [ - { - "Name": "InstanceId", - "Value": "abc" - } - ], - "EvaluationPeriods": 2, - "InsufficientDataActions": [], - "MetricName": "my-metric101", - "Namespace": "", - "OKActions": [ - "arn::sns::111111111111:" - ], - "Period": 10, - "StateReason": "testing OK state", - "StateTransitionedTimestamp": "timestamp", - "StateUpdatedTimestamp": "timestamp", - "StateValue": "OK", - "Statistic": "Average", - "Threshold": 2.0, - "TreatMissingData": "ignore", - "Unit": "Seconds" + "Id": "result_Average", + "Label": "metric1 Average", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 19.416666666666668 + ] + }, + { + "Id": "result_Sum", + "Label": "metric1 Sum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 116.5 + ] + }, + { + "Id": "result_Minimum", + "Label": "metric1 Minimum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 0.0 + ] + }, + { + "Id": "result_Maximum", + "Label": "metric1 Maximum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 100.0 + ] } ], "ResponseMetadata": { @@ -259,298 +4969,155 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_breaching_alarm_actions": { - "recorded-date": "12-09-2023, 11:56:52", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_statistics[query]": { + "recorded-date": "19-09-2025, 22:47:57", "recorded-content": { - "cloudwatch_sns_subscription": { - "SubscriptionArn": "arn::sns::111111111111::", + "get_metric_statistics": { + "Datapoints": [ + { + "Average": 4.5, + "Maximum": 9.0, + "Minimum": 0.0, + "SampleCount": 10.0, + "Sum": 45.0, + "Timestamp": "timestamp", + "Unit": "None" + } + ], + "Label": "metric", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "alarm-1-describe": { - "CompositeAlarms": [], - "MetricAlarms": [ + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_statistics[json]": { + "recorded-date": "19-09-2025, 22:48:01", + "recorded-content": { + "get_metric_statistics": { + "Datapoints": [ { - "ActionsEnabled": true, - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "testing cloudwatch alarms", - "AlarmName": "", - "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [ - { - "Name": "InstanceId", - "Value": "abc" - } - ], - "EvaluationPeriods": 2, - "InsufficientDataActions": [], - "MetricName": "my-metric101", - "Namespace": "", - "OKActions": [ - "arn::sns::111111111111:" - ], - "Period": 10, - "StateReason": "Threshold Crossed: no datapoints were received for 2 periods and 2 missing datapoints were treated as [Breaching].", - "StateReasonData": { - "version": "1.0", - "queryDate": "date", - "unit": "Seconds", - "statistic": "Average", - "period": 10, - "recentDatapoints": [], - "threshold": 2.0, - "evaluatedDatapoints": [ - { - "timestamp": "date" - }, - { - "timestamp": "date" - } - ] - }, - "StateTransitionedTimestamp": "timestamp", - "StateUpdatedTimestamp": "timestamp", - "StateValue": "ALARM", - "Statistic": "Average", - "Threshold": 2.0, - "TreatMissingData": "breaching", - "Unit": "Seconds" + "Average": 4.5, + "Maximum": 9.0, + "Minimum": 0.0, + "SampleCount": 10.0, + "Sum": 45.0, + "Timestamp": "timestamp", + "Unit": "None" + } + ], + "Label": "metric", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_statistics[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 22:48:04", + "recorded-content": { + "get_metric_statistics": { + "Datapoints": [ + { + "Average": 4.5, + "Maximum": 9.0, + "Minimum": 0.0, + "SampleCount": 10.0, + "Sum": 45.0, + "Timestamp": "timestamp", + "Unit": "None" } ], + "Label": "metric", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "alarm-1-sqs-msg": { - "AWSAccountId": "111111111111", - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "date", - "AlarmDescription": "testing cloudwatch alarms", - "AlarmName": "", - "InsufficientDataActions": [], - "NewStateReason": "Threshold Crossed: no datapoints were received for 2 periods and 2 missing datapoints were treated as [Breaching].", - "NewStateValue": "ALARM", - "OKActions": [ - "arn::sns::111111111111:" - ], - "OldStateValue": "INSUFFICIENT_DATA", - "Region": "", - "StateChangeTime": "date", - "Trigger": { - "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [ - { - "name": "InstanceId", - "value": "abc" - } - ], - "EvaluateLowSampleCountPercentile": "", - "EvaluationPeriods": 2, - "MetricName": "my-metric101", - "Namespace": "", - "Period": 10, - "Statistic": "AVERAGE", - "StatisticType": "Statistic", - "Threshold": 2.0, - "TreatMissingData": "breaching", - "Unit": "Seconds" - } } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm": { - "recorded-date": "12-05-2025, 16:20:57", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_handle_different_units[query]": { + "recorded-date": "19-09-2025, 22:56:18", "recorded-content": { - "describe-alarm": { - "CompositeAlarms": [], - "MetricAlarms": [ + "get_metric_statistics_with_different_units": { + "Datapoints": [ { - "ActionsEnabled": true, - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "testing cloudwatch alarms", - "AlarmName": "", - "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [ - { - "Name": "InstanceId", - "Value": "abc" - } - ], - "EvaluationPeriods": 1, - "InsufficientDataActions": [], - "MetricName": "my-metric1", - "Namespace": "", - "OKActions": [ - "arn::sns::111111111111:" - ], - "Period": 10, - "StateReason": "Unchecked: Initial alarm creation", - "StateTransitionedTimestamp": "timestamp", - "StateUpdatedTimestamp": "timestamp", - "StateValue": "INSUFFICIENT_DATA", - "Statistic": "Average", - "Threshold": 21.0, - "TreatMissingData": "ignore", + "Average": 10.0, + "Timestamp": "timestamp", + "Unit": "None" + }, + { + "Average": 5.0, + "Timestamp": "timestamp", + "Unit": "Count" + }, + { + "Average": 1.0, + "Timestamp": "timestamp", "Unit": "Seconds" } ], + "Label": "m-test", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "alarm-triggered-sqs-msg": { - "AWSAccountId": "111111111111", - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "date", - "AlarmDescription": "testing cloudwatch alarms", - "AlarmName": "", - "InsufficientDataActions": [], - "NewStateReason": "Threshold Crossed: 1 datapoint [21.5 (MM/DD/YY HH:MM:SS)] was greater than the threshold (21.0).", - "NewStateValue": "ALARM", - "OKActions": [ - "arn::sns::111111111111:" - ], - "OldStateValue": "INSUFFICIENT_DATA", - "Region": "", - "StateChangeTime": "date", - "Trigger": { - "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [ - { - "name": "InstanceId", - "value": "abc" - } - ], - "EvaluateLowSampleCountPercentile": "", - "EvaluationPeriods": 1, - "MetricName": "my-metric1", - "Namespace": "", - "Period": 10, - "Statistic": "AVERAGE", - "StatisticType": "Statistic", - "Threshold": 21.0, - "TreatMissingData": "ignore", - "Unit": "Seconds" - } - }, - "describe-alarm-history": { - "AlarmHistoryItems": [ + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_handle_different_units[json]": { + "recorded-date": "19-09-2025, 22:56:20", + "recorded-content": { + "get_metric_statistics_with_different_units": { + "Datapoints": [ { - "AlarmName": "", - "AlarmType": "MetricAlarm", - "HistoryData": { - "version": "1.0", - "oldState": { - "stateValue": "INSUFFICIENT_DATA", - "stateReason": "Unchecked: Initial alarm creation" - }, - "newState": { - "stateValue": "ALARM", - "stateReason": "Threshold Crossed: 1 datapoint [21.5 (MM/DD/YY HH:MM:SS)] was greater than the threshold (21.0).", - "stateReasonData": { - "version": "1.0", - "queryDate": "date", - "startDate": "date", - "unit": "Seconds", - "statistic": "Average", - "period": 10, - "recentDatapoints": [ - 21.5 - ], - "threshold": 21.0, - "evaluatedDatapoints": [ - { - "timestamp": "date", - "sampleCount": 2.0, - "value": 21.5 - } - ] - } - } - }, - "HistoryItemType": "StateUpdate", - "HistorySummary": "Alarm updated from INSUFFICIENT_DATA to ALARM", - "Timestamp": "timestamp" + "Average": 10.0, + "Timestamp": "timestamp", + "Unit": "None" + }, + { + "Average": 5.0, + "Timestamp": "timestamp", + "Unit": "Count" + }, + { + "Average": 1.0, + "Timestamp": "timestamp", + "Unit": "Seconds" } ], + "Label": "m-test", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "describe-alarms-for-metric": { - "MetricAlarms": [ + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_handle_different_units[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 22:56:22", + "recorded-content": { + "get_metric_statistics_with_different_units": { + "Datapoints": [ { - "ActionsEnabled": true, - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "testing cloudwatch alarms", - "AlarmName": "", - "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [ - { - "Name": "InstanceId", - "Value": "abc" - } - ], - "EvaluationPeriods": 1, - "InsufficientDataActions": [], - "MetricName": "my-metric1", - "Namespace": "", - "OKActions": [ - "arn::sns::111111111111:" - ], - "Period": 10, - "StateReason": "Threshold Crossed: 1 datapoint [21.5 (MM/DD/YY HH:MM:SS)] was greater than the threshold (21.0).", - "StateReasonData": { - "version": "1.0", - "queryDate": "date", - "startDate": "date", - "unit": "Seconds", - "statistic": "Average", - "period": 10, - "recentDatapoints": [ - 21.5 - ], - "threshold": 21.0, - "evaluatedDatapoints": [ - { - "timestamp": "date", - "sampleCount": 2.0, - "value": 21.5 - } - ] - }, - "StateTransitionedTimestamp": "timestamp", - "StateUpdatedTimestamp": "timestamp", - "StateValue": "ALARM", - "Statistic": "Average", - "Threshold": 21.0, - "TreatMissingData": "ignore", + "Average": 10.0, + "Timestamp": "timestamp", + "Unit": "None" + }, + { + "Average": 5.0, + "Timestamp": "timestamp", + "Unit": "Count" + }, + { + "Average": 1.0, + "Timestamp": "timestamp", "Unit": "Seconds" } ], + "Label": "m-test", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -558,33 +5125,15 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_aws_sqs_metrics_created": { - "recorded-date": "25-09-2023, 10:25:29", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_different_units[query]": { + "recorded-date": "19-09-2025, 22:57:24", "recorded-content": { - "get_metric_data": { + "get_metric_data_with_different_units": { "Messages": [], "MetricDataResults": [ { - "Id": "sent", - "Label": "NumberOfMessagesSent", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 1.0 - ] - }, - { - "Id": "sent_size", - "Label": "SentMessageSize", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 5.0 - ] - }, - { - "Id": "empty_receives", - "Label": "NumberOfEmptyReceives", + "Id": "m1", + "Label": "m-test", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ @@ -596,22 +5145,18 @@ "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "get_metric_data_2": { + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_different_units[json]": { + "recorded-date": "19-09-2025, 22:57:26", + "recorded-content": { + "get_metric_data_with_different_units": { "Messages": [], "MetricDataResults": [ { - "Id": "num_msg_received", - "Label": "NumberOfMessagesReceived", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 1.0 - ] - }, - { - "Id": "num_msg_deleted", - "Label": "NumberOfMessagesDeleted", + "Id": "m1", + "Label": "m-test", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ @@ -626,20 +5171,22 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_values_list": { - "recorded-date": "25-09-2023, 10:26:17", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_different_units[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 22:57:28", "recorded-content": { - "get_metric_statistics": { - "Datapoints": [ + "get_metric_data_with_different_units": { + "Messages": [], + "MetricDataResults": [ { - "Maximum": 10.0, - "SampleCount": 6.0, - "Sum": 42.0, - "Timestamp": "timestamp", - "Unit": "Count" + "Id": "m1", + "Label": "m-test", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 1.0 + ] } ], - "Label": "test-metric", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -647,73 +5194,84 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_store_tags": { - "recorded-date": "02-09-2024, 14:03:31", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[query-metric_data0]": { + "recorded-date": "19-09-2025, 22:59:13", "recorded-content": { - "put_metric_alarm": { - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "describe_alarms": { - "CompositeAlarms": [], - "MetricAlarms": [ + "get_metric_data_with_no_unit_specified": { + "Messages": [], + "MetricDataResults": [ { - "ActionsEnabled": true, - "AlarmActions": [], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmName": "", - "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [], - "EvaluationPeriods": 1, - "InsufficientDataActions": [], - "MetricName": "store_tags", - "Namespace": "", - "OKActions": [], - "Period": 60, - "StateReason": "Unchecked: Initial alarm creation", - "StateTransitionedTimestamp": "timestamp", - "StateUpdatedTimestamp": "timestamp", - "StateValue": "INSUFFICIENT_DATA", - "Statistic": "Sum", - "Threshold": 30.0 + "Id": "m1", + "Label": "m-test", + "Messages": [ + { + "Code": "MultipleUnits", + "Value": "Multiple units returned: '[Milliseconds, Seconds]'" + } + ], + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 60000.0 + ] } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "list_tags_for_resource_empty ": { - "Tags": [], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "list_tags_for_resource": { - "Tags": [ - { - "Key": "tag1", - "Value": "foo" - }, + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[query-metric_data1]": { + "recorded-date": "19-09-2025, 22:59:15", + "recorded-content": { + "get_metric_data_with_no_unit_specified": { + "Messages": [], + "MetricDataResults": [ { - "Key": "tag2", - "Value": "bar" + "Id": "m1", + "Label": "m-test", + "Messages": [ + { + "Code": "MultipleUnits", + "Value": "Multiple units returned: '[Milliseconds, Seconds]'" + } + ], + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 120000.0 + ] } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "list_tags_for_resource_post_untag": { - "Tags": [ + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[query-metric_data2]": { + "recorded-date": "19-09-2025, 22:59:17", + "recorded-content": { + "get_metric_data_with_no_unit_specified": { + "Messages": [], + "MetricDataResults": [ { - "Key": "tag2", - "Value": "bar" + "Id": "m1", + "Label": "m-test", + "Messages": [ + { + "Code": "MultipleUnits", + "Value": "Multiple units returned: '[Count, Milliseconds, Seconds]'" + } + ], + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 5.0 + ] } ], "ResponseMetadata": { @@ -723,123 +5281,173 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_dashboard_lifecycle": { - "recorded-date": "21-11-2023, 13:38:11", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[json-metric_data0]": { + "recorded-date": "19-09-2025, 22:59:20", "recorded-content": { - "get_dashboard": { - "DashboardArn": "arn::cloudwatch::111111111111:dashboard/", - "DashboardBody": { - "widgets": [ - { - "type": "metric", - "x": 0, - "y": 0, - "width": 6, - "height": 6, - "properties": { - "metrics": [ - [ - "AWS/EC2", - "CPUUtilization", - "InstanceId", - "i-12345678" - ] - ], - "region": "", - "view": "timeSeries", - "stacked": false - } - } - ] - }, - "DashboardName": "", - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "list_dashboards": { - "DashboardEntries": [ + "get_metric_data_with_no_unit_specified": { + "Messages": [], + "MetricDataResults": [ { - "DashboardArn": "arn::cloudwatch::111111111111:dashboard/", - "DashboardName": "", - "LastModified": "datetime", - "Size": 225 + "Id": "m1", + "Label": "m-test", + "Messages": [ + { + "Code": "MultipleUnits", + "Value": "Multiple units returned: '[Milliseconds, Seconds]'" + } + ], + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 60000.0 + ] } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "list_dashboards_prefix_empty": { - "DashboardEntries": [], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "list_dashboards_prefix": { - "DashboardEntries": [ + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[json-metric_data1]": { + "recorded-date": "19-09-2025, 22:59:22", + "recorded-content": { + "get_metric_data_with_no_unit_specified": { + "Messages": [], + "MetricDataResults": [ { - "DashboardArn": "arn::cloudwatch::111111111111:dashboard/", - "DashboardName": "", - "LastModified": "datetime", - "Size": 225 + "Id": "m1", + "Label": "m-test", + "Messages": [ + { + "Code": "MultipleUnits", + "Value": "Multiple units returned: '[Milliseconds, Seconds]'" + } + ], + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 120000.0 + ] } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "list_dashboards_empty": { - "DashboardEntries": [], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_create_metric_stream": { - "recorded-date": "26-10-2023, 09:12:10", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[json-metric_data2]": { + "recorded-date": "19-09-2025, 22:59:24", "recorded-content": { - "create_metric_stream": { - "Arn": "arn::cloudwatch::111111111111:metric-stream/", - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "get_metric_stream": { - "Arn": "arn::cloudwatch::111111111111:metric-stream/", - "CreationDate": "datetime", - "FirehoseArn": "", - "IncludeLinkedAccountsMetrics": false, - "LastUpdateDate": "datetime", - "Name": "", - "OutputFormat": "json", - "RoleArn": "", - "State": "running", + "get_metric_data_with_no_unit_specified": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "m1", + "Label": "m-test", + "Messages": [ + { + "Code": "MultipleUnits", + "Value": "Multiple units returned: '[Count, Milliseconds, Seconds]'" + } + ], + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 5.0 + ] + } + ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "start_metric_stream": { + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[smithy-rpc-v2-cbor-metric_data0]": { + "recorded-date": "19-09-2025, 22:59:27", + "recorded-content": { + "get_metric_data_with_no_unit_specified": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "m1", + "Label": "m-test", + "Messages": [ + { + "Code": "MultipleUnits", + "Value": "Multiple units returned: '[Milliseconds, Seconds]'" + } + ], + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 60000.0 + ] + } + ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "stop_metric_stream": { + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[smithy-rpc-v2-cbor-metric_data1]": { + "recorded-date": "19-09-2025, 22:59:29", + "recorded-content": { + "get_metric_data_with_no_unit_specified": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "m1", + "Label": "m-test", + "Messages": [ + { + "Code": "MultipleUnits", + "Value": "Multiple units returned: '[Milliseconds, Seconds]'" + } + ], + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 120000.0 + ] + } + ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "delete_metric_stream": { + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[smithy-rpc-v2-cbor-metric_data2]": { + "recorded-date": "19-09-2025, 22:59:31", + "recorded-content": { + "get_metric_data_with_no_unit_specified": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "m1", + "Label": "m-test", + "Messages": [ + { + "Code": "MultipleUnits", + "Value": "Multiple units returned: '[Count, Milliseconds, Seconds]'" + } + ], + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 5.0 + ] + } + ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -847,329 +5455,399 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_insight_rule": { - "recorded-date": "26-10-2023, 10:07:59", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs0]": { + "recorded-date": "19-09-2025, 23:00:36", "recorded-content": { - "create_insight_rule": { + "label_generation": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result_Sum_60_Seconds", + "Label": "metric1 Sum 60", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] + }, + { + "Id": "result_Minimum_30_Seconds", + "Label": "metric1 Minimum 30", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 0.0 + ] + } + ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "describe_insight_rule": { - "InsightRules": [ - { - "Definition": { - "Schema": { - "Name": "", - "Version": 1 - }, - "LogGroupNames": [ - "API-Gateway-Access-Logs*" - ], - "LogFormat": "CLF", - "Fields": { - "4": "IpAddress", - "7": "StatusCode" - }, - "Contribution": { - "Keys": [ - "IpAddress" - ], - "Filters": [ - { - "Match": "StatusCode", - "EqualTo": 200 - } - ] - }, - "AggregateOn": "Count" - }, - "ManagedRule": false, - "Name": "", - "Schema": "/1", - "State": "ENABLED" - }, - { - "Definition": { - "Schema": { - "Name": "", - "Version": 1 - }, - "LogGroupNames": [ - "API-Gateway-Access-Logs*" - ], - "LogFormat": "CLF", - "Fields": { - "4": "IpAddress", - "7": "StatusCode" - }, - "Contribution": { - "Keys": [ - "IpAddress" - ], - "Filters": [ - { - "Match": "StatusCode", - "EqualTo": 200 - } - ] - }, - "AggregateOn": "Count" - }, - "ManagedRule": false, - "Name": "", - "Schema": "/1", - "State": "ENABLED" - }, + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs1]": { + "recorded-date": "19-09-2025, 23:00:37", + "recorded-content": { + "label_generation": { + "Messages": [], + "MetricDataResults": [ { - "Definition": { - "Schema": { - "Name": "", - "Version": 1 - }, - "LogGroupNames": [ - "API-Gateway-Access-Logs*" - ], - "LogFormat": "CLF", - "Fields": { - "4": "IpAddress", - "7": "StatusCode" - }, - "Contribution": { - "Keys": [ - "IpAddress" - ], - "Filters": [ - { - "Match": "StatusCode", - "EqualTo": 200 - } - ] - }, - "AggregateOn": "Count" - }, - "ManagedRule": false, - "Name": "", - "Schema": "/1", - "State": "ENABLED" + "Id": "result_Sum_60_Seconds", + "Label": "metric1 Sum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] + }, + { + "Id": "result_Minimum_60_Seconds", + "Label": "metric1 Minimum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 0.0 + ] } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "disable_insight_rule": { - "Failures": [], + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs2]": { + "recorded-date": "19-09-2025, 23:00:39", + "recorded-content": { + "label_generation": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result_Sum_60_Seconds", + "Label": "metric1 60", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] + }, + { + "Id": "result_Sum_30_Seconds", + "Label": "metric1 30", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] + } + ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "enable_insight_rule": { - "Failures": [], + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs3]": { + "recorded-date": "19-09-2025, 23:00:40", + "recorded-content": { + "label_generation": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result_Sum_60_Seconds", + "Label": "metric1 Sum 60", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] + }, + { + "Id": "result_Minimum_30_Milliseconds", + "Label": "metric1 Minimum 30", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] + } + ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "get_insight_rule_report": { - "AggregateValue": 0.0, - "AggregationStatistic": "SampleCount", - "ApproximateUniqueCount": 0, - "Contributors": [], - "KeyLabels": [ - "IpAddress" - ], - "MetricDatapoints": [ - { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 - }, + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs4]": { + "recorded-date": "19-09-2025, 23:00:42", + "recorded-content": { + "label_generation": { + "Messages": [], + "MetricDataResults": [ { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 + "Id": "result_Sum_60_Seconds", + "Label": "metric1 Sum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] }, { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 - }, + "Id": "result_Minimum_60_Milliseconds", + "Label": "metric1 Minimum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs5]": { + "recorded-date": "19-09-2025, 23:00:44", + "recorded-content": { + "label_generation": { + "Messages": [], + "MetricDataResults": [ { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 + "Id": "result_Sum_60_Seconds", + "Label": "metric1 60", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] }, { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 - }, + "Id": "result_Sum_30_Milliseconds", + "Label": "metric1 30", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs6]": { + "recorded-date": "19-09-2025, 23:00:45", + "recorded-content": { + "label_generation": { + "Messages": [], + "MetricDataResults": [ { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 + "Id": "result_Sum_60_Seconds", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] }, { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 - }, + "Id": "result_Sum_60_Milliseconds", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs0]": { + "recorded-date": "19-09-2025, 23:00:46", + "recorded-content": { + "label_generation": { + "Messages": [], + "MetricDataResults": [ { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 + "Id": "result_Sum_60_Seconds", + "Label": "metric1 Sum 60", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] }, { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 - }, + "Id": "result_Minimum_30_Seconds", + "Label": "metric1 Minimum 30", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 0.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs1]": { + "recorded-date": "19-09-2025, 23:00:47", + "recorded-content": { + "label_generation": { + "Messages": [], + "MetricDataResults": [ { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 + "Id": "result_Sum_60_Seconds", + "Label": "metric1 Sum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] }, { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 - }, + "Id": "result_Minimum_60_Seconds", + "Label": "metric1 Minimum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 0.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs2]": { + "recorded-date": "19-09-2025, 23:00:49", + "recorded-content": { + "label_generation": { + "Messages": [], + "MetricDataResults": [ { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 + "Id": "result_Sum_60_Seconds", + "Label": "metric1 60", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] }, { - "Timestamp": "timestamp", - "UniqueContributors": 0.0 + "Id": "result_Sum_30_Seconds", + "Label": "metric1 30", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "delete_insight_rule": { - "Failures": [], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_anomaly_detector_lifecycle": { - "recorded-date": "26-10-2023, 10:42:43", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs3]": { + "recorded-date": "19-09-2025, 23:00:50", "recorded-content": { - "create_anomaly_detector": { - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "describe_anomaly_detector": { - "AnomalyDetectors": [ + "label_generation": { + "Messages": [], + "MetricDataResults": [ { - "Configuration": { - "ExcludedTimeRanges": [] - }, - "Dimensions": [ - { - "Name": "DimensionName", - "Value": "DimensionValue" - } - ], - "MetricName": "MyMetric", - "Namespace": "MyNamespace", - "SingleMetricAnomalyDetector": { - "Dimensions": [ - { - "Name": "DimensionName", - "Value": "DimensionValue" - } - ], - "MetricName": "MyMetric", - "Namespace": "MyNamespace", - "Stat": "Sum" - }, - "Stat": "Sum", - "StateValue": "PENDING_TRAINING" + "Id": "result_Sum_60_Seconds", + "Label": "metric1 Sum 60", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] + }, + { + "Id": "result_Minimum_30_Milliseconds", + "Label": "metric1 Minimum 30", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "delete_anomaly_detector": { - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_alarm_lambda_target": { - "recorded-date": "03-01-2024, 17:30:00", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs4]": { + "recorded-date": "19-09-2025, 23:00:52", "recorded-content": { - "lambda-alarm-invocations": { - "source": "aws.cloudwatch", - "alarmArn": "arn::cloudwatch::111111111111:alarm:", - "accountId": "111111111111", - "time": "date", - "region": "", - "alarmData": { - "alarmName": "", - "state": { - "value": "ALARM", - "reason": "testing alarm", - "timestamp": "date" - }, - "previousState": { - "value": "INSUFFICIENT_DATA", - "reason": "Unchecked: Initial alarm creation", - "timestamp": "date" - }, - "configuration": { - "description": "testing lambda alarm action", - "metrics": [ - { - "id": "", - "metricStat": { - "metric": { - "namespace": "namespace", - "name": "metric1", - "dimensions": {} - }, - "period": 10, - "stat": "Average" - }, - "returnData": true - } + "label_generation": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result_Sum_60_Seconds", + "Label": "metric1 Sum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 ] + }, + { + "Id": "result_Minimum_60_Milliseconds", + "Label": "metric1 Minimum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 } } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_minimal_metric_alarm": { - "recorded-date": "25-10-2023, 17:17:06", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs5]": { + "recorded-date": "19-09-2025, 23:00:53", "recorded-content": { - "describe_minimal_metric_alarm": { - "CompositeAlarms": [], - "MetricAlarms": [ + "label_generation": { + "Messages": [], + "MetricDataResults": [ { - "ActionsEnabled": true, - "AlarmActions": [], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmName": "", - "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [], - "EvaluationPeriods": 1, - "InsufficientDataActions": [], - "MetricName": "", - "Namespace": "", - "OKActions": [], - "Period": 10, - "StateReason": "Unchecked: Initial alarm creation", - "StateTransitionedTimestamp": "timestamp", - "StateUpdatedTimestamp": "timestamp", - "StateValue": "INSUFFICIENT_DATA", - "Statistic": "Sum", - "Threshold": 30.0 + "Id": "result_Sum_60_Seconds", + "Label": "metric1 60", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] + }, + { + "Id": "result_Sum_30_Milliseconds", + "Label": "metric1 30", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] } ], "ResponseMetadata": { @@ -1179,63 +5857,90 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm_invalid_input": { - "recorded-date": "24-11-2023, 12:23:16", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs6]": { + "recorded-date": "19-09-2025, 23:00:55", "recorded-content": { - "error-invalid-state": { - "Error": { - "Code": "ValidationError", - "Message": "1 validation error detected: Value 'INVALID' at 'stateValue' failed to satisfy constraint: Member must satisfy enum value set: [INSUFFICIENT_DATA, ALARM, OK]", - "Type": "Sender" - }, - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 400 - } - }, - "error-resource-not-found": { - "Error": { - "Code": "ResourceNotFound", - "Type": "Sender" - }, + "label_generation": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result_Sum_60_Seconds", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 + ] + }, + { + "Id": "result_Sum_60_Milliseconds", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] + } + ], "ResponseMetadata": { "HTTPHeaders": {}, - "HTTPStatusCode": 404 + "HTTPStatusCode": 200 } } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_for_multiple_metrics": { - "recorded-date": "23-11-2023, 14:39:07", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs0]": { + "recorded-date": "19-09-2025, 23:00:56", "recorded-content": { - "get_metric_data": { + "label_generation": { "Messages": [], "MetricDataResults": [ { - "Id": "result1", - "Label": "metric1", + "Id": "result_Sum_60_Seconds", + "Label": "metric1 Sum 60", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ - 50.0 + 109.0 ] }, { - "Id": "result2", - "Label": "metric2", + "Id": "result_Minimum_30_Seconds", + "Label": "metric1 Minimum 30", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ - 25.0 + 0.0 + ] + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs1]": { + "recorded-date": "19-09-2025, 23:00:58", + "recorded-content": { + "label_generation": { + "Messages": [], + "MetricDataResults": [ + { + "Id": "result_Sum_60_Seconds", + "Label": "metric1 Sum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 ] }, { - "Id": "result3", - "Label": "metric3", + "Id": "result_Minimum_60_Seconds", + "Label": "metric1 Minimum", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ - 55.0 + 0.0 ] } ], @@ -1246,19 +5951,28 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[Sum]": { - "recorded-date": "04-12-2023, 12:22:53", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs2]": { + "recorded-date": "19-09-2025, 23:00:59", "recorded-content": { - "get_metric_data": { + "label_generation": { "Messages": [], "MetricDataResults": [ { - "Id": "result1", - "Label": "metric1", + "Id": "result_Sum_60_Seconds", + "Label": "metric1 60", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ - 66.0 + 109.0 + ] + }, + { + "Id": "result_Sum_30_Seconds", + "Label": "metric1 30", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [ + 109.0 ] } ], @@ -1269,20 +5983,27 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[SampleCount]": { - "recorded-date": "04-12-2023, 12:22:55", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs3]": { + "recorded-date": "19-09-2025, 23:01:01", "recorded-content": { - "get_metric_data": { + "label_generation": { "Messages": [], "MetricDataResults": [ { - "Id": "result1", - "Label": "metric1", + "Id": "result_Sum_60_Seconds", + "Label": "metric1 Sum 60", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ - 11.0 + 109.0 ] + }, + { + "Id": "result_Minimum_30_Milliseconds", + "Label": "metric1 Minimum 30", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] } ], "ResponseMetadata": { @@ -1292,20 +6013,27 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[Minimum]": { - "recorded-date": "04-12-2023, 12:22:58", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs4]": { + "recorded-date": "19-09-2025, 23:01:02", "recorded-content": { - "get_metric_data": { + "label_generation": { "Messages": [], "MetricDataResults": [ { - "Id": "result1", - "Label": "metric1", + "Id": "result_Sum_60_Seconds", + "Label": "metric1 Sum", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ - 1.0 + 109.0 ] + }, + { + "Id": "result_Minimum_60_Milliseconds", + "Label": "metric1 Minimum", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] } ], "ResponseMetadata": { @@ -1315,20 +6043,27 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[Maximum]": { - "recorded-date": "04-12-2023, 12:23:00", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs5]": { + "recorded-date": "19-09-2025, 23:01:03", "recorded-content": { - "get_metric_data": { + "label_generation": { "Messages": [], "MetricDataResults": [ { - "Id": "result1", - "Label": "metric1", + "Id": "result_Sum_60_Seconds", + "Label": "metric1 60", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ - 11.0 + 109.0 ] + }, + { + "Id": "result_Sum_30_Milliseconds", + "Label": "metric1 30", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] } ], "ResponseMetadata": { @@ -1338,20 +6073,27 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[Average]": { - "recorded-date": "04-12-2023, 12:23:02", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs6]": { + "recorded-date": "19-09-2025, 23:01:05", "recorded-content": { - "get_metric_data": { + "label_generation": { "Messages": [], "MetricDataResults": [ { - "Id": "result1", + "Id": "result_Sum_60_Seconds", "Label": "metric1", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ - 6.0 + 109.0 ] + }, + { + "Id": "result_Sum_60_Milliseconds", + "Label": "metric1", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] } ], "ResponseMetadata": { @@ -1361,20 +6103,18 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_dimensions": { - "recorded-date": "23-11-2023, 15:10:11", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_null_dimensions[query]": { + "recorded-date": "19-09-2025, 23:02:11", "recorded-content": { - "get_metric_data": { + "get_metric_with_null_dimensions": { "Messages": [], "MetricDataResults": [ { - "Id": "result1", - "Label": "metric1", + "Id": "", + "Label": "", "StatusCode": "Complete", "Timestamps": "timestamp", - "Values": [ - 11.0 - ] + "Values": [] } ], "ResponseMetadata": { @@ -1384,22 +6124,20 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_statistics": { - "recorded-date": "04-12-2023, 14:13:08", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_null_dimensions[json]": { + "recorded-date": "19-09-2025, 23:02:13", "recorded-content": { - "get_metric_statistics": { - "Datapoints": [ + "get_metric_with_null_dimensions": { + "Messages": [], + "MetricDataResults": [ { - "Average": 4.5, - "Maximum": 9.0, - "Minimum": 0.0, - "SampleCount": 10.0, - "Sum": 45.0, - "Timestamp": "timestamp", - "Unit": "None" + "Id": "", + "Label": "", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] } ], - "Label": "metric", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -1407,47 +6145,18 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_zero_and_labels": { - "recorded-date": "04-12-2023, 09:13:59", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_null_dimensions[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 23:02:15", "recorded-content": { - "get_metric_data_with_zero_and_labels": { + "get_metric_with_null_dimensions": { "Messages": [], "MetricDataResults": [ { - "Id": "result_Average", - "Label": "metric1 Average", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 19.416666666666668 - ] - }, - { - "Id": "result_Sum", - "Label": "metric1 Sum", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 116.5 - ] - }, - { - "Id": "result_Minimum", - "Label": "metric1 Minimum", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 0.0 - ] - }, - { - "Id": "result_Maximum", - "Label": "metric1 Maximum", + "Id": "", + "Label": "", "StatusCode": "Complete", "Timestamps": "timestamp", - "Values": [ - 100.0 - ] + "Values": [] } ], "ResponseMetadata": { @@ -1457,28 +6166,20 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_handle_different_units": { - "recorded-date": "05-01-2024, 16:15:39", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_no_results[query]": { + "recorded-date": "19-09-2025, 23:04:56", "recorded-content": { - "get_metric_statistics_with_different_units": { - "Datapoints": [ - { - "Average": 10.0, - "Timestamp": "timestamp", - "Unit": "None" - }, - { - "Average": 5.0, - "Timestamp": "timestamp", - "Unit": "Count" - }, + "result": { + "Messages": [], + "MetricDataResults": [ { - "Average": 1.0, - "Timestamp": "timestamp", - "Unit": "Seconds" + "Id": "result", + "Label": "", + "StatusCode": "Complete", + "Timestamps": "timestamp", + "Values": [] } ], - "Label": "m-test", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -1486,20 +6187,18 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_different_units": { - "recorded-date": "07-12-2023, 14:04:49", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_no_results[json]": { + "recorded-date": "19-09-2025, 23:05:05", "recorded-content": { - "get_metric_data_with_different_units": { + "result": { "Messages": [], "MetricDataResults": [ { - "Id": "m1", - "Label": "m-test", + "Id": "result", + "Label": "", "StatusCode": "Complete", "Timestamps": "timestamp", - "Values": [ - 1.0 - ] + "Values": [] } ], "ResponseMetadata": { @@ -1509,26 +6208,18 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[metric_data0]": { - "recorded-date": "15-12-2023, 08:58:39", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_no_results[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 23:05:13", "recorded-content": { - "get_metric_data_with_no_unit_specified": { + "result": { "Messages": [], "MetricDataResults": [ { - "Id": "m1", - "Label": "m-test", - "Messages": [ - { - "Code": "MultipleUnits", - "Value": "Multiple units returned: '[Milliseconds, Seconds]'" - } - ], + "Id": "result", + "Label": "", "StatusCode": "Complete", "Timestamps": "timestamp", - "Values": [ - 60000.0 - ] + "Values": [] } ], "ResponseMetadata": { @@ -1538,63 +6229,97 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[input_pairs0]": { - "recorded-date": "15-12-2023, 11:27:23", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_delete_alarm[query]": { + "recorded-date": "19-09-2025, 23:06:53", "recorded-content": { - "label_generation": { - "Messages": [], - "MetricDataResults": [ + "describe-alarm": { + "CompositeAlarms": [], + "MetricAlarms": [ { - "Id": "result_Sum_60_Seconds", - "Label": "metric1 Sum 60", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 109.0 - ] - }, + "ActionsEnabled": true, + "AlarmActions": [], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "metric1", + "Namespace": "", + "OKActions": [], + "Period": 60, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 30.0 + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "delete-alarm": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe-after-delete": { + "CompositeAlarms": [], + "MetricAlarms": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_delete_alarm[json]": { + "recorded-date": "19-09-2025, 23:06:54", + "recorded-content": { + "describe-alarm": { + "CompositeAlarms": [], + "MetricAlarms": [ { - "Id": "result_Minimum_30_Seconds", - "Label": "metric1 Minimum 30", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 0.0 - ] + "ActionsEnabled": true, + "AlarmActions": [], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "metric1", + "Namespace": "", + "OKActions": [], + "Period": 60, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 30.0 } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - } - } - }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[input_pairs1]": { - "recorded-date": "15-12-2023, 11:27:25", - "recorded-content": { - "label_generation": { - "Messages": [], - "MetricDataResults": [ - { - "Id": "result_Sum_60_Seconds", - "Label": "metric1 Sum", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 109.0 - ] - }, - { - "Id": "result_Minimum_60_Seconds", - "Label": "metric1 Minimum", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 0.0 - ] - } - ], + }, + "delete-alarm": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe-after-delete": { + "CompositeAlarms": [], + "MetricAlarms": [], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -1602,57 +6327,68 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[metric_data1]": { - "recorded-date": "15-12-2023, 08:58:42", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_delete_alarm[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 23:06:55", "recorded-content": { - "get_metric_data_with_no_unit_specified": { - "Messages": [], - "MetricDataResults": [ + "describe-alarm": { + "CompositeAlarms": [], + "MetricAlarms": [ { - "Id": "m1", - "Label": "m-test", - "Messages": [ - { - "Code": "MultipleUnits", - "Value": "Multiple units returned: '[Milliseconds, Seconds]'" - } - ], - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 120000.0 - ] + "ActionsEnabled": true, + "AlarmActions": [], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [], + "EvaluationPeriods": 1, + "InsufficientDataActions": [], + "MetricName": "metric1", + "Namespace": "", + "OKActions": [], + "Period": 60, + "StateReason": "Unchecked: Initial alarm creation", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "INSUFFICIENT_DATA", + "Statistic": "Sum", + "Threshold": 30.0 } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } + }, + "delete-alarm": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe-after-delete": { + "CompositeAlarms": [], + "MetricAlarms": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[input_pairs2]": { - "recorded-date": "15-12-2023, 11:27:27", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_multiple_dimensions_statistics[query]": { + "recorded-date": "19-09-2025, 23:07:39", "recorded-content": { - "label_generation": { + "get-metric-stats-max": { "Messages": [], "MetricDataResults": [ { - "Id": "result_Sum_60_Seconds", - "Label": "metric1 60", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 109.0 - ] - }, - { - "Id": "result_Sum_30_Seconds", - "Label": "metric1 30", + "Id": "result1", + "Label": "http.server.requests.count", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ - 109.0 + 5.0 ] } ], @@ -1660,29 +6396,38 @@ "HTTPHeaders": {}, "HTTPStatusCode": 200 } - } - } - }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[metric_data2]": { - "recorded-date": "15-12-2023, 08:58:44", - "recorded-content": { - "get_metric_data_with_no_unit_specified": { - "Messages": [], - "MetricDataResults": [ + }, + "list-metrics": { + "Metrics": [ { - "Id": "m1", - "Label": "m-test", - "Messages": [ + "Dimensions": [ { - "Code": "MultipleUnits", - "Value": "Multiple units returned: '[Count, Milliseconds, Seconds]'" + "Name": "error", + "Value": "none" + }, + { + "Name": "exception", + "Value": "none" + }, + { + "Name": "method", + "Value": "GET" + }, + { + "Name": "outcome", + "Value": "SUCCESS" + }, + { + "Name": "status", + "Value": "200" + }, + { + "Name": "uri", + "Value": "/greetings" } ], - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 5.0 - ] + "MetricName": "http.server.requests.count", + "Namespace": "" } ], "ResponseMetadata": { @@ -1692,57 +6437,58 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[input_pairs3]": { - "recorded-date": "15-12-2023, 11:27:29", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_multiple_dimensions_statistics[json]": { + "recorded-date": "19-09-2025, 23:07:47", "recorded-content": { - "label_generation": { + "get-metric-stats-max": { "Messages": [], "MetricDataResults": [ { - "Id": "result_Sum_60_Seconds", - "Label": "metric1 Sum 60", + "Id": "result1", + "Label": "http.server.requests.count", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ - 109.0 + 5.0 ] - }, - { - "Id": "result_Minimum_30_Milliseconds", - "Label": "metric1 Minimum 30", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [] } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - } - } - }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[input_pairs4]": { - "recorded-date": "15-12-2023, 11:27:31", - "recorded-content": { - "label_generation": { - "Messages": [], - "MetricDataResults": [ - { - "Id": "result_Sum_60_Seconds", - "Label": "metric1 Sum", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 109.0 - ] - }, + }, + "list-metrics": { + "Metrics": [ { - "Id": "result_Minimum_60_Milliseconds", - "Label": "metric1 Minimum", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [] + "Dimensions": [ + { + "Name": "error", + "Value": "none" + }, + { + "Name": "exception", + "Value": "none" + }, + { + "Name": "method", + "Value": "GET" + }, + { + "Name": "outcome", + "Value": "SUCCESS" + }, + { + "Name": "status", + "Value": "200" + }, + { + "Name": "uri", + "Value": "/greetings" + } + ], + "MetricName": "http.server.requests.count", + "Namespace": "" } ], "ResponseMetadata": { @@ -1752,27 +6498,58 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[input_pairs5]": { - "recorded-date": "15-12-2023, 11:27:33", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_multiple_dimensions_statistics[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 23:07:59", "recorded-content": { - "label_generation": { + "get-metric-stats-max": { "Messages": [], "MetricDataResults": [ { - "Id": "result_Sum_60_Seconds", - "Label": "metric1 60", + "Id": "result1", + "Label": "http.server.requests.count", "StatusCode": "Complete", "Timestamps": "timestamp", "Values": [ - 109.0 + 5.0 ] - }, + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list-metrics": { + "Metrics": [ { - "Id": "result_Sum_30_Milliseconds", - "Label": "metric1 30", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [] + "Dimensions": [ + { + "Name": "error", + "Value": "none" + }, + { + "Name": "exception", + "Value": "none" + }, + { + "Name": "method", + "Value": "GET" + }, + { + "Name": "outcome", + "Value": "SUCCESS" + }, + { + "Name": "status", + "Value": "200" + }, + { + "Name": "uri", + "Value": "/greetings" + } + ], + "MetricName": "http.server.requests.count", + "Namespace": "" } ], "ResponseMetadata": { @@ -1782,29 +6559,34 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[input_pairs6]": { - "recorded-date": "15-12-2023, 11:27:35", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_amount_of_datapoints[query]": { + "recorded-date": "19-09-2025, 23:08:52", "recorded-content": { - "label_generation": { - "Messages": [], - "MetricDataResults": [ - { - "Id": "result_Sum_60_Seconds", - "Label": "metric1", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 109.0 - ] - }, - { - "Id": "result_Sum_60_Milliseconds", - "Label": "metric1", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [] - } - ], + "error-invalid-amount-datapoints": { + "Error": { + "Code": "InvalidParameterCombination", + "Message": "You have requested up to 86400 datapoints, which exceeds the limit of 1440. You may reduce the datapoints requested by increasing Period, or decreasing the time range.", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "error-invalid-time-frame": { + "Error": { + "Code": "InvalidParameterValue", + "Message": "The parameter StartTime must be less than the parameter EndTime.", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "get-metric-statitics": { + "Datapoints": [], + "Label": "metric_name", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -1812,20 +6594,38 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_no_results": { - "recorded-date": "10-01-2024, 15:29:50", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_amount_of_datapoints[json]": { + "recorded-date": "19-09-2025, 23:08:53", "recorded-content": { - "result": { - "Messages": [], - "MetricDataResults": [ - { - "Id": "result", - "Label": "", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [] - } - ], + "error-invalid-amount-datapoints": { + "Error": { + "Code": "InvalidParameterCombination", + "Message": "You have requested up to 86400 datapoints, which exceeds the limit of 1440. You may reduce the datapoints requested by increasing Period, or decreasing the time range.", + "QueryErrorCode": "InvalidParameterCombinationException", + "Type": "Sender" + }, + "message": "You have requested up to 86400 datapoints, which exceeds the limit of 1440. You may reduce the datapoints requested by increasing Period, or decreasing the time range.", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "error-invalid-time-frame": { + "Error": { + "Code": "InvalidParameterValue", + "Message": "The parameter StartTime must be less than the parameter EndTime.", + "QueryErrorCode": "InvalidParameterValueException", + "Type": "Sender" + }, + "message": "The parameter StartTime must be less than the parameter EndTime.", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "get-metric-statitics": { + "Datapoints": [], + "Label": "metric_name", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -1833,20 +6633,38 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_null_dimensions": { - "recorded-date": "09-01-2024, 20:13:11", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_amount_of_datapoints[smithy-rpc-v2-cbor]": { + "recorded-date": "19-09-2025, 23:08:54", "recorded-content": { - "get_metric_with_null_dimensions": { - "Messages": [], - "MetricDataResults": [ - { - "Id": "", - "Label": "", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [] - } - ], + "error-invalid-amount-datapoints": { + "Error": { + "Code": "InvalidParameterCombination", + "Message": "You have requested up to 86400 datapoints, which exceeds the limit of 1440. You may reduce the datapoints requested by increasing Period, or decreasing the time range.", + "QueryErrorCode": "InvalidParameterCombinationException", + "Type": "Sender" + }, + "message": "You have requested up to 86400 datapoints, which exceeds the limit of 1440. You may reduce the datapoints requested by increasing Period, or decreasing the time range.", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "error-invalid-time-frame": { + "Error": { + "Code": "InvalidParameterValue", + "Message": "The parameter StartTime must be less than the parameter EndTime.", + "QueryErrorCode": "InvalidParameterValueException", + "Type": "Sender" + }, + "message": "The parameter StartTime must be less than the parameter EndTime.", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "get-metric-statitics": { + "Datapoints": [], + "Label": "metric_name", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -1854,8 +6672,8 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm": { - "recorded-date": "19-01-2024, 15:29:42", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm[query]": { + "recorded-date": "22-09-2025, 19:14:19", "recorded-content": { "triggered-alarm": { "CompositeAlarms": [], @@ -1943,32 +6761,44 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_delete_alarm": { - "recorded-date": "12-01-2024, 14:06:14", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm[json]": { + "recorded-date": "22-09-2025, 19:14:26", "recorded-content": { - "describe-alarm": { + "triggered-alarm": { "CompositeAlarms": [], "MetricAlarms": [ { "ActionsEnabled": true, - "AlarmActions": [], + "AlarmActions": [ + "arn::sns::111111111111:" + ], "AlarmArn": "arn::cloudwatch::111111111111:alarm:", "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "Test Alarm when CPU exceeds 50 percent", "AlarmName": "", "ComparisonOperator": "GreaterThanThreshold", - "Dimensions": [], - "EvaluationPeriods": 1, + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "i-0317828c84edbe100" + } + ], + "EvaluationPeriods": 2, "InsufficientDataActions": [], - "MetricName": "metric1", + "MetricName": "CPUUtilization-3", "Namespace": "", - "OKActions": [], - "Period": 60, - "StateReason": "Unchecked: Initial alarm creation", + "OKActions": [ + "" + ], + "Period": 300, + "StateReason": "testing alarm", "StateTransitionedTimestamp": "timestamp", "StateUpdatedTimestamp": "timestamp", - "StateValue": "INSUFFICIENT_DATA", - "Statistic": "Sum", - "Threshold": 30.0 + "StateValue": "ALARM", + "Statistic": "Average", + "Threshold": 50.0, + "TreatMissingData": "ignore", + "Unit": "Percent" } ], "ResponseMetadata": { @@ -1976,15 +6806,43 @@ "HTTPStatusCode": 200 } }, - "delete-alarm": { - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "describe-after-delete": { + "reset-alarm": { "CompositeAlarms": [], - "MetricAlarms": [], + "MetricAlarms": [ + { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "Test Alarm when CPU exceeds 50 percent", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "i-0317828c84edbe100" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "CPUUtilization-3", + "Namespace": "", + "OKActions": [ + "" + ], + "Period": 300, + "StateReason": "resetting alarm", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK", + "Statistic": "Average", + "Threshold": 50.0, + "TreatMissingData": "ignore", + "Unit": "Percent" + } + ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -1992,20 +6850,44 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_multiple_dimensions_statistics": { - "recorded-date": "26-07-2024, 15:38:56", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm[smithy-rpc-v2-cbor]": { + "recorded-date": "22-09-2025, 19:14:33", "recorded-content": { - "get-metric-stats-max": { - "Messages": [], - "MetricDataResults": [ + "triggered-alarm": { + "CompositeAlarms": [], + "MetricAlarms": [ { - "Id": "result1", - "Label": "http.server.requests.count", - "StatusCode": "Complete", - "Timestamps": "timestamp", - "Values": [ - 5.0 - ] + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "Test Alarm when CPU exceeds 50 percent", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "InstanceId", + "Value": "i-0317828c84edbe100" + } + ], + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "CPUUtilization-3", + "Namespace": "", + "OKActions": [ + "" + ], + "Period": 300, + "StateReason": "testing alarm", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "ALARM", + "Statistic": "Average", + "Threshold": 50.0, + "TreatMissingData": "ignore", + "Unit": "Percent" } ], "ResponseMetadata": { @@ -2013,37 +6895,41 @@ "HTTPStatusCode": 200 } }, - "list-metrics": { - "Metrics": [ + "reset-alarm": { + "CompositeAlarms": [], + "MetricAlarms": [ { + "ActionsEnabled": true, + "AlarmActions": [ + "arn::sns::111111111111:" + ], + "AlarmArn": "arn::cloudwatch::111111111111:alarm:", + "AlarmConfigurationUpdatedTimestamp": "timestamp", + "AlarmDescription": "Test Alarm when CPU exceeds 50 percent", + "AlarmName": "", + "ComparisonOperator": "GreaterThanThreshold", "Dimensions": [ { - "Name": "error", - "Value": "none" - }, - { - "Name": "exception", - "Value": "none" - }, - { - "Name": "method", - "Value": "GET" - }, - { - "Name": "outcome", - "Value": "SUCCESS" - }, - { - "Name": "status", - "Value": "200" - }, - { - "Name": "uri", - "Value": "/greetings" + "Name": "InstanceId", + "Value": "i-0317828c84edbe100" } ], - "MetricName": "http.server.requests.count", - "Namespace": "" + "EvaluationPeriods": 2, + "InsufficientDataActions": [], + "MetricName": "CPUUtilization-3", + "Namespace": "", + "OKActions": [ + "" + ], + "Period": 300, + "StateReason": "resetting alarm", + "StateTransitionedTimestamp": "timestamp", + "StateUpdatedTimestamp": "timestamp", + "StateValue": "OK", + "Statistic": "Average", + "Threshold": 50.0, + "TreatMissingData": "ignore", + "Unit": "Percent" } ], "ResponseMetadata": { @@ -2053,13 +6939,13 @@ } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_amount_of_datapoints": { - "recorded-date": "23-08-2024, 14:14:54", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm_invalid_input[query]": { + "recorded-date": "22-09-2025, 19:16:47", "recorded-content": { - "error-invalid-amount-datapoints": { + "error-invalid-state": { "Error": { - "Code": "InvalidParameterCombination", - "Message": "You have requested up to 86400 datapoints, which exceeds the limit of 1440. You may reduce the datapoints requested by increasing Period, or decreasing the time range.", + "Code": "ValidationError", + "Message": "1 validation error detected: Value 'INVALID' at 'stateValue' failed to satisfy constraint: Member must satisfy enum value set: [INSUFFICIENT_DATA, ALARM, OK]", "Type": "Sender" }, "ResponseMetadata": { @@ -2067,201 +6953,73 @@ "HTTPStatusCode": 400 } }, - "error-invalid-time-frame": { + "error-resource-not-found": { "Error": { - "Code": "InvalidParameterValue", - "Message": "The parameter StartTime must be less than the parameter EndTime.", + "Code": "ResourceNotFound", "Type": "Sender" }, "ResponseMetadata": { "HTTPHeaders": {}, - "HTTPStatusCode": 400 - } - }, - "get-metric-statitics": { - "Datapoints": [], - "Label": "metric_name", - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 + "HTTPStatusCode": 404 } } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_dashboard_name": { - "recorded-date": "04-09-2024, 16:28:05", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm_invalid_input[json]": { + "recorded-date": "22-09-2025, 19:16:48", "recorded-content": { - "error-invalid-dashboardname": { + "error-invalid-state": { "Error": { - "Code": "InvalidParameterValue", - "Message": "The value for field DashboardName contains invalid characters. It can only contain alphanumerics, dash (-) and underscore (_).\n", + "Code": "ValidationError", + "Message": "1 validation error detected: Value 'INVALID' at 'stateValue' failed to satisfy constraint: Member must satisfy enum value set: [INSUFFICIENT_DATA, ALARM, OK]", + "QueryErrorCode": "ValidationException", "Type": "Sender" }, "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 400 } + }, + "error-resource-not-found": { + "Error": { + "Code": "ResourceNotFound", + "Message": "", + "QueryErrorCode": "ResourceNotFound", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 404 + } } } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_trigger_composite_alarm": { - "recorded-date": "14-11-2024, 14:25:30", + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm_invalid_input[smithy-rpc-v2-cbor]": { + "recorded-date": "22-09-2025, 19:16:48", "recorded-content": { - "put-composite-alarm": { + "error-invalid-state": { + "Error": { + "Code": "ValidationError", + "Message": "1 validation error detected: Value 'INVALID' at 'stateValue' failed to satisfy constraint: Member must satisfy enum value set: [INSUFFICIENT_DATA, ALARM, OK]", + "QueryErrorCode": "ValidationException", + "Type": "Sender" + }, "ResponseMetadata": { "HTTPHeaders": {}, - "HTTPStatusCode": 200 + "HTTPStatusCode": 400 } }, - "composite-alarm-in-alarm-when-alarm-1-is-in-alarm": { - "ActionsEnabled": true, - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "composite alarm description", - "AlarmName": "", - "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", - "InsufficientDataActions": [], - "OKActions": [ - "arn::sns::111111111111:" - ], - "StateReason": "", - "StateReasonData": { - "triggeringAlarms": [ - { - "arn": "arn::cloudwatch::111111111111:alarm:", - "state": { - "value": "ALARM", - "timestamp": "date" - } - } - ] - }, - "StateTransitionedTimestamp": "timestamp", - "StateUpdatedTimestamp": "timestamp", - "StateValue": "ALARM" - }, - "composite-alarm-in-ok-when-alarm-1-is-back-to-ok": { - "ActionsEnabled": true, - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "composite alarm description", - "AlarmName": "", - "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", - "InsufficientDataActions": [], - "OKActions": [ - "arn::sns::111111111111:" - ], - "StateReason": "", - "StateReasonData": { - "triggeringAlarms": [ - { - "arn": "arn::cloudwatch::111111111111:alarm:", - "state": { - "value": "OK", - "timestamp": "date" - } - } - ] - }, - "StateTransitionedTimestamp": "timestamp", - "StateUpdatedTimestamp": "timestamp", - "StateValue": "OK" - }, - "composite-alarm-in-alarm-when-alarm-2-is-in-alarm": { - "ActionsEnabled": true, - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "composite alarm description", - "AlarmName": "", - "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", - "InsufficientDataActions": [], - "OKActions": [ - "arn::sns::111111111111:" - ], - "StateReason": "", - "StateReasonData": { - "triggeringAlarms": [ - { - "arn": "arn::cloudwatch::111111111111:alarm:", - "state": { - "value": "ALARM", - "timestamp": "date" - } - } - ] - }, - "StateTransitionedTimestamp": "timestamp", - "StateUpdatedTimestamp": "timestamp", - "StateValue": "ALARM" - }, - "composite-alarm-in-ok-when-alarm-2-is-back-to-ok": { - "ActionsEnabled": true, - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "composite alarm description", - "AlarmName": "", - "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", - "InsufficientDataActions": [], - "OKActions": [ - "arn::sns::111111111111:" - ], - "StateReason": "", - "StateReasonData": { - "triggeringAlarms": [ - { - "arn": "arn::cloudwatch::111111111111:alarm:", - "state": { - "value": "OK", - "timestamp": "date" - } - } - ] - }, - "StateTransitionedTimestamp": "timestamp", - "StateUpdatedTimestamp": "timestamp", - "StateValue": "OK" - }, - "composite-alarm-is-triggered-by-alarm-1-and-then-unchanged-by-alarm-2": { - "ActionsEnabled": true, - "AlarmActions": [ - "arn::sns::111111111111:" - ], - "AlarmArn": "arn::cloudwatch::111111111111:alarm:", - "AlarmConfigurationUpdatedTimestamp": "timestamp", - "AlarmDescription": "composite alarm description", - "AlarmName": "", - "AlarmRule": "ALARM(\"arn::cloudwatch::111111111111:alarm:\") OR ALARM(\"arn::cloudwatch::111111111111:alarm:\")", - "InsufficientDataActions": [], - "OKActions": [ - "arn::sns::111111111111:" - ], - "StateReason": "", - "StateReasonData": { - "triggeringAlarms": [ - { - "arn": "arn::cloudwatch::111111111111:alarm:", - "state": { - "value": "ALARM", - "timestamp": "date" - } - } - ] + "error-resource-not-found": { + "Error": { + "Code": "ResourceNotFound", + "Message": "", + "QueryErrorCode": "ResourceNotFound", + "Type": "Sender" }, - "StateTransitionedTimestamp": "timestamp", - "StateUpdatedTimestamp": "timestamp", - "StateValue": "ALARM" + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 404 + } } } } diff --git a/tests/aws/services/cloudwatch/test_cloudwatch.validation.json b/tests/aws/services/cloudwatch/test_cloudwatch.validation.json index 428f5d6c84b8f..c80899f39e84a 100644 --- a/tests/aws/services/cloudwatch/test_cloudwatch.validation.json +++ b/tests/aws/services/cloudwatch/test_cloudwatch.validation.json @@ -1,74 +1,1544 @@ { + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_basic_operations_multiple_protocols[json]": { + "last_validated_date": "2025-09-18T13:56:03+00:00", + "durations_in_seconds": { + "setup": 0.48, + "call": 3.45, + "teardown": 0.0, + "total": 3.93 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_basic_operations_multiple_protocols[query]": { + "last_validated_date": "2025-09-18T13:56:09+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 3.05, + "teardown": 0.0, + "total": 3.05 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_basic_operations_multiple_protocols[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-18T13:56:06+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 3.05, + "teardown": 0.0, + "total": 3.05 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_exception_serializing_with_no_shape_in_spec[json]": { + "last_validated_date": "2025-09-22T19:17:52+00:00", + "durations_in_seconds": { + "setup": 0.43, + "call": 2.35, + "teardown": 0.01, + "total": 2.79 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_exception_serializing_with_no_shape_in_spec[query]": { + "last_validated_date": "2025-09-22T19:17:54+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.02, + "teardown": 0.0, + "total": 1.02 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_exception_serializing_with_no_shape_in_spec[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-22T19:17:53+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.32, + "teardown": 0.0, + "total": 1.32 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_multi_protocol_client_fixture[json]": { + "last_validated_date": "2025-09-19T17:26:49+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.13, + "teardown": 0.0, + "total": 0.13 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_multi_protocol_client_fixture[query]": { + "last_validated_date": "2025-09-19T17:26:49+00:00", + "durations_in_seconds": { + "setup": 0.05, + "call": 0.59, + "teardown": 0.0, + "total": 0.64 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudWatchMultiProtocol::test_multi_protocol_client_fixture[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T17:26:49+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.14, + "teardown": 0.0, + "total": 0.14 + } + }, "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_alarm_lambda_target": { "last_validated_date": "2024-01-03T17:30:00+00:00" }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_anomaly_detector_lifecycle": { - "last_validated_date": "2023-10-26T08:42:43+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_anomaly_detector_lifecycle[json]": { + "last_validated_date": "2025-09-19T21:45:48+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.33, + "teardown": 0.0, + "total": 0.33 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_anomaly_detector_lifecycle[query]": { + "last_validated_date": "2025-09-19T21:45:48+00:00", + "durations_in_seconds": { + "setup": 0.49, + "call": 0.67, + "teardown": 0.0, + "total": 1.16 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_anomaly_detector_lifecycle[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T21:45:48+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.35, + "teardown": 0.0, + "total": 0.35 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_aws_sqs_metrics_created[json]": { + "last_validated_date": "2025-09-19T21:29:01+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 119.29, + "teardown": 0.17, + "total": 119.46 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_aws_sqs_metrics_created[query]": { + "last_validated_date": "2025-09-19T21:27:01+00:00", + "durations_in_seconds": { + "setup": 0.53, + "call": 100.09, + "teardown": 0.19, + "total": 100.81 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_aws_sqs_metrics_created[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T21:31:00+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 119.3, + "teardown": 0.22, + "total": 119.52 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_breaching_alarm_actions[json]": { + "last_validated_date": "2025-09-19T21:12:42+00:00", + "durations_in_seconds": { + "setup": 0.15, + "call": 59.84, + "teardown": 1.27, + "total": 61.26 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_breaching_alarm_actions[query]": { + "last_validated_date": "2025-09-19T21:11:41+00:00", + "durations_in_seconds": { + "setup": 0.99, + "call": 18.72, + "teardown": 1.11, + "total": 20.82 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_breaching_alarm_actions[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T21:13:17+00:00", + "durations_in_seconds": { + "setup": 0.15, + "call": 33.39, + "teardown": 1.29, + "total": 34.83 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_create_metric_stream[json]": { + "last_validated_date": "2025-09-19T21:39:09+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 188.54, + "teardown": 3.22, + "total": 191.76 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_create_metric_stream[query]": { + "last_validated_date": "2025-09-19T21:35:58+00:00", + "durations_in_seconds": { + "setup": 0.48, + "call": 111.63, + "teardown": 3.45, + "total": 115.56 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_create_metric_stream[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T21:41:41+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 148.02, + "teardown": 3.33, + "total": 151.35 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_dashboard_lifecycle[json]": { + "last_validated_date": "2025-09-19T21:32:56+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.37, + "teardown": 0.01, + "total": 1.38 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_dashboard_lifecycle[query]": { + "last_validated_date": "2025-09-19T21:32:55+00:00", + "durations_in_seconds": { + "setup": 0.48, + "call": 1.44, + "teardown": 0.0, + "total": 1.92 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_dashboard_lifecycle[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T21:32:58+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.4, + "teardown": 0.0, + "total": 1.4 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_default_ordering[json]": { + "last_validated_date": "2025-09-19T22:55:32+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.69, + "teardown": 0.0, + "total": 1.69 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_default_ordering[query]": { + "last_validated_date": "2025-09-19T22:55:31+00:00", + "durations_in_seconds": { + "setup": 0.05, + "call": 3.35, + "teardown": 0.0, + "total": 3.4 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_default_ordering[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T22:55:36+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 3.35, + "teardown": 0.0, + "total": 3.35 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_delete_alarm[json]": { + "last_validated_date": "2025-09-19T23:06:54+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.7, + "teardown": 0.0, + "total": 0.7 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_delete_alarm[query]": { + "last_validated_date": "2025-09-19T23:06:53+00:00", + "durations_in_seconds": { + "setup": 0.51, + "call": 1.06, + "teardown": 0.01, + "total": 1.58 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_delete_alarm[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T23:06:55+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.06, + "teardown": 0.0, + "total": 1.06 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_alarms_converts_date_format_correctly[json]": { + "last_validated_date": "2025-09-19T20:38:28+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.33, + "teardown": 0.24, + "total": 0.57 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_alarms_converts_date_format_correctly[query]": { + "last_validated_date": "2025-09-19T20:38:27+00:00", + "durations_in_seconds": { + "setup": 0.48, + "call": 0.78, + "teardown": 0.22, + "total": 1.48 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_alarms_converts_date_format_correctly[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T20:38:28+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.36, + "teardown": 0.22, + "total": 0.58 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_minimal_metric_alarm[json]": { + "last_validated_date": "2025-09-19T21:47:20+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.38, + "teardown": 0.35, + "total": 0.73 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_minimal_metric_alarm[query]": { + "last_validated_date": "2025-09-19T21:47:20+00:00", + "durations_in_seconds": { + "setup": 0.5, + "call": 0.79, + "teardown": 0.33, + "total": 1.62 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_minimal_metric_alarm[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T21:47:21+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.31, + "teardown": 0.21, + "total": 0.52 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_enable_disable_alarm_actions[json]": { + "last_validated_date": "2025-09-19T21:17:46+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 13.26, + "teardown": 1.11, + "total": 14.37 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_enable_disable_alarm_actions[query]": { + "last_validated_date": "2025-09-19T21:17:32+00:00", + "durations_in_seconds": { + "setup": 0.55, + "call": 13.76, + "teardown": 1.41, + "total": 15.72 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_enable_disable_alarm_actions[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T21:18:00+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 13.05, + "teardown": 1.11, + "total": 14.16 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data[json]": { + "last_validated_date": "2025-09-19T20:25:48+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.58, + "teardown": 0.0, + "total": 2.58 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data[query]": { + "last_validated_date": "2025-09-19T20:25:46+00:00", + "durations_in_seconds": { + "setup": 0.08, + "call": 2.92, + "teardown": 0.0, + "total": 3.0 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T20:25:51+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.86, + "teardown": 0.0, + "total": 2.86 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[json-metric_data0]": { + "last_validated_date": "2025-09-19T22:59:20+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.24, + "teardown": 0.01, + "total": 2.25 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[json-metric_data1]": { + "last_validated_date": "2025-09-19T22:59:22+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.23, + "teardown": 0.0, + "total": 2.23 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[json-metric_data2]": { + "last_validated_date": "2025-09-19T22:59:24+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.55, + "teardown": 0.0, + "total": 2.55 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[query-metric_data0]": { + "last_validated_date": "2025-09-19T22:59:13+00:00", + "durations_in_seconds": { + "setup": 0.5, + "call": 2.55, + "teardown": 0.01, + "total": 3.06 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[query-metric_data1]": { + "last_validated_date": "2025-09-19T22:59:15+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.24, + "teardown": 0.01, + "total": 2.25 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[query-metric_data2]": { + "last_validated_date": "2025-09-19T22:59:17+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.24, + "teardown": 0.01, + "total": 2.25 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[smithy-rpc-v2-cbor-metric_data0]": { + "last_validated_date": "2025-09-19T22:59:27+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.23, + "teardown": 0.01, + "total": 2.24 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[smithy-rpc-v2-cbor-metric_data1]": { + "last_validated_date": "2025-09-19T22:59:29+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.23, + "teardown": 0.0, + "total": 2.23 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[smithy-rpc-v2-cbor-metric_data2]": { + "last_validated_date": "2025-09-19T22:59:31+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.23, + "teardown": 0.0, + "total": 2.23 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_for_multiple_metrics[json]": { + "last_validated_date": "2025-09-19T20:29:18+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.44, + "teardown": 0.0, + "total": 1.44 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_for_multiple_metrics[query]": { + "last_validated_date": "2025-09-19T20:29:17+00:00", + "durations_in_seconds": { + "setup": 0.49, + "call": 2.94, + "teardown": 0.01, + "total": 3.44 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_for_multiple_metrics[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T20:29:20+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.76, + "teardown": 0.0, + "total": 1.76 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_pagination[json]": { + "last_validated_date": "2025-09-19T22:54:12+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 4.75, + "teardown": 0.0, + "total": 4.75 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_pagination[query]": { + "last_validated_date": "2025-09-19T22:54:08+00:00", + "durations_in_seconds": { + "setup": 0.05, + "call": 5.12, + "teardown": 0.0, + "total": 5.17 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_pagination[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T22:54:17+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 4.77, + "teardown": 0.0, + "total": 4.77 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[json-Average]": { + "last_validated_date": "2025-09-19T20:32:15+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.34, + "teardown": 0.01, + "total": 2.35 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[json-Maximum]": { + "last_validated_date": "2025-09-19T20:32:13+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.33, + "teardown": 0.0, + "total": 2.33 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[json-Minimum]": { + "last_validated_date": "2025-09-19T20:32:10+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.36, + "teardown": 0.0, + "total": 2.36 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[json-SampleCount]": { + "last_validated_date": "2025-09-19T20:32:08+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.68, + "teardown": 0.0, + "total": 2.68 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[json-Sum]": { + "last_validated_date": "2025-09-19T20:32:05+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.37, + "teardown": 0.0, + "total": 2.37 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[query-Average]": { + "last_validated_date": "2025-09-19T20:32:03+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 3.83, + "teardown": 0.0, + "total": 3.83 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[query-Maximum]": { + "last_validated_date": "2025-09-19T20:31:59+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.77, + "teardown": 0.0, + "total": 2.77 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[query-Minimum]": { + "last_validated_date": "2025-09-19T20:31:56+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.35, + "teardown": 0.01, + "total": 2.36 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[query-SampleCount]": { + "last_validated_date": "2025-09-19T20:31:54+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.33, + "teardown": 0.01, + "total": 2.34 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[query-Sum]": { + "last_validated_date": "2025-09-19T20:31:51+00:00", + "durations_in_seconds": { + "setup": 0.5, + "call": 2.65, + "teardown": 0.0, + "total": 3.15 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[smithy-rpc-v2-cbor-Average]": { + "last_validated_date": "2025-09-19T20:32:27+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.34, + "teardown": 0.01, + "total": 2.35 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[smithy-rpc-v2-cbor-Maximum]": { + "last_validated_date": "2025-09-19T20:32:25+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.66, + "teardown": 0.0, + "total": 2.66 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[smithy-rpc-v2-cbor-Minimum]": { + "last_validated_date": "2025-09-19T20:32:22+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.32, + "teardown": 0.0, + "total": 2.32 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[smithy-rpc-v2-cbor-SampleCount]": { + "last_validated_date": "2025-09-19T20:32:20+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.34, + "teardown": 0.0, + "total": 2.34 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_stats[smithy-rpc-v2-cbor-Sum]": { + "last_validated_date": "2025-09-19T20:32:18+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.65, + "teardown": 0.0, + "total": 2.65 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_different_units[json]": { + "last_validated_date": "2025-09-19T22:57:26+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.22, + "teardown": 0.0, + "total": 2.22 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_different_units[query]": { + "last_validated_date": "2025-09-19T22:57:24+00:00", + "durations_in_seconds": { + "setup": 0.47, + "call": 2.55, + "teardown": 0.0, + "total": 3.02 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_different_units[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T22:57:28+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.24, + "teardown": 0.01, + "total": 2.25 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_dimensions[json]": { + "last_validated_date": "2025-09-19T20:33:41+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.44, + "teardown": 0.01, + "total": 2.45 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_dimensions[query]": { + "last_validated_date": "2025-09-19T20:33:39+00:00", + "durations_in_seconds": { + "setup": 0.49, + "call": 2.77, + "teardown": 0.0, + "total": 3.26 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_dimensions[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T20:33:44+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.74, + "teardown": 0.0, + "total": 2.74 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_zero_and_labels[json]": { + "last_validated_date": "2025-09-19T22:47:00+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.34, + "teardown": 0.01, + "total": 1.35 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_zero_and_labels[query]": { + "last_validated_date": "2025-09-19T22:46:59+00:00", + "durations_in_seconds": { + "setup": 0.5, + "call": 1.7, + "teardown": 0.01, + "total": 2.21 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_with_zero_and_labels[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T22:47:02+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.35, + "teardown": 0.0, + "total": 1.35 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_statistics[json]": { + "last_validated_date": "2025-09-19T22:48:01+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 3.54, + "teardown": 0.0, + "total": 3.54 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_statistics[query]": { + "last_validated_date": "2025-09-19T22:47:57+00:00", + "durations_in_seconds": { + "setup": 0.47, + "call": 3.81, + "teardown": 0.01, + "total": 4.29 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_statistics[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T22:48:04+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 3.46, + "teardown": 0.01, + "total": 3.47 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_no_results[json]": { + "last_validated_date": "2025-09-19T23:05:05+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 8.46, + "teardown": 0.01, + "total": 8.47 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_no_results[query]": { + "last_validated_date": "2025-09-19T23:04:56+00:00", + "durations_in_seconds": { + "setup": 0.52, + "call": 11.03, + "teardown": 0.01, + "total": 11.56 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_no_results[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T23:05:13+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 8.43, + "teardown": 0.0, + "total": 8.43 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_null_dimensions[json]": { + "last_validated_date": "2025-09-19T23:02:13+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.22, + "teardown": 0.0, + "total": 2.22 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_null_dimensions[query]": { + "last_validated_date": "2025-09-19T23:02:11+00:00", + "durations_in_seconds": { + "setup": 0.51, + "call": 2.56, + "teardown": 0.01, + "total": 3.08 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_null_dimensions[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T23:02:15+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.22, + "teardown": 0.0, + "total": 2.22 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_handle_different_units[json]": { + "last_validated_date": "2025-09-19T22:56:20+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.22, + "teardown": 0.01, + "total": 2.23 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_handle_different_units[query]": { + "last_validated_date": "2025-09-19T22:56:18+00:00", + "durations_in_seconds": { + "setup": 0.49, + "call": 2.54, + "teardown": 0.01, + "total": 3.04 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_handle_different_units[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T22:56:22+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.22, + "teardown": 0.01, + "total": 2.23 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_insight_rule[json]": { + "last_validated_date": "2025-09-19T21:45:14+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.04, + "teardown": 0.0, + "total": 2.04 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_insight_rule[query]": { + "last_validated_date": "2025-09-19T21:45:12+00:00", + "durations_in_seconds": { + "setup": 0.51, + "call": 1.13, + "teardown": 0.0, + "total": 1.64 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_insight_rule[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T21:45:15+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.53, + "teardown": 0.0, + "total": 1.53 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_amount_of_datapoints[json]": { + "last_validated_date": "2025-09-19T23:08:53+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.92, + "teardown": 0.01, + "total": 0.93 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_amount_of_datapoints[query]": { + "last_validated_date": "2025-09-19T23:08:52+00:00", + "durations_in_seconds": { + "setup": 0.51, + "call": 1.27, + "teardown": 0.01, + "total": 1.79 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_amount_of_datapoints[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T23:08:54+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.94, + "teardown": 0.0, + "total": 0.94 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_dashboard_name[json]": { + "last_validated_date": "2025-09-19T21:32:13+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.43, + "teardown": 0.01, + "total": 0.44 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_dashboard_name[query]": { + "last_validated_date": "2025-09-19T21:32:13+00:00", + "durations_in_seconds": { + "setup": 0.48, + "call": 0.44, + "teardown": 0.01, + "total": 0.93 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_dashboard_name[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T21:32:13+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.51, + "teardown": 0.01, + "total": 0.52 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs0]": { + "last_validated_date": "2025-09-19T23:00:46+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.36, + "teardown": 0.01, + "total": 1.37 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs1]": { + "last_validated_date": "2025-09-19T23:00:47+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.34, + "teardown": 0.0, + "total": 1.34 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs2]": { + "last_validated_date": "2025-09-19T23:00:49+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.35, + "teardown": 0.01, + "total": 1.36 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs3]": { + "last_validated_date": "2025-09-19T23:00:50+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.69, + "teardown": 0.01, + "total": 1.7 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs4]": { + "last_validated_date": "2025-09-19T23:00:52+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.34, + "teardown": 0.0, + "total": 1.34 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs5]": { + "last_validated_date": "2025-09-19T23:00:53+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.33, + "teardown": 0.0, + "total": 1.33 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[json-input_pairs6]": { + "last_validated_date": "2025-09-19T23:00:55+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.67, + "teardown": 0.01, + "total": 1.68 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs0]": { + "last_validated_date": "2025-09-19T23:00:36+00:00", + "durations_in_seconds": { + "setup": 0.49, + "call": 1.68, + "teardown": 0.01, + "total": 2.18 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs1]": { + "last_validated_date": "2025-09-19T23:00:37+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.35, + "teardown": 0.01, + "total": 1.36 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs2]": { + "last_validated_date": "2025-09-19T23:00:39+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.35, + "teardown": 0.01, + "total": 1.36 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs3]": { + "last_validated_date": "2025-09-19T23:00:40+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.68, + "teardown": 0.01, + "total": 1.69 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs4]": { + "last_validated_date": "2025-09-19T23:00:42+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.35, + "teardown": 0.01, + "total": 1.36 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs5]": { + "last_validated_date": "2025-09-19T23:00:44+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.46, + "teardown": 0.01, + "total": 2.47 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[query-input_pairs6]": { + "last_validated_date": "2025-09-19T23:00:45+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.53, + "teardown": 0.01, + "total": 0.54 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs0]": { + "last_validated_date": "2025-09-19T23:00:56+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.48, + "teardown": 0.01, + "total": 1.49 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs1]": { + "last_validated_date": "2025-09-19T23:00:58+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.35, + "teardown": 0.01, + "total": 1.36 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs2]": { + "last_validated_date": "2025-09-19T23:00:59+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.67, + "teardown": 0.01, + "total": 1.68 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs3]": { + "last_validated_date": "2025-09-19T23:01:01+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.33, + "teardown": 0.0, + "total": 1.33 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs4]": { + "last_validated_date": "2025-09-19T23:01:02+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.33, + "teardown": 0.0, + "total": 1.33 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs5]": { + "last_validated_date": "2025-09-19T23:01:03+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.35, + "teardown": 0.0, + "total": 1.35 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_label_generation[smithy-rpc-v2-cbor-input_pairs6]": { + "last_validated_date": "2025-09-19T23:01:05+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.66, + "teardown": 0.01, + "total": 1.67 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_list_metrics_pagination[json]": { + "last_validated_date": "2025-09-19T22:51:19+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 79.99, + "teardown": 0.0, + "total": 79.99 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_list_metrics_pagination[query]": { + "last_validated_date": "2025-09-19T22:49:59+00:00", + "durations_in_seconds": { + "setup": 0.05, + "call": 82.22, + "teardown": 0.0, + "total": 82.27 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_list_metrics_pagination[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T22:52:38+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 79.3, + "teardown": 0.0, + "total": 79.3 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_list_metrics_uniqueness[json]": { + "last_validated_date": "2025-09-19T20:44:59+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 21.42, + "teardown": 0.0, + "total": 21.42 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_list_metrics_uniqueness[query]": { + "last_validated_date": "2025-09-19T20:44:38+00:00", + "durations_in_seconds": { + "setup": 0.05, + "call": 21.66, + "teardown": 0.0, + "total": 21.71 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_list_metrics_uniqueness[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T20:45:20+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 21.34, + "teardown": 0.0, + "total": 21.34 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_list_metrics_with_filters[json]": { + "last_validated_date": "2025-09-19T20:48:10+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 42.35, + "teardown": 0.0, + "total": 42.35 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_list_metrics_with_filters[query]": { + "last_validated_date": "2025-09-19T20:47:28+00:00", + "durations_in_seconds": { + "setup": 0.05, + "call": 42.68, + "teardown": 0.0, + "total": 42.73 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_list_metrics_with_filters[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T20:48:52+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 42.3, + "teardown": 0.0, + "total": 42.3 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_metric_widget[json]": { + "last_validated_date": "2025-09-19T21:46:33+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.64, + "teardown": 0.0, + "total": 0.64 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_metric_widget[query]": { + "last_validated_date": "2025-09-19T21:46:32+00:00", + "durations_in_seconds": { + "setup": 0.05, + "call": 0.86, + "teardown": 0.0, + "total": 0.91 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_metric_widget[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T21:46:34+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.65, + "teardown": 0.0, + "total": 0.65 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_multiple_dimensions[json]": { + "last_validated_date": "2025-09-19T20:36:02+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 8.96, + "teardown": 0.0, + "total": 8.96 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_multiple_dimensions[query]": { + "last_validated_date": "2025-09-19T20:35:53+00:00", + "durations_in_seconds": { + "setup": 0.05, + "call": 7.56, + "teardown": 0.0, + "total": 7.61 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_multiple_dimensions[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T20:36:11+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 9.21, + "teardown": 0.0, + "total": 9.21 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_multiple_dimensions_statistics[json]": { + "last_validated_date": "2025-09-19T23:07:47+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 8.12, + "teardown": 0.01, + "total": 8.13 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_multiple_dimensions_statistics[query]": { + "last_validated_date": "2025-09-19T23:07:39+00:00", + "durations_in_seconds": { + "setup": 0.51, + "call": 4.83, + "teardown": 0.01, + "total": 5.35 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_multiple_dimensions_statistics[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T23:07:59+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 11.47, + "teardown": 0.01, + "total": 11.48 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_composite_alarm_describe_alarms[json]": { + "last_validated_date": "2025-09-19T20:41:15+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.65, + "teardown": 0.47, + "total": 1.12 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_composite_alarm_describe_alarms[query]": { + "last_validated_date": "2025-09-19T20:41:14+00:00", + "durations_in_seconds": { + "setup": 0.05, + "call": 0.95, + "teardown": 0.47, + "total": 1.47 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_composite_alarm_describe_alarms[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T20:41:17+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.97, + "teardown": 0.4, + "total": 1.37 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm[json]": { + "last_validated_date": "2025-09-19T21:08:43+00:00", + "durations_in_seconds": { + "setup": 0.14, + "call": 17.57, + "teardown": 1.17, + "total": 18.88 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm[query]": { + "last_validated_date": "2025-09-19T21:08:24+00:00", + "durations_in_seconds": { + "setup": 0.96, + "call": 18.11, + "teardown": 1.08, + "total": 20.15 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T21:09:02+00:00", + "durations_in_seconds": { + "setup": 0.16, + "call": 17.47, + "teardown": 1.2, + "total": 18.83 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm_escape_character[json]": { + "last_validated_date": "2025-09-24T15:16:45+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.4, + "teardown": 0.34, + "total": 0.74 + } + }, + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm_escape_character[query]": { + "last_validated_date": "2025-09-24T15:16:45+00:00", + "durations_in_seconds": { + "setup": 0.49, + "call": 0.71, + "teardown": 0.23, + "total": 1.43 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_aws_sqs_metrics_created": { - "last_validated_date": "2023-09-25T08:25:29+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm_escape_character[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-24T15:16:46+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.31, + "teardown": 0.23, + "total": 0.54 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_breaching_alarm_actions": { - "last_validated_date": "2024-01-19T15:05:50+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_validation[json]": { + "last_validated_date": "2025-09-19T18:08:04+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.34, + "teardown": 0.0, + "total": 1.34 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_create_metric_stream": { - "last_validated_date": "2023-10-26T07:12:10+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_validation[query]": { + "last_validated_date": "2025-09-19T18:08:03+00:00", + "durations_in_seconds": { + "setup": 0.5, + "call": 1.67, + "teardown": 0.0, + "total": 2.17 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_dashboard_lifecycle": { - "last_validated_date": "2023-10-25T11:16:20+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_validation[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T18:08:05+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.36, + "teardown": 0.0, + "total": 1.36 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_delete_alarm": { - "last_validated_date": "2024-01-12T14:06:42+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_values_list[json]": { + "last_validated_date": "2025-09-19T17:40:31+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.83, + "teardown": 0.0, + "total": 0.83 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_describe_alarms_converts_date_format_correctly": { - "last_validated_date": "2024-09-04T15:59:17+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_values_list[query]": { + "last_validated_date": "2025-09-19T17:40:30+00:00", + "durations_in_seconds": { + "setup": 0.51, + "call": 1.14, + "teardown": 0.0, + "total": 1.65 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_enable_disable_alarm_actions": { - "last_validated_date": "2023-09-12T10:00:45+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_values_list[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T17:40:32+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.43, + "teardown": 0.0, + "total": 1.43 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[metric_data0]": { - "last_validated_date": "2024-01-11T11:07:38+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_uses_utc[json]": { + "last_validated_date": "2025-09-19T22:54:30+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.67, + "teardown": 0.0, + "total": 1.67 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[metric_data1]": { - "last_validated_date": "2024-01-11T11:07:41+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_uses_utc[query]": { + "last_validated_date": "2025-09-19T22:54:28+00:00", + "durations_in_seconds": { + "setup": 0.05, + "call": 2.02, + "teardown": 0.0, + "total": 2.07 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_data_different_units_no_unit_in_query[metric_data2]": { - "last_validated_date": "2024-01-11T11:07:43+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_uses_utc[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T22:54:32+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 2.03, + "teardown": 0.0, + "total": 2.03 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_no_results": { - "last_validated_date": "2024-01-10T15:29:50+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm[json]": { + "last_validated_date": "2025-09-19T20:56:55+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 5.58, + "teardown": 1.29, + "total": 6.87 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_get_metric_with_null_dimensions": { - "last_validated_date": "2024-03-05T14:34:47+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm[query]": { + "last_validated_date": "2025-09-19T20:56:48+00:00", + "durations_in_seconds": { + "setup": 0.5, + "call": 6.51, + "teardown": 1.36, + "total": 8.37 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_handle_different_units": { - "last_validated_date": "2024-01-05T16:15:39+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T20:57:02+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 5.37, + "teardown": 1.35, + "total": 6.72 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_insight_rule": { - "last_validated_date": "2023-10-26T08:07:59+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm_invalid_input[json]": { + "last_validated_date": "2025-09-19T22:29:27+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.77, + "teardown": 0.28, + "total": 1.05 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_amount_of_datapoints": { - "last_validated_date": "2024-08-23T14:16:44+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm_invalid_input[query]": { + "last_validated_date": "2025-09-19T22:29:26+00:00", + "durations_in_seconds": { + "setup": 0.49, + "call": 1.1, + "teardown": 0.24, + "total": 1.83 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_invalid_dashboard_name": { - "last_validated_date": "2024-09-04T16:28:05+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm_invalid_input[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T22:29:28+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 0.74, + "teardown": 0.21, + "total": 0.95 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_multiple_dimensions_statistics": { - "last_validated_date": "2024-07-29T07:56:05+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_store_tags[json]": { + "last_validated_date": "2025-09-19T20:42:26+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.31, + "teardown": 0.23, + "total": 1.54 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_alarm": { - "last_validated_date": "2025-05-12T16:20:56+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_store_tags[query]": { + "last_validated_date": "2025-09-19T20:42:24+00:00", + "durations_in_seconds": { + "setup": 0.48, + "call": 1.39, + "teardown": 0.22, + "total": 2.09 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_put_metric_data_values_list": { - "last_validated_date": "2023-09-25T08:26:17+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_store_tags[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T20:42:28+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 1.41, + "teardown": 0.22, + "total": 1.63 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_set_alarm": { - "last_validated_date": "2024-01-19T15:30:05+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_trigger_composite_alarm[json]": { + "last_validated_date": "2025-09-19T21:03:36+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 10.2, + "teardown": 2.24, + "total": 12.44 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_store_tags": { - "last_validated_date": "2024-09-02T14:03:31+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_trigger_composite_alarm[query]": { + "last_validated_date": "2025-09-19T21:03:23+00:00", + "durations_in_seconds": { + "setup": 0.5, + "call": 12.35, + "teardown": 2.05, + "total": 14.9 + } }, - "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_trigger_composite_alarm": { - "last_validated_date": "2024-11-14T14:25:30+00:00" + "tests/aws/services/cloudwatch/test_cloudwatch.py::TestCloudwatch::test_trigger_composite_alarm[smithy-rpc-v2-cbor]": { + "last_validated_date": "2025-09-19T21:03:50+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 11.87, + "teardown": 2.21, + "total": 14.08 + } } } diff --git a/tests/aws/services/cloudwatch/utils.py b/tests/aws/services/cloudwatch/utils.py new file mode 100644 index 0000000000000..f735a33c151f4 --- /dev/null +++ b/tests/aws/services/cloudwatch/utils.py @@ -0,0 +1,185 @@ +import abc +import json +from typing import Any + +import xmltodict +from botocore.auth import SigV4Auth +from botocore.serialize import create_serializer +from cbor2._decoder import loads as cbor2_loads +from requests import Response + +from localstack import constants +from localstack.aws.spec import get_service_catalog +from localstack.config import LOCALSTACK_HOST +from localstack.testing.aws.util import is_aws_cloud + + +class BaseCloudWatchHttpClient(abc.ABC): + """ + Simple HTTP client for making CloudWatch requests manually using different protocols. + + This serialization type is not yet available via boto3 client, and we have more control over raw responses. + """ + + protocol: str = "" + + def __init__( + self, + region_name: str, + client_factory, + ): + self.region_name = region_name + # CloudWatch uses the signing name `monitoring` + self._client = client_factory( + "monitoring", region=self.region_name, signer_factory=SigV4Auth + ) + self.service_model = get_service_catalog().get("cloudwatch") + self.target_prefix = self.service_model.metadata.get("targetPrefix") or "" + + @abc.abstractmethod + def _deserialize_response(self, response: Response) -> Any: ... + + @abc.abstractmethod + def _build_headers(self, operation: str, query_mode: bool = False) -> dict: ... + + def _serialize_body(self, body: dict, operation: str) -> str | bytes: + # here we use the Botocore serializer directly, since it has some complex behavior, + # and we know CloudWatch supports it by default + query_serializer = create_serializer(self.protocol) + operation_model = self.service_model.operation_model(operation) + request = query_serializer.serialize_to_request(body, operation_model) + return request["body"] + + @property + def host(self) -> str: + return ( + f"monitoring.{self.region_name}.amazonaws.com" + if is_aws_cloud() + else LOCALSTACK_HOST.host_and_port() + ) + + def _build_endpoint(self, operation: str) -> str: + return f"https://{self.host}" + + def post_raw( + self, operation: str, payload: dict, query_mode: bool = False, **kwargs + ) -> Response: + """ + Perform a CloudWatch operation, encoding the request payload based on the `_serialize_body` and returning + the raw response without any processing or checks. + """ + response = self._client.post( + self._build_endpoint(operation), + data=self._serialize_body(payload, operation), + headers=self._build_headers(operation, query_mode), + **kwargs, + ) + return response + + def post( + self, operation: str, payload: dict, status_code: int = 200, query_mode: bool = False + ) -> Any: + """ + Perform a CloudWatch operation and decode it based on the `_deserialize_response` implementation + """ + response = self.post_raw(operation, payload, query_mode=query_mode) + response_body = self._deserialize_response(response) + if response.status_code != status_code: + raise ValueError(f"Bad status: {response.status_code}, response body: {response_body}") + return response_body + + +class CloudWatchCBORHTTPClient(BaseCloudWatchHttpClient): + protocol = "smithy-rpc-v2-cbor" + + def _deserialize_response(self, response: Response) -> Any: + if response.content: + return cbor2_loads(response.content) + return {} + + def _build_headers(self, operation: str, query_mode: bool = False) -> dict: + headers = { + "content-type": constants.APPLICATION_CBOR, + "accept": constants.APPLICATION_CBOR, + "host": self.host, + "Smithy-Protocol": "rpc-v2-cbor", + } + if query_mode: + headers["x-amzn-query-mode"] = "true" + + return headers + + def _build_endpoint(self, operation: str) -> str: + return f"https://{self.host}/service/{self.target_prefix}/operation/{operation}" + + +class CloudWatchJSONHTTPClient(BaseCloudWatchHttpClient): + protocol = "json" + + def _deserialize_response(self, response: Response) -> Any: + if response.content: + return json.loads(response.content) + return {} + + def _build_headers(self, operation: str, query_mode: bool = False) -> dict: + headers = { + "Content-Type": constants.APPLICATION_AMZ_JSON_1_0, + "X-Amz-Target": f"{self.target_prefix}.{operation}", + "Host": self.host, + "x-amzn-query-mode": "true" if query_mode else "false", + } + if query_mode: + headers["x-amzn-query-mode"] = "true" + + return headers + + +class CloudWatchQueryHTTPClient(BaseCloudWatchHttpClient): + protocol = "query" + + def _deserialize_response(self, response: Response) -> Any: + if not response.content: + return {} + content_type = response.headers.get("Content-Type", "") + if content_type.startswith(constants.APPLICATION_XML) or content_type.startswith( + constants.TEXT_XML + ): + # FIXME: the snapshot library doesn't deal well with ResponseMetadata in raw responses, will be fixed when + # we release a new version + # https://github.com/localstack/localstack-snapshot/pull/13 + response = xmltodict.parse(response.content) + container = response[next(iter(response.keys()))] + if isinstance(container, dict): + container.pop("ResponseMetadata", None) + return response + elif content_type.startswith(constants.APPLICATION_JSON): + return json.loads(response.content) + else: + return response.content + + def _build_headers(self, operation: str, query_mode: bool = False) -> dict: + return { + "content-type": constants.APPLICATION_X_WWW_FORM_URLENCODED, + "host": self.host, + } + + +def get_cloudwatch_client(client_factory, region: str, protocol: str) -> BaseCloudWatchHttpClient: + match protocol: + case "smithy-rpc-v2-cbor": + return CloudWatchCBORHTTPClient( + region_name=region, + client_factory=client_factory, + ) + case "json": + return CloudWatchJSONHTTPClient( + region_name=region, + client_factory=client_factory, + ) + case "query": + return CloudWatchQueryHTTPClient( + region_name=region, + client_factory=client_factory, + ) + case _: + raise ValueError("protocol must be in ['smithy-rpc-v2-cbor', 'json', 'query']") diff --git a/tests/aws/services/es/test_es.py b/tests/aws/services/es/test_es.py index a736aed91cc06..c8f1118edbbb5 100644 --- a/tests/aws/services/es/test_es.py +++ b/tests/aws/services/es/test_es.py @@ -5,8 +5,12 @@ import pytest from localstack import config -from localstack.constants import ELASTICSEARCH_DEFAULT_VERSION, OPENSEARCH_DEFAULT_VERSION -from localstack.services.opensearch.packages import elasticsearch_package, opensearch_package +from localstack.services.opensearch.packages import ( + ELASTICSEARCH_DEFAULT_VERSION, + OPENSEARCH_DEFAULT_VERSION, + elasticsearch_package, + opensearch_package, +) from localstack.testing.pytest import markers from localstack.utils.common import safe_requests as requests from localstack.utils.common import short_uid, start_worker_thread diff --git a/tests/aws/services/kinesis/test_kinesis.py b/tests/aws/services/kinesis/test_kinesis.py index 8d618074c4717..d226df5523543 100644 --- a/tests/aws/services/kinesis/test_kinesis.py +++ b/tests/aws/services/kinesis/test_kinesis.py @@ -24,6 +24,7 @@ from localstack.testing.aws.util import is_aws_cloud from localstack.testing.pytest import markers from localstack.utils.aws import resources +from localstack.utils.aws.arns import kinesis_stream_arn from localstack.utils.common import retry, select_attributes, short_uid from localstack.utils.files import load_file from localstack.utils.kinesis import kinesis_connector @@ -133,6 +134,95 @@ def test_create_stream_without_shard_count( snapshot.match("Shards", shards) + @markers.aws.validated + def test_resource_policy_crud( + self, + account_id, + kinesis_create_stream, + wait_for_stream_ready, + aws_client, + snapshot, + ): + """Test complete CRUD cycle for Kinesis resource policies""" + + stream_name = kinesis_create_stream() + wait_for_stream_ready(stream_name) + describe_stream = aws_client.kinesis.describe_stream(StreamName=stream_name) + resource_arn = describe_stream["StreamDescription"]["StreamARN"] + principal_arn = f"arn:aws:iam::{account_id}:root" + + # retrieve, no policy yet + resp = aws_client.kinesis.get_resource_policy(ResourceARN=resource_arn) + snapshot.match("default_policy_if_not_set", resp) + + # put + policy = { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowCrossAccountWrite", + "Effect": "Allow", + "Principal": {"AWS": principal_arn}, + "Action": "kinesis:PutRecord", + "Resource": resource_arn, + } + ], + } + resp = aws_client.kinesis.put_resource_policy( + ResourceARN=resource_arn, Policy=json.dumps(policy) + ) + snapshot.match("put_resource_policy_if_not_set", resp) + + # retrieve + resp = aws_client.kinesis.get_resource_policy(ResourceARN=resource_arn) + snapshot.match("get_resource_policy_after_set", resp) + + # update + updated_policy = { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowCrossAccountReadWrite", + "Effect": "Allow", + "Principal": {"AWS": principal_arn}, + "Action": ["kinesis:PutRecord", "kinesis:GetRecords"], + "Resource": resource_arn, + } + ], + } + resp = aws_client.kinesis.put_resource_policy( + ResourceARN=resource_arn, Policy=json.dumps(updated_policy) + ) + snapshot.match("update_resource_policy", resp) + + # get the right policy after updating + resp = aws_client.kinesis.get_resource_policy(ResourceARN=resource_arn) + snapshot.match("get_resource_policy_after_update", resp) + + # delete it + resp = aws_client.kinesis.delete_resource_policy(ResourceARN=resource_arn) + snapshot.match("delete_resource_policy", resp) + + # get, policy should no longer exist + resp = aws_client.kinesis.get_resource_policy(ResourceARN=resource_arn) + snapshot.match("get_resource_policy_after_delete", resp) + + # deleting non existent policy for a valid arn + with pytest.raises(ClientError): + aws_client.kinesis.delete_resource_policy(ResourceARN=resource_arn) + + # put a policy for a non-existent stream + not_existent_arn = "arn:aws:kinesis:us-east-1:000000000000:stream/non-existent-xxxxxx" + policy["Statement"][0]["Resource"] = not_existent_arn + with pytest.raises(ClientError): + aws_client.kinesis.put_resource_policy( + ResourceARN=not_existent_arn, Policy=json.dumps(policy) + ) + + # TODO put a policy for an invalid stream arn, but it behaves differently + # on localstack and AWS, as the later triggers end-point-resolution in botocore + # and fails client side + @markers.aws.validated def test_stream_consumers( self, @@ -723,6 +813,36 @@ def _get_record(): record = retry(_get_record, sleep=1, retries=5) assert record["Data"].decode("utf-8") == test_data + @markers.aws.validated + @markers.snapshot.skip_snapshot_verify( + # error message is wrong in Kinesis (returns the full ARN) + paths=["$..message"], + ) + def test_cbor_exceptions( + self, + kinesis_create_stream, + wait_for_stream_ready, + aws_client, + kinesis_http_client, + region_name, + account_id, + snapshot, + ): + fake_name = "wrong-stream-name" + fake_stream_arn = kinesis_stream_arn( + account_id=account_id, region_name=region_name, stream_name=fake_name + ) + describe_response_raw = kinesis_http_client.post_raw( + operation="DescribeStream", + payload={"StreamARN": fake_stream_arn}, + ) + assert describe_response_raw.status_code == 400 + cbor_content = describe_response_raw.content + describe_response_data = cbor2_loads(cbor_content) + snapshot.match("cbor-error", describe_response_data) + assert describe_response_data["__type"] == "ResourceNotFoundException" + # TODO: add manual assertion on CBOR body? + class TestKinesisJavaSDK: # the lambda function is stored in the lambda common functions folder to re-use existing caching in CI diff --git a/tests/aws/services/kinesis/test_kinesis.snapshot.json b/tests/aws/services/kinesis/test_kinesis.snapshot.json index 66198f47cdf2d..73f6d14d62b50 100644 --- a/tests/aws/services/kinesis/test_kinesis.snapshot.json +++ b/tests/aws/services/kinesis/test_kinesis.snapshot.json @@ -226,5 +226,94 @@ } ] } + }, + "tests/aws/services/kinesis/test_kinesis.py::TestKinesis::test_cbor_exceptions": { + "recorded-date": "04-09-2025, 16:59:26", + "recorded-content": { + "cbor-error": { + "__type": "ResourceNotFoundException", + "message": "Stream wrong-stream-name under account 111111111111 not found." + } + } + }, + "tests/aws/services/kinesis/test_kinesis.py::TestKinesis::test_resource_policy_crud": { + "recorded-date": "01-08-2025, 13:00:05", + "recorded-content": { + "default_policy_if_not_set": { + "Policy": {}, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "put_resource_policy_if_not_set": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get_resource_policy_after_set": { + "Policy": { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowCrossAccountWrite", + "Effect": "Allow", + "Principal": { + "AWS": "arn::iam::111111111111:root" + }, + "Action": "kinesis:PutRecord", + "Resource": "arn::kinesis::111111111111:" + } + ] + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "update_resource_policy": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get_resource_policy_after_update": { + "Policy": { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowCrossAccountReadWrite", + "Effect": "Allow", + "Principal": { + "AWS": "arn::iam::111111111111:root" + }, + "Action": [ + "kinesis:PutRecord", + "kinesis:GetRecords" + ], + "Resource": "arn::kinesis::111111111111:" + } + ] + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "delete_resource_policy": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get_resource_policy_after_delete": { + "Policy": {}, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } } } diff --git a/tests/aws/services/kinesis/test_kinesis.validation.json b/tests/aws/services/kinesis/test_kinesis.validation.json index 414678e5add66..bf6e1dd09245c 100644 --- a/tests/aws/services/kinesis/test_kinesis.validation.json +++ b/tests/aws/services/kinesis/test_kinesis.validation.json @@ -5,6 +5,15 @@ "tests/aws/services/kinesis/test_kinesis.py::TestKinesis::test_cbor_blob_handling": { "last_validated_date": "2024-07-31T11:17:28+00:00" }, + "tests/aws/services/kinesis/test_kinesis.py::TestKinesis::test_cbor_exceptions": { + "last_validated_date": "2025-09-16T15:31:43+00:00", + "durations_in_seconds": { + "setup": 0.57, + "call": 0.48, + "teardown": 0.0, + "total": 1.05 + } + }, "tests/aws/services/kinesis/test_kinesis.py::TestKinesis::test_create_stream_without_shard_count": { "last_validated_date": "2022-08-26T07:30:59+00:00" }, @@ -20,6 +29,15 @@ "tests/aws/services/kinesis/test_kinesis.py::TestKinesis::test_record_lifecycle_data_integrity": { "last_validated_date": "2022-08-25T10:39:44+00:00" }, + "tests/aws/services/kinesis/test_kinesis.py::TestKinesis::test_resource_policy_crud": { + "last_validated_date": "2025-08-05T15:23:27+00:00", + "durations_in_seconds": { + "setup": 0.63, + "call": 6.43, + "teardown": 0.08, + "total": 7.14 + } + }, "tests/aws/services/kinesis/test_kinesis.py::TestKinesis::test_stream_consumers": { "last_validated_date": "2022-08-26T08:23:46+00:00" }, diff --git a/tests/aws/services/kms/test_kms.py b/tests/aws/services/kms/test_kms.py index 5609a6dd1509c..ed8ec0ed43bbb 100644 --- a/tests/aws/services/kms/test_kms.py +++ b/tests/aws/services/kms/test_kms.py @@ -735,12 +735,7 @@ def test_generate_random_invalid_number_of_bytes( snapshot.match("generate-random-exc", e.value.response) @markers.aws.validated - @markers.snapshot.skip_snapshot_verify( - paths=[ - "$..Error.Message", - "$..message", - ] - ) + @markers.snapshot.skip_snapshot_verify(paths=["$..Error.Message"]) @pytest.mark.parametrize( "key_spec,sign_algo", [ @@ -1459,11 +1454,6 @@ def test_rotate_key_on_demand_raises_error_given_key_that_does_not_exist( snapshot.match("error-response", e.value.response) @markers.aws.validated - @markers.snapshot.skip_snapshot_verify( - paths=[ - "$..message", - ], - ) def test_rotate_key_on_demand_raises_error_given_non_symmetric_key( self, kms_create_key, aws_client, snapshot ): @@ -1968,7 +1958,6 @@ def test_invalid_generate_mac(self, kms_create_key, key_spec, mac_algo, snapshot snapshot.match("generate-mac", e.value.response) @markers.aws.validated - @markers.snapshot.skip_snapshot_verify(paths=["$..message"]) @pytest.mark.parametrize( "key_spec,mac_algo,verify_msg", [ @@ -2065,8 +2054,9 @@ def test_plaintext_size_for_encrypt(self, kms_create_key, snapshot, aws_client): snapshot.match("invalid-plaintext-size-encrypt", e.value.response) @markers.aws.validated - @markers.snapshot.skip_snapshot_verify(paths=["$..message", "$..KeyMaterialId"]) + @markers.snapshot.skip_snapshot_verify(paths=["$..KeyMaterialId"]) def test_encrypt_decrypt_encryption_context(self, kms_create_key, snapshot, aws_client): + snapshot.add_transformer(snapshot.transform.key_value("KeyMaterialId")) key_id = kms_create_key()["KeyId"] message = b"test message 123 !%$@ 1234567890" encryption_context = {"context-key": "context-value"} @@ -2417,7 +2407,6 @@ def test_generate_data_key_without_plaintext(self, kms_key, aws_client, snapshot snapshot.match("generate-data-key-without-plaintext", result) @markers.aws.validated - @markers.snapshot.skip_snapshot_verify(paths=["$..Error.Message", "$..message"]) def test_encryption_context_generate_data_key(self, kms_key, aws_client, snapshot): encryption_context = {"context-key": "context-value"} key_id = kms_key["KeyId"] @@ -2430,7 +2419,6 @@ def test_encryption_context_generate_data_key(self, kms_key, aws_client, snapsho snapshot.match("decrypt-without-encryption-context", e.value.response) @markers.aws.validated - @markers.snapshot.skip_snapshot_verify(paths=["$..Error.Message", "$..message"]) def test_encryption_context_generate_data_key_without_plaintext( self, kms_key, aws_client, snapshot ): @@ -2445,7 +2433,6 @@ def test_encryption_context_generate_data_key_without_plaintext( snapshot.match("decrypt-without-encryption-context", e.value.response) @markers.aws.validated - @markers.snapshot.skip_snapshot_verify(paths=["$..message"]) def test_encryption_context_generate_data_key_pair(self, kms_key, aws_client, snapshot): encryption_context = {"context-key": "context-value"} key_id = kms_key["KeyId"] @@ -2458,7 +2445,6 @@ def test_encryption_context_generate_data_key_pair(self, kms_key, aws_client, sn snapshot.match("decrypt-without-encryption-context", e.value.response) @markers.aws.validated - @markers.snapshot.skip_snapshot_verify(paths=["$..message"]) def test_encryption_context_generate_data_key_pair_without_plaintext( self, kms_key, aws_client, snapshot ): diff --git a/tests/aws/services/kms/test_kms.snapshot.json b/tests/aws/services/kms/test_kms.snapshot.json index c69fa48ebdf89..2a3cfaae82d17 100644 --- a/tests/aws/services/kms/test_kms.snapshot.json +++ b/tests/aws/services/kms/test_kms.snapshot.json @@ -1565,7 +1565,7 @@ } }, "tests/aws/services/kms/test_kms.py::TestKMS::test_encrypt_decrypt_encryption_context": { - "recorded-date": "08-07-2025, 05:53:27", + "recorded-date": "22-09-2025, 21:45:01", "recorded-content": { "encrypt_response": { "CiphertextBlob": "ciphertext-blob", @@ -1579,7 +1579,7 @@ "decrypt_response_with_encryption_context": { "EncryptionAlgorithm": "SYMMETRIC_DEFAULT", "KeyId": "", - "KeyMaterialId": "e2333676b9bf055cb0caa2bec3957d7f3e60b7545a3706314e397746cd26122e", + "KeyMaterialId": "", "Plaintext": "plaintext", "ResponseMetadata": { "HTTPHeaders": {}, diff --git a/tests/aws/services/kms/test_kms.validation.json b/tests/aws/services/kms/test_kms.validation.json index 139126d1707d8..aa83a4a410612 100644 --- a/tests/aws/services/kms/test_kms.validation.json +++ b/tests/aws/services/kms/test_kms.validation.json @@ -69,12 +69,12 @@ "last_validated_date": "2024-04-11T15:53:18+00:00" }, "tests/aws/services/kms/test_kms.py::TestKMS::test_encrypt_decrypt_encryption_context": { - "last_validated_date": "2025-07-08T05:53:27+00:00", + "last_validated_date": "2025-09-22T21:45:01+00:00", "durations_in_seconds": { - "setup": 0.74, - "call": 1.08, - "teardown": 0.15, - "total": 1.97 + "setup": 0.45, + "call": 0.99, + "teardown": 0.12, + "total": 1.56 } }, "tests/aws/services/kms/test_kms.py::TestKMS::test_encrypt_validate_plaintext_size_per_key_type[RSA_2048-RSAES_OAEP_SHA_1]": { diff --git a/tests/aws/services/lambda_/test_lambda_developer_tools.py b/tests/aws/services/lambda_/test_lambda_developer_tools.py index 91caff74c3397..97e2025fd8d08 100644 --- a/tests/aws/services/lambda_/test_lambda_developer_tools.py +++ b/tests/aws/services/lambda_/test_lambda_developer_tools.py @@ -1,6 +1,7 @@ import json import os import time +from pathlib import Path import pytest from botocore.exceptions import ClientError @@ -60,13 +61,21 @@ def test_hot_reloading( f.write(function_content) mount_path = get_host_path_for_path_in_docker(hot_reloading_dir_path) - create_lambda_function_aws( + create_function_response = create_lambda_function_aws( FunctionName=function_name, Handler="handler.handler", Code={"S3Bucket": hot_reloading_bucket, "S3Key": mount_path}, Role=lambda_su_role, Runtime=runtime, ) + # The AWS Toolkit for VS Code depends on this naming convention: + # https://github.com/aws/aws-toolkit-vscode/blob/1f6250148ba4f2c22e89613b8e7801bd8c4be062/packages/core/src/lambda/utils.ts#L212 + assert create_function_response["CodeSha256"].startswith("hot-reloading") + + get_function_response = aws_client.lambda_.get_function(FunctionName=function_name) + code_location_path = Path.from_uri(get_function_response["Code"]["Location"]) + assert str(code_location_path) == mount_path + response = aws_client.lambda_.invoke(FunctionName=function_name, Payload=b"{}") response_dict = json.load(response["Payload"]) assert response_dict["counter"] == 1 diff --git a/tests/aws/services/opensearch/test_opensearch.py b/tests/aws/services/opensearch/test_opensearch.py index b62dfce58f93b..ebebb8f35a13b 100644 --- a/tests/aws/services/opensearch/test_opensearch.py +++ b/tests/aws/services/opensearch/test_opensearch.py @@ -24,11 +24,6 @@ OpenSearchPartitionInstanceType, VolumeType, ) -from localstack.constants import ( - ELASTICSEARCH_DEFAULT_VERSION, - OPENSEARCH_DEFAULT_VERSION, - OPENSEARCH_PLUGIN_LIST, -) from localstack.services.opensearch import provider from localstack.services.opensearch.cluster import CustomEndpoint, EdgeProxiedOpensearchCluster from localstack.services.opensearch.cluster_manager import ( @@ -39,7 +34,12 @@ SingletonClusterManager, create_cluster_manager, ) -from localstack.services.opensearch.packages import opensearch_package +from localstack.services.opensearch.packages import ( + ELASTICSEARCH_DEFAULT_VERSION, + OPENSEARCH_DEFAULT_VERSION, + OPENSEARCH_PLUGIN_LIST, + opensearch_package, +) from localstack.testing.aws.util import is_aws_cloud from localstack.testing.pytest import markers from localstack.utils.common import call_safe, poll_condition, retry, short_uid, start_worker_thread @@ -697,7 +697,6 @@ def test_route_through_edge(self): response = requests.get(cluster_url) assert response.ok, f"cluster endpoint returned an error: {response.text}" - assert response.json()["version"]["number"] == "2.11.1" response = requests.get(f"{cluster_url}/_cluster/health") assert response.ok, f"cluster health endpoint returned an error: {response.text}" diff --git a/tests/aws/services/opensearch/test_opensearch.snapshot.json b/tests/aws/services/opensearch/test_opensearch.snapshot.json index 61a80b9be103f..a3445c9f91e4b 100644 --- a/tests/aws/services/opensearch/test_opensearch.snapshot.json +++ b/tests/aws/services/opensearch/test_opensearch.snapshot.json @@ -1,6 +1,6 @@ { "tests/aws/services/opensearch/test_opensearch.py::TestOpensearchProvider::test_get_compatible_versions": { - "recorded-date": "16-07-2024, 13:05:15", + "recorded-date": "12-09-2025, 10:51:06", "recorded-content": { "source_versions": [ "Elasticsearch_5.1", @@ -25,6 +25,10 @@ "OpenSearch_1.2", "OpenSearch_1.3", "OpenSearch_2.11", + "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19", "OpenSearch_2.3", "OpenSearch_2.5", "OpenSearch_2.7", @@ -159,37 +163,70 @@ "OpenSearch_2.7", "OpenSearch_2.9", "OpenSearch_2.11", - "OpenSearch_2.13" + "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19" ], "source_opensearch_2.11": [ - "OpenSearch_2.13" + "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19" + ], + "source_opensearch_2.13": [ + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19" + ], + "source_opensearch_2.15": [ + "OpenSearch_2.17", + "OpenSearch_2.19" + ], + "source_opensearch_2.17": [ + "OpenSearch_2.19" + ], + "source_opensearch_2.19": [ + "OpenSearch_3.1" ], "source_opensearch_2.3": [ "OpenSearch_2.5", "OpenSearch_2.7", "OpenSearch_2.9", "OpenSearch_2.11", - "OpenSearch_2.13" + "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19" ], "source_opensearch_2.5": [ "OpenSearch_2.7", "OpenSearch_2.9", "OpenSearch_2.11", - "OpenSearch_2.13" + "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19" ], "source_opensearch_2.7": [ "OpenSearch_2.9", "OpenSearch_2.11", - "OpenSearch_2.13" + "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19" ], "source_opensearch_2.9": [ "OpenSearch_2.11", - "OpenSearch_2.13" + "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19" ] } }, "tests/aws/services/opensearch/test_opensearch.py::TestOpensearchProvider::test_list_versions": { - "recorded-date": "16-07-2024, 13:18:18", + "recorded-date": "12-09-2025, 07:47:04", "recorded-content": { "versions": [ "Elasticsearch_5.1", @@ -215,23 +252,29 @@ "OpenSearch_1.3", "OpenSearch_2.11", "OpenSearch_2.13", + "OpenSearch_2.15", + "OpenSearch_2.17", + "OpenSearch_2.19", "OpenSearch_2.3", "OpenSearch_2.5", "OpenSearch_2.7", - "OpenSearch_2.9" + "OpenSearch_2.9", + "OpenSearch_3.1" ] } }, "tests/aws/services/opensearch/test_opensearch.py::TestOpensearchProvider::test_sql_plugin": { - "recorded-date": "03-12-2024, 21:07:16", + "recorded-date": "12-09-2025, 07:44:41", "recorded-content": { - "sql_plugin_installed": true, "sql_query_response": { "datarows": [ [ "I'm just a simple man, trying to make my way in the universe.", "Fett", - "mandalorian armor", + [ + "mandalorian armor", + "tusken culture" + ], "Boba", 41 ] @@ -265,7 +308,7 @@ } }, "tests/aws/services/opensearch/test_opensearch.py::TestOpensearchProvider::test_domain_lifecycle": { - "recorded-date": "07-07-2025, 23:26:58", + "recorded-date": "12-09-2025, 08:04:43", "recorded-content": { "account-arn": "", "create-response": { @@ -273,6 +316,9 @@ "NaturalLanguageQueryGenerationOptions": { "CurrentState": "NOT_ENABLED", "DesiredState": "DISABLED" + }, + "S3VectorsEngine": { + "Enabled": false } }, "ARN": "arn::es::111111111111:domain/", @@ -342,6 +388,14 @@ }, "ValueType": "STRINGIFIED_JSON" }, + { + "ActiveValue": "", + "Name": "AIMLOptions.S3VectorsEngine", + "PendingValue": { + "Enabled": false + }, + "ValueType": "STRINGIFIED_JSON" + }, { "ActiveValue": "", "Name": "AdvancedOptions", @@ -363,6 +417,12 @@ "PendingValue": "false", "ValueType": "PLAIN_TEXT" }, + { + "ActiveValue": "", + "Name": "AdvancedSecurityOptions.IAMFederationOptions", + "PendingValue": "false", + "ValueType": "PLAIN_TEXT" + }, { "ActiveValue": "", "Name": "AdvancedSecurityOptions.InternalUserDatabaseEnabled", @@ -582,6 +642,9 @@ "NaturalLanguageQueryGenerationOptions": { "CurrentState": "NOT_ENABLED", "DesiredState": "DISABLED" + }, + "S3VectorsEngine": { + "Enabled": false } }, "ARN": "arn::es::111111111111:domain/", @@ -680,6 +743,9 @@ "NaturalLanguageQueryGenerationOptions": { "CurrentState": "NOT_ENABLED", "DesiredState": "DISABLED" + }, + "S3VectorsEngine": { + "Enabled": false } }, "Status": { @@ -883,7 +949,7 @@ { "Effect": "Allow", "Principal": { - "AWS": "111111111111" + "AWS": "AIDAWJDXAOVI2A5WATHZF" }, "Action": "es:*", "Resource": "arn::es::111111111111:domain//*" @@ -963,6 +1029,9 @@ "NaturalLanguageQueryGenerationOptions": { "CurrentState": "NOT_ENABLED", "DesiredState": "DISABLED" + }, + "S3VectorsEngine": { + "Enabled": false } }, "ARN": "arn::es::111111111111:domain/", @@ -1072,6 +1141,9 @@ "NaturalLanguageQueryGenerationOptions": { "CurrentState": "NOT_ENABLED", "DesiredState": "DISABLED" + }, + "S3VectorsEngine": { + "Enabled": false } }, "ARN": "arn::es::111111111111:domain/", diff --git a/tests/aws/services/opensearch/test_opensearch.validation.json b/tests/aws/services/opensearch/test_opensearch.validation.json index e7cbb17ad097b..d9d82f0c8c1ae 100644 --- a/tests/aws/services/opensearch/test_opensearch.validation.json +++ b/tests/aws/services/opensearch/test_opensearch.validation.json @@ -1,20 +1,38 @@ { "tests/aws/services/opensearch/test_opensearch.py::TestOpensearchProvider::test_domain_lifecycle": { - "last_validated_date": "2025-07-07T23:26:58+00:00", + "last_validated_date": "2025-09-12T08:04:43+00:00", "durations_in_seconds": { - "setup": 0.53, - "call": 839.91, - "teardown": 0.25, - "total": 840.69 + "setup": 0.74, + "call": 839.36, + "teardown": 0.22, + "total": 840.32 } }, "tests/aws/services/opensearch/test_opensearch.py::TestOpensearchProvider::test_get_compatible_versions": { - "last_validated_date": "2024-07-16T13:05:15+00:00" + "last_validated_date": "2025-09-12T10:51:06+00:00", + "durations_in_seconds": { + "setup": 0.8, + "call": 0.82, + "teardown": 0.01, + "total": 1.63 + } }, "tests/aws/services/opensearch/test_opensearch.py::TestOpensearchProvider::test_list_versions": { - "last_validated_date": "2024-07-16T13:18:18+00:00" + "last_validated_date": "2025-09-12T07:47:04+00:00", + "durations_in_seconds": { + "setup": 0.84, + "call": 0.92, + "teardown": 0.01, + "total": 1.77 + } }, "tests/aws/services/opensearch/test_opensearch.py::TestOpensearchProvider::test_sql_plugin": { - "last_validated_date": "2024-12-03T21:07:16+00:00" + "last_validated_date": "2025-09-12T07:44:42+00:00", + "durations_in_seconds": { + "setup": 0.94, + "call": 1317.86, + "teardown": 0.96, + "total": 1319.76 + } } } diff --git a/tests/aws/services/s3/test_s3_api.py b/tests/aws/services/s3/test_s3_api.py index 0d8eb5d0429e1..cfab12711471b 100644 --- a/tests/aws/services/s3/test_s3_api.py +++ b/tests/aws/services/s3/test_s3_api.py @@ -1613,32 +1613,44 @@ def test_object_tagging_versioned(self, s3_bucket, aws_client, snapshot): tag_set_2 = {"TagSet": [{"Key": "tag3", "Value": "tag3"}]} # test without specifying a VersionId - put_bucket_tags = aws_client.s3.put_object_tagging( + put_object_tags = aws_client.s3.put_object_tagging( Bucket=s3_bucket, Key=object_key, Tagging=tag_set_2 ) - snapshot.match("put-object-tags-current-version", put_bucket_tags) - assert put_bucket_tags["VersionId"] == version_id_2 + snapshot.match("put-object-tags-current-version", put_object_tags) + assert put_object_tags["VersionId"] == version_id_2 - get_bucket_tags = aws_client.s3.get_object_tagging(Bucket=s3_bucket, Key=object_key) - snapshot.match("get-object-tags-current-version", get_bucket_tags) + put_object_tags = aws_client.s3.get_object_tagging(Bucket=s3_bucket, Key=object_key) + snapshot.match("get-object-tags-current-version", put_object_tags) - get_bucket_tags = aws_client.s3.get_object_tagging( + put_object_tags = aws_client.s3.get_object_tagging( Bucket=s3_bucket, Key=object_key, VersionId=version_id_1 ) - snapshot.match("get-object-tags-previous-version", get_bucket_tags) + snapshot.match("get-object-tags-previous-version", put_object_tags) tag_set_2 = {"TagSet": [{"Key": "tag1", "Value": "tag1"}]} # test by specifying a VersionId to Version1 - put_bucket_tags = aws_client.s3.put_object_tagging( + put_object_tags = aws_client.s3.put_object_tagging( Bucket=s3_bucket, Key=object_key, VersionId=version_id_1, Tagging=tag_set_2 ) - snapshot.match("put-object-tags-previous-version", put_bucket_tags) - assert put_bucket_tags["VersionId"] == version_id_1 + snapshot.match("put-object-tags-previous-version", put_object_tags) + assert put_object_tags["VersionId"] == version_id_1 - get_bucket_tags = aws_client.s3.get_object_tagging( + get_object_tags = aws_client.s3.get_object_tagging( Bucket=s3_bucket, Key=object_key, VersionId=version_id_1 ) - snapshot.match("get-object-tags-previous-version-again", get_bucket_tags) + snapshot.match("get-object-tags-previous-version-again", get_object_tags) + + # delete tagging on current object + aws_client.s3.delete_object_tagging(Bucket=s3_bucket, Key=object_key) + get_object_tags = aws_client.s3.get_object_tagging(Bucket=s3_bucket, Key=object_key) + snapshot.match("get-object-tags-deleted-current", get_object_tags) + + # delete object tagging on previous version too + aws_client.s3.delete_object_tagging( + Bucket=s3_bucket, Key=object_key, VersionId=version_id_1 + ) + get_object_tags = aws_client.s3.get_object_tagging(Bucket=s3_bucket, Key=object_key) + snapshot.match("get-object-tags-previous-version-deleted", get_object_tags) # Put a DeleteMarker on top of the stack delete_current = aws_client.s3.delete_object(Bucket=s3_bucket, Key=object_key) diff --git a/tests/aws/services/s3/test_s3_api.snapshot.json b/tests/aws/services/s3/test_s3_api.snapshot.json index a2402512fb476..710c164da10ad 100644 --- a/tests/aws/services/s3/test_s3_api.snapshot.json +++ b/tests/aws/services/s3/test_s3_api.snapshot.json @@ -2007,7 +2007,7 @@ } }, "tests/aws/services/s3/test_s3_api.py::TestS3BucketObjectTagging::test_object_tagging_versioned": { - "recorded-date": "21-01-2025, 18:11:16", + "recorded-date": "19-09-2025, 23:28:45", "recorded-content": { "put-obj-0": { "ChecksumCRC32": "XCKz9A==", @@ -2084,6 +2084,22 @@ "HTTPStatusCode": 200 } }, + "get-object-tags-deleted-current": { + "TagSet": [], + "VersionId": "", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "get-object-tags-previous-version-deleted": { + "TagSet": [], + "VersionId": "", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, "put-delete-marker": { "DeleteMarker": true, "VersionId": "", diff --git a/tests/aws/services/s3/test_s3_api.validation.json b/tests/aws/services/s3/test_s3_api.validation.json index 0b7c42d54b1b8..2dbd3e8427102 100644 --- a/tests/aws/services/s3/test_s3_api.validation.json +++ b/tests/aws/services/s3/test_s3_api.validation.json @@ -63,7 +63,13 @@ } }, "tests/aws/services/s3/test_s3_api.py::TestS3BucketObjectTagging::test_object_tagging_versioned": { - "last_validated_date": "2025-01-21T18:11:16+00:00" + "last_validated_date": "2025-09-19T23:28:46+00:00", + "durations_in_seconds": { + "setup": 1.08, + "call": 2.55, + "teardown": 1.07, + "total": 4.7 + } }, "tests/aws/services/s3/test_s3_api.py::TestS3BucketObjectTagging::test_object_tags_delete_or_overwrite_object": { "last_validated_date": "2025-01-21T18:11:22+00:00" diff --git a/tests/aws/services/ses/test_ses.py b/tests/aws/services/ses/test_ses.py index 3073f4a922a2b..c6fd53dd13df4 100644 --- a/tests/aws/services/ses/test_ses.py +++ b/tests/aws/services/ses/test_ses.py @@ -6,6 +6,7 @@ import pytest import requests from botocore.exceptions import ClientError +from localstack_snapshot.snapshots.transformer import SortingTransformer import localstack.config as config from localstack.services.ses.provider import EMAILS, EMAILS_ENDPOINT @@ -287,6 +288,39 @@ def _assert_sent_quota(expected_counter: int) -> dict: _ = retry(_assert_sent_quota, expected_counter=counter + 1, retries=retries, sleep=1) # snapshot.match('get-quota-3', _) + @markers.aws.validated + def test_describe_config_set_event_destinations( + self, + aws_client, + sns_topic, + ses_configuration_set, + ses_configuration_set_sns_event_destination, + snapshot, + ): + config_set_name = f"config-set-{short_uid()}" + snapshot.add_transformer(snapshot.transform.regex(config_set_name, "")) + + topic_arn = sns_topic["Attributes"]["TopicArn"] + snapshot.add_transformer(snapshot.transform.regex(topic_arn, "")) + + ses_configuration_set(config_set_name) + event_destination_name = f"config-set-event-destination-{short_uid()}" + snapshot.add_transformer( + snapshot.transform.regex(event_destination_name, "") + ) + + snapshot.add_transformer(SortingTransformer("MatchingEventTypes")) + + ses_configuration_set_sns_event_destination( + config_set_name, event_destination_name, topic_arn + ) + + response = aws_client.ses.describe_configuration_set( + ConfigurationSetName=config_set_name, + ConfigurationSetAttributeNames=["eventDestinations"], + ) + snapshot.match("event_destinations", response) + @markers.aws.validated @markers.snapshot.skip_snapshot_verify( paths=[ diff --git a/tests/aws/services/ses/test_ses.snapshot.json b/tests/aws/services/ses/test_ses.snapshot.json index 83af3b68b959e..a0f8bbfb6c02d 100644 --- a/tests/aws/services/ses/test_ses.snapshot.json +++ b/tests/aws/services/ses/test_ses.snapshot.json @@ -806,7 +806,7 @@ } }, "tests/aws/services/ses/test_ses.py::TestSES::test_clone_receipt_rule_set": { - "recorded-date": "25-08-2023, 23:05:14", + "recorded-date": "17-09-2025, 11:56:18", "recorded-content": { "create-receipt-rule-set": { "ResponseMetadata": { @@ -1081,5 +1081,35 @@ } } } + }, + "tests/aws/services/ses/test_ses.py::TestSES::test_describe_config_set_event_destinations": { + "recorded-date": "20-08-2025, 12:00:48", + "recorded-content": { + "event_destinations": { + "ConfigurationSet": { + "Name": "" + }, + "EventDestinations": [ + { + "Enabled": true, + "MatchingEventTypes": [ + "bounce", + "click", + "delivery", + "open", + "send" + ], + "Name": "", + "SNSDestination": { + "TopicARN": "" + } + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } } } diff --git a/tests/aws/services/ses/test_ses.validation.json b/tests/aws/services/ses/test_ses.validation.json index 99979dd63f872..b344177c9dd2f 100644 --- a/tests/aws/services/ses/test_ses.validation.json +++ b/tests/aws/services/ses/test_ses.validation.json @@ -3,7 +3,13 @@ "last_validated_date": "2023-08-25T22:04:12+00:00" }, "tests/aws/services/ses/test_ses.py::TestSES::test_clone_receipt_rule_set": { - "last_validated_date": "2023-08-25T21:05:14+00:00" + "last_validated_date": "2025-09-17T11:56:18+00:00", + "durations_in_seconds": { + "setup": 1.83, + "call": 2.56, + "teardown": 2.05, + "total": 6.44 + } }, "tests/aws/services/ses/test_ses.py::TestSES::test_creating_event_destination_without_configuration_set": { "last_validated_date": "2023-08-25T22:04:35+00:00" @@ -17,6 +23,15 @@ "tests/aws/services/ses/test_ses.py::TestSES::test_deleting_non_existent_configuration_set_event_destination": { "last_validated_date": "2023-08-25T22:04:53+00:00" }, + "tests/aws/services/ses/test_ses.py::TestSES::test_describe_config_set_event_destinations": { + "last_validated_date": "2025-08-20T12:00:48+00:00", + "durations_in_seconds": { + "setup": 2.09, + "call": 1.15, + "teardown": 0.58, + "total": 3.82 + } + }, "tests/aws/services/ses/test_ses.py::TestSES::test_invalid_tags_send_email[-]": { "last_validated_date": "2024-07-30T10:18:09+00:00" }, diff --git a/tests/aws/services/sqs/test_sqs.py b/tests/aws/services/sqs/test_sqs.py index bd8645111c43c..3550246595946 100644 --- a/tests/aws/services/sqs/test_sqs.py +++ b/tests/aws/services/sqs/test_sqs.py @@ -1079,6 +1079,132 @@ def _assert(): retry(_assert) + @markers.aws.validated + def test_approximate_number_of_messages_not_visible(self, sqs_create_queue, aws_sqs_client): + # note that this test takes a bit longer when running on AWS, because we need to wait for propagation of + # queue attributes + queue_url = sqs_create_queue( + Attributes={ + "VisibilityTimeout": "60" if is_aws_cloud() else "5", + }, + ) + + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-1") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-2") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-3") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-4") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-5") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-6") + + # receive 1 message (from message group 1), now 5 messages should be visible + aws_sqs_client.receive_message(QueueUrl=queue_url, MaxNumberOfMessages=1) + + def _assert_attributes_before_visibility_timeout(): + _result = aws_sqs_client.get_queue_attributes( + QueueUrl=queue_url, + AttributeNames=[ + "ApproximateNumberOfMessages", + "ApproximateNumberOfMessagesNotVisible", + "ApproximateNumberOfMessagesDelayed", + ], + ) + assert _result["Attributes"] == { + "ApproximateNumberOfMessages": "5", + "ApproximateNumberOfMessagesNotVisible": "1", + "ApproximateNumberOfMessagesDelayed": "0", + } + + retry(_assert_attributes_before_visibility_timeout, retries=60, sleep=1) + + def _assert_after_visibility_timeout(): + _result = aws_sqs_client.get_queue_attributes( + QueueUrl=queue_url, + AttributeNames=[ + "ApproximateNumberOfMessages", + "ApproximateNumberOfMessagesNotVisible", + "ApproximateNumberOfMessagesDelayed", + ], + ) + assert _result["Attributes"] == { + "ApproximateNumberOfMessages": "6", + "ApproximateNumberOfMessagesNotVisible": "0", + "ApproximateNumberOfMessagesDelayed": "0", + } + + retry(_assert_after_visibility_timeout, retries=60, sleep=1) + + @markers.aws.validated + def test_fifo_approximate_number_of_messages_not_visible( + self, sqs_create_queue, aws_sqs_client + ): + # note that this test takes a bit longer when running on AWS, because we need to wait for propagation of + # queue attributes + queue_url = sqs_create_queue( + QueueName=f"queue-{short_uid()}.fifo", + Attributes={ + "FifoQueue": "True", + "ContentBasedDeduplication": "True", + "VisibilityTimeout": "60" if is_aws_cloud() else "5", + }, + ) + + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-1", MessageGroupId="1") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-2", MessageGroupId="1") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-3", MessageGroupId="2") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-4", MessageGroupId="2") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-5", MessageGroupId="3") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-6", MessageGroupId="3") + + # receive 2 messages (from message group 1 and group 2), now two groups are invisible (4 messages), but only 2 + # messages should be marked as truly invisible (the ones received) + message_1 = aws_sqs_client.receive_message( + QueueUrl=queue_url, + MaxNumberOfMessages=1, + MessageSystemAttributeNames=["MessageGroupId"], + ) + message_2 = aws_sqs_client.receive_message( + QueueUrl=queue_url, + MaxNumberOfMessages=1, + MessageSystemAttributeNames=["MessageGroupId"], + ) + + assert message_1["Messages"][0]["Attributes"]["MessageGroupId"] == "1" + assert message_2["Messages"][0]["Attributes"]["MessageGroupId"] == "2" + + def _assert_attributes_before_visibility_timeout(): + _result = aws_sqs_client.get_queue_attributes( + QueueUrl=queue_url, + AttributeNames=[ + "ApproximateNumberOfMessages", + "ApproximateNumberOfMessagesNotVisible", + "ApproximateNumberOfMessagesDelayed", + ], + ) + assert _result["Attributes"] == { + "ApproximateNumberOfMessages": "4", + "ApproximateNumberOfMessagesNotVisible": "2", + "ApproximateNumberOfMessagesDelayed": "0", + } + + retry(_assert_attributes_before_visibility_timeout, retries=60, sleep=1) + + def _assert_after_visibility_timeout(): + _result = aws_sqs_client.get_queue_attributes( + QueueUrl=queue_url, + AttributeNames=[ + "ApproximateNumberOfMessages", + "ApproximateNumberOfMessagesNotVisible", + "ApproximateNumberOfMessagesDelayed", + ], + ) + assert _result["Attributes"] == { + "ApproximateNumberOfMessages": "6", + "ApproximateNumberOfMessagesNotVisible": "0", + "ApproximateNumberOfMessagesDelayed": "0", + } + + retry(_assert_after_visibility_timeout, retries=60, sleep=1) + @markers.aws.validated def test_receive_after_visibility_timeout(self, sqs_create_queue, aws_sqs_client): queue_url = sqs_create_queue(Attributes={"VisibilityTimeout": "1"}) diff --git a/tests/aws/services/sqs/test_sqs.snapshot.json b/tests/aws/services/sqs/test_sqs.snapshot.json index 413816363848d..59b4b256f7276 100644 --- a/tests/aws/services/sqs/test_sqs.snapshot.json +++ b/tests/aws/services/sqs/test_sqs.snapshot.json @@ -3757,7 +3757,7 @@ } }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_send_empty_message[sqs]": { - "recorded-date": "05-03-2025, 19:25:09", + "recorded-date": "22-09-2025, 16:21:08", "recorded-content": { "send_empty_message": { "Error": { @@ -3774,7 +3774,7 @@ } }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_send_empty_message[sqs_query]": { - "recorded-date": "05-03-2025, 19:25:09", + "recorded-date": "22-09-2025, 16:21:09", "recorded-content": { "send_empty_message": { "Error": { @@ -4203,5 +4203,21 @@ } } } + }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_approximate_number_of_messages_not_visible[sqs]": { + "recorded-date": "25-09-2025, 21:46:19", + "recorded-content": {} + }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_approximate_number_of_messages_not_visible[sqs_query]": { + "recorded-date": "25-09-2025, 21:47:19", + "recorded-content": {} + }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_approximate_number_of_messages_not_visible[sqs]": { + "recorded-date": "25-09-2025, 21:53:42", + "recorded-content": {} + }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_approximate_number_of_messages_not_visible[sqs_query]": { + "recorded-date": "25-09-2025, 21:54:43", + "recorded-content": {} } } diff --git a/tests/aws/services/sqs/test_sqs.validation.json b/tests/aws/services/sqs/test_sqs.validation.json index 356fb16117169..3b62f59b54ae3 100644 --- a/tests/aws/services/sqs/test_sqs.validation.json +++ b/tests/aws/services/sqs/test_sqs.validation.json @@ -1,4 +1,22 @@ { + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_approximate_number_of_messages_not_visible[sqs]": { + "last_validated_date": "2025-09-26T16:41:28+00:00", + "durations_in_seconds": { + "setup": 0.19, + "call": 61.13, + "teardown": 0.07, + "total": 61.39 + } + }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_approximate_number_of_messages_not_visible[sqs_query]": { + "last_validated_date": "2025-09-26T16:42:30+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 61.63, + "teardown": 0.09, + "total": 61.72 + } + }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_aws_trace_header_propagation[sqs]": { "last_validated_date": "2025-06-27T10:55:55+00:00", "durations_in_seconds": { @@ -146,6 +164,24 @@ "total": 0.86 } }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_approximate_number_of_messages_not_visible[sqs]": { + "last_validated_date": "2025-09-26T16:46:35+00:00", + "durations_in_seconds": { + "setup": 5.18, + "call": 62.17, + "teardown": 0.08, + "total": 67.43 + } + }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_approximate_number_of_messages_not_visible[sqs_query]": { + "last_validated_date": "2025-09-26T16:47:37+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 62.34, + "teardown": 0.07, + "total": 62.41 + } + }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_change_to_high_throughput_after_creation[sqs]": { "last_validated_date": "2025-08-19T13:29:53+00:00", "durations_in_seconds": { @@ -405,10 +441,22 @@ "last_validated_date": "2024-04-30T13:40:12+00:00" }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_send_empty_message[sqs]": { - "last_validated_date": "2025-03-05T19:25:09+00:00" + "last_validated_date": "2025-09-22T16:21:08+00:00", + "durations_in_seconds": { + "setup": 1.03, + "call": 0.11, + "teardown": 0.17, + "total": 1.31 + } }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_send_empty_message[sqs_query]": { - "last_validated_date": "2025-03-05T19:25:09+00:00" + "last_validated_date": "2025-09-22T16:21:09+00:00", + "durations_in_seconds": { + "setup": 0.15, + "call": 0.43, + "teardown": 0.15, + "total": 0.73 + } }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_send_message_batch[sqs]": { "last_validated_date": "2024-04-30T13:40:08+00:00" diff --git a/tests/aws/services/sqs/test_sqs_backdoor.py b/tests/aws/services/sqs/test_sqs_developer_api.py similarity index 87% rename from tests/aws/services/sqs/test_sqs_backdoor.py rename to tests/aws/services/sqs/test_sqs_developer_api.py index 39669a61f51fd..582ddfe8e2a75 100644 --- a/tests/aws/services/sqs/test_sqs_backdoor.py +++ b/tests/aws/services/sqs/test_sqs_developer_api.py @@ -35,7 +35,7 @@ def _parse_attribute_map(json_message: dict) -> dict[str, str]: # @pytest.mark.usefixtures("openapi_validate") -class TestSqsDeveloperEndpoints: +class TestSqsDeveloperApi: @markers.aws.only_localstack @pytest.mark.parametrize("strategy", ["standard", "domain", "path"]) def test_list_messages_has_no_side_effects( @@ -235,6 +235,67 @@ def test_list_messages_with_invisible_messages( assert _parse_attribute_map(messages[1])["IsVisible"] == "true" assert _parse_attribute_map(messages[2])["IsVisible"] == "true" + @markers.aws.only_localstack + @pytest.mark.parametrize("strategy", ["standard", "domain", "path"]) + def test_fifo_list_messages_with_invisible_messages( + self, + sqs_create_queue, + aws_client, + monkeypatch, + strategy, + ): + monkeypatch.setattr(config, "SQS_ENDPOINT_STRATEGY", strategy) + + queue_url = sqs_create_queue( + QueueName=f"queue-{short_uid()}.fifo", + Attributes={ + "FifoQueue": "true", + "ContentBasedDeduplication": "true", + "VisibilityTimeout": "120", + }, + ) + aws_client.sqs.send_message(QueueUrl=queue_url, MessageBody="message-1", MessageGroupId="1") + aws_client.sqs.send_message(QueueUrl=queue_url, MessageBody="message-2", MessageGroupId="1") + aws_client.sqs.send_message(QueueUrl=queue_url, MessageBody="message-3", MessageGroupId="2") + aws_client.sqs.send_message(QueueUrl=queue_url, MessageBody="message-4", MessageGroupId="2") + + # check out a messages + aws_client.sqs.receive_message(QueueUrl=queue_url, MaxNumberOfMessages=1) + + response = requests.get( + "http://localhost:4566/_aws/sqs/messages", + params={"QueueUrl": queue_url, "ShowInvisible": False}, + headers={"Accept": "application/json"}, + ) + doc = response.json() + messages = doc["ReceiveMessageResponse"]["ReceiveMessageResult"]["Message"] + assert len(messages) == 2 + assert messages[0]["Body"] == "message-3" + assert messages[1]["Body"] == "message-4" + + response = requests.get( + "http://localhost:4566/_aws/sqs/messages", + params={"QueueUrl": queue_url, "ShowInvisible": True}, + headers={"Accept": "application/json"}, + ) + doc = response.json() + messages: list[dict] = doc["ReceiveMessageResponse"]["ReceiveMessageResult"]["Message"] + assert len(messages) == 4 + # there are no clear sorting rules in this scenario (fifo queues, invisible, + the way messages are collected) + messages.sort(key=lambda k: k["Body"]) + assert messages[0]["Body"] == "message-1" + assert messages[1]["Body"] == "message-2" + assert messages[2]["Body"] == "message-3" + assert messages[3]["Body"] == "message-4" + + assert _parse_attribute_map(messages[0])["IsVisible"] == "false" + # so technically the message itself IS visible, but the message *group* is invisible. implementing that this + # shows "false" for message-2 requires a bit of rework in our current implementation, so i would consider this a + # fair limitation for now, given its subject to interpretation anyway. + assert _parse_attribute_map(messages[1])["IsVisible"] == "true" + assert _parse_attribute_map(messages[2])["IsVisible"] == "true" + assert _parse_attribute_map(messages[3])["IsVisible"] == "true" + @markers.aws.only_localstack @pytest.mark.parametrize("strategy", ["standard", "domain", "path"]) def test_list_messages_with_delayed_messages( diff --git a/tests/aws/templates/cdk-lambda-redeploy.json b/tests/aws/templates/cdk-lambda-redeploy.json new file mode 100644 index 0000000000000..dc013eedf6d26 --- /dev/null +++ b/tests/aws/templates/cdk-lambda-redeploy.json @@ -0,0 +1,124 @@ +{ + "Resources": { + "ReproPaymentsHandlerServiceRole7453EE1B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + }, + "Metadata": { + "aws:cdk:path": "CdkStack/Repro/PaymentsHandler/ServiceRole/Resource" + } + }, + "ReproPaymentsHandlerFA388BE4": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "DeployBucket" + }, + "S3Key": { + "Ref": "DeployKey" + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "ReproPaymentsHandlerServiceRole7453EE1B", + "Arn" + ] + }, + "Runtime": "nodejs22.x" + }, + "DependsOn": [ + "ReproPaymentsHandlerServiceRole7453EE1B" + ], + "Metadata": { + "aws:cdk:path": "CdkStack/Repro/PaymentsHandler/Resource", + "aws:asset:path": "asset.83bd6ee15f56848d793729be08e79f53eed8a43e4d0857f3ad9a83280cb4784b", + "aws:asset:is-bundled": true, + "aws:asset:property": "Code" + } + }, + "CDKMetadata": { + "Type": "AWS::CDK::Metadata", + "Properties": { + "Analytics": "v2:deflate64:H4sIAAAAAAAA/zXNywqDMBCF4WdxP05thEKXVeiyBX0AGeMo8RKLk9SF+O4lSlff6vxHoUrueI1olVg3QzyaGrfSkR6AVqm2kaa6ocrODfeCr4Ont9qZ2YKhCbdiHhny1gZ3kLQiEXaCjwBIipnXA7uMhOGsYd7af2OHgmX2i2Y4BqWjztguBN/efbzbIXxjL5evUni9YRL1Yky8eOvMxFic/gBWkHMZyAAAAA==" + }, + "Metadata": { + "aws:cdk:path": "CdkStack/CDKMetadata/Default" + } + } + }, + "Outputs": { + "ReproLambdaName070199FC": { + "Value": { + "Ref": "ReproPaymentsHandlerFA388BE4" + } + } + }, + "Parameters": { + "DeployBucket": { + "Type": "String" + }, + "DeployKey": { + "Type": "String" + }, + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} diff --git a/tests/aws/templates/resolve_secretsmanager_with_backslashes.yaml b/tests/aws/templates/resolve_secretsmanager_with_backslashes.yaml new file mode 100644 index 0000000000000..78ceb01dcc29e --- /dev/null +++ b/tests/aws/templates/resolve_secretsmanager_with_backslashes.yaml @@ -0,0 +1,24 @@ +Parameters: + DynamicParameter: + Type: String + +Resources: + MyParameter: + Type: AWS::SSM::Parameter + Properties: + Type: String + Value: + Fn::Join: + - "" + - - "{{resolve:secretsmanager:arn:" + - Ref: AWS::Partition + - ":secretsmanager:" + - Ref: AWS::Region + - ":" + - Ref: AWS::AccountId + - ":secret:" + - Ref: DynamicParameter + - ":SecretString:::}}" +Outputs: + ParameterValue: + Value: !GetAtt MyParameter.Value diff --git a/tests/aws/templates/resolve_ssm.yaml b/tests/aws/templates/resolve_ssm.yaml index d67220bb00a3d..692d2c881688e 100644 --- a/tests/aws/templates/resolve_ssm.yaml +++ b/tests/aws/templates/resolve_ssm.yaml @@ -2,14 +2,25 @@ Parameters: DynamicParameter: Type: String + ParameterName: + Type: String + Resources: topic69831491: Type: AWS::SNS::Topic Properties: - TopicName: !Join [ "", [ "{{resolve:ssm:", !Ref DynamicParameter, "}}" ] ] + TopicName: !Join [ "", [ "{{resolve:ssm:", !Ref DynamicParameter, "}}" ] ] + MyParameter: + Type: AWS::SSM::Parameter + Properties: + Type: String + Value: !Sub "abc:{{resolve:ssm:${ParameterName}}}" Outputs: TopicName: Value: Fn::GetAtt: - topic69831491 - TopicName + + ParameterValue: + Value: !GetAtt MyParameter.Value diff --git a/tests/aws/templates/stack-id-validation.yaml b/tests/aws/templates/stack-id-validation.yaml new file mode 100644 index 0000000000000..b504527d00be5 --- /dev/null +++ b/tests/aws/templates/stack-id-validation.yaml @@ -0,0 +1,22 @@ +Resources: + MyParameter: + Type: AWS::SSM::Parameter + Properties: + Type: String + Value: !Join + - "-" + - - !Ref AWS::StackName + - "s3logs" + - Fn::Select: + - 4 + - Fn::Split: + - '-' + - Fn::Select: + - 2 + - Fn::Split: + - / + - Ref: AWS::StackId + +Outputs: + ParameterValue: + Value: !GetAtt MyParameter.Value diff --git a/tests/aws/test_moto.py b/tests/aws/test_moto.py index d5f4aa48af582..c1de30c2aba97 100644 --- a/tests/aws/test_moto.py +++ b/tests/aws/test_moto.py @@ -20,7 +20,7 @@ def test_call_with_sqs_creates_state_correctly(): qname = f"queue-{short_uid()}" response = moto.call_moto( - moto.create_aws_request_context("sqs", "CreateQueue", {"QueueName": qname}), + moto.create_aws_request_context("sqs", "CreateQueue", "json", {"QueueName": qname}), include_response_metadata=True, ) url = response["QueueUrl"] @@ -29,12 +29,14 @@ def test_call_with_sqs_creates_state_correctly(): assert response["ResponseMetadata"]["HTTPStatusCode"] == 200 assert response["QueueUrl"].endswith(f"/{qname}") - response = moto.call_moto(moto.create_aws_request_context("sqs", "ListQueues")) + response = moto.call_moto(moto.create_aws_request_context("sqs", "ListQueues", "json")) assert url in response["QueueUrls"] finally: - moto.call_moto(moto.create_aws_request_context("sqs", "DeleteQueue", {"QueueUrl": url})) + moto.call_moto( + moto.create_aws_request_context("sqs", "DeleteQueue", "json", {"QueueUrl": url}) + ) - response = moto.call_moto(moto.create_aws_request_context("sqs", "ListQueues")) + response = moto.call_moto(moto.create_aws_request_context("sqs", "ListQueues", "json")) assert url not in response.get("QueueUrls", []) @@ -45,6 +47,7 @@ def test_call_sqs_invalid_call_raises_http_exception(): moto.create_aws_request_context( "sqs", "DeleteQueue", + "json", { "QueueUrl": "http://0.0.0.0/nonexistingqueue", }, @@ -58,7 +61,7 @@ def test_call_non_implemented_operation(): with pytest.raises(NotImplementedError): # we'll need to keep finding methods that moto doesn't implement ;-) moto.call_moto( - moto.create_aws_request_context("athena", "DeleteDataCatalog", {"Name": "foo"}) + moto.create_aws_request_context("athena", "DeleteDataCatalog", "json", {"Name": "foo"}) ) @@ -70,11 +73,11 @@ def test_call_with_sqs_modifies_state_in_moto_backend(): qname = f"queue-{short_uid()}" response = moto.call_moto( - moto.create_aws_request_context("sqs", "CreateQueue", {"QueueName": qname}) + moto.create_aws_request_context("sqs", "CreateQueue", "json", {"QueueName": qname}) ) url = response["QueueUrl"] assert qname in sqs_backends[DEFAULT_MOTO_ACCOUNT_ID][AWS_REGION_US_EAST_1].queues - moto.call_moto(moto.create_aws_request_context("sqs", "DeleteQueue", {"QueueUrl": url})) + moto.call_moto(moto.create_aws_request_context("sqs", "DeleteQueue", "json", {"QueueUrl": url})) assert qname not in sqs_backends[DEFAULT_MOTO_ACCOUNT_ID][AWS_REGION_US_EAST_1].queues @@ -93,17 +96,21 @@ def test_call_s3_with_streaming_trait(payload, monkeypatch): key_name = f"key-{short_uid()}" # create the bucket - moto.call_moto(moto.create_aws_request_context("s3", "CreateBucket", {"Bucket": bucket_name})) + moto.call_moto( + moto.create_aws_request_context("s3", "CreateBucket", "rest-xml", {"Bucket": bucket_name}) + ) moto.call_moto( moto.create_aws_request_context( - "s3", "PutObject", {"Bucket": bucket_name, "Key": key_name, "Body": payload} + "s3", "PutObject", "rest-xml", {"Bucket": bucket_name, "Key": key_name, "Body": payload} ) ) # check whether it was created/received correctly response = moto.call_moto( - moto.create_aws_request_context("s3", "GetObject", {"Bucket": bucket_name, "Key": key_name}) + moto.create_aws_request_context( + "s3", "GetObject", "rest-xml", {"Bucket": bucket_name, "Key": key_name} + ) ) assert hasattr(response["Body"], "read"), ( f"expected Body to be readable, was {type(response['Body'])}" @@ -113,15 +120,17 @@ def test_call_s3_with_streaming_trait(payload, monkeypatch): # cleanup moto.call_moto( moto.create_aws_request_context( - "s3", "DeleteObject", {"Bucket": bucket_name, "Key": key_name} + "s3", "DeleteObject", "rest-xml", {"Bucket": bucket_name, "Key": key_name} ) ) - moto.call_moto(moto.create_aws_request_context("s3", "DeleteBucket", {"Bucket": bucket_name})) + moto.call_moto( + moto.create_aws_request_context("s3", "DeleteBucket", "rest-xml", {"Bucket": bucket_name}) + ) @markers.aws.only_localstack def test_call_include_response_metadata(): - ctx = moto.create_aws_request_context("sqs", "ListQueues") + ctx = moto.create_aws_request_context("sqs", "ListQueues", "json") response = moto.call_moto(ctx) assert "ResponseMetadata" not in response @@ -137,14 +146,14 @@ def test_call_with_modified_request(): qname1 = f"queue-{short_uid()}" qname2 = f"queue-{short_uid()}" - context = moto.create_aws_request_context("sqs", "CreateQueue", {"QueueName": qname1}) + context = moto.create_aws_request_context("sqs", "CreateQueue", "json", {"QueueName": qname1}) response = moto.call_moto_with_request(context, {"QueueName": qname2}) # overwrite old request url = response["QueueUrl"] assert qname2 in sqs_backends[DEFAULT_MOTO_ACCOUNT_ID][AWS_REGION_US_EAST_1].queues assert qname1 not in sqs_backends[DEFAULT_MOTO_ACCOUNT_ID][AWS_REGION_US_EAST_1].queues - moto.call_moto(moto.create_aws_request_context("sqs", "DeleteQueue", {"QueueUrl": url})) + moto.call_moto(moto.create_aws_request_context("sqs", "DeleteQueue", "json", {"QueueUrl": url})) @markers.aws.only_localstack @@ -154,6 +163,7 @@ def test_call_with_es_creates_state_correctly(): moto.create_aws_request_context( "es", "CreateElasticsearchDomain", + "rest-json", { "DomainName": domain_name, "ElasticsearchVersion": "7.10", @@ -169,7 +179,7 @@ def test_call_with_es_creates_state_correctly(): finally: response = moto.call_moto( moto.create_aws_request_context( - "es", "DeleteElasticsearchDomain", {"DomainName": domain_name} + "es", "DeleteElasticsearchDomain", "rest-json", {"DomainName": domain_name} ), include_response_metadata=True, ) @@ -185,12 +195,12 @@ def test_call_multi_region_backends(): moto.call_moto( moto.create_aws_request_context( - "sqs", "CreateQueue", {"QueueName": qname_us}, region="us-east-1" + "sqs", "CreateQueue", "json", {"QueueName": qname_us}, region="us-east-1" ) ) moto.call_moto( moto.create_aws_request_context( - "sqs", "CreateQueue", {"QueueName": qname_eu}, region="eu-central-1" + "sqs", "CreateQueue", "json", {"QueueName": qname_eu}, region="eu-central-1" ) ) @@ -211,6 +221,7 @@ def test_call_with_sqs_invalid_call_raises_exception(): moto.create_aws_request_context( "sqs", "DeleteQueue", + "json", { "QueueUrl": "http://0.0.0.0/nonexistingqueue", }, @@ -223,7 +234,7 @@ def test_call_with_sqs_returns_service_response(): qname = f"queue-{short_uid()}" create_queue_response = moto.call_moto( - moto.create_aws_request_context("sqs", "CreateQueue", {"QueueName": qname}) + moto.create_aws_request_context("sqs", "CreateQueue", "json", {"QueueName": qname}) ) assert "QueueUrl" in create_queue_response @@ -247,6 +258,7 @@ def test_call_with_sns_with_full_uri(): sns_service = load_service("sns") context = RequestContext(sns_request) context.account = "test" + context.protocol = "query" context.region = "us-west-1" context.service = sns_service context.operation = sns_service.operation_model("CreateTopic") @@ -286,7 +298,7 @@ def test_moto_fallback_dispatcher(): assert "CreateQueue" in dispatcher def _dispatch(action, params): - context = moto.create_aws_request_context("sqs", action, params) + context = moto.create_aws_request_context("sqs", action, "json", params) return dispatcher[action](context, params) qname = f"queue-{short_uid()}" @@ -346,7 +358,7 @@ def test_moto_fallback_dispatcher_error_handling(monkeypatch): dispatcher = MotoFallbackDispatcher(provider) def _dispatch(action, params): - context = moto.create_aws_request_context("s3", action, params) + context = moto.create_aws_request_context("s3", action, "rest-xml", params) return dispatcher[action](context, params) bucket_name = f"bucket-{short_uid()}" @@ -375,7 +387,7 @@ def test_request_with_response_header_location_fields(): # CreateHostedZoneResponse has a member "Location" that's located in the headers zone_name = f"zone-{short_uid()}.com" request = moto.create_aws_request_context( - "route53", "CreateHostedZone", {"Name": zone_name, "CallerReference": "test"} + "route53", "CreateHostedZone", "rest-xml", {"Name": zone_name, "CallerReference": "test"} ) response = moto.call_moto(request, include_response_metadata=True) # assert response["Location"] # FIXME: this is required according to the spec, but not returned by moto @@ -384,6 +396,14 @@ def test_request_with_response_header_location_fields(): # clean up moto.call_moto( moto.create_aws_request_context( - "route53", "DeleteHostedZone", {"Id": response["HostedZone"]["Id"]} + "route53", "DeleteHostedZone", "rest-xml", {"Id": response["HostedZone"]["Id"]} ) ) + + +@markers.aws.only_localstack +@pytest.mark.skip(reason="json protocol for CloudWatch is not yet supported by Moto") +def test_request_with_multi_protocol_non_default_protocol(): + request = moto.create_aws_request_context("cloudwatch", "DescribeAlarms", "json") + response = moto.call_moto(request) + assert "CompositeAlarms" in response diff --git a/tests/aws/test_terraform.py b/tests/aws/test_terraform.py deleted file mode 100644 index 53a4931e2626e..0000000000000 --- a/tests/aws/test_terraform.py +++ /dev/null @@ -1,246 +0,0 @@ -import os -import re -import threading - -import pytest - -from localstack.packages.terraform import terraform_package -from localstack.testing.config import ( - TEST_AWS_ACCESS_KEY_ID, - TEST_AWS_REGION_NAME, - TEST_AWS_SECRET_ACCESS_KEY, -) -from localstack.testing.pytest import markers -from localstack.utils.common import is_command_available, rm_rf, run, start_worker_thread - -# TODO: remove all of these - -BUCKET_NAME = "tf-bucket" -QUEUE_NAME = "tf-queue" -QUEUE_ARN = "arn:aws:sqs:us-east-1:{account_id}:tf-queue" - -# lambda Testing Variables -LAMBDA_NAME = "tf-lambda" -LAMBDA_ARN = "arn:aws:lambda:us-east-1:{account_id}:function:{lambda_name}" -LAMBDA_HANDLER = "index.handler" -LAMBDA_RUNTIME = "python3.8" -LAMBDA_ROLE = "arn:aws:iam::{account_id}:role/iam_for_lambda" - -INIT_LOCK = threading.RLock() - -# set after calling install() -TERRAFORM_BIN = None - - -def check_terraform_version(): - if not is_command_available(TERRAFORM_BIN): - return False, None - - ver_string = run([TERRAFORM_BIN, "-version"]) - ver_string = re.search(r"v(\d+\.\d+\.\d+)", ver_string).group(1) - if ver_string is None: - return False, None - return True, ver_string - - -@pytest.fixture(scope="module", autouse=True) -def setup_test(account_id, region_name): - with INIT_LOCK: - available, version = check_terraform_version() - - if not available: - msg = "could not find a compatible version of terraform" - if version: - msg += f" (version = {version})" - else: - msg += " (command not found)" - - return pytest.skip(msg) - - env_vars = { - "AWS_ACCESS_KEY_ID": account_id, - "AWS_SECRET_ACCESS_KEY": account_id, - "AWS_REGION": region_name, - } - - run( - f"cd {get_base_dir()}; {TERRAFORM_BIN} apply -input=false tfplan", - env_vars=env_vars, - ) - - yield - - # clean up - run(f"cd {get_base_dir()}; {TERRAFORM_BIN} destroy -auto-approve", env_vars=env_vars) - - -def get_base_dir(): - return os.path.join(os.path.dirname(__file__), "terraform") - - -# TODO: replace "clouddrove/api-gateway/aws" with normal apigateway module and update terraform -# TODO: rework this setup for multiple (potentially parallel) terraform tests by providing variables (see .auto.tfvars) -# TODO: fetch generated ARNs from terraform instead of static/building ARNs -@pytest.mark.skip(reason="disabled until further notice due to flakiness and lacking quality") -class TestTerraform: - @classmethod - def init_async(cls): - def _run(*args): - with INIT_LOCK: - terraform_package.install() - global TERRAFORM_BIN - TERRAFORM_BIN = terraform_package.get_installer().get_executable_path() - base_dir = get_base_dir() - env_vars = { - "AWS_ACCESS_KEY_ID": TEST_AWS_ACCESS_KEY_ID, - "AWS_SECRET_ACCESS_KEY": TEST_AWS_SECRET_ACCESS_KEY, - "AWS_REGION": TEST_AWS_REGION_NAME, - } - if not os.path.exists(os.path.join(base_dir, ".terraform", "plugins")): - run(f"cd {base_dir}; {TERRAFORM_BIN} init -input=false", env_vars=env_vars) - # remove any cache files from previous runs - for tf_file in [ - "tfplan", - "terraform.tfstate", - "terraform.tfstate.backup", - ]: - rm_rf(os.path.join(base_dir, tf_file)) - # create TF plan - run( - f"cd {base_dir}; {TERRAFORM_BIN} plan -out=tfplan -input=false", - env_vars=env_vars, - ) - - start_worker_thread(_run) - - @markers.skip_offline - @markers.aws.needs_fixing - def test_bucket_exists(self, aws_client): - response = aws_client.s3.head_bucket(Bucket=BUCKET_NAME) - assert response["ResponseMetadata"]["HTTPStatusCode"] == 200 - - cors = { - "AllowedHeaders": ["*"], - "AllowedMethods": ["GET", "PUT", "POST"], - "AllowedOrigins": ["*"], - "ExposeHeaders": ["ETag", "x-amz-version-id"], - "MaxAgeSeconds": 3000, - } - - response = aws_client.s3.get_bucket_cors(Bucket=BUCKET_NAME) - assert response["CORSRules"][0] == cors - - response = aws_client.s3.get_bucket_versioning(Bucket=BUCKET_NAME) - assert response["Status"] == "Enabled" - - @markers.skip_offline - @markers.aws.needs_fixing - def test_sqs(self, aws_client): - queue_url = aws_client.sqs.get_queue_url(QueueName=QUEUE_NAME)["QueueUrl"] - response = aws_client.sqs.get_queue_attributes(QueueUrl=queue_url, AttributeNames=["All"]) - - assert response["Attributes"]["DelaySeconds"] == "90" - assert response["Attributes"]["MaximumMessageSize"] == "2048" - assert response["Attributes"]["MessageRetentionPeriod"] == "86400" - assert response["Attributes"]["ReceiveMessageWaitTimeSeconds"] == "10" - - @markers.skip_offline - @markers.aws.needs_fixing - def test_lambda(self, aws_client, account_id): - response = aws_client.lambda_.get_function(FunctionName=LAMBDA_NAME) - assert response["Configuration"]["FunctionName"] == LAMBDA_NAME - assert response["Configuration"]["Handler"] == LAMBDA_HANDLER - assert response["Configuration"]["Runtime"] == LAMBDA_RUNTIME - assert response["Configuration"]["Role"] == LAMBDA_ROLE.format(account_id=account_id) - - @markers.skip_offline - @markers.aws.needs_fixing - def test_event_source_mapping(self, aws_client, account_id): - queue_arn = QUEUE_ARN.format(account_id=account_id) - lambda_arn = LAMBDA_ARN.format(account_id=account_id, lambda_name=LAMBDA_NAME) - all_mappings = aws_client.lambda_.list_event_source_mappings( - EventSourceArn=queue_arn, FunctionName=LAMBDA_NAME - ) - function_mapping = all_mappings.get("EventSourceMappings")[0] - assert function_mapping["FunctionArn"] == lambda_arn - assert function_mapping["EventSourceArn"] == queue_arn - - @markers.skip_offline - @pytest.mark.skip(reason="flaky") - @markers.aws.needs_fixing - def test_apigateway(self, aws_client): - rest_apis = aws_client.apigateway.get_rest_apis() - - rest_id = None - for rest_api in rest_apis["items"]: - if rest_api["name"] == "test-tf-apigateway": - rest_id = rest_api["id"] - break - - assert rest_id - resources = aws_client.apigateway.get_resources(restApiId=rest_id)["items"] - - # We always have 1 default root resource (with path "/") - assert len(resources) == 3 - - res1 = [r for r in resources if r.get("pathPart") == "mytestresource"] - assert res1 - assert res1[0]["path"] == "/mytestresource" - assert len(res1[0]["resourceMethods"]) == 2 - assert res1[0]["resourceMethods"]["GET"]["methodIntegration"]["type"] == "MOCK" - - res2 = [r for r in resources if r.get("pathPart") == "mytestresource1"] - assert res2 - assert res2[0]["path"] == "/mytestresource1" - assert len(res2[0]["resourceMethods"]) == 2 - assert res2[0]["resourceMethods"]["GET"]["methodIntegration"]["type"] == "AWS_PROXY" - assert res2[0]["resourceMethods"]["GET"]["methodIntegration"]["uri"] - - @markers.skip_offline - @markers.aws.needs_fixing - def test_route53(self, aws_client): - response = aws_client.route53.create_hosted_zone(Name="zone123", CallerReference="ref123") - assert response["ResponseMetadata"]["HTTPStatusCode"] == 201 - change_id = response.get("ChangeInfo", {}).get("Id", "change123") - - response = aws_client.route53.get_change(Id=change_id) - assert response["ResponseMetadata"]["HTTPStatusCode"] == 200 - - @markers.skip_offline - @markers.aws.needs_fixing - def test_acm(self, aws_client): - certs = aws_client.acm.list_certificates()["CertificateSummaryList"] - certs = [c for c in certs if c.get("DomainName") == "example.com"] - assert len(certs) == 1 - - @markers.skip_offline - @pytest.mark.skip(reason="flaky") - @markers.aws.needs_fixing - def test_apigateway_escaped_policy(self, aws_client): - rest_apis = aws_client.apigateway.get_rest_apis() - - service_apis = [] - - for rest_api in rest_apis["items"]: - if rest_api["name"] == "service_api": - service_apis.append(rest_api) - - assert len(service_apis) == 1 - - @markers.skip_offline - @markers.aws.needs_fixing - def test_dynamodb(self, aws_client): - def _table_exists(tablename, dynamotables): - return any(name for name in dynamotables["TableNames"] if name == tablename) - - tables = aws_client.dynamodb.list_tables() - assert _table_exists("tf_dynamotable1", tables) - assert _table_exists("tf_dynamotable2", tables) - assert _table_exists("tf_dynamotable3", tables) - - @markers.skip_offline - @markers.aws.needs_fixing - def test_security_groups(self, aws_client): - rules = aws_client.ec2.describe_security_groups(MaxResults=100)["SecurityGroups"] - matching = [r for r in rules if r["Description"] == "TF SG with ingress / egress rules"] - assert matching diff --git a/tests/unit/aws/handlers/test_analytics.py b/tests/unit/aws/handlers/test_analytics.py index 26e52c02a26dc..aefa0691e319a 100644 --- a/tests/unit/aws/handlers/test_analytics.py +++ b/tests/unit/aws/handlers/test_analytics.py @@ -23,13 +23,13 @@ def test_starts_aggregator_after_first_call(self): counter = ServiceRequestCounter(service_request_aggregator=aggregator) aggregator.start.assert_not_called() - context = create_aws_request_context("s3", "ListBuckets") + context = create_aws_request_context("s3", "ListBuckets", "rest-xml") chain = HandlerChain([counter]) chain.handle(context, Response()) aggregator.start.assert_called_once() - context = create_aws_request_context("s3", "ListBuckets") + context = create_aws_request_context("s3", "ListBuckets", "rest-xml") chain = HandlerChain([counter]) chain.handle(context, Response()) @@ -53,7 +53,7 @@ def test_ignores_requests_when_analytics_is_disabled(self, monkeypatch): chain = HandlerChain([counter]) chain.handle( - create_aws_request_context("s3", "ListBuckets"), + create_aws_request_context("s3", "ListBuckets", "rest-xml"), Response(), ) @@ -66,12 +66,12 @@ def test_calls_aggregator(self): chain = HandlerChain([counter]) chain.handle( - create_aws_request_context("s3", "ListBuckets"), + create_aws_request_context("s3", "ListBuckets", "rest-xml"), Response(), ) counter( chain, - create_aws_request_context("s3", "HeadBucket", {"Bucket": "foobar"}), + create_aws_request_context("s3", "HeadBucket", "rest-xml", {"Bucket": "foobar"}), Response(), ) @@ -88,7 +88,9 @@ def test_parses_error_correctly(self): chain = HandlerChain([counter]) chain.handle( - create_aws_request_context("opensearch", "DescribeDomain", {"DomainName": "foobar"}), + create_aws_request_context( + "opensearch", "DescribeDomain", "rest-json", {"DomainName": "foobar"} + ), Response( b'{"__type": "ResourceNotFoundException", "message": "Domain not found: foobar"}', 404, @@ -111,7 +113,9 @@ def test_invalid_error_behaves_like_botocore(self): chain = HandlerChain([counter]) chain.handle( - create_aws_request_context("opensearch", "DescribeDomain", {"DomainName": "foobar"}), + create_aws_request_context( + "opensearch", "DescribeDomain", "rest-json", {"DomainName": "foobar"} + ), Response(b'{"__type": "ResourceN}', 404), ) diff --git a/tests/unit/aws/handlers/test_response_enrichment.py b/tests/unit/aws/handlers/test_response_enrichment.py index 4f5f3d7e8ab6d..f130d4d3b18bb 100644 --- a/tests/unit/aws/handlers/test_response_enrichment.py +++ b/tests/unit/aws/handlers/test_response_enrichment.py @@ -14,7 +14,7 @@ def response_handler_chain() -> HandlerChain: class TestResponseMetadataEnricher: def test_adds_header_to_successful_response(self, response_handler_chain): - context = create_aws_request_context("s3", "ListBuckets") + context = create_aws_request_context("s3", "ListBuckets", "rest-xml") response = Response("success", 200) response_handler_chain.handle(context, response) @@ -23,7 +23,7 @@ def test_adds_header_to_successful_response(self, response_handler_chain): def test_adds_header_to_error_response(self, response_handler_chain): context = create_aws_request_context( - "opensearch", "DescribeDomain", {"DomainName": "foobar"} + "opensearch", "DescribeDomain", "rest-json", {"DomainName": "foobar"} ) response = Response(b'{"__type": "ResourceNotFoundException"}', 409) diff --git a/tests/unit/aws/handlers/test_service.py b/tests/unit/aws/handlers/test_service.py index 28ef66e4730b6..ee8c9c3127b37 100644 --- a/tests/unit/aws/handlers/test_service.py +++ b/tests/unit/aws/handlers/test_service.py @@ -16,14 +16,16 @@ def service_response_handler_chain() -> HandlerChain: class TestServiceResponseHandler: def test_use_set_response(self, service_response_handler_chain): - context = create_aws_request_context("opensearch", "CreateDomain", {"DomainName": "foobar"}) + context = create_aws_request_context( + "opensearch", "CreateDomain", "rest-json", {"DomainName": "foobar"} + ) context.service_response = {"sure": "why not"} service_response_handler_chain.handle(context, Response(status=200)) assert context.service_response == {"sure": "why not"} def test_parse_response(self, service_response_handler_chain): - context = create_aws_request_context("sqs", "CreateQueue", {"QueueName": "foobar"}) + context = create_aws_request_context("sqs", "CreateQueue", "json", {"QueueName": "foobar"}) backend_response = {"QueueUrl": "http://localhost:4566/000000000000/foobar"} http_response = create_serializer(context.service).serialize_to_response( backend_response, context.operation, context.request.headers, context.request_id @@ -33,7 +35,9 @@ def test_parse_response(self, service_response_handler_chain): assert context.service_response == backend_response def test_parse_response_with_streaming_response(self, service_response_handler_chain): - context = create_aws_request_context("s3", "GetObject", {"Bucket": "foo", "Key": "bar.bin"}) + context = create_aws_request_context( + "s3", "GetObject", "rest-xml", {"Bucket": "foo", "Key": "bar.bin"} + ) backend_response = {"Body": b"\x00\x01foo", "ContentType": "application/octet-stream"} http_response = create_serializer(context.service).serialize_to_response( backend_response, context.operation, context.request.headers, context.request_id @@ -45,7 +49,9 @@ def test_parse_response_with_streaming_response(self, service_response_handler_c assert context.service_response["Body"].read() == b"\x00\x01foo" def test_common_service_exception(self, service_response_handler_chain): - context = create_aws_request_context("opensearch", "CreateDomain", {"DomainName": "foobar"}) + context = create_aws_request_context( + "opensearch", "CreateDomain", "rest-json", {"DomainName": "foobar"} + ) context.service_exception = CommonServiceException( "MyCommonException", "oh noes", status_code=409, sender_fault=True ) @@ -59,7 +65,9 @@ def test_common_service_exception(self, service_response_handler_chain): def test_service_exception(self, service_response_handler_chain): from localstack.aws.api.opensearch import ResourceAlreadyExistsException - context = create_aws_request_context("opensearch", "CreateDomain", {"DomainName": "foobar"}) + context = create_aws_request_context( + "opensearch", "CreateDomain", "rest-json", {"DomainName": "foobar"} + ) context.service_exception = ResourceAlreadyExistsException("oh noes") response = create_serializer(context.service).serialize_error_to_response( @@ -81,6 +89,7 @@ def test_service_exception_with_code_from_spec(self, service_response_handler_ch context = create_aws_request_context( "sqs", "SendMessage", + "json", {"QueueUrl": "http://localhost:4566/000000000000/foobared", "MessageBody": "foo"}, ) context.service_exception = QueueDoesNotExist() @@ -101,7 +110,7 @@ def test_service_exception_with_code_from_spec(self, service_response_handler_ch def test_sets_exception_from_error_response(self, service_response_handler_chain): context = create_aws_request_context( - "opensearch", "DescribeDomain", {"DomainName": "foobar"} + "opensearch", "DescribeDomain", "rest-json", {"DomainName": "foobar"} ) response = Response( b'{"__type": "ResourceNotFoundException", "message": "Domain not found: foobar"}', @@ -126,7 +135,7 @@ def test_nothing_set_does_nothing(self, service_response_handler_chain): def test_invalid_exception_does_nothing(self, service_response_handler_chain): context = create_aws_request_context( - "opensearch", "DescribeDomain", {"DomainName": "foobar"} + "opensearch", "DescribeDomain", "rest-json", {"DomainName": "foobar"} ) context.service_exception = ValueError() service_response_handler_chain.handle(context, Response(status=500)) @@ -145,7 +154,7 @@ class TestServiceExceptionSerializer: ) def test_not_implemented_error(self, message, output): context = create_aws_request_context( - "opensearch", "DescribeDomain", {"DomainName": "foobar"} + "opensearch", "DescribeDomain", "rest-json", {"DomainName": "foobar"} ) not_implemented_exception = NotImplementedError(message) @@ -181,7 +190,7 @@ def capture_original_exception_handler( ) err_context = create_aws_request_context( - "opensearch", "DescribeDomain", {"DomainName": "foobar"} + "opensearch", "DescribeDomain", "rest-json", {"DomainName": "foobar"} ) err_chain.handle(err_context, Response()) diff --git a/tests/unit/aws/protocol/test_parser.py b/tests/unit/aws/protocol/test_parser.py index 332d80b0116f0..78a5297a3bbb4 100644 --- a/tests/unit/aws/protocol/test_parser.py +++ b/tests/unit/aws/protocol/test_parser.py @@ -7,6 +7,7 @@ from botocore.serialize import create_serializer from localstack.aws.protocol.parser import ( + CBORRequestParser, OperationNotFoundParserError, ProtocolParserError, QueryRequestParser, @@ -339,12 +340,19 @@ def test_query_parser_pass_str_as_int_raises_error(): def _botocore_parser_integration_test( - service: str, action: str, headers: dict = None, expected: dict = None, **kwargs + *, + service: str, + action: str, + protocol: str = None, + headers: dict = None, + expected: dict = None, + **kwargs, ): # Load the appropriate service service = load_service(service) + service_protocol = protocol or service.protocol # Use the serializer from botocore to serialize the request params - serializer = create_serializer(service.protocol) + serializer = create_serializer(service_protocol) operation_model = service.operation_model(action) serialized_request = serializer.serialize_to_request(kwargs, operation_model) @@ -365,12 +373,12 @@ def _botocore_parser_integration_test( # use custom headers (if provided), or headers from serialized request as default headers = serialized_request.get("headers") if headers is None else headers - if service.protocol in ["query", "ec2"]: + if service_protocol in ["query", "ec2"]: # Serialize the body as query parameter body = urlencode(serialized_request["body"]) # Use our parser to parse the serialized body - parser = create_parser(service) + parser = create_parser(service, service_protocol) parsed_operation_model, parsed_request = parser.parse( HttpRequest( method=serialized_request.get("method") or "GET", @@ -626,7 +634,10 @@ def test_json_parser_cognito_with_botocore(): ) -def test_json_cbor_blob_parsing(): +# TODO: once Kinesis supports multi protocols (json/cbor), update this test to select the protocol instead when +# creating the parser +@pytest.mark.parametrize("parser_factory", [CBORRequestParser, create_parser]) +def test_json_cbor_blob_parsing(parser_factory): serialized_request = { "url_path": "/", "query_string": "", @@ -655,7 +666,7 @@ def test_json_cbor_blob_parsing(): # Load the appropriate service service = load_service("kinesis") operation_model = service.operation_model("PutRecord") - parser = create_parser(service) + parser = parser_factory(service) parsed_operation_model, parsed_request = parser.parse( HttpRequest( method=serialized_request.get("method") or "GET", @@ -678,7 +689,10 @@ def test_json_cbor_blob_parsing(): assert parsed_request["PartitionKey"] == "partitionkey" -def test_json_cbor_blob_parsing_w_timestamp(snapshot): +# TODO: once Kinesis supports multi protocols (json/cbor), update this test to select the protocol instead when +# creating the parser +@pytest.mark.parametrize("parser_factory", [CBORRequestParser, create_parser]) +def test_json_cbor_blob_parsing_w_timestamp(snapshot, parser_factory): serialized_request = { "url_path": "/", "query_string": "", @@ -707,7 +721,7 @@ def test_json_cbor_blob_parsing_w_timestamp(snapshot): # Load the appropriate service service = load_service("kinesis") operation_model = service.operation_model("SubscribeToShard") - parser = create_parser(service) + parser = parser_factory(service) parsed_operation_model, parsed_request = parser.parse( HttpRequest( method=serialized_request.get("method"), @@ -721,6 +735,7 @@ def test_json_cbor_blob_parsing_w_timestamp(snapshot): # Check if the determined operation_model is correct assert parsed_operation_model == operation_model + assert isinstance(parsed_request["StartingPosition"]["Timestamp"], datetime) snapshot.match("parsed_request", parsed_request) @@ -730,7 +745,7 @@ def test_restjson_parser_xray_with_botocore(): action="PutTelemetryRecords", TelemetryRecords=[ { - "Timestamp": datetime(2015, 1, 1), + "Timestamp": datetime(2015, 1, 1, tzinfo=UTC), "SegmentsReceivedCount": 123, "SegmentsSentCount": 123, "SegmentsSpilloverCount": 123, @@ -838,7 +853,7 @@ def test_restjson_opensearch_with_botocore(): "RollbackOnDisable": "DEFAULT_ROLLBACK", "MaintenanceSchedules": [ { - "StartAt": datetime(2015, 1, 1), + "StartAt": datetime(2015, 1, 1, tzinfo=UTC), "Duration": {"Value": 123, "Unit": "HOURS"}, "CronExpressionForRecurrence": "string", }, @@ -1432,3 +1447,111 @@ def test_restxml_ignores_get_body(): assert "Bucket" in parsed_request assert parsed_request["Bucket"] == "test-bucket" assert parsed_request["Key"] == "foo" + + +def test_smithy_rpc_v2_cbor(): + # we are using a service that LocalStack does not implement yet because it implements `smithy-rpc-v2-cbor` + # we can replace this service by CloudWatch once it has support in Botocore + # TODO: test timestamp parsing + # example taken from: + # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/arc-region-switch/client/create_plan.html + + _botocore_parser_integration_test( + service="arc-region-switch", + protocol="smithy-rpc-v2-cbor", + action="CreatePlan", + description="string", + workflows=[ + { + "steps": [ + { + "name": "string", + "description": "string", + "executionBlockConfiguration": { + "customActionLambdaConfig": { + "timeoutMinutes": 123, + "lambdas": [ + { + "crossAccountRole": "string", + "externalId": "string", + "arn": "string", + }, + ], + "retryIntervalMinutes": 10.0, + "regionToRun": "activatingRegion", + "ungraceful": {"behavior": "skip"}, + }, + }, + "executionBlockType": "CustomActionLambda", + }, + ], + "workflowTargetAction": "activate", + "workflowTargetRegion": "string", + "workflowDescription": "string", + }, + ], + executionRole="string", + recoveryTimeObjectiveMinutes=123, + associatedAlarms={ + "string": { + "crossAccountRole": "string", + "externalId": "string", + "resourceIdentifier": "string", + "alarmType": "applicationHealth", + } + }, + triggers=[ + { + "description": "string", + "targetRegion": "string", + "action": "activate", + "conditions": [ + { + "associatedAlarmName": "string", + "condition": "red", + }, + ], + "minDelayMinutesBetweenExecutions": 123, + }, + ], + name="string", + regions=[ + "region1", + "region2", + ], + recoveryApproach="activeActive", + primaryRegion="string", + tags={"string": "string"}, + ) + + +@pytest.mark.parametrize("protocol", ("json", "smithy-rpc-v2-cbor")) +def test_protocol_selection(protocol): + # we are using a service that LocalStack does not implement yet because it implements `smithy-rpc-v2-cbor` + # we can replace this service by CloudWatch once it has support in Botocore + + _botocore_parser_integration_test( + service="arc-region-switch", + protocol=protocol, + action="TagResource", + arn="string", + tags={"string": "string"}, + ) + + +def test_rpcv2_operation_detection_with_prefix(): + """ + Every request for the rpcv2Cbor protocol MUST be sent to a URL with the following form: + {prefix?}/service/{serviceName}/operation/{operationName} + The Smithy RPCv2 CBOR protocol will only use the last four segments of the URL when routing requests. + For example, a service could use a v1 prefix in the URL path, which would not affect the operation a request + is routed to: `v1/service/FooService/operation/BarOperation` + """ + request = HttpRequest( + method="POST", + path="/v1/service/ArcRegionSwitch/operation/TagResource", + body=b"\xa2carnfstringdtags\xa1fstringfstring", + ) + parser = create_parser(load_service("arc-region-switch")) + parsed_operation_model, parsed_request = parser.parse(request) + assert parsed_operation_model.name == "TagResource" diff --git a/tests/unit/aws/protocol/test_parser.snapshot.json b/tests/unit/aws/protocol/test_parser.snapshot.json index bf3fab6bc744b..404ffca79e03f 100644 --- a/tests/unit/aws/protocol/test_parser.snapshot.json +++ b/tests/unit/aws/protocol/test_parser.snapshot.json @@ -1,12 +1,25 @@ { - "tests/unit/aws/protocol/test_parser.py::test_json_cbor_blob_parsing_w_timestamp": { - "recorded-date": "21-06-2024, 13:58:29", + "tests/unit/aws/protocol/test_parser.py::test_json_cbor_blob_parsing_w_timestamp[CBORRequestParser]": { + "recorded-date": "17-09-2025, 21:46:34", "recorded-content": { "parsed_request": { "ConsumerARN": "", "ShardId": "", "StartingPosition": { - "Timestamp": "2024-06-21 08:54:08.123000", + "Timestamp": "2024-06-21 08:54:08.123000+00:00", + "Type": "AT_TIMESTAMP" + } + } + } + }, + "tests/unit/aws/protocol/test_parser.py::test_json_cbor_blob_parsing_w_timestamp[create_parser]": { + "recorded-date": "17-09-2025, 21:46:34", + "recorded-content": { + "parsed_request": { + "ConsumerARN": "", + "ShardId": "", + "StartingPosition": { + "Timestamp": "2024-06-21 08:54:08.123000+00:00", "Type": "AT_TIMESTAMP" } } diff --git a/tests/unit/aws/protocol/test_parser.validation.json b/tests/unit/aws/protocol/test_parser.validation.json deleted file mode 100644 index d667ef29c28c0..0000000000000 --- a/tests/unit/aws/protocol/test_parser.validation.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "tests/unit/aws/protocol/test_parser.py::test_json_cbor_blob_parsing_w_timestamp": { - "last_validated_date": "2024-06-21T13:58:29+00:00" - } -} diff --git a/tests/unit/aws/protocol/test_serializer.py b/tests/unit/aws/protocol/test_serializer.py index 79a47f43c2ad1..5deb13d118c3b 100644 --- a/tests/unit/aws/protocol/test_serializer.py +++ b/tests/unit/aws/protocol/test_serializer.py @@ -38,6 +38,7 @@ ) from localstack.aws.api.sts import Credentials, GetSessionTokenResponse from localstack.aws.protocol.serializer import ( + CBORResponseSerializer, ProtocolSerializerError, QueryResponseSerializer, UnknownSerializerError, @@ -45,7 +46,7 @@ create_serializer, ) from localstack.aws.spec import load_service -from localstack.constants import APPLICATION_AMZ_CBOR_1_1 +from localstack.constants import APPLICATION_AMZ_CBOR_1_1, APPLICATION_CBOR from localstack.http import Request, Response from localstack.utils.common import to_str from localstack.utils.strings import long_uid @@ -54,17 +55,19 @@ def _botocore_serializer_integration_test( + *, service: str, action: str, response: dict, - status_code=200, - expected_response_content: dict = None, + status_code: int = 200, + expected_response_content: dict | None = None, + protocol: str | None = None, ) -> dict: """ Performs an integration test for the serializer using botocore as parser. It executes the following steps: - Load the given service (f.e. "sqs") - - Serialize the response with the appropriate serializer from the AWS Serivce Framework + - Serialize the response with the appropriate serializer from the AWS Service Framework - Parse the serialized response using the botocore parser - Checks if the metadata is correct (status code, requestID,...) - Checks if the parsed response content is equal to the input to the serializer @@ -75,14 +78,17 @@ def _botocore_serializer_integration_test( :param status_code: Optional - expected status code of the response - defaults to 200 :param expected_response_content: Optional - if the input data ("response") differs from the actually expected data (because f.e. it contains None values) + :param: protocol: Optional: to specify which protocol to use for the service. If not provided, + fallback to the service's default protocol :return: boto-parsed serialized response """ # Load the appropriate service service = load_service(service) + service_protocol = protocol or service.protocol # Use our serializer to serialize the response - response_serializer = create_serializer(service) + response_serializer = create_serializer(service, protocol=service_protocol) # The serializer changes the incoming dict, therefore copy it before passing it to the serializer response_to_parse = copy.deepcopy(response) serialized_response = response_serializer.serialize_to_response( @@ -90,7 +96,7 @@ def _botocore_serializer_integration_test( ) # Use the parser from botocore to parse the serialized response - response_parser = create_parser(service.protocol) + response_parser = create_parser(service_protocol) parsed_response = response_parser.parse( serialized_response.to_readonly_response_dict(), service.operation_model(action).output_shape, @@ -122,7 +128,8 @@ def _botocore_error_serializer_integration_test( status_code: int, message: str | None, is_sender_fault: bool = False, - **additional_error_fields: dict[str, Any], + protocol: str | None = None, + **additional_error_fields: Any, ) -> dict: """ Performs an integration test for the error serialization using botocore as parser. @@ -147,11 +154,12 @@ def _botocore_error_serializer_integration_test( # Load the appropriate service service = load_service(service_model_name) + service_protocol = protocol or service.protocol # Use our serializer to serialize the response - response_serializer = create_serializer(service) + response_serializer = create_serializer(service, service_protocol) serialized_response = response_serializer.serialize_error_to_response( - exception, service.operation_model(action), None, long_uid() + exception, service.operation_model(action), {}, long_uid() ) # Use the parser from botocore to parse the serialized response @@ -161,7 +169,7 @@ def _botocore_error_serializer_integration_test( # f.e. needed for x-amzn-errortype response_dict["headers"] = HeadersDict(response_dict["headers"]) - response_parser: ResponseParser = create_parser(service.protocol) + response_parser: ResponseParser = create_parser(service_protocol) parsed_response = response_parser.parse( response_dict, service.operation_model(action).output_shape, @@ -186,6 +194,8 @@ def _botocore_error_serializer_integration_test( type = parsed_response["Error"].get("Type") if is_sender_fault: assert type == "Sender" + elif service_protocol == "smithy-rpc-v2-cbor" and service.is_query_compatible: + assert type == "Receiver" else: assert type is None if additional_error_fields: @@ -248,6 +258,10 @@ def _botocore_event_streaming_test( assert actual_events == expected_events +def _cbor_serializer_factory(service): + return CBORResponseSerializer() + + def test_rest_xml_serializer_cloudfront_with_botocore(): parameters = { "TestResult": { @@ -272,7 +286,9 @@ def test_rest_xml_serializer_cloudfront_with_botocore(): "FunctionOutput": "string", } } - _botocore_serializer_integration_test("cloudfront", "TestFunction", parameters) + _botocore_serializer_integration_test( + service="cloudfront", action="TestFunction", response=parameters + ) def test_rest_xml_serializer_route53_with_botocore(): @@ -285,7 +301,9 @@ def test_rest_xml_serializer_route53_with_botocore(): }, "DelegationSet": {"NameServers": ["dns.localhost.localstack.cloud"]}, } - _botocore_serializer_integration_test("route53", "CreateHostedZone", parameters, 201) + _botocore_serializer_integration_test( + service="route53", action="CreateHostedZone", response=parameters, status_code=201 + ) def test_rest_xml_serializer_s3_with_botocore(): @@ -317,7 +335,9 @@ def test_rest_xml_serializer_s3_with_botocore(): }, } } - _botocore_serializer_integration_test("s3", "GetBucketAnalyticsConfiguration", parameters) + _botocore_serializer_integration_test( + service="s3", action="GetBucketAnalyticsConfiguration", response=parameters + ) def test_rest_xml_serializer_s3_2_with_botocore(): @@ -357,7 +377,7 @@ def test_rest_xml_serializer_s3_2_with_botocore(): "ObjectLockLegalHoldStatus": "ON", "StatusCode": 200, } - _botocore_serializer_integration_test("s3", "GetObject", parameters) + _botocore_serializer_integration_test(service="s3", action="GetObject", response=parameters) def test_query_serializer_cloudformation_with_botocore(): @@ -387,7 +407,9 @@ def test_query_serializer_cloudformation_with_botocore(): "Timestamp": datetime(2015, 1, 1, 23, 59, 59, 6000, tzinfo=tzutc()), } } - _botocore_serializer_integration_test("cloudformation", "DetectStackResourceDrift", parameters) + _botocore_serializer_integration_test( + service="cloudformation", action="DetectStackResourceDrift", response=parameters + ) def test_query_serializer_redshift_with_botocore(): @@ -412,11 +434,13 @@ def test_query_serializer_redshift_with_botocore(): }, ], } - _botocore_serializer_integration_test("redshift", "DescribeClusterDbRevisions", parameters) + _botocore_serializer_integration_test( + service="redshift", action="DescribeClusterDbRevisions", response=parameters + ) def test_query_serializer_sqs_empty_return_shape_with_botocore(): - _botocore_serializer_integration_test("sqs", "SetQueueAttributes", {}) + _botocore_serializer_integration_test(service="sqs", action="SetQueueAttributes", response={}) def test_query_serializer_sqs_flattened_list_with_botocore(): @@ -426,7 +450,7 @@ def test_query_serializer_sqs_flattened_list_with_botocore(): "http://localhost:4566/000000000000/myqueue2", ] } - _botocore_serializer_integration_test("sqs", "ListQueues", response) + _botocore_serializer_integration_test(service="sqs", action="ListQueues", response=response) def test_query_serializer_sqs_flattened_map_with_botocore(): @@ -436,7 +460,9 @@ def test_query_serializer_sqs_flattened_map_with_botocore(): "DelaySeconds": "0", } } - _botocore_serializer_integration_test("sqs", "GetQueueAttributes", response) + _botocore_serializer_integration_test( + service="sqs", action="GetQueueAttributes", response=response + ) def test_query_serializer_sqs_flattened_list_map_with_botocore(): @@ -456,7 +482,7 @@ def test_query_serializer_sqs_flattened_list_map_with_botocore(): } ] } - _botocore_serializer_integration_test("sqs", "ReceiveMessage", response) + _botocore_serializer_integration_test(service="sqs", action="ReceiveMessage", response=response) def test_query_serializer_sqs_none_value_in_map(): @@ -473,7 +499,13 @@ def test_query_serializer_sqs_none_value_in_map(): } expected_response = copy.deepcopy(response) del expected_response["Messages"][0]["Attributes"] - _botocore_serializer_integration_test("sqs", "ReceiveMessage", response, 200, expected_response) + _botocore_serializer_integration_test( + service="sqs", + action="ReceiveMessage", + response=response, + status_code=200, + expected_response_content=expected_response, + ) def test_query_protocol_error_serialization(): @@ -732,6 +764,96 @@ def test_json_protocol_error_serialization_with_shaped_default_members_on_root() assert "message" not in parsed_body +def test_json_protocol_error_serialization_empty_message(): + # if the exception message is not passed when instantiating the exception, the message attribute will be set to + # an empty string "". This is not serialized if the message field is not a required member + exception = TransactionCanceledException() + + response = _botocore_error_serializer_integration_test( + "dynamodb", + "ExecuteTransaction", + exception, + "TransactionCanceledException", + 400, + "", + ) + assert "message" not in response + + +@pytest.mark.parametrize("empty_value", ("", None)) +def test_json_protocol_error_serialization_with_empty_non_required_members(empty_value): + class _ResourceNotFoundException(ServiceException): + code: str = "ResourceNotFoundException" + sender_fault: bool = True + status_code: int = 404 + ResourceType: str | None + ResourceId: str | None + + exception = _ResourceNotFoundException("Not Found", ResourceType="") + + response = _botocore_error_serializer_integration_test( + "arc-region-switch", + "ApprovePlanExecutionStep", + exception, + "ResourceNotFoundException", + 404, + "Not Found", + protocol="json", + ) + assert "ResourceType" not in response + + +def test_json_protocol_error_serialization_falsy_non_required_members(): + # if the exception message is not passed, an empty message will be passed as an empty string `""` + exception = TransactionCanceledException("Exception message!", CancellationReasons=[]) + + response = _botocore_error_serializer_integration_test( + "dynamodb", + "ExecuteTransaction", + exception, + "TransactionCanceledException", + 400, + "Exception message!", + Message="Exception message!", + ) + assert "CancellationReasons" not in response + + +@pytest.mark.parametrize("empty_value", ("", None)) +def test_json_protocol_error_serialization_with_empty_required_members(empty_value): + class _ResourceNotFoundException(ServiceException): + code: str = "ResourceNotFoundException" + sender_fault: bool = False + status_code: int = 404 + resourceId: str + resourceType: str + + resource_type = "test" + exception = _ResourceNotFoundException( + "Exception message!", + resourceType=resource_type, + resourceId=empty_value, + ) + expected_exception_values = { + "resourceType": resource_type, + } + # if the value is None, it is not serialized, even if required + # but if it is an empty string, it will be + if empty_value is not None: + expected_exception_values["resourceId"] = "" + + response = _botocore_error_serializer_integration_test( + "verifiedpermissions", + "IsAuthorizedWithToken", + exception, + "ResourceNotFoundException", + 404, + "Exception message!", + **expected_exception_values, + ) + assert "" not in response + + def test_rest_json_protocol_error_serialization_with_additional_members(): class NotFoundException(ServiceException): code: str = "NotFoundException" @@ -843,6 +965,102 @@ class InvalidObjectState(ServiceException): ) +def test_rpc_v2_cbor_protocol_error_serialization(): + class _ResourceNotFoundException(ServiceException): + code: str = "ResourceNotFoundException" + sender_fault: bool = True + status_code: int = 404 + + exception = _ResourceNotFoundException("Not Found!") + + _botocore_error_serializer_integration_test( + "arc-region-switch", + "ListPlans", + exception, + "ResourceNotFoundException", + 404, + "Not Found!", + ) + + +def test_rpc_v2_cbor_protocol_custom_error_serialization(): + # CBOR needs a shape for the error, and we have to implement a custom way to serialize user defined exception + exception = CommonServiceException( + "UserDefinedException", "Parameter x was invalid!", sender_fault=True + ) + _botocore_error_serializer_integration_test( + "cloudwatch", + "SetAlarmState", + exception, + "UserDefinedException", + 400, + "Parameter x was invalid!", + protocol="smithy-rpc-v2-cbor", + is_sender_fault=True, + ) + + +def test_rpc_v2_cbor_protocol_error_serialization_default_headers(): + class _ResourceNotFoundException(ServiceException): + code: str = "ResourceNotFoundException" + sender_fault: bool = True + status_code: int = 404 + + exception = _ResourceNotFoundException("Not Found!") + service = load_service("arc-region-switch") + response_serializer = create_serializer(service) + serialized_response = response_serializer.serialize_error_to_response( + exception, + service.operation_model("ListPlans"), + {}, + long_uid(), + ) + assert serialized_response.headers["Content-Type"] == APPLICATION_CBOR + assert serialized_response.headers["Smithy-Protocol"] == "rpc-v2-cbor" + + +@pytest.mark.parametrize("empty_value", ("", None)) +def test_rpc_v2_cbor_protocol_error_serialization_with_empty_required_members(empty_value): + class AccessDeniedException(ServiceException): + code: str = "AccessDeniedException" + sender_fault: bool = True + status_code: int = 403 + + exception = AccessDeniedException("") + + _botocore_error_serializer_integration_test( + "arc-region-switch", + "ApprovePlanExecutionStep", + exception, + "AccessDeniedException", + 403, + "", + ) + + +@pytest.mark.parametrize("empty_value", ("", None)) +def test_rpc_v2_cbor_protocol_error_serialization_with_empty_non_required_members(empty_value): + class _ResourceNotFoundException(ServiceException): + code: str = "ResourceNotFoundException" + sender_fault: bool = True + status_code: int = 404 + ResourceType: str | None + ResourceId: str | None + + exception = _ResourceNotFoundException("Not Found", ResourceType="") + + response = _botocore_error_serializer_integration_test( + "arc-region-switch", + "ApprovePlanExecutionStep", + exception, + "ResourceNotFoundException", + 404, + "Not Found", + protocol="smithy-rpc-v2-cbor", + ) + assert "ResourceType" not in response + + def test_json_protocol_content_type_1_0(): """AppRunner defines the jsonVersion 1.0, therefore the Content-Type needs to be application/x-amz-json-1.0.""" service = load_service("apprunner") @@ -970,7 +1188,9 @@ def test_json_serializer_cognito_with_botocore(): }, } } - _botocore_serializer_integration_test("cognito-idp", "DescribeUserPool", parameters) + _botocore_serializer_integration_test( + service="cognito-idp", action="DescribeUserPool", response=parameters + ) def test_json_serializer_date_serialization_with_botocore(): @@ -979,7 +1199,9 @@ def test_json_serializer_date_serialization_with_botocore(): "LastModifiedDate": datetime(2022, 2, 8, 9, 17, 40, 122939, tzinfo=tzlocal()), } } - _botocore_serializer_integration_test("cognito-idp", "DescribeUserPool", parameters) + _botocore_serializer_integration_test( + service="cognito-idp", action="DescribeUserPool", response=parameters + ) def test_restjson_protocol_error_serialization(): @@ -1052,7 +1274,9 @@ def test_restjson_serializer_xray_with_botocore(): } } - _botocore_serializer_integration_test("xray", "UpdateSamplingRule", parameters) + _botocore_serializer_integration_test( + service="xray", action="UpdateSamplingRule", response=parameters + ) def test_restjson_header_target_serialization(): @@ -1088,9 +1312,9 @@ def test_restjson_header_target_serialization(): } result = _botocore_serializer_integration_test( - "glacier", - "InitiateJob", - response, + service="glacier", + action="InitiateJob", + response=response, status_code=202, ) @@ -1119,7 +1343,10 @@ def test_restjson_headers_target_serialization(): # skipping assert here, because the response will contain all HTTP headers (given the nature of "ResponseHeaders" # attribute). result = _botocore_serializer_integration_test( - "dataexchange", "SendApiAsset", response, expected_response_content=_skip_assert + service="dataexchange", + action="SendApiAsset", + response=response, + expected_response_content=_skip_assert, ) assert result["Body"] == "hello" @@ -1135,9 +1362,9 @@ def test_restjson_headers_target_serialization(): def test_restjson_statuscode_target_serialization(): _botocore_serializer_integration_test( - "lambda", - "Invoke", - { + service="lambda", + action="Invoke", + response={ "StatusCode": 203, "LogResult": "Log Message!", "ExecutedVersion": "Latest", @@ -1179,9 +1406,9 @@ def test_restjson_payload_serialization(): } result = _botocore_serializer_integration_test( - "appconfig", - "GetConfiguration", - response, + service="appconfig", + action="GetConfiguration", + response=response, status_code=200, ) headers = result["ResponseMetadata"]["HTTPHeaders"] @@ -1203,7 +1430,11 @@ def test_restjson_none_serialization(): "DeadLetterConfig": {}, } _botocore_serializer_integration_test( - "lambda", "CreateFunction", parameters, status_code=201, expected_response_content=expected + service="lambda", + action="CreateFunction", + response=parameters, + status_code=201, + expected_response_content=expected, ) exception = CommonServiceException("CodeVerificationFailedException", None) _botocore_error_serializer_integration_test( @@ -1219,18 +1450,27 @@ def test_restjson_none_serialization(): def test_restxml_none_serialization(): # Structure = None _botocore_serializer_integration_test( - "route53", "ListHostedZonesByName", {}, expected_response_content={} + service="route53", + action="ListHostedZonesByName", + response={}, + expected_response_content={}, ) # Structure Value = None parameters = {"HostedZones": None} _botocore_serializer_integration_test( - "route53", "ListHostedZonesByName", parameters, expected_response_content={} + service="route53", + action="ListHostedZonesByName", + response=parameters, + expected_response_content={}, ) # List Value = None parameters = {"HostedZones": [None]} expected = {"HostedZones": []} _botocore_serializer_integration_test( - "route53", "ListHostedZonesByName", parameters, expected_response_content=expected + service="route53", + action="ListHostedZonesByName", + response=parameters, + expected_response_content=expected, ) # Exception without a message exception = CommonServiceException("NoSuchKeySigningKey", None) @@ -1251,7 +1491,9 @@ def test_restjson_int_header_serialization(): "NextPollConfigurationToken": "abcdefg", "NextPollIntervalInSeconds": 42, } - _botocore_serializer_integration_test("appconfigdata", "GetLatestConfiguration", response) + _botocore_serializer_integration_test( + service="appconfigdata", action="GetLatestConfiguration", response=response + ) def test_ec2_serializer_ec2_with_botocore(): @@ -1286,11 +1528,13 @@ def test_ec2_serializer_ec2_with_botocore(): } } - _botocore_serializer_integration_test("ec2", "CreateInstanceEventWindow", parameters) + _botocore_serializer_integration_test( + service="ec2", action="CreateInstanceEventWindow", response=parameters + ) def test_ec2_serializer_ec2_with_empty_response(): - _botocore_serializer_integration_test("ec2", "CreateTags", {}) + _botocore_serializer_integration_test(service="ec2", action="CreateTags", response={}) def test_ec2_protocol_custom_error_serialization(): @@ -1339,15 +1583,17 @@ def test_restxml_s3_errors_have_error_root_element(): def test_restxml_without_output_shape(): - _botocore_serializer_integration_test("cloudfront", "DeleteDistribution", {}, status_code=204) + _botocore_serializer_integration_test( + service="cloudfront", action="DeleteDistribution", response={}, status_code=204 + ) def test_restxml_header_location(): """Tests fields with the location trait "header" for rest-xml.""" _botocore_serializer_integration_test( - "cloudfront", - "CreateCloudFrontOriginAccessIdentity", - { + service="cloudfront", + action="CreateCloudFrontOriginAccessIdentity", + response={ "Location": "location-header-field", "ETag": "location-etag-field", "CloudFrontOriginAccessIdentity": {}, @@ -1363,15 +1609,15 @@ def test_restxml_header_location(): "Metadata": {"string": "string"}, "StatusCode": 200, } - _botocore_serializer_integration_test("s3", "GetObject", parameters) + _botocore_serializer_integration_test(service="s3", action="GetObject", response=parameters) def test_restxml_headers_location(): """Tests fields with the location trait "headers" for rest-xml.""" _botocore_serializer_integration_test( - "s3", - "HeadObject", - { + service="s3", + action="HeadObject", + response={ "DeleteMarker": False, "Metadata": {"headers_key1": "headers_value1", "headers_key2": "headers_value2"}, "ContentType": "application/octet-stream", @@ -1385,16 +1631,18 @@ def test_restxml_headers_location(): def test_restjson_header_location(): """Tests fields with the location trait "header" for rest-xml.""" _botocore_serializer_integration_test( - "ebs", "GetSnapshotBlock", {"BlockData": "binary-data", "DataLength": 15} + service="ebs", + action="GetSnapshotBlock", + response={"BlockData": "binary-data", "DataLength": 15}, ) def test_restjson_headers_location(): """Tests fields with the location trait "headers" for rest-json.""" response = _botocore_serializer_integration_test( - "dataexchange", - "SendApiAsset", - { + service="dataexchange", + action="SendApiAsset", + response={ "ResponseHeaders": {"headers_key1": "headers_value1", "headers_key2": "headers_value2"}, }, expected_response_content=_skip_assert, @@ -1409,6 +1657,125 @@ def test_restjson_headers_location(): assert "headers_value2" == response["ResponseHeaders"]["headers_key2"] +def test_rpc_v2_cbor_serializer_arc_region_switch_with_botocore(): + # we are using a service that LocalStack does not implement yet because it implements `smithy-rpc-v2-cbor` + # we can replace this service by CloudWatch once it has support in Botocore + # example taken from: + # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/arc-region-switch/client/list_plans.html + parameters = { + "plans": [ + { + "arn": "string", + "owner": "string", + "name": "string", + "regions": [ + "string", + ], + "recoveryApproach": "activeActive", + "primaryRegion": "string", + "version": "string", + # validated with AWS, it returns millisecond precision + "updatedAt": datetime(2015, 1, 1, 23, 59, 59, microsecond=262000, tzinfo=tzlocal()), + "description": "string", + "executionRole": "string", + "activePlanExecution": "string", + "recoveryTimeObjectiveMinutes": 123, + }, + ], + "nextToken": "string", + } + + _botocore_serializer_integration_test( + service="arc-region-switch", + action="ListPlans", + response=parameters, + protocol="smithy-rpc-v2-cbor", + ) + + +def test_rpc_v2_undefined_map_and_list_serialization(): + # they are two types of map/list in CBOR, indefinite length types and "defined" ones: + # You'd use \xbf to indicate a map with indefinite length, then \xff to indicate the end of the map. + # You can also use \xa4 to indicate a map with exactly 4 things in it, so \xff is not required at the end. + # The Smithy specs are asking to follow the CBOR specs and use defined length structures, but AWS is returning + # undefined one with `\xbf` and `\xff` + service = load_service("arc-region-switch") + response_serializer = create_serializer(service, protocol="smithy-rpc-v2-cbor") + response_data = { + "plans": [ + { + "arn": "arn:aws:arc-region-switch::671107678412:plan/TestPlan:a1b61f", + "owner": "671107678412", + "name": "TestPlan", + "regions": ["us-east-1", "us-east-2"], + "recoveryApproach": "activePassive", + "primaryRegion": "us-east-1", + "version": "1", + "executionRole": "arn:aws:iam::671107678412:role/testswitch", + } + ] + } + + result: Response = response_serializer.serialize_to_response( + response_data, service.operation_model("ListPlans"), {}, long_uid() + ) + assert result is not None + assert result.content_type == "application/cbor" + raw_response_body = result.data + # this has been validated with AWS, AWS uses undefined length CBOR map and list + assert raw_response_body[:10] == b"\xbfeplans\x9f\xbfc" + assert raw_response_body[-3:] == b"\xff\xff\xff" + + +def test_rpc_v2_timestamp_serialization(): + service = load_service("arc-region-switch") + response_serializer = create_serializer(service, protocol="smithy-rpc-v2-cbor") + response_data = { + "plans": [ + { + "arn": "arn:aws:arc-region-switch::671107678412:plan/TestPlan:a1b61f", + "owner": "671107678412", + "name": "TestPlan", + "regions": ["us-east-1", "us-east-2"], + "recoveryApproach": "activePassive", + "primaryRegion": "us-east-1", + "version": "1", + "updatedAt": datetime(2025, 9, 16, 14, 54, 5, 262000, tzinfo=tzutc()), + "executionRole": "arn:aws:iam::671107678412:role/testswitch", + } + ] + } + + result: Response = response_serializer.serialize_to_response( + response_data, service.operation_model("ListPlans"), {}, long_uid() + ) + raw_response_body = result.data + # we also validate its timestamp serialization (with double) + timestamp_key_index = raw_response_body.find(b"updatedAt") + timestamp_value_index = timestamp_key_index + len(b"updatedAt") + # this has been validated with AWS as well, it encodes the timestamp as a double of length 8 + assert ( + raw_response_body[timestamp_value_index : timestamp_value_index + 8] + == b"\xc1\xfbA\xda2^\x83P" + ) + + +@pytest.mark.parametrize("protocol", ("json", "smithy-rpc-v2-cbor")) +def test_protocol_selection(protocol): + # we are using a service that LocalStack does not implement yet because it implements `smithy-rpc-v2-cbor` + # we can replace this service by CloudWatch once it has support in Botocore + parameters = { + "resourceTags": {"string": "string"}, + } + + _botocore_serializer_integration_test( + service="arc-region-switch", + protocol=protocol, + action="ListTagsForResource", + response=parameters, + ) + + def _iterable_to_stream(iterable, buffer_size=io.DEFAULT_BUFFER_SIZE): """ Concerts a given iterable (generator) to a stream for testing purposes. @@ -1502,9 +1869,9 @@ def test_all_non_existing_key(): """Tests the different protocols to allow non-existing keys in structures / dicts.""" # query _botocore_serializer_integration_test( - "cloudformation", - "DetectStackResourceDrift", - { + service="cloudformation", + action="DetectStackResourceDrift", + response={ "StackResourceDrift": { "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/MyStack/d0a825a0-e4cd-xmpl-b9fb-061c69e99204", "unknown": {"foo": "bar"}, @@ -1518,9 +1885,9 @@ def test_all_non_existing_key(): ) # ec2 _botocore_serializer_integration_test( - "ec2", - "CreateInstanceEventWindow", - { + service="ec2", + action="CreateInstanceEventWindow", + response={ "InstanceEventWindow": { "InstanceEventWindowId": "string", "unknown": {"foo": "bar"}, @@ -1535,9 +1902,9 @@ def test_all_non_existing_key(): ) # json _botocore_serializer_integration_test( - "cognito-idp", - "DescribeUserPool", - { + service="cognito-idp", + action="DescribeUserPool", + response={ "UserPool": { "Id": "string", "Unknown": "Ignored", @@ -1551,9 +1918,9 @@ def test_all_non_existing_key(): ) # rest-json _botocore_serializer_integration_test( - "xray", - "UpdateSamplingRule", - { + service="xray", + action="UpdateSamplingRule", + response={ "SamplingRuleRecord": { "SamplingRule": { "ResourceARN": "123456789001234567890", @@ -1571,9 +1938,9 @@ def test_all_non_existing_key(): ) # rest-xml _botocore_serializer_integration_test( - "cloudfront", - "TestFunction", - { + service="cloudfront", + action="TestFunction", + response={ "TestResult": { "FunctionErrorMessage": "string", }, @@ -1721,7 +2088,7 @@ def test_restxml_streaming_payload(payload): "Metadata": {}, "StatusCode": 200, } - _botocore_serializer_integration_test("s3", "GetObject", parameters) + _botocore_serializer_integration_test(service="s3", action="GetObject", response=parameters) @pytest.mark.parametrize( @@ -1737,9 +2104,9 @@ def test_restxml_streaming_payload(payload): def test_restjson_streaming_payload(payload): """See docs for ``test_restxml_streaming_payload``.""" _botocore_serializer_integration_test( - "lambda", - "Invoke", - { + service="lambda", + action="Invoke", + response={ "StatusCode": 200, "Payload": payload, }, @@ -1844,9 +2211,11 @@ def test_query_protocol_json_serialization(headers_dict): "headers_dict", [{"Content-Type": "application/cbor"}, {"Accept": "application/cbor"}], ) -def test_json_protocol_cbor_serialization(headers_dict): +@pytest.mark.parametrize("serializer_factory", [create_serializer, _cbor_serializer_factory]) +def test_json_protocol_cbor_serialization(headers_dict, serializer_factory): + # TODO: test datetime serialization format for Kinesis manually service = load_service("kinesis") - response_serializer = create_serializer(service) + response_serializer = serializer_factory(service) headers = Headers(headers_dict) response_data = GetRecordsOutput( Records=[ diff --git a/tests/unit/aws/test_mocking.py b/tests/unit/aws/test_mocking.py index 87db62b2967c6..4fa97cbdc717c 100644 --- a/tests/unit/aws/test_mocking.py +++ b/tests/unit/aws/test_mocking.py @@ -55,7 +55,7 @@ def test_get_mocking_skeleton(): skeleton = get_mocking_skeleton("sqs") request = {"QueueName": "my-queue-name"} - context = create_aws_request_context("sqs", "CreateQueue", request) + context = create_aws_request_context("sqs", "CreateQueue", "json", request) response = skeleton.invoke(context) # just a smoke test assert b"QueueUrl" in response.data diff --git a/tests/unit/aws/test_service_router.py b/tests/unit/aws/test_service_router.py index 8a7c6f8722892..2b82a579b18bf 100644 --- a/tests/unit/aws/test_service_router.py +++ b/tests/unit/aws/test_service_router.py @@ -6,13 +6,40 @@ from botocore.awsrequest import AWSRequest, create_request_object from botocore.config import Config from botocore.model import OperationModel, ServiceModel, Shape, StructureShape +from botocore.serialize import create_serializer -from localstack.aws.protocol.service_router import determine_aws_service_model +from localstack.aws.protocol.service_router import ( + ProtocolError, + determine_aws_protocol, + determine_aws_service_model, + match_available_protocols, +) from localstack.aws.spec import get_service_catalog from localstack.http import Request from localstack.utils.run import to_str +@pytest.fixture +def get_aws_client_for_protocol(aws_client_factory, monkeypatch): + def _create_client(service_name: str, protocol: str): + client = aws_client_factory.get_client( + service_name, + config=Config( + connect_timeout=1_000, + read_timeout=1_000, + retries={"total_max_attempts": 1}, + parameter_validation=False, + user_agent="aws-cli/1.33.7", + ), + ) + # TODO: we should come up with a better way to create a protocol-aware client + protocol_serializer = create_serializer(protocol_name=protocol, include_validation=False) + monkeypatch.setattr(client, "_serializer", protocol_serializer) + return client + + yield _create_client + + def _collect_operations() -> tuple[ServiceModel, OperationModel]: """ Collects all service<>operation combinations to test. @@ -20,88 +47,93 @@ def _collect_operations() -> tuple[ServiceModel, OperationModel]: service_catalog = get_service_catalog() for service_name in service_catalog.service_names: service = service_catalog.get(service_name) - for operation_name in service.operation_names: - # FIXME try to support more and more services, get these exclusions down! - # Exclude all operations for the following, currently _not_ supported services - if service.service_name in [ - "bedrock-agent", - "bedrock-agentcore", - "bedrock-agentcore-control", - "bedrock-agent-runtime", - "bedrock-data-automation", - "bedrock-data-automation-runtime", - "chime", - "chime-sdk-identity", - "chime-sdk-media-pipelines", - "chime-sdk-meetings", - "chime-sdk-messaging", - "chime-sdk-voice", - "codecatalyst", - "connect", - "connect-contact-lens", - "connectcampaigns", - "connectcampaignsv2", - "greengrassv2", - "iot1click", - "iot1click-devices", - "iot1click-projects", - "ivs", - "ivs-realtime", - "kinesis-video-archived", - "kinesis-video-archived-media", - "kinesis-video-media", - "kinesis-video-signaling", - "kinesis-video-webrtc-storage", - "kinesisvideo", - "lex-models", - "lex-runtime", - "lexv2-models", - "lexv2-runtime", - "mailmanager", - "marketplace-catalog", - "marketplace-deployment", - "marketplace-reporting", - "personalize", - "personalize-events", - "personalize-runtime", - "pinpoint-sms-voice", - "qconnect", - "sagemaker-edge", - "sagemaker-featurestore-runtime", - "sagemaker-metrics", - "sms-voice", - "sso", - "sso-oidc", - "wisdom", - "workdocs", - ]: - yield pytest.param( - service, - service.protocol, - service.operation_model(operation_name), - marks=pytest.mark.skip( - reason=f"{service.service_name} is currently not supported by the service router" - ), - ) - # Exclude services / operations which have ambiguities and where the service routing needs to resolve those - elif ( - service.service_name in ["docdb", "neptune"] # maps to rds - or service.service_name in "timestream-write" # maps to timestream-query - or ( - service.service_name == "sesv2" - and operation_name == "PutEmailIdentityDkimSigningAttributes" - ) - ): - yield pytest.param( - service, - service.protocol, - service.operation_model(operation_name), - marks=pytest.mark.skip( - reason=f"{service.service_name} may differ due to ambiguities in the service specs" - ), - ) - else: - yield service, service.protocol, service.operation_model(operation_name) + service_protocols = {service.protocol} + if protocols := service.metadata.get("protocols"): + service_protocols.update(protocols) + + for service_protocol in sorted(service_protocols): + for operation_name in service.operation_names: + # FIXME try to support more and more services, get these exclusions down! + # Exclude all operations for the following, currently _not_ supported services + if service.service_name in [ + "bedrock-agent", + "bedrock-agentcore", + "bedrock-agentcore-control", + "bedrock-agent-runtime", + "bedrock-data-automation", + "bedrock-data-automation-runtime", + "chime", + "chime-sdk-identity", + "chime-sdk-media-pipelines", + "chime-sdk-meetings", + "chime-sdk-messaging", + "chime-sdk-voice", + "codecatalyst", + "connect", + "connect-contact-lens", + "connectcampaigns", + "connectcampaignsv2", + "greengrassv2", + "iot1click", + "iot1click-devices", + "iot1click-projects", + "ivs", + "ivs-realtime", + "kinesis-video-archived", + "kinesis-video-archived-media", + "kinesis-video-media", + "kinesis-video-signaling", + "kinesis-video-webrtc-storage", + "kinesisvideo", + "lex-models", + "lex-runtime", + "lexv2-models", + "lexv2-runtime", + "mailmanager", + "marketplace-catalog", + "marketplace-deployment", + "marketplace-reporting", + "personalize", + "personalize-events", + "personalize-runtime", + "pinpoint-sms-voice", + "qconnect", + "sagemaker-edge", + "sagemaker-featurestore-runtime", + "sagemaker-metrics", + "sms-voice", + "sso", + "sso-oidc", + "wisdom", + "workdocs", + ]: + yield pytest.param( + service, + service_protocol, + service.operation_model(operation_name), + marks=pytest.mark.skip( + reason=f"{service.service_name} is currently not supported by the service router" + ), + ) + # Exclude services / operations which have ambiguities and where the service routing needs to resolve those + elif ( + service.service_name in ["docdb", "neptune"] # maps to rds + or service.service_name in "timestream-write" # maps to timestream-query + or ( + service.service_name == "sesv2" + and operation_name == "PutEmailIdentityDkimSigningAttributes" + ) + ): + yield pytest.param( + service, + service_protocol, + service.operation_model(operation_name), + marks=pytest.mark.skip( + reason=f"{service.service_name} may differ due to ambiguities in the service specs" + ), + ) + else: + yield service, service_protocol, service.operation_model(operation_name) def _botocore_request_to_localstack_request(request_object: AWSRequest) -> Request: @@ -161,7 +193,11 @@ def _generate_test_name(param: Any): ids=_generate_test_name, ) def test_service_router_works_for_every_service( - service: ServiceModel, protocol: str, operation: OperationModel, caplog, aws_client_factory + service: ServiceModel, + protocol: str, + operation: OperationModel, + caplog, + get_aws_client_for_protocol, ): caplog.set_level("CRITICAL", "botocore") @@ -174,16 +210,7 @@ def test_service_router_works_for_every_service( ) # Create a dummy request for the service router - client = aws_client_factory.get_client( - service_name, - config=Config( - connect_timeout=1_000, - read_timeout=1_000, - retries={"total_max_attempts": 1}, - parameter_validation=False, - user_agent="aws-cli/1.33.7", - ), - ) + client = get_aws_client_for_protocol(service_name, protocol) request_context = { "client_region": client.meta.region_name, @@ -205,10 +232,11 @@ def test_service_router_works_for_every_service( # Execute the service router detected_service_model = determine_aws_service_model(request) + detected_service_protocol = determine_aws_protocol(request, detected_service_model) - # Make sure the detected service is the same as the one we generated the request for + # Make sure the detected service and protocol are the same as the one we generated the request for assert detected_service_model.service_name == service.service_name - assert detected_service_model.protocol == service.protocol + assert detected_service_protocol == protocol def test_endpoint_prefix_based_routing(): @@ -265,3 +293,78 @@ def test_endpoint_prefix_based_routing_for_sqs(): ) assert detected_service_model.service_name == "sqs" assert detected_service_model.protocol == "json" + + +def test_multi_protocols_detection(): + rpc_v2_request = Request( + method="POST", + path="/service/ArcRegionSwitch/operation/ListPlans", + headers={ + "Host": "localhost.localstack.cloud", + "Smithy-Protocol": "rpc-v2-cbor", + "Content-Type": "application/cbor", + }, + ) + + detected_service_model = determine_aws_service_model(rpc_v2_request) + assert detected_service_model.service_name == "arc-region-switch" + assert detected_service_model.protocol == "smithy-rpc-v2-cbor" + + detected_protocol = determine_aws_protocol(rpc_v2_request, detected_service_model) + assert detected_protocol == "smithy-rpc-v2-cbor" + + # test explicitly with JSON + json_request = Request( + method="POST", + path="/", + headers={ + "Host": "localhost.localstack.cloud", + "X-Amz-Target": "ArcRegionSwitch.ListPlans", + "Content-Type": "application/x-amz-json-1.0", + }, + ) + + detected_service_model = determine_aws_service_model(json_request) + assert detected_service_model.service_name == "arc-region-switch" + # the default service model protocol is still RPC v2 CBOR + assert detected_service_model.protocol == "smithy-rpc-v2-cbor" + + detected_protocol = determine_aws_protocol(json_request, detected_service_model) + assert detected_protocol == "json" + + +def test_multi_protocols_detection_error(): + bad_request = Request( + method="POST", + path="/", + headers={ + "Host": "localhost.localstack.cloud", + "X-Amz-Target": "ArcRegionSwitch.ListPlans", + }, + ) + + detected_service_model = determine_aws_service_model(bad_request) + assert detected_service_model.service_name == "arc-region-switch" + # the default service model protocol is still RPC v2 CBOR + assert detected_service_model.protocol == "smithy-rpc-v2-cbor" + + with pytest.raises(ProtocolError): + determine_aws_protocol(bad_request, detected_service_model) + + +def test_query_protocol_detection_with_empty_body(): + sns_request = Request( + method="POST", + path="/", + headers={ + "Host": "localhost.localstack.cloud", + }, + query_string="Action=ConfirmSubscription&TopicArn=bad_arn&Token=token", + ) + + detected_service_model = determine_aws_service_model(sns_request) + assert detected_service_model.service_name == "sns" + assert detected_service_model.protocol == "query" + + # we set wrong protocol here just to verify we can match `query` even if we don't have the Content-Type + assert match_available_protocols(sns_request, ["query", "json"]) diff --git a/tests/unit/services/cloudformation/test_cloudformation.py b/tests/unit/services/cloudformation/test_cloudformation.py index 98644ff410e6c..62caa4d958983 100644 --- a/tests/unit/services/cloudformation/test_cloudformation.py +++ b/tests/unit/services/cloudformation/test_cloudformation.py @@ -1,9 +1,12 @@ +import pytest + from localstack.services.cloudformation.api_utils import is_local_service_url from localstack.services.cloudformation.deployment_utils import ( PLACEHOLDER_AWS_NO_VALUE, remove_none_values, ) from localstack.services.cloudformation.engine.template_deployer import order_resources +from localstack.services.cloudformation.engine.v2.resolving import REGEX_DYNAMIC_REF def test_is_local_service_url(): @@ -60,3 +63,28 @@ def test_order_resources(): ) assert list(sorted_resources.keys()) == ["A", "B"] + + +class TestDynamicResolving: + @pytest.mark.parametrize( + "value,matches", + [ + pytest.param("{{resolve:ssm:abc123}}", True, id="ssm-basic"), + pytest.param("abc:{{resolve:ssm:abc123}}:foo", True, id="ssm-with-surrounding"), + pytest.param("{{resolve:ssm:${ParameterName}}}", False, id="ssm-in-sub"), + pytest.param("{{resolve:secretsmanager:foo}}", True, id="secrets-basic"), + pytest.param( + "{{resolve:secretsmanager:arn:aws:secretsmanager:us-east-1:000000000000:secret:foo:SecretString:}}", + True, + id="secrets-partial", + ), + pytest.param( + "{{resolve:secretsmanager:arn:aws:secretsmanager:us-east-1:000000000000:secret:foo:SecretString:::}}", + True, + id="secrets-full", + ), + pytest.param("{{resolve:secretsmanager:${SecretName}}}", False, id="secrets-in-sub"), + ], + ) + def test_dynamic_ref_regex_matches(self, value, matches): + assert bool(REGEX_DYNAMIC_REF.search(value)) == matches diff --git a/tests/unit/services/sqs/test_sqs.py b/tests/unit/services/sqs/test_sqs.py index b8e9cddefaaf7..1515d6d8507df 100644 --- a/tests/unit/services/sqs/test_sqs.py +++ b/tests/unit/services/sqs/test_sqs.py @@ -4,8 +4,8 @@ import localstack.services.sqs.models from localstack.services.sqs import provider from localstack.services.sqs.constants import DEFAULT_MAXIMUM_MESSAGE_SIZE -from localstack.services.sqs.provider import _create_message_attribute_hash from localstack.services.sqs.utils import ( + create_message_attribute_hash, guess_endpoint_strategy_and_host, is_sqs_queue_url, parse_queue_url, @@ -20,7 +20,7 @@ def test_sqs_message_attrs_md5(): "DataType": "Number", } } - md5 = _create_message_attribute_hash(msg_attrs) + md5 = create_message_attribute_hash(msg_attrs) assert md5 == "235c5c510d26fb653d073faed50ae77c" diff --git a/tests/unit/test_common.py b/tests/unit/test_common.py index 2084531db690b..e0e93b4be7669 100644 --- a/tests/unit/test_common.py +++ b/tests/unit/test_common.py @@ -5,7 +5,7 @@ import threading import time import zipfile -from datetime import UTC, date, datetime +from datetime import UTC, date, datetime, timedelta from zoneinfo import ZoneInfo import pytest @@ -46,6 +46,23 @@ def test_isoformat_milliseconds(self): env = common.isoformat_milliseconds(datetime(2010, 3, 20, 7, 24, 00, 0)) assert env == "2010-03-20T07:24:00.000" + @pytest.mark.parametrize( + "str_format", + [ + "2025-09-18T14:07:30", + "2025-09-18T14:07:30Z", + "2025-09-18T14:07:30.70300Z", + "18/Sep/2025:14:07:30 +0000", + ], + ) + def test_parse_timestamp_timezone_aware(self, str_format): + datetime_obj = common.parse_timestamp(str_format) + # we cannot assert that tzinfo is `datetime.UTC` because it is only supported starting Python 3.11 + # so we assert manually that the returned `tzinfo` is UTC + # we are using ZoneInfo("UTC") in the `parse_timestamp` utility as it is in the import path of the CLI + assert datetime_obj.tzinfo is not None + assert datetime_obj.utcoffset() == timedelta(0) + def test_base64_to_hex(self): env = common.base64_to_hex("Zm9vIGJhcg ==") assert env == b"666f6f20626172" diff --git a/tests/unit/test_dns_server.py b/tests/unit/test_dns_server.py index 96ffd172b1f7a..c64f9ff5538be 100644 --- a/tests/unit/test_dns_server.py +++ b/tests/unit/test_dns_server.py @@ -137,6 +137,24 @@ def test_dns_server_add_multiple_hosts(self, dns_server, query_dns): assert answer.answer assert "123.123.123.123" in answer.to_text() + def test_dns_server_resolves_alias_wildcards(self, dns_server, query_dns): + """Check if server resolves aliases with wildcards""" + dns_server.add_host("example.org", TargetRecord("1.1.1.1", RecordType.A)) + answer = query_dns("subdomain1.example.org", "A") + assert len(answer.answer) == 0 + + dns_server.add_alias( + source_name="*.example.org", + record_type=RecordType.A, + target=AliasTarget(target="example.org"), + ) + answer = query_dns("subdomain1.example.org", "A") + assert answer.answer + assert "1.1.1.1" in answer.to_text() + answer = query_dns("subdomain2.example.org", "A") + assert answer.answer + assert "1.1.1.1" in answer.to_text() + def test_overriding_with_dns_resolve_ip(self, dns_server, query_dns, monkeypatch): monkeypatch.setattr(config, "DNS_RESOLVE_IP", "2.2.2.2") diff --git a/tests/unit/utils/test_catalog.py b/tests/unit/utils/test_catalog.py new file mode 100644 index 0000000000000..974cd01920820 --- /dev/null +++ b/tests/unit/utils/test_catalog.py @@ -0,0 +1,131 @@ +import pytest + +from localstack.utils.catalog.catalog import AwsCatalogRemoteStatePlugin +from localstack.utils.catalog.catalog_loader import RemoteCatalogLoader +from localstack.utils.catalog.common import ( + AwsRemoteCatalog, + AwsServiceOperationsSupportInLatest, + AwsServicesSupportInLatest, + CloudFormationResourcesSupportAtRuntime, + CloudFormationResourcesSupportInLatest, + LocalStackMetadata, +) + + +class FakeCatalogLoader(RemoteCatalogLoader): + def __init__(self, catalog: AwsRemoteCatalog): + self.catalog = catalog + + def get_remote_catalog(self) -> AwsRemoteCatalog: + return self.catalog + + +CATALOG = AwsRemoteCatalog( + schema_version="1.0", + localstack=LocalStackMetadata(version="4.7"), + services={ + "athena": { + "pro": { + "provider": "athena:pro", + "operations": ["StartQueryExecution", "GetQueryExecution"], + "plans": ["ultimate", "enterprise"], + } + }, + "s3": { + "community": { + "provider": "s3:default", + "operations": ["CreateBucket"], + "plans": ["free", "base", "ultimate", "enterprise"], + }, + "pro": { + "provider": "s3:pro", + "operations": ["SelectObjectContent"], + "plans": ["base", "ultimate", "enterprise"], + }, + }, + "kms": { + "community": { + "provider": "kms:default", + "operations": ["ListKeys"], + "plans": ["free", "base", "ultimate", "enterprise"], + } + }, + }, + cloudformation_resources={ + "community": {"AWS::S3::Bucket": {"methods": ["Create", "Delete"]}}, + "pro": {"AWS::Athena::CapacitiesReservation": {"methods": ["Create", "Update", "Delete"]}}, + }, +) + + +@pytest.fixture(scope="class", autouse=True) +def aws_catalog(): + return AwsCatalogRemoteStatePlugin(FakeCatalogLoader(CATALOG)) + + +class TestAwsCatalog: + @pytest.mark.parametrize( + "service_name,expected_status", + [ + ("s3", AwsServicesSupportInLatest.SUPPORTED), + ("athena", AwsServicesSupportInLatest.SUPPORTED_WITH_LICENSE_UPGRADE), + ("nonexistent", AwsServicesSupportInLatest.NOT_SUPPORTED), + ], + ) + def test_get_service_status(self, aws_catalog, service_name, expected_status): + result = aws_catalog.get_aws_service_status(service_name) + assert result == expected_status + + @pytest.mark.parametrize( + "service_name,operation_name,expected_status", + [ + ("kms", "ListKeys", AwsServiceOperationsSupportInLatest.SUPPORTED), + ( + "s3", + "SelectObjectContent", + AwsServiceOperationsSupportInLatest.SUPPORTED_WITH_LICENSE_UPGRADE, + ), + ("s3", "UnsupportedOp", AwsServiceOperationsSupportInLatest.NOT_SUPPORTED), + ], + ) + def test_get_service_status_with_operation( + self, aws_catalog, service_name, operation_name, expected_status + ): + result = aws_catalog.get_aws_service_status(service_name, operation_name) + assert result == expected_status + + def test_get_service_status_with_only_one_emulator_type(self, aws_catalog): + result = aws_catalog.get_aws_service_status("athena") + assert result == AwsServicesSupportInLatest.SUPPORTED_WITH_LICENSE_UPGRADE + + def test_get_service_status_with_empty_operation(self, aws_catalog): + assert ( + aws_catalog.get_aws_service_status("s3", None) == AwsServicesSupportInLatest.SUPPORTED + ) + assert ( + aws_catalog.get_aws_service_status("s3", "") + == AwsServiceOperationsSupportInLatest.SUPPORTED + ) + + @pytest.mark.parametrize( + "resource_name,service_name,expected_status", + [ + ("AWS::S3::Bucket", "s3", CloudFormationResourcesSupportAtRuntime.AVAILABLE), + ("AWS::S3::NonExistent", "s3", CloudFormationResourcesSupportInLatest.NOT_SUPPORTED), + ( + "AWS::Athena::CapacitiesReservation", + "athena", + AwsServicesSupportInLatest.SUPPORTED_WITH_LICENSE_UPGRADE, + ), + ( + "AWS::NonExistentService::NonExistent", + "nonexistentservice", + AwsServicesSupportInLatest.NOT_SUPPORTED, + ), + ], + ) + def test_get_cfn_resource_status( + self, aws_catalog, resource_name, service_name, expected_status + ): + result = aws_catalog.get_cloudformation_resource_status(resource_name, service_name) + assert result == expected_status