From 19319de90e496383a54f5bd39fa57487f239808d Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Fri, 14 Aug 2026 20:56:08 -0400 Subject: [PATCH 01/19] Build for aws cli --- .github/workflows/configs.yml | 2 +- INSTALLATION.md | 4 +- src/bindings-jvm/README.md | 32 + src/bindings-jvm/src/lib.rs | 47 +- .../amazon/cloudformation/validate/Api.kt | 111 ++ .../tests/kotlin/src/test/kotlin/SmokeTest.kt | 88 ++ src/bindings-python/README.md | 36 +- src/bindings-python/build.sh | 17 +- src/bindings-python/pyproject.toml | 4 +- .../cloudformation_validate/__init__.py | 126 +- src/bindings-python/src/lib.rs | 47 +- src/bindings-python/tests/run.sh | 8 +- src/bindings-python/tests/smoke_test.py | 80 + src/data-source/build.rs | 66 +- src/schema-validator/src/lib.rs | 19 + src/schema-validator/src/resource_schema.rs | 174 +++ src/schema-validator/src/store.rs | 4 + src/validation-engine/API.md | 51 + src/validation-engine/src/aws_api.rs | 1327 +++++++++++++++++ src/validation-engine/src/lib.rs | 6 + 20 files changed, 2230 insertions(+), 19 deletions(-) create mode 100644 src/schema-validator/src/resource_schema.rs create mode 100644 src/validation-engine/src/aws_api.rs diff --git a/.github/workflows/configs.yml b/.github/workflows/configs.yml index eb18f3a4..53855e5e 100644 --- a/.github/workflows/configs.yml +++ b/.github/workflows/configs.yml @@ -75,7 +75,7 @@ jobs: WORKING_DIR: 'src' RUST_TOOLCHAIN: '1.96.0' # keep in sync with src/rust-toolchain.toml NODE_VERSION: '22.x' - PYTHON_VERSION: '3.12' + PYTHON_VERSION: '3.10' GO_VERSION: '1.26' UNIFFI_BINDGEN_GO_TAG: 'v0.7.1+v0.31.0' # keep in sync with bindings-go/README.md and the uniffi pin in bindings-go/Cargo.toml JAVA_VERSION: '21' diff --git a/INSTALLATION.md b/INSTALLATION.md index f895d6d0..2bcfc92d 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -53,7 +53,7 @@ See the [Node.js API and examples](src/bindings-wasm/README.md). ### Python Production versions are published to [PyPI](https://pypi.org/project/cloudformation-validate/); prereleases are -published to [TestPyPI](https://test.pypi.org/project/cloudformation-validate/). The package requires Python 3.12 or +published to [TestPyPI](https://test.pypi.org/project/cloudformation-validate/). The package requires Python 3.10 or later, and its platform-specific wheels have no runtime package dependencies. ```bash @@ -185,7 +185,7 @@ testing the project from source need the tools below. Pinned versions live in | Kotlin (`kotlinc`) | 2.4.0 | JVM binding build | | | `ktlint` | 1.8.0 | JVM binding formatting | | | Gradle | 9.6.1 | JVM binding build/test | Must be on `PATH` - `bindings-jvm/build.sh` and the JVM test runner invoke `gradle` | -| Python | 3.12+ | Python binding build/test, license generation, `scripts/` | `setuptools` for the wheel build; no other packages required | +| Python | 3.10+ | Python binding build/test, license generation, `scripts/` | `setuptools` for the wheel build; no other packages required | | Go | 1.26+ | Go binding build/test | cgo must be enabled (default); Windows also needs `rustup target add x86_64-pc-windows-gnu` and MinGW-w64 `gcc` | | `uniffi-bindgen-go` | 0.7.1 | Go binding generation | `cargo install --git https://github.com/NordSecurity/uniffi-bindgen-go --tag v0.7.1+v0.31.0` | | `git`, `curl`, `openssl` | - | source control, fetching JVM deps, verifying releases | Usually preinstalled | diff --git a/src/bindings-jvm/README.md b/src/bindings-jvm/README.md index 4b7dd67a..2d781232 100644 --- a/src/bindings-jvm/README.md +++ b/src/bindings-jvm/README.md @@ -76,6 +76,38 @@ interface Engine { `template` is a `java.io.File` - the engine reads the bytes and uses the file path for diagnostic source locations. +### AWS API request validation + +Use `validateAwsApiRequest` for AWS SDK-style request values rather than a complete template. The validator classifies +the operation, selects a CloudFormation resource type, models representable create/update state, and validates the +resulting template entirely offline: + +```kotlin +val result = RegoEngine().validateAwsApiRequest( + AwsApiRequest( + serviceName = "s3", + servicePrefix = "s3", + operationName = "CreateBucket", + httpMethod = "PUT", + parameters = mapOf( + "Bucket" to "example-bucket", + "Tags" to mapOf("Team" to "Platform"), + ), + ), +) + +result.report?.diagnostics?.forEach { diagnostic -> + println("${diagnostic.ruleId}: ${diagnostic.message}") +} ?: println("${result.status}: ${result.reason}") +``` + +`AwsApiRequest.parameters` accepts nested maps, iterables and arrays, scalars, byte arrays, and Java temporal values +without mutating the supplied map. `TemplateBody` bytes are validated exactly; `TemplateURL` is skipped because the +validator does not perform network requests. Every result reports `status`, `operationKind`, `templateSource`, +`resourceTypes`, and `reason`; skipped requests have a null `report`. `validateAwsApiRequestStandard` returns standard +diagnostics, while `validateAwsApiRequestDetailed` and its `validateAwsApiRequest` alias return detailed diagnostics. +The same classes and methods are callable from Java with conventional generated getters. + ### `EngineConfig` Passed to the constructor. All fields default to empty lists. diff --git a/src/bindings-jvm/src/lib.rs b/src/bindings-jvm/src/lib.rs index 261b6c5d..e3508e94 100644 --- a/src/bindings-jvm/src/lib.rs +++ b/src/bindings-jvm/src/lib.rs @@ -22,7 +22,10 @@ pub use template_model::model::{ }; pub use template_model::resolver::{MapEntry, ParameterInfo, RefKind, ResolvedValue}; pub use template_model::{JsonValue, PseudoParameterOverrides, SourceSpan}; -pub use validation_engine::{EngineConfig, EngineType, ExternalRuleSource}; +pub use validation_engine::{ + AwsApiOperationKind, AwsApiRequestContext, AwsApiRequestValidationStatus, AwsApiTemplateSource, AwsApiValue, + DetailedAwsApiRequestValidation, EngineConfig, EngineType, ExternalRuleSource, StandardAwsApiRequestValidation, +}; pub use schema_validator::SchemaValidatorConfig; @@ -197,6 +200,48 @@ macro_rules! impl_jvm_engine { ) } + pub fn validate_aws_api_request_standard( + &self, + request: AwsApiRequestContext, + config: ValidateConfig, + ) -> Result { + validation_engine::catch_panics( + || { + let core_config = config.to_core(DetailLevel::Standard); + let validation = validation_engine::validate_aws_api_request( + &self.engine, + &self.schema_validator, + &request, + core_config, + ) + .map_err(|e| ValidationError::Engine { msg: e.to_string() })?; + Ok(validation.to_standard()) + }, + panic_to_error, + ) + } + + pub fn validate_aws_api_request_detailed( + &self, + request: AwsApiRequestContext, + config: ValidateConfig, + ) -> Result { + validation_engine::catch_panics( + || { + let core_config = config.to_core(DetailLevel::Detailed); + let validation = validation_engine::validate_aws_api_request( + &self.engine, + &self.schema_validator, + &request, + core_config, + ) + .map_err(|e| ValidationError::Engine { msg: e.to_string() })?; + Ok(validation.to_detailed()) + }, + panic_to_error, + ) + } + pub fn list_rules(&self) -> Result, ValidationError> { validation_engine::catch_panics(|| Ok(self.engine.list_rules()), panic_to_error) } diff --git a/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt b/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt index 0947bf53..ee054564 100644 --- a/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt +++ b/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt @@ -4,8 +4,12 @@ import software.amazon.cloudformation.validate.datasource.AdditionalSchemaSource import software.amazon.cloudformation.validate.diagnostics.DetailedReport import software.amazon.cloudformation.validate.diagnostics.StandardDiagnostic import software.amazon.cloudformation.validate.diagnostics.StandardReport +import software.amazon.cloudformation.validate.engine.AwsApiRequestContext as NativeAwsApiRequest +import software.amazon.cloudformation.validate.engine.AwsApiValue as NativeAwsApiValue +import software.amazon.cloudformation.validate.engine.DetailedAwsApiRequestValidation import software.amazon.cloudformation.validate.engine.EngineConfig import software.amazon.cloudformation.validate.engine.ExternalRuleSource +import software.amazon.cloudformation.validate.engine.StandardAwsApiRequestValidation import software.amazon.cloudformation.validate.rules.RuleInfo import software.amazon.cloudformation.validate.schemavalidator.SchemaValidatorConfig import java.io.File @@ -13,10 +17,97 @@ import java.io.File interface Engine { fun validateStandard(template: File, config: ValidateConfig = ValidateConfig()): StandardReport fun validateDetailed(template: File, config: ValidateConfig = ValidateConfig()): DetailedReport + fun validateAwsApiRequest( + request: AwsApiRequest, + config: ValidateConfig = ValidateConfig(), + ): DetailedAwsApiRequestValidation = validateAwsApiRequestDetailed(request, config) + fun validateAwsApiRequestStandard( + request: AwsApiRequest, + config: ValidateConfig = ValidateConfig(), + ): StandardAwsApiRequestValidation = + throw UnsupportedOperationException( + "AWS API request validation is not supported by this Engine implementation", + ) + fun validateAwsApiRequestDetailed( + request: AwsApiRequest, + config: ValidateConfig = ValidateConfig(), + ): DetailedAwsApiRequestValidation = + throw UnsupportedOperationException( + "AWS API request validation is not supported by this Engine implementation", + ) fun listRules(): List fun engineName(): String } +/** + * Service, operation, and request values for CloudFormation validation. + * + * [parameters] accepts nested maps/lists, strings, numbers, booleans, nulls, + * byte arrays, and Java time values. Unsupported values are marked explicitly + * and conservatively omitted during request-to-template synthesis. + */ +class AwsApiRequest @JvmOverloads constructor( + val serviceName: String, + val operationName: String, + parameters: Map, + val servicePrefix: String? = null, + val httpMethod: String? = null, + val isReadOnly: Boolean? = null, +) { + val parameters: Map = LinkedHashMap(parameters) + + internal fun toNative(): NativeAwsApiRequest = + NativeAwsApiRequest( + serviceName = serviceName, + operationName = operationName, + parameters = parameters.mapValues { (_, value) -> value.toNativeAwsApiValue() }, + servicePrefix = servicePrefix, + httpMethod = httpMethod, + isReadOnly = isReadOnly, + ) +} + +private fun Any?.toNativeAwsApiValue(): NativeAwsApiValue = + when (this) { + null -> NativeAwsApiValue.Null + is Boolean -> NativeAwsApiValue.Boolean(value = this) + is Byte -> NativeAwsApiValue.Integer(value = toLong()) + is Short -> NativeAwsApiValue.Integer(value = toLong()) + is Int -> NativeAwsApiValue.Integer(value = toLong()) + is Long -> NativeAwsApiValue.Integer(value = this) + is UByte -> NativeAwsApiValue.UnsignedInteger(value = toULong()) + is UShort -> NativeAwsApiValue.UnsignedInteger(value = toULong()) + is UInt -> NativeAwsApiValue.UnsignedInteger(value = toULong()) + is ULong -> NativeAwsApiValue.UnsignedInteger(value = this) + is Float -> + if (isFinite()) { + NativeAwsApiValue.Number(value = toDouble()) + } else { + NativeAwsApiValue.Unsupported(typeName = "non-finite floating-point number") + } + is Double -> + if (isFinite()) { + NativeAwsApiValue.Number(value = this) + } else { + NativeAwsApiValue.Unsupported(typeName = "non-finite floating-point number") + } + is String -> NativeAwsApiValue.String(value = this) + is ByteArray -> NativeAwsApiValue.Bytes(value = this) + is java.time.temporal.TemporalAccessor -> NativeAwsApiValue.String(value = toString()) + is Map<*, *> -> { + if (keys.any { it !is String }) { + NativeAwsApiValue.Unsupported(typeName = "mapping with non-string keys") + } else { + NativeAwsApiValue.Object( + entries = entries.associate { (key, value) -> key as String to value.toNativeAwsApiValue() }, + ) + } + } + is Iterable<*> -> NativeAwsApiValue.Array(items = map { it.toNativeAwsApiValue() }) + is Array<*> -> NativeAwsApiValue.Array(items = map { it.toNativeAwsApiValue() }) + else -> NativeAwsApiValue.Unsupported(typeName = javaClass.name) + } + /** * Reads a resource provider schema file into an [AdditionalSchemaSource] for * [SchemaValidatorConfig.additionalSchemas]. [typeName] may be omitted when the @@ -70,6 +161,16 @@ class RegoEngine( override fun validateDetailed(template: File, config: ValidateConfig): DetailedReport = inner.validateDetailed(template.readBytes(), config, template.path) + override fun validateAwsApiRequestStandard( + request: AwsApiRequest, + config: ValidateConfig, + ): StandardAwsApiRequestValidation = inner.validateAwsApiRequestStandard(request.toNative(), config) + + override fun validateAwsApiRequestDetailed( + request: AwsApiRequest, + config: ValidateConfig, + ): DetailedAwsApiRequestValidation = inner.validateAwsApiRequestDetailed(request.toNative(), config) + override fun listRules(): List = inner.listRules() override fun engineName(): String = inner.engineName() } @@ -85,6 +186,16 @@ class CelEngine( override fun validateDetailed(template: File, config: ValidateConfig): DetailedReport = inner.validateDetailed(template.readBytes(), config, template.path) + override fun validateAwsApiRequestStandard( + request: AwsApiRequest, + config: ValidateConfig, + ): StandardAwsApiRequestValidation = inner.validateAwsApiRequestStandard(request.toNative(), config) + + override fun validateAwsApiRequestDetailed( + request: AwsApiRequest, + config: ValidateConfig, + ): DetailedAwsApiRequestValidation = inner.validateAwsApiRequestDetailed(request.toNative(), config) + override fun listRules(): List = inner.listRules() override fun engineName(): String = inner.engineName() } diff --git a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt index e664eb92..f6de5fa4 100644 --- a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt +++ b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt @@ -140,6 +140,94 @@ class SmokeTest { } } + @Test + fun synthesizedAwsApiCreateValidatesWithBothEnginesWithoutMutatingInput() { + val parameters = linkedMapOf( + "Bucket" to "synthetic-bucket", + "Tags" to linkedMapOf("Team" to "CLI"), + ) + val request = AwsApiRequest( + serviceName = "s3", + operationName = "CreateBucket", + parameters = parameters, + servicePrefix = "s3", + httpMethod = "POST", + ) + + val results = listOf(REGO, CEL).map { it.validateAwsApiRequest(request, defaultConfig()) } + + for (result in results) { + assertEquals(AwsApiRequestValidationStatus.VALIDATED, result.status) + assertEquals(AwsApiOperationKind.CLOUD_FORMATION_CREATE, result.operationKind) + assertEquals(AwsApiTemplateSource.SYNTHESIZED_CREATE, result.templateSource) + assertEquals(listOf("AWS::S3::Bucket"), result.resourceTypes) + assertNotNull(result.report) + } + assertEquals(gson.toJson(results[0].report?.diagnostics), gson.toJson(results[1].report?.diagnostics)) + assertEquals( + linkedMapOf("Bucket" to "synthetic-bucket", "Tags" to linkedMapOf("Team" to "CLI")), + parameters, + ) + } + + @Test + fun awsApiTemplateBodyPreservesBytesAndReadOnlyRequestsReportSkips() { + val templateResult = REGO.validateAwsApiRequest( + AwsApiRequest( + serviceName = "cloudformation", + operationName = "CreateChangeSet", + parameters = mapOf("TemplateBody" to "{\"Resources\":{}}".toByteArray()), + servicePrefix = "cloudformation", + httpMethod = "POST", + ), + defaultConfig(), + ) + assertEquals(AwsApiRequestValidationStatus.VALIDATED, templateResult.status) + assertEquals(AwsApiTemplateSource.TEMPLATE_BODY, templateResult.templateSource) + assertEquals(ReportStatus.OK, templateResult.report?.status) + + val readResult = REGO.validateAwsApiRequest( + AwsApiRequest( + serviceName = "iam", + operationName = "GetRole", + parameters = mapOf("RoleName" to "Synthetic"), + servicePrefix = "iam", + httpMethod = "POST", + ), + defaultConfig(), + ) + assertEquals(AwsApiRequestValidationStatus.SKIPPED, readResult.status) + assertEquals(AwsApiOperationKind.READ_ONLY, readResult.operationKind) + assertNull(readResult.report) + assertTrue(readResult.reason.contains("read-only")) + } + + @Test + fun awsApiPartialUpdateScopesDiagnosticsAndKeepsCountsConsistent() { + val result = REGO.validateAwsApiRequest( + AwsApiRequest( + serviceName = "lambda", + operationName = "UpdateFunctionConfiguration", + parameters = mapOf("FunctionName" to "Synthetic", "MemorySize" to 0), + servicePrefix = "lambda", + httpMethod = "POST", + ), + defaultConfig(), + ) + val report = result.report ?: fail("synthesized update must return a report") + + assertEquals(AwsApiTemplateSource.SYNTHESIZED_UPDATE, result.templateSource) + assertTrue( + report.diagnostics.all { it.propertyPath?.contains("MemorySize") == true }, + report.diagnostics.toString(), + ) + val counts = report.metadata.counts + assertEquals( + report.diagnostics.size.toUInt(), + counts.fatal + counts.errors + counts.warnings + counts.informational + counts.debug, + ) + } + // ── SchemaValidator ────────────────────────────────────────────────────── @Test diff --git a/src/bindings-python/README.md b/src/bindings-python/README.md index 1a516409..0fb5f35f 100644 --- a/src/bindings-python/README.md +++ b/src/bindings-python/README.md @@ -17,7 +17,7 @@ Available on [PyPI](https://pypi.org/project/cloudformation-validate/) as `cloud pip install cloudformation-validate ``` -Requires Python 3.12+ and has no runtime dependencies. PyPI publishes a separate wheel for every supported native +Requires Python 3.10+ and has no runtime dependencies. PyPI publishes a separate wheel for every supported native target. Each wheel carries exactly one native library and an accurate platform tag, so pip downloads only the artifact compatible with the installer host. @@ -57,6 +57,40 @@ the same template and config. `template` is a file path (`str` / `os.PathLike`) or raw `bytes`; `config` is an optional `ValidateConfig`. +### AWS API request validation + +Use `validate_aws_api_request` when the input is an AWS SDK-style request rather than a complete template. The +validator classifies the operation, selects a CloudFormation resource type, models representable create/update state, +and validates the resulting template entirely offline: + +```python +from cloudformation_validate import AwsApiRequest, RegoEngine + +engine = RegoEngine() +result = engine.validate_aws_api_request( + AwsApiRequest( + service_name="s3", + service_prefix="s3", + operation_name="CreateBucket", + http_method="PUT", + parameters={"Bucket": "example-bucket", "Tags": {"Team": "Platform"}}, + ) +) + +if result.report is not None: + for diagnostic in result.report.diagnostics: + print(diagnostic.rule_id, diagnostic.message) +else: + print(result.status.name, result.reason) +``` + +`AwsApiRequest.parameters` accepts nested mappings and sequences, scalars, `bytes`, and `datetime.datetime` values +without mutating the supplied mapping. `TemplateBody` bytes are validated exactly; `TemplateURL` is skipped because the +validator does not perform network requests. The result always reports `status`, `operation_kind`, `template_source`, +`resource_types`, and `reason`; skipped requests have `report is None`. Use +`validate_aws_api_request_standard` for standard diagnostics or `validate_aws_api_request_detailed` (also exposed as +`validate_aws_api_request`) for detailed diagnostics. + ### EngineConfig Passed to the constructor. All fields default to empty lists. diff --git a/src/bindings-python/build.sh b/src/bindings-python/build.sh index 7e512398..5d832ca9 100755 --- a/src/bindings-python/build.sh +++ b/src/bindings-python/build.sh @@ -10,6 +10,7 @@ RELEASE_DIR="$WORKSPACE/target/release" PYTHON_SRC="$SCRIPT_DIR/python/cloudformation_validate" PACKAGE_DIR="$GENERATED_DIR/cloudformation_validate" WHEEL_DIR="$GENERATED_DIR/dist" +PYTHON="${PYTHON:-python3}" ARCH="$(bash "$REPOSITORY_ROOT/scripts/build-support/rust-host-architecture.sh")" case "$ARCH" in @@ -65,12 +66,12 @@ Build directories: EOF # ── Prerequisites ───────────────────────────────────────────────────────────── -command -v python3 &>/dev/null || { echo "Error: python3 not found on PATH" >&2; exit 1; } +command -v "$PYTHON" &>/dev/null || { echo "Error: $PYTHON not found on PATH" >&2; exit 1; } command -v unzip &>/dev/null || { echo "Error: unzip not found on PATH" >&2; exit 1; } -python3 -c 'import sys; sys.exit(0 if sys.version_info >= (3, 12) else 1)' \ - || { echo "Error: Python 3.12+ required, found $(python3 --version)" >&2; exit 1; } -python3 -m pip --version &>/dev/null \ - || { echo "Error: pip not available (python3 -m pip failed)" >&2; exit 1; } +"$PYTHON" -c 'import sys; sys.exit(0 if sys.version_info >= (3, 10) else 1)' \ + || { echo "Error: Python 3.10+ required, found $("$PYTHON" --version)" >&2; exit 1; } +"$PYTHON" -m pip --version &>/dev/null \ + || { echo "Error: pip not available ($PYTHON -m pip failed)" >&2; exit 1; } # ── Clean ───────────────────────────────────────────────────────────────────── echo "Cleaning previous build..." @@ -103,7 +104,7 @@ echo "Generating Python bindings..." # generated code is deterministic regardless of build host; these bindings # are order-independent, so this is safe. echo "Patching native loader and normalizing generated modules..." -python3 - "$PACKAGE_DIR" <<'EOF' +"$PYTHON" - "$PACKAGE_DIR" <<'EOF' import pathlib import re import sys @@ -169,7 +170,7 @@ cp "$SCRIPT_DIR/README.md" "$PACKAGE_DIR/README.md" # ── Build wheel ─────────────────────────────────────────────────────────────── echo "Building wheel..." cd "$GENERATED_DIR" -python3 -m pip wheel --no-deps --wheel-dir "$WHEEL_DIR" . --quiet +"$PYTHON" -m pip wheel --no-deps --wheel-dir "$WHEEL_DIR" . --quiet # ── Retag wheel with the host platform ─────────────────────────────────────── case "$OS" in @@ -194,7 +195,7 @@ case "$OS" in ;; esac echo "Retagging wheel as py3-none-${PLATFORM_TAG}..." -python3 - "$WHEEL_DIR" "$PLATFORM_TAG" <<'EOF' +"$PYTHON" - "$WHEEL_DIR" "$PLATFORM_TAG" <<'EOF' import base64 import csv import hashlib diff --git a/src/bindings-python/pyproject.toml b/src/bindings-python/pyproject.toml index 4f7cf2e3..fa8e16b0 100644 --- a/src/bindings-python/pyproject.toml +++ b/src/bindings-python/pyproject.toml @@ -9,7 +9,7 @@ description = "Fast, offline, embeddable validation for AWS CloudFormation templ readme = "README.md" license = "Apache-2.0" license-files = ["LICENSE", "NOTICE", "THIRD-PARTY-LICENSES.txt"] -requires-python = ">=3.12" +requires-python = ">=3.10" authors = [{ name = "Amazon Web Services" }] keywords = [ "aws", @@ -30,6 +30,8 @@ classifiers = [ "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Rust", "Topic :: Software Development :: Libraries :: Python Modules", diff --git a/src/bindings-python/python/cloudformation_validate/__init__.py b/src/bindings-python/python/cloudformation_validate/__init__.py index f6fc1d5b..499eb6ad 100644 --- a/src/bindings-python/python/cloudformation_validate/__init__.py +++ b/src/bindings-python/python/cloudformation_validate/__init__.py @@ -15,8 +15,11 @@ from __future__ import annotations +import datetime +import math import os import typing +from collections.abc import Mapping from .bindings_python import ( PyCelEngine as _PyCelEngine, @@ -90,14 +93,30 @@ ) from .data_source import AdditionalSchemaSource from .schema_validator import SchemaValidatorConfig -from .validation_engine import EngineConfig, EngineType, ExternalRuleSource +from .validation_engine import ( + AwsApiOperationKind, + AwsApiRequestContext as _NativeAwsApiRequest, + AwsApiRequestValidationStatus, + AwsApiTemplateSource, + AwsApiValue as _NativeAwsApiValue, + DetailedAwsApiRequestValidation, + EngineConfig, + EngineType, + ExternalRuleSource, + StandardAwsApiRequestValidation, +) __all__ = [ "AdditionalSchemaSource", + "AwsApiOperationKind", + "AwsApiRequest", + "AwsApiRequestValidationStatus", + "AwsApiTemplateSource", "CelEngine", "ConditionalNull", "ConditionalNullEntry", "DetailLevel", + "DetailedAwsApiRequestValidation", "DetailedDiagnostic", "DetailedReport", "DiagnosticCondition", @@ -153,6 +172,7 @@ "ServiceFilter", "Severity", "SourceSpan", + "StandardAwsApiRequestValidation", "StandardDiagnostic", "StandardReport", "Summary", @@ -203,6 +223,80 @@ def file_to_external_rule_source(path: typing.Union[str, os.PathLike]) -> Extern return ExternalRuleSource(name=str(resolved), content=f.read()) +class AwsApiRequest: + """Service, operation, and request values for CloudFormation validation. + + ``parameters`` accepts the same Python values used by botocore request + dictionaries, including nested mappings/sequences, ``bytes``, and + ``datetime.datetime``. Values that cannot be represented are carried as an + explicit unsupported marker and are conservatively omitted during synthesis. + """ + + def __init__( + self, + service_name: str, + operation_name: str, + parameters: Mapping[str, object], + *, + service_prefix: typing.Optional[str] = None, + http_method: typing.Optional[str] = None, + is_read_only: typing.Optional[bool] = None, + ): + if not isinstance(parameters, Mapping): + raise TypeError("parameters must be a mapping") + if not all(isinstance(name, str) for name in parameters): + raise TypeError("request parameter names must be strings") + self.service_name = service_name + self.operation_name = operation_name + self.parameters = dict(parameters) + self.service_prefix = service_prefix + self.http_method = http_method + self.is_read_only = is_read_only + + def _to_native(self) -> _NativeAwsApiRequest: + return _NativeAwsApiRequest( + service_name=self.service_name, + operation_name=self.operation_name, + parameters={name: _to_native_aws_api_value(value) for name, value in self.parameters.items()}, + service_prefix=self.service_prefix, + http_method=self.http_method, + is_read_only=self.is_read_only, + ) + + +def _to_native_aws_api_value(value: object) -> _NativeAwsApiValue: + if value is None: + return _NativeAwsApiValue.NULL() + if isinstance(value, bool): + return _NativeAwsApiValue.BOOLEAN(value=value) + if isinstance(value, int): + if -(2**63) <= value < 2**63: + return _NativeAwsApiValue.INTEGER(value=value) + if 0 <= value < 2**64: + return _NativeAwsApiValue.UNSIGNED_INTEGER(value=value) + return _NativeAwsApiValue.UNSUPPORTED(type_name="integer outside the 64-bit request range") + if isinstance(value, float): + if math.isfinite(value): + return _NativeAwsApiValue.NUMBER(value=value) + return _NativeAwsApiValue.UNSUPPORTED(type_name="non-finite floating-point number") + if isinstance(value, str): + return _NativeAwsApiValue.STRING(value=value) + if isinstance(value, (bytes, bytearray, memoryview)): + return _NativeAwsApiValue.BYTES(value=bytes(value)) + if isinstance(value, datetime.datetime): + return _NativeAwsApiValue.STRING(value=value.isoformat()) + if isinstance(value, Mapping): + if not all(isinstance(name, str) for name in value): + return _NativeAwsApiValue.UNSUPPORTED(type_name="mapping with non-string keys") + return _NativeAwsApiValue.OBJECT( + entries={name: _to_native_aws_api_value(item) for name, item in value.items()} + ) + if isinstance(value, (list, tuple)): + return _NativeAwsApiValue.ARRAY(items=[_to_native_aws_api_value(item) for item in value]) + value_type = type(value) + return _NativeAwsApiValue.UNSUPPORTED(type_name=f"{value_type.__module__}.{value_type.__qualname__}") + + class Engine: """Validates CloudFormation templates against the built-in rule set. @@ -232,6 +326,36 @@ def validate_detailed(self, template: Template, config: typing.Optional[Validate content, path = _template_bytes(template) return self._inner.validate_detailed(content, config if config is not None else ValidateConfig(), path) + def validate_aws_api_request( + self, request: AwsApiRequest, config: typing.Optional[ValidateConfig] = None + ) -> DetailedAwsApiRequestValidation: + """Classifies, models, and validates an AWS API request. + + This detailed variant is the primary integration entry point. A skipped + request has ``report is None`` and an explicit status and reason. + """ + return self.validate_aws_api_request_detailed(request, config) + + def validate_aws_api_request_standard( + self, request: AwsApiRequest, config: typing.Optional[ValidateConfig] = None + ) -> StandardAwsApiRequestValidation: + """Validates an AWS API request and returns standard diagnostics.""" + if not isinstance(request, AwsApiRequest): + raise TypeError("request must be an AwsApiRequest") + return self._inner.validate_aws_api_request_standard( + request._to_native(), config if config is not None else ValidateConfig() + ) + + def validate_aws_api_request_detailed( + self, request: AwsApiRequest, config: typing.Optional[ValidateConfig] = None + ) -> DetailedAwsApiRequestValidation: + """Validates an AWS API request and returns detailed diagnostics.""" + if not isinstance(request, AwsApiRequest): + raise TypeError("request must be an AwsApiRequest") + return self._inner.validate_aws_api_request_detailed( + request._to_native(), config if config is not None else ValidateConfig() + ) + def list_rules(self) -> typing.List[RuleInfo]: """Lists every rule this engine evaluates, sorted by rule ID.""" return self._inner.list_rules() diff --git a/src/bindings-python/src/lib.rs b/src/bindings-python/src/lib.rs index 84d2727b..e9da2086 100644 --- a/src/bindings-python/src/lib.rs +++ b/src/bindings-python/src/lib.rs @@ -22,7 +22,10 @@ pub use template_model::model::{ }; pub use template_model::resolver::{MapEntry, ParameterInfo, RefKind, ResolvedValue}; pub use template_model::{JsonValue, PseudoParameterOverrides, SourceSpan}; -pub use validation_engine::{EngineConfig, EngineType, ExternalRuleSource}; +pub use validation_engine::{ + AwsApiOperationKind, AwsApiRequestContext, AwsApiRequestValidationStatus, AwsApiTemplateSource, AwsApiValue, + DetailedAwsApiRequestValidation, EngineConfig, EngineType, ExternalRuleSource, StandardAwsApiRequestValidation, +}; pub use schema_validator::SchemaValidatorConfig; @@ -197,6 +200,48 @@ macro_rules! impl_py_engine { ) } + pub fn validate_aws_api_request_standard( + &self, + request: AwsApiRequestContext, + config: ValidateConfig, + ) -> Result { + validation_engine::catch_panics( + || { + let core_config = config.to_core(DetailLevel::Standard); + let validation = validation_engine::validate_aws_api_request( + &self.engine, + &self.schema_validator, + &request, + core_config, + ) + .map_err(|e| ValidationError::Engine { msg: e.to_string() })?; + Ok(validation.to_standard()) + }, + panic_to_error, + ) + } + + pub fn validate_aws_api_request_detailed( + &self, + request: AwsApiRequestContext, + config: ValidateConfig, + ) -> Result { + validation_engine::catch_panics( + || { + let core_config = config.to_core(DetailLevel::Detailed); + let validation = validation_engine::validate_aws_api_request( + &self.engine, + &self.schema_validator, + &request, + core_config, + ) + .map_err(|e| ValidationError::Engine { msg: e.to_string() })?; + Ok(validation.to_detailed()) + }, + panic_to_error, + ) + } + pub fn list_rules(&self) -> Result, ValidationError> { validation_engine::catch_panics(|| Ok(self.engine.list_rules()), panic_to_error) } diff --git a/src/bindings-python/tests/run.sh b/src/bindings-python/tests/run.sh index 2bd5a503..d3b8c62d 100755 --- a/src/bindings-python/tests/run.sh +++ b/src/bindings-python/tests/run.sh @@ -5,6 +5,12 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" BINDINGS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" WHEEL_DIR="$BINDINGS_DIR/generated/dist" VENV_DIR="$SCRIPT_DIR/.venv" +PYTHON="${PYTHON:-python3}" + +if ! command -v "$PYTHON" &>/dev/null; then + echo "Error: $PYTHON not found on PATH" >&2 + exit 1 +fi if ! compgen -G "$WHEEL_DIR/cloudformation_validate-*.whl" >/dev/null; then echo "Error: no wheel in $WHEEL_DIR - run build.sh first" >&2 @@ -15,7 +21,7 @@ fi # consumers install, not the loose build tree. echo "Installing the compatible wheel from $WHEEL_DIR into test venv..." rm -rf "$VENV_DIR" -python3 -m venv "$VENV_DIR" +"$PYTHON" -m venv "$VENV_DIR" if [ -x "$VENV_DIR/bin/python" ]; then VENV_PYTHON="$VENV_DIR/bin/python" else diff --git a/src/bindings-python/tests/smoke_test.py b/src/bindings-python/tests/smoke_test.py index 058a60e7..994b4a0a 100644 --- a/src/bindings-python/tests/smoke_test.py +++ b/src/bindings-python/tests/smoke_test.py @@ -15,6 +15,10 @@ import cloudformation_validate._native as native_loader from cloudformation_validate import ( AdditionalSchemaSource, + AwsApiOperationKind, + AwsApiRequest, + AwsApiRequestValidationStatus, + AwsApiTemplateSource, CelEngine, EngineConfig, EntityType, @@ -181,6 +185,82 @@ def test_unparseable_template_reports_error_status(self): self.assertTrue(report.diagnostics, "parse failure must surface as a diagnostic") +class AwsApiRequestValidationTest(unittest.TestCase): + def test_synthesized_create_validates_with_both_engines(self): + parameters = {"Bucket": "synthetic-bucket", "Tags": {"Team": "CLI"}} + request = AwsApiRequest( + "s3", + "CreateBucket", + parameters, + service_prefix="s3", + http_method="POST", + ) + results = [engine.validate_aws_api_request(request) for engine in (REGO, CEL)] + + for result in results: + self.assertEqual(AwsApiRequestValidationStatus.VALIDATED, result.status) + self.assertEqual(AwsApiOperationKind.CLOUD_FORMATION_CREATE, result.operation_kind) + self.assertEqual(AwsApiTemplateSource.SYNTHESIZED_CREATE, result.template_source) + self.assertEqual(["AWS::S3::Bucket"], result.resource_types) + self.assertIsNotNone(result.report) + self.assertEqual(diagnostic_keys(results[0].report), diagnostic_keys(results[1].report)) + self.assertEqual({"Bucket": "synthetic-bucket", "Tags": {"Team": "CLI"}}, parameters) + + def test_template_body_bytes_are_validated_exactly(self): + request = AwsApiRequest( + "cloudformation", + "CreateChangeSet", + {"TemplateBody": b'{"Resources":{}}'}, + service_prefix="cloudformation", + http_method="POST", + ) + + result = REGO.validate_aws_api_request(request) + + self.assertEqual(AwsApiRequestValidationStatus.VALIDATED, result.status) + self.assertEqual(AwsApiTemplateSource.TEMPLATE_BODY, result.template_source) + self.assertEqual(ReportStatus.OK, result.report.status) + + def test_read_only_request_reports_explicit_skip(self): + request = AwsApiRequest( + "iam", + "GetRole", + {"RoleName": "Synthetic"}, + service_prefix="iam", + http_method="POST", + ) + + result = REGO.validate_aws_api_request(request) + + self.assertEqual(AwsApiRequestValidationStatus.SKIPPED, result.status) + self.assertEqual(AwsApiOperationKind.READ_ONLY, result.operation_kind) + self.assertIsNone(result.report) + self.assertIn("read-only", result.reason) + + def test_partial_update_diagnostics_are_scoped_and_counts_match(self): + request = AwsApiRequest( + "lambda", + "UpdateFunctionConfiguration", + {"FunctionName": "Synthetic", "MemorySize": 0}, + service_prefix="lambda", + http_method="POST", + ) + + result = REGO.validate_aws_api_request(request) + report = result.report + + self.assertEqual(AwsApiTemplateSource.SYNTHESIZED_UPDATE, result.template_source) + self.assertTrue( + all(d.property_path and "MemorySize" in d.property_path for d in report.diagnostics), + report.diagnostics, + ) + counts = report.metadata.counts + self.assertEqual( + len(report.diagnostics), + counts.fatal + counts.errors + counts.warnings + counts.informational + counts.debug, + ) + + class AdditionalSchemasTest(unittest.TestCase): def test_additional_schemas_apply_through_the_public_config_on_both_engines(self): from cloudformation_validate import SchemaValidatorConfig diff --git a/src/data-source/build.rs b/src/data-source/build.rs index 6ffccacc..00fdc9dc 100644 --- a/src/data-source/build.rs +++ b/src/data-source/build.rs @@ -2,6 +2,7 @@ mod source_versions; use source_versions::{SOURCE_VERSIONS_FILE, SourceVersions}; +use std::collections::{BTreeMap, BTreeSet}; use std::env; use std::fs; use std::io::Cursor; @@ -79,9 +80,10 @@ fn main() { let generated_sv_dir = manifest_dir.join("generated").join("schema-validator"); let generated_cel_dir = manifest_dir.join("generated").join("cel-rules"); let handwritten_dir = manifest_dir.join("handwritten"); + let upstream_schema_dir = manifest_dir.join("upstream").join("schemas"); let rego_hw_dir = manifest_dir.parent().unwrap().join("rego-engine").join("handwritten").join("rego"); - for dir in [&generated_data_dir, &generated_sv_dir, &generated_cel_dir, &handwritten_dir] { + for dir in [&generated_data_dir, &generated_sv_dir, &generated_cel_dir, &handwritten_dir, &upstream_schema_dir] { println!("cargo:rerun-if-changed={}", dir.display()); } println!("cargo:rerun-if-changed={}", rego_hw_dir.display()); @@ -120,6 +122,9 @@ fn main() { embed_minified_json(&path, const_name, &out_dir, &mut code); } + let aws_api_actions = build_aws_api_action_catalog(&upstream_schema_dir); + embed_minified_value(&aws_api_actions, "AWS_API_ACTIONS", &out_dir, &mut code); + // CEL generated rules let cel_rules_path = generated_cel_dir.join("generated_rules.json"); if !cel_rules_path.exists() { @@ -141,6 +146,7 @@ fn main() { for (_filename, const_name) in GENERATED_JSON.iter().chain(HANDWRITTEN_JSON.iter()) { code.push_str(&format!(" let _ = &*{}_BYTES;\n", const_name)); } + code.push_str(" let _ = &*AWS_API_ACTIONS_BYTES;\n"); code.push_str(" let _ = &*GENERATED_RULES_BYTES;\n"); code.push_str("}\n"); @@ -180,13 +186,69 @@ fn assert_exists(path: &Path, _label: &str) { } } +/// Build an IAM action -> provider handler role -> CloudFormation resource type +/// catalog directly from the checked-in enhanced provider schemas. The schemas +/// remain the source of truth; this compact index exists only in Cargo's output +/// directory and is never checked in as a second metadata bundle. +fn build_aws_api_action_catalog(schema_dir: &Path) -> serde_json::Value { + let mut paths: Vec = fs::read_dir(schema_dir) + .unwrap_or_else(|error| panic!("failed to read enhanced schema directory {}: {error}", schema_dir.display())) + .map(|entry| { + entry.unwrap_or_else(|error| panic!("failed to read an entry in {}: {error}", schema_dir.display())).path() + }) + .filter(|path| path.extension().and_then(|extension| extension.to_str()) == Some("json")) + .collect(); + paths.sort(); + + let mut actions: BTreeMap>> = BTreeMap::new(); + for path in paths { + let raw = fs::read_to_string(&path) + .unwrap_or_else(|error| panic!("failed to read enhanced schema {}: {error}", path.display())); + let schema: serde_json::Value = serde_json::from_str(&raw) + .unwrap_or_else(|error| panic!("failed to parse enhanced schema {}: {error}", path.display())); + let Some(type_name) = schema.get("typeName").and_then(serde_json::Value::as_str) else { + continue; + }; + let Some(handlers) = schema.get("handlers").and_then(serde_json::Value::as_object) else { + continue; + }; + for role in ["create", "update", "delete", "read", "list"] { + let Some(permissions) = handlers + .get(role) + .and_then(serde_json::Value::as_object) + .and_then(|handler| handler.get("permissions")) + .and_then(serde_json::Value::as_array) + else { + continue; + }; + for action in permissions.iter().filter_map(serde_json::Value::as_str) { + if !action.contains(':') { + continue; + } + actions + .entry(action.to_ascii_lowercase()) + .or_default() + .entry(role.to_string()) + .or_default() + .insert(type_name.to_string()); + } + } + } + + serde_json::to_value(actions).expect("AWS API action catalog must serialize") +} + /// Minify JSON, compress with zstd level 9, and embed as /// `pub static NAME_BYTES: LazyLock>` that lazily decompresses on first access. /// Uses `ruzstd` (pure-Rust decoder) at runtime to keep WASM builds portable. fn embed_minified_json(path: &Path, const_name: &str, out_dir: &Path, code: &mut String) { let raw = fs::read_to_string(path).unwrap(); let value: serde_json::Value = serde_json::from_str(&raw).unwrap(); - let minified = serde_json::to_vec(&value).unwrap(); + embed_minified_value(&value, const_name, out_dir, code); +} + +fn embed_minified_value(value: &serde_json::Value, const_name: &str, out_dir: &Path, code: &mut String) { + let minified = serde_json::to_vec(value).unwrap(); let compressed = zstd::encode_all(Cursor::new(&minified), 9).unwrap(); let bin_path = out_dir.join(format!("{}.json.zst", const_name.to_lowercase())); diff --git a/src/schema-validator/src/lib.rs b/src/schema-validator/src/lib.rs index 62c0ac80..1bbd68f4 100644 --- a/src/schema-validator/src/lib.rs +++ b/src/schema-validator/src/lib.rs @@ -2,6 +2,7 @@ pub mod catalog; pub(crate) mod compiled; pub(crate) mod convert; pub mod overlay; +pub mod resource_schema; pub mod store; pub mod validate; @@ -11,6 +12,7 @@ uniffi::setup_scaffolding!(); pub use catalog::OverlayCatalog; pub use data_source::{AdditionalSchemaSource, SchemaSourceError}; pub use overlay::{MAX_OVERLAY_DEPTH, SchemaOverlayError}; +pub use resource_schema::{PropertyValueType, ResourceSchemaMetadata}; pub use store::{CompiledSchemaStore, OverlayOutcome}; /// Eagerly decompress all embedded data LazyLocks. Intended to be called once at @@ -278,6 +280,23 @@ impl SchemaValidator { self.store.len() } + /// Returns the schema fields needed to map request parameters to one + /// CloudFormation resource type, including configured schema overlays. + pub fn resource_schema_metadata(&self, type_name: &str) -> Option { + self.store.get(type_name).map(ResourceSchemaMetadata::from_compiled) + } + + /// Whether this validator has a bundled or caller-provided schema for a + /// CloudFormation resource type. + pub fn has_resource_type(&self, type_name: &str) -> bool { + self.store.get(type_name).is_some() + } + + /// Iterates every bundled and caller-provided CloudFormation resource type. + pub fn resource_type_names(&self) -> impl Iterator { + self.store.type_names() + } + pub fn list_rules(&self) -> Vec { // Every rule ID the schema-validator can emit (see src/validate.rs). const SCHEMA_RULE_IDS: &[&str] = &[ diff --git a/src/schema-validator/src/resource_schema.rs b/src/schema-validator/src/resource_schema.rs new file mode 100644 index 00000000..9cae198a --- /dev/null +++ b/src/schema-validator/src/resource_schema.rs @@ -0,0 +1,174 @@ +use crate::compiled::{CompiledSchema, PropSchema}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; + +/// JSON value categories accepted by a CloudFormation resource property. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum PropertyValueType { + Any, + Array, + Object, + Boolean, + Integer, + Number, + String, +} + +/// Schema information needed to map an AWS API request into one resource. +/// +/// This is intentionally narrower than the validator's compiled schema model: +/// callers can select and type-check resource properties without depending on +/// validation implementation details. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResourceSchemaMetadata { + pub type_name: String, + pub property_types: BTreeMap>, + pub required_properties: BTreeSet, + pub read_only_properties: BTreeSet, + pub primary_identifier_properties: BTreeSet, +} + +impl ResourceSchemaMetadata { + pub(crate) fn from_compiled(schema: &CompiledSchema) -> Self { + let property_types = schema + .properties + .iter() + .map(|(name, property)| (name.clone(), accepted_value_types(property, &schema.definitions))) + .collect(); + Self { + type_name: schema.type_name.clone(), + property_types, + required_properties: schema.required.iter().cloned().collect(), + read_only_properties: schema.read_only_properties.iter().cloned().collect(), + primary_identifier_properties: schema.primary_identifier.iter().cloned().collect(), + } + } +} + +const MAX_COMPOSITION_DEPTH: usize = 64; + +fn accepted_value_types( + property: &PropSchema, + definitions: &HashMap, +) -> BTreeSet { + let mut accepted = BTreeSet::new(); + collect_value_types(property, definitions, 0, &mut accepted); + accepted.remove(&PropertyValueType::Any); + if accepted.is_empty() { + accepted.insert(PropertyValueType::Any); + } + accepted +} + +fn collect_value_types( + property: &PropSchema, + definitions: &HashMap, + depth: usize, + accepted: &mut BTreeSet, +) { + if depth >= MAX_COMPOSITION_DEPTH { + accepted.insert(PropertyValueType::Any); + return; + } + + let property = property.resolve(definitions); + if let Some(property_type) = &property.prop_type { + for name in property_type.names() { + match name { + "array" => { + accepted.insert(PropertyValueType::Array); + } + "object" => { + accepted.insert(PropertyValueType::Object); + } + "boolean" => { + accepted.insert(PropertyValueType::Boolean); + } + "integer" => { + accepted.insert(PropertyValueType::Integer); + } + "number" => { + accepted.insert(PropertyValueType::Number); + } + "string" => { + accepted.insert(PropertyValueType::String); + } + "null" => {} + _ => { + accepted.insert(PropertyValueType::Any); + } + } + } + } + if property.items.is_some() || property.min_items.is_some() || property.max_items.is_some() { + accepted.insert(PropertyValueType::Array); + } + if !property.properties.is_empty() + || !property.pattern_properties.is_empty() + || property.additional_properties.is_some() + || property.min_properties.is_some() + || property.max_properties.is_some() + { + accepted.insert(PropertyValueType::Object); + } + for alternative in property.all_of.iter().chain(property.any_of.iter()).chain(property.one_of.iter()) { + collect_value_types(alternative, definitions, depth + 1, accepted); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::compiled::PropType; + + #[test] + fn metadata_resolves_referenced_property_types() { + let mut definitions = HashMap::new(); + definitions.insert( + "Configuration".to_string(), + PropSchema { prop_type: Some(PropType::Single("object".into())), ..Default::default() }, + ); + let schema = CompiledSchema { + type_name: "AWS::Test::Thing".into(), + properties: HashMap::from([( + "Configuration".into(), + PropSchema { ref_name: Some("Configuration".into()), ..Default::default() }, + )]), + definitions, + required: vec!["Configuration".into()], + read_only_properties: vec!["Arn".into()], + primary_identifier: vec!["Name".into()], + ..Default::default() + }; + + let metadata = ResourceSchemaMetadata::from_compiled(&schema); + + assert_eq!(metadata.property_types["Configuration"], BTreeSet::from([PropertyValueType::Object])); + assert!(metadata.required_properties.contains("Configuration")); + assert!(metadata.read_only_properties.contains("Arn")); + assert!(metadata.primary_identifier_properties.contains("Name")); + } + + #[test] + fn metadata_unions_composed_property_types() { + let property = PropSchema { + one_of: vec![ + PropSchema { prop_type: Some(PropType::Single("string".into())), ..Default::default() }, + PropSchema { prop_type: Some(PropType::Single("integer".into())), ..Default::default() }, + ], + ..Default::default() + }; + + assert_eq!( + accepted_value_types(&property, &HashMap::new()), + BTreeSet::from([PropertyValueType::Integer, PropertyValueType::String]) + ); + } + + #[test] + fn metadata_uses_any_when_no_value_type_is_known() { + assert_eq!( + accepted_value_types(&PropSchema::default(), &HashMap::new()), + BTreeSet::from([PropertyValueType::Any]) + ); + } +} diff --git a/src/schema-validator/src/store.rs b/src/schema-validator/src/store.rs index 7c06fc5c..f830969d 100644 --- a/src/schema-validator/src/store.rs +++ b/src/schema-validator/src/store.rs @@ -85,6 +85,10 @@ impl CompiledSchemaStore { self.schemas.get(type_name) } + pub fn type_names(&self) -> impl Iterator { + self.schemas.keys().map(String::as_str) + } + /// Merge an overlay CloudFormation resource provider schema (raw registry /// JSON) into the store under `type_name`. /// diff --git a/src/validation-engine/API.md b/src/validation-engine/API.md index abcffdbc..ab3599c6 100644 --- a/src/validation-engine/API.md +++ b/src/validation-engine/API.md @@ -33,6 +33,57 @@ for d in &report.diagnostics { On parse failure, `validate_bytes_with_path` returns `Ok(report)` with a synthetic `F1101` diagnostic and `status=Error` rather than returning `Err`. This ensures callers always get a structured report. +## Validating an AWS API Request + +`validate_aws_api_request` accepts raw service, operation, HTTP, trait, and request-parameter context. It owns operation +classification, CloudFormation resource-type selection, request-to-template modeling, schema-backed property mapping, +and partial-update diagnostic scoping: + +```rust +use rego_engine::RegoEngine; +use schema_validator::SchemaValidator; +use validation_engine::{ + AwsApiRequest, AwsApiValue, EngineConfig, ValidateConfig, validate_aws_api_request, +}; + +let engine = RegoEngine::new(EngineConfig::default())?; +let schema_validator = SchemaValidator::default(); +let request = AwsApiRequest::new( + "s3", + "CreateBucket", + [ + ("Bucket".into(), AwsApiValue::String { value: "example-bucket".into() }), + ("Tags".into(), AwsApiValue::Object { + entries: [("Team".into(), AwsApiValue::String { value: "Platform".into() })] + .into_iter() + .collect(), + }), + ], +) +.with_service_prefix("s3") +.with_http_method("PUT"); + +let result = validate_aws_api_request( + &engine, + &schema_validator, + &request, + ValidateConfig::default(), +)?; +if let Some(report) = result.report { + for diagnostic in report.diagnostics { + println!("{}: {}", diagnostic.rule_id, diagnostic.message); + } +} else { + println!("{:?}: {}", result.status, result.reason); +} +``` + +`AwsApiValue` preserves bytes and 64-bit integer widths and explicitly marks unsupported values. Exact `TemplateBody` +bytes are validated without rewriting; `TemplateURL` is skipped because validation is offline. Every result includes +an operation kind, validation status, optional template source, resource candidates, and reason. `Validated` means the +modeled template reached the normal validation pipeline; `Skipped` has no report and explains why. Use +`validate_aws_api_request_with_path` when the embedding application needs a custom report path. + ## Constructing an Engine Both engines take a single `EngineConfig` and return `anyhow::Result`: diff --git a/src/validation-engine/src/aws_api.rs b/src/validation-engine/src/aws_api.rs new file mode 100644 index 00000000..578b8f84 --- /dev/null +++ b/src/validation-engine/src/aws_api.rs @@ -0,0 +1,1327 @@ +use data_source::embedded::AWS_API_ACTIONS_BYTES; +use diagnostics::{DetailedReport, StandardReport, Summary, ValidationReport}; +use rules::Severity; +use schema_validator::{PropertyValueType, ResourceSchemaMetadata, SchemaValidator}; +use serde::{Deserialize, Serialize}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::sync::LazyLock; + +use crate::{ValidateConfig, ValidationEngine, ValidationError, validate_bytes_with_path}; + +/// A recursively typed value from an AWS API request. +/// +/// Unlike JSON, this model preserves byte strings such as CloudFormation's +/// `TemplateBody`. `Unsupported` lets language bindings carry an explicit marker +/// for a runtime value they cannot represent rather than coercing it silently. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Enum))] +#[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum AwsApiValue { + Null, + Boolean { value: bool }, + Integer { value: i64 }, + UnsignedInteger { value: u64 }, + Number { value: f64 }, + String { value: String }, + Bytes { value: Vec }, + Array { items: Vec }, + Object { entries: HashMap }, + Unsupported { type_name: String }, +} + +impl AwsApiValue { + /// Converts a JSON value without losing integer width. + pub fn from_json(value: serde_json::Value) -> Self { + match value { + serde_json::Value::Null => Self::Null, + serde_json::Value::Bool(value) => Self::Boolean { value }, + serde_json::Value::Number(value) => { + if let Some(value) = value.as_i64() { + Self::Integer { value } + } else if let Some(value) = value.as_u64() { + Self::UnsignedInteger { value } + } else if let Some(value) = value.as_f64() { + Self::Number { value } + } else { + Self::Unsupported { type_name: "JSON number".into() } + } + } + serde_json::Value::String(value) => Self::String { value }, + serde_json::Value::Array(items) => Self::Array { items: items.into_iter().map(Self::from_json).collect() }, + serde_json::Value::Object(entries) => Self::Object { + entries: entries.into_iter().map(|(key, value)| (key, Self::from_json(value))).collect(), + }, + } + } + + /// Converts to JSON when this value and all of its children are JSON-safe. + pub fn to_json(&self) -> Result { + self.json_value().ok_or_else(|| match self { + Self::Bytes { .. } => "byte strings are not JSON values".to_string(), + Self::Number { .. } => "non-finite numbers are not JSON values".to_string(), + Self::Unsupported { type_name } => format!("{type_name} is not a supported request value"), + _ => "a nested request value is not JSON-compatible".to_string(), + }) + } + + fn json_value(&self) -> Option { + match self { + Self::Null => Some(serde_json::Value::Null), + Self::Boolean { value } => Some(serde_json::Value::Bool(*value)), + Self::Integer { value } => Some(serde_json::json!(value)), + Self::UnsignedInteger { value } => Some(serde_json::json!(value)), + Self::Number { value } => serde_json::Number::from_f64(*value).map(serde_json::Value::Number), + Self::String { value } => Some(serde_json::Value::String(value.clone())), + Self::Bytes { .. } | Self::Unsupported { .. } => None, + Self::Array { items } => { + items.iter().map(Self::json_value).collect::>>().map(serde_json::Value::Array) + } + Self::Object { entries } => entries + .iter() + .map(|(key, value)| value.json_value().map(|value| (key.clone(), value))) + .collect::>>() + .map(serde_json::Value::Object), + } + } +} + +impl From for AwsApiValue { + fn from(value: serde_json::Value) -> Self { + Self::from_json(value) + } +} + +/// AWS service, operation, and input values needed to model one API request as +/// CloudFormation resource state. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))] +#[serde(rename_all = "camelCase")] +pub struct AwsApiRequestContext { + pub service_name: String, + pub operation_name: String, + pub parameters: HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + #[cfg_attr(feature = "uniffi-bindings", uniffi(default))] + pub service_prefix: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + #[cfg_attr(feature = "uniffi-bindings", uniffi(default))] + pub http_method: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + #[cfg_attr(feature = "uniffi-bindings", uniffi(default))] + pub is_read_only: Option, +} + +/// Idiomatic Rust name for the AWS API request context record. +pub type AwsApiRequest = AwsApiRequestContext; + +impl AwsApiRequestContext { + pub fn new( + service_name: impl Into, + operation_name: impl Into, + parameters: impl IntoIterator, + ) -> Self { + Self { + service_name: service_name.into(), + operation_name: operation_name.into(), + parameters: parameters.into_iter().collect(), + service_prefix: None, + http_method: None, + is_read_only: None, + } + } + + pub fn with_service_prefix(mut self, service_prefix: impl Into) -> Self { + self.service_prefix = Some(service_prefix.into()); + self + } + + pub fn with_http_method(mut self, http_method: impl Into) -> Self { + self.http_method = Some(http_method.into()); + self + } + + pub fn with_read_only(mut self, is_read_only: bool) -> Self { + self.is_read_only = Some(is_read_only); + self + } + + fn effective_service_prefix(&self) -> &str { + self.service_prefix.as_deref().filter(|prefix| !prefix.is_empty()).unwrap_or(&self.service_name) + } + + fn default_file_path(&self) -> String { + format!("aws-api://{}/{}", self.service_name, self.operation_name) + } +} + +/// Closed classification of an AWS API operation. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Enum))] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum AwsApiOperationKind { + ReadOnly, + CloudFormationCreate, + CloudFormationUpdate, + CloudFormationDelete, + DataPlaneMutation, + UnmappedMutation, +} + +/// Whether a request reached template validation or was conservatively skipped. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Enum))] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum AwsApiRequestValidationStatus { + Validated, + Skipped, +} + +/// Provenance of the template validated for an AWS API request. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Enum))] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum AwsApiTemplateSource { + TemplateBody, + CloudControlDesiredState, + SynthesizedCreate, + SynthesizedUpdate, +} + +/// Full Rust result for AWS API request validation. +#[derive(Debug, Clone)] +#[must_use] +pub struct AwsApiRequestValidation { + pub operation_kind: AwsApiOperationKind, + pub status: AwsApiRequestValidationStatus, + pub template_source: Option, + pub resource_types: Vec, + pub reason: String, + pub report: Option, +} + +/// AWS API request result containing standard diagnostics. +#[derive(Debug, Clone, Serialize)] +#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))] +#[serde(rename_all = "camelCase")] +pub struct StandardAwsApiRequestValidation { + pub operation_kind: AwsApiOperationKind, + pub status: AwsApiRequestValidationStatus, + pub template_source: Option, + pub resource_types: Vec, + pub reason: String, + pub report: Option, +} + +/// AWS API request result containing detailed diagnostics and context. +#[derive(Debug, Clone, Serialize)] +#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))] +#[serde(rename_all = "camelCase")] +pub struct DetailedAwsApiRequestValidation { + pub operation_kind: AwsApiOperationKind, + pub status: AwsApiRequestValidationStatus, + pub template_source: Option, + pub resource_types: Vec, + pub reason: String, + pub report: Option, +} + +impl AwsApiRequestValidation { + pub fn to_standard(&self) -> StandardAwsApiRequestValidation { + StandardAwsApiRequestValidation { + operation_kind: self.operation_kind, + status: self.status, + template_source: self.template_source, + resource_types: self.resource_types.clone(), + reason: self.reason.clone(), + report: self.report.as_ref().map(ValidationReport::to_standard), + } + } + + pub fn to_detailed(&self) -> DetailedAwsApiRequestValidation { + DetailedAwsApiRequestValidation { + operation_kind: self.operation_kind, + status: self.status, + template_source: self.template_source, + resource_types: self.resource_types.clone(), + reason: self.reason.clone(), + report: self.report.as_ref().map(ValidationReport::to_detailed), + } + } +} + +/// Classifies, models, and validates one AWS API request entirely offline. +pub fn validate_aws_api_request( + engine: &dyn ValidationEngine, + schema_validator: &SchemaValidator, + request: &AwsApiRequest, + config: ValidateConfig, +) -> Result { + validate_aws_api_request_with_path(engine, schema_validator, request, config, request.default_file_path()) +} + +/// Same as [`validate_aws_api_request`], with an explicit report path supplied +/// by the embedding application. +pub fn validate_aws_api_request_with_path( + engine: &dyn ValidationEngine, + schema_validator: &SchemaValidator, + request: &AwsApiRequest, + config: ValidateConfig, + file_path: String, +) -> Result { + let catalog = action_catalog()?; + let classification = classify_operation(request, schema_validator, catalog); + let synthesis = synthesize_request(request, &classification, schema_validator)?; + let Some(template) = synthesis.template else { + return Ok(AwsApiRequestValidation { + operation_kind: classification.kind, + status: AwsApiRequestValidationStatus::Skipped, + template_source: None, + resource_types: synthesis.resource_types, + reason: synthesis.reason, + report: None, + }); + }; + + let mut report = validate_bytes_with_path(engine, schema_validator, &template, config, file_path)?; + if let Some(properties) = synthesis.diagnostic_properties.as_ref() { + scope_partial_update_report(&mut report, properties); + } + Ok(AwsApiRequestValidation { + operation_kind: classification.kind, + status: AwsApiRequestValidationStatus::Validated, + template_source: synthesis.source, + resource_types: synthesis.resource_types, + reason: synthesis.reason, + report: Some(report), + }) +} + +type HandlerRoles = HashMap>; +type ActionCatalog = HashMap; + +static ACTION_CATALOG: LazyLock> = LazyLock::new(|| { + serde_json::from_slice(&AWS_API_ACTIONS_BYTES) + .map_err(|error| format!("embedded AWS API action catalog is invalid: {error}")) +}); + +fn action_catalog() -> Result<&'static ActionCatalog, ValidationError> { + ACTION_CATALOG.as_ref().map_err(|message| ValidationError::Engine(message.clone())) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum OperationPhase { + Read, + Create, + Update, + Delete, + Data, + Unknown, +} + +impl OperationPhase { + fn handler_role(self) -> Option<&'static str> { + match self { + Self::Create => Some("create"), + Self::Update => Some("update"), + Self::Delete => Some("delete"), + _ => None, + } + } +} + +#[derive(Debug, Clone)] +struct Classification { + kind: AwsApiOperationKind, + phase: OperationPhase, + candidates: Vec, +} + +const MODIFIER_PREFIXES: &[&str] = &["Admin", "Batch", "Bulk", "Transact"]; +const READ_VERBS: &[&str] = &[ + "Calculate", + "Check", + "Compare", + "Contains", + "Count", + "Decode", + "Describe", + "Discover", + "Estimate", + "Filter", + "Find", + "Forecast", + "Get", + "Head", + "Is", + "List", + "Lookup", + "Preview", + "Query", + "Read", + "Resolve", + "Retrieve", + "Sample", + "Scan", + "Search", + "Select", + "Simulate", + "Validate", + "Verify", + "View", +]; +const CREATE_VERBS: &[&str] = &[ + "Add", + "Allocate", + "Build", + "Clone", + "Copy", + "Create", + "Define", + "Deploy", + "Import", + "Index", + "Initialize", + "Install", + "Instantiate", + "Invite", + "Issue", + "Join", + "Launch", + "Provision", + "Purchase", + "Register", + "Request", + "Restore", + "Run", + "Schedule", + "Start", + "Submit", +]; +const UPDATE_VERBS: &[&str] = &[ + "Accept", + "Activate", + "Apply", + "Approve", + "Assign", + "Associate", + "Attach", + "Authorize", + "Change", + "Configure", + "Connect", + "Deactivate", + "Decrease", + "Disable", + "Disassociate", + "Dissociate", + "Detach", + "Enable", + "Grant", + "Increase", + "Link", + "Lock", + "Merge", + "Modify", + "Move", + "Promote", + "Put", + "Reboot", + "Refresh", + "Replace", + "Reset", + "Resize", + "Restart", + "Resume", + "Rotate", + "Set", + "Share", + "Subscribe", + "Suspend", + "Swap", + "Tag", + "Transfer", + "Unassign", + "Unlock", + "Unshare", + "Unsubscribe", + "Untag", + "Update", + "Upgrade", +]; +const DELETE_VERBS: &[&str] = &[ + "Abort", + "Block", + "Cancel", + "Close", + "Decline", + "Delete", + "Deny", + "Deprovision", + "Deregister", + "Destroy", + "Discard", + "Dispose", + "Expire", + "Forget", + "Leave", + "Purge", + "Reject", + "Release", + "Remove", + "Retire", + "Revoke", + "Shutdown", + "Stop", + "Terminate", + "Unregister", +]; +const DATA_PLANE_VERBS: &[&str] = &[ + "Analyze", + "Chat", + "Complete", + "Convert", + "Converse", + "Decrypt", + "Deliver", + "Encrypt", + "Execute", + "Generate", + "Infer", + "Invoke", + "Meter", + "Notify", + "Post", + "Predict", + "Publish", + "Receive", + "Recognize", + "Render", + "Respond", + "Send", + "Signal", + "Sign", + "Stream", + "Synthesize", + "Test", + "Translate", + "Upload", + "Write", +]; +const DATA_PLANE_IF_UNMAPPED_VERBS: &[&str] = + &["Execute", "Invoke", "Post", "Publish", "Put", "Send", "Upload", "Write"]; + +fn classify_operation( + request: &AwsApiRequest, + schema_validator: &SchemaValidator, + catalog: &ActionCatalog, +) -> Classification { + let prefix = request.effective_service_prefix(); + let words = operation_words(&request.operation_name); + let verb = effective_verb(&words); + let action_key = format!("{prefix}:{}", request.operation_name).to_ascii_lowercase(); + let action_roles = catalog.get(&action_key); + let phase = operation_phase(request, action_roles, verb); + + match phase { + OperationPhase::Read => Classification { kind: AwsApiOperationKind::ReadOnly, phase, candidates: Vec::new() }, + OperationPhase::Data => { + Classification { kind: AwsApiOperationKind::DataPlaneMutation, phase, candidates: Vec::new() } + } + OperationPhase::Create | OperationPhase::Update | OperationPhase::Delete => { + let candidates = + explicit_resource_type(request, schema_validator).map(|type_name| vec![type_name]).unwrap_or_else( + || candidate_types(schema_validator, action_roles, phase, prefix, operation_noun(&words)), + ); + if candidates.is_empty() { + let is_data_plane = DATA_PLANE_IF_UNMAPPED_VERBS.contains(&verb); + Classification { + kind: if is_data_plane { + AwsApiOperationKind::DataPlaneMutation + } else { + AwsApiOperationKind::UnmappedMutation + }, + phase: if is_data_plane { OperationPhase::Data } else { phase }, + candidates, + } + } else { + let kind = match phase { + OperationPhase::Create => AwsApiOperationKind::CloudFormationCreate, + OperationPhase::Update => AwsApiOperationKind::CloudFormationUpdate, + OperationPhase::Delete => AwsApiOperationKind::CloudFormationDelete, + _ => AwsApiOperationKind::UnmappedMutation, + }; + Classification { kind, phase, candidates } + } + } + OperationPhase::Unknown => { + Classification { kind: AwsApiOperationKind::UnmappedMutation, phase, candidates: Vec::new() } + } + } +} + +fn operation_phase(request: &AwsApiRequest, action_roles: Option<&HandlerRoles>, verb: &str) -> OperationPhase { + if request.is_read_only == Some(true) || READ_VERBS.contains(&verb) { + return OperationPhase::Read; + } + if DATA_PLANE_VERBS.contains(&verb) { + return OperationPhase::Data; + } + if CREATE_VERBS.contains(&verb) { + return OperationPhase::Create; + } + if UPDATE_VERBS.contains(&verb) { + return OperationPhase::Update; + } + if DELETE_VERBS.contains(&verb) { + return OperationPhase::Delete; + } + match request.http_method.as_deref().map(str::to_ascii_uppercase).as_deref() { + Some("GET" | "HEAD") => return OperationPhase::Read, + Some("DELETE") => return OperationPhase::Delete, + _ => {} + } + action_roles.and_then(phase_from_roles).unwrap_or(OperationPhase::Unknown) +} + +fn phase_from_roles(action_roles: &HandlerRoles) -> Option { + if action_roles.contains_key("read") || action_roles.contains_key("list") { + return None; + } + let write_roles: Vec<&str> = + ["create", "update", "delete"].into_iter().filter(|role| action_roles.contains_key(*role)).collect(); + match write_roles.as_slice() { + ["create"] => Some(OperationPhase::Create), + ["update"] => Some(OperationPhase::Update), + ["delete"] => Some(OperationPhase::Delete), + _ => None, + } +} + +fn operation_words(operation_name: &str) -> Vec { + let characters: Vec = operation_name.chars().collect(); + if characters.is_empty() { + return Vec::new(); + } + let mut words = Vec::new(); + let mut start = 0; + for index in 1..characters.len() { + let previous = characters[index - 1]; + let current = characters[index]; + let next = characters.get(index + 1).copied(); + let boundary = (current.is_ascii_digit() && !previous.is_ascii_digit()) + || (!current.is_ascii_digit() && previous.is_ascii_digit()) + || (current.is_ascii_uppercase() && previous.is_ascii_lowercase()) + || (current.is_ascii_uppercase() + && previous.is_ascii_uppercase() + && next.is_some_and(|next| next.is_ascii_lowercase())); + if boundary { + words.push(characters[start..index].iter().collect()); + start = index; + } + } + words.push(characters[start..].iter().collect()); + words +} + +fn effective_verb(words: &[String]) -> &str { + if words.len() > 1 && MODIFIER_PREFIXES.contains(&words[0].as_str()) { + &words[1] + } else { + words.first().map(String::as_str).unwrap_or("") + } +} + +fn operation_noun(words: &[String]) -> String { + let words = + if words.first().is_some_and(|word| MODIFIER_PREFIXES.contains(&word.as_str())) { &words[1..] } else { words }; + words.iter().skip(1).map(String::as_str).collect() +} + +fn explicit_resource_type(request: &AwsApiRequest, schema_validator: &SchemaValidator) -> Option { + match request.parameters.get("TypeName") { + Some(AwsApiValue::String { value }) if schema_validator.has_resource_type(value) => Some(value.clone()), + _ => None, + } +} + +fn candidate_types( + schema_validator: &SchemaValidator, + action_roles: Option<&HandlerRoles>, + phase: OperationPhase, + prefix: &str, + noun: String, +) -> Vec { + let mut role_candidates = BTreeSet::new(); + if let (Some(action_roles), Some(role)) = (action_roles, phase.handler_role()) { + if let Some(candidates) = action_roles.get(role) { + role_candidates + .extend(candidates.iter().filter(|candidate| schema_validator.has_resource_type(candidate)).cloned()); + } + if phase == OperationPhase::Update + && let Some(candidates) = action_roles.get("create") + { + role_candidates + .extend(candidates.iter().filter(|candidate| schema_validator.has_resource_type(candidate)).cloned()); + } + } + + let mut resource_candidates = role_candidates.clone(); + resource_candidates.extend( + schema_validator + .resource_type_names() + .filter(|type_name| score_candidate(type_name, prefix, &noun) > 0) + .map(str::to_string), + ); + let scores: BTreeMap = resource_candidates + .into_iter() + .map(|type_name| { + let score = score_candidate(&type_name, prefix, &noun); + (type_name, score) + }) + .collect(); + let best_score = scores.values().copied().max().unwrap_or(0); + if best_score < 120 { + return Vec::new(); + } + scores.into_iter().filter_map(|(type_name, score)| (score == best_score).then_some(type_name)).collect() +} + +fn normalize(value: &str) -> String { + value.chars().filter(char::is_ascii_alphanumeric).flat_map(char::to_lowercase).collect() +} + +fn namespace_score(namespace: &str, prefix: &str) -> u32 { + let namespace = normalize(namespace); + let prefix = normalize(prefix); + if namespace == prefix { + 100 + } else if !namespace.is_empty() + && !prefix.is_empty() + && (namespace.contains(&prefix) || prefix.contains(&namespace)) + { + 70 + } else { + 0 + } +} + +fn resource_score(resource_name: &str, noun: &str) -> u32 { + let resource = normalize(resource_name); + let noun = normalize(noun); + if resource.is_empty() || noun.is_empty() { + 0 + } else if resource == noun { + 100 + } else if noun.contains(&resource) { + 40 + (40 * resource.len() / noun.len()) as u32 + } else if resource.contains(&noun) { + 30 + (30 * noun.len() / resource.len()) as u32 + } else { + 0 + } +} + +fn score_candidate(type_name: &str, prefix: &str, noun: &str) -> u32 { + let parts: Vec<&str> = type_name.split("::").collect(); + if parts.len() != 3 { + return 0; + } + let namespace = namespace_score(parts[1], prefix); + let resource = resource_score(parts[2], noun); + if namespace == 0 || resource == 0 { 0 } else { namespace + resource } +} + +struct Synthesis { + template: Option>, + source: Option, + reason: String, + resource_types: Vec, + diagnostic_properties: Option>, +} + +impl Synthesis { + fn skipped(reason: impl Into, resource_types: Vec) -> Self { + Self { template: None, source: None, reason: reason.into(), resource_types, diagnostic_properties: None } + } +} + +fn synthesize_request( + request: &AwsApiRequest, + classification: &Classification, + schema_validator: &SchemaValidator, +) -> Result { + if classification.kind == AwsApiOperationKind::ReadOnly { + return Ok(Synthesis::skipped("read-only calls do not need validation", Vec::new())); + } + if let Some(template) = template_body_bytes(request.parameters.get("TemplateBody")) { + return Ok(Synthesis { + template: Some(template), + source: Some(AwsApiTemplateSource::TemplateBody), + reason: "using exact request TemplateBody".into(), + resource_types: Vec::new(), + diagnostic_properties: None, + }); + } + if request.parameters.contains_key("TemplateURL") { + return Ok(Synthesis::skipped("TemplateURL content is unavailable to the offline validator", Vec::new())); + } + if request.parameters.contains_key("TypeName") && request.parameters.contains_key("DesiredState") { + return desired_state_template(request, schema_validator); + } + generic_template(request, classification, schema_validator) +} + +fn template_body_bytes(value: Option<&AwsApiValue>) -> Option> { + match value { + Some(AwsApiValue::Bytes { value }) if !value.is_empty() => Some(value.clone()), + Some(AwsApiValue::String { value }) if !value.is_empty() => Some(value.as_bytes().to_vec()), + _ => None, + } +} + +fn desired_state_template( + request: &AwsApiRequest, + schema_validator: &SchemaValidator, +) -> Result { + let Some(AwsApiValue::String { value: type_name }) = request.parameters.get("TypeName") else { + return Ok(Synthesis::skipped("DesiredState has no known CloudFormation TypeName", Vec::new())); + }; + if !schema_validator.has_resource_type(type_name) { + return Ok(Synthesis::skipped("DesiredState has no known CloudFormation TypeName", Vec::new())); + } + let Some(desired_state) = request.parameters.get("DesiredState") else { + return Ok(Synthesis::skipped("DesiredState is missing", vec![type_name.clone()])); + }; + let properties = match desired_state { + AwsApiValue::String { value } if !value.is_empty() => serde_json::from_str(value), + AwsApiValue::Bytes { value } if !value.is_empty() => serde_json::from_slice(value), + _ => return Ok(Synthesis::skipped("DesiredState is missing", vec![type_name.clone()])), + }; + let properties: serde_json::Value = match properties { + Ok(properties) => properties, + Err(_) => return Ok(Synthesis::skipped("DesiredState is not valid JSON", vec![type_name.clone()])), + }; + let Some(properties) = properties.as_object() else { + return Ok(Synthesis::skipped("DesiredState is not a JSON object", vec![type_name.clone()])); + }; + Ok(Synthesis { + template: Some(resource_template(type_name, properties)?), + source: Some(AwsApiTemplateSource::CloudControlDesiredState), + reason: "wrapped exact Cloud Control desired state".into(), + resource_types: vec![type_name.clone()], + diagnostic_properties: None, + }) +} + +fn generic_template( + request: &AwsApiRequest, + classification: &Classification, + schema_validator: &SchemaValidator, +) -> Result { + if !matches!( + classification.kind, + AwsApiOperationKind::CloudFormationCreate | AwsApiOperationKind::CloudFormationUpdate + ) { + return Ok(Synthesis::skipped( + "classification has no representable resource state", + classification.candidates.clone(), + )); + } + if classification.candidates.len() != 1 { + return Ok(Synthesis::skipped( + "CloudFormation resource candidate is ambiguous", + classification.candidates.clone(), + )); + } + let type_name = &classification.candidates[0]; + let Some(schema) = schema_validator.resource_schema_metadata(type_name) else { + return Ok(Synthesis::skipped("CloudFormation resource candidate is unknown", vec![type_name.clone()])); + }; + let properties = match map_properties(&request.parameters, &schema, classification.phase) { + Ok(properties) => properties, + Err(reason) => return Ok(Synthesis::skipped(reason, vec![type_name.clone()])), + }; + let diagnostic_properties = + (classification.phase == OperationPhase::Update).then(|| properties.keys().cloned().collect::>()); + let source = if classification.phase == OperationPhase::Update { + AwsApiTemplateSource::SynthesizedUpdate + } else { + AwsApiTemplateSource::SynthesizedCreate + }; + let reason = if classification.phase == OperationPhase::Update { + "synthesized explicitly updated CloudFormation properties" + } else { + "synthesized one unambiguous CloudFormation resource" + }; + let properties: serde_json::Map = properties.into_iter().collect(); + Ok(Synthesis { + template: Some(resource_template(type_name, &properties)?), + source: Some(source), + reason: reason.into(), + resource_types: vec![type_name.clone()], + diagnostic_properties, + }) +} + +fn resource_template( + type_name: &str, + properties: &serde_json::Map, +) -> Result, ValidationError> { + serde_json::to_vec(&serde_json::json!({ + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "Resource": { + "Type": type_name, + "Properties": properties, + } + } + })) + .map_err(|error| ValidationError::Engine(format!("failed to serialize synthesized template: {error}"))) +} + +fn map_properties( + parameters: &HashMap, + schema: &ResourceSchemaMetadata, + phase: OperationPhase, +) -> Result, String> { + let resource_name = schema.type_name.rsplit("::").next().unwrap_or(&schema.type_name); + let mut excluded = schema.read_only_properties.clone(); + if phase == OperationPhase::Update { + excluded.extend(schema.primary_identifier_properties.iter().cloned()); + } + let mut mapped = BTreeMap::new(); + let mut parameters: Vec<(&String, &AwsApiValue)> = parameters.iter().collect(); + parameters.sort_by_key(|(name, _)| name.as_str()); + + for (parameter_name, value) in parameters { + let matches: Vec<&String> = schema + .property_types + .keys() + .filter(|property_name| { + !excluded.contains(*property_name) && property_matches(parameter_name, property_name, resource_name) + }) + .collect(); + if matches.len() > 1 { + return Err(format!("parameter {parameter_name} maps to multiple resource properties")); + } + let Some(property_name) = matches.first().copied() else { + continue; + }; + if mapped.contains_key(property_name) { + return Err(format!("multiple parameters map to {property_name}")); + } + let accepted_types = &schema.property_types[property_name]; + if let Some(value) = mapped_value(value, accepted_types, property_name) { + mapped.insert(property_name.clone(), value); + } + } + + if mapped.is_empty() { + return Err("no request parameters map to resource properties".into()); + } + if phase == OperationPhase::Create { + let mapped_properties: BTreeSet = mapped.keys().cloned().collect(); + let missing: Vec<&String> = schema.required_properties.difference(&mapped_properties).collect(); + if !missing.is_empty() { + return Err(format!( + "required resource properties are absent: {}", + missing.into_iter().map(String::as_str).collect::>().join(", ") + )); + } + } + Ok(mapped) +} + +fn property_matches(parameter_name: &str, property_name: &str, resource_name: &str) -> bool { + let parameter = normalize(parameter_name); + let property = normalize(property_name); + let resource = normalize(resource_name); + parameter == property + || property == format!("{resource}{parameter}") + || (property == format!("{parameter}name") && parameter == resource) +} + +fn mapped_value( + value: &AwsApiValue, + accepted_types: &BTreeSet, + property_name: &str, +) -> Option { + if value_matches_types(value, accepted_types) { + return value.json_value(); + } + if property_name == "Tags" + && accepts_type(accepted_types, PropertyValueType::Array) + && let AwsApiValue::Object { entries } = value + && entries.values().all(|value| matches!(value, AwsApiValue::String { .. })) + { + let mut tags: Vec<(&String, &AwsApiValue)> = entries.iter().collect(); + tags.sort_by_key(|(key, _)| key.as_str()); + return Some(serde_json::Value::Array( + tags.into_iter() + .filter_map(|(key, value)| match value { + AwsApiValue::String { value } => Some(serde_json::json!({"Key": key, "Value": value})), + _ => None, + }) + .collect(), + )); + } + if accepts_type(accepted_types, PropertyValueType::String) { + let value = match value { + AwsApiValue::Boolean { value } => Some(value.to_string()), + AwsApiValue::Integer { value } => Some(value.to_string()), + AwsApiValue::UnsignedInteger { value } => Some(value.to_string()), + AwsApiValue::Number { value } => serde_json::Number::from_f64(*value).map(|value| value.to_string()), + _ => None, + }; + return value.map(serde_json::Value::String); + } + None +} + +fn accepts_type(types: &BTreeSet, expected: PropertyValueType) -> bool { + types.contains(&PropertyValueType::Any) || types.contains(&expected) +} + +fn value_matches_types(value: &AwsApiValue, types: &BTreeSet) -> bool { + if types.contains(&PropertyValueType::Any) { + return !matches!(value, AwsApiValue::Null | AwsApiValue::Bytes { .. } | AwsApiValue::Unsupported { .. }); + } + match value { + AwsApiValue::Array { .. } => types.contains(&PropertyValueType::Array), + AwsApiValue::Object { .. } => types.contains(&PropertyValueType::Object), + AwsApiValue::Boolean { .. } => types.contains(&PropertyValueType::Boolean), + AwsApiValue::Integer { .. } | AwsApiValue::UnsignedInteger { .. } => { + types.contains(&PropertyValueType::Integer) || types.contains(&PropertyValueType::Number) + } + AwsApiValue::Number { .. } => types.contains(&PropertyValueType::Number), + AwsApiValue::String { .. } => types.contains(&PropertyValueType::String), + AwsApiValue::Null | AwsApiValue::Bytes { .. } | AwsApiValue::Unsupported { .. } => false, + } +} + +fn scope_partial_update_report(report: &mut ValidationReport, properties: &BTreeSet) { + let before = report.diagnostics.len(); + report.diagnostics.retain(|diagnostic| { + diagnostic.property_path.as_deref().is_some_and(|path| diagnostic_in_scope(path, properties)) + }); + let removed = before.saturating_sub(report.diagnostics.len()) as u32; + report.metadata.suppressed = report.metadata.suppressed.saturating_add(removed); + report.metadata.counts = summarize_diagnostics(&report.diagnostics); +} + +fn diagnostic_in_scope(property_path: &str, properties: &BTreeSet) -> bool { + properties.iter().any(|property_name| { + [format!("Properties.{property_name}"), format!("/Properties/{property_name}")].into_iter().any(|marker| { + property_path.find(&marker).is_some_and(|start| { + let end = start + marker.len(); + end == property_path.len() + || property_path[end..].chars().next().is_some_and(|separator| matches!(separator, '.' | '[' | '/')) + }) + }) + }) +} + +fn summarize_diagnostics(diagnostics: &[diagnostics::Diagnostic]) -> Summary { + let fatal = diagnostics.iter().filter(|diagnostic| diagnostic.severity == Severity::Fatal).count() as u32; + let errors = diagnostics.iter().filter(|diagnostic| diagnostic.severity == Severity::Error).count() as u32; + let warnings = diagnostics.iter().filter(|diagnostic| diagnostic.severity == Severity::Warn).count() as u32; + let debug = diagnostics.iter().filter(|diagnostic| diagnostic.severity == Severity::Debug).count() as u32; + let informational = diagnostics.len() as u32 - fatal - errors - warnings - debug; + Summary { fatal, errors, warnings, informational, debug } +} + +#[cfg(test)] +mod tests { + use super::*; + use diagnostics::{Diagnostic, PhaseMetric}; + use rules::{RuleInfo, RuleMetadataEntry}; + use std::sync::Arc; + use template_model::SemanticModel; + + struct NoopEngine { + metadata: HashMap, + init_metric: PhaseMetric, + } + + impl Default for NoopEngine { + fn default() -> Self { + Self { metadata: HashMap::new(), init_metric: PhaseMetric { duration_ms: 0.0 } } + } + } + + impl ValidationEngine for NoopEngine { + fn engine_name(&self) -> &str { + "noop" + } + + fn evaluate_rules( + &self, + _model: &Arc, + _config: &ValidateConfig, + ) -> Result, ValidationError> { + Ok(Vec::new()) + } + + fn list_rules(&self) -> Vec { + Vec::new() + } + + fn rule_metadata(&self) -> &HashMap { + &self.metadata + } + + fn external_rule_metadata(&self) -> HashMap { + HashMap::new() + } + + fn init_metric(&self) -> &PhaseMetric { + &self.init_metric + } + } + + fn value(value: serde_json::Value) -> AwsApiValue { + AwsApiValue::from_json(value) + } + + fn request(service: &str, operation: &str, parameters: serde_json::Value) -> AwsApiRequest { + let parameters: HashMap = parameters + .as_object() + .expect("test parameters must be an object") + .iter() + .map(|(name, value)| (name.clone(), AwsApiValue::from_json(value.clone()))) + .collect(); + AwsApiRequest::new(service, operation, parameters).with_service_prefix(service).with_http_method("POST") + } + + fn synthesized_json(request: &AwsApiRequest) -> (Classification, Synthesis, serde_json::Value) { + let schema_validator = SchemaValidator::default(); + let classification = classify_operation(request, &schema_validator, action_catalog().expect("catalog loads")); + let synthesis = synthesize_request(request, &classification, &schema_validator).expect("synthesis succeeds"); + let template = synthesis.template.as_ref().expect("request must synthesize"); + let document = serde_json::from_slice(template).expect("template must be JSON"); + (classification, synthesis, document) + } + + #[test] + fn operation_words_preserve_acronyms_for_noun_matching() { + assert_eq!(operation_words("BatchCreateDB2Cluster"), ["Batch", "Create", "DB", "2", "Cluster"]); + assert_eq!(effective_verb(&operation_words("BatchCreateDB2Cluster")), "Create"); + assert_eq!(operation_noun(&operation_words("BatchCreateDB2Cluster")), "DB2Cluster"); + } + + #[test] + fn representative_operations_have_closed_classifications() { + let schema_validator = SchemaValidator::default(); + let catalog = action_catalog().expect("catalog loads"); + for (service, operation, expected, candidate) in [ + ("s3", "CreateBucket", AwsApiOperationKind::CloudFormationCreate, Some("AWS::S3::Bucket")), + ("dynamodb", "CreateTable", AwsApiOperationKind::CloudFormationCreate, Some("AWS::DynamoDB::Table")), + ("iam", "GetRole", AwsApiOperationKind::ReadOnly, None), + ("lambda", "Invoke", AwsApiOperationKind::DataPlaneMutation, None), + ("s3", "DeleteBucket", AwsApiOperationKind::CloudFormationDelete, Some("AWS::S3::Bucket")), + ] { + let classification = + classify_operation(&request(service, operation, serde_json::json!({})), &schema_validator, catalog); + assert_eq!(classification.kind, expected, "{service}:{operation}"); + if let Some(candidate) = candidate { + assert_eq!(classification.candidates, [candidate], "{service}:{operation}"); + } + } + } + + #[test] + fn explicit_readonly_and_http_get_are_authoritative_read_signals() { + let schema_validator = SchemaValidator::default(); + let catalog = ActionCatalog::new(); + let mut explicitly_readonly = request("test", "CreateThing", serde_json::json!({})); + explicitly_readonly.is_read_only = Some(true); + assert_eq!( + classify_operation(&explicitly_readonly, &schema_validator, &catalog).kind, + AwsApiOperationKind::ReadOnly + ); + let mut get_request = request("test", "FrobnicateThing", serde_json::json!({})); + get_request.http_method = Some("GET".into()); + assert_eq!(classify_operation(&get_request, &schema_validator, &catalog).kind, AwsApiOperationKind::ReadOnly); + } + + #[test] + fn exact_template_body_bytes_are_not_rewritten() { + let schema_validator = SchemaValidator::default(); + let mut request = request("cloudformation", "CreateChangeSet", serde_json::json!({})); + let template = br#"{"Resources":{}}"#.to_vec(); + request.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: template.clone() }); + let classification = classify_operation(&request, &schema_validator, action_catalog().expect("catalog loads")); + let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::TemplateBody)); + assert_eq!(synthesis.template, Some(template)); + } + + #[test] + fn template_url_is_not_fetched() { + let schema_validator = SchemaValidator::default(); + let request = request( + "cloudformation", + "CreateStack", + serde_json::json!({"TemplateURL": "https://example.com/template.json"}), + ); + let classification = classify_operation(&request, &schema_validator, action_catalog().expect("catalog loads")); + let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none()); + assert!(synthesis.reason.contains("unavailable")); + } + + #[test] + fn desired_state_wraps_any_known_type_and_rejects_unknown_types() { + let known = request( + "cloudcontrol", + "CreateResource", + serde_json::json!({"TypeName": "AWS::SNS::Topic", "DesiredState": "{\"TopicName\":\"Synthetic\"}"}), + ); + let (classification, synthesis, document) = synthesized_json(&known); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.candidates, ["AWS::SNS::Topic"]); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::CloudControlDesiredState)); + assert_eq!(document["Resources"]["Resource"]["Properties"]["TopicName"], "Synthetic"); + + let schema_validator = SchemaValidator::default(); + let unknown = request( + "cloudcontrol", + "CreateResource", + serde_json::json!({"TypeName": "AWS::Unknown::Type", "DesiredState": "{}"}), + ); + let classification = classify_operation(&unknown, &schema_validator, action_catalog().expect("catalog loads")); + let synthesis = synthesize_request(&unknown, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none()); + assert!(synthesis.reason.contains("known CloudFormation TypeName")); + } + + #[test] + fn generic_create_maps_aliases_and_tag_objects() { + let request = + request("s3", "CreateBucket", serde_json::json!({"Bucket": "synthetic-bucket", "Tags": {"Team": "CLI"}})); + let (classification, synthesis, document) = synthesized_json(&request); + assert_eq!(classification.candidates, ["AWS::S3::Bucket"]); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(document["Resources"]["Resource"]["Properties"]["BucketName"], "synthetic-bucket"); + assert_eq!( + document["Resources"]["Resource"]["Properties"]["Tags"], + serde_json::json!([{"Key": "Team", "Value": "CLI"}]) + ); + } + + #[test] + fn generic_create_requires_complete_resource_state() { + let schema_validator = SchemaValidator::default(); + let request = + request("lambda", "CreateFunction", serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 128})); + let classification = classify_operation(&request, &schema_validator, action_catalog().expect("catalog loads")); + let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none()); + assert!(synthesis.reason.contains("Code, Role"), "{}", synthesis.reason); + } + + #[test] + fn generic_update_excludes_primary_identifier_and_tracks_changed_properties() { + let request = request( + "lambda", + "UpdateFunctionConfiguration", + serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 128}), + ); + let (_, synthesis, document) = synthesized_json(&request); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedUpdate)); + assert_eq!(document["Resources"]["Resource"]["Properties"], serde_json::json!({"MemorySize": 128})); + assert_eq!(synthesis.diagnostic_properties, Some(BTreeSet::from(["MemorySize".into()]))); + } + + #[test] + fn incompatible_optional_property_is_omitted() { + let request = + request("s3", "CreateBucket", serde_json::json!({"Bucket": "synthetic-bucket", "Tags": {"Key": 42}})); + let (_, _, document) = synthesized_json(&request); + assert_eq!( + document["Resources"]["Resource"]["Properties"], + serde_json::json!({"BucketName": "synthetic-bucket"}) + ); + } + + #[test] + fn data_plane_and_delete_requests_do_not_fabricate_resource_state() { + let schema_validator = SchemaValidator::default(); + for request in [ + request("dynamodb", "PutItem", serde_json::json!({"TableName": "Synthetic"})), + request("s3", "DeleteBucket", serde_json::json!({"Bucket": "synthetic-bucket"})), + ] { + let classification = + classify_operation(&request, &schema_validator, action_catalog().expect("catalog loads")); + let synthesis = + synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none()); + assert!(synthesis.reason.contains("representable resource state")); + } + } + + #[test] + fn high_level_api_validates_exact_template_and_reports_skips() { + let engine = NoopEngine::default(); + let schema_validator = SchemaValidator::default(); + let mut exact = request("cloudformation", "CreateChangeSet", serde_json::json!({})); + exact.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: br#"{"Resources":{}}"#.to_vec() }); + let validation = validate_aws_api_request(&engine, &schema_validator, &exact, ValidateConfig::default()) + .expect("validation succeeds"); + assert_eq!(validation.status, AwsApiRequestValidationStatus::Validated); + assert_eq!(validation.template_source, Some(AwsApiTemplateSource::TemplateBody)); + assert!(validation.report.is_some()); + + let read = request("iam", "GetRole", serde_json::json!({"RoleName": "Synthetic"})); + let validation = validate_aws_api_request(&engine, &schema_validator, &read, ValidateConfig::default()) + .expect("classification succeeds"); + assert_eq!(validation.status, AwsApiRequestValidationStatus::Skipped); + assert_eq!(validation.operation_kind, AwsApiOperationKind::ReadOnly); + assert!(validation.report.is_none()); + } + + #[test] + fn partial_update_scoping_keeps_report_counts_consistent() { + let engine = NoopEngine::default(); + let schema_validator = SchemaValidator::default(); + let update = request( + "lambda", + "UpdateFunctionConfiguration", + serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 0}), + ); + let validation = validate_aws_api_request(&engine, &schema_validator, &update, ValidateConfig::default()) + .expect("validation succeeds"); + let report = validation.report.expect("update is validated"); + assert!( + report + .diagnostics + .iter() + .all(|diagnostic| diagnostic.property_path.as_deref().is_some_and(|path| path.contains("MemorySize"))) + ); + let counts = &report.metadata.counts; + assert_eq!( + report.diagnostics.len() as u32, + counts.fatal + counts.errors + counts.warnings + counts.informational + counts.debug + ); + } + + #[test] + fn request_parameters_are_not_mutated() { + let parameters = HashMap::from([ + ("TableName".into(), value(serde_json::json!("Synthetic"))), + ("KeySchema".into(), value(serde_json::json!([{"AttributeName": "id", "KeyType": "HASH"}]))), + ("AttributeDefinitions".into(), value(serde_json::json!([{"AttributeName": "id", "AttributeType": "S"}]))), + ]); + let original = parameters.clone(); + let request = AwsApiRequest::new("dynamodb", "CreateTable", parameters).with_service_prefix("dynamodb"); + let _ = synthesized_json(&request); + assert_eq!(request.parameters, original); + } + + #[test] + fn json_conversion_rejects_non_json_values_without_coercion() { + assert!(AwsApiValue::Bytes { value: vec![1, 2] }.to_json().is_err()); + assert!(AwsApiValue::Number { value: f64::NAN }.to_json().is_err()); + assert!(AwsApiValue::Unsupported { type_name: "timestamp".into() }.to_json().is_err()); + } +} diff --git a/src/validation-engine/src/lib.rs b/src/validation-engine/src/lib.rs index d7bfcd73..9e9d43a3 100644 --- a/src/validation-engine/src/lib.rs +++ b/src/validation-engine/src/lib.rs @@ -1,10 +1,16 @@ #[cfg(feature = "uniffi-bindings")] uniffi::setup_scaffolding!(); +pub mod aws_api; pub mod engine; pub mod guard; pub(crate) mod step_functions; +pub use aws_api::{ + AwsApiOperationKind, AwsApiRequest, AwsApiRequestContext, AwsApiRequestValidation, AwsApiRequestValidationStatus, + AwsApiTemplateSource, AwsApiValue, DetailedAwsApiRequestValidation, StandardAwsApiRequestValidation, + validate_aws_api_request, validate_aws_api_request_with_path, +}; pub use engine::{ EngineConfig, EngineType, ExternalRuleSource, ValidateConfig, ValidationEngine, ValidationError, build_rule_list, catch_panics, extract_diagnostics, make_resource_diagnostic, make_resource_diagnostic_at_source, From a3f05e0eeacae64282b4976d9253f3e1ca65bd86 Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Mon, 17 Aug 2026 11:26:36 -0400 Subject: [PATCH 02/19] fixes --- .github/workflows/configs.yml | 2 +- INSTALLATION.md | 6 +- src/bindings-jvm/README.md | 91 ++++++++++++- src/bindings-jvm/build.gradle.kts | 9 ++ src/bindings-jvm/build.sh | 8 +- .../tests/kotlin/src/test/kotlin/SmokeTest.kt | 3 + src/bindings-jvm/uniffi.toml | 1 + src/bindings-python/README.md | 42 +++++- src/bindings-python/build.sh | 7 +- src/bindings-python/pyproject.toml | 3 +- src/bindings-python/tests/smoke_test.py | 3 + src/data-source/build.rs | 66 +--------- src/data-source/uniffi.toml | 1 + src/diagnostics/uniffi.toml | 1 + src/rules/uniffi.toml | 1 + src/schema-validator/uniffi.toml | 1 + src/template-model/uniffi.toml | 1 + src/validation-engine/src/aws_api.rs | 121 ++++-------------- src/validation-engine/uniffi.toml | 1 + 19 files changed, 194 insertions(+), 174 deletions(-) diff --git a/.github/workflows/configs.yml b/.github/workflows/configs.yml index 53855e5e..eb12a352 100644 --- a/.github/workflows/configs.yml +++ b/.github/workflows/configs.yml @@ -75,7 +75,7 @@ jobs: WORKING_DIR: 'src' RUST_TOOLCHAIN: '1.96.0' # keep in sync with src/rust-toolchain.toml NODE_VERSION: '22.x' - PYTHON_VERSION: '3.10' + PYTHON_VERSION: '3.9' GO_VERSION: '1.26' UNIFFI_BINDGEN_GO_TAG: 'v0.7.1+v0.31.0' # keep in sync with bindings-go/README.md and the uniffi pin in bindings-go/Cargo.toml JAVA_VERSION: '21' diff --git a/INSTALLATION.md b/INSTALLATION.md index 2bcfc92d..bd73f1cd 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -53,7 +53,7 @@ See the [Node.js API and examples](src/bindings-wasm/README.md). ### Python Production versions are published to [PyPI](https://pypi.org/project/cloudformation-validate/); prereleases are -published to [TestPyPI](https://test.pypi.org/project/cloudformation-validate/). The package requires Python 3.10 or +published to [TestPyPI](https://test.pypi.org/project/cloudformation-validate/). The package requires Python 3.9 or later, and its platform-specific wheels have no runtime package dependencies. ```bash @@ -95,7 +95,7 @@ See the [Go API and examples](src/bindings-go/README.md). The JVM library is published to [Maven Central as `software.amazon.cloudformation:cloudformation-validate`](https://central.sonatype.com/artifact/software.amazon.cloudformation/cloudformation-validate) -and requires JDK 21 or later. The jar includes native libraries for all supported platforms; Maven or Gradle resolves +and requires Java 8 or later. The jar includes native libraries for all supported platforms; Maven or Gradle resolves JNA, Gson, and the Kotlin standard library. Gradle (Kotlin DSL): @@ -185,7 +185,7 @@ testing the project from source need the tools below. Pinned versions live in | Kotlin (`kotlinc`) | 2.4.0 | JVM binding build | | | `ktlint` | 1.8.0 | JVM binding formatting | | | Gradle | 9.6.1 | JVM binding build/test | Must be on `PATH` - `bindings-jvm/build.sh` and the JVM test runner invoke `gradle` | -| Python | 3.10+ | Python binding build/test, license generation, `scripts/` | `setuptools` for the wheel build; no other packages required | +| Python | 3.9+ | Python binding build/test, license generation, `scripts/` | `setuptools` for the wheel build; no other packages required | | Go | 1.26+ | Go binding build/test | cgo must be enabled (default); Windows also needs `rustup target add x86_64-pc-windows-gnu` and MinGW-w64 `gcc` | | `uniffi-bindgen-go` | 0.7.1 | Go binding generation | `cargo install --git https://github.com/NordSecurity/uniffi-bindgen-go --tag v0.7.1+v0.31.0` | | `git`, `curl`, `openssl` | - | source control, fetching JVM deps, verifying releases | Usually preinstalled | diff --git a/src/bindings-jvm/README.md b/src/bindings-jvm/README.md index 2d781232..fe1766ab 100644 --- a/src/bindings-jvm/README.md +++ b/src/bindings-jvm/README.md @@ -13,8 +13,8 @@ All types live in the `software.amazon.cloudformation.validate` package. Available on [Maven Central](https://central.sonatype.com/artifact/software.amazon.cloudformation/cloudformation-validate) -as `software.amazon.cloudformation:cloudformation-validate`. Both snippets below resolve the latest published version; -substitute a specific version to pin one. +as `software.amazon.cloudformation:cloudformation-validate`. The library requires Java 8 or later. Both snippets below +resolve the latest published version; substitute a specific version to pin one. Gradle: @@ -108,6 +108,93 @@ validator does not perform network requests. Every result reports `status`, `ope diagnostics, while `validateAwsApiRequestDetailed` and its `validateAwsApiRequest` alias return detailed diagnostics. The same classes and methods are callable from Java with conventional generated getters. +#### AWS SDK for Java 2.x integration + +An `ExecutionInterceptor` can validate the real `SdkRequest` in `beforeExecution`, before the SDK marshals or sends it. +Convert `SdkPojo` fields recursively so nested request models and `SdkBytes` retain their values: + +```java +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.core.SdkField; +import software.amazon.awssdk.core.SdkPojo; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; +import software.amazon.cloudformation.validate.AwsApiRequest; +import software.amazon.cloudformation.validate.RegoEngine; +import software.amazon.cloudformation.validate.ValidateConfig; +import software.amazon.cloudformation.validate.engine.DetailedAwsApiRequestValidation; + +public final class CloudFormationValidationInterceptor implements ExecutionInterceptor { + private final RegoEngine engine = new RegoEngine(); + + @Override + public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes attributes) { + DetailedAwsApiRequestValidation result = engine.validateAwsApiRequest( + new AwsApiRequest( + attributes.getAttribute(SdkExecutionAttribute.SERVICE_NAME), + attributes.getAttribute(SdkExecutionAttribute.OPERATION_NAME), + sdkFields(context.request()) + ), + new ValidateConfig() + ); + + if (result.getReport() != null) { + result.getReport().getDiagnostics().forEach(diagnostic -> + System.out.println(diagnostic.getRuleId() + ": " + diagnostic.getMessage()) + ); + } else { + System.out.println(result.getStatus() + ": " + result.getReason()); + } + } + + private static Map sdkFields(SdkPojo pojo) { + Map values = new LinkedHashMap<>(); + for (SdkField field : pojo.sdkFields()) { + Object value = field.getValueOrDefault(pojo); + if (value != null) { + values.put(field.memberName(), sdkValue(value)); + } + } + return values; + } + + private static Object sdkValue(Object value) { + if (value instanceof SdkBytes) { + return ((SdkBytes) value).asByteArray(); + } + if (value instanceof SdkPojo) { + return sdkFields((SdkPojo) value); + } + if (value instanceof Map) { + Map converted = new LinkedHashMap<>(); + for (Map.Entry entry : ((Map) value).entrySet()) { + converted.put(String.valueOf(entry.getKey()), sdkValue(entry.getValue())); + } + return converted; + } + if (value instanceof Iterable) { + List converted = new ArrayList<>(); + for (Object item : (Iterable) value) { + converted.add(sdkValue(item)); + } + return converted; + } + return value; + } +} +``` + +Register the interceptor through the SDK client's `overrideConfiguration`. The engine is safe to reuse; constructing it +for every request needlessly recompiles the bundled rules. The example reports findings, but an interceptor can instead +throw after applying application-specific policy to prevent the API call. The adapter adds no AWS SDK dependency to +`cloudformation-validate` itself. + ### `EngineConfig` Passed to the constructor. All fields default to empty lists. diff --git a/src/bindings-jvm/build.gradle.kts b/src/bindings-jvm/build.gradle.kts index 63a757ac..0eacabb8 100644 --- a/src/bindings-jvm/build.gradle.kts +++ b/src/bindings-jvm/build.gradle.kts @@ -1,3 +1,4 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget import java.util.Properties plugins { @@ -44,6 +45,14 @@ dependencies { kotlin { jvmToolchain(21) // keep in sync with configs.yml java-version + compilerOptions { + jvmTarget.set(JvmTarget.JVM_1_8) + } +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } // ── Source layout ─────────────────────────────────────────────────────────────── diff --git a/src/bindings-jvm/build.sh b/src/bindings-jvm/build.sh index b8b466e3..3639807b 100755 --- a/src/bindings-jvm/build.sh +++ b/src/bindings-jvm/build.sh @@ -122,6 +122,12 @@ if [ "$CLASS_COUNT" -eq 0 ] || [ "$KT_COUNT" -eq 0 ]; then echo "Error: $JAR_FILE is missing compiled output - $CLASS_COUNT .class and $KT_COUNT .kt entries (both must be non-zero)." >&2 exit 1 fi +PUBLIC_API_CLASS="software.amazon.cloudformation.validate.ApiKt" +CLASS_FILE_MAJOR=$(javap -classpath "$JAR_FILE" -verbose "$PUBLIC_API_CLASS" | awk '/major version:/ { print $3; exit }') +if [ "$CLASS_FILE_MAJOR" != "52" ]; then + echo "Error: $JAR_FILE must target Java 8 classfile version 52, found $CLASS_FILE_MAJOR." >&2 + exit 1 +fi for required_metadata in LICENSE NOTICE README.md THIRD-PARTY-LICENSES.txt; do if ! jar tf "$JAR_FILE" | grep -Fxq "META-INF/$required_metadata"; then echo "Error: $JAR_FILE is missing META-INF/$required_metadata" >&2 @@ -137,7 +143,7 @@ JAR_SIZE=$(du -sh "$JAR_FILE" | cut -f1) echo "" echo "Build complete: $GENERATED_DIR" echo " Kotlin sources: $KT_SIZE ($KT_COUNT .kt files bundled)" -echo " Compiled classes: $CLASS_COUNT .class entries bundled" +echo " Compiled classes: $CLASS_COUNT .class entries bundled (Java 8 bytecode)" echo " Native library: $LIB_SIZE ($LIB_NAME, bundled in jar)" echo " JAR: $JAR_SIZE ($(basename "$JAR_FILE"))" echo "" diff --git a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt index f6de5fa4..a874ec0a 100644 --- a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt +++ b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt @@ -164,6 +164,9 @@ class SmokeTest { assertNotNull(result.report) } assertEquals(gson.toJson(results[0].report?.diagnostics), gson.toJson(results[1].report?.diagnostics)) + val standard = REGO.validateAwsApiRequestStandard(request, defaultConfig()) + assertEquals(AwsApiRequestValidationStatus.VALIDATED, standard.status) + assertNotNull(standard.report) assertEquals( linkedMapOf("Bucket" to "synthetic-bucket", "Tags" to linkedMapOf("Team" to "CLI")), parameters, diff --git a/src/bindings-jvm/uniffi.toml b/src/bindings-jvm/uniffi.toml index 5d7d9ef6..b8964653 100644 --- a/src/bindings-jvm/uniffi.toml +++ b/src/bindings-jvm/uniffi.toml @@ -1,6 +1,7 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate" generate_immutable_records = true +disable_java_cleaner = true [bindings.kotlin.external_packages] diagnostics = "software.amazon.cloudformation.validate.diagnostics" diff --git a/src/bindings-python/README.md b/src/bindings-python/README.md index 0fb5f35f..68a006ce 100644 --- a/src/bindings-python/README.md +++ b/src/bindings-python/README.md @@ -17,7 +17,7 @@ Available on [PyPI](https://pypi.org/project/cloudformation-validate/) as `cloud pip install cloudformation-validate ``` -Requires Python 3.10+ and has no runtime dependencies. PyPI publishes a separate wheel for every supported native +Requires Python 3.9+ and has no runtime dependencies. PyPI publishes a separate wheel for every supported native target. Each wheel carries exactly one native library and an accurate platform tag, so pip downloads only the artifact compatible with the installer host. @@ -91,6 +91,46 @@ validator does not perform network requests. The result always reports `status`, `validate_aws_api_request_standard` for standard diagnostics or `validate_aws_api_request_detailed` (also exposed as `validate_aws_api_request`) for detailed diagnostics. +#### AWS CLI integration + +AWS CLI emits `provide-client-params..` before serializing or sending each request. Register a +handler on the CLI's botocore session to validate the exact parameter dictionary without making another network call: + +```python +from cloudformation_validate import AwsApiRequest, RegoEngine + +engine = RegoEngine() # construct once and reuse + + +def validate_create_stack(params, model, **_kwargs): + service = model.service_model + result = engine.validate_aws_api_request( + AwsApiRequest( + service_name=service.service_name, + service_prefix=service.signing_name, + operation_name=model.name, + http_method=model.http.get("method"), + parameters=params, + ) + ) + if result.report is not None: + for diagnostic in result.report.diagnostics: + print(diagnostic.rule_id, diagnostic.message) + else: + print(result.status.name, result.reason) + + +# `session` is the botocore session owned by the AWS CLI driver or plugin. +session.register( + "provide-client-params.cloudformation.CreateStack", + validate_create_stack, +) +``` + +The callback receives the real botocore `OperationModel`, so it does not need a duplicate service model. For +`CreateStack` and `UpdateStack`, the request's `TemplateBody` string or bytes are validated exactly. A handler that +must prevent the API call can raise after applying its own policy to the returned diagnostics. + ### EngineConfig Passed to the constructor. All fields default to empty lists. diff --git a/src/bindings-python/build.sh b/src/bindings-python/build.sh index 5d832ca9..1d25b878 100755 --- a/src/bindings-python/build.sh +++ b/src/bindings-python/build.sh @@ -68,8 +68,8 @@ EOF # ── Prerequisites ───────────────────────────────────────────────────────────── command -v "$PYTHON" &>/dev/null || { echo "Error: $PYTHON not found on PATH" >&2; exit 1; } command -v unzip &>/dev/null || { echo "Error: unzip not found on PATH" >&2; exit 1; } -"$PYTHON" -c 'import sys; sys.exit(0 if sys.version_info >= (3, 10) else 1)' \ - || { echo "Error: Python 3.10+ required, found $("$PYTHON" --version)" >&2; exit 1; } +"$PYTHON" -c 'import sys; sys.exit(0 if sys.version_info >= (3, 9) else 1)' \ + || { echo "Error: Python 3.9+ required, found $("$PYTHON" --version)" >&2; exit 1; } "$PYTHON" -m pip --version &>/dev/null \ || { echo "Error: pip not available ($PYTHON -m pip failed)" >&2; exit 1; } @@ -142,7 +142,8 @@ for module in modules: text = module.read_text(encoding="utf-8") if OLD not in text: sys.exit(f"error: expected loader line not found in {module.name} - did the uniffi template change?") - module.write_text(sort_relative_imports(text.replace(OLD, NEW)), encoding="utf-8", newline="\n") + with module.open("w", encoding="utf-8", newline="\n") as output: + output.write(sort_relative_imports(text.replace(OLD, NEW))) print(f" patched {len(modules)} modules") EOF diff --git a/src/bindings-python/pyproject.toml b/src/bindings-python/pyproject.toml index fa8e16b0..bd727ed9 100644 --- a/src/bindings-python/pyproject.toml +++ b/src/bindings-python/pyproject.toml @@ -9,7 +9,7 @@ description = "Fast, offline, embeddable validation for AWS CloudFormation templ readme = "README.md" license = "Apache-2.0" license-files = ["LICENSE", "NOTICE", "THIRD-PARTY-LICENSES.txt"] -requires-python = ">=3.10" +requires-python = ">=3.9" authors = [{ name = "Amazon Web Services" }] keywords = [ "aws", @@ -30,6 +30,7 @@ classifiers = [ "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/src/bindings-python/tests/smoke_test.py b/src/bindings-python/tests/smoke_test.py index 994b4a0a..a83e8f33 100644 --- a/src/bindings-python/tests/smoke_test.py +++ b/src/bindings-python/tests/smoke_test.py @@ -204,6 +204,9 @@ def test_synthesized_create_validates_with_both_engines(self): self.assertEqual(["AWS::S3::Bucket"], result.resource_types) self.assertIsNotNone(result.report) self.assertEqual(diagnostic_keys(results[0].report), diagnostic_keys(results[1].report)) + standard = REGO.validate_aws_api_request_standard(request) + self.assertEqual(AwsApiRequestValidationStatus.VALIDATED, standard.status) + self.assertIsNotNone(standard.report) self.assertEqual({"Bucket": "synthetic-bucket", "Tags": {"Team": "CLI"}}, parameters) def test_template_body_bytes_are_validated_exactly(self): diff --git a/src/data-source/build.rs b/src/data-source/build.rs index 00fdc9dc..6ffccacc 100644 --- a/src/data-source/build.rs +++ b/src/data-source/build.rs @@ -2,7 +2,6 @@ mod source_versions; use source_versions::{SOURCE_VERSIONS_FILE, SourceVersions}; -use std::collections::{BTreeMap, BTreeSet}; use std::env; use std::fs; use std::io::Cursor; @@ -80,10 +79,9 @@ fn main() { let generated_sv_dir = manifest_dir.join("generated").join("schema-validator"); let generated_cel_dir = manifest_dir.join("generated").join("cel-rules"); let handwritten_dir = manifest_dir.join("handwritten"); - let upstream_schema_dir = manifest_dir.join("upstream").join("schemas"); let rego_hw_dir = manifest_dir.parent().unwrap().join("rego-engine").join("handwritten").join("rego"); - for dir in [&generated_data_dir, &generated_sv_dir, &generated_cel_dir, &handwritten_dir, &upstream_schema_dir] { + for dir in [&generated_data_dir, &generated_sv_dir, &generated_cel_dir, &handwritten_dir] { println!("cargo:rerun-if-changed={}", dir.display()); } println!("cargo:rerun-if-changed={}", rego_hw_dir.display()); @@ -122,9 +120,6 @@ fn main() { embed_minified_json(&path, const_name, &out_dir, &mut code); } - let aws_api_actions = build_aws_api_action_catalog(&upstream_schema_dir); - embed_minified_value(&aws_api_actions, "AWS_API_ACTIONS", &out_dir, &mut code); - // CEL generated rules let cel_rules_path = generated_cel_dir.join("generated_rules.json"); if !cel_rules_path.exists() { @@ -146,7 +141,6 @@ fn main() { for (_filename, const_name) in GENERATED_JSON.iter().chain(HANDWRITTEN_JSON.iter()) { code.push_str(&format!(" let _ = &*{}_BYTES;\n", const_name)); } - code.push_str(" let _ = &*AWS_API_ACTIONS_BYTES;\n"); code.push_str(" let _ = &*GENERATED_RULES_BYTES;\n"); code.push_str("}\n"); @@ -186,69 +180,13 @@ fn assert_exists(path: &Path, _label: &str) { } } -/// Build an IAM action -> provider handler role -> CloudFormation resource type -/// catalog directly from the checked-in enhanced provider schemas. The schemas -/// remain the source of truth; this compact index exists only in Cargo's output -/// directory and is never checked in as a second metadata bundle. -fn build_aws_api_action_catalog(schema_dir: &Path) -> serde_json::Value { - let mut paths: Vec = fs::read_dir(schema_dir) - .unwrap_or_else(|error| panic!("failed to read enhanced schema directory {}: {error}", schema_dir.display())) - .map(|entry| { - entry.unwrap_or_else(|error| panic!("failed to read an entry in {}: {error}", schema_dir.display())).path() - }) - .filter(|path| path.extension().and_then(|extension| extension.to_str()) == Some("json")) - .collect(); - paths.sort(); - - let mut actions: BTreeMap>> = BTreeMap::new(); - for path in paths { - let raw = fs::read_to_string(&path) - .unwrap_or_else(|error| panic!("failed to read enhanced schema {}: {error}", path.display())); - let schema: serde_json::Value = serde_json::from_str(&raw) - .unwrap_or_else(|error| panic!("failed to parse enhanced schema {}: {error}", path.display())); - let Some(type_name) = schema.get("typeName").and_then(serde_json::Value::as_str) else { - continue; - }; - let Some(handlers) = schema.get("handlers").and_then(serde_json::Value::as_object) else { - continue; - }; - for role in ["create", "update", "delete", "read", "list"] { - let Some(permissions) = handlers - .get(role) - .and_then(serde_json::Value::as_object) - .and_then(|handler| handler.get("permissions")) - .and_then(serde_json::Value::as_array) - else { - continue; - }; - for action in permissions.iter().filter_map(serde_json::Value::as_str) { - if !action.contains(':') { - continue; - } - actions - .entry(action.to_ascii_lowercase()) - .or_default() - .entry(role.to_string()) - .or_default() - .insert(type_name.to_string()); - } - } - } - - serde_json::to_value(actions).expect("AWS API action catalog must serialize") -} - /// Minify JSON, compress with zstd level 9, and embed as /// `pub static NAME_BYTES: LazyLock>` that lazily decompresses on first access. /// Uses `ruzstd` (pure-Rust decoder) at runtime to keep WASM builds portable. fn embed_minified_json(path: &Path, const_name: &str, out_dir: &Path, code: &mut String) { let raw = fs::read_to_string(path).unwrap(); let value: serde_json::Value = serde_json::from_str(&raw).unwrap(); - embed_minified_value(&value, const_name, out_dir, code); -} - -fn embed_minified_value(value: &serde_json::Value, const_name: &str, out_dir: &Path, code: &mut String) { - let minified = serde_json::to_vec(value).unwrap(); + let minified = serde_json::to_vec(&value).unwrap(); let compressed = zstd::encode_all(Cursor::new(&minified), 9).unwrap(); let bin_path = out_dir.join(format!("{}.json.zst", const_name.to_lowercase())); diff --git a/src/data-source/uniffi.toml b/src/data-source/uniffi.toml index f7802e9c..f0c156ff 100644 --- a/src/data-source/uniffi.toml +++ b/src/data-source/uniffi.toml @@ -1,3 +1,4 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate.datasource" generate_immutable_records = true +disable_java_cleaner = true diff --git a/src/diagnostics/uniffi.toml b/src/diagnostics/uniffi.toml index 00a38af0..db9d160e 100644 --- a/src/diagnostics/uniffi.toml +++ b/src/diagnostics/uniffi.toml @@ -1,6 +1,7 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate.diagnostics" generate_immutable_records = true +disable_java_cleaner = true [bindings.kotlin.external_packages] rules = "software.amazon.cloudformation.validate.rules" diff --git a/src/rules/uniffi.toml b/src/rules/uniffi.toml index e1357541..96a21b0b 100644 --- a/src/rules/uniffi.toml +++ b/src/rules/uniffi.toml @@ -1,3 +1,4 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate.rules" generate_immutable_records = true +disable_java_cleaner = true diff --git a/src/schema-validator/uniffi.toml b/src/schema-validator/uniffi.toml index cee50680..b1d7c48d 100644 --- a/src/schema-validator/uniffi.toml +++ b/src/schema-validator/uniffi.toml @@ -1,6 +1,7 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate.schemavalidator" generate_immutable_records = true +disable_java_cleaner = true [bindings.kotlin.external_packages] data_source = "software.amazon.cloudformation.validate.datasource" diff --git a/src/template-model/uniffi.toml b/src/template-model/uniffi.toml index 297e2738..2c7a4983 100644 --- a/src/template-model/uniffi.toml +++ b/src/template-model/uniffi.toml @@ -1,6 +1,7 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate.templatemodel" generate_immutable_records = true +disable_java_cleaner = true [bindings.kotlin.external_packages] diagnostics = "software.amazon.cloudformation.validate.diagnostics" diff --git a/src/validation-engine/src/aws_api.rs b/src/validation-engine/src/aws_api.rs index 578b8f84..88151a83 100644 --- a/src/validation-engine/src/aws_api.rs +++ b/src/validation-engine/src/aws_api.rs @@ -1,10 +1,8 @@ -use data_source::embedded::AWS_API_ACTIONS_BYTES; use diagnostics::{DetailedReport, StandardReport, Summary, ValidationReport}; use rules::Severity; use schema_validator::{PropertyValueType, ResourceSchemaMetadata, SchemaValidator}; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet, HashMap}; -use std::sync::LazyLock; use crate::{ValidateConfig, ValidationEngine, ValidationError, validate_bytes_with_path}; @@ -268,8 +266,7 @@ pub fn validate_aws_api_request_with_path( config: ValidateConfig, file_path: String, ) -> Result { - let catalog = action_catalog()?; - let classification = classify_operation(request, schema_validator, catalog); + let classification = classify_operation(request, schema_validator); let synthesis = synthesize_request(request, &classification, schema_validator)?; let Some(template) = synthesis.template else { return Ok(AwsApiRequestValidation { @@ -296,18 +293,6 @@ pub fn validate_aws_api_request_with_path( }) } -type HandlerRoles = HashMap>; -type ActionCatalog = HashMap; - -static ACTION_CATALOG: LazyLock> = LazyLock::new(|| { - serde_json::from_slice(&AWS_API_ACTIONS_BYTES) - .map_err(|error| format!("embedded AWS API action catalog is invalid: {error}")) -}); - -fn action_catalog() -> Result<&'static ActionCatalog, ValidationError> { - ACTION_CATALOG.as_ref().map_err(|message| ValidationError::Engine(message.clone())) -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum OperationPhase { Read, @@ -318,17 +303,6 @@ enum OperationPhase { Unknown, } -impl OperationPhase { - fn handler_role(self) -> Option<&'static str> { - match self { - Self::Create => Some("create"), - Self::Update => Some("update"), - Self::Delete => Some("delete"), - _ => None, - } - } -} - #[derive(Debug, Clone)] struct Classification { kind: AwsApiOperationKind, @@ -510,17 +484,11 @@ const DATA_PLANE_VERBS: &[&str] = &[ const DATA_PLANE_IF_UNMAPPED_VERBS: &[&str] = &["Execute", "Invoke", "Post", "Publish", "Put", "Send", "Upload", "Write"]; -fn classify_operation( - request: &AwsApiRequest, - schema_validator: &SchemaValidator, - catalog: &ActionCatalog, -) -> Classification { +fn classify_operation(request: &AwsApiRequest, schema_validator: &SchemaValidator) -> Classification { let prefix = request.effective_service_prefix(); let words = operation_words(&request.operation_name); let verb = effective_verb(&words); - let action_key = format!("{prefix}:{}", request.operation_name).to_ascii_lowercase(); - let action_roles = catalog.get(&action_key); - let phase = operation_phase(request, action_roles, verb); + let phase = operation_phase(request, verb); match phase { OperationPhase::Read => Classification { kind: AwsApiOperationKind::ReadOnly, phase, candidates: Vec::new() }, @@ -528,10 +496,9 @@ fn classify_operation( Classification { kind: AwsApiOperationKind::DataPlaneMutation, phase, candidates: Vec::new() } } OperationPhase::Create | OperationPhase::Update | OperationPhase::Delete => { - let candidates = - explicit_resource_type(request, schema_validator).map(|type_name| vec![type_name]).unwrap_or_else( - || candidate_types(schema_validator, action_roles, phase, prefix, operation_noun(&words)), - ); + let candidates = explicit_resource_type(request, schema_validator) + .map(|type_name| vec![type_name]) + .unwrap_or_else(|| candidate_types(schema_validator, prefix, operation_noun(&words))); if candidates.is_empty() { let is_data_plane = DATA_PLANE_IF_UNMAPPED_VERBS.contains(&verb); Classification { @@ -559,7 +526,7 @@ fn classify_operation( } } -fn operation_phase(request: &AwsApiRequest, action_roles: Option<&HandlerRoles>, verb: &str) -> OperationPhase { +fn operation_phase(request: &AwsApiRequest, verb: &str) -> OperationPhase { if request.is_read_only == Some(true) || READ_VERBS.contains(&verb) { return OperationPhase::Read; } @@ -580,21 +547,7 @@ fn operation_phase(request: &AwsApiRequest, action_roles: Option<&HandlerRoles>, Some("DELETE") => return OperationPhase::Delete, _ => {} } - action_roles.and_then(phase_from_roles).unwrap_or(OperationPhase::Unknown) -} - -fn phase_from_roles(action_roles: &HandlerRoles) -> Option { - if action_roles.contains_key("read") || action_roles.contains_key("list") { - return None; - } - let write_roles: Vec<&str> = - ["create", "update", "delete"].into_iter().filter(|role| action_roles.contains_key(*role)).collect(); - match write_roles.as_slice() { - ["create"] => Some(OperationPhase::Create), - ["update"] => Some(OperationPhase::Update), - ["delete"] => Some(OperationPhase::Delete), - _ => None, - } + OperationPhase::Unknown } fn operation_words(operation_name: &str) -> Vec { @@ -644,34 +597,12 @@ fn explicit_resource_type(request: &AwsApiRequest, schema_validator: &SchemaVali } } -fn candidate_types( - schema_validator: &SchemaValidator, - action_roles: Option<&HandlerRoles>, - phase: OperationPhase, - prefix: &str, - noun: String, -) -> Vec { - let mut role_candidates = BTreeSet::new(); - if let (Some(action_roles), Some(role)) = (action_roles, phase.handler_role()) { - if let Some(candidates) = action_roles.get(role) { - role_candidates - .extend(candidates.iter().filter(|candidate| schema_validator.has_resource_type(candidate)).cloned()); - } - if phase == OperationPhase::Update - && let Some(candidates) = action_roles.get("create") - { - role_candidates - .extend(candidates.iter().filter(|candidate| schema_validator.has_resource_type(candidate)).cloned()); - } - } - - let mut resource_candidates = role_candidates.clone(); - resource_candidates.extend( - schema_validator - .resource_type_names() - .filter(|type_name| score_candidate(type_name, prefix, &noun) > 0) - .map(str::to_string), - ); +fn candidate_types(schema_validator: &SchemaValidator, prefix: &str, noun: String) -> Vec { + let resource_candidates: BTreeSet = schema_validator + .resource_type_names() + .filter(|type_name| score_candidate(type_name, prefix, &noun) > 0) + .map(str::to_string) + .collect(); let scores: BTreeMap = resource_candidates .into_iter() .map(|type_name| { @@ -1095,7 +1026,7 @@ mod tests { fn synthesized_json(request: &AwsApiRequest) -> (Classification, Synthesis, serde_json::Value) { let schema_validator = SchemaValidator::default(); - let classification = classify_operation(request, &schema_validator, action_catalog().expect("catalog loads")); + let classification = classify_operation(request, &schema_validator); let synthesis = synthesize_request(request, &classification, &schema_validator).expect("synthesis succeeds"); let template = synthesis.template.as_ref().expect("request must synthesize"); let document = serde_json::from_slice(template).expect("template must be JSON"); @@ -1112,7 +1043,6 @@ mod tests { #[test] fn representative_operations_have_closed_classifications() { let schema_validator = SchemaValidator::default(); - let catalog = action_catalog().expect("catalog loads"); for (service, operation, expected, candidate) in [ ("s3", "CreateBucket", AwsApiOperationKind::CloudFormationCreate, Some("AWS::S3::Bucket")), ("dynamodb", "CreateTable", AwsApiOperationKind::CloudFormationCreate, Some("AWS::DynamoDB::Table")), @@ -1121,7 +1051,7 @@ mod tests { ("s3", "DeleteBucket", AwsApiOperationKind::CloudFormationDelete, Some("AWS::S3::Bucket")), ] { let classification = - classify_operation(&request(service, operation, serde_json::json!({})), &schema_validator, catalog); + classify_operation(&request(service, operation, serde_json::json!({})), &schema_validator); assert_eq!(classification.kind, expected, "{service}:{operation}"); if let Some(candidate) = candidate { assert_eq!(classification.candidates, [candidate], "{service}:{operation}"); @@ -1132,16 +1062,12 @@ mod tests { #[test] fn explicit_readonly_and_http_get_are_authoritative_read_signals() { let schema_validator = SchemaValidator::default(); - let catalog = ActionCatalog::new(); let mut explicitly_readonly = request("test", "CreateThing", serde_json::json!({})); explicitly_readonly.is_read_only = Some(true); - assert_eq!( - classify_operation(&explicitly_readonly, &schema_validator, &catalog).kind, - AwsApiOperationKind::ReadOnly - ); + assert_eq!(classify_operation(&explicitly_readonly, &schema_validator).kind, AwsApiOperationKind::ReadOnly); let mut get_request = request("test", "FrobnicateThing", serde_json::json!({})); get_request.http_method = Some("GET".into()); - assert_eq!(classify_operation(&get_request, &schema_validator, &catalog).kind, AwsApiOperationKind::ReadOnly); + assert_eq!(classify_operation(&get_request, &schema_validator).kind, AwsApiOperationKind::ReadOnly); } #[test] @@ -1150,7 +1076,7 @@ mod tests { let mut request = request("cloudformation", "CreateChangeSet", serde_json::json!({})); let template = br#"{"Resources":{}}"#.to_vec(); request.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: template.clone() }); - let classification = classify_operation(&request, &schema_validator, action_catalog().expect("catalog loads")); + let classification = classify_operation(&request, &schema_validator); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); assert_eq!(synthesis.source, Some(AwsApiTemplateSource::TemplateBody)); assert_eq!(synthesis.template, Some(template)); @@ -1164,7 +1090,7 @@ mod tests { "CreateStack", serde_json::json!({"TemplateURL": "https://example.com/template.json"}), ); - let classification = classify_operation(&request, &schema_validator, action_catalog().expect("catalog loads")); + let classification = classify_operation(&request, &schema_validator); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); assert!(synthesis.reason.contains("unavailable")); @@ -1189,7 +1115,7 @@ mod tests { "CreateResource", serde_json::json!({"TypeName": "AWS::Unknown::Type", "DesiredState": "{}"}), ); - let classification = classify_operation(&unknown, &schema_validator, action_catalog().expect("catalog loads")); + let classification = classify_operation(&unknown, &schema_validator); let synthesis = synthesize_request(&unknown, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); assert!(synthesis.reason.contains("known CloudFormation TypeName")); @@ -1214,7 +1140,7 @@ mod tests { let schema_validator = SchemaValidator::default(); let request = request("lambda", "CreateFunction", serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 128})); - let classification = classify_operation(&request, &schema_validator, action_catalog().expect("catalog loads")); + let classification = classify_operation(&request, &schema_validator); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); assert!(synthesis.reason.contains("Code, Role"), "{}", synthesis.reason); @@ -1251,8 +1177,7 @@ mod tests { request("dynamodb", "PutItem", serde_json::json!({"TableName": "Synthetic"})), request("s3", "DeleteBucket", serde_json::json!({"Bucket": "synthetic-bucket"})), ] { - let classification = - classify_operation(&request, &schema_validator, action_catalog().expect("catalog loads")); + let classification = classify_operation(&request, &schema_validator); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); diff --git a/src/validation-engine/uniffi.toml b/src/validation-engine/uniffi.toml index dee40e93..6aa4f76f 100644 --- a/src/validation-engine/uniffi.toml +++ b/src/validation-engine/uniffi.toml @@ -1,6 +1,7 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate.engine" generate_immutable_records = true +disable_java_cleaner = true [bindings.kotlin.external_packages] rules = "software.amazon.cloudformation.validate.rules" From 4e7f04d74d1ea2dd51b3e485b9c3102ced5c9650 Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Tue, 18 Aug 2026 09:53:02 -0400 Subject: [PATCH 03/19] catalog generator --- src/bindings-jvm/README.md | 11 +- .../tests/kotlin/src/test/kotlin/SmokeTest.kt | 36 +- src/bindings-python/README.md | 12 +- src/bindings-python/tests/smoke_test.py | 33 +- src/data-source/README.md | 25 + src/data-source/build.rs | 1 + .../data/aws_api_operation_catalog.json | 49706 ++++++++++++++++ .../scripts/generate_aws_api_catalog.py | 673 + .../scripts/test_generate_aws_api_catalog.py | 107 + src/validation-engine/API.md | 25 +- src/validation-engine/src/aws_api.rs | 1532 +- 11 files changed, 51776 insertions(+), 385 deletions(-) create mode 100644 src/data-source/generated/data/aws_api_operation_catalog.json create mode 100644 src/data-source/scripts/generate_aws_api_catalog.py create mode 100644 src/data-source/scripts/test_generate_aws_api_catalog.py diff --git a/src/bindings-jvm/README.md b/src/bindings-jvm/README.md index fe1766ab..1a0c107b 100644 --- a/src/bindings-jvm/README.md +++ b/src/bindings-jvm/README.md @@ -91,7 +91,6 @@ val result = RegoEngine().validateAwsApiRequest( httpMethod = "PUT", parameters = mapOf( "Bucket" to "example-bucket", - "Tags" to mapOf("Team" to "Platform"), ), ), ) @@ -108,6 +107,16 @@ validator does not perform network requests. Every result reports `status`, `ope diagnostics, while `validateAwsApiRequestDetailed` and its `validateAwsApiRequest` alias return detailed diagnostics. The same classes and methods are callable from Java with conventional generated getters. +Operation-to-resource mapping uses a deterministic closed adapter catalog generated from each resource type's own +provider handler metadata and verified against botocore models and the compiled CloudFormation schemas: only +verified service+operation pairs produce inferred resource types and synthesized templates. Unregistered operations are classified as +`UNMAPPED_MUTATION` or `DATA_PLANE_MUTATION` with `SKIPPED` status and no inferred resource types. Cloud Control +`UpdateResource` and `DeleteResource` may echo a known `TypeName` supplied by the request, but never synthesize state. +The canonical `serviceName` is authoritative; `servicePrefix` cannot override it. Case normalization accepts CLI names +(for example, `s3`) and Java SDK `SERVICE_NAME` values (for example, `S3`) without fuzzy or punctuation aliases. +`TemplateBody` validation is restricted to CloudFormation operations that accept it, and +`TypeName`+`DesiredState` wrapping applies only to exact Cloud Control `CreateResource`. + #### AWS SDK for Java 2.x integration An `ExecutionInterceptor` can validate the real `SdkRequest` in `beforeExecution`, before the SDK marshals or sends it. diff --git a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt index a874ec0a..87472947 100644 --- a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt +++ b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt @@ -144,7 +144,6 @@ class SmokeTest { fun synthesizedAwsApiCreateValidatesWithBothEnginesWithoutMutatingInput() { val parameters = linkedMapOf( "Bucket" to "synthetic-bucket", - "Tags" to linkedMapOf("Team" to "CLI"), ) val request = AwsApiRequest( serviceName = "s3", @@ -168,7 +167,7 @@ class SmokeTest { assertEquals(AwsApiRequestValidationStatus.VALIDATED, standard.status) assertNotNull(standard.report) assertEquals( - linkedMapOf("Bucket" to "synthetic-bucket", "Tags" to linkedMapOf("Team" to "CLI")), + linkedMapOf("Bucket" to "synthetic-bucket"), parameters, ) } @@ -231,6 +230,39 @@ class SmokeTest { ) } + @Test + fun awsApiJavaSdkServiceNameCasingResolvesAdapter() { + val result = REGO.validateAwsApiRequest( + AwsApiRequest( + serviceName = "S3", + operationName = "CreateBucket", + parameters = mapOf("Bucket" to "synthetic-bucket"), + servicePrefix = "S3", + httpMethod = "PUT", + ), + defaultConfig(), + ) + assertEquals(AwsApiOperationKind.CLOUD_FORMATION_CREATE, result.operationKind) + assertEquals(listOf("AWS::S3::Bucket"), result.resourceTypes) + } + + @Test + fun awsApiUnregisteredOperationNeverMapsToResourceType() { + val result = REGO.validateAwsApiRequest( + AwsApiRequest( + serviceName = "ecs", + operationName = "RunTask", + parameters = mapOf("TaskDefinition" to "my-task"), + servicePrefix = "ecs", + httpMethod = "POST", + ), + defaultConfig(), + ) + assertEquals(AwsApiRequestValidationStatus.SKIPPED, result.status) + assertTrue(result.resourceTypes.isEmpty(), "unregistered operation must not produce resource types") + assertNull(result.report) + } + // ── SchemaValidator ────────────────────────────────────────────────────── @Test diff --git a/src/bindings-python/README.md b/src/bindings-python/README.md index 68a006ce..21701bf5 100644 --- a/src/bindings-python/README.md +++ b/src/bindings-python/README.md @@ -73,7 +73,7 @@ result = engine.validate_aws_api_request( service_prefix="s3", operation_name="CreateBucket", http_method="PUT", - parameters={"Bucket": "example-bucket", "Tags": {"Team": "Platform"}}, + parameters={"Bucket": "example-bucket"}, ) ) @@ -91,6 +91,16 @@ validator does not perform network requests. The result always reports `status`, `validate_aws_api_request_standard` for standard diagnostics or `validate_aws_api_request_detailed` (also exposed as `validate_aws_api_request`) for detailed diagnostics. +Operation-to-resource mapping uses a deterministic closed adapter catalog generated from each resource type's own +provider handler metadata and verified against botocore models and the compiled CloudFormation schemas: only +verified service+operation pairs produce inferred resource types and synthesized templates. Unregistered operations are classified as +`UNMAPPED_MUTATION` or `DATA_PLANE_MUTATION` with `SKIPPED` status and no inferred resource types. Cloud Control +`UpdateResource` and `DeleteResource` may echo a known `TypeName` supplied by the request, but never synthesize state. +The canonical `service_name` is authoritative; `service_prefix` cannot override it. Case normalization accepts CLI +names (for example, `s3`) and Java SDK casing (for example, `S3`) without fuzzy or punctuation aliases. +`TemplateBody` validation is restricted to CloudFormation operations that accept it, and +`TypeName`+`DesiredState` wrapping applies only to exact Cloud Control `CreateResource`. + #### AWS CLI integration AWS CLI emits `provide-client-params..` before serializing or sending each request. Register a diff --git a/src/bindings-python/tests/smoke_test.py b/src/bindings-python/tests/smoke_test.py index a83e8f33..37c45071 100644 --- a/src/bindings-python/tests/smoke_test.py +++ b/src/bindings-python/tests/smoke_test.py @@ -187,7 +187,7 @@ def test_unparseable_template_reports_error_status(self): class AwsApiRequestValidationTest(unittest.TestCase): def test_synthesized_create_validates_with_both_engines(self): - parameters = {"Bucket": "synthetic-bucket", "Tags": {"Team": "CLI"}} + parameters = {"Bucket": "synthetic-bucket"} request = AwsApiRequest( "s3", "CreateBucket", @@ -207,7 +207,7 @@ def test_synthesized_create_validates_with_both_engines(self): standard = REGO.validate_aws_api_request_standard(request) self.assertEqual(AwsApiRequestValidationStatus.VALIDATED, standard.status) self.assertIsNotNone(standard.report) - self.assertEqual({"Bucket": "synthetic-bucket", "Tags": {"Team": "CLI"}}, parameters) + self.assertEqual({"Bucket": "synthetic-bucket"}, parameters) def test_template_body_bytes_are_validated_exactly(self): request = AwsApiRequest( @@ -263,6 +263,35 @@ def test_partial_update_diagnostics_are_scoped_and_counts_match(self): counts.fatal + counts.errors + counts.warnings + counts.informational + counts.debug, ) + def test_unregistered_operation_never_maps_to_resource_type(self): + request = AwsApiRequest( + "ecs", + "RunTask", + {"TaskDefinition": "my-task"}, + service_prefix="ecs", + http_method="POST", + ) + + result = REGO.validate_aws_api_request(request) + + self.assertEqual(AwsApiRequestValidationStatus.SKIPPED, result.status) + self.assertEqual([], result.resource_types) + self.assertIsNone(result.report) + + def test_java_sdk_service_name_casing_resolves_adapter(self): + request = AwsApiRequest( + "S3", + "CreateBucket", + {"Bucket": "synthetic-bucket"}, + service_prefix="S3", + http_method="PUT", + ) + + result = REGO.validate_aws_api_request(request) + + self.assertEqual(AwsApiOperationKind.CLOUD_FORMATION_CREATE, result.operation_kind) + self.assertEqual(["AWS::S3::Bucket"], result.resource_types) + class AdditionalSchemasTest(unittest.TestCase): def test_additional_schemas_apply_through_the_public_config_on_both_engines(self): diff --git a/src/data-source/README.md b/src/data-source/README.md index 4e32b9e1..f0e6f0e0 100644 --- a/src/data-source/README.md +++ b/src/data-source/README.md @@ -38,3 +38,28 @@ data-source/ ├── cel-rules/ # CEL rule descriptors └── schema-validator/ # Compiled schemas for schema-validator ``` + +## AWS API operation catalog + +`generated/data/aws_api_operation_catalog.json` is produced by +`scripts/generate_aws_api_catalog.py`. The generator derives create and delete adapters from each resource type's own +handler permissions in the public +[`resource-provider-enhanced-schemas`](https://github.com/aws-cloudformation/resource-provider-enhanced-schemas) +release, resolves actions against a pinned botocore checkout, and verifies writable mappings against this repository's +compiled schemas. It rejects unavailable lifecycle operations, unsafe nested shapes, unreviewed operation collisions, +and known data-plane actions. Curated update adapters remain explicit because update requests carry partial state. + +Maintainers regenerate it only after the compiled schemas have been generated: + +```bash +PYTHONPATH= \ +python3 scripts/generate_aws_api_catalog.py \ + --provider-schemas \ + --compiled-schemas generated/schema-validator/compiled_schemas.json \ + --output generated/data/aws_api_operation_catalog.json +``` + +The output records SHA-256 hashes for both schema inputs, the botocore version and service count, and source/type counts. +Use the same enhanced-schema release, compiled-schema artifact, and botocore version to reproduce a catalog byte for +byte. Run `python3 -m unittest scripts/test_generate_aws_api_catalog.py` with that botocore checkout on `PYTHONPATH` +before committing a maintainer-generated artifact. diff --git a/src/data-source/build.rs b/src/data-source/build.rs index 6ffccacc..454dddf2 100644 --- a/src/data-source/build.rs +++ b/src/data-source/build.rs @@ -25,6 +25,7 @@ const GENERATED_JSON: &[(&str, &str)] = &[ ("getatt_attributes", "GETATT_ATTRIBUTES"), ("known_resource_types", "KNOWN_RESOURCE_TYPES"), ("stateful_resource_types", "STATEFUL_RESOURCE_TYPES"), + ("aws_api_operation_catalog", "AWS_API_OPERATION_CATALOG"), // Tables extracted from cfn-lint rule code during sync ("retention_period_requirements", "RETENTION_PERIOD_REQUIREMENTS"), ("codepipeline_action_artifact_counts", "CODEPIPELINE_ACTION_ARTIFACT_COUNTS"), diff --git a/src/data-source/generated/data/aws_api_operation_catalog.json b/src/data-source/generated/data/aws_api_operation_catalog.json new file mode 100644 index 00000000..5416d8b7 --- /dev/null +++ b/src/data-source/generated/data/aws_api_operation_catalog.json @@ -0,0 +1,49706 @@ +{ + "adapters": [ + { + "cfn_type": "AWS::ACMPCA::Certificate", + "mappings": [ + { + "source": "ApiPassthrough", + "target": "ApiPassthrough" + }, + { + "source": "CertificateAuthorityArn", + "target": "CertificateAuthorityArn" + }, + { + "source": "SigningAlgorithm", + "target": "SigningAlgorithm" + }, + { + "source": "TemplateArn", + "target": "TemplateArn" + }, + { + "source": "Validity", + "target": "Validity" + }, + { + "source": "ValidityNotBefore", + "target": "ValidityNotBefore" + } + ], + "operation": "IssueCertificate", + "phase": "create", + "service": "acm-pca" + }, + { + "cfn_type": "AWS::ACMPCA::CertificateAuthority", + "mappings": [ + { + "source": "KeyStorageSecurityStandard", + "target": "KeyStorageSecurityStandard" + }, + { + "source": "RevocationConfiguration", + "target": "RevocationConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UsageMode", + "target": "UsageMode" + } + ], + "operation": "CreateCertificateAuthority", + "phase": "create", + "service": "acm-pca" + }, + { + "cfn_type": "AWS::ACMPCA::CertificateAuthority", + "mappings": [], + "operation": "DeleteCertificateAuthority", + "phase": "delete", + "service": "acm-pca" + }, + { + "cfn_type": "AWS::ACMPCA::CertificateAuthorityActivation", + "mappings": [ + { + "source": "Certificate", + "target": "Certificate" + }, + { + "source": "CertificateAuthorityArn", + "target": "CertificateAuthorityArn" + }, + { + "source": "CertificateChain", + "target": "CertificateChain" + } + ], + "operation": "ImportCertificateAuthorityCertificate", + "phase": "create", + "service": "acm-pca" + }, + { + "cfn_type": "AWS::ACMPCA::Permission", + "mappings": [ + { + "source": "Actions", + "target": "Actions" + }, + { + "source": "CertificateAuthorityArn", + "target": "CertificateAuthorityArn" + }, + { + "source": "Principal", + "target": "Principal" + }, + { + "source": "SourceAccount", + "target": "SourceAccount" + } + ], + "operation": "CreatePermission", + "phase": "create", + "service": "acm-pca" + }, + { + "cfn_type": "AWS::ACMPCA::Permission", + "mappings": [ + { + "source": "CertificateAuthorityArn", + "target": "CertificateAuthorityArn" + }, + { + "source": "Principal", + "target": "Principal" + }, + { + "source": "SourceAccount", + "target": "SourceAccount" + } + ], + "operation": "DeletePermission", + "phase": "delete", + "service": "acm-pca" + }, + { + "cfn_type": "AWS::AIOps::InvestigationGroup", + "mappings": [ + { + "source": "crossAccountConfigurations", + "target": "CrossAccountConfigurations" + }, + { + "source": "isCloudTrailEventHistoryEnabled", + "target": "IsCloudTrailEventHistoryEnabled" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "retentionInDays", + "target": "RetentionInDays" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tagKeyBoundaries", + "target": "TagKeyBoundaries" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateInvestigationGroup", + "phase": "create", + "service": "aiops" + }, + { + "cfn_type": "AWS::AIOps::InvestigationGroup", + "mappings": [], + "operation": "DeleteInvestigationGroup", + "phase": "delete", + "service": "aiops" + }, + { + "cfn_type": "AWS::APS::RuleGroupsNamespace", + "mappings": [ + { + "source": "data", + "target": "Data" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRuleGroupsNamespace", + "phase": "create", + "service": "amp" + }, + { + "cfn_type": "AWS::APS::RuleGroupsNamespace", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteRuleGroupsNamespace", + "phase": "delete", + "service": "amp" + }, + { + "cfn_type": "AWS::APS::Scraper", + "mappings": [ + { + "source": "alias", + "target": "Alias" + }, + { + "source": "destination", + "target": "Destination" + }, + { + "source": "roleConfiguration", + "target": "RoleConfiguration" + }, + { + "source": "scrapeConfiguration", + "target": "ScrapeConfiguration" + }, + { + "source": "source", + "target": "Source" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateScraper", + "phase": "create", + "service": "amp" + }, + { + "cfn_type": "AWS::APS::Scraper", + "mappings": [], + "operation": "DeleteScraper", + "phase": "delete", + "service": "amp" + }, + { + "cfn_type": "AWS::APS::Workspace", + "mappings": [ + { + "source": "alias", + "target": "Alias" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateWorkspace", + "phase": "create", + "service": "amp" + }, + { + "cfn_type": "AWS::APS::Workspace", + "mappings": [], + "operation": "DeleteWorkspace", + "phase": "delete", + "service": "amp" + }, + { + "cfn_type": "AWS::ARCRegionSwitch::Plan", + "mappings": [ + { + "source": "associatedAlarms", + "target": "AssociatedAlarms" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "executionRole", + "target": "ExecutionRole" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "primaryRegion", + "target": "PrimaryRegion" + }, + { + "source": "recoveryApproach", + "target": "RecoveryApproach" + }, + { + "source": "recoveryTimeObjectiveMinutes", + "target": "RecoveryTimeObjectiveMinutes" + }, + { + "source": "regions", + "target": "Regions" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "triggers", + "target": "Triggers" + }, + { + "source": "workflows", + "target": "Workflows" + } + ], + "operation": "CreatePlan", + "phase": "create", + "service": "arc-region-switch" + }, + { + "cfn_type": "AWS::ARCRegionSwitch::Plan", + "mappings": [], + "operation": "DeletePlan", + "phase": "delete", + "service": "arc-region-switch" + }, + { + "cfn_type": "AWS::AccessAnalyzer::Analyzer", + "mappings": [ + { + "source": "analyzerName", + "target": "AnalyzerName" + }, + { + "source": "archiveRules", + "target": "ArchiveRules" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateAnalyzer", + "phase": "create", + "service": "accessanalyzer" + }, + { + "cfn_type": "AWS::AccessAnalyzer::Analyzer", + "mappings": [ + { + "source": "analyzerName", + "target": "AnalyzerName" + } + ], + "operation": "DeleteAnalyzer", + "phase": "delete", + "service": "accessanalyzer" + }, + { + "cfn_type": "AWS::AmazonMQ::Broker", + "mappings": [ + { + "source": "AuthenticationStrategy", + "target": "AuthenticationStrategy" + }, + { + "source": "AutoMinorVersionUpgrade", + "target": "AutoMinorVersionUpgrade" + }, + { + "source": "BrokerName", + "target": "BrokerName" + }, + { + "source": "Configuration", + "target": "Configuration" + }, + { + "source": "DataReplicationMode", + "target": "DataReplicationMode" + }, + { + "source": "DataReplicationPrimaryBrokerArn", + "target": "DataReplicationPrimaryBrokerArn" + }, + { + "source": "DeploymentMode", + "target": "DeploymentMode" + }, + { + "source": "EncryptionOptions", + "target": "EncryptionOptions" + }, + { + "source": "EngineType", + "target": "EngineType" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "HostInstanceType", + "target": "HostInstanceType" + }, + { + "source": "LdapServerMetadata", + "target": "LdapServerMetadata" + }, + { + "source": "Logs", + "target": "Logs" + }, + { + "source": "MaintenanceWindowStartTime", + "target": "MaintenanceWindowStartTime" + }, + { + "source": "PubliclyAccessible", + "target": "PubliclyAccessible" + }, + { + "source": "SecurityGroups", + "target": "SecurityGroups" + }, + { + "source": "StorageType", + "target": "StorageType" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Users", + "target": "Users" + } + ], + "operation": "CreateBroker", + "phase": "create", + "service": "mq" + }, + { + "cfn_type": "AWS::AmazonMQ::Broker", + "mappings": [], + "operation": "DeleteBroker", + "phase": "delete", + "service": "mq" + }, + { + "cfn_type": "AWS::AmazonMQ::Configuration", + "mappings": [ + { + "source": "AuthenticationStrategy", + "target": "AuthenticationStrategy" + }, + { + "source": "EngineType", + "target": "EngineType" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateConfiguration", + "phase": "create", + "service": "mq" + }, + { + "cfn_type": "AWS::AmazonMQ::Configuration", + "mappings": [], + "operation": "DeleteConfiguration", + "phase": "delete", + "service": "mq" + }, + { + "cfn_type": "AWS::Amplify::App", + "mappings": [ + { + "source": "accessToken", + "target": "AccessToken" + }, + { + "source": "autoBranchCreationConfig", + "target": "AutoBranchCreationConfig" + }, + { + "source": "buildSpec", + "target": "BuildSpec" + }, + { + "source": "cacheConfig", + "target": "CacheConfig" + }, + { + "source": "computeRoleArn", + "target": "ComputeRoleArn" + }, + { + "source": "customHeaders", + "target": "CustomHeaders" + }, + { + "source": "customRules", + "target": "CustomRules" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "enableBranchAutoDeletion", + "target": "EnableBranchAutoDeletion" + }, + { + "source": "environmentVariables", + "target": "EnvironmentVariables" + }, + { + "source": "jobConfig", + "target": "JobConfig" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "oauthToken", + "target": "OauthToken" + }, + { + "source": "platform", + "target": "Platform" + }, + { + "source": "repository", + "target": "Repository" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateApp", + "phase": "create", + "service": "amplify" + }, + { + "cfn_type": "AWS::Amplify::App", + "mappings": [], + "operation": "DeleteApp", + "phase": "delete", + "service": "amplify" + }, + { + "cfn_type": "AWS::Amplify::Branch", + "mappings": [ + { + "source": "appId", + "target": "AppId" + }, + { + "source": "backend", + "target": "Backend" + }, + { + "source": "branchName", + "target": "BranchName" + }, + { + "source": "buildSpec", + "target": "BuildSpec" + }, + { + "source": "computeRoleArn", + "target": "ComputeRoleArn" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "enableAutoBuild", + "target": "EnableAutoBuild" + }, + { + "source": "enablePerformanceMode", + "target": "EnablePerformanceMode" + }, + { + "source": "enablePullRequestPreview", + "target": "EnablePullRequestPreview" + }, + { + "source": "enableSkewProtection", + "target": "EnableSkewProtection" + }, + { + "source": "environmentVariables", + "target": "EnvironmentVariables" + }, + { + "source": "framework", + "target": "Framework" + }, + { + "source": "pullRequestEnvironmentName", + "target": "PullRequestEnvironmentName" + }, + { + "source": "stage", + "target": "Stage" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateBranch", + "phase": "create", + "service": "amplify" + }, + { + "cfn_type": "AWS::Amplify::Branch", + "mappings": [ + { + "source": "appId", + "target": "AppId" + }, + { + "source": "branchName", + "target": "BranchName" + } + ], + "operation": "DeleteBranch", + "phase": "delete", + "service": "amplify" + }, + { + "cfn_type": "AWS::Amplify::Domain", + "mappings": [ + { + "source": "appId", + "target": "AppId" + }, + { + "source": "autoSubDomainCreationPatterns", + "target": "AutoSubDomainCreationPatterns" + }, + { + "source": "autoSubDomainIAMRole", + "target": "AutoSubDomainIAMRole" + }, + { + "source": "certificateSettings", + "target": "CertificateSettings" + }, + { + "source": "domainName", + "target": "DomainName" + }, + { + "source": "enableAutoSubDomain", + "target": "EnableAutoSubDomain" + }, + { + "source": "subDomainSettings", + "target": "SubDomainSettings" + } + ], + "operation": "CreateDomainAssociation", + "phase": "create", + "service": "amplify" + }, + { + "cfn_type": "AWS::Amplify::Domain", + "mappings": [ + { + "source": "appId", + "target": "AppId" + }, + { + "source": "domainName", + "target": "DomainName" + } + ], + "operation": "DeleteDomainAssociation", + "phase": "delete", + "service": "amplify" + }, + { + "cfn_type": "AWS::AmplifyUIBuilder::Component", + "mappings": [ + { + "source": "appId", + "target": "AppId" + }, + { + "source": "environmentName", + "target": "EnvironmentName" + } + ], + "operation": "CreateComponent", + "phase": "create", + "service": "amplifyuibuilder" + }, + { + "cfn_type": "AWS::AmplifyUIBuilder::Component", + "mappings": [ + { + "source": "appId", + "target": "AppId" + }, + { + "source": "environmentName", + "target": "EnvironmentName" + } + ], + "operation": "DeleteComponent", + "phase": "delete", + "service": "amplifyuibuilder" + }, + { + "cfn_type": "AWS::AmplifyUIBuilder::Form", + "mappings": [ + { + "source": "appId", + "target": "AppId" + }, + { + "source": "environmentName", + "target": "EnvironmentName" + } + ], + "operation": "CreateForm", + "phase": "create", + "service": "amplifyuibuilder" + }, + { + "cfn_type": "AWS::AmplifyUIBuilder::Form", + "mappings": [ + { + "source": "appId", + "target": "AppId" + }, + { + "source": "environmentName", + "target": "EnvironmentName" + } + ], + "operation": "DeleteForm", + "phase": "delete", + "service": "amplifyuibuilder" + }, + { + "cfn_type": "AWS::AmplifyUIBuilder::Theme", + "mappings": [ + { + "source": "appId", + "target": "AppId" + }, + { + "source": "environmentName", + "target": "EnvironmentName" + } + ], + "operation": "CreateTheme", + "phase": "create", + "service": "amplifyuibuilder" + }, + { + "cfn_type": "AWS::AmplifyUIBuilder::Theme", + "mappings": [ + { + "source": "appId", + "target": "AppId" + }, + { + "source": "environmentName", + "target": "EnvironmentName" + } + ], + "operation": "DeleteTheme", + "phase": "delete", + "service": "amplifyuibuilder" + }, + { + "cfn_type": "AWS::ApiGatewayV2::RoutingRule", + "mappings": [ + { + "source": "Actions", + "target": "Actions" + }, + { + "source": "Conditions", + "target": "Conditions" + }, + { + "source": "Priority", + "target": "Priority" + } + ], + "operation": "CreateRoutingRule", + "phase": "create", + "service": "apigatewayv2" + }, + { + "cfn_type": "AWS::ApiGatewayV2::RoutingRule", + "mappings": [], + "operation": "DeleteRoutingRule", + "phase": "delete", + "service": "apigatewayv2" + }, + { + "cfn_type": "AWS::AppConfig::Application", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::Application", + "mappings": [], + "operation": "DeleteApplication", + "phase": "delete", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::ConfigurationProfile", + "mappings": [ + { + "source": "ApplicationId", + "target": "ApplicationId" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "KmsKeyIdentifier", + "target": "KmsKeyIdentifier" + }, + { + "source": "LocationUri", + "target": "LocationUri" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RetrievalRoleArn", + "target": "RetrievalRoleArn" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + }, + { + "source": "Validators", + "target": "Validators" + } + ], + "operation": "CreateConfigurationProfile", + "phase": "create", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::ConfigurationProfile", + "mappings": [ + { + "source": "ApplicationId", + "target": "ApplicationId" + }, + { + "source": "DeletionProtectionCheck", + "target": "DeletionProtectionCheck" + } + ], + "operation": "DeleteConfigurationProfile", + "phase": "delete", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::Deployment", + "mappings": [ + { + "source": "ApplicationId", + "target": "ApplicationId" + }, + { + "source": "ConfigurationProfileId", + "target": "ConfigurationProfileId" + }, + { + "source": "ConfigurationVersion", + "target": "ConfigurationVersion" + }, + { + "source": "DeploymentStrategyId", + "target": "DeploymentStrategyId" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DynamicExtensionParameters", + "target": "DynamicExtensionParameters" + }, + { + "source": "EnvironmentId", + "target": "EnvironmentId" + }, + { + "source": "KmsKeyIdentifier", + "target": "KmsKeyIdentifier" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "StartDeployment", + "phase": "create", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::DeploymentStrategy", + "mappings": [ + { + "source": "DeploymentDurationInMinutes", + "target": "DeploymentDurationInMinutes" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "FinalBakeTimeInMinutes", + "target": "FinalBakeTimeInMinutes" + }, + { + "source": "GrowthFactor", + "target": "GrowthFactor" + }, + { + "source": "GrowthType", + "target": "GrowthType" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ReplicateTo", + "target": "ReplicateTo" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDeploymentStrategy", + "phase": "create", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::DeploymentStrategy", + "mappings": [], + "operation": "DeleteDeploymentStrategy", + "phase": "delete", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::Environment", + "mappings": [ + { + "source": "ApplicationId", + "target": "ApplicationId" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Monitors", + "target": "Monitors" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEnvironment", + "phase": "create", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::Environment", + "mappings": [ + { + "source": "ApplicationId", + "target": "ApplicationId" + }, + { + "source": "DeletionProtectionCheck", + "target": "DeletionProtectionCheck" + } + ], + "operation": "DeleteEnvironment", + "phase": "delete", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::Extension", + "mappings": [ + { + "source": "Actions", + "target": "Actions" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "LatestVersionNumber", + "target": "LatestVersionNumber" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Parameters", + "target": "Parameters" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateExtension", + "phase": "create", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::Extension", + "mappings": [], + "operation": "DeleteExtension", + "phase": "delete", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::ExtensionAssociation", + "mappings": [ + { + "source": "ExtensionIdentifier", + "target": "ExtensionIdentifier" + }, + { + "source": "ExtensionVersionNumber", + "target": "ExtensionVersionNumber" + }, + { + "source": "Parameters", + "target": "Parameters" + }, + { + "source": "ResourceIdentifier", + "target": "ResourceIdentifier" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateExtensionAssociation", + "phase": "create", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::ExtensionAssociation", + "mappings": [], + "operation": "DeleteExtensionAssociation", + "phase": "delete", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::HostedConfigurationVersion", + "mappings": [ + { + "source": "ApplicationId", + "target": "ApplicationId" + }, + { + "source": "ConfigurationProfileId", + "target": "ConfigurationProfileId" + }, + { + "source": "Content", + "target": "Content" + }, + { + "source": "ContentType", + "target": "ContentType" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "LatestVersionNumber", + "target": "LatestVersionNumber" + }, + { + "source": "VersionLabel", + "target": "VersionLabel" + } + ], + "operation": "CreateHostedConfigurationVersion", + "phase": "create", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::HostedConfigurationVersion", + "mappings": [ + { + "source": "ApplicationId", + "target": "ApplicationId" + }, + { + "source": "ConfigurationProfileId", + "target": "ConfigurationProfileId" + } + ], + "operation": "DeleteHostedConfigurationVersion", + "phase": "delete", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppFlow::Connector", + "mappings": [ + { + "source": "connectorLabel", + "target": "ConnectorLabel" + }, + { + "source": "connectorProvisioningConfig", + "target": "ConnectorProvisioningConfig" + }, + { + "source": "connectorProvisioningType", + "target": "ConnectorProvisioningType" + }, + { + "source": "description", + "target": "Description" + } + ], + "operation": "RegisterConnector", + "phase": "create", + "service": "appflow" + }, + { + "cfn_type": "AWS::AppFlow::ConnectorProfile", + "mappings": [ + { + "source": "connectionMode", + "target": "ConnectionMode" + }, + { + "source": "connectorLabel", + "target": "ConnectorLabel" + }, + { + "source": "connectorProfileConfig", + "target": "ConnectorProfileConfig" + }, + { + "source": "connectorProfileName", + "target": "ConnectorProfileName" + }, + { + "source": "connectorType", + "target": "ConnectorType" + }, + { + "source": "kmsArn", + "target": "KMSArn" + } + ], + "operation": "CreateConnectorProfile", + "phase": "create", + "service": "appflow" + }, + { + "cfn_type": "AWS::AppFlow::ConnectorProfile", + "mappings": [ + { + "source": "connectorProfileName", + "target": "ConnectorProfileName" + } + ], + "operation": "DeleteConnectorProfile", + "phase": "delete", + "service": "appflow" + }, + { + "cfn_type": "AWS::AppFlow::Flow", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "destinationFlowConfigList", + "target": "DestinationFlowConfigList" + }, + { + "source": "flowName", + "target": "FlowName" + }, + { + "source": "kmsArn", + "target": "KMSArn" + }, + { + "source": "metadataCatalogConfig", + "target": "MetadataCatalogConfig" + }, + { + "source": "sourceFlowConfig", + "target": "SourceFlowConfig" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "tasks", + "target": "Tasks" + }, + { + "source": "triggerConfig", + "target": "TriggerConfig" + } + ], + "operation": "CreateFlow", + "phase": "create", + "service": "appflow" + }, + { + "cfn_type": "AWS::AppFlow::Flow", + "mappings": [ + { + "source": "flowName", + "target": "FlowName" + } + ], + "operation": "DeleteFlow", + "phase": "delete", + "service": "appflow" + }, + { + "cfn_type": "AWS::AppIntegrations::Application", + "mappings": [ + { + "source": "ApplicationConfig", + "target": "ApplicationConfig" + }, + { + "source": "ApplicationSourceConfig", + "target": "ApplicationSourceConfig" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "IframeConfig", + "target": "IframeConfig" + }, + { + "source": "InitializationTimeout", + "target": "InitializationTimeout" + }, + { + "source": "IsService", + "target": "IsService" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Namespace", + "target": "Namespace" + }, + { + "source": "Permissions", + "target": "Permissions" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "appintegrations" + }, + { + "cfn_type": "AWS::AppIntegrations::Application", + "mappings": [], + "operation": "DeleteApplication", + "phase": "delete", + "service": "appintegrations" + }, + { + "cfn_type": "AWS::AppIntegrations::DataIntegration", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "FileConfiguration", + "target": "FileConfiguration" + }, + { + "source": "KmsKey", + "target": "KmsKey" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ObjectConfiguration", + "target": "ObjectConfiguration" + }, + { + "source": "ScheduleConfig", + "target": "ScheduleConfig" + }, + { + "source": "SourceURI", + "target": "SourceURI" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDataIntegration", + "phase": "create", + "service": "appintegrations" + }, + { + "cfn_type": "AWS::AppIntegrations::DataIntegration", + "mappings": [], + "operation": "DeleteDataIntegration", + "phase": "delete", + "service": "appintegrations" + }, + { + "cfn_type": "AWS::AppIntegrations::EventIntegration", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "EventBridgeBus", + "target": "EventBridgeBus" + }, + { + "source": "EventFilter", + "target": "EventFilter" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEventIntegration", + "phase": "create", + "service": "appintegrations" + }, + { + "cfn_type": "AWS::AppIntegrations::EventIntegration", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteEventIntegration", + "phase": "delete", + "service": "appintegrations" + }, + { + "cfn_type": "AWS::AppRunner::AutoScalingConfiguration", + "mappings": [ + { + "source": "AutoScalingConfigurationName", + "target": "AutoScalingConfigurationName" + }, + { + "source": "MaxConcurrency", + "target": "MaxConcurrency" + }, + { + "source": "MaxSize", + "target": "MaxSize" + }, + { + "source": "MinSize", + "target": "MinSize" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAutoScalingConfiguration", + "phase": "create", + "service": "apprunner" + }, + { + "cfn_type": "AWS::AppRunner::AutoScalingConfiguration", + "mappings": [], + "operation": "DeleteAutoScalingConfiguration", + "phase": "delete", + "service": "apprunner" + }, + { + "cfn_type": "AWS::AppRunner::ObservabilityConfiguration", + "mappings": [ + { + "source": "ObservabilityConfigurationName", + "target": "ObservabilityConfigurationName" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TraceConfiguration", + "target": "TraceConfiguration" + } + ], + "operation": "CreateObservabilityConfiguration", + "phase": "create", + "service": "apprunner" + }, + { + "cfn_type": "AWS::AppRunner::ObservabilityConfiguration", + "mappings": [], + "operation": "DeleteObservabilityConfiguration", + "phase": "delete", + "service": "apprunner" + }, + { + "cfn_type": "AWS::AppRunner::Service", + "mappings": [ + { + "source": "AutoScalingConfigurationArn", + "target": "AutoScalingConfigurationArn" + }, + { + "source": "EncryptionConfiguration", + "target": "EncryptionConfiguration" + }, + { + "source": "HealthCheckConfiguration", + "target": "HealthCheckConfiguration" + }, + { + "source": "InstanceConfiguration", + "target": "InstanceConfiguration" + }, + { + "source": "NetworkConfiguration", + "target": "NetworkConfiguration" + }, + { + "source": "ObservabilityConfiguration", + "target": "ObservabilityConfiguration" + }, + { + "source": "ServiceName", + "target": "ServiceName" + }, + { + "source": "SourceConfiguration", + "target": "SourceConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateService", + "phase": "create", + "service": "apprunner" + }, + { + "cfn_type": "AWS::AppRunner::Service", + "mappings": [], + "operation": "DeleteService", + "phase": "delete", + "service": "apprunner" + }, + { + "cfn_type": "AWS::AppRunner::VpcConnector", + "mappings": [ + { + "source": "SecurityGroups", + "target": "SecurityGroups" + }, + { + "source": "Subnets", + "target": "Subnets" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcConnectorName", + "target": "VpcConnectorName" + } + ], + "operation": "CreateVpcConnector", + "phase": "create", + "service": "apprunner" + }, + { + "cfn_type": "AWS::AppRunner::VpcConnector", + "mappings": [], + "operation": "DeleteVpcConnector", + "phase": "delete", + "service": "apprunner" + }, + { + "cfn_type": "AWS::AppRunner::VpcIngressConnection", + "mappings": [ + { + "source": "IngressVpcConfiguration", + "target": "IngressVpcConfiguration" + }, + { + "source": "ServiceArn", + "target": "ServiceArn" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcIngressConnectionName", + "target": "VpcIngressConnectionName" + } + ], + "operation": "CreateVpcIngressConnection", + "phase": "create", + "service": "apprunner" + }, + { + "cfn_type": "AWS::AppRunner::VpcIngressConnection", + "mappings": [], + "operation": "DeleteVpcIngressConnection", + "phase": "delete", + "service": "apprunner" + }, + { + "cfn_type": "AWS::AppStream::AppBlock", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "PackagingType", + "target": "PackagingType" + }, + { + "source": "PostSetupScriptDetails", + "target": "PostSetupScriptDetails" + }, + { + "source": "SetupScriptDetails", + "target": "SetupScriptDetails" + }, + { + "source": "SourceS3Location", + "target": "SourceS3Location" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAppBlock", + "phase": "create", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::AppBlock", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteAppBlock", + "phase": "delete", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::AppBlockBuilder", + "mappings": [ + { + "source": "AccessEndpoints", + "target": "AccessEndpoints" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "EnableDefaultInternetAccess", + "target": "EnableDefaultInternetAccess" + }, + { + "source": "IamRoleArn", + "target": "IamRoleArn" + }, + { + "source": "InstanceType", + "target": "InstanceType" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Platform", + "target": "Platform" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcConfig", + "target": "VpcConfig" + } + ], + "operation": "CreateAppBlockBuilder", + "phase": "create", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::AppBlockBuilder", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteAppBlockBuilder", + "phase": "delete", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::Application", + "mappings": [ + { + "source": "AppBlockArn", + "target": "AppBlockArn" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "IconS3Location", + "target": "IconS3Location" + }, + { + "source": "InstanceFamilies", + "target": "InstanceFamilies" + }, + { + "source": "LaunchParameters", + "target": "LaunchParameters" + }, + { + "source": "LaunchPath", + "target": "LaunchPath" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Platforms", + "target": "Platforms" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "WorkingDirectory", + "target": "WorkingDirectory" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::Application", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteApplication", + "phase": "delete", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::ApplicationEntitlementAssociation", + "mappings": [ + { + "source": "ApplicationIdentifier", + "target": "ApplicationIdentifier" + }, + { + "source": "EntitlementName", + "target": "EntitlementName" + }, + { + "source": "StackName", + "target": "StackName" + } + ], + "operation": "AssociateApplicationToEntitlement", + "phase": "create", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::ApplicationEntitlementAssociation", + "mappings": [ + { + "source": "ApplicationIdentifier", + "target": "ApplicationIdentifier" + }, + { + "source": "EntitlementName", + "target": "EntitlementName" + }, + { + "source": "StackName", + "target": "StackName" + } + ], + "operation": "DisassociateApplicationFromEntitlement", + "phase": "delete", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::ApplicationFleetAssociation", + "mappings": [ + { + "source": "ApplicationArn", + "target": "ApplicationArn" + }, + { + "source": "FleetName", + "target": "FleetName" + } + ], + "operation": "AssociateApplicationFleet", + "phase": "create", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::ApplicationFleetAssociation", + "mappings": [ + { + "source": "ApplicationArn", + "target": "ApplicationArn" + }, + { + "source": "FleetName", + "target": "FleetName" + } + ], + "operation": "DisassociateApplicationFleet", + "phase": "delete", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::DirectoryConfig", + "mappings": [ + { + "source": "CertificateBasedAuthProperties", + "target": "CertificateBasedAuthProperties" + }, + { + "source": "DirectoryName", + "target": "DirectoryName" + }, + { + "source": "OrganizationalUnitDistinguishedNames", + "target": "OrganizationalUnitDistinguishedNames" + }, + { + "source": "ServiceAccountCredentials", + "target": "ServiceAccountCredentials" + } + ], + "operation": "CreateDirectoryConfig", + "phase": "create", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::DirectoryConfig", + "mappings": [ + { + "source": "DirectoryName", + "target": "DirectoryName" + } + ], + "operation": "DeleteDirectoryConfig", + "phase": "delete", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::Entitlement", + "mappings": [ + { + "source": "AppVisibility", + "target": "AppVisibility" + }, + { + "source": "Attributes", + "target": "Attributes" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "StackName", + "target": "StackName" + } + ], + "operation": "CreateEntitlement", + "phase": "create", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::Entitlement", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "StackName", + "target": "StackName" + } + ], + "operation": "DeleteEntitlement", + "phase": "delete", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::ImageBuilder", + "mappings": [ + { + "source": "AccessEndpoints", + "target": "AccessEndpoints" + }, + { + "source": "AppstreamAgentVersion", + "target": "AppstreamAgentVersion" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "DomainJoinInfo", + "target": "DomainJoinInfo" + }, + { + "source": "EnableDefaultInternetAccess", + "target": "EnableDefaultInternetAccess" + }, + { + "source": "IamRoleArn", + "target": "IamRoleArn" + }, + { + "source": "ImageArn", + "target": "ImageArn" + }, + { + "source": "ImageName", + "target": "ImageName" + }, + { + "source": "InstanceType", + "target": "InstanceType" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcConfig", + "target": "VpcConfig" + } + ], + "operation": "CreateImageBuilder", + "phase": "create", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::ImageBuilder", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteImageBuilder", + "phase": "delete", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::Stack", + "mappings": [ + { + "source": "AccessEndpoints", + "target": "AccessEndpoints" + }, + { + "source": "ApplicationSettings", + "target": "ApplicationSettings" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "EmbedHostDomains", + "target": "EmbedHostDomains" + }, + { + "source": "FeedbackURL", + "target": "FeedbackURL" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RedirectURL", + "target": "RedirectURL" + }, + { + "source": "StorageConnectors", + "target": "StorageConnectors" + }, + { + "source": "StreamingExperienceSettings", + "target": "StreamingExperienceSettings" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UserSettings", + "target": "UserSettings" + } + ], + "operation": "CreateStack", + "phase": "create", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::Stack", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteStack", + "phase": "delete", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::User", + "mappings": [ + { + "source": "AuthenticationType", + "target": "AuthenticationType" + }, + { + "source": "FirstName", + "target": "FirstName" + }, + { + "source": "LastName", + "target": "LastName" + }, + { + "source": "MessageAction", + "target": "MessageAction" + }, + { + "source": "UserName", + "target": "UserName" + } + ], + "operation": "CreateUser", + "phase": "create", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppStream::User", + "mappings": [ + { + "source": "AuthenticationType", + "target": "AuthenticationType" + }, + { + "source": "UserName", + "target": "UserName" + } + ], + "operation": "DeleteUser", + "phase": "delete", + "service": "appstream" + }, + { + "cfn_type": "AWS::AppSync::Api", + "mappings": [ + { + "source": "eventConfig", + "target": "EventConfig" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "ownerContact", + "target": "OwnerContact" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateApi", + "phase": "create", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::Api", + "mappings": [], + "operation": "DeleteApi", + "phase": "delete", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::ChannelNamespace", + "mappings": [ + { + "source": "apiId", + "target": "ApiId" + }, + { + "source": "codeHandlers", + "target": "CodeHandlers" + }, + { + "source": "handlerConfigs", + "target": "HandlerConfigs" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "publishAuthModes", + "target": "PublishAuthModes" + }, + { + "source": "subscribeAuthModes", + "target": "SubscribeAuthModes" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateChannelNamespace", + "phase": "create", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::ChannelNamespace", + "mappings": [ + { + "source": "apiId", + "target": "ApiId" + }, + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteChannelNamespace", + "phase": "delete", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::DataSource", + "mappings": [ + { + "source": "apiId", + "target": "ApiId" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "dynamodbConfig", + "target": "DynamoDBConfig" + }, + { + "source": "elasticsearchConfig", + "target": "ElasticsearchConfig" + }, + { + "source": "eventBridgeConfig", + "target": "EventBridgeConfig" + }, + { + "source": "httpConfig", + "target": "HttpConfig" + }, + { + "source": "lambdaConfig", + "target": "LambdaConfig" + }, + { + "source": "metricsConfig", + "target": "MetricsConfig" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "openSearchServiceConfig", + "target": "OpenSearchServiceConfig" + }, + { + "source": "relationalDatabaseConfig", + "target": "RelationalDatabaseConfig" + }, + { + "source": "serviceRoleArn", + "target": "ServiceRoleArn" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateDataSource", + "phase": "create", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::DataSource", + "mappings": [ + { + "source": "apiId", + "target": "ApiId" + }, + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteDataSource", + "phase": "delete", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::DomainName", + "mappings": [ + { + "source": "certificateArn", + "target": "CertificateArn" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "domainName", + "target": "DomainName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDomainName", + "phase": "create", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::DomainName", + "mappings": [ + { + "source": "domainName", + "target": "DomainName" + } + ], + "operation": "DeleteDomainName", + "phase": "delete", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::DomainNameApiAssociation", + "mappings": [ + { + "source": "apiId", + "target": "ApiId" + }, + { + "source": "domainName", + "target": "DomainName" + } + ], + "operation": "AssociateApi", + "phase": "create", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::FunctionConfiguration", + "mappings": [ + { + "source": "apiId", + "target": "ApiId" + }, + { + "source": "code", + "target": "Code" + }, + { + "source": "dataSourceName", + "target": "DataSourceName" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "functionVersion", + "target": "FunctionVersion" + }, + { + "source": "maxBatchSize", + "target": "MaxBatchSize" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "requestMappingTemplate", + "target": "RequestMappingTemplate" + }, + { + "source": "responseMappingTemplate", + "target": "ResponseMappingTemplate" + }, + { + "source": "runtime", + "target": "Runtime" + }, + { + "source": "syncConfig", + "target": "SyncConfig" + } + ], + "operation": "CreateFunction", + "phase": "create", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::GraphQLApi", + "mappings": [ + { + "source": "additionalAuthenticationProviders", + "target": "AdditionalAuthenticationProviders" + }, + { + "source": "apiType", + "target": "ApiType" + }, + { + "source": "authenticationType", + "target": "AuthenticationType" + }, + { + "source": "enhancedMetricsConfig", + "target": "EnhancedMetricsConfig" + }, + { + "source": "introspectionConfig", + "target": "IntrospectionConfig" + }, + { + "source": "lambdaAuthorizerConfig", + "target": "LambdaAuthorizerConfig" + }, + { + "source": "logConfig", + "target": "LogConfig" + }, + { + "source": "mergedApiExecutionRoleArn", + "target": "MergedApiExecutionRoleArn" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "openIDConnectConfig", + "target": "OpenIDConnectConfig" + }, + { + "source": "ownerContact", + "target": "OwnerContact" + }, + { + "source": "queryDepthLimit", + "target": "QueryDepthLimit" + }, + { + "source": "resolverCountLimit", + "target": "ResolverCountLimit" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "userPoolConfig", + "target": "UserPoolConfig" + }, + { + "source": "visibility", + "target": "Visibility" + }, + { + "source": "xrayEnabled", + "target": "XrayEnabled" + } + ], + "operation": "CreateGraphqlApi", + "phase": "create", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::GraphQLApi", + "mappings": [], + "operation": "DeleteGraphqlApi", + "phase": "delete", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::Resolver", + "mappings": [ + { + "source": "apiId", + "target": "ApiId" + }, + { + "source": "cachingConfig", + "target": "CachingConfig" + }, + { + "source": "code", + "target": "Code" + }, + { + "source": "dataSourceName", + "target": "DataSourceName" + }, + { + "source": "fieldName", + "target": "FieldName" + }, + { + "source": "kind", + "target": "Kind" + }, + { + "source": "maxBatchSize", + "target": "MaxBatchSize" + }, + { + "source": "metricsConfig", + "target": "MetricsConfig" + }, + { + "source": "pipelineConfig", + "target": "PipelineConfig" + }, + { + "source": "requestMappingTemplate", + "target": "RequestMappingTemplate" + }, + { + "source": "responseMappingTemplate", + "target": "ResponseMappingTemplate" + }, + { + "source": "runtime", + "target": "Runtime" + }, + { + "source": "syncConfig", + "target": "SyncConfig" + }, + { + "source": "typeName", + "target": "TypeName" + } + ], + "operation": "CreateResolver", + "phase": "create", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::Resolver", + "mappings": [ + { + "source": "apiId", + "target": "ApiId" + }, + { + "source": "fieldName", + "target": "FieldName" + }, + { + "source": "typeName", + "target": "TypeName" + } + ], + "operation": "DeleteResolver", + "phase": "delete", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppTest::TestCase", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "steps", + "target": "Steps" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateTestCase", + "phase": "create", + "service": "apptest" + }, + { + "cfn_type": "AWS::AppTest::TestCase", + "mappings": [], + "operation": "DeleteTestCase", + "phase": "delete", + "service": "apptest" + }, + { + "cfn_type": "AWS::ApplicationAutoScaling::ScalableTarget", + "mappings": [ + { + "source": "MaxCapacity", + "target": "MaxCapacity" + }, + { + "source": "MinCapacity", + "target": "MinCapacity" + }, + { + "source": "ResourceId", + "target": "ResourceId" + }, + { + "source": "RoleARN", + "target": "RoleARN" + }, + { + "source": "ScalableDimension", + "target": "ScalableDimension" + }, + { + "source": "ServiceNamespace", + "target": "ServiceNamespace" + }, + { + "source": "SuspendedState", + "target": "SuspendedState" + } + ], + "operation": "RegisterScalableTarget", + "phase": "create", + "service": "application-autoscaling" + }, + { + "cfn_type": "AWS::ApplicationAutoScaling::ScalableTarget", + "mappings": [ + { + "source": "ResourceId", + "target": "ResourceId" + }, + { + "source": "ScalableDimension", + "target": "ScalableDimension" + }, + { + "source": "ServiceNamespace", + "target": "ServiceNamespace" + } + ], + "operation": "DeregisterScalableTarget", + "phase": "delete", + "service": "application-autoscaling" + }, + { + "cfn_type": "AWS::ApplicationAutoScaling::ScalingPolicy", + "mappings": [ + { + "source": "PolicyName", + "target": "PolicyName" + }, + { + "source": "PolicyType", + "target": "PolicyType" + }, + { + "source": "PredictiveScalingPolicyConfiguration", + "target": "PredictiveScalingPolicyConfiguration" + }, + { + "source": "ResourceId", + "target": "ResourceId" + }, + { + "source": "ScalableDimension", + "target": "ScalableDimension" + }, + { + "source": "ServiceNamespace", + "target": "ServiceNamespace" + }, + { + "source": "StepScalingPolicyConfiguration", + "target": "StepScalingPolicyConfiguration" + }, + { + "source": "TargetTrackingScalingPolicyConfiguration", + "target": "TargetTrackingScalingPolicyConfiguration" + } + ], + "operation": "PutScalingPolicy", + "phase": "create", + "service": "application-autoscaling" + }, + { + "cfn_type": "AWS::ApplicationAutoScaling::ScalingPolicy", + "mappings": [ + { + "source": "PolicyName", + "target": "PolicyName" + }, + { + "source": "ResourceId", + "target": "ResourceId" + }, + { + "source": "ScalableDimension", + "target": "ScalableDimension" + }, + { + "source": "ServiceNamespace", + "target": "ServiceNamespace" + } + ], + "operation": "DeleteScalingPolicy", + "phase": "delete", + "service": "application-autoscaling" + }, + { + "cfn_type": "AWS::ApplicationInsights::Application", + "mappings": [ + { + "source": "AttachMissingPermission", + "target": "AttachMissingPermission" + }, + { + "source": "CWEMonitorEnabled", + "target": "CWEMonitorEnabled" + }, + { + "source": "GroupingType", + "target": "GroupingType" + }, + { + "source": "OpsCenterEnabled", + "target": "OpsCenterEnabled" + }, + { + "source": "OpsItemSNSTopicArn", + "target": "OpsItemSNSTopicArn" + }, + { + "source": "ResourceGroupName", + "target": "ResourceGroupName" + }, + { + "source": "SNSNotificationArn", + "target": "SNSNotificationArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "application-insights" + }, + { + "cfn_type": "AWS::ApplicationInsights::Application", + "mappings": [ + { + "source": "ResourceGroupName", + "target": "ResourceGroupName" + } + ], + "operation": "DeleteApplication", + "phase": "delete", + "service": "application-insights" + }, + { + "cfn_type": "AWS::ApplicationSignals::ServiceLevelObjective", + "mappings": [ + { + "source": "BurnRateConfigurations", + "target": "BurnRateConfigurations" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Goal", + "target": "Goal" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateServiceLevelObjective", + "phase": "create", + "service": "application-signals" + }, + { + "cfn_type": "AWS::ApplicationSignals::ServiceLevelObjective", + "mappings": [], + "operation": "DeleteServiceLevelObjective", + "phase": "delete", + "service": "application-signals" + }, + { + "cfn_type": "AWS::Athena::CapacityReservation", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetDpus", + "target": "TargetDpus" + } + ], + "operation": "CreateCapacityReservation", + "phase": "create", + "service": "athena" + }, + { + "cfn_type": "AWS::Athena::DataCatalog", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Parameters", + "target": "Parameters" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateDataCatalog", + "phase": "create", + "service": "athena" + }, + { + "cfn_type": "AWS::Athena::DataCatalog", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteDataCatalog", + "phase": "delete", + "service": "athena" + }, + { + "cfn_type": "AWS::Athena::NamedQuery", + "mappings": [ + { + "source": "Database", + "target": "Database" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "QueryString", + "target": "QueryString" + }, + { + "source": "WorkGroup", + "target": "WorkGroup" + } + ], + "operation": "CreateNamedQuery", + "phase": "create", + "service": "athena" + }, + { + "cfn_type": "AWS::Athena::NamedQuery", + "mappings": [], + "operation": "DeleteNamedQuery", + "phase": "delete", + "service": "athena" + }, + { + "cfn_type": "AWS::Athena::PreparedStatement", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "QueryStatement", + "target": "QueryStatement" + }, + { + "source": "StatementName", + "target": "StatementName" + }, + { + "source": "WorkGroup", + "target": "WorkGroup" + } + ], + "operation": "CreatePreparedStatement", + "phase": "create", + "service": "athena" + }, + { + "cfn_type": "AWS::Athena::PreparedStatement", + "mappings": [ + { + "source": "StatementName", + "target": "StatementName" + }, + { + "source": "WorkGroup", + "target": "WorkGroup" + } + ], + "operation": "DeletePreparedStatement", + "phase": "delete", + "service": "athena" + }, + { + "cfn_type": "AWS::Athena::WorkGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateWorkGroup", + "phase": "create", + "service": "athena" + }, + { + "cfn_type": "AWS::Athena::WorkGroup", + "mappings": [ + { + "source": "RecursiveDeleteOption", + "target": "RecursiveDeleteOption" + } + ], + "operation": "DeleteWorkGroup", + "phase": "delete", + "service": "athena" + }, + { + "cfn_type": "AWS::AuditManager::Assessment", + "mappings": [ + { + "source": "assessmentReportsDestination", + "target": "AssessmentReportsDestination" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "frameworkId", + "target": "FrameworkId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "roles", + "target": "Roles" + }, + { + "source": "scope", + "target": "Scope" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAssessment", + "phase": "create", + "service": "auditmanager" + }, + { + "cfn_type": "AWS::AuditManager::Assessment", + "mappings": [], + "operation": "DeleteAssessment", + "phase": "delete", + "service": "auditmanager" + }, + { + "cfn_type": "AWS::AuditManager::AssessmentFramework", + "mappings": [ + { + "source": "complianceType", + "target": "ComplianceType" + }, + { + "source": "controlSets", + "target": "ControlSets" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAssessmentFramework", + "phase": "create", + "service": "auditmanager" + }, + { + "cfn_type": "AWS::AuditManager::AssessmentFramework", + "mappings": [], + "operation": "DeleteAssessmentFramework", + "phase": "delete", + "service": "auditmanager" + }, + { + "cfn_type": "AWS::AuditManager::Control", + "mappings": [ + { + "source": "actionPlanInstructions", + "target": "ActionPlanInstructions" + }, + { + "source": "actionPlanTitle", + "target": "ActionPlanTitle" + }, + { + "source": "controlMappingSources", + "target": "ControlMappingSources" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "testingInformation", + "target": "TestingInformation" + } + ], + "operation": "CreateControl", + "phase": "create", + "service": "auditmanager" + }, + { + "cfn_type": "AWS::AuditManager::Control", + "mappings": [], + "operation": "DeleteControl", + "phase": "delete", + "service": "auditmanager" + }, + { + "cfn_type": "AWS::AutoScaling::AutoScalingGroup", + "mappings": [ + { + "source": "AutoScalingGroupName", + "target": "AutoScalingGroupName" + }, + { + "source": "AvailabilityZoneDistribution", + "target": "AvailabilityZoneDistribution" + }, + { + "source": "AvailabilityZoneImpairmentPolicy", + "target": "AvailabilityZoneImpairmentPolicy" + }, + { + "source": "AvailabilityZones", + "target": "AvailabilityZones" + }, + { + "source": "CapacityRebalance", + "target": "CapacityRebalance" + }, + { + "source": "CapacityReservationSpecification", + "target": "CapacityReservationSpecification" + }, + { + "source": "Context", + "target": "Context" + }, + { + "source": "DefaultInstanceWarmup", + "target": "DefaultInstanceWarmup" + }, + { + "source": "DesiredCapacity", + "target": "DesiredCapacity" + }, + { + "source": "DesiredCapacityType", + "target": "DesiredCapacityType" + }, + { + "source": "HealthCheckGracePeriod", + "target": "HealthCheckGracePeriod" + }, + { + "source": "HealthCheckType", + "target": "HealthCheckType" + }, + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "InstanceMaintenancePolicy", + "target": "InstanceMaintenancePolicy" + }, + { + "source": "LaunchConfigurationName", + "target": "LaunchConfigurationName" + }, + { + "source": "LaunchTemplate", + "target": "LaunchTemplate" + }, + { + "source": "LifecycleHookSpecificationList", + "target": "LifecycleHookSpecificationList" + }, + { + "source": "LoadBalancerNames", + "target": "LoadBalancerNames" + }, + { + "source": "MaxInstanceLifetime", + "target": "MaxInstanceLifetime" + }, + { + "source": "MaxSize", + "target": "MaxSize" + }, + { + "source": "MinSize", + "target": "MinSize" + }, + { + "source": "MixedInstancesPolicy", + "target": "MixedInstancesPolicy" + }, + { + "source": "NewInstancesProtectedFromScaleIn", + "target": "NewInstancesProtectedFromScaleIn" + }, + { + "source": "PlacementGroup", + "target": "PlacementGroup" + }, + { + "source": "ServiceLinkedRoleARN", + "target": "ServiceLinkedRoleARN" + }, + { + "source": "SkipZonalShiftValidation", + "target": "SkipZonalShiftValidation" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetGroupARNs", + "target": "TargetGroupARNs" + }, + { + "source": "TerminationPolicies", + "target": "TerminationPolicies" + }, + { + "source": "TrafficSources", + "target": "TrafficSources" + }, + { + "source": "VPCZoneIdentifier", + "target": "VPCZoneIdentifier" + } + ], + "operation": "CreateAutoScalingGroup", + "phase": "create", + "service": "autoscaling" + }, + { + "cfn_type": "AWS::AutoScaling::AutoScalingGroup", + "mappings": [ + { + "source": "AutoScalingGroupName", + "target": "AutoScalingGroupName" + } + ], + "operation": "DeleteAutoScalingGroup", + "phase": "delete", + "service": "autoscaling" + }, + { + "cfn_type": "AWS::AutoScaling::LaunchConfiguration", + "mappings": [ + { + "source": "AssociatePublicIpAddress", + "target": "AssociatePublicIpAddress" + }, + { + "source": "BlockDeviceMappings", + "target": "BlockDeviceMappings" + }, + { + "source": "ClassicLinkVPCId", + "target": "ClassicLinkVPCId" + }, + { + "source": "ClassicLinkVPCSecurityGroups", + "target": "ClassicLinkVPCSecurityGroups" + }, + { + "source": "EbsOptimized", + "target": "EbsOptimized" + }, + { + "source": "IamInstanceProfile", + "target": "IamInstanceProfile" + }, + { + "source": "ImageId", + "target": "ImageId" + }, + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "InstanceMonitoring", + "target": "InstanceMonitoring" + }, + { + "source": "InstanceType", + "target": "InstanceType" + }, + { + "source": "KernelId", + "target": "KernelId" + }, + { + "source": "KeyName", + "target": "KeyName" + }, + { + "source": "LaunchConfigurationName", + "target": "LaunchConfigurationName" + }, + { + "source": "MetadataOptions", + "target": "MetadataOptions" + }, + { + "source": "PlacementTenancy", + "target": "PlacementTenancy" + }, + { + "source": "RamdiskId", + "target": "RamDiskId" + }, + { + "source": "SecurityGroups", + "target": "SecurityGroups" + }, + { + "source": "SpotPrice", + "target": "SpotPrice" + }, + { + "source": "UserData", + "target": "UserData" + } + ], + "operation": "CreateLaunchConfiguration", + "phase": "create", + "service": "autoscaling" + }, + { + "cfn_type": "AWS::AutoScaling::LaunchConfiguration", + "mappings": [ + { + "source": "LaunchConfigurationName", + "target": "LaunchConfigurationName" + } + ], + "operation": "DeleteLaunchConfiguration", + "phase": "delete", + "service": "autoscaling" + }, + { + "cfn_type": "AWS::AutoScaling::LifecycleHook", + "mappings": [ + { + "source": "AutoScalingGroupName", + "target": "AutoScalingGroupName" + }, + { + "source": "DefaultResult", + "target": "DefaultResult" + }, + { + "source": "HeartbeatTimeout", + "target": "HeartbeatTimeout" + }, + { + "source": "LifecycleHookName", + "target": "LifecycleHookName" + }, + { + "source": "LifecycleTransition", + "target": "LifecycleTransition" + }, + { + "source": "NotificationMetadata", + "target": "NotificationMetadata" + }, + { + "source": "NotificationTargetARN", + "target": "NotificationTargetARN" + }, + { + "source": "RoleARN", + "target": "RoleARN" + } + ], + "operation": "PutLifecycleHook", + "phase": "create", + "service": "autoscaling" + }, + { + "cfn_type": "AWS::AutoScaling::LifecycleHook", + "mappings": [ + { + "source": "AutoScalingGroupName", + "target": "AutoScalingGroupName" + }, + { + "source": "LifecycleHookName", + "target": "LifecycleHookName" + } + ], + "operation": "DeleteLifecycleHook", + "phase": "delete", + "service": "autoscaling" + }, + { + "cfn_type": "AWS::AutoScaling::ScalingPolicy", + "mappings": [ + { + "source": "AdjustmentType", + "target": "AdjustmentType" + }, + { + "source": "AutoScalingGroupName", + "target": "AutoScalingGroupName" + }, + { + "source": "Cooldown", + "target": "Cooldown" + }, + { + "source": "EstimatedInstanceWarmup", + "target": "EstimatedInstanceWarmup" + }, + { + "source": "MetricAggregationType", + "target": "MetricAggregationType" + }, + { + "source": "MinAdjustmentMagnitude", + "target": "MinAdjustmentMagnitude" + }, + { + "source": "PolicyType", + "target": "PolicyType" + }, + { + "source": "PredictiveScalingConfiguration", + "target": "PredictiveScalingConfiguration" + }, + { + "source": "ScalingAdjustment", + "target": "ScalingAdjustment" + }, + { + "source": "StepAdjustments", + "target": "StepAdjustments" + }, + { + "source": "TargetTrackingConfiguration", + "target": "TargetTrackingConfiguration" + } + ], + "operation": "PutScalingPolicy", + "phase": "create", + "service": "autoscaling" + }, + { + "cfn_type": "AWS::AutoScaling::ScheduledAction", + "mappings": [ + { + "source": "AutoScalingGroupName", + "target": "AutoScalingGroupName" + }, + { + "source": "DesiredCapacity", + "target": "DesiredCapacity" + }, + { + "source": "EndTime", + "target": "EndTime" + }, + { + "source": "MaxSize", + "target": "MaxSize" + }, + { + "source": "MinSize", + "target": "MinSize" + }, + { + "source": "Recurrence", + "target": "Recurrence" + }, + { + "source": "StartTime", + "target": "StartTime" + }, + { + "source": "TimeZone", + "target": "TimeZone" + } + ], + "operation": "PutScheduledUpdateGroupAction", + "phase": "create", + "service": "autoscaling" + }, + { + "cfn_type": "AWS::AutoScaling::ScheduledAction", + "mappings": [ + { + "source": "AutoScalingGroupName", + "target": "AutoScalingGroupName" + } + ], + "operation": "DeleteScheduledAction", + "phase": "delete", + "service": "autoscaling" + }, + { + "cfn_type": "AWS::AutoScaling::WarmPool", + "mappings": [ + { + "source": "AutoScalingGroupName", + "target": "AutoScalingGroupName" + }, + { + "source": "InstanceReusePolicy", + "target": "InstanceReusePolicy" + }, + { + "source": "MaxGroupPreparedCapacity", + "target": "MaxGroupPreparedCapacity" + }, + { + "source": "MinSize", + "target": "MinSize" + }, + { + "source": "PoolState", + "target": "PoolState" + } + ], + "operation": "PutWarmPool", + "phase": "create", + "service": "autoscaling" + }, + { + "cfn_type": "AWS::AutoScaling::WarmPool", + "mappings": [ + { + "source": "AutoScalingGroupName", + "target": "AutoScalingGroupName" + } + ], + "operation": "DeleteWarmPool", + "phase": "delete", + "service": "autoscaling" + }, + { + "cfn_type": "AWS::B2BI::Capability", + "mappings": [ + { + "source": "configuration", + "target": "Configuration" + }, + { + "source": "instructionsDocuments", + "target": "InstructionsDocuments" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateCapability", + "phase": "create", + "service": "b2bi" + }, + { + "cfn_type": "AWS::B2BI::Capability", + "mappings": [], + "operation": "DeleteCapability", + "phase": "delete", + "service": "b2bi" + }, + { + "cfn_type": "AWS::B2BI::Partnership", + "mappings": [ + { + "source": "capabilities", + "target": "Capabilities" + }, + { + "source": "capabilityOptions", + "target": "CapabilityOptions" + }, + { + "source": "email", + "target": "Email" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "phone", + "target": "Phone" + }, + { + "source": "profileId", + "target": "ProfileId" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePartnership", + "phase": "create", + "service": "b2bi" + }, + { + "cfn_type": "AWS::B2BI::Partnership", + "mappings": [], + "operation": "DeletePartnership", + "phase": "delete", + "service": "b2bi" + }, + { + "cfn_type": "AWS::B2BI::Profile", + "mappings": [ + { + "source": "businessName", + "target": "BusinessName" + }, + { + "source": "email", + "target": "Email" + }, + { + "source": "logging", + "target": "Logging" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "phone", + "target": "Phone" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateProfile", + "phase": "create", + "service": "b2bi" + }, + { + "cfn_type": "AWS::B2BI::Profile", + "mappings": [], + "operation": "DeleteProfile", + "phase": "delete", + "service": "b2bi" + }, + { + "cfn_type": "AWS::B2BI::Transformer", + "mappings": [ + { + "source": "ediType", + "target": "EdiType" + }, + { + "source": "fileFormat", + "target": "FileFormat" + }, + { + "source": "inputConversion", + "target": "InputConversion" + }, + { + "source": "mapping", + "target": "Mapping" + }, + { + "source": "mappingTemplate", + "target": "MappingTemplate" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "outputConversion", + "target": "OutputConversion" + }, + { + "source": "sampleDocument", + "target": "SampleDocument" + }, + { + "source": "sampleDocuments", + "target": "SampleDocuments" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateTransformer", + "phase": "create", + "service": "b2bi" + }, + { + "cfn_type": "AWS::B2BI::Transformer", + "mappings": [], + "operation": "DeleteTransformer", + "phase": "delete", + "service": "b2bi" + }, + { + "cfn_type": "AWS::BCMDataExports::Export", + "mappings": [ + { + "source": "Export", + "target": "Export" + } + ], + "operation": "CreateExport", + "phase": "create", + "service": "bcm-data-exports" + }, + { + "cfn_type": "AWS::BCMDataExports::Export", + "mappings": [], + "operation": "DeleteExport", + "phase": "delete", + "service": "bcm-data-exports" + }, + { + "cfn_type": "AWS::Backup::BackupPlan", + "mappings": [ + { + "source": "BackupPlan", + "target": "BackupPlan" + }, + { + "source": "BackupPlanTags", + "target": "BackupPlanTags" + } + ], + "operation": "CreateBackupPlan", + "phase": "create", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::BackupPlan", + "mappings": [], + "operation": "DeleteBackupPlan", + "phase": "delete", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::BackupSelection", + "mappings": [ + { + "source": "BackupPlanId", + "target": "BackupPlanId" + }, + { + "source": "BackupSelection", + "target": "BackupSelection" + } + ], + "operation": "CreateBackupSelection", + "phase": "create", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::BackupSelection", + "mappings": [ + { + "source": "BackupPlanId", + "target": "BackupPlanId" + } + ], + "operation": "DeleteBackupSelection", + "phase": "delete", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::BackupVault", + "mappings": [ + { + "source": "BackupVaultName", + "target": "BackupVaultName" + }, + { + "source": "BackupVaultTags", + "target": "BackupVaultTags" + }, + { + "source": "EncryptionKeyArn", + "target": "EncryptionKeyArn" + } + ], + "operation": "CreateBackupVault", + "phase": "create", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::BackupVault", + "mappings": [ + { + "source": "BackupVaultName", + "target": "BackupVaultName" + } + ], + "operation": "DeleteBackupVault", + "phase": "delete", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::Framework", + "mappings": [ + { + "source": "FrameworkControls", + "target": "FrameworkControls" + }, + { + "source": "FrameworkDescription", + "target": "FrameworkDescription" + }, + { + "source": "FrameworkName", + "target": "FrameworkName" + }, + { + "source": "FrameworkTags", + "target": "FrameworkTags" + } + ], + "operation": "CreateFramework", + "phase": "create", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::Framework", + "mappings": [ + { + "source": "FrameworkName", + "target": "FrameworkName" + } + ], + "operation": "DeleteFramework", + "phase": "delete", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::LegalHold", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "RecoveryPointSelection", + "target": "RecoveryPointSelection" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Title", + "target": "Title" + } + ], + "operation": "CreateLegalHold", + "phase": "create", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::LegalHold", + "mappings": [], + "operation": "CancelLegalHold", + "phase": "delete", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::LogicallyAirGappedBackupVault", + "mappings": [ + { + "source": "BackupVaultName", + "target": "BackupVaultName" + }, + { + "source": "BackupVaultTags", + "target": "BackupVaultTags" + }, + { + "source": "MaxRetentionDays", + "target": "MaxRetentionDays" + }, + { + "source": "MinRetentionDays", + "target": "MinRetentionDays" + } + ], + "operation": "CreateLogicallyAirGappedBackupVault", + "phase": "create", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::ReportPlan", + "mappings": [ + { + "source": "ReportDeliveryChannel", + "target": "ReportDeliveryChannel" + }, + { + "source": "ReportPlanDescription", + "target": "ReportPlanDescription" + }, + { + "source": "ReportPlanName", + "target": "ReportPlanName" + }, + { + "source": "ReportPlanTags", + "target": "ReportPlanTags" + }, + { + "source": "ReportSetting", + "target": "ReportSetting" + } + ], + "operation": "CreateReportPlan", + "phase": "create", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::ReportPlan", + "mappings": [ + { + "source": "ReportPlanName", + "target": "ReportPlanName" + } + ], + "operation": "DeleteReportPlan", + "phase": "delete", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::RestoreTestingPlan", + "mappings": [ + { + "source": "RestoreTestingPlan", + "target": "RestoreTestingPlanName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRestoreTestingPlan", + "phase": "create", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::RestoreTestingPlan", + "mappings": [ + { + "source": "RestoreTestingPlanName", + "target": "RestoreTestingPlanName" + } + ], + "operation": "DeleteRestoreTestingPlan", + "phase": "delete", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::RestoreTestingSelection", + "mappings": [ + { + "source": "RestoreTestingPlanName", + "target": "RestoreTestingPlanName" + }, + { + "source": "RestoreTestingSelection", + "target": "RestoreTestingSelectionName" + } + ], + "operation": "CreateRestoreTestingSelection", + "phase": "create", + "service": "backup" + }, + { + "cfn_type": "AWS::Backup::RestoreTestingSelection", + "mappings": [ + { + "source": "RestoreTestingPlanName", + "target": "RestoreTestingPlanName" + }, + { + "source": "RestoreTestingSelectionName", + "target": "RestoreTestingSelectionName" + } + ], + "operation": "DeleteRestoreTestingSelection", + "phase": "delete", + "service": "backup" + }, + { + "cfn_type": "AWS::BackupGateway::Hypervisor", + "mappings": [ + { + "source": "Host", + "target": "Host" + }, + { + "source": "KmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Password", + "target": "Password" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Username", + "target": "Username" + } + ], + "operation": "ImportHypervisorConfiguration", + "phase": "create", + "service": "backup-gateway" + }, + { + "cfn_type": "AWS::BackupGateway::Hypervisor", + "mappings": [], + "operation": "DeleteHypervisor", + "phase": "delete", + "service": "backup-gateway" + }, + { + "cfn_type": "AWS::Batch::ComputeEnvironment", + "mappings": [ + { + "source": "computeEnvironmentName", + "target": "ComputeEnvironmentName" + }, + { + "source": "computeResources", + "target": "ComputeResources" + }, + { + "source": "context", + "target": "Context" + }, + { + "source": "eksConfiguration", + "target": "EksConfiguration" + }, + { + "source": "serviceRole", + "target": "ServiceRole" + }, + { + "source": "state", + "target": "State" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + }, + { + "source": "unmanagedvCpus", + "target": "UnmanagedvCpus" + } + ], + "operation": "CreateComputeEnvironment", + "phase": "create", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::ComputeEnvironment", + "mappings": [ + { + "source": "computeEnvironment", + "target": "ComputeEnvironmentName" + } + ], + "operation": "DeleteComputeEnvironment", + "phase": "delete", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::ConsumableResource", + "mappings": [ + { + "source": "consumableResourceName", + "target": "ConsumableResourceName" + }, + { + "source": "resourceType", + "target": "ResourceType" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "totalQuantity", + "target": "TotalQuantity" + } + ], + "operation": "CreateConsumableResource", + "phase": "create", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::ConsumableResource", + "mappings": [ + { + "source": "consumableResource", + "target": "ConsumableResourceName" + } + ], + "operation": "DeleteConsumableResource", + "phase": "delete", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::JobDefinition", + "mappings": [ + { + "source": "consumableResourceProperties", + "target": "ConsumableResourceProperties" + }, + { + "source": "containerProperties", + "target": "ContainerProperties" + }, + { + "source": "ecsProperties", + "target": "EcsProperties" + }, + { + "source": "eksProperties", + "target": "EksProperties" + }, + { + "source": "jobDefinitionName", + "target": "JobDefinitionName" + }, + { + "source": "nodeProperties", + "target": "NodeProperties" + }, + { + "source": "parameters", + "target": "Parameters" + }, + { + "source": "platformCapabilities", + "target": "PlatformCapabilities" + }, + { + "source": "propagateTags", + "target": "PropagateTags" + }, + { + "source": "retryStrategy", + "target": "RetryStrategy" + }, + { + "source": "schedulingPriority", + "target": "SchedulingPriority" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "timeout", + "target": "Timeout" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "RegisterJobDefinition", + "phase": "create", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::JobDefinition", + "mappings": [ + { + "source": "jobDefinition", + "target": "JobDefinitionName" + } + ], + "operation": "DeregisterJobDefinition", + "phase": "delete", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::JobQueue", + "mappings": [ + { + "source": "computeEnvironmentOrder", + "target": "ComputeEnvironmentOrder" + }, + { + "source": "jobQueueName", + "target": "JobQueueName" + }, + { + "source": "jobQueueType", + "target": "JobQueueType" + }, + { + "source": "jobStateTimeLimitActions", + "target": "JobStateTimeLimitActions" + }, + { + "source": "priority", + "target": "Priority" + }, + { + "source": "schedulingPolicyArn", + "target": "SchedulingPolicyArn" + }, + { + "source": "serviceEnvironmentOrder", + "target": "ServiceEnvironmentOrder" + }, + { + "source": "state", + "target": "State" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateJobQueue", + "phase": "create", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::JobQueue", + "mappings": [ + { + "source": "jobQueue", + "target": "JobQueueName" + } + ], + "operation": "DeleteJobQueue", + "phase": "delete", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::SchedulingPolicy", + "mappings": [ + { + "source": "fairsharePolicy", + "target": "FairsharePolicy" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSchedulingPolicy", + "phase": "create", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::SchedulingPolicy", + "mappings": [], + "operation": "DeleteSchedulingPolicy", + "phase": "delete", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::ServiceEnvironment", + "mappings": [ + { + "source": "capacityLimits", + "target": "CapacityLimits" + }, + { + "source": "serviceEnvironmentName", + "target": "ServiceEnvironmentName" + }, + { + "source": "serviceEnvironmentType", + "target": "ServiceEnvironmentType" + }, + { + "source": "state", + "target": "State" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateServiceEnvironment", + "phase": "create", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::ServiceEnvironment", + "mappings": [ + { + "source": "serviceEnvironment", + "target": "ServiceEnvironmentName" + } + ], + "operation": "DeleteServiceEnvironment", + "phase": "delete", + "service": "batch" + }, + { + "cfn_type": "AWS::BcmPricingCalculator::BillScenario", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateBillScenario", + "phase": "create", + "service": "bcm-pricing-calculator" + }, + { + "cfn_type": "AWS::BcmPricingCalculator::BillScenario", + "mappings": [], + "operation": "DeleteBillScenario", + "phase": "delete", + "service": "bcm-pricing-calculator" + }, + { + "cfn_type": "AWS::Bedrock::AgentAlias", + "mappings": [ + { + "source": "agentAliasName", + "target": "AgentAliasName" + }, + { + "source": "agentId", + "target": "AgentId" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "routingConfiguration", + "target": "RoutingConfiguration" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAgentAlias", + "phase": "create", + "service": "bedrock-agent" + }, + { + "cfn_type": "AWS::Bedrock::AgentAlias", + "mappings": [ + { + "source": "agentId", + "target": "AgentId" + } + ], + "operation": "DeleteAgentAlias", + "phase": "delete", + "service": "bedrock-agent" + }, + { + "cfn_type": "AWS::Bedrock::ApplicationInferenceProfile", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "inferenceProfileName", + "target": "InferenceProfileName" + }, + { + "source": "modelSource", + "target": "ModelSource" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateInferenceProfile", + "phase": "create", + "service": "bedrock" + }, + { + "cfn_type": "AWS::Bedrock::AutomatedReasoningPolicy", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "policyDefinition", + "target": "PolicyDefinition" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAutomatedReasoningPolicy", + "phase": "create", + "service": "bedrock" + }, + { + "cfn_type": "AWS::Bedrock::AutomatedReasoningPolicy", + "mappings": [], + "operation": "DeleteAutomatedReasoningPolicy", + "phase": "delete", + "service": "bedrock" + }, + { + "cfn_type": "AWS::Bedrock::AutomatedReasoningPolicyVersion", + "mappings": [ + { + "source": "lastUpdatedDefinitionHash", + "target": "LastUpdatedDefinitionHash" + }, + { + "source": "policyArn", + "target": "PolicyArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAutomatedReasoningPolicyVersion", + "phase": "create", + "service": "bedrock" + }, + { + "cfn_type": "AWS::Bedrock::Blueprint", + "mappings": [ + { + "source": "blueprintName", + "target": "BlueprintName" + }, + { + "source": "schema", + "target": "Schema" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateBlueprint", + "phase": "create", + "service": "bedrock-data-automation" + }, + { + "cfn_type": "AWS::Bedrock::Blueprint", + "mappings": [], + "operation": "DeleteBlueprint", + "phase": "delete", + "service": "bedrock-data-automation" + }, + { + "cfn_type": "AWS::Bedrock::DataAutomationProject", + "mappings": [ + { + "source": "customOutputConfiguration", + "target": "CustomOutputConfiguration" + }, + { + "source": "overrideConfiguration", + "target": "OverrideConfiguration" + }, + { + "source": "projectDescription", + "target": "ProjectDescription" + }, + { + "source": "projectName", + "target": "ProjectName" + }, + { + "source": "standardOutputConfiguration", + "target": "StandardOutputConfiguration" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDataAutomationProject", + "phase": "create", + "service": "bedrock-data-automation" + }, + { + "cfn_type": "AWS::Bedrock::DataAutomationProject", + "mappings": [], + "operation": "DeleteDataAutomationProject", + "phase": "delete", + "service": "bedrock-data-automation" + }, + { + "cfn_type": "AWS::Bedrock::DataSource", + "mappings": [ + { + "source": "dataDeletionPolicy", + "target": "DataDeletionPolicy" + }, + { + "source": "dataSourceConfiguration", + "target": "DataSourceConfiguration" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "knowledgeBaseId", + "target": "KnowledgeBaseId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "serverSideEncryptionConfiguration", + "target": "ServerSideEncryptionConfiguration" + }, + { + "source": "vectorIngestionConfiguration", + "target": "VectorIngestionConfiguration" + } + ], + "operation": "CreateDataSource", + "phase": "create", + "service": "bedrock-agent" + }, + { + "cfn_type": "AWS::Bedrock::DataSource", + "mappings": [ + { + "source": "knowledgeBaseId", + "target": "KnowledgeBaseId" + } + ], + "operation": "DeleteDataSource", + "phase": "delete", + "service": "bedrock-agent" + }, + { + "cfn_type": "AWS::Bedrock::FlowAlias", + "mappings": [ + { + "source": "concurrencyConfiguration", + "target": "ConcurrencyConfiguration" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "routingConfiguration", + "target": "RoutingConfiguration" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateFlowAlias", + "phase": "create", + "service": "bedrock-agent" + }, + { + "cfn_type": "AWS::Bedrock::FlowAlias", + "mappings": [], + "operation": "DeleteFlowAlias", + "phase": "delete", + "service": "bedrock-agent" + }, + { + "cfn_type": "AWS::Bedrock::Guardrail", + "mappings": [ + { + "source": "automatedReasoningPolicyConfig", + "target": "AutomatedReasoningPolicyConfig" + }, + { + "source": "blockedInputMessaging", + "target": "BlockedInputMessaging" + }, + { + "source": "blockedOutputsMessaging", + "target": "BlockedOutputsMessaging" + }, + { + "source": "contentPolicyConfig", + "target": "ContentPolicyConfig" + }, + { + "source": "contextualGroundingPolicyConfig", + "target": "ContextualGroundingPolicyConfig" + }, + { + "source": "crossRegionConfig", + "target": "CrossRegionConfig" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "sensitiveInformationPolicyConfig", + "target": "SensitiveInformationPolicyConfig" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "topicPolicyConfig", + "target": "TopicPolicyConfig" + }, + { + "source": "wordPolicyConfig", + "target": "WordPolicyConfig" + } + ], + "operation": "CreateGuardrail", + "phase": "create", + "service": "bedrock" + }, + { + "cfn_type": "AWS::Bedrock::Guardrail", + "mappings": [], + "operation": "DeleteGuardrail", + "phase": "delete", + "service": "bedrock" + }, + { + "cfn_type": "AWS::Bedrock::GuardrailVersion", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "guardrailIdentifier", + "target": "GuardrailIdentifier" + } + ], + "operation": "CreateGuardrailVersion", + "phase": "create", + "service": "bedrock" + }, + { + "cfn_type": "AWS::Bedrock::IntelligentPromptRouter", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "fallbackModel", + "target": "FallbackModel" + }, + { + "source": "models", + "target": "Models" + }, + { + "source": "promptRouterName", + "target": "PromptRouterName" + }, + { + "source": "routingCriteria", + "target": "RoutingCriteria" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePromptRouter", + "phase": "create", + "service": "bedrock" + }, + { + "cfn_type": "AWS::Bedrock::KnowledgeBase", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "knowledgeBaseConfiguration", + "target": "KnowledgeBaseConfiguration" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "storageConfiguration", + "target": "StorageConfiguration" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateKnowledgeBase", + "phase": "create", + "service": "bedrock-agent" + }, + { + "cfn_type": "AWS::Bedrock::KnowledgeBase", + "mappings": [], + "operation": "DeleteKnowledgeBase", + "phase": "delete", + "service": "bedrock-agent" + }, + { + "cfn_type": "AWS::Bedrock::Prompt", + "mappings": [ + { + "source": "customerEncryptionKeyArn", + "target": "CustomerEncryptionKeyArn" + }, + { + "source": "defaultVariant", + "target": "DefaultVariant" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "variants", + "target": "Variants" + } + ], + "operation": "CreatePrompt", + "phase": "create", + "service": "bedrock-agent" + }, + { + "cfn_type": "AWS::Bedrock::Prompt", + "mappings": [], + "operation": "DeletePrompt", + "phase": "delete", + "service": "bedrock-agent" + }, + { + "cfn_type": "AWS::Bedrock::PromptVersion", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePromptVersion", + "phase": "create", + "service": "bedrock-agent" + }, + { + "cfn_type": "AWS::BedrockAgentCore::ApiKeyCredentialProvider", + "mappings": [ + { + "source": "apiKey", + "target": "ApiKey" + }, + { + "source": "name", + "target": "Name" + } + ], + "operation": "CreateApiKeyCredentialProvider", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::ApiKeyCredentialProvider", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteApiKeyCredentialProvider", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::BrowserCustom", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "executionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "networkConfiguration", + "target": "NetworkConfiguration" + } + ], + "operation": "CreateBrowser", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::CodeInterpreterCustom", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "executionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "networkConfiguration", + "target": "NetworkConfiguration" + } + ], + "operation": "CreateCodeInterpreter", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Gateway", + "mappings": [ + { + "source": "authorizerConfiguration", + "target": "AuthorizerConfiguration" + }, + { + "source": "authorizerType", + "target": "AuthorizerType" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "exceptionLevel", + "target": "ExceptionLevel" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "protocolConfiguration", + "target": "ProtocolConfiguration" + }, + { + "source": "protocolType", + "target": "ProtocolType" + }, + { + "source": "roleArn", + "target": "RoleArn" + } + ], + "operation": "CreateGateway", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Gateway", + "mappings": [], + "operation": "DeleteGateway", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::GatewayTarget", + "mappings": [ + { + "source": "credentialProviderConfigurations", + "target": "CredentialProviderConfigurations" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "gatewayIdentifier", + "target": "GatewayIdentifier" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "targetConfiguration", + "target": "TargetConfiguration" + } + ], + "operation": "CreateGatewayTarget", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::GatewayTarget", + "mappings": [ + { + "source": "gatewayIdentifier", + "target": "GatewayIdentifier" + } + ], + "operation": "DeleteGatewayTarget", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Memory", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "encryptionKeyArn", + "target": "EncryptionKeyArn" + }, + { + "source": "eventExpiryDuration", + "target": "EventExpiryDuration" + }, + { + "source": "memoryExecutionRoleArn", + "target": "MemoryExecutionRoleArn" + }, + { + "source": "memoryStrategies", + "target": "MemoryStrategies" + }, + { + "source": "name", + "target": "Name" + } + ], + "operation": "CreateMemory", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Memory", + "mappings": [], + "operation": "DeleteMemory", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::OAuth2CredentialProvider", + "mappings": [ + { + "source": "credentialProviderVendor", + "target": "CredentialProviderVendor" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "oauth2ProviderConfigInput", + "target": "Oauth2ProviderConfigInput" + } + ], + "operation": "CreateOauth2CredentialProvider", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::OAuth2CredentialProvider", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteOauth2CredentialProvider", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Runtime", + "mappings": [ + { + "source": "agentRuntimeArtifact", + "target": "AgentRuntimeArtifact" + }, + { + "source": "agentRuntimeName", + "target": "AgentRuntimeName" + }, + { + "source": "authorizerConfiguration", + "target": "AuthorizerConfiguration" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "environmentVariables", + "target": "EnvironmentVariables" + }, + { + "source": "networkConfiguration", + "target": "NetworkConfiguration" + }, + { + "source": "protocolConfiguration", + "target": "ProtocolConfiguration" + }, + { + "source": "roleArn", + "target": "RoleArn" + } + ], + "operation": "CreateAgentRuntime", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Runtime", + "mappings": [], + "operation": "DeleteAgentRuntime", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::RuntimeEndpoint", + "mappings": [ + { + "source": "agentRuntimeId", + "target": "AgentRuntimeId" + }, + { + "source": "agentRuntimeVersion", + "target": "AgentRuntimeVersion" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + } + ], + "operation": "CreateAgentRuntimeEndpoint", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::RuntimeEndpoint", + "mappings": [ + { + "source": "agentRuntimeId", + "target": "AgentRuntimeId" + } + ], + "operation": "DeleteAgentRuntimeEndpoint", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::WorkloadIdentity", + "mappings": [ + { + "source": "allowedResourceOauth2ReturnUrls", + "target": "AllowedResourceOauth2ReturnUrls" + }, + { + "source": "name", + "target": "Name" + } + ], + "operation": "CreateWorkloadIdentity", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::WorkloadIdentity", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteWorkloadIdentity", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::Billing::BillingView", + "mappings": [ + { + "source": "dataFilterExpression", + "target": "DataFilterExpression" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "sourceViews", + "target": "SourceViews" + } + ], + "operation": "CreateBillingView", + "phase": "create", + "service": "billing" + }, + { + "cfn_type": "AWS::Billing::BillingView", + "mappings": [], + "operation": "DeleteBillingView", + "phase": "delete", + "service": "billing" + }, + { + "cfn_type": "AWS::BillingConductor::BillingGroup", + "mappings": [ + { + "source": "AccountGrouping", + "target": "AccountGrouping" + }, + { + "source": "ComputationPreference", + "target": "ComputationPreference" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "PrimaryAccountId", + "target": "PrimaryAccountId" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateBillingGroup", + "phase": "create", + "service": "billingconductor" + }, + { + "cfn_type": "AWS::BillingConductor::BillingGroup", + "mappings": [], + "operation": "DeleteBillingGroup", + "phase": "delete", + "service": "billingconductor" + }, + { + "cfn_type": "AWS::BillingConductor::CustomLineItem", + "mappings": [ + { + "source": "AccountId", + "target": "AccountId" + }, + { + "source": "BillingGroupArn", + "target": "BillingGroupArn" + }, + { + "source": "BillingPeriodRange", + "target": "BillingPeriodRange" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCustomLineItem", + "phase": "create", + "service": "billingconductor" + }, + { + "cfn_type": "AWS::BillingConductor::CustomLineItem", + "mappings": [ + { + "source": "BillingPeriodRange", + "target": "BillingPeriodRange" + } + ], + "operation": "DeleteCustomLineItem", + "phase": "delete", + "service": "billingconductor" + }, + { + "cfn_type": "AWS::BillingConductor::PricingPlan", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "PricingRuleArns", + "target": "PricingRuleArns" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePricingPlan", + "phase": "create", + "service": "billingconductor" + }, + { + "cfn_type": "AWS::BillingConductor::PricingPlan", + "mappings": [], + "operation": "DeletePricingPlan", + "phase": "delete", + "service": "billingconductor" + }, + { + "cfn_type": "AWS::BillingConductor::PricingRule", + "mappings": [ + { + "source": "BillingEntity", + "target": "BillingEntity" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "ModifierPercentage", + "target": "ModifierPercentage" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Operation", + "target": "Operation" + }, + { + "source": "Scope", + "target": "Scope" + }, + { + "source": "Service", + "target": "Service" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Tiering", + "target": "Tiering" + }, + { + "source": "Type", + "target": "Type" + }, + { + "source": "UsageType", + "target": "UsageType" + } + ], + "operation": "CreatePricingRule", + "phase": "create", + "service": "billingconductor" + }, + { + "cfn_type": "AWS::BillingConductor::PricingRule", + "mappings": [], + "operation": "DeletePricingRule", + "phase": "delete", + "service": "billingconductor" + }, + { + "cfn_type": "AWS::Budgets::BudgetsAction", + "mappings": [ + { + "source": "ActionThreshold", + "target": "ActionThreshold" + }, + { + "source": "ActionType", + "target": "ActionType" + }, + { + "source": "ApprovalModel", + "target": "ApprovalModel" + }, + { + "source": "BudgetName", + "target": "BudgetName" + }, + { + "source": "Definition", + "target": "Definition" + }, + { + "source": "ExecutionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "NotificationType", + "target": "NotificationType" + }, + { + "source": "ResourceTags", + "target": "ResourceTags" + }, + { + "source": "Subscribers", + "target": "Subscribers" + } + ], + "operation": "CreateBudgetAction", + "phase": "create", + "service": "budgets" + }, + { + "cfn_type": "AWS::CE::AnomalyMonitor", + "mappings": [ + { + "source": "ResourceTags", + "target": "ResourceTags" + } + ], + "operation": "CreateAnomalyMonitor", + "phase": "create", + "service": "ce" + }, + { + "cfn_type": "AWS::CE::AnomalyMonitor", + "mappings": [], + "operation": "DeleteAnomalyMonitor", + "phase": "delete", + "service": "ce" + }, + { + "cfn_type": "AWS::CE::AnomalySubscription", + "mappings": [ + { + "source": "ResourceTags", + "target": "ResourceTags" + } + ], + "operation": "CreateAnomalySubscription", + "phase": "create", + "service": "ce" + }, + { + "cfn_type": "AWS::CE::AnomalySubscription", + "mappings": [], + "operation": "DeleteAnomalySubscription", + "phase": "delete", + "service": "ce" + }, + { + "cfn_type": "AWS::CE::CostCategory", + "mappings": [ + { + "source": "DefaultValue", + "target": "DefaultValue" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RuleVersion", + "target": "RuleVersion" + }, + { + "source": "Rules", + "target": "Rules" + }, + { + "source": "SplitChargeRules", + "target": "SplitChargeRules" + } + ], + "operation": "CreateCostCategoryDefinition", + "phase": "create", + "service": "ce" + }, + { + "cfn_type": "AWS::CUR::ReportDefinition", + "mappings": [ + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "PutReportDefinition", + "phase": "create", + "service": "cur" + }, + { + "cfn_type": "AWS::CUR::ReportDefinition", + "mappings": [ + { + "source": "ReportName", + "target": "ReportName" + } + ], + "operation": "DeleteReportDefinition", + "phase": "delete", + "service": "cur" + }, + { + "cfn_type": "AWS::Cases::CaseRule", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "domainId", + "target": "DomainId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "rule", + "target": "Rule" + } + ], + "operation": "CreateCaseRule", + "phase": "create", + "service": "connectcases" + }, + { + "cfn_type": "AWS::Cases::CaseRule", + "mappings": [ + { + "source": "domainId", + "target": "DomainId" + } + ], + "operation": "DeleteCaseRule", + "phase": "delete", + "service": "connectcases" + }, + { + "cfn_type": "AWS::Cases::Domain", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "CreateDomain", + "phase": "create", + "service": "connectcases" + }, + { + "cfn_type": "AWS::Cases::Domain", + "mappings": [], + "operation": "DeleteDomain", + "phase": "delete", + "service": "connectcases" + }, + { + "cfn_type": "AWS::Cases::Field", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "domainId", + "target": "DomainId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateField", + "phase": "create", + "service": "connectcases" + }, + { + "cfn_type": "AWS::Cases::Field", + "mappings": [ + { + "source": "domainId", + "target": "DomainId" + } + ], + "operation": "DeleteField", + "phase": "delete", + "service": "connectcases" + }, + { + "cfn_type": "AWS::Cases::Layout", + "mappings": [ + { + "source": "content", + "target": "Content" + }, + { + "source": "domainId", + "target": "DomainId" + }, + { + "source": "name", + "target": "Name" + } + ], + "operation": "CreateLayout", + "phase": "create", + "service": "connectcases" + }, + { + "cfn_type": "AWS::Cases::Layout", + "mappings": [ + { + "source": "domainId", + "target": "DomainId" + } + ], + "operation": "DeleteLayout", + "phase": "delete", + "service": "connectcases" + }, + { + "cfn_type": "AWS::Cases::Template", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "domainId", + "target": "DomainId" + }, + { + "source": "layoutConfiguration", + "target": "LayoutConfiguration" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "requiredFields", + "target": "RequiredFields" + }, + { + "source": "rules", + "target": "Rules" + }, + { + "source": "status", + "target": "Status" + } + ], + "operation": "CreateTemplate", + "phase": "create", + "service": "connectcases" + }, + { + "cfn_type": "AWS::Cases::Template", + "mappings": [ + { + "source": "domainId", + "target": "DomainId" + } + ], + "operation": "DeleteTemplate", + "phase": "delete", + "service": "connectcases" + }, + { + "cfn_type": "AWS::CertificateManager::Certificate", + "mappings": [ + { + "source": "CertificateAuthorityArn", + "target": "CertificateAuthorityArn" + }, + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "DomainValidationOptions", + "target": "DomainValidationOptions" + }, + { + "source": "KeyAlgorithm", + "target": "KeyAlgorithm" + }, + { + "source": "SubjectAlternativeNames", + "target": "SubjectAlternativeNames" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "ValidationMethod", + "target": "ValidationMethod" + } + ], + "operation": "RequestCertificate", + "phase": "create", + "service": "acm" + }, + { + "cfn_type": "AWS::CertificateManager::Certificate", + "mappings": [ + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "RemoveTagsFromCertificate", + "phase": "delete", + "service": "acm" + }, + { + "cfn_type": "AWS::Chatbot::CustomAction", + "mappings": [ + { + "source": "ActionName", + "target": "ActionName" + }, + { + "source": "AliasName", + "target": "AliasName" + }, + { + "source": "Attachments", + "target": "Attachments" + }, + { + "source": "Definition", + "target": "Definition" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCustomAction", + "phase": "create", + "service": "chatbot" + }, + { + "cfn_type": "AWS::Chatbot::CustomAction", + "mappings": [], + "operation": "DeleteCustomAction", + "phase": "delete", + "service": "chatbot" + }, + { + "cfn_type": "AWS::Chatbot::MicrosoftTeamsChannelConfiguration", + "mappings": [ + { + "source": "ConfigurationName", + "target": "ConfigurationName" + }, + { + "source": "IamRoleArn", + "target": "IamRoleArn" + }, + { + "source": "LoggingLevel", + "target": "LoggingLevel" + }, + { + "source": "SnsTopicArns", + "target": "SnsTopicArns" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TeamId", + "target": "TeamId" + } + ], + "operation": "CreateMicrosoftTeamsChannelConfiguration", + "phase": "create", + "service": "chatbot" + }, + { + "cfn_type": "AWS::Chatbot::MicrosoftTeamsChannelConfiguration", + "mappings": [], + "operation": "DeleteMicrosoftTeamsChannelConfiguration", + "phase": "delete", + "service": "chatbot" + }, + { + "cfn_type": "AWS::Chatbot::SlackChannelConfiguration", + "mappings": [ + { + "source": "ConfigurationName", + "target": "ConfigurationName" + }, + { + "source": "IamRoleArn", + "target": "IamRoleArn" + }, + { + "source": "LoggingLevel", + "target": "LoggingLevel" + }, + { + "source": "SlackChannelId", + "target": "SlackChannelId" + }, + { + "source": "SnsTopicArns", + "target": "SnsTopicArns" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSlackChannelConfiguration", + "phase": "create", + "service": "chatbot" + }, + { + "cfn_type": "AWS::Chatbot::SlackChannelConfiguration", + "mappings": [], + "operation": "DeleteSlackChannelConfiguration", + "phase": "delete", + "service": "chatbot" + }, + { + "cfn_type": "AWS::Chime::AppInstance", + "mappings": [ + { + "source": "Metadata", + "target": "Metadata" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAppInstance", + "phase": "create", + "service": "chime-sdk-identity" + }, + { + "cfn_type": "AWS::Chime::AppInstance", + "mappings": [], + "operation": "DeleteAppInstance", + "phase": "delete", + "service": "chime-sdk-identity" + }, + { + "cfn_type": "AWS::Chime::AppInstanceBot", + "mappings": [ + { + "source": "AppInstanceArn", + "target": "AppInstanceArn" + }, + { + "source": "Configuration", + "target": "Configuration" + }, + { + "source": "Metadata", + "target": "Metadata" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAppInstanceBot", + "phase": "create", + "service": "chime-sdk-identity" + }, + { + "cfn_type": "AWS::Chime::AppInstanceBot", + "mappings": [], + "operation": "DeleteAppInstanceBot", + "phase": "delete", + "service": "chime-sdk-identity" + }, + { + "cfn_type": "AWS::Chime::AppInstanceUser", + "mappings": [ + { + "source": "AppInstanceArn", + "target": "AppInstanceArn" + }, + { + "source": "AppInstanceUserId", + "target": "AppInstanceUserId" + }, + { + "source": "ExpirationSettings", + "target": "ExpirationSettings" + }, + { + "source": "Metadata", + "target": "Metadata" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAppInstanceUser", + "phase": "create", + "service": "chime-sdk-identity" + }, + { + "cfn_type": "AWS::Chime::AppInstanceUser", + "mappings": [], + "operation": "DeleteAppInstanceUser", + "phase": "delete", + "service": "chime-sdk-identity" + }, + { + "cfn_type": "AWS::CleanRooms::AnalysisTemplate", + "mappings": [ + { + "source": "analysisParameters", + "target": "AnalysisParameters" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "format", + "target": "Format" + }, + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "schema", + "target": "Schema" + }, + { + "source": "source", + "target": "Source" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAnalysisTemplate", + "phase": "create", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::AnalysisTemplate", + "mappings": [ + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + } + ], + "operation": "DeleteAnalysisTemplate", + "phase": "delete", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::Collaboration", + "mappings": [ + { + "source": "analyticsEngine", + "target": "AnalyticsEngine" + }, + { + "source": "creatorDisplayName", + "target": "CreatorDisplayName" + }, + { + "source": "creatorMLMemberAbilities", + "target": "CreatorMLMemberAbilities" + }, + { + "source": "creatorMemberAbilities", + "target": "CreatorMemberAbilities" + }, + { + "source": "creatorPaymentConfiguration", + "target": "CreatorPaymentConfiguration" + }, + { + "source": "dataEncryptionMetadata", + "target": "DataEncryptionMetadata" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "jobLogStatus", + "target": "JobLogStatus" + }, + { + "source": "members", + "target": "Members" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "queryLogStatus", + "target": "QueryLogStatus" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCollaboration", + "phase": "create", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::Collaboration", + "mappings": [], + "operation": "DeleteCollaboration", + "phase": "delete", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::ConfiguredTable", + "mappings": [ + { + "source": "allowedColumns", + "target": "AllowedColumns" + }, + { + "source": "analysisMethod", + "target": "AnalysisMethod" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "selectedAnalysisMethods", + "target": "SelectedAnalysisMethods" + }, + { + "source": "tableReference", + "target": "TableReference" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateConfiguredTable", + "phase": "create", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::ConfiguredTable", + "mappings": [], + "operation": "DeleteConfiguredTable", + "phase": "delete", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::ConfiguredTableAssociation", + "mappings": [ + { + "source": "configuredTableIdentifier", + "target": "ConfiguredTableIdentifier" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateConfiguredTableAssociation", + "phase": "create", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::ConfiguredTableAssociation", + "mappings": [ + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + } + ], + "operation": "DeleteConfiguredTableAssociation", + "phase": "delete", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::IdMappingTable", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "inputReferenceConfig", + "target": "InputReferenceConfig" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateIdMappingTable", + "phase": "create", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::IdMappingTable", + "mappings": [ + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + } + ], + "operation": "DeleteIdMappingTable", + "phase": "delete", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::IdNamespaceAssociation", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "idMappingConfig", + "target": "IdMappingConfig" + }, + { + "source": "inputReferenceConfig", + "target": "InputReferenceConfig" + }, + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateIdNamespaceAssociation", + "phase": "create", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::IdNamespaceAssociation", + "mappings": [ + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + } + ], + "operation": "DeleteIdNamespaceAssociation", + "phase": "delete", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::Membership", + "mappings": [ + { + "source": "collaborationIdentifier", + "target": "CollaborationIdentifier" + }, + { + "source": "defaultJobResultConfiguration", + "target": "DefaultJobResultConfiguration" + }, + { + "source": "defaultResultConfiguration", + "target": "DefaultResultConfiguration" + }, + { + "source": "jobLogStatus", + "target": "JobLogStatus" + }, + { + "source": "paymentConfiguration", + "target": "PaymentConfiguration" + }, + { + "source": "queryLogStatus", + "target": "QueryLogStatus" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateMembership", + "phase": "create", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::Membership", + "mappings": [], + "operation": "DeleteMembership", + "phase": "delete", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::PrivacyBudgetTemplate", + "mappings": [ + { + "source": "autoRefresh", + "target": "AutoRefresh" + }, + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + }, + { + "source": "parameters", + "target": "Parameters" + }, + { + "source": "privacyBudgetType", + "target": "PrivacyBudgetType" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePrivacyBudgetTemplate", + "phase": "create", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::PrivacyBudgetTemplate", + "mappings": [ + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + } + ], + "operation": "DeletePrivacyBudgetTemplate", + "phase": "delete", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRoomsML::ConfiguredModelAlgorithm", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "inferenceContainerConfig", + "target": "InferenceContainerConfig" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "trainingContainerConfig", + "target": "TrainingContainerConfig" + } + ], + "operation": "CreateConfiguredModelAlgorithm", + "phase": "create", + "service": "cleanroomsml" + }, + { + "cfn_type": "AWS::CleanRoomsML::ConfiguredModelAlgorithm", + "mappings": [], + "operation": "DeleteConfiguredModelAlgorithm", + "phase": "delete", + "service": "cleanroomsml" + }, + { + "cfn_type": "AWS::CleanRoomsML::ConfiguredModelAlgorithmAssociation", + "mappings": [ + { + "source": "configuredModelAlgorithmArn", + "target": "ConfiguredModelAlgorithmArn" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "privacyConfiguration", + "target": "PrivacyConfiguration" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateConfiguredModelAlgorithmAssociation", + "phase": "create", + "service": "cleanroomsml" + }, + { + "cfn_type": "AWS::CleanRoomsML::ConfiguredModelAlgorithmAssociation", + "mappings": [ + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + } + ], + "operation": "DeleteConfiguredModelAlgorithmAssociation", + "phase": "delete", + "service": "cleanroomsml" + }, + { + "cfn_type": "AWS::CleanRoomsML::TrainingDataset", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "trainingData", + "target": "TrainingData" + } + ], + "operation": "CreateTrainingDataset", + "phase": "create", + "service": "cleanroomsml" + }, + { + "cfn_type": "AWS::CleanRoomsML::TrainingDataset", + "mappings": [], + "operation": "DeleteTrainingDataset", + "phase": "delete", + "service": "cleanroomsml" + }, + { + "cfn_type": "AWS::CloudFront::AnycastIpList", + "mappings": [ + { + "source": "IpCount", + "target": "IpCount" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAnycastIpList", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::AnycastIpList", + "mappings": [], + "operation": "DeleteAnycastIpList", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::CachePolicy", + "mappings": [ + { + "source": "CachePolicyConfig", + "target": "CachePolicyConfig" + } + ], + "operation": "CreateCachePolicy", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::CachePolicy", + "mappings": [], + "operation": "DeleteCachePolicy", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "mappings": [ + { + "source": "CloudFrontOriginAccessIdentityConfig", + "target": "CloudFrontOriginAccessIdentityConfig" + } + ], + "operation": "CreateCloudFrontOriginAccessIdentity", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "mappings": [], + "operation": "DeleteCloudFrontOriginAccessIdentity", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::ConnectionGroup", + "mappings": [ + { + "source": "AnycastIpListId", + "target": "AnycastIpListId" + }, + { + "source": "Enabled", + "target": "Enabled" + }, + { + "source": "Ipv6Enabled", + "target": "Ipv6Enabled" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateConnectionGroup", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::ConnectionGroup", + "mappings": [], + "operation": "DeleteConnectionGroup", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::ContinuousDeploymentPolicy", + "mappings": [ + { + "source": "ContinuousDeploymentPolicyConfig", + "target": "ContinuousDeploymentPolicyConfig" + } + ], + "operation": "CreateContinuousDeploymentPolicy", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::ContinuousDeploymentPolicy", + "mappings": [], + "operation": "DeleteContinuousDeploymentPolicy", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::Distribution", + "mappings": [ + { + "source": "DistributionConfig", + "target": "DistributionConfig" + } + ], + "operation": "CreateDistribution", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::Distribution", + "mappings": [], + "operation": "DeleteDistribution", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::DistributionTenant", + "mappings": [ + { + "source": "ConnectionGroupId", + "target": "ConnectionGroupId" + }, + { + "source": "Customizations", + "target": "Customizations" + }, + { + "source": "DistributionId", + "target": "DistributionId" + }, + { + "source": "Domains", + "target": "Domains" + }, + { + "source": "Enabled", + "target": "Enabled" + }, + { + "source": "ManagedCertificateRequest", + "target": "ManagedCertificateRequest" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Parameters", + "target": "Parameters" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDistributionTenant", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::DistributionTenant", + "mappings": [], + "operation": "DeleteDistributionTenant", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::Function", + "mappings": [ + { + "source": "FunctionCode", + "target": "FunctionCode" + }, + { + "source": "FunctionConfig", + "target": "FunctionConfig" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateFunction", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::Function", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteFunction", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::KeyGroup", + "mappings": [ + { + "source": "KeyGroupConfig", + "target": "KeyGroupConfig" + } + ], + "operation": "CreateKeyGroup", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::KeyGroup", + "mappings": [], + "operation": "DeleteKeyGroup", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::KeyValueStore", + "mappings": [ + { + "source": "Comment", + "target": "Comment" + }, + { + "source": "ImportSource", + "target": "ImportSource" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateKeyValueStore", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::KeyValueStore", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteKeyValueStore", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::MonitoringSubscription", + "mappings": [ + { + "source": "DistributionId", + "target": "DistributionId" + }, + { + "source": "MonitoringSubscription", + "target": "MonitoringSubscription" + } + ], + "operation": "CreateMonitoringSubscription", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::MonitoringSubscription", + "mappings": [ + { + "source": "DistributionId", + "target": "DistributionId" + } + ], + "operation": "DeleteMonitoringSubscription", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::OriginAccessControl", + "mappings": [ + { + "source": "OriginAccessControlConfig", + "target": "OriginAccessControlConfig" + } + ], + "operation": "CreateOriginAccessControl", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::OriginAccessControl", + "mappings": [], + "operation": "DeleteOriginAccessControl", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::OriginRequestPolicy", + "mappings": [ + { + "source": "OriginRequestPolicyConfig", + "target": "OriginRequestPolicyConfig" + } + ], + "operation": "CreateOriginRequestPolicy", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::OriginRequestPolicy", + "mappings": [], + "operation": "DeleteOriginRequestPolicy", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::PublicKey", + "mappings": [ + { + "source": "PublicKeyConfig", + "target": "PublicKeyConfig" + } + ], + "operation": "CreatePublicKey", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::PublicKey", + "mappings": [], + "operation": "DeletePublicKey", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::RealtimeLogConfig", + "mappings": [ + { + "source": "EndPoints", + "target": "EndPoints" + }, + { + "source": "Fields", + "target": "Fields" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "SamplingRate", + "target": "SamplingRate" + } + ], + "operation": "CreateRealtimeLogConfig", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::RealtimeLogConfig", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteRealtimeLogConfig", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::ResponseHeadersPolicy", + "mappings": [ + { + "source": "ResponseHeadersPolicyConfig", + "target": "ResponseHeadersPolicyConfig" + } + ], + "operation": "CreateResponseHeadersPolicy", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::ResponseHeadersPolicy", + "mappings": [], + "operation": "DeleteResponseHeadersPolicy", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::VpcOrigin", + "mappings": [ + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcOriginEndpointConfig", + "target": "VpcOriginEndpointConfig" + } + ], + "operation": "CreateVpcOrigin", + "phase": "create", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::VpcOrigin", + "mappings": [], + "operation": "DeleteVpcOrigin", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudTrail::Channel", + "mappings": [ + { + "source": "Destinations", + "target": "Destinations" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Source", + "target": "Source" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateChannel", + "phase": "create", + "service": "cloudtrail" + }, + { + "cfn_type": "AWS::CloudTrail::Channel", + "mappings": [], + "operation": "DeleteChannel", + "phase": "delete", + "service": "cloudtrail" + }, + { + "cfn_type": "AWS::CloudTrail::Dashboard", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "RefreshSchedule", + "target": "RefreshSchedule" + }, + { + "source": "TerminationProtectionEnabled", + "target": "TerminationProtectionEnabled" + }, + { + "source": "Widgets", + "target": "Widgets" + } + ], + "operation": "CreateDashboard", + "phase": "create", + "service": "cloudtrail" + }, + { + "cfn_type": "AWS::CloudTrail::Dashboard", + "mappings": [], + "operation": "DeleteDashboard", + "phase": "delete", + "service": "cloudtrail" + }, + { + "cfn_type": "AWS::CloudTrail::EventDataStore", + "mappings": [ + { + "source": "AdvancedEventSelectors", + "target": "AdvancedEventSelectors" + }, + { + "source": "BillingMode", + "target": "BillingMode" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "MultiRegionEnabled", + "target": "MultiRegionEnabled" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "OrganizationEnabled", + "target": "OrganizationEnabled" + }, + { + "source": "RetentionPeriod", + "target": "RetentionPeriod" + }, + { + "source": "TerminationProtectionEnabled", + "target": "TerminationProtectionEnabled" + } + ], + "operation": "CreateEventDataStore", + "phase": "create", + "service": "cloudtrail" + }, + { + "cfn_type": "AWS::CloudTrail::EventDataStore", + "mappings": [], + "operation": "DeleteEventDataStore", + "phase": "delete", + "service": "cloudtrail" + }, + { + "cfn_type": "AWS::CloudTrail::ResourcePolicy", + "mappings": [ + { + "source": "ResourceArn", + "target": "ResourceArn" + }, + { + "source": "ResourcePolicy", + "target": "ResourcePolicy" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "cloudtrail" + }, + { + "cfn_type": "AWS::CloudTrail::ResourcePolicy", + "mappings": [ + { + "source": "ResourceArn", + "target": "ResourceArn" + } + ], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "cloudtrail" + }, + { + "cfn_type": "AWS::CloudTrail::Trail", + "mappings": [ + { + "source": "CloudWatchLogsLogGroupArn", + "target": "CloudWatchLogsLogGroupArn" + }, + { + "source": "CloudWatchLogsRoleArn", + "target": "CloudWatchLogsRoleArn" + }, + { + "source": "EnableLogFileValidation", + "target": "EnableLogFileValidation" + }, + { + "source": "IncludeGlobalServiceEvents", + "target": "IncludeGlobalServiceEvents" + }, + { + "source": "IsMultiRegionTrail", + "target": "IsMultiRegionTrail" + }, + { + "source": "IsOrganizationTrail", + "target": "IsOrganizationTrail" + }, + { + "source": "KmsKeyId", + "target": "KMSKeyId" + }, + { + "source": "Name", + "target": "TrailName" + }, + { + "source": "S3BucketName", + "target": "S3BucketName" + }, + { + "source": "S3KeyPrefix", + "target": "S3KeyPrefix" + }, + { + "source": "SnsTopicName", + "target": "SnsTopicName" + } + ], + "operation": "CreateTrail", + "phase": "create", + "service": "cloudtrail" + }, + { + "cfn_type": "AWS::CloudTrail::Trail", + "mappings": [ + { + "source": "Name", + "target": "TrailName" + } + ], + "operation": "DeleteTrail", + "phase": "delete", + "service": "cloudtrail" + }, + { + "cfn_type": "AWS::CloudWatch::Alarm", + "mappings": [ + { + "source": "ActionsEnabled", + "target": "ActionsEnabled" + }, + { + "source": "AlarmActions", + "target": "AlarmActions" + }, + { + "source": "AlarmDescription", + "target": "AlarmDescription" + }, + { + "source": "AlarmName", + "target": "AlarmName" + }, + { + "source": "ComparisonOperator", + "target": "ComparisonOperator" + }, + { + "source": "DatapointsToAlarm", + "target": "DatapointsToAlarm" + }, + { + "source": "Dimensions", + "target": "Dimensions" + }, + { + "source": "EvaluateLowSampleCountPercentile", + "target": "EvaluateLowSampleCountPercentile" + }, + { + "source": "EvaluationPeriods", + "target": "EvaluationPeriods" + }, + { + "source": "ExtendedStatistic", + "target": "ExtendedStatistic" + }, + { + "source": "InsufficientDataActions", + "target": "InsufficientDataActions" + }, + { + "source": "MetricName", + "target": "MetricName" + }, + { + "source": "Metrics", + "target": "Metrics" + }, + { + "source": "Namespace", + "target": "Namespace" + }, + { + "source": "OKActions", + "target": "OKActions" + }, + { + "source": "Period", + "target": "Period" + }, + { + "source": "Statistic", + "target": "Statistic" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Threshold", + "target": "Threshold" + }, + { + "source": "ThresholdMetricId", + "target": "ThresholdMetricId" + }, + { + "source": "TreatMissingData", + "target": "TreatMissingData" + }, + { + "source": "Unit", + "target": "Unit" + } + ], + "operation": "PutMetricAlarm", + "phase": "create", + "service": "cloudwatch" + }, + { + "cfn_type": "AWS::CloudWatch::Alarm", + "mappings": [], + "operation": "DeleteAlarms", + "phase": "delete", + "service": "cloudwatch" + }, + { + "cfn_type": "AWS::CloudWatch::CompositeAlarm", + "mappings": [ + { + "source": "ActionsEnabled", + "target": "ActionsEnabled" + }, + { + "source": "ActionsSuppressor", + "target": "ActionsSuppressor" + }, + { + "source": "ActionsSuppressorExtensionPeriod", + "target": "ActionsSuppressorExtensionPeriod" + }, + { + "source": "ActionsSuppressorWaitPeriod", + "target": "ActionsSuppressorWaitPeriod" + }, + { + "source": "AlarmActions", + "target": "AlarmActions" + }, + { + "source": "AlarmDescription", + "target": "AlarmDescription" + }, + { + "source": "AlarmName", + "target": "AlarmName" + }, + { + "source": "AlarmRule", + "target": "AlarmRule" + }, + { + "source": "InsufficientDataActions", + "target": "InsufficientDataActions" + }, + { + "source": "OKActions", + "target": "OKActions" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "PutCompositeAlarm", + "phase": "create", + "service": "cloudwatch" + }, + { + "cfn_type": "AWS::CloudWatch::Dashboard", + "mappings": [ + { + "source": "DashboardBody", + "target": "DashboardBody" + }, + { + "source": "DashboardName", + "target": "DashboardName" + } + ], + "operation": "PutDashboard", + "phase": "create", + "service": "cloudwatch" + }, + { + "cfn_type": "AWS::CloudWatch::Dashboard", + "mappings": [], + "operation": "DeleteDashboards", + "phase": "delete", + "service": "cloudwatch" + }, + { + "cfn_type": "AWS::CloudWatch::InsightRule", + "mappings": [ + { + "source": "ApplyOnTransformedLogs", + "target": "ApplyOnTransformedLogs" + }, + { + "source": "RuleName", + "target": "RuleName" + }, + { + "source": "RuleState", + "target": "RuleState" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "PutInsightRule", + "phase": "create", + "service": "cloudwatch" + }, + { + "cfn_type": "AWS::CloudWatch::InsightRule", + "mappings": [], + "operation": "DeleteInsightRules", + "phase": "delete", + "service": "cloudwatch" + }, + { + "cfn_type": "AWS::CloudWatch::MetricStream", + "mappings": [ + { + "source": "ExcludeFilters", + "target": "ExcludeFilters" + }, + { + "source": "FirehoseArn", + "target": "FirehoseArn" + }, + { + "source": "IncludeFilters", + "target": "IncludeFilters" + }, + { + "source": "IncludeLinkedAccountsMetrics", + "target": "IncludeLinkedAccountsMetrics" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "OutputFormat", + "target": "OutputFormat" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "StatisticsConfigurations", + "target": "StatisticsConfigurations" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "PutMetricStream", + "phase": "create", + "service": "cloudwatch" + }, + { + "cfn_type": "AWS::CloudWatch::MetricStream", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteMetricStream", + "phase": "delete", + "service": "cloudwatch" + }, + { + "cfn_type": "AWS::CodeArtifact::Domain", + "mappings": [ + { + "source": "domain", + "target": "DomainName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDomain", + "phase": "create", + "service": "codeartifact" + }, + { + "cfn_type": "AWS::CodeArtifact::Domain", + "mappings": [ + { + "source": "domain", + "target": "DomainName" + } + ], + "operation": "DeleteDomain", + "phase": "delete", + "service": "codeartifact" + }, + { + "cfn_type": "AWS::CodeArtifact::PackageGroup", + "mappings": [ + { + "source": "contactInfo", + "target": "ContactInfo" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "domain", + "target": "DomainName" + }, + { + "source": "domainOwner", + "target": "DomainOwner" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePackageGroup", + "phase": "create", + "service": "codeartifact" + }, + { + "cfn_type": "AWS::CodeArtifact::PackageGroup", + "mappings": [ + { + "source": "domain", + "target": "DomainName" + }, + { + "source": "domainOwner", + "target": "DomainOwner" + } + ], + "operation": "DeletePackageGroup", + "phase": "delete", + "service": "codeartifact" + }, + { + "cfn_type": "AWS::CodeArtifact::Repository", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "domain", + "target": "DomainName" + }, + { + "source": "repository", + "target": "RepositoryName" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "upstreams", + "target": "Upstreams" + } + ], + "operation": "CreateRepository", + "phase": "create", + "service": "codeartifact" + }, + { + "cfn_type": "AWS::CodeArtifact::Repository", + "mappings": [ + { + "source": "domain", + "target": "DomainName" + }, + { + "source": "repository", + "target": "RepositoryName" + } + ], + "operation": "DeleteRepository", + "phase": "delete", + "service": "codeartifact" + }, + { + "cfn_type": "AWS::CodeBuild::Fleet", + "mappings": [ + { + "source": "baseCapacity", + "target": "BaseCapacity" + }, + { + "source": "computeConfiguration", + "target": "ComputeConfiguration" + }, + { + "source": "computeType", + "target": "ComputeType" + }, + { + "source": "environmentType", + "target": "EnvironmentType" + }, + { + "source": "fleetServiceRole", + "target": "FleetServiceRole" + }, + { + "source": "imageId", + "target": "ImageId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "overflowBehavior", + "target": "OverflowBehavior" + }, + { + "source": "scalingConfiguration", + "target": "ScalingConfiguration" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateFleet", + "phase": "create", + "service": "codebuild" + }, + { + "cfn_type": "AWS::CodeBuild::Fleet", + "mappings": [], + "operation": "DeleteFleet", + "phase": "delete", + "service": "codebuild" + }, + { + "cfn_type": "AWS::CodeConnections::Connection", + "mappings": [ + { + "source": "ConnectionName", + "target": "ConnectionName" + }, + { + "source": "HostArn", + "target": "HostArn" + }, + { + "source": "ProviderType", + "target": "ProviderType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateConnection", + "phase": "create", + "service": "codeconnections" + }, + { + "cfn_type": "AWS::CodeConnections::Connection", + "mappings": [], + "operation": "DeleteConnection", + "phase": "delete", + "service": "codeconnections" + }, + { + "cfn_type": "AWS::CodeDeploy::Application", + "mappings": [ + { + "source": "applicationName", + "target": "ApplicationName" + }, + { + "source": "computePlatform", + "target": "ComputePlatform" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "codedeploy" + }, + { + "cfn_type": "AWS::CodeDeploy::Application", + "mappings": [ + { + "source": "applicationName", + "target": "ApplicationName" + } + ], + "operation": "DeleteApplication", + "phase": "delete", + "service": "codedeploy" + }, + { + "cfn_type": "AWS::CodeDeploy::DeploymentConfig", + "mappings": [ + { + "source": "computePlatform", + "target": "ComputePlatform" + }, + { + "source": "deploymentConfigName", + "target": "DeploymentConfigName" + }, + { + "source": "minimumHealthyHosts", + "target": "MinimumHealthyHosts" + }, + { + "source": "trafficRoutingConfig", + "target": "TrafficRoutingConfig" + }, + { + "source": "zonalConfig", + "target": "ZonalConfig" + } + ], + "operation": "CreateDeploymentConfig", + "phase": "create", + "service": "codedeploy" + }, + { + "cfn_type": "AWS::CodeDeploy::DeploymentConfig", + "mappings": [ + { + "source": "deploymentConfigName", + "target": "DeploymentConfigName" + } + ], + "operation": "DeleteDeploymentConfig", + "phase": "delete", + "service": "codedeploy" + }, + { + "cfn_type": "AWS::CodeDeploy::DeploymentGroup", + "mappings": [ + { + "source": "alarmConfiguration", + "target": "AlarmConfiguration" + }, + { + "source": "applicationName", + "target": "ApplicationName" + }, + { + "source": "autoRollbackConfiguration", + "target": "AutoRollbackConfiguration" + }, + { + "source": "autoScalingGroups", + "target": "AutoScalingGroups" + }, + { + "source": "blueGreenDeploymentConfiguration", + "target": "BlueGreenDeploymentConfiguration" + }, + { + "source": "deploymentConfigName", + "target": "DeploymentConfigName" + }, + { + "source": "deploymentGroupName", + "target": "DeploymentGroupName" + }, + { + "source": "deploymentStyle", + "target": "DeploymentStyle" + }, + { + "source": "ec2TagFilters", + "target": "Ec2TagFilters" + }, + { + "source": "ec2TagSet", + "target": "Ec2TagSet" + }, + { + "source": "ecsServices", + "target": "ECSServices" + }, + { + "source": "loadBalancerInfo", + "target": "LoadBalancerInfo" + }, + { + "source": "onPremisesInstanceTagFilters", + "target": "OnPremisesInstanceTagFilters" + }, + { + "source": "onPremisesTagSet", + "target": "OnPremisesTagSet" + }, + { + "source": "outdatedInstancesStrategy", + "target": "OutdatedInstancesStrategy" + }, + { + "source": "serviceRoleArn", + "target": "ServiceRoleArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "terminationHookEnabled", + "target": "TerminationHookEnabled" + }, + { + "source": "triggerConfigurations", + "target": "TriggerConfigurations" + } + ], + "operation": "CreateDeploymentGroup", + "phase": "create", + "service": "codedeploy" + }, + { + "cfn_type": "AWS::CodeDeploy::DeploymentGroup", + "mappings": [ + { + "source": "applicationName", + "target": "ApplicationName" + }, + { + "source": "deploymentGroupName", + "target": "DeploymentGroupName" + } + ], + "operation": "DeleteDeploymentGroup", + "phase": "delete", + "service": "codedeploy" + }, + { + "cfn_type": "AWS::CodeGuruProfiler::ProfilingGroup", + "mappings": [ + { + "source": "computePlatform", + "target": "ComputePlatform" + }, + { + "source": "profilingGroupName", + "target": "ProfilingGroupName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateProfilingGroup", + "phase": "create", + "service": "codeguruprofiler" + }, + { + "cfn_type": "AWS::CodeGuruProfiler::ProfilingGroup", + "mappings": [ + { + "source": "profilingGroupName", + "target": "ProfilingGroupName" + } + ], + "operation": "DeleteProfilingGroup", + "phase": "delete", + "service": "codeguruprofiler" + }, + { + "cfn_type": "AWS::CodePipeline::CustomActionType", + "mappings": [ + { + "source": "category", + "target": "Category" + }, + { + "source": "configurationProperties", + "target": "ConfigurationProperties" + }, + { + "source": "inputArtifactDetails", + "target": "InputArtifactDetails" + }, + { + "source": "outputArtifactDetails", + "target": "OutputArtifactDetails" + }, + { + "source": "provider", + "target": "Provider" + }, + { + "source": "settings", + "target": "Settings" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "version", + "target": "Version" + } + ], + "operation": "CreateCustomActionType", + "phase": "create", + "service": "codepipeline" + }, + { + "cfn_type": "AWS::CodePipeline::CustomActionType", + "mappings": [ + { + "source": "category", + "target": "Category" + }, + { + "source": "provider", + "target": "Provider" + }, + { + "source": "version", + "target": "Version" + } + ], + "operation": "DeleteCustomActionType", + "phase": "delete", + "service": "codepipeline" + }, + { + "cfn_type": "AWS::CodePipeline::Pipeline", + "mappings": [ + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePipeline", + "phase": "create", + "service": "codepipeline" + }, + { + "cfn_type": "AWS::CodePipeline::Pipeline", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeletePipeline", + "phase": "delete", + "service": "codepipeline" + }, + { + "cfn_type": "AWS::CodePipeline::Webhook", + "mappings": [ + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "PutWebhook", + "phase": "create", + "service": "codepipeline" + }, + { + "cfn_type": "AWS::CodePipeline::Webhook", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteWebhook", + "phase": "delete", + "service": "codepipeline" + }, + { + "cfn_type": "AWS::CodeStarConnections::Connection", + "mappings": [ + { + "source": "ConnectionName", + "target": "ConnectionName" + }, + { + "source": "HostArn", + "target": "HostArn" + }, + { + "source": "ProviderType", + "target": "ProviderType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateConnection", + "phase": "create", + "service": "codestar-connections" + }, + { + "cfn_type": "AWS::CodeStarConnections::Connection", + "mappings": [], + "operation": "DeleteConnection", + "phase": "delete", + "service": "codestar-connections" + }, + { + "cfn_type": "AWS::CodeStarConnections::RepositoryLink", + "mappings": [ + { + "source": "ConnectionArn", + "target": "ConnectionArn" + }, + { + "source": "EncryptionKeyArn", + "target": "EncryptionKeyArn" + }, + { + "source": "OwnerId", + "target": "OwnerId" + }, + { + "source": "RepositoryName", + "target": "RepositoryName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRepositoryLink", + "phase": "create", + "service": "codestar-connections" + }, + { + "cfn_type": "AWS::CodeStarConnections::RepositoryLink", + "mappings": [], + "operation": "DeleteRepositoryLink", + "phase": "delete", + "service": "codestar-connections" + }, + { + "cfn_type": "AWS::CodeStarConnections::SyncConfiguration", + "mappings": [ + { + "source": "Branch", + "target": "Branch" + }, + { + "source": "ConfigFile", + "target": "ConfigFile" + }, + { + "source": "PublishDeploymentStatus", + "target": "PublishDeploymentStatus" + }, + { + "source": "RepositoryLinkId", + "target": "RepositoryLinkId" + }, + { + "source": "ResourceName", + "target": "ResourceName" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "SyncType", + "target": "SyncType" + }, + { + "source": "TriggerResourceUpdateOn", + "target": "TriggerResourceUpdateOn" + } + ], + "operation": "CreateSyncConfiguration", + "phase": "create", + "service": "codestar-connections" + }, + { + "cfn_type": "AWS::CodeStarConnections::SyncConfiguration", + "mappings": [ + { + "source": "ResourceName", + "target": "ResourceName" + }, + { + "source": "SyncType", + "target": "SyncType" + } + ], + "operation": "DeleteSyncConfiguration", + "phase": "delete", + "service": "codestar-connections" + }, + { + "cfn_type": "AWS::CodeStarNotifications::NotificationRule", + "mappings": [ + { + "source": "DetailType", + "target": "DetailType" + }, + { + "source": "EventTypeIds", + "target": "EventTypeIds" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Resource", + "target": "Resource" + }, + { + "source": "Status", + "target": "Status" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Targets", + "target": "Targets" + } + ], + "operation": "CreateNotificationRule", + "phase": "create", + "service": "codestar-notifications" + }, + { + "cfn_type": "AWS::CodeStarNotifications::NotificationRule", + "mappings": [], + "operation": "DeleteNotificationRule", + "phase": "delete", + "service": "codestar-notifications" + }, + { + "cfn_type": "AWS::Cognito::IdentityPool", + "mappings": [ + { + "source": "AllowClassicFlow", + "target": "AllowClassicFlow" + }, + { + "source": "AllowUnauthenticatedIdentities", + "target": "AllowUnauthenticatedIdentities" + }, + { + "source": "CognitoIdentityProviders", + "target": "CognitoIdentityProviders" + }, + { + "source": "DeveloperProviderName", + "target": "DeveloperProviderName" + }, + { + "source": "IdentityPoolName", + "target": "IdentityPoolName" + }, + { + "source": "IdentityPoolTags", + "target": "IdentityPoolTags" + }, + { + "source": "OpenIdConnectProviderARNs", + "target": "OpenIdConnectProviderARNs" + }, + { + "source": "SamlProviderARNs", + "target": "SamlProviderARNs" + }, + { + "source": "SupportedLoginProviders", + "target": "SupportedLoginProviders" + } + ], + "operation": "CreateIdentityPool", + "phase": "create", + "service": "cognito-identity" + }, + { + "cfn_type": "AWS::Cognito::IdentityPool", + "mappings": [], + "operation": "DeleteIdentityPool", + "phase": "delete", + "service": "cognito-identity" + }, + { + "cfn_type": "AWS::Cognito::IdentityPoolPrincipalTag", + "mappings": [ + { + "source": "IdentityPoolId", + "target": "IdentityPoolId" + }, + { + "source": "IdentityProviderName", + "target": "IdentityProviderName" + }, + { + "source": "PrincipalTags", + "target": "PrincipalTags" + }, + { + "source": "UseDefaults", + "target": "UseDefaults" + } + ], + "operation": "SetPrincipalTagAttributeMap", + "phase": "create", + "service": "cognito-identity" + }, + { + "cfn_type": "AWS::Cognito::IdentityPoolRoleAttachment", + "mappings": [ + { + "source": "IdentityPoolId", + "target": "IdentityPoolId" + }, + { + "source": "RoleMappings", + "target": "RoleMappings" + }, + { + "source": "Roles", + "target": "Roles" + } + ], + "operation": "SetIdentityPoolRoles", + "phase": "create", + "service": "cognito-identity" + }, + { + "cfn_type": "AWS::Cognito::LogDeliveryConfiguration", + "mappings": [ + { + "source": "LogConfigurations", + "target": "LogConfigurations" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "SetLogDeliveryConfiguration", + "phase": "create", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::ManagedLoginBranding", + "mappings": [ + { + "source": "Assets", + "target": "Assets" + }, + { + "source": "ClientId", + "target": "ClientId" + }, + { + "source": "Settings", + "target": "Settings" + }, + { + "source": "UseCognitoProvidedValues", + "target": "UseCognitoProvidedValues" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "CreateManagedLoginBranding", + "phase": "create", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::ManagedLoginBranding", + "mappings": [ + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "DeleteManagedLoginBranding", + "phase": "delete", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPool", + "mappings": [ + { + "source": "AccountRecoverySetting", + "target": "AccountRecoverySetting" + }, + { + "source": "AdminCreateUserConfig", + "target": "AdminCreateUserConfig" + }, + { + "source": "AliasAttributes", + "target": "AliasAttributes" + }, + { + "source": "AutoVerifiedAttributes", + "target": "AutoVerifiedAttributes" + }, + { + "source": "DeletionProtection", + "target": "DeletionProtection" + }, + { + "source": "DeviceConfiguration", + "target": "DeviceConfiguration" + }, + { + "source": "EmailConfiguration", + "target": "EmailConfiguration" + }, + { + "source": "EmailVerificationMessage", + "target": "EmailVerificationMessage" + }, + { + "source": "EmailVerificationSubject", + "target": "EmailVerificationSubject" + }, + { + "source": "LambdaConfig", + "target": "LambdaConfig" + }, + { + "source": "MfaConfiguration", + "target": "MfaConfiguration" + }, + { + "source": "Policies", + "target": "Policies" + }, + { + "source": "Schema", + "target": "Schema" + }, + { + "source": "SmsAuthenticationMessage", + "target": "SmsAuthenticationMessage" + }, + { + "source": "SmsConfiguration", + "target": "SmsConfiguration" + }, + { + "source": "SmsVerificationMessage", + "target": "SmsVerificationMessage" + }, + { + "source": "UserAttributeUpdateSettings", + "target": "UserAttributeUpdateSettings" + }, + { + "source": "UserPoolAddOns", + "target": "UserPoolAddOns" + }, + { + "source": "UserPoolTags", + "target": "UserPoolTags" + }, + { + "source": "UserPoolTier", + "target": "UserPoolTier" + }, + { + "source": "UsernameAttributes", + "target": "UsernameAttributes" + }, + { + "source": "UsernameConfiguration", + "target": "UsernameConfiguration" + }, + { + "source": "VerificationMessageTemplate", + "target": "VerificationMessageTemplate" + } + ], + "operation": "CreateUserPool", + "phase": "create", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPool", + "mappings": [], + "operation": "DeleteUserPool", + "phase": "delete", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolClient", + "mappings": [ + { + "source": "AccessTokenValidity", + "target": "AccessTokenValidity" + }, + { + "source": "AllowedOAuthFlows", + "target": "AllowedOAuthFlows" + }, + { + "source": "AllowedOAuthFlowsUserPoolClient", + "target": "AllowedOAuthFlowsUserPoolClient" + }, + { + "source": "AllowedOAuthScopes", + "target": "AllowedOAuthScopes" + }, + { + "source": "AnalyticsConfiguration", + "target": "AnalyticsConfiguration" + }, + { + "source": "AuthSessionValidity", + "target": "AuthSessionValidity" + }, + { + "source": "CallbackURLs", + "target": "CallbackURLs" + }, + { + "source": "ClientName", + "target": "ClientName" + }, + { + "source": "DefaultRedirectURI", + "target": "DefaultRedirectURI" + }, + { + "source": "EnablePropagateAdditionalUserContextData", + "target": "EnablePropagateAdditionalUserContextData" + }, + { + "source": "EnableTokenRevocation", + "target": "EnableTokenRevocation" + }, + { + "source": "ExplicitAuthFlows", + "target": "ExplicitAuthFlows" + }, + { + "source": "GenerateSecret", + "target": "GenerateSecret" + }, + { + "source": "IdTokenValidity", + "target": "IdTokenValidity" + }, + { + "source": "LogoutURLs", + "target": "LogoutURLs" + }, + { + "source": "PreventUserExistenceErrors", + "target": "PreventUserExistenceErrors" + }, + { + "source": "ReadAttributes", + "target": "ReadAttributes" + }, + { + "source": "RefreshTokenRotation", + "target": "RefreshTokenRotation" + }, + { + "source": "RefreshTokenValidity", + "target": "RefreshTokenValidity" + }, + { + "source": "SupportedIdentityProviders", + "target": "SupportedIdentityProviders" + }, + { + "source": "TokenValidityUnits", + "target": "TokenValidityUnits" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + }, + { + "source": "WriteAttributes", + "target": "WriteAttributes" + } + ], + "operation": "CreateUserPoolClient", + "phase": "create", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolClient", + "mappings": [ + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "DeleteUserPoolClient", + "phase": "delete", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolDomain", + "mappings": [ + { + "source": "CustomDomainConfig", + "target": "CustomDomainConfig" + }, + { + "source": "Domain", + "target": "Domain" + }, + { + "source": "ManagedLoginVersion", + "target": "ManagedLoginVersion" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "CreateUserPoolDomain", + "phase": "create", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolDomain", + "mappings": [ + { + "source": "Domain", + "target": "Domain" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "DeleteUserPoolDomain", + "phase": "delete", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "GroupName", + "target": "GroupName" + }, + { + "source": "Precedence", + "target": "Precedence" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "CreateGroup", + "phase": "create", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolGroup", + "mappings": [ + { + "source": "GroupName", + "target": "GroupName" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "DeleteGroup", + "phase": "delete", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolIdentityProvider", + "mappings": [ + { + "source": "AttributeMapping", + "target": "AttributeMapping" + }, + { + "source": "IdpIdentifiers", + "target": "IdpIdentifiers" + }, + { + "source": "ProviderDetails", + "target": "ProviderDetails" + }, + { + "source": "ProviderName", + "target": "ProviderName" + }, + { + "source": "ProviderType", + "target": "ProviderType" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "CreateIdentityProvider", + "phase": "create", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolIdentityProvider", + "mappings": [ + { + "source": "ProviderName", + "target": "ProviderName" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "DeleteIdentityProvider", + "phase": "delete", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolResourceServer", + "mappings": [ + { + "source": "Identifier", + "target": "Identifier" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Scopes", + "target": "Scopes" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "CreateResourceServer", + "phase": "create", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolResourceServer", + "mappings": [ + { + "source": "Identifier", + "target": "Identifier" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "DeleteResourceServer", + "phase": "delete", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolRiskConfigurationAttachment", + "mappings": [ + { + "source": "AccountTakeoverRiskConfiguration", + "target": "AccountTakeoverRiskConfiguration" + }, + { + "source": "ClientId", + "target": "ClientId" + }, + { + "source": "CompromisedCredentialsRiskConfiguration", + "target": "CompromisedCredentialsRiskConfiguration" + }, + { + "source": "RiskExceptionConfiguration", + "target": "RiskExceptionConfiguration" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "SetRiskConfiguration", + "phase": "create", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolUICustomizationAttachment", + "mappings": [ + { + "source": "CSS", + "target": "CSS" + }, + { + "source": "ClientId", + "target": "ClientId" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "SetUICustomization", + "phase": "create", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Comprehend::DocumentClassifier", + "mappings": [ + { + "source": "DataAccessRoleArn", + "target": "DataAccessRoleArn" + }, + { + "source": "DocumentClassifierName", + "target": "DocumentClassifierName" + }, + { + "source": "InputDataConfig", + "target": "InputDataConfig" + }, + { + "source": "LanguageCode", + "target": "LanguageCode" + }, + { + "source": "Mode", + "target": "Mode" + }, + { + "source": "ModelKmsKeyId", + "target": "ModelKmsKeyId" + }, + { + "source": "ModelPolicy", + "target": "ModelPolicy" + }, + { + "source": "OutputDataConfig", + "target": "OutputDataConfig" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VersionName", + "target": "VersionName" + }, + { + "source": "VolumeKmsKeyId", + "target": "VolumeKmsKeyId" + }, + { + "source": "VpcConfig", + "target": "VpcConfig" + } + ], + "operation": "CreateDocumentClassifier", + "phase": "create", + "service": "comprehend" + }, + { + "cfn_type": "AWS::Comprehend::DocumentClassifier", + "mappings": [], + "operation": "DeleteDocumentClassifier", + "phase": "delete", + "service": "comprehend" + }, + { + "cfn_type": "AWS::Comprehend::Flywheel", + "mappings": [ + { + "source": "ActiveModelArn", + "target": "ActiveModelArn" + }, + { + "source": "DataAccessRoleArn", + "target": "DataAccessRoleArn" + }, + { + "source": "DataLakeS3Uri", + "target": "DataLakeS3Uri" + }, + { + "source": "DataSecurityConfig", + "target": "DataSecurityConfig" + }, + { + "source": "FlywheelName", + "target": "FlywheelName" + }, + { + "source": "ModelType", + "target": "ModelType" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TaskConfig", + "target": "TaskConfig" + } + ], + "operation": "CreateFlywheel", + "phase": "create", + "service": "comprehend" + }, + { + "cfn_type": "AWS::Comprehend::Flywheel", + "mappings": [], + "operation": "DeleteFlywheel", + "phase": "delete", + "service": "comprehend" + }, + { + "cfn_type": "AWS::Config::AggregationAuthorization", + "mappings": [ + { + "source": "AuthorizedAccountId", + "target": "AuthorizedAccountId" + }, + { + "source": "AuthorizedAwsRegion", + "target": "AuthorizedAwsRegion" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "PutAggregationAuthorization", + "phase": "create", + "service": "config" + }, + { + "cfn_type": "AWS::Config::AggregationAuthorization", + "mappings": [ + { + "source": "AuthorizedAccountId", + "target": "AuthorizedAccountId" + }, + { + "source": "AuthorizedAwsRegion", + "target": "AuthorizedAwsRegion" + } + ], + "operation": "DeleteAggregationAuthorization", + "phase": "delete", + "service": "config" + }, + { + "cfn_type": "AWS::Config::ConfigRule", + "mappings": [ + { + "source": "ConfigRule", + "target": "ConfigRuleName" + } + ], + "operation": "PutConfigRule", + "phase": "create", + "service": "config" + }, + { + "cfn_type": "AWS::Config::ConfigRule", + "mappings": [ + { + "source": "ConfigRuleName", + "target": "ConfigRuleName" + } + ], + "operation": "DeleteConfigRule", + "phase": "delete", + "service": "config" + }, + { + "cfn_type": "AWS::Config::ConfigurationAggregator", + "mappings": [ + { + "source": "AccountAggregationSources", + "target": "AccountAggregationSources" + }, + { + "source": "ConfigurationAggregatorName", + "target": "ConfigurationAggregatorName" + }, + { + "source": "OrganizationAggregationSource", + "target": "OrganizationAggregationSource" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "PutConfigurationAggregator", + "phase": "create", + "service": "config" + }, + { + "cfn_type": "AWS::Config::ConfigurationAggregator", + "mappings": [ + { + "source": "ConfigurationAggregatorName", + "target": "ConfigurationAggregatorName" + } + ], + "operation": "DeleteConfigurationAggregator", + "phase": "delete", + "service": "config" + }, + { + "cfn_type": "AWS::Config::ConformancePack", + "mappings": [ + { + "source": "ConformancePackInputParameters", + "target": "ConformancePackInputParameters" + }, + { + "source": "ConformancePackName", + "target": "ConformancePackName" + }, + { + "source": "DeliveryS3Bucket", + "target": "DeliveryS3Bucket" + }, + { + "source": "DeliveryS3KeyPrefix", + "target": "DeliveryS3KeyPrefix" + }, + { + "source": "TemplateBody", + "target": "TemplateBody" + }, + { + "source": "TemplateS3Uri", + "target": "TemplateS3Uri" + }, + { + "source": "TemplateSSMDocumentDetails", + "target": "TemplateSSMDocumentDetails" + } + ], + "operation": "PutConformancePack", + "phase": "create", + "service": "config" + }, + { + "cfn_type": "AWS::Config::ConformancePack", + "mappings": [ + { + "source": "ConformancePackName", + "target": "ConformancePackName" + } + ], + "operation": "DeleteConformancePack", + "phase": "delete", + "service": "config" + }, + { + "cfn_type": "AWS::Config::OrganizationConformancePack", + "mappings": [ + { + "source": "ConformancePackInputParameters", + "target": "ConformancePackInputParameters" + }, + { + "source": "DeliveryS3Bucket", + "target": "DeliveryS3Bucket" + }, + { + "source": "DeliveryS3KeyPrefix", + "target": "DeliveryS3KeyPrefix" + }, + { + "source": "ExcludedAccounts", + "target": "ExcludedAccounts" + }, + { + "source": "OrganizationConformancePackName", + "target": "OrganizationConformancePackName" + }, + { + "source": "TemplateBody", + "target": "TemplateBody" + }, + { + "source": "TemplateS3Uri", + "target": "TemplateS3Uri" + } + ], + "operation": "PutOrganizationConformancePack", + "phase": "create", + "service": "config" + }, + { + "cfn_type": "AWS::Config::OrganizationConformancePack", + "mappings": [ + { + "source": "OrganizationConformancePackName", + "target": "OrganizationConformancePackName" + } + ], + "operation": "DeleteOrganizationConformancePack", + "phase": "delete", + "service": "config" + }, + { + "cfn_type": "AWS::Config::RemediationConfiguration", + "mappings": [ + { + "source": "ConfigRuleName", + "target": "ConfigRuleName" + }, + { + "source": "ResourceType", + "target": "ResourceType" + } + ], + "operation": "DeleteRemediationConfiguration", + "phase": "delete", + "service": "config" + }, + { + "cfn_type": "AWS::Config::StoredQuery", + "mappings": [ + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "PutStoredQuery", + "phase": "create", + "service": "config" + }, + { + "cfn_type": "AWS::Config::StoredQuery", + "mappings": [ + { + "source": "QueryName", + "target": "QueryName" + } + ], + "operation": "DeleteStoredQuery", + "phase": "delete", + "service": "config" + }, + { + "cfn_type": "AWS::Connect::AgentStatus", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayOrder", + "target": "DisplayOrder" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "State", + "target": "State" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAgentStatus", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ApprovedOrigin", + "mappings": [ + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "Origin", + "target": "Origin" + } + ], + "operation": "AssociateApprovedOrigin", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ApprovedOrigin", + "mappings": [ + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "Origin", + "target": "Origin" + } + ], + "operation": "DisassociateApprovedOrigin", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ContactFlow", + "mappings": [ + { + "source": "Content", + "target": "Content" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateContactFlow", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ContactFlow", + "mappings": [], + "operation": "DeleteContactFlow", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ContactFlowModule", + "mappings": [ + { + "source": "Content", + "target": "Content" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateContactFlowModule", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ContactFlowModule", + "mappings": [], + "operation": "DeleteContactFlowModule", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ContactFlowVersion", + "mappings": [ + { + "source": "ContactFlowId", + "target": "ContactFlowId" + }, + { + "source": "Description", + "target": "Description" + } + ], + "operation": "CreateContactFlowVersion", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ContactFlowVersion", + "mappings": [ + { + "source": "ContactFlowId", + "target": "ContactFlowId" + } + ], + "operation": "DeleteContactFlowVersion", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::DataLakeAssociation", + "mappings": [ + { + "source": "DataSetId", + "target": "DataSetId" + }, + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "TargetAccountId", + "target": "TargetAccountId" + } + ], + "operation": "AssociateAnalyticsDataSet", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::DataLakeAssociation", + "mappings": [ + { + "source": "DataSetId", + "target": "DataSetId" + }, + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "TargetAccountId", + "target": "TargetAccountId" + } + ], + "operation": "DisassociateAnalyticsDataSet", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::EmailAddress", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "EmailAddress", + "target": "EmailAddress" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEmailAddress", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::EmailAddress", + "mappings": [], + "operation": "DeleteEmailAddress", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::EvaluationForm", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Items", + "target": "Items" + }, + { + "source": "ScoringStrategy", + "target": "ScoringStrategy" + }, + { + "source": "Title", + "target": "Title" + } + ], + "operation": "CreateEvaluationForm", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::EvaluationForm", + "mappings": [], + "operation": "DeleteEvaluationForm", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::HoursOfOperation", + "mappings": [ + { + "source": "Config", + "target": "Config" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TimeZone", + "target": "TimeZone" + } + ], + "operation": "CreateHoursOfOperation", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::HoursOfOperation", + "mappings": [], + "operation": "DeleteHoursOfOperation", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::Instance", + "mappings": [ + { + "source": "DirectoryId", + "target": "DirectoryId" + }, + { + "source": "IdentityManagementType", + "target": "IdentityManagementType" + }, + { + "source": "InstanceAlias", + "target": "InstanceAlias" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateInstance", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::Instance", + "mappings": [], + "operation": "DeleteInstance", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::InstanceStorageConfig", + "mappings": [ + { + "source": "ResourceType", + "target": "ResourceType" + } + ], + "operation": "AssociateInstanceStorageConfig", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::InstanceStorageConfig", + "mappings": [ + { + "source": "ResourceType", + "target": "ResourceType" + } + ], + "operation": "DisassociateInstanceStorageConfig", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::IntegrationAssociation", + "mappings": [ + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "IntegrationArn", + "target": "IntegrationArn" + }, + { + "source": "IntegrationType", + "target": "IntegrationType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateIntegrationAssociation", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::IntegrationAssociation", + "mappings": [ + { + "source": "InstanceId", + "target": "InstanceId" + } + ], + "operation": "DeleteIntegrationAssociation", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::PhoneNumber", + "mappings": [ + { + "source": "SourcePhoneNumberArn", + "target": "SourcePhoneNumberArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "ImportPhoneNumber", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::PhoneNumber", + "mappings": [], + "operation": "ReleasePhoneNumber", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::PredefinedAttribute", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Values", + "target": "Values" + } + ], + "operation": "CreatePredefinedAttribute", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::PredefinedAttribute", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeletePredefinedAttribute", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::Prompt", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "S3Uri", + "target": "S3Uri" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePrompt", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::Prompt", + "mappings": [], + "operation": "DeletePrompt", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::Queue", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "MaxContacts", + "target": "MaxContacts" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "OutboundCallerConfig", + "target": "OutboundCallerConfig" + }, + { + "source": "OutboundEmailConfig", + "target": "OutboundEmailConfig" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateQueue", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::Queue", + "mappings": [], + "operation": "DeleteQueue", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::QuickConnect", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "QuickConnectConfig", + "target": "QuickConnectConfig" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateQuickConnect", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::QuickConnect", + "mappings": [], + "operation": "DeleteQuickConnect", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::RoutingProfile", + "mappings": [ + { + "source": "AgentAvailabilityTimer", + "target": "AgentAvailabilityTimer" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "MediaConcurrencies", + "target": "MediaConcurrencies" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "QueueConfigs", + "target": "QueueConfigs" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRoutingProfile", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::RoutingProfile", + "mappings": [], + "operation": "DeleteRoutingProfile", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::Rule", + "mappings": [ + { + "source": "Actions", + "target": "Actions" + }, + { + "source": "Function", + "target": "Function" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "PublishStatus", + "target": "PublishStatus" + }, + { + "source": "TriggerEventSource", + "target": "TriggerEventSource" + } + ], + "operation": "CreateRule", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::Rule", + "mappings": [], + "operation": "DeleteRule", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::SecurityKey", + "mappings": [ + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "Key", + "target": "Key" + } + ], + "operation": "AssociateSecurityKey", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::SecurityKey", + "mappings": [ + { + "source": "InstanceId", + "target": "InstanceId" + } + ], + "operation": "DisassociateSecurityKey", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::SecurityProfile", + "mappings": [ + { + "source": "AllowedAccessControlHierarchyGroupId", + "target": "AllowedAccessControlHierarchyGroupId" + }, + { + "source": "AllowedAccessControlTags", + "target": "AllowedAccessControlTags" + }, + { + "source": "Applications", + "target": "Applications" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "HierarchyRestrictedResources", + "target": "HierarchyRestrictedResources" + }, + { + "source": "Permissions", + "target": "Permissions" + }, + { + "source": "SecurityProfileName", + "target": "SecurityProfileName" + }, + { + "source": "TagRestrictedResources", + "target": "TagRestrictedResources" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSecurityProfile", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::SecurityProfile", + "mappings": [], + "operation": "DeleteSecurityProfile", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::TaskTemplate", + "mappings": [ + { + "source": "ClientToken", + "target": "ClientToken" + }, + { + "source": "Constraints", + "target": "Constraints" + }, + { + "source": "Defaults", + "target": "Defaults" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Fields", + "target": "Fields" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Status", + "target": "Status" + } + ], + "operation": "CreateTaskTemplate", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::TaskTemplate", + "mappings": [], + "operation": "DeleteTaskTemplate", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::TrafficDistributionGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateTrafficDistributionGroup", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::TrafficDistributionGroup", + "mappings": [], + "operation": "DeleteTrafficDistributionGroup", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::User", + "mappings": [ + { + "source": "DirectoryUserId", + "target": "DirectoryUserId" + }, + { + "source": "IdentityInfo", + "target": "IdentityInfo" + }, + { + "source": "Password", + "target": "Password" + }, + { + "source": "PhoneConfig", + "target": "PhoneConfig" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Username", + "target": "Username" + } + ], + "operation": "CreateUser", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::User", + "mappings": [], + "operation": "DeleteUser", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::UserHierarchyGroup", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateUserHierarchyGroup", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::UserHierarchyGroup", + "mappings": [], + "operation": "DeleteUserHierarchyGroup", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::View", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateView", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::View", + "mappings": [], + "operation": "DeleteView", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ViewVersion", + "mappings": [ + { + "source": "VersionDescription", + "target": "VersionDescription" + }, + { + "source": "ViewContentSha256", + "target": "ViewContentSha256" + } + ], + "operation": "CreateViewVersion", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ViewVersion", + "mappings": [], + "operation": "DeleteViewVersion", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::ConnectCampaigns::Campaign", + "mappings": [ + { + "source": "dialerConfig", + "target": "DialerConfig" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "outboundCallConfig", + "target": "OutboundCallConfig" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCampaign", + "phase": "create", + "service": "connectcampaigns" + }, + { + "cfn_type": "AWS::ConnectCampaigns::Campaign", + "mappings": [], + "operation": "DeleteCampaign", + "phase": "delete", + "service": "connectcampaigns" + }, + { + "cfn_type": "AWS::ConnectCampaignsV2::Campaign", + "mappings": [ + { + "source": "channelSubtypeConfig", + "target": "ChannelSubtypeConfig" + }, + { + "source": "communicationLimitsOverride", + "target": "CommunicationLimitsOverride" + }, + { + "source": "communicationTimeConfig", + "target": "CommunicationTimeConfig" + }, + { + "source": "connectCampaignFlowArn", + "target": "ConnectCampaignFlowArn" + }, + { + "source": "connectInstanceId", + "target": "ConnectInstanceId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "schedule", + "target": "Schedule" + }, + { + "source": "source", + "target": "Source" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCampaign", + "phase": "create", + "service": "connectcampaignsv2" + }, + { + "cfn_type": "AWS::ConnectCampaignsV2::Campaign", + "mappings": [], + "operation": "DeleteCampaign", + "phase": "delete", + "service": "connectcampaignsv2" + }, + { + "cfn_type": "AWS::ControlTower::EnabledBaseline", + "mappings": [ + { + "source": "baselineIdentifier", + "target": "BaselineIdentifier" + }, + { + "source": "baselineVersion", + "target": "BaselineVersion" + }, + { + "source": "parameters", + "target": "Parameters" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetIdentifier", + "target": "TargetIdentifier" + } + ], + "operation": "EnableBaseline", + "phase": "create", + "service": "controltower" + }, + { + "cfn_type": "AWS::ControlTower::EnabledControl", + "mappings": [ + { + "source": "controlIdentifier", + "target": "ControlIdentifier" + }, + { + "source": "parameters", + "target": "Parameters" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetIdentifier", + "target": "TargetIdentifier" + } + ], + "operation": "EnableControl", + "phase": "create", + "service": "controltower" + }, + { + "cfn_type": "AWS::ControlTower::LandingZone", + "mappings": [ + { + "source": "manifest", + "target": "Manifest" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "version", + "target": "Version" + } + ], + "operation": "CreateLandingZone", + "phase": "create", + "service": "controltower" + }, + { + "cfn_type": "AWS::ControlTower::LandingZone", + "mappings": [], + "operation": "DeleteLandingZone", + "phase": "delete", + "service": "controltower" + }, + { + "cfn_type": "AWS::CustomerProfiles::CalculatedAttributeDefinition", + "mappings": [ + { + "source": "AttributeDetails", + "target": "AttributeDetails" + }, + { + "source": "CalculatedAttributeName", + "target": "CalculatedAttributeName" + }, + { + "source": "Conditions", + "target": "Conditions" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "Statistic", + "target": "Statistic" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UseHistoricalData", + "target": "UseHistoricalData" + } + ], + "operation": "CreateCalculatedAttributeDefinition", + "phase": "create", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::CalculatedAttributeDefinition", + "mappings": [ + { + "source": "CalculatedAttributeName", + "target": "CalculatedAttributeName" + }, + { + "source": "DomainName", + "target": "DomainName" + } + ], + "operation": "DeleteCalculatedAttributeDefinition", + "phase": "delete", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::Domain", + "mappings": [ + { + "source": "DeadLetterQueueUrl", + "target": "DeadLetterQueueUrl" + }, + { + "source": "DefaultEncryptionKey", + "target": "DefaultEncryptionKey" + }, + { + "source": "DefaultExpirationDays", + "target": "DefaultExpirationDays" + }, + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "Matching", + "target": "Matching" + }, + { + "source": "RuleBasedMatching", + "target": "RuleBasedMatching" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDomain", + "phase": "create", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::Domain", + "mappings": [ + { + "source": "DomainName", + "target": "DomainName" + } + ], + "operation": "DeleteDomain", + "phase": "delete", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::EventStream", + "mappings": [ + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "EventStreamName", + "target": "EventStreamName" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Uri", + "target": "Uri" + } + ], + "operation": "CreateEventStream", + "phase": "create", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::EventStream", + "mappings": [ + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "EventStreamName", + "target": "EventStreamName" + } + ], + "operation": "DeleteEventStream", + "phase": "delete", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::EventTrigger", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "EventTriggerConditions", + "target": "EventTriggerConditions" + }, + { + "source": "EventTriggerLimits", + "target": "EventTriggerLimits" + }, + { + "source": "EventTriggerName", + "target": "EventTriggerName" + }, + { + "source": "ObjectTypeName", + "target": "ObjectTypeName" + }, + { + "source": "SegmentFilter", + "target": "SegmentFilter" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEventTrigger", + "phase": "create", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::EventTrigger", + "mappings": [ + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "EventTriggerName", + "target": "EventTriggerName" + } + ], + "operation": "DeleteEventTrigger", + "phase": "delete", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::Integration", + "mappings": [ + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "EventTriggerNames", + "target": "EventTriggerNames" + }, + { + "source": "FlowDefinition", + "target": "FlowDefinition" + }, + { + "source": "ObjectTypeName", + "target": "ObjectTypeName" + }, + { + "source": "ObjectTypeNames", + "target": "ObjectTypeNames" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Uri", + "target": "Uri" + } + ], + "operation": "PutIntegration", + "phase": "create", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::Integration", + "mappings": [ + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "Uri", + "target": "Uri" + } + ], + "operation": "DeleteIntegration", + "phase": "delete", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::ObjectType", + "mappings": [ + { + "source": "AllowProfileCreation", + "target": "AllowProfileCreation" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "EncryptionKey", + "target": "EncryptionKey" + }, + { + "source": "ExpirationDays", + "target": "ExpirationDays" + }, + { + "source": "Fields", + "target": "Fields" + }, + { + "source": "Keys", + "target": "Keys" + }, + { + "source": "MaxProfileObjectCount", + "target": "MaxProfileObjectCount" + }, + { + "source": "ObjectTypeName", + "target": "ObjectTypeName" + }, + { + "source": "SourceLastUpdatedTimestampFormat", + "target": "SourceLastUpdatedTimestampFormat" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TemplateId", + "target": "TemplateId" + } + ], + "operation": "PutProfileObjectType", + "phase": "create", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::ObjectType", + "mappings": [ + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "ObjectTypeName", + "target": "ObjectTypeName" + } + ], + "operation": "DeleteProfileObjectType", + "phase": "delete", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::SegmentDefinition", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "SegmentDefinitionName", + "target": "SegmentDefinitionName" + }, + { + "source": "SegmentGroups", + "target": "SegmentGroups" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSegmentDefinition", + "phase": "create", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::SegmentDefinition", + "mappings": [ + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "SegmentDefinitionName", + "target": "SegmentDefinitionName" + } + ], + "operation": "DeleteSegmentDefinition", + "phase": "delete", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::DMS::Certificate", + "mappings": [ + { + "source": "CertificateIdentifier", + "target": "CertificateIdentifier" + }, + { + "source": "CertificatePem", + "target": "CertificatePem" + }, + { + "source": "CertificateWallet", + "target": "CertificateWallet" + } + ], + "operation": "ImportCertificate", + "phase": "create", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::Certificate", + "mappings": [], + "operation": "DeleteCertificate", + "phase": "delete", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::DataMigration", + "mappings": [ + { + "source": "DataMigrationName", + "target": "DataMigrationName" + }, + { + "source": "DataMigrationType", + "target": "DataMigrationType" + }, + { + "source": "MigrationProjectIdentifier", + "target": "MigrationProjectIdentifier" + }, + { + "source": "ServiceAccessRoleArn", + "target": "ServiceAccessRoleArn" + }, + { + "source": "SourceDataSettings", + "target": "SourceDataSettings" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDataMigration", + "phase": "create", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::DataMigration", + "mappings": [ + { + "source": "DataMigrationIdentifier", + "target": "DataMigrationIdentifier" + } + ], + "operation": "DeleteDataMigration", + "phase": "delete", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::DataProvider", + "mappings": [ + { + "source": "DataProviderName", + "target": "DataProviderName" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "Settings", + "target": "Settings" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDataProvider", + "phase": "create", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::DataProvider", + "mappings": [ + { + "source": "DataProviderIdentifier", + "target": "DataProviderIdentifier" + } + ], + "operation": "DeleteDataProvider", + "phase": "delete", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::Endpoint", + "mappings": [ + { + "source": "CertificateArn", + "target": "CertificateArn" + }, + { + "source": "DatabaseName", + "target": "DatabaseName" + }, + { + "source": "DocDbSettings", + "target": "DocDbSettings" + }, + { + "source": "DynamoDbSettings", + "target": "DynamoDbSettings" + }, + { + "source": "ElasticsearchSettings", + "target": "ElasticsearchSettings" + }, + { + "source": "EndpointIdentifier", + "target": "EndpointIdentifier" + }, + { + "source": "EndpointType", + "target": "EndpointType" + }, + { + "source": "EngineName", + "target": "EngineName" + }, + { + "source": "ExtraConnectionAttributes", + "target": "ExtraConnectionAttributes" + }, + { + "source": "GcpMySQLSettings", + "target": "GcpMySQLSettings" + }, + { + "source": "IBMDb2Settings", + "target": "IbmDb2Settings" + }, + { + "source": "KafkaSettings", + "target": "KafkaSettings" + }, + { + "source": "KinesisSettings", + "target": "KinesisSettings" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "MicrosoftSQLServerSettings", + "target": "MicrosoftSqlServerSettings" + }, + { + "source": "MongoDbSettings", + "target": "MongoDbSettings" + }, + { + "source": "MySQLSettings", + "target": "MySqlSettings" + }, + { + "source": "NeptuneSettings", + "target": "NeptuneSettings" + }, + { + "source": "OracleSettings", + "target": "OracleSettings" + }, + { + "source": "Password", + "target": "Password" + }, + { + "source": "Port", + "target": "Port" + }, + { + "source": "PostgreSQLSettings", + "target": "PostgreSqlSettings" + }, + { + "source": "RedisSettings", + "target": "RedisSettings" + }, + { + "source": "RedshiftSettings", + "target": "RedshiftSettings" + }, + { + "source": "ResourceIdentifier", + "target": "ResourceIdentifier" + }, + { + "source": "S3Settings", + "target": "S3Settings" + }, + { + "source": "ServerName", + "target": "ServerName" + }, + { + "source": "SslMode", + "target": "SslMode" + }, + { + "source": "SybaseSettings", + "target": "SybaseSettings" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Username", + "target": "Username" + } + ], + "operation": "CreateEndpoint", + "phase": "create", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::Endpoint", + "mappings": [], + "operation": "DeleteEndpoint", + "phase": "delete", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::EventSubscription", + "mappings": [ + { + "source": "Enabled", + "target": "Enabled" + }, + { + "source": "EventCategories", + "target": "EventCategories" + }, + { + "source": "SnsTopicArn", + "target": "SnsTopicArn" + }, + { + "source": "SourceIds", + "target": "SourceIds" + }, + { + "source": "SourceType", + "target": "SourceType" + }, + { + "source": "SubscriptionName", + "target": "SubscriptionName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEventSubscription", + "phase": "create", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::EventSubscription", + "mappings": [ + { + "source": "SubscriptionName", + "target": "SubscriptionName" + } + ], + "operation": "DeleteEventSubscription", + "phase": "delete", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::InstanceProfile", + "mappings": [ + { + "source": "AvailabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "InstanceProfileName", + "target": "InstanceProfileName" + }, + { + "source": "KmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "NetworkType", + "target": "NetworkType" + }, + { + "source": "PubliclyAccessible", + "target": "PubliclyAccessible" + }, + { + "source": "SubnetGroupIdentifier", + "target": "SubnetGroupIdentifier" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcSecurityGroups", + "target": "VpcSecurityGroups" + } + ], + "operation": "CreateInstanceProfile", + "phase": "create", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::InstanceProfile", + "mappings": [ + { + "source": "InstanceProfileIdentifier", + "target": "InstanceProfileIdentifier" + } + ], + "operation": "DeleteInstanceProfile", + "phase": "delete", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::MigrationProject", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "InstanceProfileIdentifier", + "target": "InstanceProfileIdentifier" + }, + { + "source": "MigrationProjectName", + "target": "MigrationProjectName" + }, + { + "source": "SchemaConversionApplicationAttributes", + "target": "SchemaConversionApplicationAttributes" + }, + { + "source": "SourceDataProviderDescriptors", + "target": "SourceDataProviderDescriptors" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetDataProviderDescriptors", + "target": "TargetDataProviderDescriptors" + }, + { + "source": "TransformationRules", + "target": "TransformationRules" + } + ], + "operation": "CreateMigrationProject", + "phase": "create", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::MigrationProject", + "mappings": [ + { + "source": "MigrationProjectIdentifier", + "target": "MigrationProjectIdentifier" + } + ], + "operation": "DeleteMigrationProject", + "phase": "delete", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::ReplicationConfig", + "mappings": [ + { + "source": "ComputeConfig", + "target": "ComputeConfig" + }, + { + "source": "ReplicationConfigIdentifier", + "target": "ReplicationConfigIdentifier" + }, + { + "source": "ReplicationSettings", + "target": "ReplicationSettings" + }, + { + "source": "ReplicationType", + "target": "ReplicationType" + }, + { + "source": "ResourceIdentifier", + "target": "ResourceIdentifier" + }, + { + "source": "SourceEndpointArn", + "target": "SourceEndpointArn" + }, + { + "source": "SupplementalSettings", + "target": "SupplementalSettings" + }, + { + "source": "TableMappings", + "target": "TableMappings" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetEndpointArn", + "target": "TargetEndpointArn" + } + ], + "operation": "CreateReplicationConfig", + "phase": "create", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::ReplicationConfig", + "mappings": [], + "operation": "DeleteReplicationConfig", + "phase": "delete", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::ReplicationSubnetGroup", + "mappings": [ + { + "source": "ReplicationSubnetGroupDescription", + "target": "ReplicationSubnetGroupDescription" + }, + { + "source": "ReplicationSubnetGroupIdentifier", + "target": "ReplicationSubnetGroupIdentifier" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateReplicationSubnetGroup", + "phase": "create", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::ReplicationSubnetGroup", + "mappings": [ + { + "source": "ReplicationSubnetGroupIdentifier", + "target": "ReplicationSubnetGroupIdentifier" + } + ], + "operation": "DeleteReplicationSubnetGroup", + "phase": "delete", + "service": "dms" + }, + { + "cfn_type": "AWS::DSQL::Cluster", + "mappings": [ + { + "source": "deletionProtectionEnabled", + "target": "DeletionProtectionEnabled" + }, + { + "source": "kmsEncryptionKey", + "target": "KmsEncryptionKey" + }, + { + "source": "multiRegionProperties", + "target": "MultiRegionProperties" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCluster", + "phase": "create", + "service": "dsql" + }, + { + "cfn_type": "AWS::DSQL::Cluster", + "mappings": [], + "operation": "DeleteCluster", + "phase": "delete", + "service": "dsql" + }, + { + "cfn_type": "AWS::DataBrew::Dataset", + "mappings": [ + { + "source": "Format", + "target": "Format" + }, + { + "source": "FormatOptions", + "target": "FormatOptions" + }, + { + "source": "Input", + "target": "Input" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "PathOptions", + "target": "PathOptions" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDataset", + "phase": "create", + "service": "databrew" + }, + { + "cfn_type": "AWS::DataBrew::Dataset", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteDataset", + "phase": "delete", + "service": "databrew" + }, + { + "cfn_type": "AWS::DataBrew::Job", + "mappings": [ + { + "source": "DataCatalogOutputs", + "target": "DataCatalogOutputs" + }, + { + "source": "DatabaseOutputs", + "target": "DatabaseOutputs" + }, + { + "source": "DatasetName", + "target": "DatasetName" + }, + { + "source": "EncryptionKeyArn", + "target": "EncryptionKeyArn" + }, + { + "source": "EncryptionMode", + "target": "EncryptionMode" + }, + { + "source": "LogSubscription", + "target": "LogSubscription" + }, + { + "source": "MaxCapacity", + "target": "MaxCapacity" + }, + { + "source": "MaxRetries", + "target": "MaxRetries" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Outputs", + "target": "Outputs" + }, + { + "source": "ProjectName", + "target": "ProjectName" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Timeout", + "target": "Timeout" + } + ], + "operation": "CreateRecipeJob", + "phase": "create", + "service": "databrew" + }, + { + "cfn_type": "AWS::DataBrew::Job", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteJob", + "phase": "delete", + "service": "databrew" + }, + { + "cfn_type": "AWS::DataBrew::Project", + "mappings": [ + { + "source": "DatasetName", + "target": "DatasetName" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RecipeName", + "target": "RecipeName" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Sample", + "target": "Sample" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateProject", + "phase": "create", + "service": "databrew" + }, + { + "cfn_type": "AWS::DataBrew::Project", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteProject", + "phase": "delete", + "service": "databrew" + }, + { + "cfn_type": "AWS::DataBrew::Recipe", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Steps", + "target": "Steps" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRecipe", + "phase": "create", + "service": "databrew" + }, + { + "cfn_type": "AWS::DataBrew::Ruleset", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Rules", + "target": "Rules" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetArn", + "target": "TargetArn" + } + ], + "operation": "CreateRuleset", + "phase": "create", + "service": "databrew" + }, + { + "cfn_type": "AWS::DataBrew::Ruleset", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteRuleset", + "phase": "delete", + "service": "databrew" + }, + { + "cfn_type": "AWS::DataBrew::Schedule", + "mappings": [ + { + "source": "CronExpression", + "target": "CronExpression" + }, + { + "source": "JobNames", + "target": "JobNames" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSchedule", + "phase": "create", + "service": "databrew" + }, + { + "cfn_type": "AWS::DataBrew::Schedule", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteSchedule", + "phase": "delete", + "service": "databrew" + }, + { + "cfn_type": "AWS::DataPipeline::Pipeline", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + } + ], + "operation": "CreatePipeline", + "phase": "create", + "service": "datapipeline" + }, + { + "cfn_type": "AWS::DataPipeline::Pipeline", + "mappings": [], + "operation": "DeletePipeline", + "phase": "delete", + "service": "datapipeline" + }, + { + "cfn_type": "AWS::DataSync::Agent", + "mappings": [ + { + "source": "ActivationKey", + "target": "ActivationKey" + }, + { + "source": "AgentName", + "target": "AgentName" + }, + { + "source": "SecurityGroupArns", + "target": "SecurityGroupArns" + }, + { + "source": "SubnetArns", + "target": "SubnetArns" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcEndpointId", + "target": "VpcEndpointId" + } + ], + "operation": "CreateAgent", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::Agent", + "mappings": [], + "operation": "DeleteAgent", + "phase": "delete", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::LocationAzureBlob", + "mappings": [ + { + "source": "AgentArns", + "target": "AgentArns" + }, + { + "source": "CmkSecretConfig", + "target": "CmkSecretConfig" + }, + { + "source": "CustomSecretConfig", + "target": "CustomSecretConfig" + }, + { + "source": "Subdirectory", + "target": "Subdirectory" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLocationAzureBlob", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::LocationEFS", + "mappings": [ + { + "source": "AccessPointArn", + "target": "AccessPointArn" + }, + { + "source": "Ec2Config", + "target": "Ec2Config" + }, + { + "source": "EfsFilesystemArn", + "target": "EfsFilesystemArn" + }, + { + "source": "FileSystemAccessRoleArn", + "target": "FileSystemAccessRoleArn" + }, + { + "source": "InTransitEncryption", + "target": "InTransitEncryption" + }, + { + "source": "Subdirectory", + "target": "Subdirectory" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLocationEfs", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::LocationFSxLustre", + "mappings": [ + { + "source": "FsxFilesystemArn", + "target": "FsxFilesystemArn" + }, + { + "source": "SecurityGroupArns", + "target": "SecurityGroupArns" + }, + { + "source": "Subdirectory", + "target": "Subdirectory" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLocationFsxLustre", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::LocationFSxONTAP", + "mappings": [ + { + "source": "Protocol", + "target": "Protocol" + }, + { + "source": "SecurityGroupArns", + "target": "SecurityGroupArns" + }, + { + "source": "StorageVirtualMachineArn", + "target": "StorageVirtualMachineArn" + }, + { + "source": "Subdirectory", + "target": "Subdirectory" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLocationFsxOntap", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::LocationFSxOpenZFS", + "mappings": [ + { + "source": "FsxFilesystemArn", + "target": "FsxFilesystemArn" + }, + { + "source": "Protocol", + "target": "Protocol" + }, + { + "source": "SecurityGroupArns", + "target": "SecurityGroupArns" + }, + { + "source": "Subdirectory", + "target": "Subdirectory" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLocationFsxOpenZfs", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::LocationFSxWindows", + "mappings": [ + { + "source": "Domain", + "target": "Domain" + }, + { + "source": "FsxFilesystemArn", + "target": "FsxFilesystemArn" + }, + { + "source": "Password", + "target": "Password" + }, + { + "source": "SecurityGroupArns", + "target": "SecurityGroupArns" + }, + { + "source": "Subdirectory", + "target": "Subdirectory" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "User", + "target": "User" + } + ], + "operation": "CreateLocationFsxWindows", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::LocationHDFS", + "mappings": [ + { + "source": "AgentArns", + "target": "AgentArns" + }, + { + "source": "AuthenticationType", + "target": "AuthenticationType" + }, + { + "source": "BlockSize", + "target": "BlockSize" + }, + { + "source": "KerberosKeytab", + "target": "KerberosKeytab" + }, + { + "source": "KerberosKrb5Conf", + "target": "KerberosKrb5Conf" + }, + { + "source": "KerberosPrincipal", + "target": "KerberosPrincipal" + }, + { + "source": "KmsKeyProviderUri", + "target": "KmsKeyProviderUri" + }, + { + "source": "NameNodes", + "target": "NameNodes" + }, + { + "source": "QopConfiguration", + "target": "QopConfiguration" + }, + { + "source": "ReplicationFactor", + "target": "ReplicationFactor" + }, + { + "source": "SimpleUser", + "target": "SimpleUser" + }, + { + "source": "Subdirectory", + "target": "Subdirectory" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLocationHdfs", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::LocationNFS", + "mappings": [ + { + "source": "MountOptions", + "target": "MountOptions" + }, + { + "source": "OnPremConfig", + "target": "OnPremConfig" + }, + { + "source": "ServerHostname", + "target": "ServerHostname" + }, + { + "source": "Subdirectory", + "target": "Subdirectory" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLocationNfs", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::LocationObjectStorage", + "mappings": [ + { + "source": "AccessKey", + "target": "AccessKey" + }, + { + "source": "AgentArns", + "target": "AgentArns" + }, + { + "source": "BucketName", + "target": "BucketName" + }, + { + "source": "CmkSecretConfig", + "target": "CmkSecretConfig" + }, + { + "source": "CustomSecretConfig", + "target": "CustomSecretConfig" + }, + { + "source": "SecretKey", + "target": "SecretKey" + }, + { + "source": "ServerCertificate", + "target": "ServerCertificate" + }, + { + "source": "ServerHostname", + "target": "ServerHostname" + }, + { + "source": "ServerPort", + "target": "ServerPort" + }, + { + "source": "ServerProtocol", + "target": "ServerProtocol" + }, + { + "source": "Subdirectory", + "target": "Subdirectory" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLocationObjectStorage", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::LocationS3", + "mappings": [ + { + "source": "S3BucketArn", + "target": "S3BucketArn" + }, + { + "source": "S3Config", + "target": "S3Config" + }, + { + "source": "S3StorageClass", + "target": "S3StorageClass" + }, + { + "source": "Subdirectory", + "target": "Subdirectory" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLocationS3", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::LocationSMB", + "mappings": [ + { + "source": "AgentArns", + "target": "AgentArns" + }, + { + "source": "AuthenticationType", + "target": "AuthenticationType" + }, + { + "source": "DnsIpAddresses", + "target": "DnsIpAddresses" + }, + { + "source": "Domain", + "target": "Domain" + }, + { + "source": "KerberosKeytab", + "target": "KerberosKeytab" + }, + { + "source": "KerberosKrb5Conf", + "target": "KerberosKrb5Conf" + }, + { + "source": "KerberosPrincipal", + "target": "KerberosPrincipal" + }, + { + "source": "MountOptions", + "target": "MountOptions" + }, + { + "source": "Password", + "target": "Password" + }, + { + "source": "ServerHostname", + "target": "ServerHostname" + }, + { + "source": "Subdirectory", + "target": "Subdirectory" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "User", + "target": "User" + } + ], + "operation": "CreateLocationSmb", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::Task", + "mappings": [ + { + "source": "CloudWatchLogGroupArn", + "target": "CloudWatchLogGroupArn" + }, + { + "source": "DestinationLocationArn", + "target": "DestinationLocationArn" + }, + { + "source": "Excludes", + "target": "Excludes" + }, + { + "source": "Includes", + "target": "Includes" + }, + { + "source": "ManifestConfig", + "target": "ManifestConfig" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Options", + "target": "Options" + }, + { + "source": "Schedule", + "target": "Schedule" + }, + { + "source": "SourceLocationArn", + "target": "SourceLocationArn" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TaskMode", + "target": "TaskMode" + }, + { + "source": "TaskReportConfig", + "target": "TaskReportConfig" + } + ], + "operation": "CreateTask", + "phase": "create", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataSync::Task", + "mappings": [], + "operation": "DeleteTask", + "phase": "delete", + "service": "datasync" + }, + { + "cfn_type": "AWS::DataZone::Connection", + "mappings": [ + { + "source": "awsLocation", + "target": "AwsLocation" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "environmentIdentifier", + "target": "EnvironmentIdentifier" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "props", + "target": "Props" + } + ], + "operation": "CreateConnection", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::Connection", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + } + ], + "operation": "DeleteConnection", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::DataSource", + "mappings": [ + { + "source": "assetFormsInput", + "target": "AssetFormsInput" + }, + { + "source": "configuration", + "target": "Configuration" + }, + { + "source": "connectionIdentifier", + "target": "ConnectionIdentifier" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "enableSetting", + "target": "EnableSetting" + }, + { + "source": "environmentIdentifier", + "target": "EnvironmentIdentifier" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "projectIdentifier", + "target": "ProjectIdentifier" + }, + { + "source": "publishOnImport", + "target": "PublishOnImport" + }, + { + "source": "recommendation", + "target": "Recommendation" + }, + { + "source": "schedule", + "target": "Schedule" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateDataSource", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::DataSource", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + } + ], + "operation": "DeleteDataSource", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::Domain", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "domainExecutionRole", + "target": "DomainExecutionRole" + }, + { + "source": "domainVersion", + "target": "DomainVersion" + }, + { + "source": "kmsKeyIdentifier", + "target": "KmsKeyIdentifier" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "serviceRole", + "target": "ServiceRole" + }, + { + "source": "singleSignOn", + "target": "SingleSignOn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDomain", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::Domain", + "mappings": [], + "operation": "DeleteDomain", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::DomainUnit", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "parentDomainUnitIdentifier", + "target": "ParentDomainUnitIdentifier" + } + ], + "operation": "CreateDomainUnit", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::DomainUnit", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + } + ], + "operation": "DeleteDomainUnit", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::Environment", + "mappings": [ + { + "source": "deploymentOrder", + "target": "DeploymentOrder" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "environmentAccountIdentifier", + "target": "EnvironmentAccountIdentifier" + }, + { + "source": "environmentAccountRegion", + "target": "EnvironmentAccountRegion" + }, + { + "source": "environmentBlueprintIdentifier", + "target": "EnvironmentBlueprintIdentifier" + }, + { + "source": "environmentConfigurationId", + "target": "EnvironmentConfigurationId" + }, + { + "source": "environmentProfileIdentifier", + "target": "EnvironmentProfileIdentifier" + }, + { + "source": "glossaryTerms", + "target": "GlossaryTerms" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "projectIdentifier", + "target": "ProjectIdentifier" + }, + { + "source": "userParameters", + "target": "UserParameters" + } + ], + "operation": "CreateEnvironment", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::Environment", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + } + ], + "operation": "DeleteEnvironment", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::EnvironmentActions", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "environmentIdentifier", + "target": "EnvironmentIdentifier" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "parameters", + "target": "Parameters" + } + ], + "operation": "CreateEnvironmentAction", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::EnvironmentActions", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "environmentIdentifier", + "target": "EnvironmentIdentifier" + }, + { + "source": "identifier", + "target": "Identifier" + } + ], + "operation": "DeleteEnvironmentAction", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::EnvironmentBlueprintConfiguration", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "enabledRegions", + "target": "EnabledRegions" + }, + { + "source": "environmentBlueprintIdentifier", + "target": "EnvironmentBlueprintIdentifier" + }, + { + "source": "environmentRolePermissionBoundary", + "target": "EnvironmentRolePermissionBoundary" + }, + { + "source": "manageAccessRoleArn", + "target": "ManageAccessRoleArn" + }, + { + "source": "provisioningConfigurations", + "target": "ProvisioningConfigurations" + }, + { + "source": "provisioningRoleArn", + "target": "ProvisioningRoleArn" + }, + { + "source": "regionalParameters", + "target": "RegionalParameters" + } + ], + "operation": "PutEnvironmentBlueprintConfiguration", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::EnvironmentBlueprintConfiguration", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "environmentBlueprintIdentifier", + "target": "EnvironmentBlueprintIdentifier" + } + ], + "operation": "DeleteEnvironmentBlueprintConfiguration", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::EnvironmentProfile", + "mappings": [ + { + "source": "awsAccountId", + "target": "AwsAccountId" + }, + { + "source": "awsAccountRegion", + "target": "AwsAccountRegion" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "environmentBlueprintIdentifier", + "target": "EnvironmentBlueprintIdentifier" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "projectIdentifier", + "target": "ProjectIdentifier" + }, + { + "source": "userParameters", + "target": "UserParameters" + } + ], + "operation": "CreateEnvironmentProfile", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::EnvironmentProfile", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + } + ], + "operation": "DeleteEnvironmentProfile", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::FormType", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "model", + "target": "Model" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "owningProjectIdentifier", + "target": "OwningProjectIdentifier" + }, + { + "source": "status", + "target": "Status" + } + ], + "operation": "CreateFormType", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::FormType", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + } + ], + "operation": "DeleteFormType", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::GroupProfile", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "groupIdentifier", + "target": "GroupIdentifier" + } + ], + "operation": "CreateGroupProfile", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::Owner", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "entityIdentifier", + "target": "EntityIdentifier" + }, + { + "source": "entityType", + "target": "EntityType" + }, + { + "source": "owner", + "target": "Owner" + } + ], + "operation": "AddEntityOwner", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::Owner", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "entityIdentifier", + "target": "EntityIdentifier" + }, + { + "source": "entityType", + "target": "EntityType" + }, + { + "source": "owner", + "target": "Owner" + } + ], + "operation": "RemoveEntityOwner", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::PolicyGrant", + "mappings": [ + { + "source": "detail", + "target": "Detail" + }, + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "entityIdentifier", + "target": "EntityIdentifier" + }, + { + "source": "entityType", + "target": "EntityType" + }, + { + "source": "policyType", + "target": "PolicyType" + }, + { + "source": "principal", + "target": "Principal" + } + ], + "operation": "AddPolicyGrant", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::PolicyGrant", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "entityIdentifier", + "target": "EntityIdentifier" + }, + { + "source": "entityType", + "target": "EntityType" + }, + { + "source": "policyType", + "target": "PolicyType" + }, + { + "source": "principal", + "target": "Principal" + } + ], + "operation": "RemovePolicyGrant", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::Project", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "domainUnitId", + "target": "DomainUnitId" + }, + { + "source": "glossaryTerms", + "target": "GlossaryTerms" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "projectProfileId", + "target": "ProjectProfileId" + }, + { + "source": "userParameters", + "target": "UserParameters" + } + ], + "operation": "CreateProject", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::Project", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + } + ], + "operation": "DeleteProject", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::ProjectMembership", + "mappings": [ + { + "source": "designation", + "target": "Designation" + }, + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "member", + "target": "Member" + }, + { + "source": "projectIdentifier", + "target": "ProjectIdentifier" + } + ], + "operation": "CreateProjectMembership", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::ProjectMembership", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "member", + "target": "Member" + }, + { + "source": "projectIdentifier", + "target": "ProjectIdentifier" + } + ], + "operation": "DeleteProjectMembership", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::ProjectProfile", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "domainUnitIdentifier", + "target": "DomainUnitIdentifier" + }, + { + "source": "environmentConfigurations", + "target": "EnvironmentConfigurations" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "status", + "target": "Status" + } + ], + "operation": "CreateProjectProfile", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::ProjectProfile", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + } + ], + "operation": "DeleteProjectProfile", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::SubscriptionTarget", + "mappings": [ + { + "source": "applicableAssetTypes", + "target": "ApplicableAssetTypes" + }, + { + "source": "authorizedPrincipals", + "target": "AuthorizedPrincipals" + }, + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "environmentIdentifier", + "target": "EnvironmentIdentifier" + }, + { + "source": "manageAccessRole", + "target": "ManageAccessRole" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "provider", + "target": "Provider" + }, + { + "source": "subscriptionTargetConfig", + "target": "SubscriptionTargetConfig" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateSubscriptionTarget", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::SubscriptionTarget", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "environmentIdentifier", + "target": "EnvironmentIdentifier" + } + ], + "operation": "DeleteSubscriptionTarget", + "phase": "delete", + "service": "datazone" + }, + { + "cfn_type": "AWS::DataZone::UserProfile", + "mappings": [ + { + "source": "domainIdentifier", + "target": "DomainIdentifier" + }, + { + "source": "userIdentifier", + "target": "UserIdentifier" + }, + { + "source": "userType", + "target": "UserType" + } + ], + "operation": "CreateUserProfile", + "phase": "create", + "service": "datazone" + }, + { + "cfn_type": "AWS::Deadline::Farm", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateFarm", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::Farm", + "mappings": [], + "operation": "DeleteFarm", + "phase": "delete", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::Fleet", + "mappings": [ + { + "source": "configuration", + "target": "Configuration" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "hostConfiguration", + "target": "HostConfiguration" + }, + { + "source": "maxWorkerCount", + "target": "MaxWorkerCount" + }, + { + "source": "minWorkerCount", + "target": "MinWorkerCount" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateFleet", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::Fleet", + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + } + ], + "operation": "DeleteFleet", + "phase": "delete", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::LicenseEndpoint", + "mappings": [ + { + "source": "securityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "subnetIds", + "target": "SubnetIds" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "vpcId", + "target": "VpcId" + } + ], + "operation": "CreateLicenseEndpoint", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::LicenseEndpoint", + "mappings": [], + "operation": "DeleteLicenseEndpoint", + "phase": "delete", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::Limit", + "mappings": [ + { + "source": "amountRequirementName", + "target": "AmountRequirementName" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "maxCount", + "target": "MaxCount" + } + ], + "operation": "CreateLimit", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::Limit", + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + } + ], + "operation": "DeleteLimit", + "phase": "delete", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::MeteredProduct", + "mappings": [ + { + "source": "licenseEndpointId", + "target": "LicenseEndpointId" + }, + { + "source": "productId", + "target": "ProductId" + } + ], + "operation": "PutMeteredProduct", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::MeteredProduct", + "mappings": [ + { + "source": "licenseEndpointId", + "target": "LicenseEndpointId" + }, + { + "source": "productId", + "target": "ProductId" + } + ], + "operation": "DeleteMeteredProduct", + "phase": "delete", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::Monitor", + "mappings": [ + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "identityCenterInstanceArn", + "target": "IdentityCenterInstanceArn" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "subdomain", + "target": "Subdomain" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateMonitor", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::Monitor", + "mappings": [], + "operation": "DeleteMonitor", + "phase": "delete", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::Queue", + "mappings": [ + { + "source": "allowedStorageProfileIds", + "target": "AllowedStorageProfileIds" + }, + { + "source": "defaultBudgetAction", + "target": "DefaultBudgetAction" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "jobAttachmentSettings", + "target": "JobAttachmentSettings" + }, + { + "source": "jobRunAsUser", + "target": "JobRunAsUser" + }, + { + "source": "requiredFileSystemLocationNames", + "target": "RequiredFileSystemLocationNames" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateQueue", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::Queue", + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + } + ], + "operation": "DeleteQueue", + "phase": "delete", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::QueueEnvironment", + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "priority", + "target": "Priority" + }, + { + "source": "queueId", + "target": "QueueId" + }, + { + "source": "template", + "target": "Template" + }, + { + "source": "templateType", + "target": "TemplateType" + } + ], + "operation": "CreateQueueEnvironment", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::QueueEnvironment", + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "queueId", + "target": "QueueId" + } + ], + "operation": "DeleteQueueEnvironment", + "phase": "delete", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::QueueFleetAssociation", + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "fleetId", + "target": "FleetId" + }, + { + "source": "queueId", + "target": "QueueId" + } + ], + "operation": "CreateQueueFleetAssociation", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::QueueFleetAssociation", + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "fleetId", + "target": "FleetId" + }, + { + "source": "queueId", + "target": "QueueId" + } + ], + "operation": "DeleteQueueFleetAssociation", + "phase": "delete", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::QueueLimitAssociation", + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "limitId", + "target": "LimitId" + }, + { + "source": "queueId", + "target": "QueueId" + } + ], + "operation": "CreateQueueLimitAssociation", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::QueueLimitAssociation", + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "limitId", + "target": "LimitId" + }, + { + "source": "queueId", + "target": "QueueId" + } + ], + "operation": "DeleteQueueLimitAssociation", + "phase": "delete", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::StorageProfile", + "mappings": [ + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "fileSystemLocations", + "target": "FileSystemLocations" + }, + { + "source": "osFamily", + "target": "OsFamily" + } + ], + "operation": "CreateStorageProfile", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::StorageProfile", + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + } + ], + "operation": "DeleteStorageProfile", + "phase": "delete", + "service": "deadline" + }, + { + "cfn_type": "AWS::Detective::Graph", + "mappings": [ + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateGraph", + "phase": "create", + "service": "detective" + }, + { + "cfn_type": "AWS::Detective::Graph", + "mappings": [], + "operation": "DeleteGraph", + "phase": "delete", + "service": "detective" + }, + { + "cfn_type": "AWS::Detective::MemberInvitation", + "mappings": [ + { + "source": "DisableEmailNotification", + "target": "DisableEmailNotification" + }, + { + "source": "GraphArn", + "target": "GraphArn" + }, + { + "source": "Message", + "target": "Message" + } + ], + "operation": "CreateMembers", + "phase": "create", + "service": "detective" + }, + { + "cfn_type": "AWS::DevOpsGuru::NotificationChannel", + "mappings": [ + { + "source": "Config", + "target": "Config" + } + ], + "operation": "AddNotificationChannel", + "phase": "create", + "service": "devops-guru" + }, + { + "cfn_type": "AWS::DevOpsGuru::NotificationChannel", + "mappings": [], + "operation": "RemoveNotificationChannel", + "phase": "delete", + "service": "devops-guru" + }, + { + "cfn_type": "AWS::DeviceFarm::DevicePool", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "maxDevices", + "target": "MaxDevices" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "projectArn", + "target": "ProjectArn" + }, + { + "source": "rules", + "target": "Rules" + } + ], + "operation": "CreateDevicePool", + "phase": "create", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DeviceFarm::DevicePool", + "mappings": [], + "operation": "DeleteDevicePool", + "phase": "delete", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DeviceFarm::InstanceProfile", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "excludeAppPackagesFromCleanup", + "target": "ExcludeAppPackagesFromCleanup" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "packageCleanup", + "target": "PackageCleanup" + }, + { + "source": "rebootAfterUse", + "target": "RebootAfterUse" + } + ], + "operation": "CreateInstanceProfile", + "phase": "create", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DeviceFarm::InstanceProfile", + "mappings": [], + "operation": "DeleteInstanceProfile", + "phase": "delete", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DeviceFarm::NetworkProfile", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "downlinkBandwidthBits", + "target": "DownlinkBandwidthBits" + }, + { + "source": "downlinkDelayMs", + "target": "DownlinkDelayMs" + }, + { + "source": "downlinkJitterMs", + "target": "DownlinkJitterMs" + }, + { + "source": "downlinkLossPercent", + "target": "DownlinkLossPercent" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "projectArn", + "target": "ProjectArn" + }, + { + "source": "uplinkBandwidthBits", + "target": "UplinkBandwidthBits" + }, + { + "source": "uplinkDelayMs", + "target": "UplinkDelayMs" + }, + { + "source": "uplinkJitterMs", + "target": "UplinkJitterMs" + }, + { + "source": "uplinkLossPercent", + "target": "UplinkLossPercent" + } + ], + "operation": "CreateNetworkProfile", + "phase": "create", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DeviceFarm::NetworkProfile", + "mappings": [], + "operation": "DeleteNetworkProfile", + "phase": "delete", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DeviceFarm::Project", + "mappings": [ + { + "source": "defaultJobTimeoutMinutes", + "target": "DefaultJobTimeoutMinutes" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "vpcConfig", + "target": "VpcConfig" + } + ], + "operation": "CreateProject", + "phase": "create", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DeviceFarm::Project", + "mappings": [], + "operation": "DeleteProject", + "phase": "delete", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DeviceFarm::TestGridProject", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "vpcConfig", + "target": "VpcConfig" + } + ], + "operation": "CreateTestGridProject", + "phase": "create", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DeviceFarm::TestGridProject", + "mappings": [], + "operation": "DeleteTestGridProject", + "phase": "delete", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DeviceFarm::VPCEConfiguration", + "mappings": [ + { + "source": "serviceDnsName", + "target": "ServiceDnsName" + }, + { + "source": "vpceConfigurationDescription", + "target": "VpceConfigurationDescription" + }, + { + "source": "vpceConfigurationName", + "target": "VpceConfigurationName" + }, + { + "source": "vpceServiceName", + "target": "VpceServiceName" + } + ], + "operation": "CreateVPCEConfiguration", + "phase": "create", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DeviceFarm::VPCEConfiguration", + "mappings": [], + "operation": "DeleteVPCEConfiguration", + "phase": "delete", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DirectConnect::Connection", + "mappings": [ + { + "source": "bandwidth", + "target": "Bandwidth" + }, + { + "source": "connectionName", + "target": "ConnectionName" + }, + { + "source": "lagId", + "target": "LagId" + }, + { + "source": "location", + "target": "Location" + }, + { + "source": "providerName", + "target": "ProviderName" + }, + { + "source": "requestMACSec", + "target": "RequestMACSec" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateConnection", + "phase": "create", + "service": "directconnect" + }, + { + "cfn_type": "AWS::DirectConnect::Connection", + "mappings": [], + "operation": "DeleteConnection", + "phase": "delete", + "service": "directconnect" + }, + { + "cfn_type": "AWS::DirectConnect::DirectConnectGateway", + "mappings": [ + { + "source": "amazonSideAsn", + "target": "AmazonSideAsn" + }, + { + "source": "directConnectGatewayName", + "target": "DirectConnectGatewayName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDirectConnectGateway", + "phase": "create", + "service": "directconnect" + }, + { + "cfn_type": "AWS::DirectConnect::DirectConnectGateway", + "mappings": [], + "operation": "DeleteDirectConnectGateway", + "phase": "delete", + "service": "directconnect" + }, + { + "cfn_type": "AWS::DirectConnect::DirectConnectGatewayAssociation", + "mappings": [ + { + "source": "directConnectGatewayId", + "target": "DirectConnectGatewayId" + } + ], + "operation": "CreateDirectConnectGatewayAssociation", + "phase": "create", + "service": "directconnect" + }, + { + "cfn_type": "AWS::DirectConnect::DirectConnectGatewayAssociation", + "mappings": [ + { + "source": "directConnectGatewayId", + "target": "DirectConnectGatewayId" + } + ], + "operation": "DeleteDirectConnectGatewayAssociation", + "phase": "delete", + "service": "directconnect" + }, + { + "cfn_type": "AWS::DirectConnect::Lag", + "mappings": [ + { + "source": "connectionsBandwidth", + "target": "ConnectionsBandwidth" + }, + { + "source": "lagName", + "target": "LagName" + }, + { + "source": "location", + "target": "Location" + }, + { + "source": "providerName", + "target": "ProviderName" + }, + { + "source": "requestMACSec", + "target": "RequestMACSec" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateLag", + "phase": "create", + "service": "directconnect" + }, + { + "cfn_type": "AWS::DirectConnect::Lag", + "mappings": [], + "operation": "DeleteLag", + "phase": "delete", + "service": "directconnect" + }, + { + "cfn_type": "AWS::DirectConnect::PrivateVirtualInterface", + "mappings": [ + { + "source": "connectionId", + "target": "ConnectionId" + } + ], + "operation": "CreatePrivateVirtualInterface", + "phase": "create", + "service": "directconnect" + }, + { + "cfn_type": "AWS::DirectConnect::PublicVirtualInterface", + "mappings": [ + { + "source": "connectionId", + "target": "ConnectionId" + } + ], + "operation": "CreatePublicVirtualInterface", + "phase": "create", + "service": "directconnect" + }, + { + "cfn_type": "AWS::DirectConnect::TransitVirtualInterface", + "mappings": [ + { + "source": "connectionId", + "target": "ConnectionId" + } + ], + "operation": "CreateTransitVirtualInterface", + "phase": "create", + "service": "directconnect" + }, + { + "cfn_type": "AWS::DirectoryService::SimpleAD", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Password", + "target": "Password" + }, + { + "source": "ShortName", + "target": "ShortName" + }, + { + "source": "Size", + "target": "Size" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcSettings", + "target": "VpcSettings" + } + ], + "operation": "CreateDirectory", + "phase": "create", + "service": "ds" + }, + { + "cfn_type": "AWS::DocDB::DBClusterParameterGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDBClusterParameterGroup", + "phase": "create", + "service": "docdb" + }, + { + "cfn_type": "AWS::DocDB::DBClusterParameterGroup", + "mappings": [], + "operation": "DeleteDBClusterParameterGroup", + "phase": "delete", + "service": "docdb" + }, + { + "cfn_type": "AWS::DocDB::DBSubnetGroup", + "mappings": [ + { + "source": "DBSubnetGroupDescription", + "target": "DBSubnetGroupDescription" + }, + { + "source": "DBSubnetGroupName", + "target": "DBSubnetGroupName" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDBSubnetGroup", + "phase": "create", + "service": "docdb" + }, + { + "cfn_type": "AWS::DocDB::DBSubnetGroup", + "mappings": [ + { + "source": "DBSubnetGroupName", + "target": "DBSubnetGroupName" + } + ], + "operation": "DeleteDBSubnetGroup", + "phase": "delete", + "service": "docdb" + }, + { + "cfn_type": "AWS::DocDB::EventSubscription", + "mappings": [ + { + "source": "Enabled", + "target": "Enabled" + }, + { + "source": "EventCategories", + "target": "EventCategories" + }, + { + "source": "SnsTopicArn", + "target": "SnsTopicArn" + }, + { + "source": "SourceIds", + "target": "SourceIds" + }, + { + "source": "SourceType", + "target": "SourceType" + }, + { + "source": "SubscriptionName", + "target": "SubscriptionName" + } + ], + "operation": "CreateEventSubscription", + "phase": "create", + "service": "docdb" + }, + { + "cfn_type": "AWS::DocDB::EventSubscription", + "mappings": [ + { + "source": "SubscriptionName", + "target": "SubscriptionName" + } + ], + "operation": "DeleteEventSubscription", + "phase": "delete", + "service": "docdb" + }, + { + "cfn_type": "AWS::DocDB::GlobalCluster", + "mappings": [ + { + "source": "DeletionProtection", + "target": "DeletionProtection" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "GlobalClusterIdentifier", + "target": "GlobalClusterIdentifier" + }, + { + "source": "SourceDBClusterIdentifier", + "target": "SourceDBClusterIdentifier" + }, + { + "source": "StorageEncrypted", + "target": "StorageEncrypted" + } + ], + "operation": "CreateGlobalCluster", + "phase": "create", + "service": "docdb" + }, + { + "cfn_type": "AWS::DocDB::GlobalCluster", + "mappings": [ + { + "source": "GlobalClusterIdentifier", + "target": "GlobalClusterIdentifier" + } + ], + "operation": "DeleteGlobalCluster", + "phase": "delete", + "service": "docdb" + }, + { + "cfn_type": "AWS::DocDBElastic::Cluster", + "mappings": [ + { + "source": "adminUserName", + "target": "AdminUserName" + }, + { + "source": "adminUserPassword", + "target": "AdminUserPassword" + }, + { + "source": "authType", + "target": "AuthType" + }, + { + "source": "backupRetentionPeriod", + "target": "BackupRetentionPeriod" + }, + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "preferredBackupWindow", + "target": "PreferredBackupWindow" + }, + { + "source": "preferredMaintenanceWindow", + "target": "PreferredMaintenanceWindow" + }, + { + "source": "shardCapacity", + "target": "ShardCapacity" + }, + { + "source": "shardCount", + "target": "ShardCount" + }, + { + "source": "shardInstanceCount", + "target": "ShardInstanceCount" + }, + { + "source": "subnetIds", + "target": "SubnetIds" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "vpcSecurityGroupIds", + "target": "VpcSecurityGroupIds" + } + ], + "operation": "CreateCluster", + "phase": "create", + "service": "docdb-elastic" + }, + { + "cfn_type": "AWS::DocDBElastic::Cluster", + "mappings": [], + "operation": "DeleteCluster", + "phase": "delete", + "service": "docdb-elastic" + }, + { + "cfn_type": "AWS::DynamoDB::Table", + "mappings": [ + { + "source": "AttributeDefinitions", + "target": "AttributeDefinitions" + }, + { + "source": "BillingMode", + "target": "BillingMode" + }, + { + "source": "DeletionProtectionEnabled", + "target": "DeletionProtectionEnabled" + }, + { + "source": "GlobalSecondaryIndexes", + "target": "GlobalSecondaryIndexes" + }, + { + "source": "KeySchema", + "target": "KeySchema" + }, + { + "source": "LocalSecondaryIndexes", + "target": "LocalSecondaryIndexes" + }, + { + "source": "OnDemandThroughput", + "target": "OnDemandThroughput" + }, + { + "source": "ProvisionedThroughput", + "target": "ProvisionedThroughput" + }, + { + "source": "ResourcePolicy", + "target": "ResourcePolicy" + }, + { + "source": "SSESpecification", + "target": "SSESpecification" + }, + { + "source": "StreamSpecification", + "target": "StreamSpecification" + }, + { + "source": "TableClass", + "target": "TableClass" + }, + { + "source": "TableName", + "target": "TableName" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "WarmThroughput", + "target": "WarmThroughput" + } + ], + "operation": "CreateTable", + "phase": "create", + "service": "dynamodb" + }, + { + "cfn_type": "AWS::DynamoDB::Table", + "mappings": [ + { + "source": "TableName", + "target": "TableName" + } + ], + "operation": "DeleteTable", + "phase": "delete", + "service": "dynamodb" + }, + { + "cfn_type": "AWS::EC2::CapacityReservation", + "mappings": [ + { + "source": "AvailabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "AvailabilityZoneId", + "target": "AvailabilityZoneId" + }, + { + "source": "EbsOptimized", + "target": "EbsOptimized" + }, + { + "source": "EndDate", + "target": "EndDate" + }, + { + "source": "EndDateType", + "target": "EndDateType" + }, + { + "source": "EphemeralStorage", + "target": "EphemeralStorage" + }, + { + "source": "InstanceCount", + "target": "InstanceCount" + }, + { + "source": "InstanceMatchCriteria", + "target": "InstanceMatchCriteria" + }, + { + "source": "InstancePlatform", + "target": "InstancePlatform" + }, + { + "source": "InstanceType", + "target": "InstanceType" + }, + { + "source": "OutpostArn", + "target": "OutPostArn" + }, + { + "source": "PlacementGroupArn", + "target": "PlacementGroupArn" + }, + { + "source": "TagSpecifications", + "target": "TagSpecifications" + }, + { + "source": "Tenancy", + "target": "Tenancy" + } + ], + "operation": "CreateCapacityReservation", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::CapacityReservation", + "mappings": [], + "operation": "CancelCapacityReservation", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::CapacityReservationFleet", + "mappings": [ + { + "source": "AllocationStrategy", + "target": "AllocationStrategy" + }, + { + "source": "EndDate", + "target": "EndDate" + }, + { + "source": "InstanceMatchCriteria", + "target": "InstanceMatchCriteria" + }, + { + "source": "InstanceTypeSpecifications", + "target": "InstanceTypeSpecifications" + }, + { + "source": "TagSpecifications", + "target": "TagSpecifications" + }, + { + "source": "Tenancy", + "target": "Tenancy" + }, + { + "source": "TotalTargetCapacity", + "target": "TotalTargetCapacity" + } + ], + "operation": "CreateCapacityReservationFleet", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::CapacityReservationFleet", + "mappings": [], + "operation": "CancelCapacityReservationFleets", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::CarrierGateway", + "mappings": [ + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateCarrierGateway", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::CarrierGateway", + "mappings": [], + "operation": "DeleteCarrierGateway", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::CustomerGateway", + "mappings": [ + { + "source": "BgpAsn", + "target": "BgpAsn" + }, + { + "source": "BgpAsnExtended", + "target": "BgpAsnExtended" + }, + { + "source": "CertificateArn", + "target": "CertificateArn" + }, + { + "source": "DeviceName", + "target": "DeviceName" + }, + { + "source": "IpAddress", + "target": "IpAddress" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateCustomerGateway", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::CustomerGateway", + "mappings": [], + "operation": "DeleteCustomerGateway", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::DHCPOptions", + "mappings": [], + "operation": "DeleteDhcpOptions", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::EC2Fleet", + "mappings": [ + { + "source": "Context", + "target": "Context" + }, + { + "source": "ExcessCapacityTerminationPolicy", + "target": "ExcessCapacityTerminationPolicy" + }, + { + "source": "LaunchTemplateConfigs", + "target": "LaunchTemplateConfigs" + }, + { + "source": "OnDemandOptions", + "target": "OnDemandOptions" + }, + { + "source": "ReplaceUnhealthyInstances", + "target": "ReplaceUnhealthyInstances" + }, + { + "source": "SpotOptions", + "target": "SpotOptions" + }, + { + "source": "TagSpecifications", + "target": "TagSpecifications" + }, + { + "source": "TargetCapacitySpecification", + "target": "TargetCapacitySpecification" + }, + { + "source": "TerminateInstancesWithExpiration", + "target": "TerminateInstancesWithExpiration" + }, + { + "source": "Type", + "target": "Type" + }, + { + "source": "ValidFrom", + "target": "ValidFrom" + }, + { + "source": "ValidUntil", + "target": "ValidUntil" + } + ], + "operation": "CreateFleet", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::EIP", + "mappings": [ + { + "source": "Address", + "target": "Address" + }, + { + "source": "Domain", + "target": "Domain" + }, + { + "source": "IpamPoolId", + "target": "IpamPoolId" + }, + { + "source": "NetworkBorderGroup", + "target": "NetworkBorderGroup" + }, + { + "source": "PublicIpv4Pool", + "target": "PublicIpv4Pool" + } + ], + "operation": "AllocateAddress", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::EIPAssociation", + "mappings": [ + { + "source": "AllocationId", + "target": "AllocationId" + }, + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "NetworkInterfaceId", + "target": "NetworkInterfaceId" + }, + { + "source": "PrivateIpAddress", + "target": "PrivateIpAddress" + } + ], + "operation": "AssociateAddress", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::EgressOnlyInternetGateway", + "mappings": [ + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateEgressOnlyInternetGateway", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::EgressOnlyInternetGateway", + "mappings": [], + "operation": "DeleteEgressOnlyInternetGateway", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::EnclaveCertificateIamRoleAssociation", + "mappings": [ + { + "source": "CertificateArn", + "target": "CertificateArn" + }, + { + "source": "RoleArn", + "target": "RoleArn" + } + ], + "operation": "AssociateEnclaveCertificateIamRole", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::EnclaveCertificateIamRoleAssociation", + "mappings": [ + { + "source": "CertificateArn", + "target": "CertificateArn" + }, + { + "source": "RoleArn", + "target": "RoleArn" + } + ], + "operation": "DisassociateEnclaveCertificateIamRole", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::FlowLog", + "mappings": [ + { + "source": "DeliverCrossAccountRole", + "target": "DeliverCrossAccountRole" + }, + { + "source": "DeliverLogsPermissionArn", + "target": "DeliverLogsPermissionArn" + }, + { + "source": "DestinationOptions", + "target": "DestinationOptions" + }, + { + "source": "LogDestination", + "target": "LogDestination" + }, + { + "source": "LogDestinationType", + "target": "LogDestinationType" + }, + { + "source": "LogFormat", + "target": "LogFormat" + }, + { + "source": "LogGroupName", + "target": "LogGroupName" + }, + { + "source": "MaxAggregationInterval", + "target": "MaxAggregationInterval" + }, + { + "source": "ResourceType", + "target": "ResourceType" + }, + { + "source": "TrafficType", + "target": "TrafficType" + } + ], + "operation": "CreateFlowLogs", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::FlowLog", + "mappings": [], + "operation": "DeleteFlowLogs", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::Host", + "mappings": [ + { + "source": "AutoPlacement", + "target": "AutoPlacement" + }, + { + "source": "AvailabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "HostMaintenance", + "target": "HostMaintenance" + }, + { + "source": "HostRecovery", + "target": "HostRecovery" + }, + { + "source": "InstanceFamily", + "target": "InstanceFamily" + }, + { + "source": "InstanceType", + "target": "InstanceType" + }, + { + "source": "OutpostArn", + "target": "OutpostArn" + } + ], + "operation": "AllocateHosts", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::Host", + "mappings": [], + "operation": "ReleaseHosts", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAM", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "EnablePrivateGua", + "target": "EnablePrivateGua" + }, + { + "source": "MeteredAccount", + "target": "MeteredAccount" + }, + { + "source": "OperatingRegions", + "target": "OperatingRegions" + }, + { + "source": "Tier", + "target": "Tier" + } + ], + "operation": "CreateIpam", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAM", + "mappings": [], + "operation": "DeleteIpam", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMAllocation", + "mappings": [ + { + "source": "Cidr", + "target": "Cidr" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "IpamPoolId", + "target": "IpamPoolId" + }, + { + "source": "NetmaskLength", + "target": "NetmaskLength" + } + ], + "operation": "AllocateIpamPoolCidr", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMAllocation", + "mappings": [ + { + "source": "Cidr", + "target": "Cidr" + }, + { + "source": "IpamPoolId", + "target": "IpamPoolId" + } + ], + "operation": "ReleaseIpamPoolAllocation", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMPool", + "mappings": [ + { + "source": "AddressFamily", + "target": "AddressFamily" + }, + { + "source": "AllocationDefaultNetmaskLength", + "target": "AllocationDefaultNetmaskLength" + }, + { + "source": "AllocationMaxNetmaskLength", + "target": "AllocationMaxNetmaskLength" + }, + { + "source": "AllocationMinNetmaskLength", + "target": "AllocationMinNetmaskLength" + }, + { + "source": "AllocationResourceTags", + "target": "AllocationResourceTags" + }, + { + "source": "AutoImport", + "target": "AutoImport" + }, + { + "source": "AwsService", + "target": "AwsService" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "IpamScopeId", + "target": "IpamScopeId" + }, + { + "source": "Locale", + "target": "Locale" + }, + { + "source": "PublicIpSource", + "target": "PublicIpSource" + }, + { + "source": "PubliclyAdvertisable", + "target": "PubliclyAdvertisable" + }, + { + "source": "SourceIpamPoolId", + "target": "SourceIpamPoolId" + }, + { + "source": "SourceResource", + "target": "SourceResource" + } + ], + "operation": "CreateIpamPool", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMPool", + "mappings": [], + "operation": "DeleteIpamPool", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMPoolCidr", + "mappings": [ + { + "source": "Cidr", + "target": "Cidr" + }, + { + "source": "IpamPoolId", + "target": "IpamPoolId" + }, + { + "source": "NetmaskLength", + "target": "NetmaskLength" + } + ], + "operation": "ProvisionIpamPoolCidr", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMPoolCidr", + "mappings": [ + { + "source": "Cidr", + "target": "Cidr" + }, + { + "source": "IpamPoolId", + "target": "IpamPoolId" + } + ], + "operation": "DeprovisionIpamPoolCidr", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMResourceDiscovery", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "OperatingRegions", + "target": "OperatingRegions" + } + ], + "operation": "CreateIpamResourceDiscovery", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMResourceDiscovery", + "mappings": [], + "operation": "DeleteIpamResourceDiscovery", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMResourceDiscoveryAssociation", + "mappings": [ + { + "source": "IpamId", + "target": "IpamId" + }, + { + "source": "IpamResourceDiscoveryId", + "target": "IpamResourceDiscoveryId" + } + ], + "operation": "AssociateIpamResourceDiscovery", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMScope", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "IpamId", + "target": "IpamId" + } + ], + "operation": "CreateIpamScope", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMScope", + "mappings": [], + "operation": "DeleteIpamScope", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::Instance", + "mappings": [ + { + "source": "AdditionalInfo", + "target": "AdditionalInfo" + }, + { + "source": "BlockDeviceMappings", + "target": "BlockDeviceMappings" + }, + { + "source": "CpuOptions", + "target": "CpuOptions" + }, + { + "source": "CreditSpecification", + "target": "CreditSpecification" + }, + { + "source": "DisableApiTermination", + "target": "DisableApiTermination" + }, + { + "source": "EbsOptimized", + "target": "EbsOptimized" + }, + { + "source": "ElasticInferenceAccelerators", + "target": "ElasticInferenceAccelerators" + }, + { + "source": "EnclaveOptions", + "target": "EnclaveOptions" + }, + { + "source": "HibernationOptions", + "target": "HibernationOptions" + }, + { + "source": "IamInstanceProfile", + "target": "IamInstanceProfile" + }, + { + "source": "ImageId", + "target": "ImageId" + }, + { + "source": "InstanceInitiatedShutdownBehavior", + "target": "InstanceInitiatedShutdownBehavior" + }, + { + "source": "InstanceType", + "target": "InstanceType" + }, + { + "source": "Ipv6AddressCount", + "target": "Ipv6AddressCount" + }, + { + "source": "Ipv6Addresses", + "target": "Ipv6Addresses" + }, + { + "source": "KernelId", + "target": "KernelId" + }, + { + "source": "KeyName", + "target": "KeyName" + }, + { + "source": "LaunchTemplate", + "target": "LaunchTemplate" + }, + { + "source": "LicenseSpecifications", + "target": "LicenseSpecifications" + }, + { + "source": "MetadataOptions", + "target": "MetadataOptions" + }, + { + "source": "Monitoring", + "target": "Monitoring" + }, + { + "source": "NetworkInterfaces", + "target": "NetworkInterfaces" + }, + { + "source": "PrivateDnsNameOptions", + "target": "PrivateDnsNameOptions" + }, + { + "source": "PrivateIpAddress", + "target": "PrivateIpAddress" + }, + { + "source": "RamdiskId", + "target": "RamdiskId" + }, + { + "source": "SecurityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "SecurityGroups", + "target": "SecurityGroups" + }, + { + "source": "SubnetId", + "target": "SubnetId" + }, + { + "source": "UserData", + "target": "UserData" + } + ], + "operation": "RunInstances", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::Instance", + "mappings": [], + "operation": "TerminateInstances", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::InstanceConnectEndpoint", + "mappings": [ + { + "source": "ClientToken", + "target": "ClientToken" + }, + { + "source": "PreserveClientIp", + "target": "PreserveClientIp" + }, + { + "source": "SecurityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "SubnetId", + "target": "SubnetId" + } + ], + "operation": "CreateInstanceConnectEndpoint", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::InstanceConnectEndpoint", + "mappings": [], + "operation": "DeleteInstanceConnectEndpoint", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::InternetGateway", + "mappings": [], + "operation": "DeleteInternetGateway", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::KeyPair", + "mappings": [ + { + "source": "KeyFormat", + "target": "KeyFormat" + }, + { + "source": "KeyName", + "target": "KeyName" + }, + { + "source": "KeyType", + "target": "KeyType" + } + ], + "operation": "CreateKeyPair", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::KeyPair", + "mappings": [ + { + "source": "KeyName", + "target": "KeyName" + } + ], + "operation": "DeleteKeyPair", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LaunchTemplate", + "mappings": [ + { + "source": "LaunchTemplateData", + "target": "LaunchTemplateData" + }, + { + "source": "LaunchTemplateName", + "target": "LaunchTemplateName" + }, + { + "source": "TagSpecifications", + "target": "TagSpecifications" + }, + { + "source": "VersionDescription", + "target": "VersionDescription" + } + ], + "operation": "CreateLaunchTemplate", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LaunchTemplate", + "mappings": [ + { + "source": "LaunchTemplateName", + "target": "LaunchTemplateName" + } + ], + "operation": "DeleteLaunchTemplate", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LocalGatewayRoute", + "mappings": [ + { + "source": "DestinationCidrBlock", + "target": "DestinationCidrBlock" + }, + { + "source": "LocalGatewayRouteTableId", + "target": "LocalGatewayRouteTableId" + }, + { + "source": "LocalGatewayVirtualInterfaceGroupId", + "target": "LocalGatewayVirtualInterfaceGroupId" + }, + { + "source": "NetworkInterfaceId", + "target": "NetworkInterfaceId" + } + ], + "operation": "CreateLocalGatewayRoute", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LocalGatewayRoute", + "mappings": [ + { + "source": "DestinationCidrBlock", + "target": "DestinationCidrBlock" + }, + { + "source": "LocalGatewayRouteTableId", + "target": "LocalGatewayRouteTableId" + } + ], + "operation": "DeleteLocalGatewayRoute", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LocalGatewayRouteTable", + "mappings": [ + { + "source": "LocalGatewayId", + "target": "LocalGatewayId" + }, + { + "source": "Mode", + "target": "Mode" + } + ], + "operation": "CreateLocalGatewayRouteTable", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LocalGatewayRouteTable", + "mappings": [], + "operation": "DeleteLocalGatewayRouteTable", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LocalGatewayRouteTableVPCAssociation", + "mappings": [ + { + "source": "LocalGatewayRouteTableId", + "target": "LocalGatewayRouteTableId" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateLocalGatewayRouteTableVpcAssociation", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LocalGatewayRouteTableVPCAssociation", + "mappings": [], + "operation": "DeleteLocalGatewayRouteTableVpcAssociation", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LocalGatewayRouteTableVirtualInterfaceGroupAssociation", + "mappings": [ + { + "source": "LocalGatewayRouteTableId", + "target": "LocalGatewayRouteTableId" + }, + { + "source": "LocalGatewayVirtualInterfaceGroupId", + "target": "LocalGatewayVirtualInterfaceGroupId" + } + ], + "operation": "CreateLocalGatewayRouteTableVirtualInterfaceGroupAssociation", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LocalGatewayRouteTableVirtualInterfaceGroupAssociation", + "mappings": [], + "operation": "DeleteLocalGatewayRouteTableVirtualInterfaceGroupAssociation", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LocalGatewayVirtualInterface", + "mappings": [ + { + "source": "LocalAddress", + "target": "LocalAddress" + }, + { + "source": "LocalGatewayVirtualInterfaceGroupId", + "target": "LocalGatewayVirtualInterfaceGroupId" + }, + { + "source": "OutpostLagId", + "target": "OutpostLagId" + }, + { + "source": "PeerAddress", + "target": "PeerAddress" + }, + { + "source": "PeerBgpAsn", + "target": "PeerBgpAsn" + }, + { + "source": "PeerBgpAsnExtended", + "target": "PeerBgpAsnExtended" + }, + { + "source": "Vlan", + "target": "Vlan" + } + ], + "operation": "CreateLocalGatewayVirtualInterface", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LocalGatewayVirtualInterface", + "mappings": [], + "operation": "DeleteLocalGatewayVirtualInterface", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LocalGatewayVirtualInterfaceGroup", + "mappings": [ + { + "source": "LocalBgpAsn", + "target": "LocalBgpAsn" + }, + { + "source": "LocalBgpAsnExtended", + "target": "LocalBgpAsnExtended" + }, + { + "source": "LocalGatewayId", + "target": "LocalGatewayId" + } + ], + "operation": "CreateLocalGatewayVirtualInterfaceGroup", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::LocalGatewayVirtualInterfaceGroup", + "mappings": [], + "operation": "DeleteLocalGatewayVirtualInterfaceGroup", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NatGateway", + "mappings": [ + { + "source": "AllocationId", + "target": "AllocationId" + }, + { + "source": "ConnectivityType", + "target": "ConnectivityType" + }, + { + "source": "PrivateIpAddress", + "target": "PrivateIpAddress" + }, + { + "source": "SecondaryAllocationIds", + "target": "SecondaryAllocationIds" + }, + { + "source": "SecondaryPrivateIpAddressCount", + "target": "SecondaryPrivateIpAddressCount" + }, + { + "source": "SecondaryPrivateIpAddresses", + "target": "SecondaryPrivateIpAddresses" + }, + { + "source": "SubnetId", + "target": "SubnetId" + } + ], + "operation": "CreateNatGateway", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NatGateway", + "mappings": [], + "operation": "DeleteNatGateway", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkAcl", + "mappings": [ + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateNetworkAcl", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkAcl", + "mappings": [], + "operation": "DeleteNetworkAcl", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkAclEntry", + "mappings": [ + { + "source": "CidrBlock", + "target": "CidrBlock" + }, + { + "source": "Egress", + "target": "Egress" + }, + { + "source": "Ipv6CidrBlock", + "target": "Ipv6CidrBlock" + }, + { + "source": "NetworkAclId", + "target": "NetworkAclId" + }, + { + "source": "PortRange", + "target": "PortRange" + }, + { + "source": "Protocol", + "target": "Protocol" + }, + { + "source": "RuleAction", + "target": "RuleAction" + }, + { + "source": "RuleNumber", + "target": "RuleNumber" + } + ], + "operation": "CreateNetworkAclEntry", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkAclEntry", + "mappings": [ + { + "source": "Egress", + "target": "Egress" + }, + { + "source": "NetworkAclId", + "target": "NetworkAclId" + }, + { + "source": "RuleNumber", + "target": "RuleNumber" + } + ], + "operation": "DeleteNetworkAclEntry", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkInsightsAccessScope", + "mappings": [ + { + "source": "ExcludePaths", + "target": "ExcludePaths" + }, + { + "source": "MatchPaths", + "target": "MatchPaths" + } + ], + "operation": "CreateNetworkInsightsAccessScope", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkInsightsAccessScope", + "mappings": [], + "operation": "DeleteNetworkInsightsAccessScope", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkInsightsAccessScopeAnalysis", + "mappings": [ + { + "source": "NetworkInsightsAccessScopeId", + "target": "NetworkInsightsAccessScopeId" + } + ], + "operation": "StartNetworkInsightsAccessScopeAnalysis", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkInsightsAccessScopeAnalysis", + "mappings": [], + "operation": "DeleteNetworkInsightsAccessScopeAnalysis", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkInsightsAnalysis", + "mappings": [ + { + "source": "AdditionalAccounts", + "target": "AdditionalAccounts" + }, + { + "source": "FilterInArns", + "target": "FilterInArns" + }, + { + "source": "FilterOutArns", + "target": "FilterOutArns" + }, + { + "source": "NetworkInsightsPathId", + "target": "NetworkInsightsPathId" + } + ], + "operation": "StartNetworkInsightsAnalysis", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkInsightsAnalysis", + "mappings": [], + "operation": "DeleteNetworkInsightsAnalysis", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkInsightsPath", + "mappings": [ + { + "source": "Destination", + "target": "Destination" + }, + { + "source": "DestinationIp", + "target": "DestinationIp" + }, + { + "source": "DestinationPort", + "target": "DestinationPort" + }, + { + "source": "FilterAtDestination", + "target": "FilterAtDestination" + }, + { + "source": "FilterAtSource", + "target": "FilterAtSource" + }, + { + "source": "Protocol", + "target": "Protocol" + }, + { + "source": "Source", + "target": "Source" + }, + { + "source": "SourceIp", + "target": "SourceIp" + } + ], + "operation": "CreateNetworkInsightsPath", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkInsightsPath", + "mappings": [], + "operation": "DeleteNetworkInsightsPath", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkInterface", + "mappings": [ + { + "source": "ConnectionTrackingSpecification", + "target": "ConnectionTrackingSpecification" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EnablePrimaryIpv6", + "target": "EnablePrimaryIpv6" + }, + { + "source": "InterfaceType", + "target": "InterfaceType" + }, + { + "source": "Ipv4PrefixCount", + "target": "Ipv4PrefixCount" + }, + { + "source": "Ipv4Prefixes", + "target": "Ipv4Prefixes" + }, + { + "source": "Ipv6AddressCount", + "target": "Ipv6AddressCount" + }, + { + "source": "Ipv6Addresses", + "target": "Ipv6Addresses" + }, + { + "source": "Ipv6PrefixCount", + "target": "Ipv6PrefixCount" + }, + { + "source": "Ipv6Prefixes", + "target": "Ipv6Prefixes" + }, + { + "source": "PrivateIpAddress", + "target": "PrivateIpAddress" + }, + { + "source": "PrivateIpAddresses", + "target": "PrivateIpAddresses" + }, + { + "source": "SecondaryPrivateIpAddressCount", + "target": "SecondaryPrivateIpAddressCount" + }, + { + "source": "SubnetId", + "target": "SubnetId" + } + ], + "operation": "CreateNetworkInterface", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkInterface", + "mappings": [], + "operation": "DeleteNetworkInterface", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkInterfaceAttachment", + "mappings": [ + { + "source": "DeviceIndex", + "target": "DeviceIndex" + }, + { + "source": "EnaQueueCount", + "target": "EnaQueueCount" + }, + { + "source": "EnaSrdSpecification", + "target": "EnaSrdSpecification" + }, + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "NetworkInterfaceId", + "target": "NetworkInterfaceId" + } + ], + "operation": "AttachNetworkInterface", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::NetworkPerformanceMetricSubscription", + "mappings": [ + { + "source": "Destination", + "target": "Destination" + }, + { + "source": "Metric", + "target": "Metric" + }, + { + "source": "Source", + "target": "Source" + }, + { + "source": "Statistic", + "target": "Statistic" + } + ], + "operation": "EnableAwsNetworkPerformanceMetricSubscription", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::PlacementGroup", + "mappings": [ + { + "source": "PartitionCount", + "target": "PartitionCount" + }, + { + "source": "SpreadLevel", + "target": "SpreadLevel" + }, + { + "source": "Strategy", + "target": "Strategy" + } + ], + "operation": "CreatePlacementGroup", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::PlacementGroup", + "mappings": [], + "operation": "DeletePlacementGroup", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::PrefixList", + "mappings": [ + { + "source": "AddressFamily", + "target": "AddressFamily" + }, + { + "source": "Entries", + "target": "Entries" + }, + { + "source": "MaxEntries", + "target": "MaxEntries" + }, + { + "source": "PrefixListName", + "target": "PrefixListName" + } + ], + "operation": "CreateManagedPrefixList", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::PrefixList", + "mappings": [], + "operation": "DeleteManagedPrefixList", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::Route", + "mappings": [ + { + "source": "CarrierGatewayId", + "target": "CarrierGatewayId" + }, + { + "source": "CoreNetworkArn", + "target": "CoreNetworkArn" + }, + { + "source": "DestinationCidrBlock", + "target": "DestinationCidrBlock" + }, + { + "source": "DestinationIpv6CidrBlock", + "target": "DestinationIpv6CidrBlock" + }, + { + "source": "DestinationPrefixListId", + "target": "DestinationPrefixListId" + }, + { + "source": "EgressOnlyInternetGatewayId", + "target": "EgressOnlyInternetGatewayId" + }, + { + "source": "GatewayId", + "target": "GatewayId" + }, + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "LocalGatewayId", + "target": "LocalGatewayId" + }, + { + "source": "NatGatewayId", + "target": "NatGatewayId" + }, + { + "source": "NetworkInterfaceId", + "target": "NetworkInterfaceId" + }, + { + "source": "OdbNetworkArn", + "target": "OdbNetworkArn" + }, + { + "source": "RouteTableId", + "target": "RouteTableId" + }, + { + "source": "TransitGatewayId", + "target": "TransitGatewayId" + }, + { + "source": "VpcEndpointId", + "target": "VpcEndpointId" + }, + { + "source": "VpcPeeringConnectionId", + "target": "VpcPeeringConnectionId" + } + ], + "operation": "CreateRoute", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::Route", + "mappings": [ + { + "source": "DestinationCidrBlock", + "target": "DestinationCidrBlock" + }, + { + "source": "DestinationIpv6CidrBlock", + "target": "DestinationIpv6CidrBlock" + }, + { + "source": "DestinationPrefixListId", + "target": "DestinationPrefixListId" + }, + { + "source": "RouteTableId", + "target": "RouteTableId" + } + ], + "operation": "DeleteRoute", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::RouteServer", + "mappings": [ + { + "source": "AmazonSideAsn", + "target": "AmazonSideAsn" + }, + { + "source": "PersistRoutes", + "target": "PersistRoutes" + }, + { + "source": "PersistRoutesDuration", + "target": "PersistRoutesDuration" + }, + { + "source": "SnsNotificationsEnabled", + "target": "SnsNotificationsEnabled" + } + ], + "operation": "CreateRouteServer", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::RouteServer", + "mappings": [], + "operation": "DeleteRouteServer", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::RouteServerAssociation", + "mappings": [ + { + "source": "RouteServerId", + "target": "RouteServerId" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "AssociateRouteServer", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::RouteServerAssociation", + "mappings": [ + { + "source": "RouteServerId", + "target": "RouteServerId" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "DisassociateRouteServer", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::RouteServerEndpoint", + "mappings": [ + { + "source": "RouteServerId", + "target": "RouteServerId" + }, + { + "source": "SubnetId", + "target": "SubnetId" + } + ], + "operation": "CreateRouteServerEndpoint", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::RouteServerEndpoint", + "mappings": [], + "operation": "DeleteRouteServerEndpoint", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::RouteServerPeer", + "mappings": [ + { + "source": "BgpOptions", + "target": "BgpOptions" + }, + { + "source": "PeerAddress", + "target": "PeerAddress" + }, + { + "source": "RouteServerEndpointId", + "target": "RouteServerEndpointId" + } + ], + "operation": "CreateRouteServerPeer", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::RouteServerPeer", + "mappings": [], + "operation": "DeleteRouteServerPeer", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::RouteServerPropagation", + "mappings": [ + { + "source": "RouteServerId", + "target": "RouteServerId" + }, + { + "source": "RouteTableId", + "target": "RouteTableId" + } + ], + "operation": "EnableRouteServerPropagation", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::RouteTable", + "mappings": [ + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateRouteTable", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::RouteTable", + "mappings": [], + "operation": "DeleteRouteTable", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::SecurityGroup", + "mappings": [ + { + "source": "GroupName", + "target": "GroupName" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateSecurityGroup", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::SecurityGroup", + "mappings": [ + { + "source": "GroupName", + "target": "GroupName" + } + ], + "operation": "DeleteSecurityGroup", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::SecurityGroupEgress", + "mappings": [ + { + "source": "CidrIp", + "target": "CidrIp" + }, + { + "source": "FromPort", + "target": "FromPort" + }, + { + "source": "GroupId", + "target": "GroupId" + }, + { + "source": "IpProtocol", + "target": "IpProtocol" + }, + { + "source": "ToPort", + "target": "ToPort" + } + ], + "operation": "RevokeSecurityGroupEgress", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::SecurityGroupIngress", + "mappings": [ + { + "source": "CidrIp", + "target": "CidrIp" + }, + { + "source": "FromPort", + "target": "FromPort" + }, + { + "source": "GroupId", + "target": "GroupId" + }, + { + "source": "GroupName", + "target": "GroupName" + }, + { + "source": "IpProtocol", + "target": "IpProtocol" + }, + { + "source": "SourceSecurityGroupName", + "target": "SourceSecurityGroupName" + }, + { + "source": "SourceSecurityGroupOwnerId", + "target": "SourceSecurityGroupOwnerId" + }, + { + "source": "ToPort", + "target": "ToPort" + } + ], + "operation": "RevokeSecurityGroupIngress", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::SecurityGroupVpcAssociation", + "mappings": [ + { + "source": "GroupId", + "target": "GroupId" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "AssociateSecurityGroupVpc", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::SecurityGroupVpcAssociation", + "mappings": [ + { + "source": "GroupId", + "target": "GroupId" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "DisassociateSecurityGroupVpc", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::SnapshotBlockPublicAccess", + "mappings": [ + { + "source": "State", + "target": "State" + } + ], + "operation": "EnableSnapshotBlockPublicAccess", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::Subnet", + "mappings": [ + { + "source": "AvailabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "AvailabilityZoneId", + "target": "AvailabilityZoneId" + }, + { + "source": "CidrBlock", + "target": "CidrBlock" + }, + { + "source": "Ipv4IpamPoolId", + "target": "Ipv4IpamPoolId" + }, + { + "source": "Ipv4NetmaskLength", + "target": "Ipv4NetmaskLength" + }, + { + "source": "Ipv6CidrBlock", + "target": "Ipv6CidrBlock" + }, + { + "source": "Ipv6IpamPoolId", + "target": "Ipv6IpamPoolId" + }, + { + "source": "Ipv6Native", + "target": "Ipv6Native" + }, + { + "source": "Ipv6NetmaskLength", + "target": "Ipv6NetmaskLength" + }, + { + "source": "OutpostArn", + "target": "OutpostArn" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateSubnet", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::Subnet", + "mappings": [], + "operation": "DeleteSubnet", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::SubnetCidrBlock", + "mappings": [ + { + "source": "Ipv6CidrBlock", + "target": "Ipv6CidrBlock" + }, + { + "source": "Ipv6IpamPoolId", + "target": "Ipv6IpamPoolId" + }, + { + "source": "Ipv6NetmaskLength", + "target": "Ipv6NetmaskLength" + }, + { + "source": "SubnetId", + "target": "SubnetId" + } + ], + "operation": "AssociateSubnetCidrBlock", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::SubnetCidrBlock", + "mappings": [], + "operation": "DisassociateSubnetCidrBlock", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TrafficMirrorFilter", + "mappings": [ + { + "source": "Description", + "target": "Description" + } + ], + "operation": "CreateTrafficMirrorFilter", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TrafficMirrorFilter", + "mappings": [], + "operation": "DeleteTrafficMirrorFilter", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TrafficMirrorFilterRule", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DestinationCidrBlock", + "target": "DestinationCidrBlock" + }, + { + "source": "DestinationPortRange", + "target": "DestinationPortRange" + }, + { + "source": "Protocol", + "target": "Protocol" + }, + { + "source": "RuleAction", + "target": "RuleAction" + }, + { + "source": "RuleNumber", + "target": "RuleNumber" + }, + { + "source": "SourceCidrBlock", + "target": "SourceCidrBlock" + }, + { + "source": "SourcePortRange", + "target": "SourcePortRange" + }, + { + "source": "TrafficDirection", + "target": "TrafficDirection" + }, + { + "source": "TrafficMirrorFilterId", + "target": "TrafficMirrorFilterId" + } + ], + "operation": "CreateTrafficMirrorFilterRule", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TrafficMirrorFilterRule", + "mappings": [], + "operation": "DeleteTrafficMirrorFilterRule", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TrafficMirrorSession", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "NetworkInterfaceId", + "target": "NetworkInterfaceId" + }, + { + "source": "PacketLength", + "target": "PacketLength" + }, + { + "source": "SessionNumber", + "target": "SessionNumber" + }, + { + "source": "TrafficMirrorFilterId", + "target": "TrafficMirrorFilterId" + }, + { + "source": "TrafficMirrorTargetId", + "target": "TrafficMirrorTargetId" + }, + { + "source": "VirtualNetworkId", + "target": "VirtualNetworkId" + } + ], + "operation": "CreateTrafficMirrorSession", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TrafficMirrorSession", + "mappings": [], + "operation": "DeleteTrafficMirrorSession", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TrafficMirrorTarget", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "GatewayLoadBalancerEndpointId", + "target": "GatewayLoadBalancerEndpointId" + }, + { + "source": "NetworkInterfaceId", + "target": "NetworkInterfaceId" + }, + { + "source": "NetworkLoadBalancerArn", + "target": "NetworkLoadBalancerArn" + } + ], + "operation": "CreateTrafficMirrorTarget", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TrafficMirrorTarget", + "mappings": [], + "operation": "DeleteTrafficMirrorTarget", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGateway", + "mappings": [ + { + "source": "Description", + "target": "Description" + } + ], + "operation": "CreateTransitGateway", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGateway", + "mappings": [], + "operation": "DeleteTransitGateway", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayConnect", + "mappings": [ + { + "source": "Options", + "target": "Options" + }, + { + "source": "TransportTransitGatewayAttachmentId", + "target": "TransportTransitGatewayAttachmentId" + } + ], + "operation": "CreateTransitGatewayConnect", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayConnect", + "mappings": [], + "operation": "DeleteTransitGatewayConnect", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayConnectPeer", + "mappings": [ + { + "source": "TransitGatewayAttachmentId", + "target": "TransitGatewayAttachmentId" + } + ], + "operation": "CreateTransitGatewayConnectPeer", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayConnectPeer", + "mappings": [], + "operation": "DeleteTransitGatewayConnectPeer", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayMulticastDomain", + "mappings": [ + { + "source": "Options", + "target": "Options" + }, + { + "source": "TransitGatewayId", + "target": "TransitGatewayId" + } + ], + "operation": "CreateTransitGatewayMulticastDomain", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayMulticastDomain", + "mappings": [], + "operation": "DeleteTransitGatewayMulticastDomain", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayMulticastDomainAssociation", + "mappings": [ + { + "source": "TransitGatewayAttachmentId", + "target": "TransitGatewayAttachmentId" + }, + { + "source": "TransitGatewayMulticastDomainId", + "target": "TransitGatewayMulticastDomainId" + } + ], + "operation": "AssociateTransitGatewayMulticastDomain", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayMulticastDomainAssociation", + "mappings": [ + { + "source": "TransitGatewayAttachmentId", + "target": "TransitGatewayAttachmentId" + }, + { + "source": "TransitGatewayMulticastDomainId", + "target": "TransitGatewayMulticastDomainId" + } + ], + "operation": "DisassociateTransitGatewayMulticastDomain", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayMulticastGroupMember", + "mappings": [ + { + "source": "GroupIpAddress", + "target": "GroupIpAddress" + }, + { + "source": "TransitGatewayMulticastDomainId", + "target": "TransitGatewayMulticastDomainId" + } + ], + "operation": "RegisterTransitGatewayMulticastGroupMembers", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayMulticastGroupMember", + "mappings": [ + { + "source": "GroupIpAddress", + "target": "GroupIpAddress" + }, + { + "source": "TransitGatewayMulticastDomainId", + "target": "TransitGatewayMulticastDomainId" + } + ], + "operation": "DeregisterTransitGatewayMulticastGroupMembers", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayMulticastGroupSource", + "mappings": [ + { + "source": "GroupIpAddress", + "target": "GroupIpAddress" + }, + { + "source": "TransitGatewayMulticastDomainId", + "target": "TransitGatewayMulticastDomainId" + } + ], + "operation": "RegisterTransitGatewayMulticastGroupSources", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayMulticastGroupSource", + "mappings": [ + { + "source": "GroupIpAddress", + "target": "GroupIpAddress" + }, + { + "source": "TransitGatewayMulticastDomainId", + "target": "TransitGatewayMulticastDomainId" + } + ], + "operation": "DeregisterTransitGatewayMulticastGroupSources", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayPeeringAttachment", + "mappings": [ + { + "source": "PeerAccountId", + "target": "PeerAccountId" + }, + { + "source": "PeerRegion", + "target": "PeerRegion" + }, + { + "source": "PeerTransitGatewayId", + "target": "PeerTransitGatewayId" + }, + { + "source": "TransitGatewayId", + "target": "TransitGatewayId" + } + ], + "operation": "CreateTransitGatewayPeeringAttachment", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayPeeringAttachment", + "mappings": [], + "operation": "DeleteTransitGatewayPeeringAttachment", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayPolicyTable", + "mappings": [ + { + "source": "TransitGatewayId", + "target": "TransitGatewayId" + } + ], + "operation": "CreateTransitGatewayPolicyTable", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayPolicyTable", + "mappings": [], + "operation": "DeleteTransitGatewayPolicyTable", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayPolicyTableAssociation", + "mappings": [ + { + "source": "TransitGatewayAttachmentId", + "target": "TransitGatewayAttachmentId" + }, + { + "source": "TransitGatewayPolicyTableId", + "target": "TransitGatewayPolicyTableId" + } + ], + "operation": "AssociateTransitGatewayPolicyTable", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayPolicyTableAssociation", + "mappings": [ + { + "source": "TransitGatewayAttachmentId", + "target": "TransitGatewayAttachmentId" + }, + { + "source": "TransitGatewayPolicyTableId", + "target": "TransitGatewayPolicyTableId" + } + ], + "operation": "DisassociateTransitGatewayPolicyTable", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayRoute", + "mappings": [ + { + "source": "Blackhole", + "target": "Blackhole" + }, + { + "source": "DestinationCidrBlock", + "target": "DestinationCidrBlock" + }, + { + "source": "TransitGatewayAttachmentId", + "target": "TransitGatewayAttachmentId" + }, + { + "source": "TransitGatewayRouteTableId", + "target": "TransitGatewayRouteTableId" + } + ], + "operation": "CreateTransitGatewayRoute", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayRoute", + "mappings": [ + { + "source": "DestinationCidrBlock", + "target": "DestinationCidrBlock" + }, + { + "source": "TransitGatewayRouteTableId", + "target": "TransitGatewayRouteTableId" + } + ], + "operation": "DeleteTransitGatewayRoute", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayRouteTable", + "mappings": [ + { + "source": "TransitGatewayId", + "target": "TransitGatewayId" + } + ], + "operation": "CreateTransitGatewayRouteTable", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayRouteTableAssociation", + "mappings": [ + { + "source": "TransitGatewayAttachmentId", + "target": "TransitGatewayAttachmentId" + }, + { + "source": "TransitGatewayRouteTableId", + "target": "TransitGatewayRouteTableId" + } + ], + "operation": "AssociateTransitGatewayRouteTable", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayRouteTableAssociation", + "mappings": [ + { + "source": "TransitGatewayAttachmentId", + "target": "TransitGatewayAttachmentId" + }, + { + "source": "TransitGatewayRouteTableId", + "target": "TransitGatewayRouteTableId" + } + ], + "operation": "DisassociateTransitGatewayRouteTable", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayRouteTablePropagation", + "mappings": [ + { + "source": "TransitGatewayAttachmentId", + "target": "TransitGatewayAttachmentId" + }, + { + "source": "TransitGatewayRouteTableId", + "target": "TransitGatewayRouteTableId" + } + ], + "operation": "EnableTransitGatewayRouteTablePropagation", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayVpcAttachment", + "mappings": [ + { + "source": "Options", + "target": "Options" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "TransitGatewayId", + "target": "TransitGatewayId" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateTransitGatewayVpcAttachment", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayVpcAttachment", + "mappings": [], + "operation": "DeleteTransitGatewayVpcAttachment", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPC", + "mappings": [ + { + "source": "CidrBlock", + "target": "CidrBlock" + }, + { + "source": "InstanceTenancy", + "target": "InstanceTenancy" + }, + { + "source": "Ipv4IpamPoolId", + "target": "Ipv4IpamPoolId" + }, + { + "source": "Ipv4NetmaskLength", + "target": "Ipv4NetmaskLength" + } + ], + "operation": "CreateVpc", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPC", + "mappings": [], + "operation": "DeleteVpc", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCBlockPublicAccessExclusion", + "mappings": [ + { + "source": "InternetGatewayExclusionMode", + "target": "InternetGatewayExclusionMode" + }, + { + "source": "SubnetId", + "target": "SubnetId" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateVpcBlockPublicAccessExclusion", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCBlockPublicAccessExclusion", + "mappings": [], + "operation": "DeleteVpcBlockPublicAccessExclusion", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCCidrBlock", + "mappings": [ + { + "source": "AmazonProvidedIpv6CidrBlock", + "target": "AmazonProvidedIpv6CidrBlock" + }, + { + "source": "CidrBlock", + "target": "CidrBlock" + }, + { + "source": "Ipv4IpamPoolId", + "target": "Ipv4IpamPoolId" + }, + { + "source": "Ipv4NetmaskLength", + "target": "Ipv4NetmaskLength" + }, + { + "source": "Ipv6CidrBlock", + "target": "Ipv6CidrBlock" + }, + { + "source": "Ipv6CidrBlockNetworkBorderGroup", + "target": "Ipv6CidrBlockNetworkBorderGroup" + }, + { + "source": "Ipv6IpamPoolId", + "target": "Ipv6IpamPoolId" + }, + { + "source": "Ipv6NetmaskLength", + "target": "Ipv6NetmaskLength" + }, + { + "source": "Ipv6Pool", + "target": "Ipv6Pool" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "AssociateVpcCidrBlock", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCCidrBlock", + "mappings": [], + "operation": "DisassociateVpcCidrBlock", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCDHCPOptionsAssociation", + "mappings": [ + { + "source": "DhcpOptionsId", + "target": "DhcpOptionsId" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "AssociateDhcpOptions", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCEndpoint", + "mappings": [ + { + "source": "DnsOptions", + "target": "DnsOptions" + }, + { + "source": "IpAddressType", + "target": "IpAddressType" + }, + { + "source": "PolicyDocument", + "target": "PolicyDocument" + }, + { + "source": "PrivateDnsEnabled", + "target": "PrivateDnsEnabled" + }, + { + "source": "ResourceConfigurationArn", + "target": "ResourceConfigurationArn" + }, + { + "source": "RouteTableIds", + "target": "RouteTableIds" + }, + { + "source": "SecurityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "ServiceName", + "target": "ServiceName" + }, + { + "source": "ServiceNetworkArn", + "target": "ServiceNetworkArn" + }, + { + "source": "ServiceRegion", + "target": "ServiceRegion" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "VpcEndpointType", + "target": "VpcEndpointType" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateVpcEndpoint", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCEndpoint", + "mappings": [], + "operation": "DeleteVpcEndpoints", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCEndpointConnectionNotification", + "mappings": [ + { + "source": "ConnectionEvents", + "target": "ConnectionEvents" + }, + { + "source": "ConnectionNotificationArn", + "target": "ConnectionNotificationArn" + }, + { + "source": "ServiceId", + "target": "ServiceId" + }, + { + "source": "VpcEndpointId", + "target": "VPCEndpointId" + } + ], + "operation": "CreateVpcEndpointConnectionNotification", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCEndpointConnectionNotification", + "mappings": [], + "operation": "DeleteVpcEndpointConnectionNotifications", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCEndpointService", + "mappings": [ + { + "source": "AcceptanceRequired", + "target": "AcceptanceRequired" + }, + { + "source": "GatewayLoadBalancerArns", + "target": "GatewayLoadBalancerArns" + }, + { + "source": "NetworkLoadBalancerArns", + "target": "NetworkLoadBalancerArns" + }, + { + "source": "SupportedIpAddressTypes", + "target": "SupportedIpAddressTypes" + }, + { + "source": "SupportedRegions", + "target": "SupportedRegions" + } + ], + "operation": "CreateVpcEndpointServiceConfiguration", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCPeeringConnection", + "mappings": [ + { + "source": "PeerOwnerId", + "target": "PeerOwnerId" + }, + { + "source": "PeerRegion", + "target": "PeerRegion" + }, + { + "source": "PeerVpcId", + "target": "PeerVpcId" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateVpcPeeringConnection", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCPeeringConnection", + "mappings": [], + "operation": "DeleteVpcPeeringConnection", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPNConnection", + "mappings": [ + { + "source": "CustomerGatewayId", + "target": "CustomerGatewayId" + }, + { + "source": "PreSharedKeyStorage", + "target": "PreSharedKeyStorage" + }, + { + "source": "TransitGatewayId", + "target": "TransitGatewayId" + }, + { + "source": "Type", + "target": "Type" + }, + { + "source": "VpnGatewayId", + "target": "VpnGatewayId" + } + ], + "operation": "CreateVpnConnection", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPNConnection", + "mappings": [], + "operation": "DeleteVpnConnection", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPNConnectionRoute", + "mappings": [ + { + "source": "DestinationCidrBlock", + "target": "DestinationCidrBlock" + }, + { + "source": "VpnConnectionId", + "target": "VpnConnectionId" + } + ], + "operation": "CreateVpnConnectionRoute", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPNConnectionRoute", + "mappings": [ + { + "source": "DestinationCidrBlock", + "target": "DestinationCidrBlock" + }, + { + "source": "VpnConnectionId", + "target": "VpnConnectionId" + } + ], + "operation": "DeleteVpnConnectionRoute", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPNGateway", + "mappings": [ + { + "source": "AmazonSideAsn", + "target": "AmazonSideAsn" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateVpnGateway", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPNGateway", + "mappings": [], + "operation": "DeleteVpnGateway", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VerifiedAccessEndpoint", + "mappings": [ + { + "source": "ApplicationDomain", + "target": "ApplicationDomain" + }, + { + "source": "AttachmentType", + "target": "AttachmentType" + }, + { + "source": "CidrOptions", + "target": "CidrOptions" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DomainCertificateArn", + "target": "DomainCertificateArn" + }, + { + "source": "EndpointDomainPrefix", + "target": "EndpointDomainPrefix" + }, + { + "source": "EndpointType", + "target": "EndpointType" + }, + { + "source": "LoadBalancerOptions", + "target": "LoadBalancerOptions" + }, + { + "source": "NetworkInterfaceOptions", + "target": "NetworkInterfaceOptions" + }, + { + "source": "PolicyDocument", + "target": "PolicyDocument" + }, + { + "source": "RdsOptions", + "target": "RdsOptions" + }, + { + "source": "SecurityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "SseSpecification", + "target": "SseSpecification" + }, + { + "source": "VerifiedAccessGroupId", + "target": "VerifiedAccessGroupId" + } + ], + "operation": "CreateVerifiedAccessEndpoint", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VerifiedAccessEndpoint", + "mappings": [], + "operation": "DeleteVerifiedAccessEndpoint", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VerifiedAccessGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "PolicyDocument", + "target": "PolicyDocument" + }, + { + "source": "SseSpecification", + "target": "SseSpecification" + }, + { + "source": "VerifiedAccessInstanceId", + "target": "VerifiedAccessInstanceId" + } + ], + "operation": "CreateVerifiedAccessGroup", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VerifiedAccessGroup", + "mappings": [], + "operation": "DeleteVerifiedAccessGroup", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VerifiedAccessInstance", + "mappings": [ + { + "source": "CidrEndpointsCustomSubDomain", + "target": "CidrEndpointsCustomSubDomain" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "FIPSEnabled", + "target": "FipsEnabled" + } + ], + "operation": "CreateVerifiedAccessInstance", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VerifiedAccessInstance", + "mappings": [], + "operation": "DeleteVerifiedAccessInstance", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VerifiedAccessTrustProvider", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DeviceOptions", + "target": "DeviceOptions" + }, + { + "source": "DeviceTrustProviderType", + "target": "DeviceTrustProviderType" + }, + { + "source": "NativeApplicationOidcOptions", + "target": "NativeApplicationOidcOptions" + }, + { + "source": "OidcOptions", + "target": "OidcOptions" + }, + { + "source": "PolicyReferenceName", + "target": "PolicyReferenceName" + }, + { + "source": "SseSpecification", + "target": "SseSpecification" + }, + { + "source": "TrustProviderType", + "target": "TrustProviderType" + }, + { + "source": "UserTrustProviderType", + "target": "UserTrustProviderType" + } + ], + "operation": "CreateVerifiedAccessTrustProvider", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VerifiedAccessTrustProvider", + "mappings": [], + "operation": "DeleteVerifiedAccessTrustProvider", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::Volume", + "mappings": [ + { + "source": "AvailabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "Encrypted", + "target": "Encrypted" + }, + { + "source": "Iops", + "target": "Iops" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "MultiAttachEnabled", + "target": "MultiAttachEnabled" + }, + { + "source": "OutpostArn", + "target": "OutpostArn" + }, + { + "source": "Size", + "target": "Size" + }, + { + "source": "SnapshotId", + "target": "SnapshotId" + }, + { + "source": "Throughput", + "target": "Throughput" + }, + { + "source": "VolumeInitializationRate", + "target": "VolumeInitializationRate" + }, + { + "source": "VolumeType", + "target": "VolumeType" + } + ], + "operation": "CreateVolume", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::Volume", + "mappings": [], + "operation": "DeleteVolume", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VolumeAttachment", + "mappings": [ + { + "source": "Device", + "target": "Device" + }, + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "VolumeId", + "target": "VolumeId" + } + ], + "operation": "AttachVolume", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VolumeAttachment", + "mappings": [ + { + "source": "Device", + "target": "Device" + }, + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "VolumeId", + "target": "VolumeId" + } + ], + "operation": "DetachVolume", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::ECR::PublicRepository", + "mappings": [ + { + "source": "repositoryName", + "target": "RepositoryName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRepository", + "phase": "create", + "service": "ecr-public" + }, + { + "cfn_type": "AWS::ECR::PullThroughCacheRule", + "mappings": [ + { + "source": "credentialArn", + "target": "CredentialArn" + }, + { + "source": "customRoleArn", + "target": "CustomRoleArn" + }, + { + "source": "ecrRepositoryPrefix", + "target": "EcrRepositoryPrefix" + }, + { + "source": "upstreamRegistry", + "target": "UpstreamRegistry" + }, + { + "source": "upstreamRegistryUrl", + "target": "UpstreamRegistryUrl" + }, + { + "source": "upstreamRepositoryPrefix", + "target": "UpstreamRepositoryPrefix" + } + ], + "operation": "CreatePullThroughCacheRule", + "phase": "create", + "service": "ecr" + }, + { + "cfn_type": "AWS::ECR::PullThroughCacheRule", + "mappings": [ + { + "source": "ecrRepositoryPrefix", + "target": "EcrRepositoryPrefix" + } + ], + "operation": "DeletePullThroughCacheRule", + "phase": "delete", + "service": "ecr" + }, + { + "cfn_type": "AWS::ECR::RegistryPolicy", + "mappings": [ + { + "source": "policyText", + "target": "PolicyText" + } + ], + "operation": "PutRegistryPolicy", + "phase": "create", + "service": "ecr" + }, + { + "cfn_type": "AWS::ECR::RegistryPolicy", + "mappings": [], + "operation": "DeleteRegistryPolicy", + "phase": "delete", + "service": "ecr" + }, + { + "cfn_type": "AWS::ECR::RegistryScanningConfiguration", + "mappings": [ + { + "source": "rules", + "target": "Rules" + }, + { + "source": "scanType", + "target": "ScanType" + } + ], + "operation": "PutRegistryScanningConfiguration", + "phase": "create", + "service": "ecr" + }, + { + "cfn_type": "AWS::ECR::ReplicationConfiguration", + "mappings": [ + { + "source": "replicationConfiguration", + "target": "ReplicationConfiguration" + } + ], + "operation": "PutReplicationConfiguration", + "phase": "create", + "service": "ecr" + }, + { + "cfn_type": "AWS::ECR::Repository", + "mappings": [ + { + "source": "encryptionConfiguration", + "target": "EncryptionConfiguration" + }, + { + "source": "imageScanningConfiguration", + "target": "ImageScanningConfiguration" + }, + { + "source": "imageTagMutability", + "target": "ImageTagMutability" + }, + { + "source": "imageTagMutabilityExclusionFilters", + "target": "ImageTagMutabilityExclusionFilters" + }, + { + "source": "repositoryName", + "target": "RepositoryName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRepository", + "phase": "create", + "service": "ecr" + }, + { + "cfn_type": "AWS::ECR::Repository", + "mappings": [ + { + "source": "repositoryName", + "target": "RepositoryName" + } + ], + "operation": "DeleteRepository", + "phase": "delete", + "service": "ecr" + }, + { + "cfn_type": "AWS::ECR::RepositoryCreationTemplate", + "mappings": [ + { + "source": "appliedFor", + "target": "AppliedFor" + }, + { + "source": "customRoleArn", + "target": "CustomRoleArn" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "encryptionConfiguration", + "target": "EncryptionConfiguration" + }, + { + "source": "imageTagMutability", + "target": "ImageTagMutability" + }, + { + "source": "imageTagMutabilityExclusionFilters", + "target": "ImageTagMutabilityExclusionFilters" + }, + { + "source": "lifecyclePolicy", + "target": "LifecyclePolicy" + }, + { + "source": "prefix", + "target": "Prefix" + }, + { + "source": "repositoryPolicy", + "target": "RepositoryPolicy" + }, + { + "source": "resourceTags", + "target": "ResourceTags" + } + ], + "operation": "CreateRepositoryCreationTemplate", + "phase": "create", + "service": "ecr" + }, + { + "cfn_type": "AWS::ECR::RepositoryCreationTemplate", + "mappings": [ + { + "source": "prefix", + "target": "Prefix" + } + ], + "operation": "DeleteRepositoryCreationTemplate", + "phase": "delete", + "service": "ecr" + }, + { + "cfn_type": "AWS::ECS::CapacityProvider", + "mappings": [ + { + "source": "autoScalingGroupProvider", + "target": "AutoScalingGroupProvider" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCapacityProvider", + "phase": "create", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::CapacityProvider", + "mappings": [], + "operation": "DeleteCapacityProvider", + "phase": "delete", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::Cluster", + "mappings": [ + { + "source": "capacityProviders", + "target": "CapacityProviders" + }, + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "configuration", + "target": "Configuration" + }, + { + "source": "defaultCapacityProviderStrategy", + "target": "DefaultCapacityProviderStrategy" + }, + { + "source": "serviceConnectDefaults", + "target": "ServiceConnectDefaults" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCluster", + "phase": "create", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::Cluster", + "mappings": [ + { + "source": "cluster", + "target": "ClusterName" + } + ], + "operation": "DeleteCluster", + "phase": "delete", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::ClusterCapacityProviderAssociations", + "mappings": [ + { + "source": "capacityProviders", + "target": "CapacityProviders" + }, + { + "source": "cluster", + "target": "Cluster" + }, + { + "source": "defaultCapacityProviderStrategy", + "target": "DefaultCapacityProviderStrategy" + } + ], + "operation": "PutClusterCapacityProviders", + "phase": "create", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::Service", + "mappings": [ + { + "source": "availabilityZoneRebalancing", + "target": "AvailabilityZoneRebalancing" + }, + { + "source": "capacityProviderStrategy", + "target": "CapacityProviderStrategy" + }, + { + "source": "cluster", + "target": "Cluster" + }, + { + "source": "deploymentConfiguration", + "target": "DeploymentConfiguration" + }, + { + "source": "deploymentController", + "target": "DeploymentController" + }, + { + "source": "desiredCount", + "target": "DesiredCount" + }, + { + "source": "enableECSManagedTags", + "target": "EnableECSManagedTags" + }, + { + "source": "enableExecuteCommand", + "target": "EnableExecuteCommand" + }, + { + "source": "healthCheckGracePeriodSeconds", + "target": "HealthCheckGracePeriodSeconds" + }, + { + "source": "launchType", + "target": "LaunchType" + }, + { + "source": "loadBalancers", + "target": "LoadBalancers" + }, + { + "source": "networkConfiguration", + "target": "NetworkConfiguration" + }, + { + "source": "placementConstraints", + "target": "PlacementConstraints" + }, + { + "source": "platformVersion", + "target": "PlatformVersion" + }, + { + "source": "propagateTags", + "target": "PropagateTags" + }, + { + "source": "role", + "target": "Role" + }, + { + "source": "schedulingStrategy", + "target": "SchedulingStrategy" + }, + { + "source": "serviceConnectConfiguration", + "target": "ServiceConnectConfiguration" + }, + { + "source": "serviceName", + "target": "ServiceName" + }, + { + "source": "serviceRegistries", + "target": "ServiceRegistries" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "taskDefinition", + "target": "TaskDefinition" + }, + { + "source": "volumeConfigurations", + "target": "VolumeConfigurations" + }, + { + "source": "vpcLatticeConfigurations", + "target": "VpcLatticeConfigurations" + } + ], + "operation": "CreateService", + "phase": "create", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::Service", + "mappings": [ + { + "source": "cluster", + "target": "Cluster" + }, + { + "source": "service", + "target": "ServiceName" + } + ], + "operation": "DeleteService", + "phase": "delete", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::TaskDefinition", + "mappings": [ + { + "source": "containerDefinitions", + "target": "ContainerDefinitions" + }, + { + "source": "cpu", + "target": "Cpu" + }, + { + "source": "enableFaultInjection", + "target": "EnableFaultInjection" + }, + { + "source": "ephemeralStorage", + "target": "EphemeralStorage" + }, + { + "source": "executionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "family", + "target": "Family" + }, + { + "source": "inferenceAccelerators", + "target": "InferenceAccelerators" + }, + { + "source": "ipcMode", + "target": "IpcMode" + }, + { + "source": "memory", + "target": "Memory" + }, + { + "source": "networkMode", + "target": "NetworkMode" + }, + { + "source": "pidMode", + "target": "PidMode" + }, + { + "source": "placementConstraints", + "target": "PlacementConstraints" + }, + { + "source": "proxyConfiguration", + "target": "ProxyConfiguration" + }, + { + "source": "requiresCompatibilities", + "target": "RequiresCompatibilities" + }, + { + "source": "runtimePlatform", + "target": "RuntimePlatform" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "taskRoleArn", + "target": "TaskRoleArn" + }, + { + "source": "volumes", + "target": "Volumes" + } + ], + "operation": "RegisterTaskDefinition", + "phase": "create", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::TaskDefinition", + "mappings": [], + "operation": "DeregisterTaskDefinition", + "phase": "delete", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::TaskSet", + "mappings": [ + { + "source": "capacityProviderStrategy", + "target": "CapacityProviderStrategy" + }, + { + "source": "cluster", + "target": "Cluster" + }, + { + "source": "externalId", + "target": "ExternalId" + }, + { + "source": "launchType", + "target": "LaunchType" + }, + { + "source": "loadBalancers", + "target": "LoadBalancers" + }, + { + "source": "networkConfiguration", + "target": "NetworkConfiguration" + }, + { + "source": "platformVersion", + "target": "PlatformVersion" + }, + { + "source": "scale", + "target": "Scale" + }, + { + "source": "service", + "target": "Service" + }, + { + "source": "serviceRegistries", + "target": "ServiceRegistries" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "taskDefinition", + "target": "TaskDefinition" + } + ], + "operation": "CreateTaskSet", + "phase": "create", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::TaskSet", + "mappings": [ + { + "source": "cluster", + "target": "Cluster" + }, + { + "source": "service", + "target": "Service" + } + ], + "operation": "DeleteTaskSet", + "phase": "delete", + "service": "ecs" + }, + { + "cfn_type": "AWS::EFS::AccessPoint", + "mappings": [ + { + "source": "ClientToken", + "target": "ClientToken" + }, + { + "source": "FileSystemId", + "target": "FileSystemId" + }, + { + "source": "PosixUser", + "target": "PosixUser" + }, + { + "source": "RootDirectory", + "target": "RootDirectory" + } + ], + "operation": "CreateAccessPoint", + "phase": "create", + "service": "efs" + }, + { + "cfn_type": "AWS::EFS::AccessPoint", + "mappings": [], + "operation": "DeleteAccessPoint", + "phase": "delete", + "service": "efs" + }, + { + "cfn_type": "AWS::EFS::FileSystem", + "mappings": [ + { + "source": "AvailabilityZoneName", + "target": "AvailabilityZoneName" + }, + { + "source": "Encrypted", + "target": "Encrypted" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "PerformanceMode", + "target": "PerformanceMode" + }, + { + "source": "ProvisionedThroughputInMibps", + "target": "ProvisionedThroughputInMibps" + }, + { + "source": "ThroughputMode", + "target": "ThroughputMode" + } + ], + "operation": "CreateFileSystem", + "phase": "create", + "service": "efs" + }, + { + "cfn_type": "AWS::EFS::FileSystem", + "mappings": [], + "operation": "DeleteFileSystem", + "phase": "delete", + "service": "efs" + }, + { + "cfn_type": "AWS::EFS::MountTarget", + "mappings": [ + { + "source": "FileSystemId", + "target": "FileSystemId" + }, + { + "source": "IpAddress", + "target": "IpAddress" + }, + { + "source": "IpAddressType", + "target": "IpAddressType" + }, + { + "source": "Ipv6Address", + "target": "Ipv6Address" + }, + { + "source": "SecurityGroups", + "target": "SecurityGroups" + }, + { + "source": "SubnetId", + "target": "SubnetId" + } + ], + "operation": "CreateMountTarget", + "phase": "create", + "service": "efs" + }, + { + "cfn_type": "AWS::EFS::MountTarget", + "mappings": [], + "operation": "DeleteMountTarget", + "phase": "delete", + "service": "efs" + }, + { + "cfn_type": "AWS::EKS::AccessEntry", + "mappings": [ + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "kubernetesGroups", + "target": "KubernetesGroups" + }, + { + "source": "principalArn", + "target": "PrincipalArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + }, + { + "source": "username", + "target": "Username" + } + ], + "operation": "CreateAccessEntry", + "phase": "create", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::AccessEntry", + "mappings": [ + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "principalArn", + "target": "PrincipalArn" + } + ], + "operation": "DeleteAccessEntry", + "phase": "delete", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::Addon", + "mappings": [ + { + "source": "addonName", + "target": "AddonName" + }, + { + "source": "addonVersion", + "target": "AddonVersion" + }, + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "configurationValues", + "target": "ConfigurationValues" + }, + { + "source": "podIdentityAssociations", + "target": "PodIdentityAssociations" + }, + { + "source": "resolveConflicts", + "target": "ResolveConflicts" + }, + { + "source": "serviceAccountRoleArn", + "target": "ServiceAccountRoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAddon", + "phase": "create", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::Addon", + "mappings": [ + { + "source": "addonName", + "target": "AddonName" + }, + { + "source": "clusterName", + "target": "ClusterName" + } + ], + "operation": "DeleteAddon", + "phase": "delete", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::Cluster", + "mappings": [ + { + "source": "accessConfig", + "target": "AccessConfig" + }, + { + "source": "bootstrapSelfManagedAddons", + "target": "BootstrapSelfManagedAddons" + }, + { + "source": "computeConfig", + "target": "ComputeConfig" + }, + { + "source": "deletionProtection", + "target": "DeletionProtection" + }, + { + "source": "encryptionConfig", + "target": "EncryptionConfig" + }, + { + "source": "kubernetesNetworkConfig", + "target": "KubernetesNetworkConfig" + }, + { + "source": "logging", + "target": "Logging" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "outpostConfig", + "target": "OutpostConfig" + }, + { + "source": "remoteNetworkConfig", + "target": "RemoteNetworkConfig" + }, + { + "source": "resourcesVpcConfig", + "target": "ResourcesVpcConfig" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "storageConfig", + "target": "StorageConfig" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "upgradePolicy", + "target": "UpgradePolicy" + }, + { + "source": "version", + "target": "Version" + }, + { + "source": "zonalShiftConfig", + "target": "ZonalShiftConfig" + } + ], + "operation": "CreateCluster", + "phase": "create", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::Cluster", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteCluster", + "phase": "delete", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::FargateProfile", + "mappings": [ + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "fargateProfileName", + "target": "FargateProfileName" + }, + { + "source": "podExecutionRoleArn", + "target": "PodExecutionRoleArn" + }, + { + "source": "selectors", + "target": "Selectors" + }, + { + "source": "subnets", + "target": "Subnets" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateFargateProfile", + "phase": "create", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::FargateProfile", + "mappings": [ + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "fargateProfileName", + "target": "FargateProfileName" + } + ], + "operation": "DeleteFargateProfile", + "phase": "delete", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::IdentityProviderConfig", + "mappings": [ + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "oidc", + "target": "Oidc" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "AssociateIdentityProviderConfig", + "phase": "create", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::IdentityProviderConfig", + "mappings": [ + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "identityProviderConfig", + "target": "IdentityProviderConfigName" + } + ], + "operation": "DisassociateIdentityProviderConfig", + "phase": "delete", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::Nodegroup", + "mappings": [ + { + "source": "amiType", + "target": "AmiType" + }, + { + "source": "capacityType", + "target": "CapacityType" + }, + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "diskSize", + "target": "DiskSize" + }, + { + "source": "instanceTypes", + "target": "InstanceTypes" + }, + { + "source": "labels", + "target": "Labels" + }, + { + "source": "launchTemplate", + "target": "LaunchTemplate" + }, + { + "source": "nodeRepairConfig", + "target": "NodeRepairConfig" + }, + { + "source": "nodeRole", + "target": "NodeRole" + }, + { + "source": "nodegroupName", + "target": "NodegroupName" + }, + { + "source": "releaseVersion", + "target": "ReleaseVersion" + }, + { + "source": "remoteAccess", + "target": "RemoteAccess" + }, + { + "source": "scalingConfig", + "target": "ScalingConfig" + }, + { + "source": "subnets", + "target": "Subnets" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "taints", + "target": "Taints" + }, + { + "source": "updateConfig", + "target": "UpdateConfig" + }, + { + "source": "version", + "target": "Version" + } + ], + "operation": "CreateNodegroup", + "phase": "create", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::Nodegroup", + "mappings": [ + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "nodegroupName", + "target": "NodegroupName" + } + ], + "operation": "DeleteNodegroup", + "phase": "delete", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::PodIdentityAssociation", + "mappings": [ + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "disableSessionTags", + "target": "DisableSessionTags" + }, + { + "source": "namespace", + "target": "Namespace" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "serviceAccount", + "target": "ServiceAccount" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetRoleArn", + "target": "TargetRoleArn" + } + ], + "operation": "CreatePodIdentityAssociation", + "phase": "create", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::PodIdentityAssociation", + "mappings": [ + { + "source": "clusterName", + "target": "ClusterName" + } + ], + "operation": "DeletePodIdentityAssociation", + "phase": "delete", + "service": "eks" + }, + { + "cfn_type": "AWS::EMR::SecurityConfiguration", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "SecurityConfiguration", + "target": "SecurityConfiguration" + } + ], + "operation": "CreateSecurityConfiguration", + "phase": "create", + "service": "emr" + }, + { + "cfn_type": "AWS::EMR::Step", + "mappings": [ + { + "source": "JobFlowId", + "target": "JobFlowId" + } + ], + "operation": "AddJobFlowSteps", + "phase": "create", + "service": "emr" + }, + { + "cfn_type": "AWS::EMR::Studio", + "mappings": [ + { + "source": "AuthMode", + "target": "AuthMode" + }, + { + "source": "DefaultS3Location", + "target": "DefaultS3Location" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EncryptionKeyArn", + "target": "EncryptionKeyArn" + }, + { + "source": "EngineSecurityGroupId", + "target": "EngineSecurityGroupId" + }, + { + "source": "IdcInstanceArn", + "target": "IdcInstanceArn" + }, + { + "source": "IdcUserAssignment", + "target": "IdcUserAssignment" + }, + { + "source": "IdpAuthUrl", + "target": "IdpAuthUrl" + }, + { + "source": "IdpRelayStateParameterName", + "target": "IdpRelayStateParameterName" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ServiceRole", + "target": "ServiceRole" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TrustedIdentityPropagationEnabled", + "target": "TrustedIdentityPropagationEnabled" + }, + { + "source": "UserRole", + "target": "UserRole" + }, + { + "source": "VpcId", + "target": "VpcId" + }, + { + "source": "WorkspaceSecurityGroupId", + "target": "WorkspaceSecurityGroupId" + } + ], + "operation": "CreateStudio", + "phase": "create", + "service": "emr" + }, + { + "cfn_type": "AWS::EMR::Studio", + "mappings": [], + "operation": "DeleteStudio", + "phase": "delete", + "service": "emr" + }, + { + "cfn_type": "AWS::EMR::StudioSessionMapping", + "mappings": [ + { + "source": "IdentityName", + "target": "IdentityName" + }, + { + "source": "IdentityType", + "target": "IdentityType" + }, + { + "source": "SessionPolicyArn", + "target": "SessionPolicyArn" + }, + { + "source": "StudioId", + "target": "StudioId" + } + ], + "operation": "CreateStudioSessionMapping", + "phase": "create", + "service": "emr" + }, + { + "cfn_type": "AWS::EMR::StudioSessionMapping", + "mappings": [ + { + "source": "IdentityName", + "target": "IdentityName" + }, + { + "source": "IdentityType", + "target": "IdentityType" + }, + { + "source": "StudioId", + "target": "StudioId" + } + ], + "operation": "DeleteStudioSessionMapping", + "phase": "delete", + "service": "emr" + }, + { + "cfn_type": "AWS::EMRContainers::Endpoint", + "mappings": [ + { + "source": "configurationOverrides", + "target": "ConfigurationOverrides" + }, + { + "source": "executionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "releaseLabel", + "target": "ReleaseLabel" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + }, + { + "source": "virtualClusterId", + "target": "VirtualClusterId" + } + ], + "operation": "CreateManagedEndpoint", + "phase": "create", + "service": "emr-containers" + }, + { + "cfn_type": "AWS::EMRContainers::Endpoint", + "mappings": [ + { + "source": "virtualClusterId", + "target": "VirtualClusterId" + } + ], + "operation": "DeleteManagedEndpoint", + "phase": "delete", + "service": "emr-containers" + }, + { + "cfn_type": "AWS::EMRContainers::SecurityConfiguration", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "securityConfigurationData", + "target": "SecurityConfigurationData" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSecurityConfiguration", + "phase": "create", + "service": "emr-containers" + }, + { + "cfn_type": "AWS::EMRContainers::VirtualCluster", + "mappings": [ + { + "source": "containerProvider", + "target": "ContainerProvider" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "securityConfigurationId", + "target": "SecurityConfigurationId" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateVirtualCluster", + "phase": "create", + "service": "emr-containers" + }, + { + "cfn_type": "AWS::EMRContainers::VirtualCluster", + "mappings": [], + "operation": "DeleteVirtualCluster", + "phase": "delete", + "service": "emr-containers" + }, + { + "cfn_type": "AWS::EMRServerless::Application", + "mappings": [ + { + "source": "architecture", + "target": "Architecture" + }, + { + "source": "autoStartConfiguration", + "target": "AutoStartConfiguration" + }, + { + "source": "autoStopConfiguration", + "target": "AutoStopConfiguration" + }, + { + "source": "identityCenterConfiguration", + "target": "IdentityCenterConfiguration" + }, + { + "source": "imageConfiguration", + "target": "ImageConfiguration" + }, + { + "source": "initialCapacity", + "target": "InitialCapacity" + }, + { + "source": "interactiveConfiguration", + "target": "InteractiveConfiguration" + }, + { + "source": "maximumCapacity", + "target": "MaximumCapacity" + }, + { + "source": "monitoringConfiguration", + "target": "MonitoringConfiguration" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "networkConfiguration", + "target": "NetworkConfiguration" + }, + { + "source": "releaseLabel", + "target": "ReleaseLabel" + }, + { + "source": "runtimeConfiguration", + "target": "RuntimeConfiguration" + }, + { + "source": "schedulerConfiguration", + "target": "SchedulerConfiguration" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + }, + { + "source": "workerTypeSpecifications", + "target": "WorkerTypeSpecifications" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "emr-serverless" + }, + { + "cfn_type": "AWS::EMRServerless::Application", + "mappings": [], + "operation": "DeleteApplication", + "phase": "delete", + "service": "emr-serverless" + }, + { + "cfn_type": "AWS::EVS::Environment", + "mappings": [ + { + "source": "connectivityInfo", + "target": "ConnectivityInfo" + }, + { + "source": "environmentName", + "target": "EnvironmentName" + }, + { + "source": "hosts", + "target": "Hosts" + }, + { + "source": "initialVlans", + "target": "InitialVlans" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "licenseInfo", + "target": "LicenseInfo" + }, + { + "source": "serviceAccessSecurityGroups", + "target": "ServiceAccessSecurityGroups" + }, + { + "source": "serviceAccessSubnetId", + "target": "ServiceAccessSubnetId" + }, + { + "source": "siteId", + "target": "SiteId" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "termsAccepted", + "target": "TermsAccepted" + }, + { + "source": "vcfHostnames", + "target": "VcfHostnames" + }, + { + "source": "vcfVersion", + "target": "VcfVersion" + }, + { + "source": "vpcId", + "target": "VpcId" + } + ], + "operation": "CreateEnvironment", + "phase": "create", + "service": "evs" + }, + { + "cfn_type": "AWS::EVS::Environment", + "mappings": [], + "operation": "DeleteEnvironment", + "phase": "delete", + "service": "evs" + }, + { + "cfn_type": "AWS::ElastiCache::CacheCluster", + "mappings": [ + { + "source": "AZMode", + "target": "AZMode" + }, + { + "source": "AutoMinorVersionUpgrade", + "target": "AutoMinorVersionUpgrade" + }, + { + "source": "CacheNodeType", + "target": "CacheNodeType" + }, + { + "source": "CacheParameterGroupName", + "target": "CacheParameterGroupName" + }, + { + "source": "CacheSecurityGroupNames", + "target": "CacheSecurityGroupNames" + }, + { + "source": "CacheSubnetGroupName", + "target": "CacheSubnetGroupName" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "IpDiscovery", + "target": "IpDiscovery" + }, + { + "source": "LogDeliveryConfigurations", + "target": "LogDeliveryConfigurations" + }, + { + "source": "NetworkType", + "target": "NetworkType" + }, + { + "source": "NotificationTopicArn", + "target": "NotificationTopicArn" + }, + { + "source": "NumCacheNodes", + "target": "NumCacheNodes" + }, + { + "source": "Port", + "target": "Port" + }, + { + "source": "PreferredAvailabilityZone", + "target": "PreferredAvailabilityZone" + }, + { + "source": "PreferredAvailabilityZones", + "target": "PreferredAvailabilityZones" + }, + { + "source": "PreferredMaintenanceWindow", + "target": "PreferredMaintenanceWindow" + }, + { + "source": "SnapshotArns", + "target": "SnapshotArns" + }, + { + "source": "SnapshotName", + "target": "SnapshotName" + }, + { + "source": "SnapshotRetentionLimit", + "target": "SnapshotRetentionLimit" + }, + { + "source": "SnapshotWindow", + "target": "SnapshotWindow" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TransitEncryptionEnabled", + "target": "TransitEncryptionEnabled" + } + ], + "operation": "CreateCacheCluster", + "phase": "create", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::CacheCluster", + "mappings": [], + "operation": "DeleteCacheCluster", + "phase": "delete", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::GlobalReplicationGroup", + "mappings": [ + { + "source": "GlobalReplicationGroupDescription", + "target": "GlobalReplicationGroupDescription" + }, + { + "source": "GlobalReplicationGroupIdSuffix", + "target": "GlobalReplicationGroupIdSuffix" + } + ], + "operation": "CreateGlobalReplicationGroup", + "phase": "create", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::ParameterGroup", + "mappings": [ + { + "source": "CacheParameterGroupFamily", + "target": "CacheParameterGroupFamily" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCacheParameterGroup", + "phase": "create", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::ParameterGroup", + "mappings": [], + "operation": "DeleteCacheParameterGroup", + "phase": "delete", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::ReplicationGroup", + "mappings": [ + { + "source": "AtRestEncryptionEnabled", + "target": "AtRestEncryptionEnabled" + }, + { + "source": "AuthToken", + "target": "AuthToken" + }, + { + "source": "AutoMinorVersionUpgrade", + "target": "AutoMinorVersionUpgrade" + }, + { + "source": "AutomaticFailoverEnabled", + "target": "AutomaticFailoverEnabled" + }, + { + "source": "CacheNodeType", + "target": "CacheNodeType" + }, + { + "source": "CacheParameterGroupName", + "target": "CacheParameterGroupName" + }, + { + "source": "CacheSecurityGroupNames", + "target": "CacheSecurityGroupNames" + }, + { + "source": "CacheSubnetGroupName", + "target": "CacheSubnetGroupName" + }, + { + "source": "ClusterMode", + "target": "ClusterMode" + }, + { + "source": "DataTieringEnabled", + "target": "DataTieringEnabled" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "GlobalReplicationGroupId", + "target": "GlobalReplicationGroupId" + }, + { + "source": "IpDiscovery", + "target": "IpDiscovery" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "LogDeliveryConfigurations", + "target": "LogDeliveryConfigurations" + }, + { + "source": "MultiAZEnabled", + "target": "MultiAZEnabled" + }, + { + "source": "NetworkType", + "target": "NetworkType" + }, + { + "source": "NodeGroupConfiguration", + "target": "NodeGroupConfiguration" + }, + { + "source": "NotificationTopicArn", + "target": "NotificationTopicArn" + }, + { + "source": "NumCacheClusters", + "target": "NumCacheClusters" + }, + { + "source": "NumNodeGroups", + "target": "NumNodeGroups" + }, + { + "source": "Port", + "target": "Port" + }, + { + "source": "PreferredCacheClusterAZs", + "target": "PreferredCacheClusterAZs" + }, + { + "source": "PreferredMaintenanceWindow", + "target": "PreferredMaintenanceWindow" + }, + { + "source": "PrimaryClusterId", + "target": "PrimaryClusterId" + }, + { + "source": "ReplicasPerNodeGroup", + "target": "ReplicasPerNodeGroup" + }, + { + "source": "ReplicationGroupDescription", + "target": "ReplicationGroupDescription" + }, + { + "source": "ReplicationGroupId", + "target": "ReplicationGroupId" + }, + { + "source": "SecurityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "SnapshotArns", + "target": "SnapshotArns" + }, + { + "source": "SnapshotName", + "target": "SnapshotName" + }, + { + "source": "SnapshotRetentionLimit", + "target": "SnapshotRetentionLimit" + }, + { + "source": "SnapshotWindow", + "target": "SnapshotWindow" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TransitEncryptionEnabled", + "target": "TransitEncryptionEnabled" + }, + { + "source": "TransitEncryptionMode", + "target": "TransitEncryptionMode" + }, + { + "source": "UserGroupIds", + "target": "UserGroupIds" + } + ], + "operation": "CreateReplicationGroup", + "phase": "create", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::ReplicationGroup", + "mappings": [ + { + "source": "ReplicationGroupId", + "target": "ReplicationGroupId" + } + ], + "operation": "DeleteReplicationGroup", + "phase": "delete", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::ServerlessCache", + "mappings": [ + { + "source": "CacheUsageLimits", + "target": "CacheUsageLimits" + }, + { + "source": "DailySnapshotTime", + "target": "DailySnapshotTime" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "MajorEngineVersion", + "target": "MajorEngineVersion" + }, + { + "source": "SecurityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "ServerlessCacheName", + "target": "ServerlessCacheName" + }, + { + "source": "SnapshotArnsToRestore", + "target": "SnapshotArnsToRestore" + }, + { + "source": "SnapshotRetentionLimit", + "target": "SnapshotRetentionLimit" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UserGroupId", + "target": "UserGroupId" + } + ], + "operation": "CreateServerlessCache", + "phase": "create", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::ServerlessCache", + "mappings": [ + { + "source": "FinalSnapshotName", + "target": "FinalSnapshotName" + }, + { + "source": "ServerlessCacheName", + "target": "ServerlessCacheName" + } + ], + "operation": "DeleteServerlessCache", + "phase": "delete", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::SubnetGroup", + "mappings": [ + { + "source": "CacheSubnetGroupName", + "target": "CacheSubnetGroupName" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCacheSubnetGroup", + "phase": "create", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::SubnetGroup", + "mappings": [ + { + "source": "CacheSubnetGroupName", + "target": "CacheSubnetGroupName" + } + ], + "operation": "DeleteCacheSubnetGroup", + "phase": "delete", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::User", + "mappings": [ + { + "source": "AccessString", + "target": "AccessString" + }, + { + "source": "AuthenticationMode", + "target": "AuthenticationMode" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "NoPasswordRequired", + "target": "NoPasswordRequired" + }, + { + "source": "Passwords", + "target": "Passwords" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UserId", + "target": "UserId" + }, + { + "source": "UserName", + "target": "UserName" + } + ], + "operation": "CreateUser", + "phase": "create", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::User", + "mappings": [ + { + "source": "UserId", + "target": "UserId" + } + ], + "operation": "DeleteUser", + "phase": "delete", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::UserGroup", + "mappings": [ + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UserGroupId", + "target": "UserGroupId" + }, + { + "source": "UserIds", + "target": "UserIds" + } + ], + "operation": "CreateUserGroup", + "phase": "create", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::UserGroup", + "mappings": [ + { + "source": "UserGroupId", + "target": "UserGroupId" + } + ], + "operation": "DeleteUserGroup", + "phase": "delete", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElasticBeanstalk::Application", + "mappings": [ + { + "source": "ApplicationName", + "target": "ApplicationName" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "ResourceLifecycleConfig", + "target": "ResourceLifecycleConfig" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "elasticbeanstalk" + }, + { + "cfn_type": "AWS::ElasticBeanstalk::Application", + "mappings": [ + { + "source": "ApplicationName", + "target": "ApplicationName" + } + ], + "operation": "DeleteApplication", + "phase": "delete", + "service": "elasticbeanstalk" + }, + { + "cfn_type": "AWS::ElasticBeanstalk::ApplicationVersion", + "mappings": [ + { + "source": "ApplicationName", + "target": "ApplicationName" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "SourceBundle", + "target": "SourceBundle" + } + ], + "operation": "CreateApplicationVersion", + "phase": "create", + "service": "elasticbeanstalk" + }, + { + "cfn_type": "AWS::ElasticBeanstalk::ApplicationVersion", + "mappings": [ + { + "source": "ApplicationName", + "target": "ApplicationName" + } + ], + "operation": "DeleteApplicationVersion", + "phase": "delete", + "service": "elasticbeanstalk" + }, + { + "cfn_type": "AWS::ElasticBeanstalk::ConfigurationTemplate", + "mappings": [ + { + "source": "ApplicationName", + "target": "ApplicationName" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EnvironmentId", + "target": "EnvironmentId" + }, + { + "source": "OptionSettings", + "target": "OptionSettings" + }, + { + "source": "PlatformArn", + "target": "PlatformArn" + }, + { + "source": "SolutionStackName", + "target": "SolutionStackName" + }, + { + "source": "SourceConfiguration", + "target": "SourceConfiguration" + } + ], + "operation": "CreateConfigurationTemplate", + "phase": "create", + "service": "elasticbeanstalk" + }, + { + "cfn_type": "AWS::ElasticBeanstalk::ConfigurationTemplate", + "mappings": [ + { + "source": "ApplicationName", + "target": "ApplicationName" + } + ], + "operation": "DeleteConfigurationTemplate", + "phase": "delete", + "service": "elasticbeanstalk" + }, + { + "cfn_type": "AWS::ElasticBeanstalk::Environment", + "mappings": [ + { + "source": "ApplicationName", + "target": "ApplicationName" + }, + { + "source": "CNAMEPrefix", + "target": "CNAMEPrefix" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EnvironmentName", + "target": "EnvironmentName" + }, + { + "source": "OperationsRole", + "target": "OperationsRole" + }, + { + "source": "OptionSettings", + "target": "OptionSettings" + }, + { + "source": "PlatformArn", + "target": "PlatformArn" + }, + { + "source": "SolutionStackName", + "target": "SolutionStackName" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TemplateName", + "target": "TemplateName" + }, + { + "source": "Tier", + "target": "Tier" + }, + { + "source": "VersionLabel", + "target": "VersionLabel" + } + ], + "operation": "CreateEnvironment", + "phase": "create", + "service": "elasticbeanstalk" + }, + { + "cfn_type": "AWS::ElasticBeanstalk::Environment", + "mappings": [ + { + "source": "EnvironmentName", + "target": "EnvironmentName" + } + ], + "operation": "TerminateEnvironment", + "phase": "delete", + "service": "elasticbeanstalk" + }, + { + "cfn_type": "AWS::ElasticLoadBalancing::LoadBalancer", + "mappings": [ + { + "source": "AvailabilityZones", + "target": "AvailabilityZones" + }, + { + "source": "Listeners", + "target": "Listeners" + }, + { + "source": "LoadBalancerName", + "target": "LoadBalancerName" + }, + { + "source": "Scheme", + "target": "Scheme" + }, + { + "source": "SecurityGroups", + "target": "SecurityGroups" + }, + { + "source": "Subnets", + "target": "Subnets" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLoadBalancer", + "phase": "create", + "service": "elb" + }, + { + "cfn_type": "AWS::ElasticLoadBalancing::LoadBalancer", + "mappings": [ + { + "source": "LoadBalancerName", + "target": "LoadBalancerName" + } + ], + "operation": "DeleteLoadBalancer", + "phase": "delete", + "service": "elb" + }, + { + "cfn_type": "AWS::ElasticLoadBalancingV2::Listener", + "mappings": [ + { + "source": "AlpnPolicy", + "target": "AlpnPolicy" + }, + { + "source": "Certificates", + "target": "Certificates" + }, + { + "source": "DefaultActions", + "target": "DefaultActions" + }, + { + "source": "LoadBalancerArn", + "target": "LoadBalancerArn" + }, + { + "source": "MutualAuthentication", + "target": "MutualAuthentication" + }, + { + "source": "Port", + "target": "Port" + }, + { + "source": "Protocol", + "target": "Protocol" + }, + { + "source": "SslPolicy", + "target": "SslPolicy" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateListener", + "phase": "create", + "service": "elbv2" + }, + { + "cfn_type": "AWS::ElasticLoadBalancingV2::Listener", + "mappings": [], + "operation": "DeleteListener", + "phase": "delete", + "service": "elbv2" + }, + { + "cfn_type": "AWS::ElasticLoadBalancingV2::ListenerRule", + "mappings": [ + { + "source": "Actions", + "target": "Actions" + }, + { + "source": "Conditions", + "target": "Conditions" + }, + { + "source": "ListenerArn", + "target": "ListenerArn" + }, + { + "source": "Priority", + "target": "Priority" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRule", + "phase": "create", + "service": "elbv2" + }, + { + "cfn_type": "AWS::ElasticLoadBalancingV2::LoadBalancer", + "mappings": [ + { + "source": "EnablePrefixForIpv6SourceNat", + "target": "EnablePrefixForIpv6SourceNat" + }, + { + "source": "IpAddressType", + "target": "IpAddressType" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Scheme", + "target": "Scheme" + }, + { + "source": "SecurityGroups", + "target": "SecurityGroups" + }, + { + "source": "SubnetMappings", + "target": "SubnetMappings" + }, + { + "source": "Subnets", + "target": "Subnets" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateLoadBalancer", + "phase": "create", + "service": "elbv2" + }, + { + "cfn_type": "AWS::ElasticLoadBalancingV2::TargetGroup", + "mappings": [ + { + "source": "HealthCheckEnabled", + "target": "HealthCheckEnabled" + }, + { + "source": "HealthCheckIntervalSeconds", + "target": "HealthCheckIntervalSeconds" + }, + { + "source": "HealthCheckPath", + "target": "HealthCheckPath" + }, + { + "source": "HealthCheckPort", + "target": "HealthCheckPort" + }, + { + "source": "HealthCheckProtocol", + "target": "HealthCheckProtocol" + }, + { + "source": "HealthCheckTimeoutSeconds", + "target": "HealthCheckTimeoutSeconds" + }, + { + "source": "HealthyThresholdCount", + "target": "HealthyThresholdCount" + }, + { + "source": "IpAddressType", + "target": "IpAddressType" + }, + { + "source": "Matcher", + "target": "Matcher" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Port", + "target": "Port" + }, + { + "source": "Protocol", + "target": "Protocol" + }, + { + "source": "ProtocolVersion", + "target": "ProtocolVersion" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetType", + "target": "TargetType" + }, + { + "source": "UnhealthyThresholdCount", + "target": "UnhealthyThresholdCount" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateTargetGroup", + "phase": "create", + "service": "elbv2" + }, + { + "cfn_type": "AWS::ElasticLoadBalancingV2::TargetGroup", + "mappings": [], + "operation": "DeleteTargetGroup", + "phase": "delete", + "service": "elbv2" + }, + { + "cfn_type": "AWS::ElasticLoadBalancingV2::TrustStore", + "mappings": [ + { + "source": "CaCertificatesBundleS3Bucket", + "target": "CaCertificatesBundleS3Bucket" + }, + { + "source": "CaCertificatesBundleS3Key", + "target": "CaCertificatesBundleS3Key" + }, + { + "source": "CaCertificatesBundleS3ObjectVersion", + "target": "CaCertificatesBundleS3ObjectVersion" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateTrustStore", + "phase": "create", + "service": "elbv2" + }, + { + "cfn_type": "AWS::ElasticLoadBalancingV2::TrustStore", + "mappings": [], + "operation": "DeleteTrustStore", + "phase": "delete", + "service": "elbv2" + }, + { + "cfn_type": "AWS::ElasticLoadBalancingV2::TrustStoreRevocation", + "mappings": [ + { + "source": "RevocationContents", + "target": "RevocationContents" + }, + { + "source": "TrustStoreArn", + "target": "TrustStoreArn" + } + ], + "operation": "AddTrustStoreRevocations", + "phase": "create", + "service": "elbv2" + }, + { + "cfn_type": "AWS::ElasticLoadBalancingV2::TrustStoreRevocation", + "mappings": [ + { + "source": "TrustStoreArn", + "target": "TrustStoreArn" + } + ], + "operation": "RemoveTrustStoreRevocations", + "phase": "delete", + "service": "elbv2" + }, + { + "cfn_type": "AWS::EntityResolution::IdMappingWorkflow", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "idMappingTechniques", + "target": "IdMappingTechniques" + }, + { + "source": "inputSourceConfig", + "target": "InputSourceConfig" + }, + { + "source": "outputSourceConfig", + "target": "OutputSourceConfig" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workflowName", + "target": "WorkflowName" + } + ], + "operation": "CreateIdMappingWorkflow", + "phase": "create", + "service": "entityresolution" + }, + { + "cfn_type": "AWS::EntityResolution::IdMappingWorkflow", + "mappings": [ + { + "source": "workflowName", + "target": "WorkflowName" + } + ], + "operation": "DeleteIdMappingWorkflow", + "phase": "delete", + "service": "entityresolution" + }, + { + "cfn_type": "AWS::EntityResolution::IdNamespace", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "idMappingWorkflowProperties", + "target": "IdMappingWorkflowProperties" + }, + { + "source": "idNamespaceName", + "target": "IdNamespaceName" + }, + { + "source": "inputSourceConfig", + "target": "InputSourceConfig" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateIdNamespace", + "phase": "create", + "service": "entityresolution" + }, + { + "cfn_type": "AWS::EntityResolution::IdNamespace", + "mappings": [ + { + "source": "idNamespaceName", + "target": "IdNamespaceName" + } + ], + "operation": "DeleteIdNamespace", + "phase": "delete", + "service": "entityresolution" + }, + { + "cfn_type": "AWS::EntityResolution::MatchingWorkflow", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "incrementalRunConfig", + "target": "IncrementalRunConfig" + }, + { + "source": "inputSourceConfig", + "target": "InputSourceConfig" + }, + { + "source": "outputSourceConfig", + "target": "OutputSourceConfig" + }, + { + "source": "resolutionTechniques", + "target": "ResolutionTechniques" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workflowName", + "target": "WorkflowName" + } + ], + "operation": "CreateMatchingWorkflow", + "phase": "create", + "service": "entityresolution" + }, + { + "cfn_type": "AWS::EntityResolution::MatchingWorkflow", + "mappings": [ + { + "source": "workflowName", + "target": "WorkflowName" + } + ], + "operation": "DeleteMatchingWorkflow", + "phase": "delete", + "service": "entityresolution" + }, + { + "cfn_type": "AWS::EntityResolution::PolicyStatement", + "mappings": [ + { + "source": "action", + "target": "Action" + }, + { + "source": "arn", + "target": "Arn" + }, + { + "source": "condition", + "target": "Condition" + }, + { + "source": "effect", + "target": "Effect" + }, + { + "source": "principal", + "target": "Principal" + }, + { + "source": "statementId", + "target": "StatementId" + } + ], + "operation": "AddPolicyStatement", + "phase": "create", + "service": "entityresolution" + }, + { + "cfn_type": "AWS::EntityResolution::PolicyStatement", + "mappings": [ + { + "source": "arn", + "target": "Arn" + }, + { + "source": "statementId", + "target": "StatementId" + } + ], + "operation": "DeletePolicyStatement", + "phase": "delete", + "service": "entityresolution" + }, + { + "cfn_type": "AWS::EntityResolution::SchemaMapping", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "mappedInputFields", + "target": "MappedInputFields" + }, + { + "source": "schemaName", + "target": "SchemaName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSchemaMapping", + "phase": "create", + "service": "entityresolution" + }, + { + "cfn_type": "AWS::EntityResolution::SchemaMapping", + "mappings": [ + { + "source": "schemaName", + "target": "SchemaName" + } + ], + "operation": "DeleteSchemaMapping", + "phase": "delete", + "service": "entityresolution" + }, + { + "cfn_type": "AWS::EventSchemas::Discoverer", + "mappings": [ + { + "source": "CrossAccount", + "target": "CrossAccount" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "SourceArn", + "target": "SourceArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDiscoverer", + "phase": "create", + "service": "schemas" + }, + { + "cfn_type": "AWS::EventSchemas::Discoverer", + "mappings": [], + "operation": "DeleteDiscoverer", + "phase": "delete", + "service": "schemas" + }, + { + "cfn_type": "AWS::EventSchemas::Registry", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "RegistryName", + "target": "RegistryName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRegistry", + "phase": "create", + "service": "schemas" + }, + { + "cfn_type": "AWS::EventSchemas::Registry", + "mappings": [ + { + "source": "RegistryName", + "target": "RegistryName" + } + ], + "operation": "DeleteRegistry", + "phase": "delete", + "service": "schemas" + }, + { + "cfn_type": "AWS::EventSchemas::RegistryPolicy", + "mappings": [ + { + "source": "Policy", + "target": "Policy" + }, + { + "source": "RegistryName", + "target": "RegistryName" + }, + { + "source": "RevisionId", + "target": "RevisionId" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "schemas" + }, + { + "cfn_type": "AWS::EventSchemas::Schema", + "mappings": [ + { + "source": "Content", + "target": "Content" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "RegistryName", + "target": "RegistryName" + }, + { + "source": "SchemaName", + "target": "SchemaName" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateSchema", + "phase": "create", + "service": "schemas" + }, + { + "cfn_type": "AWS::EventSchemas::Schema", + "mappings": [ + { + "source": "RegistryName", + "target": "RegistryName" + }, + { + "source": "SchemaName", + "target": "SchemaName" + } + ], + "operation": "DeleteSchema", + "phase": "delete", + "service": "schemas" + }, + { + "cfn_type": "AWS::Events::ApiDestination", + "mappings": [ + { + "source": "ConnectionArn", + "target": "ConnectionArn" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "HttpMethod", + "target": "HttpMethod" + }, + { + "source": "InvocationEndpoint", + "target": "InvocationEndpoint" + }, + { + "source": "InvocationRateLimitPerSecond", + "target": "InvocationRateLimitPerSecond" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateApiDestination", + "phase": "create", + "service": "events" + }, + { + "cfn_type": "AWS::Events::ApiDestination", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteApiDestination", + "phase": "delete", + "service": "events" + }, + { + "cfn_type": "AWS::Events::Archive", + "mappings": [ + { + "source": "ArchiveName", + "target": "ArchiveName" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EventPattern", + "target": "EventPattern" + }, + { + "source": "KmsKeyIdentifier", + "target": "KmsKeyIdentifier" + }, + { + "source": "RetentionDays", + "target": "RetentionDays" + } + ], + "operation": "CreateArchive", + "phase": "create", + "service": "events" + }, + { + "cfn_type": "AWS::Events::Archive", + "mappings": [ + { + "source": "ArchiveName", + "target": "ArchiveName" + } + ], + "operation": "DeleteArchive", + "phase": "delete", + "service": "events" + }, + { + "cfn_type": "AWS::Events::Connection", + "mappings": [ + { + "source": "AuthParameters", + "target": "AuthParameters" + }, + { + "source": "AuthorizationType", + "target": "AuthorizationType" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "InvocationConnectivityParameters", + "target": "InvocationConnectivityParameters" + }, + { + "source": "KmsKeyIdentifier", + "target": "KmsKeyIdentifier" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateConnection", + "phase": "create", + "service": "events" + }, + { + "cfn_type": "AWS::Events::Connection", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteConnection", + "phase": "delete", + "service": "events" + }, + { + "cfn_type": "AWS::Events::Endpoint", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "EventBuses", + "target": "EventBuses" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ReplicationConfig", + "target": "ReplicationConfig" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "RoutingConfig", + "target": "RoutingConfig" + } + ], + "operation": "CreateEndpoint", + "phase": "create", + "service": "events" + }, + { + "cfn_type": "AWS::Events::Endpoint", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteEndpoint", + "phase": "delete", + "service": "events" + }, + { + "cfn_type": "AWS::Events::EventBus", + "mappings": [ + { + "source": "DeadLetterConfig", + "target": "DeadLetterConfig" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EventSourceName", + "target": "EventSourceName" + }, + { + "source": "KmsKeyIdentifier", + "target": "KmsKeyIdentifier" + }, + { + "source": "LogConfig", + "target": "LogConfig" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEventBus", + "phase": "create", + "service": "events" + }, + { + "cfn_type": "AWS::Events::EventBus", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteEventBus", + "phase": "delete", + "service": "events" + }, + { + "cfn_type": "AWS::Events::EventBusPolicy", + "mappings": [ + { + "source": "Action", + "target": "Action" + }, + { + "source": "Condition", + "target": "Condition" + }, + { + "source": "EventBusName", + "target": "EventBusName" + }, + { + "source": "Principal", + "target": "Principal" + }, + { + "source": "StatementId", + "target": "StatementId" + } + ], + "operation": "PutPermission", + "phase": "create", + "service": "events" + }, + { + "cfn_type": "AWS::Events::EventBusPolicy", + "mappings": [ + { + "source": "EventBusName", + "target": "EventBusName" + }, + { + "source": "StatementId", + "target": "StatementId" + } + ], + "operation": "RemovePermission", + "phase": "delete", + "service": "events" + }, + { + "cfn_type": "AWS::Events::Rule", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "EventBusName", + "target": "EventBusName" + }, + { + "source": "EventPattern", + "target": "EventPattern" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "ScheduleExpression", + "target": "ScheduleExpression" + }, + { + "source": "State", + "target": "State" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "PutRule", + "phase": "create", + "service": "events" + }, + { + "cfn_type": "AWS::Events::Rule", + "mappings": [ + { + "source": "EventBusName", + "target": "EventBusName" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteRule", + "phase": "delete", + "service": "events" + }, + { + "cfn_type": "AWS::Evidently::Experiment", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "metricGoals", + "target": "MetricGoals" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "onlineAbConfig", + "target": "OnlineAbConfig" + }, + { + "source": "project", + "target": "Project" + }, + { + "source": "randomizationSalt", + "target": "RandomizationSalt" + }, + { + "source": "samplingRate", + "target": "SamplingRate" + }, + { + "source": "segment", + "target": "Segment" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "treatments", + "target": "Treatments" + } + ], + "operation": "CreateExperiment", + "phase": "create", + "service": "evidently" + }, + { + "cfn_type": "AWS::Evidently::Experiment", + "mappings": [ + { + "source": "project", + "target": "Project" + } + ], + "operation": "DeleteExperiment", + "phase": "delete", + "service": "evidently" + }, + { + "cfn_type": "AWS::Evidently::Feature", + "mappings": [ + { + "source": "defaultVariation", + "target": "DefaultVariation" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "entityOverrides", + "target": "EntityOverrides" + }, + { + "source": "evaluationStrategy", + "target": "EvaluationStrategy" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "project", + "target": "Project" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "variations", + "target": "Variations" + } + ], + "operation": "CreateFeature", + "phase": "create", + "service": "evidently" + }, + { + "cfn_type": "AWS::Evidently::Feature", + "mappings": [ + { + "source": "project", + "target": "Project" + } + ], + "operation": "DeleteFeature", + "phase": "delete", + "service": "evidently" + }, + { + "cfn_type": "AWS::Evidently::Launch", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "groups", + "target": "Groups" + }, + { + "source": "metricMonitors", + "target": "MetricMonitors" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "project", + "target": "Project" + }, + { + "source": "randomizationSalt", + "target": "RandomizationSalt" + }, + { + "source": "scheduledSplitsConfig", + "target": "ScheduledSplitsConfig" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateLaunch", + "phase": "create", + "service": "evidently" + }, + { + "cfn_type": "AWS::Evidently::Launch", + "mappings": [ + { + "source": "project", + "target": "Project" + } + ], + "operation": "DeleteLaunch", + "phase": "delete", + "service": "evidently" + }, + { + "cfn_type": "AWS::Evidently::Project", + "mappings": [ + { + "source": "appConfigResource", + "target": "AppConfigResource" + }, + { + "source": "dataDelivery", + "target": "DataDelivery" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateProject", + "phase": "create", + "service": "evidently" + }, + { + "cfn_type": "AWS::Evidently::Project", + "mappings": [], + "operation": "DeleteProject", + "phase": "delete", + "service": "evidently" + }, + { + "cfn_type": "AWS::Evidently::Segment", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "pattern", + "target": "Pattern" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSegment", + "phase": "create", + "service": "evidently" + }, + { + "cfn_type": "AWS::Evidently::Segment", + "mappings": [], + "operation": "DeleteSegment", + "phase": "delete", + "service": "evidently" + }, + { + "cfn_type": "AWS::FIS::ExperimentTemplate", + "mappings": [ + { + "source": "actions", + "target": "Actions" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "experimentOptions", + "target": "ExperimentOptions" + }, + { + "source": "experimentReportConfiguration", + "target": "ExperimentReportConfiguration" + }, + { + "source": "logConfiguration", + "target": "LogConfiguration" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "stopConditions", + "target": "StopConditions" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targets", + "target": "Targets" + } + ], + "operation": "CreateExperimentTemplate", + "phase": "create", + "service": "fis" + }, + { + "cfn_type": "AWS::FIS::ExperimentTemplate", + "mappings": [], + "operation": "DeleteExperimentTemplate", + "phase": "delete", + "service": "fis" + }, + { + "cfn_type": "AWS::FIS::TargetAccountConfiguration", + "mappings": [ + { + "source": "accountId", + "target": "AccountId" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "experimentTemplateId", + "target": "ExperimentTemplateId" + }, + { + "source": "roleArn", + "target": "RoleArn" + } + ], + "operation": "CreateTargetAccountConfiguration", + "phase": "create", + "service": "fis" + }, + { + "cfn_type": "AWS::FIS::TargetAccountConfiguration", + "mappings": [ + { + "source": "accountId", + "target": "AccountId" + }, + { + "source": "experimentTemplateId", + "target": "ExperimentTemplateId" + } + ], + "operation": "DeleteTargetAccountConfiguration", + "phase": "delete", + "service": "fis" + }, + { + "cfn_type": "AWS::FMS::NotificationChannel", + "mappings": [ + { + "source": "SnsRoleName", + "target": "SnsRoleName" + }, + { + "source": "SnsTopicArn", + "target": "SnsTopicArn" + } + ], + "operation": "PutNotificationChannel", + "phase": "create", + "service": "fms" + }, + { + "cfn_type": "AWS::FMS::NotificationChannel", + "mappings": [], + "operation": "DeleteNotificationChannel", + "phase": "delete", + "service": "fms" + }, + { + "cfn_type": "AWS::FMS::Policy", + "mappings": [ + { + "source": "Policy", + "target": "PolicyName" + } + ], + "operation": "PutPolicy", + "phase": "create", + "service": "fms" + }, + { + "cfn_type": "AWS::FMS::Policy", + "mappings": [ + { + "source": "DeleteAllPolicyResources", + "target": "DeleteAllPolicyResources" + } + ], + "operation": "DeletePolicy", + "phase": "delete", + "service": "fms" + }, + { + "cfn_type": "AWS::FMS::ResourceSet", + "mappings": [], + "operation": "DeleteResourceSet", + "phase": "delete", + "service": "fms" + }, + { + "cfn_type": "AWS::FSx::DataRepositoryAssociation", + "mappings": [ + { + "source": "BatchImportMetaDataOnCreate", + "target": "BatchImportMetaDataOnCreate" + }, + { + "source": "DataRepositoryPath", + "target": "DataRepositoryPath" + }, + { + "source": "FileSystemId", + "target": "FileSystemId" + }, + { + "source": "FileSystemPath", + "target": "FileSystemPath" + }, + { + "source": "ImportedFileChunkSize", + "target": "ImportedFileChunkSize" + }, + { + "source": "S3", + "target": "S3" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDataRepositoryAssociation", + "phase": "create", + "service": "fsx" + }, + { + "cfn_type": "AWS::FSx::DataRepositoryAssociation", + "mappings": [], + "operation": "DeleteDataRepositoryAssociation", + "phase": "delete", + "service": "fsx" + }, + { + "cfn_type": "AWS::FSx::S3AccessPointAttachment", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "OpenZFSConfiguration", + "target": "OpenZFSConfiguration" + }, + { + "source": "S3AccessPoint", + "target": "S3AccessPoint" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateAndAttachS3AccessPoint", + "phase": "create", + "service": "fsx" + }, + { + "cfn_type": "AWS::FinSpace::Environment", + "mappings": [ + { + "source": "dataBundles", + "target": "DataBundles" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "federationMode", + "target": "FederationMode" + }, + { + "source": "federationParameters", + "target": "FederationParameters" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "superuserParameters", + "target": "SuperuserParameters" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateEnvironment", + "phase": "create", + "service": "finspace" + }, + { + "cfn_type": "AWS::FinSpace::Environment", + "mappings": [], + "operation": "DeleteEnvironment", + "phase": "delete", + "service": "finspace" + }, + { + "cfn_type": "AWS::Forecast::Dataset", + "mappings": [ + { + "source": "DataFrequency", + "target": "DataFrequency" + }, + { + "source": "DatasetName", + "target": "DatasetName" + }, + { + "source": "DatasetType", + "target": "DatasetType" + }, + { + "source": "Domain", + "target": "Domain" + }, + { + "source": "EncryptionConfig", + "target": "EncryptionConfig" + }, + { + "source": "Schema", + "target": "Schema" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDataset", + "phase": "create", + "service": "forecast" + }, + { + "cfn_type": "AWS::Forecast::Dataset", + "mappings": [], + "operation": "DeleteDataset", + "phase": "delete", + "service": "forecast" + }, + { + "cfn_type": "AWS::Forecast::DatasetGroup", + "mappings": [ + { + "source": "DatasetArns", + "target": "DatasetArns" + }, + { + "source": "DatasetGroupName", + "target": "DatasetGroupName" + }, + { + "source": "Domain", + "target": "Domain" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDatasetGroup", + "phase": "create", + "service": "forecast" + }, + { + "cfn_type": "AWS::Forecast::DatasetGroup", + "mappings": [], + "operation": "DeleteDatasetGroup", + "phase": "delete", + "service": "forecast" + }, + { + "cfn_type": "AWS::FraudDetector::Detector", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "detectorId", + "target": "DetectorId" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "PutDetector", + "phase": "create", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::Detector", + "mappings": [ + { + "source": "detectorId", + "target": "DetectorId" + } + ], + "operation": "DeleteDetector", + "phase": "delete", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::EntityType", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "PutEntityType", + "phase": "create", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::EntityType", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteEntityType", + "phase": "delete", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::EventType", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "entityTypes", + "target": "EntityTypes" + }, + { + "source": "eventVariables", + "target": "EventVariables" + }, + { + "source": "labels", + "target": "Labels" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "PutEventType", + "phase": "create", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::EventType", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteEventType", + "phase": "delete", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::Label", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "PutLabel", + "phase": "create", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::Label", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteLabel", + "phase": "delete", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::List", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "elements", + "target": "Elements" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "variableType", + "target": "VariableType" + } + ], + "operation": "CreateList", + "phase": "create", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::List", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteList", + "phase": "delete", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::Outcome", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "PutOutcome", + "phase": "create", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::Outcome", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteOutcome", + "phase": "delete", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::Variable", + "mappings": [ + { + "source": "dataSource", + "target": "DataSource" + }, + { + "source": "dataType", + "target": "DataType" + }, + { + "source": "defaultValue", + "target": "DefaultValue" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "variableType", + "target": "VariableType" + } + ], + "operation": "CreateVariable", + "phase": "create", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::FraudDetector::Variable", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteVariable", + "phase": "delete", + "service": "frauddetector" + }, + { + "cfn_type": "AWS::GameLift::Alias", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoutingStrategy", + "target": "RoutingStrategy" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAlias", + "phase": "create", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::Alias", + "mappings": [], + "operation": "DeleteAlias", + "phase": "delete", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::Build", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "OperatingSystem", + "target": "OperatingSystem" + }, + { + "source": "ServerSdkVersion", + "target": "ServerSdkVersion" + }, + { + "source": "StorageLocation", + "target": "StorageLocation" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Version", + "target": "Version" + } + ], + "operation": "CreateBuild", + "phase": "create", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::Build", + "mappings": [], + "operation": "DeleteBuild", + "phase": "delete", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::ContainerFleet", + "mappings": [ + { + "source": "BillingType", + "target": "BillingType" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "FleetRoleArn", + "target": "FleetRoleArn" + }, + { + "source": "GameServerContainerGroupDefinitionName", + "target": "GameServerContainerGroupDefinitionName" + }, + { + "source": "GameServerContainerGroupsPerInstance", + "target": "GameServerContainerGroupsPerInstance" + }, + { + "source": "GameSessionCreationLimitPolicy", + "target": "GameSessionCreationLimitPolicy" + }, + { + "source": "InstanceConnectionPortRange", + "target": "InstanceConnectionPortRange" + }, + { + "source": "InstanceInboundPermissions", + "target": "InstanceInboundPermissions" + }, + { + "source": "InstanceType", + "target": "InstanceType" + }, + { + "source": "Locations", + "target": "Locations" + }, + { + "source": "LogConfiguration", + "target": "LogConfiguration" + }, + { + "source": "MetricGroups", + "target": "MetricGroups" + }, + { + "source": "NewGameSessionProtectionPolicy", + "target": "NewGameSessionProtectionPolicy" + }, + { + "source": "PerInstanceContainerGroupDefinitionName", + "target": "PerInstanceContainerGroupDefinitionName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateContainerFleet", + "phase": "create", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::ContainerFleet", + "mappings": [], + "operation": "DeleteContainerFleet", + "phase": "delete", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::ContainerGroupDefinition", + "mappings": [ + { + "source": "ContainerGroupType", + "target": "ContainerGroupType" + }, + { + "source": "GameServerContainerDefinition", + "target": "GameServerContainerDefinition" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "OperatingSystem", + "target": "OperatingSystem" + }, + { + "source": "SupportContainerDefinitions", + "target": "SupportContainerDefinitions" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TotalMemoryLimitMebibytes", + "target": "TotalMemoryLimitMebibytes" + }, + { + "source": "TotalVcpuLimit", + "target": "TotalVcpuLimit" + }, + { + "source": "VersionDescription", + "target": "VersionDescription" + } + ], + "operation": "CreateContainerGroupDefinition", + "phase": "create", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::ContainerGroupDefinition", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteContainerGroupDefinition", + "phase": "delete", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::Fleet", + "mappings": [ + { + "source": "AnywhereConfiguration", + "target": "AnywhereConfiguration" + }, + { + "source": "BuildId", + "target": "BuildId" + }, + { + "source": "CertificateConfiguration", + "target": "CertificateConfiguration" + }, + { + "source": "ComputeType", + "target": "ComputeType" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EC2InboundPermissions", + "target": "EC2InboundPermissions" + }, + { + "source": "EC2InstanceType", + "target": "EC2InstanceType" + }, + { + "source": "FleetType", + "target": "FleetType" + }, + { + "source": "InstanceRoleArn", + "target": "InstanceRoleARN" + }, + { + "source": "InstanceRoleCredentialsProvider", + "target": "InstanceRoleCredentialsProvider" + }, + { + "source": "Locations", + "target": "Locations" + }, + { + "source": "LogPaths", + "target": "LogPaths" + }, + { + "source": "MetricGroups", + "target": "MetricGroups" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NewGameSessionProtectionPolicy", + "target": "NewGameSessionProtectionPolicy" + }, + { + "source": "PeerVpcAwsAccountId", + "target": "PeerVpcAwsAccountId" + }, + { + "source": "PeerVpcId", + "target": "PeerVpcId" + }, + { + "source": "ResourceCreationLimitPolicy", + "target": "ResourceCreationLimitPolicy" + }, + { + "source": "RuntimeConfiguration", + "target": "RuntimeConfiguration" + }, + { + "source": "ScriptId", + "target": "ScriptId" + }, + { + "source": "ServerLaunchParameters", + "target": "ServerLaunchParameters" + }, + { + "source": "ServerLaunchPath", + "target": "ServerLaunchPath" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateFleet", + "phase": "create", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::Fleet", + "mappings": [], + "operation": "DeleteFleet", + "phase": "delete", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::GameServerGroup", + "mappings": [ + { + "source": "AutoScalingPolicy", + "target": "AutoScalingPolicy" + }, + { + "source": "BalancingStrategy", + "target": "BalancingStrategy" + }, + { + "source": "GameServerGroupName", + "target": "GameServerGroupName" + }, + { + "source": "GameServerProtectionPolicy", + "target": "GameServerProtectionPolicy" + }, + { + "source": "InstanceDefinitions", + "target": "InstanceDefinitions" + }, + { + "source": "LaunchTemplate", + "target": "LaunchTemplate" + }, + { + "source": "MaxSize", + "target": "MaxSize" + }, + { + "source": "MinSize", + "target": "MinSize" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcSubnets", + "target": "VpcSubnets" + } + ], + "operation": "CreateGameServerGroup", + "phase": "create", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::GameServerGroup", + "mappings": [ + { + "source": "DeleteOption", + "target": "DeleteOption" + }, + { + "source": "GameServerGroupName", + "target": "GameServerGroupName" + } + ], + "operation": "DeleteGameServerGroup", + "phase": "delete", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::GameSessionQueue", + "mappings": [ + { + "source": "CustomEventData", + "target": "CustomEventData" + }, + { + "source": "Destinations", + "target": "Destinations" + }, + { + "source": "FilterConfiguration", + "target": "FilterConfiguration" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NotificationTarget", + "target": "NotificationTarget" + }, + { + "source": "PlayerLatencyPolicies", + "target": "PlayerLatencyPolicies" + }, + { + "source": "PriorityConfiguration", + "target": "PriorityConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TimeoutInSeconds", + "target": "TimeoutInSeconds" + } + ], + "operation": "CreateGameSessionQueue", + "phase": "create", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::GameSessionQueue", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteGameSessionQueue", + "phase": "delete", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::Location", + "mappings": [ + { + "source": "LocationName", + "target": "LocationName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLocation", + "phase": "create", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::Location", + "mappings": [ + { + "source": "LocationName", + "target": "LocationName" + } + ], + "operation": "DeleteLocation", + "phase": "delete", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::MatchmakingConfiguration", + "mappings": [ + { + "source": "AcceptanceRequired", + "target": "AcceptanceRequired" + }, + { + "source": "AcceptanceTimeoutSeconds", + "target": "AcceptanceTimeoutSeconds" + }, + { + "source": "AdditionalPlayerCount", + "target": "AdditionalPlayerCount" + }, + { + "source": "BackfillMode", + "target": "BackfillMode" + }, + { + "source": "CustomEventData", + "target": "CustomEventData" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "FlexMatchMode", + "target": "FlexMatchMode" + }, + { + "source": "GameProperties", + "target": "GameProperties" + }, + { + "source": "GameSessionData", + "target": "GameSessionData" + }, + { + "source": "GameSessionQueueArns", + "target": "GameSessionQueueArns" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NotificationTarget", + "target": "NotificationTarget" + }, + { + "source": "RequestTimeoutSeconds", + "target": "RequestTimeoutSeconds" + }, + { + "source": "RuleSetName", + "target": "RuleSetName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateMatchmakingConfiguration", + "phase": "create", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::MatchmakingConfiguration", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteMatchmakingConfiguration", + "phase": "delete", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::MatchmakingRuleSet", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "RuleSetBody", + "target": "RuleSetBody" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateMatchmakingRuleSet", + "phase": "create", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::MatchmakingRuleSet", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteMatchmakingRuleSet", + "phase": "delete", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::Script", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "StorageLocation", + "target": "StorageLocation" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Version", + "target": "Version" + } + ], + "operation": "CreateScript", + "phase": "create", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLift::Script", + "mappings": [], + "operation": "DeleteScript", + "phase": "delete", + "service": "gamelift" + }, + { + "cfn_type": "AWS::GameLiftStreams::Application", + "mappings": [ + { + "source": "ApplicationLogOutputUri", + "target": "ApplicationLogOutputUri" + }, + { + "source": "ApplicationLogPaths", + "target": "ApplicationLogPaths" + }, + { + "source": "ApplicationSourceUri", + "target": "ApplicationSourceUri" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "ExecutablePath", + "target": "ExecutablePath" + }, + { + "source": "RuntimeEnvironment", + "target": "RuntimeEnvironment" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "gameliftstreams" + }, + { + "cfn_type": "AWS::GameLiftStreams::Application", + "mappings": [], + "operation": "DeleteApplication", + "phase": "delete", + "service": "gameliftstreams" + }, + { + "cfn_type": "AWS::GameLiftStreams::StreamGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "LocationConfigurations", + "target": "LocationConfigurations" + }, + { + "source": "StreamClass", + "target": "StreamClass" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateStreamGroup", + "phase": "create", + "service": "gameliftstreams" + }, + { + "cfn_type": "AWS::GameLiftStreams::StreamGroup", + "mappings": [], + "operation": "DeleteStreamGroup", + "phase": "delete", + "service": "gameliftstreams" + }, + { + "cfn_type": "AWS::GlobalAccelerator::Accelerator", + "mappings": [ + { + "source": "Enabled", + "target": "Enabled" + }, + { + "source": "IpAddressType", + "target": "IpAddressType" + }, + { + "source": "IpAddresses", + "target": "IpAddresses" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAccelerator", + "phase": "create", + "service": "globalaccelerator" + }, + { + "cfn_type": "AWS::GlobalAccelerator::Accelerator", + "mappings": [], + "operation": "DeleteAccelerator", + "phase": "delete", + "service": "globalaccelerator" + }, + { + "cfn_type": "AWS::GlobalAccelerator::CrossAccountAttachment", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Principals", + "target": "Principals" + }, + { + "source": "Resources", + "target": "Resources" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCrossAccountAttachment", + "phase": "create", + "service": "globalaccelerator" + }, + { + "cfn_type": "AWS::GlobalAccelerator::CrossAccountAttachment", + "mappings": [], + "operation": "DeleteCrossAccountAttachment", + "phase": "delete", + "service": "globalaccelerator" + }, + { + "cfn_type": "AWS::GlobalAccelerator::EndpointGroup", + "mappings": [ + { + "source": "EndpointConfigurations", + "target": "EndpointConfigurations" + }, + { + "source": "EndpointGroupRegion", + "target": "EndpointGroupRegion" + }, + { + "source": "HealthCheckIntervalSeconds", + "target": "HealthCheckIntervalSeconds" + }, + { + "source": "HealthCheckPath", + "target": "HealthCheckPath" + }, + { + "source": "HealthCheckPort", + "target": "HealthCheckPort" + }, + { + "source": "HealthCheckProtocol", + "target": "HealthCheckProtocol" + }, + { + "source": "ListenerArn", + "target": "ListenerArn" + }, + { + "source": "PortOverrides", + "target": "PortOverrides" + }, + { + "source": "ThresholdCount", + "target": "ThresholdCount" + }, + { + "source": "TrafficDialPercentage", + "target": "TrafficDialPercentage" + } + ], + "operation": "CreateEndpointGroup", + "phase": "create", + "service": "globalaccelerator" + }, + { + "cfn_type": "AWS::GlobalAccelerator::EndpointGroup", + "mappings": [], + "operation": "DeleteEndpointGroup", + "phase": "delete", + "service": "globalaccelerator" + }, + { + "cfn_type": "AWS::GlobalAccelerator::Listener", + "mappings": [ + { + "source": "AcceleratorArn", + "target": "AcceleratorArn" + }, + { + "source": "ClientAffinity", + "target": "ClientAffinity" + }, + { + "source": "PortRanges", + "target": "PortRanges" + }, + { + "source": "Protocol", + "target": "Protocol" + } + ], + "operation": "CreateListener", + "phase": "create", + "service": "globalaccelerator" + }, + { + "cfn_type": "AWS::GlobalAccelerator::Listener", + "mappings": [], + "operation": "DeleteListener", + "phase": "delete", + "service": "globalaccelerator" + }, + { + "cfn_type": "AWS::Glue::Blueprint", + "mappings": [ + { + "source": "BlueprintLocation", + "target": "BlueprintLocation" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateBlueprint", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Blueprint", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteBlueprint", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Catalog", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCatalog", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Catalog", + "mappings": [], + "operation": "DeleteCatalog", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Classifier", + "mappings": [ + { + "source": "CsvClassifier", + "target": "CsvClassifier" + }, + { + "source": "GrokClassifier", + "target": "GrokClassifier" + }, + { + "source": "JsonClassifier", + "target": "JsonClassifier" + }, + { + "source": "XMLClassifier", + "target": "XMLClassifier" + } + ], + "operation": "CreateClassifier", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Classifier", + "mappings": [], + "operation": "DeleteClassifier", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Crawler", + "mappings": [ + { + "source": "Classifiers", + "target": "Classifiers" + }, + { + "source": "Configuration", + "target": "Configuration" + }, + { + "source": "CrawlerSecurityConfiguration", + "target": "CrawlerSecurityConfiguration" + }, + { + "source": "DatabaseName", + "target": "DatabaseName" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "LakeFormationConfiguration", + "target": "LakeFormationConfiguration" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RecrawlPolicy", + "target": "RecrawlPolicy" + }, + { + "source": "Role", + "target": "Role" + }, + { + "source": "Schedule", + "target": "Schedule" + }, + { + "source": "SchemaChangePolicy", + "target": "SchemaChangePolicy" + }, + { + "source": "TablePrefix", + "target": "TablePrefix" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Targets", + "target": "Targets" + } + ], + "operation": "CreateCrawler", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Crawler", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteCrawler", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::CustomEntityType", + "mappings": [ + { + "source": "ContextWords", + "target": "ContextWords" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RegexString", + "target": "RegexString" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCustomEntityType", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::CustomEntityType", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteCustomEntityType", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::DataCatalogEncryptionSettings", + "mappings": [ + { + "source": "CatalogId", + "target": "CatalogId" + }, + { + "source": "DataCatalogEncryptionSettings", + "target": "DataCatalogEncryptionSettings" + } + ], + "operation": "PutDataCatalogEncryptionSettings", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::DataQualityRuleset", + "mappings": [ + { + "source": "ClientToken", + "target": "ClientToken" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Ruleset", + "target": "Ruleset" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetTable", + "target": "TargetTable" + } + ], + "operation": "CreateDataQualityRuleset", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::DataQualityRuleset", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteDataQualityRuleset", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Database", + "mappings": [ + { + "source": "CatalogId", + "target": "CatalogId" + }, + { + "source": "DatabaseInput", + "target": "DatabaseInput" + } + ], + "operation": "CreateDatabase", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Database", + "mappings": [ + { + "source": "CatalogId", + "target": "CatalogId" + }, + { + "source": "Name", + "target": "DatabaseName" + } + ], + "operation": "DeleteDatabase", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Integration", + "mappings": [ + { + "source": "AdditionalEncryptionContext", + "target": "AdditionalEncryptionContext" + }, + { + "source": "DataFilter", + "target": "DataFilter" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "IntegrationConfig", + "target": "IntegrationConfig" + }, + { + "source": "IntegrationName", + "target": "IntegrationName" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "SourceArn", + "target": "SourceArn" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetArn", + "target": "TargetArn" + } + ], + "operation": "CreateIntegration", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Integration", + "mappings": [], + "operation": "DeleteIntegration", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::IntegrationResourceProperty", + "mappings": [ + { + "source": "ResourceArn", + "target": "ResourceArn" + }, + { + "source": "SourceProcessingProperties", + "target": "SourceProcessingProperties" + }, + { + "source": "TargetProcessingProperties", + "target": "TargetProcessingProperties" + } + ], + "operation": "CreateIntegrationResourceProperty", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Job", + "mappings": [ + { + "source": "AllocatedCapacity", + "target": "AllocatedCapacity" + }, + { + "source": "Command", + "target": "Command" + }, + { + "source": "Connections", + "target": "Connections" + }, + { + "source": "DefaultArguments", + "target": "DefaultArguments" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "ExecutionClass", + "target": "ExecutionClass" + }, + { + "source": "ExecutionProperty", + "target": "ExecutionProperty" + }, + { + "source": "GlueVersion", + "target": "GlueVersion" + }, + { + "source": "JobMode", + "target": "JobMode" + }, + { + "source": "JobRunQueuingEnabled", + "target": "JobRunQueuingEnabled" + }, + { + "source": "LogUri", + "target": "LogUri" + }, + { + "source": "MaintenanceWindow", + "target": "MaintenanceWindow" + }, + { + "source": "MaxCapacity", + "target": "MaxCapacity" + }, + { + "source": "MaxRetries", + "target": "MaxRetries" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NonOverridableArguments", + "target": "NonOverridableArguments" + }, + { + "source": "NotificationProperty", + "target": "NotificationProperty" + }, + { + "source": "NumberOfWorkers", + "target": "NumberOfWorkers" + }, + { + "source": "Role", + "target": "Role" + }, + { + "source": "SecurityConfiguration", + "target": "SecurityConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Timeout", + "target": "Timeout" + }, + { + "source": "WorkerType", + "target": "WorkerType" + } + ], + "operation": "CreateJob", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Job", + "mappings": [], + "operation": "DeleteJob", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::MLTransform", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "GlueVersion", + "target": "GlueVersion" + }, + { + "source": "InputRecordTables", + "target": "InputRecordTables" + }, + { + "source": "MaxCapacity", + "target": "MaxCapacity" + }, + { + "source": "MaxRetries", + "target": "MaxRetries" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NumberOfWorkers", + "target": "NumberOfWorkers" + }, + { + "source": "Role", + "target": "Role" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Timeout", + "target": "Timeout" + }, + { + "source": "TransformEncryption", + "target": "TransformEncryption" + }, + { + "source": "WorkerType", + "target": "WorkerType" + } + ], + "operation": "CreateMLTransform", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::MLTransform", + "mappings": [], + "operation": "DeleteMLTransform", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Registry", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRegistry", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Registry", + "mappings": [], + "operation": "DeleteRegistry", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Schema", + "mappings": [ + { + "source": "Compatibility", + "target": "Compatibility" + }, + { + "source": "DataFormat", + "target": "DataFormat" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "SchemaDefinition", + "target": "SchemaDefinition" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSchema", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Schema", + "mappings": [], + "operation": "DeleteSchema", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::SchemaVersion", + "mappings": [ + { + "source": "SchemaDefinition", + "target": "SchemaDefinition" + } + ], + "operation": "RegisterSchemaVersion", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::SchemaVersion", + "mappings": [], + "operation": "DeleteSchemaVersions", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::SchemaVersionMetadata", + "mappings": [ + { + "source": "SchemaVersionId", + "target": "SchemaVersionId" + } + ], + "operation": "PutSchemaVersionMetadata", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::SchemaVersionMetadata", + "mappings": [ + { + "source": "SchemaVersionId", + "target": "SchemaVersionId" + } + ], + "operation": "RemoveSchemaVersionMetadata", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::SecurityConfiguration", + "mappings": [ + { + "source": "EncryptionConfiguration", + "target": "EncryptionConfiguration" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateSecurityConfiguration", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::SecurityConfiguration", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteSecurityConfiguration", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::TableOptimizer", + "mappings": [ + { + "source": "CatalogId", + "target": "CatalogId" + }, + { + "source": "DatabaseName", + "target": "DatabaseName" + }, + { + "source": "TableName", + "target": "TableName" + }, + { + "source": "TableOptimizerConfiguration", + "target": "TableOptimizerConfiguration" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateTableOptimizer", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::TableOptimizer", + "mappings": [ + { + "source": "CatalogId", + "target": "CatalogId" + }, + { + "source": "DatabaseName", + "target": "DatabaseName" + }, + { + "source": "TableName", + "target": "TableName" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "DeleteTableOptimizer", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Trigger", + "mappings": [ + { + "source": "Actions", + "target": "Actions" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EventBatchingCondition", + "target": "EventBatchingCondition" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Predicate", + "target": "Predicate" + }, + { + "source": "Schedule", + "target": "Schedule" + }, + { + "source": "StartOnCreation", + "target": "StartOnCreation" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + }, + { + "source": "WorkflowName", + "target": "WorkflowName" + } + ], + "operation": "CreateTrigger", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Trigger", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteTrigger", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::UsageProfile", + "mappings": [ + { + "source": "Configuration", + "target": "Configuration" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateUsageProfile", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::UsageProfile", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteUsageProfile", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::UserDefinedFunction", + "mappings": [ + { + "source": "DatabaseName", + "target": "DatabaseName" + } + ], + "operation": "CreateUserDefinedFunction", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::UserDefinedFunction", + "mappings": [ + { + "source": "DatabaseName", + "target": "DatabaseName" + }, + { + "source": "FunctionName", + "target": "FunctionName" + } + ], + "operation": "DeleteUserDefinedFunction", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Workflow", + "mappings": [ + { + "source": "DefaultRunProperties", + "target": "DefaultRunProperties" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "MaxConcurrentRuns", + "target": "MaxConcurrentRuns" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateWorkflow", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Workflow", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteWorkflow", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Grafana::Workspace", + "mappings": [ + { + "source": "accountAccessType", + "target": "AccountAccessType" + }, + { + "source": "authenticationProviders", + "target": "AuthenticationProviders" + }, + { + "source": "clientToken", + "target": "ClientToken" + }, + { + "source": "grafanaVersion", + "target": "GrafanaVersion" + }, + { + "source": "networkAccessControl", + "target": "NetworkAccessControl" + }, + { + "source": "organizationRoleName", + "target": "OrganizationRoleName" + }, + { + "source": "permissionType", + "target": "PermissionType" + }, + { + "source": "stackSetName", + "target": "StackSetName" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "vpcConfiguration", + "target": "VpcConfiguration" + } + ], + "operation": "CreateWorkspace", + "phase": "create", + "service": "grafana" + }, + { + "cfn_type": "AWS::Grafana::Workspace", + "mappings": [], + "operation": "DeleteWorkspace", + "phase": "delete", + "service": "grafana" + }, + { + "cfn_type": "AWS::GreengrassV2::ComponentVersion", + "mappings": [ + { + "source": "inlineRecipe", + "target": "InlineRecipe" + }, + { + "source": "lambdaFunction", + "target": "LambdaFunction" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateComponentVersion", + "phase": "create", + "service": "greengrassv2" + }, + { + "cfn_type": "AWS::GreengrassV2::Deployment", + "mappings": [ + { + "source": "components", + "target": "Components" + }, + { + "source": "deploymentName", + "target": "DeploymentName" + }, + { + "source": "deploymentPolicies", + "target": "DeploymentPolicies" + }, + { + "source": "iotJobConfiguration", + "target": "IotJobConfiguration" + }, + { + "source": "parentTargetArn", + "target": "ParentTargetArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetArn", + "target": "TargetArn" + } + ], + "operation": "CreateDeployment", + "phase": "create", + "service": "greengrassv2" + }, + { + "cfn_type": "AWS::GroundStation::Config", + "mappings": [ + { + "source": "configData", + "target": "ConfigData" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateConfig", + "phase": "create", + "service": "groundstation" + }, + { + "cfn_type": "AWS::GroundStation::Config", + "mappings": [], + "operation": "DeleteConfig", + "phase": "delete", + "service": "groundstation" + }, + { + "cfn_type": "AWS::GroundStation::DataflowEndpointGroup", + "mappings": [ + { + "source": "contactPostPassDurationSeconds", + "target": "ContactPostPassDurationSeconds" + }, + { + "source": "contactPrePassDurationSeconds", + "target": "ContactPrePassDurationSeconds" + }, + { + "source": "endpointDetails", + "target": "EndpointDetails" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDataflowEndpointGroup", + "phase": "create", + "service": "groundstation" + }, + { + "cfn_type": "AWS::GroundStation::DataflowEndpointGroup", + "mappings": [], + "operation": "DeleteDataflowEndpointGroup", + "phase": "delete", + "service": "groundstation" + }, + { + "cfn_type": "AWS::GroundStation::MissionProfile", + "mappings": [ + { + "source": "contactPostPassDurationSeconds", + "target": "ContactPostPassDurationSeconds" + }, + { + "source": "contactPrePassDurationSeconds", + "target": "ContactPrePassDurationSeconds" + }, + { + "source": "dataflowEdges", + "target": "DataflowEdges" + }, + { + "source": "minimumViableContactDurationSeconds", + "target": "MinimumViableContactDurationSeconds" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "streamsKmsKey", + "target": "StreamsKmsKey" + }, + { + "source": "streamsKmsRole", + "target": "StreamsKmsRole" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "trackingConfigArn", + "target": "TrackingConfigArn" + } + ], + "operation": "CreateMissionProfile", + "phase": "create", + "service": "groundstation" + }, + { + "cfn_type": "AWS::GroundStation::MissionProfile", + "mappings": [], + "operation": "DeleteMissionProfile", + "phase": "delete", + "service": "groundstation" + }, + { + "cfn_type": "AWS::GuardDuty::Detector", + "mappings": [ + { + "source": "DataSources", + "target": "DataSources" + }, + { + "source": "Enable", + "target": "Enable" + }, + { + "source": "Features", + "target": "Features" + }, + { + "source": "FindingPublishingFrequency", + "target": "FindingPublishingFrequency" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDetector", + "phase": "create", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::Detector", + "mappings": [], + "operation": "DeleteDetector", + "phase": "delete", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::Filter", + "mappings": [ + { + "source": "Action", + "target": "Action" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DetectorId", + "target": "DetectorId" + }, + { + "source": "FindingCriteria", + "target": "FindingCriteria" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Rank", + "target": "Rank" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateFilter", + "phase": "create", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::Filter", + "mappings": [ + { + "source": "DetectorId", + "target": "DetectorId" + } + ], + "operation": "DeleteFilter", + "phase": "delete", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::IPSet", + "mappings": [ + { + "source": "Activate", + "target": "Activate" + }, + { + "source": "DetectorId", + "target": "DetectorId" + }, + { + "source": "ExpectedBucketOwner", + "target": "ExpectedBucketOwner" + }, + { + "source": "Format", + "target": "Format" + }, + { + "source": "Location", + "target": "Location" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateIPSet", + "phase": "create", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::IPSet", + "mappings": [ + { + "source": "DetectorId", + "target": "DetectorId" + } + ], + "operation": "DeleteIPSet", + "phase": "delete", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::MalwareProtectionPlan", + "mappings": [ + { + "source": "Actions", + "target": "Actions" + }, + { + "source": "ProtectedResource", + "target": "ProtectedResource" + }, + { + "source": "Role", + "target": "Role" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateMalwareProtectionPlan", + "phase": "create", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::MalwareProtectionPlan", + "mappings": [], + "operation": "DeleteMalwareProtectionPlan", + "phase": "delete", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::Member", + "mappings": [ + { + "source": "DetectorId", + "target": "DetectorId" + } + ], + "operation": "CreateMembers", + "phase": "create", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::PublishingDestination", + "mappings": [ + { + "source": "DestinationProperties", + "target": "DestinationProperties" + }, + { + "source": "DestinationType", + "target": "DestinationType" + }, + { + "source": "DetectorId", + "target": "DetectorId" + } + ], + "operation": "CreatePublishingDestination", + "phase": "create", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::PublishingDestination", + "mappings": [ + { + "source": "DetectorId", + "target": "DetectorId" + } + ], + "operation": "DeletePublishingDestination", + "phase": "delete", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::ThreatIntelSet", + "mappings": [ + { + "source": "Activate", + "target": "Activate" + }, + { + "source": "DetectorId", + "target": "DetectorId" + }, + { + "source": "ExpectedBucketOwner", + "target": "ExpectedBucketOwner" + }, + { + "source": "Format", + "target": "Format" + }, + { + "source": "Location", + "target": "Location" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateThreatIntelSet", + "phase": "create", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::ThreatIntelSet", + "mappings": [ + { + "source": "DetectorId", + "target": "DetectorId" + } + ], + "operation": "DeleteThreatIntelSet", + "phase": "delete", + "service": "guardduty" + }, + { + "cfn_type": "AWS::HealthLake::FHIRDatastore", + "mappings": [ + { + "source": "DatastoreName", + "target": "DatastoreName" + }, + { + "source": "DatastoreTypeVersion", + "target": "DatastoreTypeVersion" + }, + { + "source": "IdentityProviderConfiguration", + "target": "IdentityProviderConfiguration" + }, + { + "source": "PreloadDataConfig", + "target": "PreloadDataConfig" + }, + { + "source": "SseConfiguration", + "target": "SseConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateFHIRDatastore", + "phase": "create", + "service": "healthlake" + }, + { + "cfn_type": "AWS::HealthLake::FHIRDatastore", + "mappings": [], + "operation": "DeleteFHIRDatastore", + "phase": "delete", + "service": "healthlake" + }, + { + "cfn_type": "AWS::IAM::Group", + "mappings": [ + { + "source": "GroupName", + "target": "GroupName" + }, + { + "source": "Path", + "target": "Path" + } + ], + "operation": "CreateGroup", + "phase": "create", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::Group", + "mappings": [ + { + "source": "GroupName", + "target": "GroupName" + } + ], + "operation": "DeleteGroup", + "phase": "delete", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::GroupPolicy", + "mappings": [ + { + "source": "GroupName", + "target": "GroupName" + }, + { + "source": "PolicyDocument", + "target": "PolicyDocument" + }, + { + "source": "PolicyName", + "target": "PolicyName" + } + ], + "operation": "PutGroupPolicy", + "phase": "create", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::GroupPolicy", + "mappings": [ + { + "source": "GroupName", + "target": "GroupName" + }, + { + "source": "PolicyName", + "target": "PolicyName" + } + ], + "operation": "DeleteGroupPolicy", + "phase": "delete", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::InstanceProfile", + "mappings": [ + { + "source": "InstanceProfileName", + "target": "InstanceProfileName" + }, + { + "source": "Path", + "target": "Path" + } + ], + "operation": "CreateInstanceProfile", + "phase": "create", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::InstanceProfile", + "mappings": [ + { + "source": "InstanceProfileName", + "target": "InstanceProfileName" + } + ], + "operation": "DeleteInstanceProfile", + "phase": "delete", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::ManagedPolicy", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Path", + "target": "Path" + }, + { + "source": "PolicyDocument", + "target": "PolicyDocument" + } + ], + "operation": "CreatePolicy", + "phase": "create", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::OIDCProvider", + "mappings": [ + { + "source": "ClientIDList", + "target": "ClientIdList" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "ThumbprintList", + "target": "ThumbprintList" + }, + { + "source": "Url", + "target": "Url" + } + ], + "operation": "CreateOpenIDConnectProvider", + "phase": "create", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::Role", + "mappings": [ + { + "source": "AssumeRolePolicyDocument", + "target": "AssumeRolePolicyDocument" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "MaxSessionDuration", + "target": "MaxSessionDuration" + }, + { + "source": "Path", + "target": "Path" + }, + { + "source": "PermissionsBoundary", + "target": "PermissionsBoundary" + }, + { + "source": "RoleName", + "target": "RoleName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRole", + "phase": "create", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::Role", + "mappings": [ + { + "source": "RoleName", + "target": "RoleName" + } + ], + "operation": "DeleteRole", + "phase": "delete", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::RolePolicy", + "mappings": [ + { + "source": "PolicyDocument", + "target": "PolicyDocument" + }, + { + "source": "PolicyName", + "target": "PolicyName" + }, + { + "source": "RoleName", + "target": "RoleName" + } + ], + "operation": "PutRolePolicy", + "phase": "create", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::RolePolicy", + "mappings": [ + { + "source": "PolicyName", + "target": "PolicyName" + }, + { + "source": "RoleName", + "target": "RoleName" + } + ], + "operation": "DeleteRolePolicy", + "phase": "delete", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::SAMLProvider", + "mappings": [ + { + "source": "AddPrivateKey", + "target": "AddPrivateKey" + }, + { + "source": "AssertionEncryptionMode", + "target": "AssertionEncryptionMode" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "SAMLMetadataDocument", + "target": "SamlMetadataDocument" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSAMLProvider", + "phase": "create", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::SAMLProvider", + "mappings": [], + "operation": "DeleteSAMLProvider", + "phase": "delete", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::ServerCertificate", + "mappings": [ + { + "source": "ServerCertificateName", + "target": "ServerCertificateName" + } + ], + "operation": "DeleteServerCertificate", + "phase": "delete", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::ServiceLinkedRole", + "mappings": [ + { + "source": "AWSServiceName", + "target": "AWSServiceName" + }, + { + "source": "CustomSuffix", + "target": "CustomSuffix" + }, + { + "source": "Description", + "target": "Description" + } + ], + "operation": "CreateServiceLinkedRole", + "phase": "create", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::ServiceLinkedRole", + "mappings": [], + "operation": "DeleteServiceLinkedRole", + "phase": "delete", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::User", + "mappings": [ + { + "source": "Path", + "target": "Path" + }, + { + "source": "PermissionsBoundary", + "target": "PermissionsBoundary" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UserName", + "target": "UserName" + } + ], + "operation": "CreateUser", + "phase": "create", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::User", + "mappings": [ + { + "source": "UserName", + "target": "UserName" + } + ], + "operation": "DeleteUser", + "phase": "delete", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::UserPolicy", + "mappings": [ + { + "source": "PolicyDocument", + "target": "PolicyDocument" + }, + { + "source": "PolicyName", + "target": "PolicyName" + }, + { + "source": "UserName", + "target": "UserName" + } + ], + "operation": "PutUserPolicy", + "phase": "create", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::UserPolicy", + "mappings": [ + { + "source": "PolicyName", + "target": "PolicyName" + }, + { + "source": "UserName", + "target": "UserName" + } + ], + "operation": "DeleteUserPolicy", + "phase": "delete", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::VirtualMFADevice", + "mappings": [ + { + "source": "Path", + "target": "Path" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VirtualMFADeviceName", + "target": "VirtualMfaDeviceName" + } + ], + "operation": "CreateVirtualMFADevice", + "phase": "create", + "service": "iam" + }, + { + "cfn_type": "AWS::IAM::VirtualMFADevice", + "mappings": [], + "operation": "DeleteVirtualMFADevice", + "phase": "delete", + "service": "iam" + }, + { + "cfn_type": "AWS::IVS::Channel", + "mappings": [ + { + "source": "authorized", + "target": "Authorized" + }, + { + "source": "containerFormat", + "target": "ContainerFormat" + }, + { + "source": "insecureIngest", + "target": "InsecureIngest" + }, + { + "source": "latencyMode", + "target": "LatencyMode" + }, + { + "source": "multitrackInputConfiguration", + "target": "MultitrackInputConfiguration" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "preset", + "target": "Preset" + }, + { + "source": "recordingConfigurationArn", + "target": "RecordingConfigurationArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateChannel", + "phase": "create", + "service": "ivs" + }, + { + "cfn_type": "AWS::IVS::Channel", + "mappings": [], + "operation": "DeleteChannel", + "phase": "delete", + "service": "ivs" + }, + { + "cfn_type": "AWS::IVS::EncoderConfiguration", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "video", + "target": "Video" + } + ], + "operation": "CreateEncoderConfiguration", + "phase": "create", + "service": "ivs-realtime" + }, + { + "cfn_type": "AWS::IVS::EncoderConfiguration", + "mappings": [], + "operation": "DeleteEncoderConfiguration", + "phase": "delete", + "service": "ivs-realtime" + }, + { + "cfn_type": "AWS::IVS::IngestConfiguration", + "mappings": [ + { + "source": "ingestProtocol", + "target": "IngestProtocol" + }, + { + "source": "insecureIngest", + "target": "InsecureIngest" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "stageArn", + "target": "StageArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "userId", + "target": "UserId" + } + ], + "operation": "CreateIngestConfiguration", + "phase": "create", + "service": "ivs-realtime" + }, + { + "cfn_type": "AWS::IVS::IngestConfiguration", + "mappings": [], + "operation": "DeleteIngestConfiguration", + "phase": "delete", + "service": "ivs-realtime" + }, + { + "cfn_type": "AWS::IVS::PlaybackKeyPair", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "publicKeyMaterial", + "target": "PublicKeyMaterial" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "ImportPlaybackKeyPair", + "phase": "create", + "service": "ivs" + }, + { + "cfn_type": "AWS::IVS::PlaybackKeyPair", + "mappings": [], + "operation": "DeletePlaybackKeyPair", + "phase": "delete", + "service": "ivs" + }, + { + "cfn_type": "AWS::IVS::PlaybackRestrictionPolicy", + "mappings": [ + { + "source": "allowedCountries", + "target": "AllowedCountries" + }, + { + "source": "allowedOrigins", + "target": "AllowedOrigins" + }, + { + "source": "enableStrictOriginEnforcement", + "target": "EnableStrictOriginEnforcement" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePlaybackRestrictionPolicy", + "phase": "create", + "service": "ivs" + }, + { + "cfn_type": "AWS::IVS::PlaybackRestrictionPolicy", + "mappings": [], + "operation": "DeletePlaybackRestrictionPolicy", + "phase": "delete", + "service": "ivs" + }, + { + "cfn_type": "AWS::IVS::PublicKey", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "publicKeyMaterial", + "target": "PublicKeyMaterial" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "ImportPublicKey", + "phase": "create", + "service": "ivs-realtime" + }, + { + "cfn_type": "AWS::IVS::PublicKey", + "mappings": [], + "operation": "DeletePublicKey", + "phase": "delete", + "service": "ivs-realtime" + }, + { + "cfn_type": "AWS::IVS::RecordingConfiguration", + "mappings": [ + { + "source": "destinationConfiguration", + "target": "DestinationConfiguration" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "recordingReconnectWindowSeconds", + "target": "RecordingReconnectWindowSeconds" + }, + { + "source": "renditionConfiguration", + "target": "RenditionConfiguration" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "thumbnailConfiguration", + "target": "ThumbnailConfiguration" + } + ], + "operation": "CreateRecordingConfiguration", + "phase": "create", + "service": "ivs" + }, + { + "cfn_type": "AWS::IVS::RecordingConfiguration", + "mappings": [], + "operation": "DeleteRecordingConfiguration", + "phase": "delete", + "service": "ivs" + }, + { + "cfn_type": "AWS::IVS::Stage", + "mappings": [ + { + "source": "autoParticipantRecordingConfiguration", + "target": "AutoParticipantRecordingConfiguration" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateStage", + "phase": "create", + "service": "ivs-realtime" + }, + { + "cfn_type": "AWS::IVS::Stage", + "mappings": [], + "operation": "DeleteStage", + "phase": "delete", + "service": "ivs-realtime" + }, + { + "cfn_type": "AWS::IVS::StorageConfiguration", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "s3", + "target": "S3" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateStorageConfiguration", + "phase": "create", + "service": "ivs-realtime" + }, + { + "cfn_type": "AWS::IVS::StorageConfiguration", + "mappings": [], + "operation": "DeleteStorageConfiguration", + "phase": "delete", + "service": "ivs-realtime" + }, + { + "cfn_type": "AWS::IVS::StreamKey", + "mappings": [ + { + "source": "channelArn", + "target": "ChannelArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateStreamKey", + "phase": "create", + "service": "ivs" + }, + { + "cfn_type": "AWS::IVS::StreamKey", + "mappings": [], + "operation": "DeleteStreamKey", + "phase": "delete", + "service": "ivs" + }, + { + "cfn_type": "AWS::IVSChat::LoggingConfiguration", + "mappings": [ + { + "source": "destinationConfiguration", + "target": "DestinationConfiguration" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateLoggingConfiguration", + "phase": "create", + "service": "ivschat" + }, + { + "cfn_type": "AWS::IVSChat::LoggingConfiguration", + "mappings": [], + "operation": "DeleteLoggingConfiguration", + "phase": "delete", + "service": "ivschat" + }, + { + "cfn_type": "AWS::IVSChat::Room", + "mappings": [ + { + "source": "loggingConfigurationIdentifiers", + "target": "LoggingConfigurationIdentifiers" + }, + { + "source": "maximumMessageLength", + "target": "MaximumMessageLength" + }, + { + "source": "maximumMessageRatePerSecond", + "target": "MaximumMessageRatePerSecond" + }, + { + "source": "messageReviewHandler", + "target": "MessageReviewHandler" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRoom", + "phase": "create", + "service": "ivschat" + }, + { + "cfn_type": "AWS::IVSChat::Room", + "mappings": [], + "operation": "DeleteRoom", + "phase": "delete", + "service": "ivschat" + }, + { + "cfn_type": "AWS::IdentityStore::Group", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "IdentityStoreId", + "target": "IdentityStoreId" + } + ], + "operation": "CreateGroup", + "phase": "create", + "service": "identitystore" + }, + { + "cfn_type": "AWS::IdentityStore::Group", + "mappings": [ + { + "source": "IdentityStoreId", + "target": "IdentityStoreId" + } + ], + "operation": "DeleteGroup", + "phase": "delete", + "service": "identitystore" + }, + { + "cfn_type": "AWS::IdentityStore::GroupMembership", + "mappings": [ + { + "source": "GroupId", + "target": "GroupId" + }, + { + "source": "IdentityStoreId", + "target": "IdentityStoreId" + }, + { + "source": "MemberId", + "target": "MemberId" + } + ], + "operation": "CreateGroupMembership", + "phase": "create", + "service": "identitystore" + }, + { + "cfn_type": "AWS::IdentityStore::GroupMembership", + "mappings": [ + { + "source": "IdentityStoreId", + "target": "IdentityStoreId" + } + ], + "operation": "DeleteGroupMembership", + "phase": "delete", + "service": "identitystore" + }, + { + "cfn_type": "AWS::ImageBuilder::Component", + "mappings": [ + { + "source": "changeDescription", + "target": "ChangeDescription" + }, + { + "source": "data", + "target": "Data" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "platform", + "target": "Platform" + }, + { + "source": "supportedOsVersions", + "target": "SupportedOsVersions" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "uri", + "target": "Uri" + } + ], + "operation": "CreateComponent", + "phase": "create", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::Component", + "mappings": [], + "operation": "DeleteComponent", + "phase": "delete", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::ContainerRecipe", + "mappings": [ + { + "source": "components", + "target": "Components" + }, + { + "source": "containerType", + "target": "ContainerType" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "dockerfileTemplateData", + "target": "DockerfileTemplateData" + }, + { + "source": "dockerfileTemplateUri", + "target": "DockerfileTemplateUri" + }, + { + "source": "imageOsVersionOverride", + "target": "ImageOsVersionOverride" + }, + { + "source": "instanceConfiguration", + "target": "InstanceConfiguration" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "parentImage", + "target": "ParentImage" + }, + { + "source": "platformOverride", + "target": "PlatformOverride" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetRepository", + "target": "TargetRepository" + }, + { + "source": "workingDirectory", + "target": "WorkingDirectory" + } + ], + "operation": "CreateContainerRecipe", + "phase": "create", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::ContainerRecipe", + "mappings": [], + "operation": "DeleteContainerRecipe", + "phase": "delete", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::DistributionConfiguration", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "distributions", + "target": "Distributions" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDistributionConfiguration", + "phase": "create", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::DistributionConfiguration", + "mappings": [], + "operation": "DeleteDistributionConfiguration", + "phase": "delete", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::Image", + "mappings": [ + { + "source": "containerRecipeArn", + "target": "ContainerRecipeArn" + }, + { + "source": "distributionConfigurationArn", + "target": "DistributionConfigurationArn" + }, + { + "source": "enhancedImageMetadataEnabled", + "target": "EnhancedImageMetadataEnabled" + }, + { + "source": "executionRole", + "target": "ExecutionRole" + }, + { + "source": "imageRecipeArn", + "target": "ImageRecipeArn" + }, + { + "source": "imageScanningConfiguration", + "target": "ImageScanningConfiguration" + }, + { + "source": "imageTestsConfiguration", + "target": "ImageTestsConfiguration" + }, + { + "source": "infrastructureConfigurationArn", + "target": "InfrastructureConfigurationArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workflows", + "target": "Workflows" + } + ], + "operation": "CreateImage", + "phase": "create", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::Image", + "mappings": [], + "operation": "DeleteImage", + "phase": "delete", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::ImagePipeline", + "mappings": [ + { + "source": "containerRecipeArn", + "target": "ContainerRecipeArn" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "distributionConfigurationArn", + "target": "DistributionConfigurationArn" + }, + { + "source": "enhancedImageMetadataEnabled", + "target": "EnhancedImageMetadataEnabled" + }, + { + "source": "executionRole", + "target": "ExecutionRole" + }, + { + "source": "imageRecipeArn", + "target": "ImageRecipeArn" + }, + { + "source": "imageScanningConfiguration", + "target": "ImageScanningConfiguration" + }, + { + "source": "imageTestsConfiguration", + "target": "ImageTestsConfiguration" + }, + { + "source": "infrastructureConfigurationArn", + "target": "InfrastructureConfigurationArn" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "schedule", + "target": "Schedule" + }, + { + "source": "status", + "target": "Status" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workflows", + "target": "Workflows" + } + ], + "operation": "CreateImagePipeline", + "phase": "create", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::ImagePipeline", + "mappings": [], + "operation": "DeleteImagePipeline", + "phase": "delete", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::ImageRecipe", + "mappings": [ + { + "source": "additionalInstanceConfiguration", + "target": "AdditionalInstanceConfiguration" + }, + { + "source": "blockDeviceMappings", + "target": "BlockDeviceMappings" + }, + { + "source": "components", + "target": "Components" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "parentImage", + "target": "ParentImage" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workingDirectory", + "target": "WorkingDirectory" + } + ], + "operation": "CreateImageRecipe", + "phase": "create", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::ImageRecipe", + "mappings": [], + "operation": "DeleteImageRecipe", + "phase": "delete", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::InfrastructureConfiguration", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "instanceMetadataOptions", + "target": "InstanceMetadataOptions" + }, + { + "source": "instanceProfileName", + "target": "InstanceProfileName" + }, + { + "source": "instanceTypes", + "target": "InstanceTypes" + }, + { + "source": "keyPair", + "target": "KeyPair" + }, + { + "source": "logging", + "target": "Logging" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "placement", + "target": "Placement" + }, + { + "source": "resourceTags", + "target": "ResourceTags" + }, + { + "source": "securityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "snsTopicArn", + "target": "SnsTopicArn" + }, + { + "source": "subnetId", + "target": "SubnetId" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "terminateInstanceOnFailure", + "target": "TerminateInstanceOnFailure" + } + ], + "operation": "CreateInfrastructureConfiguration", + "phase": "create", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::InfrastructureConfiguration", + "mappings": [], + "operation": "DeleteInfrastructureConfiguration", + "phase": "delete", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::LifecyclePolicy", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "executionRole", + "target": "ExecutionRole" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "policyDetails", + "target": "PolicyDetails" + }, + { + "source": "resourceSelection", + "target": "ResourceSelection" + }, + { + "source": "resourceType", + "target": "ResourceType" + }, + { + "source": "status", + "target": "Status" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateLifecyclePolicy", + "phase": "create", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::LifecyclePolicy", + "mappings": [], + "operation": "DeleteLifecyclePolicy", + "phase": "delete", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::Workflow", + "mappings": [ + { + "source": "changeDescription", + "target": "ChangeDescription" + }, + { + "source": "data", + "target": "Data" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + }, + { + "source": "uri", + "target": "Uri" + } + ], + "operation": "CreateWorkflow", + "phase": "create", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::ImageBuilder::Workflow", + "mappings": [], + "operation": "DeleteWorkflow", + "phase": "delete", + "service": "imagebuilder" + }, + { + "cfn_type": "AWS::Inspector::AssessmentTarget", + "mappings": [ + { + "source": "assessmentTargetName", + "target": "AssessmentTargetName" + }, + { + "source": "resourceGroupArn", + "target": "ResourceGroupArn" + } + ], + "operation": "CreateAssessmentTarget", + "phase": "create", + "service": "inspector" + }, + { + "cfn_type": "AWS::Inspector::AssessmentTarget", + "mappings": [], + "operation": "DeleteAssessmentTarget", + "phase": "delete", + "service": "inspector" + }, + { + "cfn_type": "AWS::Inspector::AssessmentTemplate", + "mappings": [ + { + "source": "assessmentTargetArn", + "target": "AssessmentTargetArn" + }, + { + "source": "assessmentTemplateName", + "target": "AssessmentTemplateName" + }, + { + "source": "durationInSeconds", + "target": "DurationInSeconds" + }, + { + "source": "rulesPackageArns", + "target": "RulesPackageArns" + }, + { + "source": "userAttributesForFindings", + "target": "UserAttributesForFindings" + } + ], + "operation": "CreateAssessmentTemplate", + "phase": "create", + "service": "inspector" + }, + { + "cfn_type": "AWS::Inspector::AssessmentTemplate", + "mappings": [], + "operation": "DeleteAssessmentTemplate", + "phase": "delete", + "service": "inspector" + }, + { + "cfn_type": "AWS::Inspector::ResourceGroup", + "mappings": [ + { + "source": "resourceGroupTags", + "target": "ResourceGroupTags" + } + ], + "operation": "CreateResourceGroup", + "phase": "create", + "service": "inspector" + }, + { + "cfn_type": "AWS::InternetMonitor::Monitor", + "mappings": [ + { + "source": "HealthEventsConfig", + "target": "HealthEventsConfig" + }, + { + "source": "InternetMeasurementsLogDelivery", + "target": "InternetMeasurementsLogDelivery" + }, + { + "source": "MaxCityNetworksToMonitor", + "target": "MaxCityNetworksToMonitor" + }, + { + "source": "MonitorName", + "target": "MonitorName" + }, + { + "source": "Resources", + "target": "Resources" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TrafficPercentageToMonitor", + "target": "TrafficPercentageToMonitor" + } + ], + "operation": "CreateMonitor", + "phase": "create", + "service": "internetmonitor" + }, + { + "cfn_type": "AWS::InternetMonitor::Monitor", + "mappings": [ + { + "source": "MonitorName", + "target": "MonitorName" + } + ], + "operation": "DeleteMonitor", + "phase": "delete", + "service": "internetmonitor" + }, + { + "cfn_type": "AWS::Invoicing::InvoiceUnit", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "InvoiceReceiver", + "target": "InvoiceReceiver" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ResourceTags", + "target": "ResourceTags" + }, + { + "source": "Rule", + "target": "Rule" + }, + { + "source": "TaxInheritanceDisabled", + "target": "TaxInheritanceDisabled" + } + ], + "operation": "CreateInvoiceUnit", + "phase": "create", + "service": "invoicing" + }, + { + "cfn_type": "AWS::Invoicing::InvoiceUnit", + "mappings": [], + "operation": "DeleteInvoiceUnit", + "phase": "delete", + "service": "invoicing" + }, + { + "cfn_type": "AWS::IoT::AccountAuditConfiguration", + "mappings": [], + "operation": "DeleteAccountAuditConfiguration", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Authorizer", + "mappings": [ + { + "source": "authorizerFunctionArn", + "target": "AuthorizerFunctionArn" + }, + { + "source": "authorizerName", + "target": "AuthorizerName" + }, + { + "source": "enableCachingForHttp", + "target": "EnableCachingForHttp" + }, + { + "source": "signingDisabled", + "target": "SigningDisabled" + }, + { + "source": "status", + "target": "Status" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "tokenKeyName", + "target": "TokenKeyName" + }, + { + "source": "tokenSigningPublicKeys", + "target": "TokenSigningPublicKeys" + } + ], + "operation": "CreateAuthorizer", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Authorizer", + "mappings": [ + { + "source": "authorizerName", + "target": "AuthorizerName" + } + ], + "operation": "DeleteAuthorizer", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::BillingGroup", + "mappings": [ + { + "source": "billingGroupName", + "target": "BillingGroupName" + }, + { + "source": "billingGroupProperties", + "target": "BillingGroupProperties" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateBillingGroup", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::BillingGroup", + "mappings": [ + { + "source": "billingGroupName", + "target": "BillingGroupName" + } + ], + "operation": "DeleteBillingGroup", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::CACertificate", + "mappings": [ + { + "source": "certificateMode", + "target": "CertificateMode" + }, + { + "source": "registrationConfig", + "target": "RegistrationConfig" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "RegisterCACertificate", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::CACertificate", + "mappings": [], + "operation": "DeleteCACertificate", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Certificate", + "mappings": [ + { + "source": "caCertificatePem", + "target": "CACertificatePem" + }, + { + "source": "certificatePem", + "target": "CertificatePem" + }, + { + "source": "status", + "target": "Status" + } + ], + "operation": "RegisterCertificate", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Certificate", + "mappings": [], + "operation": "DeleteCertificate", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::CertificateProvider", + "mappings": [ + { + "source": "accountDefaultForOperations", + "target": "AccountDefaultForOperations" + }, + { + "source": "certificateProviderName", + "target": "CertificateProviderName" + }, + { + "source": "lambdaFunctionArn", + "target": "LambdaFunctionArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCertificateProvider", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::CertificateProvider", + "mappings": [ + { + "source": "certificateProviderName", + "target": "CertificateProviderName" + } + ], + "operation": "DeleteCertificateProvider", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Command", + "mappings": [ + { + "source": "commandId", + "target": "CommandId" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "mandatoryParameters", + "target": "MandatoryParameters" + }, + { + "source": "namespace", + "target": "Namespace" + }, + { + "source": "payload", + "target": "Payload" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCommand", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Command", + "mappings": [ + { + "source": "commandId", + "target": "CommandId" + } + ], + "operation": "DeleteCommand", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::CustomMetric", + "mappings": [ + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "metricName", + "target": "MetricName" + }, + { + "source": "metricType", + "target": "MetricType" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCustomMetric", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::CustomMetric", + "mappings": [ + { + "source": "metricName", + "target": "MetricName" + } + ], + "operation": "DeleteCustomMetric", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Dimension", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "stringValues", + "target": "StringValues" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateDimension", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Dimension", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteDimension", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::DomainConfiguration", + "mappings": [ + { + "source": "applicationProtocol", + "target": "ApplicationProtocol" + }, + { + "source": "authenticationType", + "target": "AuthenticationType" + }, + { + "source": "authorizerConfig", + "target": "AuthorizerConfig" + }, + { + "source": "clientCertificateConfig", + "target": "ClientCertificateConfig" + }, + { + "source": "domainConfigurationName", + "target": "DomainConfigurationName" + }, + { + "source": "domainName", + "target": "DomainName" + }, + { + "source": "serverCertificateArns", + "target": "ServerCertificateArns" + }, + { + "source": "serverCertificateConfig", + "target": "ServerCertificateConfig" + }, + { + "source": "serviceType", + "target": "ServiceType" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "tlsConfig", + "target": "TlsConfig" + }, + { + "source": "validationCertificateArn", + "target": "ValidationCertificateArn" + } + ], + "operation": "CreateDomainConfiguration", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::DomainConfiguration", + "mappings": [ + { + "source": "domainConfigurationName", + "target": "DomainConfigurationName" + } + ], + "operation": "DeleteDomainConfiguration", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::FleetMetric", + "mappings": [ + { + "source": "aggregationField", + "target": "AggregationField" + }, + { + "source": "aggregationType", + "target": "AggregationType" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "indexName", + "target": "IndexName" + }, + { + "source": "metricName", + "target": "MetricName" + }, + { + "source": "period", + "target": "Period" + }, + { + "source": "queryString", + "target": "QueryString" + }, + { + "source": "queryVersion", + "target": "QueryVersion" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "unit", + "target": "Unit" + } + ], + "operation": "CreateFleetMetric", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::FleetMetric", + "mappings": [ + { + "source": "metricName", + "target": "MetricName" + } + ], + "operation": "DeleteFleetMetric", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::JobTemplate", + "mappings": [ + { + "source": "abortConfig", + "target": "AbortConfig" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "destinationPackageVersions", + "target": "DestinationPackageVersions" + }, + { + "source": "document", + "target": "Document" + }, + { + "source": "documentSource", + "target": "DocumentSource" + }, + { + "source": "jobArn", + "target": "JobArn" + }, + { + "source": "jobExecutionsRetryConfig", + "target": "JobExecutionsRetryConfig" + }, + { + "source": "jobExecutionsRolloutConfig", + "target": "JobExecutionsRolloutConfig" + }, + { + "source": "jobTemplateId", + "target": "JobTemplateId" + }, + { + "source": "maintenanceWindows", + "target": "MaintenanceWindows" + }, + { + "source": "presignedUrlConfig", + "target": "PresignedUrlConfig" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "timeoutConfig", + "target": "TimeoutConfig" + } + ], + "operation": "CreateJobTemplate", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::JobTemplate", + "mappings": [ + { + "source": "jobTemplateId", + "target": "JobTemplateId" + } + ], + "operation": "DeleteJobTemplate", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Logging", + "mappings": [ + { + "source": "defaultLogLevel", + "target": "DefaultLogLevel" + }, + { + "source": "roleArn", + "target": "RoleArn" + } + ], + "operation": "SetV2LoggingOptions", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::MitigationAction", + "mappings": [ + { + "source": "actionName", + "target": "ActionName" + }, + { + "source": "actionParams", + "target": "ActionParams" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateMitigationAction", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::MitigationAction", + "mappings": [ + { + "source": "actionName", + "target": "ActionName" + } + ], + "operation": "DeleteMitigationAction", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Policy", + "mappings": [ + { + "source": "policyDocument", + "target": "PolicyDocument" + }, + { + "source": "policyName", + "target": "PolicyName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePolicy", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Policy", + "mappings": [ + { + "source": "policyName", + "target": "PolicyName" + } + ], + "operation": "DeletePolicy", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::ProvisioningTemplate", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "enabled", + "target": "Enabled" + }, + { + "source": "preProvisioningHook", + "target": "PreProvisioningHook" + }, + { + "source": "provisioningRoleArn", + "target": "ProvisioningRoleArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "templateBody", + "target": "TemplateBody" + }, + { + "source": "templateName", + "target": "TemplateName" + } + ], + "operation": "CreateProvisioningTemplate", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::ProvisioningTemplate", + "mappings": [ + { + "source": "templateName", + "target": "TemplateName" + } + ], + "operation": "DeleteProvisioningTemplate", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::ResourceSpecificLogging", + "mappings": [ + { + "source": "targetName", + "target": "TargetName" + }, + { + "source": "targetType", + "target": "TargetType" + } + ], + "operation": "DeleteV2LoggingLevel", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::RoleAlias", + "mappings": [ + { + "source": "credentialDurationSeconds", + "target": "CredentialDurationSeconds" + }, + { + "source": "roleAlias", + "target": "RoleAlias" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRoleAlias", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::RoleAlias", + "mappings": [ + { + "source": "roleAlias", + "target": "RoleAlias" + } + ], + "operation": "DeleteRoleAlias", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::ScheduledAudit", + "mappings": [ + { + "source": "dayOfMonth", + "target": "DayOfMonth" + }, + { + "source": "dayOfWeek", + "target": "DayOfWeek" + }, + { + "source": "frequency", + "target": "Frequency" + }, + { + "source": "scheduledAuditName", + "target": "ScheduledAuditName" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetCheckNames", + "target": "TargetCheckNames" + } + ], + "operation": "CreateScheduledAudit", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::ScheduledAudit", + "mappings": [ + { + "source": "scheduledAuditName", + "target": "ScheduledAuditName" + } + ], + "operation": "DeleteScheduledAudit", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::SecurityProfile", + "mappings": [ + { + "source": "additionalMetricsToRetainV2", + "target": "AdditionalMetricsToRetainV2" + }, + { + "source": "alertTargets", + "target": "AlertTargets" + }, + { + "source": "behaviors", + "target": "Behaviors" + }, + { + "source": "metricsExportConfig", + "target": "MetricsExportConfig" + }, + { + "source": "securityProfileDescription", + "target": "SecurityProfileDescription" + }, + { + "source": "securityProfileName", + "target": "SecurityProfileName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSecurityProfile", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::SecurityProfile", + "mappings": [ + { + "source": "securityProfileName", + "target": "SecurityProfileName" + } + ], + "operation": "DeleteSecurityProfile", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::SoftwarePackage", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "packageName", + "target": "PackageName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePackage", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::SoftwarePackageVersion", + "mappings": [ + { + "source": "artifact", + "target": "Artifact" + }, + { + "source": "attributes", + "target": "Attributes" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "packageName", + "target": "PackageName" + }, + { + "source": "recipe", + "target": "Recipe" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "versionName", + "target": "VersionName" + } + ], + "operation": "CreatePackageVersion", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::SoftwarePackageVersion", + "mappings": [ + { + "source": "packageName", + "target": "PackageName" + }, + { + "source": "versionName", + "target": "VersionName" + } + ], + "operation": "DeletePackageVersion", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Thing", + "mappings": [ + { + "source": "attributePayload", + "target": "AttributePayload" + }, + { + "source": "thingName", + "target": "ThingName" + } + ], + "operation": "CreateThing", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Thing", + "mappings": [ + { + "source": "thingName", + "target": "ThingName" + } + ], + "operation": "DeleteThing", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::ThingGroup", + "mappings": [ + { + "source": "parentGroupName", + "target": "ParentGroupName" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "thingGroupName", + "target": "ThingGroupName" + }, + { + "source": "thingGroupProperties", + "target": "ThingGroupProperties" + } + ], + "operation": "CreateThingGroup", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::ThingType", + "mappings": [ + { + "source": "tags", + "target": "Tags" + }, + { + "source": "thingTypeName", + "target": "ThingTypeName" + }, + { + "source": "thingTypeProperties", + "target": "ThingTypeProperties" + } + ], + "operation": "CreateThingType", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::ThingType", + "mappings": [ + { + "source": "thingTypeName", + "target": "ThingTypeName" + } + ], + "operation": "DeleteThingType", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::TopicRule", + "mappings": [ + { + "source": "ruleName", + "target": "RuleName" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "topicRulePayload", + "target": "TopicRulePayload" + } + ], + "operation": "CreateTopicRule", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::TopicRule", + "mappings": [ + { + "source": "ruleName", + "target": "RuleName" + } + ], + "operation": "DeleteTopicRule", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::TopicRuleDestination", + "mappings": [], + "operation": "DeleteTopicRuleDestination", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoTAnalytics::Channel", + "mappings": [ + { + "source": "channelName", + "target": "ChannelName" + }, + { + "source": "channelStorage", + "target": "ChannelStorage" + }, + { + "source": "retentionPeriod", + "target": "RetentionPeriod" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateChannel", + "phase": "create", + "service": "iotanalytics" + }, + { + "cfn_type": "AWS::IoTAnalytics::Channel", + "mappings": [ + { + "source": "channelName", + "target": "ChannelName" + } + ], + "operation": "DeleteChannel", + "phase": "delete", + "service": "iotanalytics" + }, + { + "cfn_type": "AWS::IoTAnalytics::Dataset", + "mappings": [ + { + "source": "actions", + "target": "Actions" + }, + { + "source": "contentDeliveryRules", + "target": "ContentDeliveryRules" + }, + { + "source": "datasetName", + "target": "DatasetName" + }, + { + "source": "lateDataRules", + "target": "LateDataRules" + }, + { + "source": "retentionPeriod", + "target": "RetentionPeriod" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "triggers", + "target": "Triggers" + }, + { + "source": "versioningConfiguration", + "target": "VersioningConfiguration" + } + ], + "operation": "CreateDataset", + "phase": "create", + "service": "iotanalytics" + }, + { + "cfn_type": "AWS::IoTAnalytics::Dataset", + "mappings": [ + { + "source": "datasetName", + "target": "DatasetName" + } + ], + "operation": "DeleteDataset", + "phase": "delete", + "service": "iotanalytics" + }, + { + "cfn_type": "AWS::IoTAnalytics::Datastore", + "mappings": [ + { + "source": "datastoreName", + "target": "DatastoreName" + }, + { + "source": "datastorePartitions", + "target": "DatastorePartitions" + }, + { + "source": "datastoreStorage", + "target": "DatastoreStorage" + }, + { + "source": "fileFormatConfiguration", + "target": "FileFormatConfiguration" + }, + { + "source": "retentionPeriod", + "target": "RetentionPeriod" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDatastore", + "phase": "create", + "service": "iotanalytics" + }, + { + "cfn_type": "AWS::IoTAnalytics::Datastore", + "mappings": [ + { + "source": "datastoreName", + "target": "DatastoreName" + } + ], + "operation": "DeleteDatastore", + "phase": "delete", + "service": "iotanalytics" + }, + { + "cfn_type": "AWS::IoTAnalytics::Pipeline", + "mappings": [ + { + "source": "pipelineActivities", + "target": "PipelineActivities" + }, + { + "source": "pipelineName", + "target": "PipelineName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePipeline", + "phase": "create", + "service": "iotanalytics" + }, + { + "cfn_type": "AWS::IoTAnalytics::Pipeline", + "mappings": [ + { + "source": "pipelineName", + "target": "PipelineName" + } + ], + "operation": "DeletePipeline", + "phase": "delete", + "service": "iotanalytics" + }, + { + "cfn_type": "AWS::IoTEvents::AlarmModel", + "mappings": [ + { + "source": "alarmCapabilities", + "target": "AlarmCapabilities" + }, + { + "source": "alarmEventActions", + "target": "AlarmEventActions" + }, + { + "source": "alarmModelDescription", + "target": "AlarmModelDescription" + }, + { + "source": "alarmModelName", + "target": "AlarmModelName" + }, + { + "source": "alarmRule", + "target": "AlarmRule" + }, + { + "source": "key", + "target": "Key" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "severity", + "target": "Severity" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAlarmModel", + "phase": "create", + "service": "iotevents" + }, + { + "cfn_type": "AWS::IoTEvents::AlarmModel", + "mappings": [ + { + "source": "alarmModelName", + "target": "AlarmModelName" + } + ], + "operation": "DeleteAlarmModel", + "phase": "delete", + "service": "iotevents" + }, + { + "cfn_type": "AWS::IoTEvents::DetectorModel", + "mappings": [ + { + "source": "detectorModelDefinition", + "target": "DetectorModelDefinition" + }, + { + "source": "detectorModelDescription", + "target": "DetectorModelDescription" + }, + { + "source": "detectorModelName", + "target": "DetectorModelName" + }, + { + "source": "evaluationMethod", + "target": "EvaluationMethod" + }, + { + "source": "key", + "target": "Key" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDetectorModel", + "phase": "create", + "service": "iotevents" + }, + { + "cfn_type": "AWS::IoTEvents::DetectorModel", + "mappings": [ + { + "source": "detectorModelName", + "target": "DetectorModelName" + } + ], + "operation": "DeleteDetectorModel", + "phase": "delete", + "service": "iotevents" + }, + { + "cfn_type": "AWS::IoTEvents::Input", + "mappings": [ + { + "source": "inputDefinition", + "target": "InputDefinition" + }, + { + "source": "inputDescription", + "target": "InputDescription" + }, + { + "source": "inputName", + "target": "InputName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateInput", + "phase": "create", + "service": "iotevents" + }, + { + "cfn_type": "AWS::IoTEvents::Input", + "mappings": [ + { + "source": "inputName", + "target": "InputName" + } + ], + "operation": "DeleteInput", + "phase": "delete", + "service": "iotevents" + }, + { + "cfn_type": "AWS::IoTFleetWise::Campaign", + "mappings": [ + { + "source": "collectionScheme", + "target": "CollectionScheme" + }, + { + "source": "compression", + "target": "Compression" + }, + { + "source": "dataDestinationConfigs", + "target": "DataDestinationConfigs" + }, + { + "source": "dataExtraDimensions", + "target": "DataExtraDimensions" + }, + { + "source": "dataPartitions", + "target": "DataPartitions" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "diagnosticsMode", + "target": "DiagnosticsMode" + }, + { + "source": "expiryTime", + "target": "ExpiryTime" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "postTriggerCollectionDuration", + "target": "PostTriggerCollectionDuration" + }, + { + "source": "priority", + "target": "Priority" + }, + { + "source": "signalCatalogArn", + "target": "SignalCatalogArn" + }, + { + "source": "signalsToCollect", + "target": "SignalsToCollect" + }, + { + "source": "signalsToFetch", + "target": "SignalsToFetch" + }, + { + "source": "spoolingMode", + "target": "SpoolingMode" + }, + { + "source": "startTime", + "target": "StartTime" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetArn", + "target": "TargetArn" + } + ], + "operation": "CreateCampaign", + "phase": "create", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::Campaign", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteCampaign", + "phase": "delete", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::DecoderManifest", + "mappings": [ + { + "source": "defaultForUnmappedSignals", + "target": "DefaultForUnmappedSignals" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "modelManifestArn", + "target": "ModelManifestArn" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "networkInterfaces", + "target": "NetworkInterfaces" + }, + { + "source": "signalDecoders", + "target": "SignalDecoders" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDecoderManifest", + "phase": "create", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::DecoderManifest", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteDecoderManifest", + "phase": "delete", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::Fleet", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "signalCatalogArn", + "target": "SignalCatalogArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateFleet", + "phase": "create", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::Fleet", + "mappings": [], + "operation": "DeleteFleet", + "phase": "delete", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::ModelManifest", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "nodes", + "target": "Nodes" + }, + { + "source": "signalCatalogArn", + "target": "SignalCatalogArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateModelManifest", + "phase": "create", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::ModelManifest", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteModelManifest", + "phase": "delete", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::SignalCatalog", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "nodes", + "target": "Nodes" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSignalCatalog", + "phase": "create", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::SignalCatalog", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteSignalCatalog", + "phase": "delete", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::StateTemplate", + "mappings": [ + { + "source": "dataExtraDimensions", + "target": "DataExtraDimensions" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "metadataExtraDimensions", + "target": "MetadataExtraDimensions" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "signalCatalogArn", + "target": "SignalCatalogArn" + }, + { + "source": "stateTemplateProperties", + "target": "StateTemplateProperties" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateStateTemplate", + "phase": "create", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::StateTemplate", + "mappings": [], + "operation": "DeleteStateTemplate", + "phase": "delete", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::Vehicle", + "mappings": [ + { + "source": "associationBehavior", + "target": "AssociationBehavior" + }, + { + "source": "attributes", + "target": "Attributes" + }, + { + "source": "decoderManifestArn", + "target": "DecoderManifestArn" + }, + { + "source": "modelManifestArn", + "target": "ModelManifestArn" + }, + { + "source": "stateTemplates", + "target": "StateTemplates" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateVehicle", + "phase": "create", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTFleetWise::Vehicle", + "mappings": [], + "operation": "DeleteVehicle", + "phase": "delete", + "service": "iotfleetwise" + }, + { + "cfn_type": "AWS::IoTManagedIntegrations::CredentialLocker", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCredentialLocker", + "phase": "create", + "service": "iot-managed-integrations" + }, + { + "cfn_type": "AWS::IoTManagedIntegrations::CredentialLocker", + "mappings": [], + "operation": "DeleteCredentialLocker", + "phase": "delete", + "service": "iot-managed-integrations" + }, + { + "cfn_type": "AWS::IoTManagedIntegrations::ManagedThing", + "mappings": [ + { + "source": "AuthenticationMaterial", + "target": "AuthenticationMaterial" + }, + { + "source": "AuthenticationMaterialType", + "target": "AuthenticationMaterialType" + }, + { + "source": "Brand", + "target": "Brand" + }, + { + "source": "CapabilityReport", + "target": "CapabilityReport" + }, + { + "source": "Classification", + "target": "Classification" + }, + { + "source": "CredentialLockerId", + "target": "CredentialLockerId" + }, + { + "source": "MetaData", + "target": "MetaData" + }, + { + "source": "Model", + "target": "Model" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Owner", + "target": "Owner" + }, + { + "source": "Role", + "target": "Role" + }, + { + "source": "SerialNumber", + "target": "SerialNumber" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateManagedThing", + "phase": "create", + "service": "iot-managed-integrations" + }, + { + "cfn_type": "AWS::IoTManagedIntegrations::ManagedThing", + "mappings": [], + "operation": "DeleteManagedThing", + "phase": "delete", + "service": "iot-managed-integrations" + }, + { + "cfn_type": "AWS::IoTManagedIntegrations::ProvisioningProfile", + "mappings": [ + { + "source": "CaCertificate", + "target": "CaCertificate" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ProvisioningType", + "target": "ProvisioningType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateProvisioningProfile", + "phase": "create", + "service": "iot-managed-integrations" + }, + { + "cfn_type": "AWS::IoTManagedIntegrations::ProvisioningProfile", + "mappings": [], + "operation": "DeleteProvisioningProfile", + "phase": "delete", + "service": "iot-managed-integrations" + }, + { + "cfn_type": "AWS::IoTSiteWise::AccessPolicy", + "mappings": [ + { + "source": "accessPolicyIdentity", + "target": "AccessPolicyIdentity" + }, + { + "source": "accessPolicyPermission", + "target": "AccessPolicyPermission" + }, + { + "source": "accessPolicyResource", + "target": "AccessPolicyResource" + } + ], + "operation": "CreateAccessPolicy", + "phase": "create", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::AccessPolicy", + "mappings": [], + "operation": "DeleteAccessPolicy", + "phase": "delete", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Asset", + "mappings": [ + { + "source": "assetDescription", + "target": "AssetDescription" + }, + { + "source": "assetExternalId", + "target": "AssetExternalId" + }, + { + "source": "assetModelId", + "target": "AssetModelId" + }, + { + "source": "assetName", + "target": "AssetName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAsset", + "phase": "create", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::AssetModel", + "mappings": [ + { + "source": "assetModelCompositeModels", + "target": "AssetModelCompositeModels" + }, + { + "source": "assetModelDescription", + "target": "AssetModelDescription" + }, + { + "source": "assetModelExternalId", + "target": "AssetModelExternalId" + }, + { + "source": "assetModelHierarchies", + "target": "AssetModelHierarchies" + }, + { + "source": "assetModelName", + "target": "AssetModelName" + }, + { + "source": "assetModelProperties", + "target": "AssetModelProperties" + }, + { + "source": "assetModelType", + "target": "AssetModelType" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAssetModel", + "phase": "create", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::AssetModel", + "mappings": [], + "operation": "DeleteAssetModel", + "phase": "delete", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::ComputationModel", + "mappings": [ + { + "source": "computationModelConfiguration", + "target": "ComputationModelConfiguration" + }, + { + "source": "computationModelDataBinding", + "target": "ComputationModelDataBinding" + }, + { + "source": "computationModelDescription", + "target": "ComputationModelDescription" + }, + { + "source": "computationModelName", + "target": "ComputationModelName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateComputationModel", + "phase": "create", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::ComputationModel", + "mappings": [], + "operation": "DeleteComputationModel", + "phase": "delete", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Dashboard", + "mappings": [ + { + "source": "dashboardDefinition", + "target": "DashboardDefinition" + }, + { + "source": "dashboardDescription", + "target": "DashboardDescription" + }, + { + "source": "dashboardName", + "target": "DashboardName" + }, + { + "source": "projectId", + "target": "ProjectId" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDashboard", + "phase": "create", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Dashboard", + "mappings": [], + "operation": "DeleteDashboard", + "phase": "delete", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Dataset", + "mappings": [ + { + "source": "datasetDescription", + "target": "DatasetDescription" + }, + { + "source": "datasetName", + "target": "DatasetName" + }, + { + "source": "datasetSource", + "target": "DatasetSource" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDataset", + "phase": "create", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Dataset", + "mappings": [], + "operation": "DeleteDataset", + "phase": "delete", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Gateway", + "mappings": [ + { + "source": "gatewayName", + "target": "GatewayName" + }, + { + "source": "gatewayPlatform", + "target": "GatewayPlatform" + }, + { + "source": "gatewayVersion", + "target": "GatewayVersion" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateGateway", + "phase": "create", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Gateway", + "mappings": [], + "operation": "DeleteGateway", + "phase": "delete", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Portal", + "mappings": [ + { + "source": "alarms", + "target": "Alarms" + }, + { + "source": "notificationSenderEmail", + "target": "NotificationSenderEmail" + }, + { + "source": "portalAuthMode", + "target": "PortalAuthMode" + }, + { + "source": "portalContactEmail", + "target": "PortalContactEmail" + }, + { + "source": "portalDescription", + "target": "PortalDescription" + }, + { + "source": "portalName", + "target": "PortalName" + }, + { + "source": "portalType", + "target": "PortalType" + }, + { + "source": "portalTypeConfiguration", + "target": "PortalTypeConfiguration" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePortal", + "phase": "create", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Portal", + "mappings": [], + "operation": "DeletePortal", + "phase": "delete", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Project", + "mappings": [ + { + "source": "portalId", + "target": "PortalId" + }, + { + "source": "projectDescription", + "target": "ProjectDescription" + }, + { + "source": "projectName", + "target": "ProjectName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateProject", + "phase": "create", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Project", + "mappings": [], + "operation": "DeleteProject", + "phase": "delete", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTTwinMaker::ComponentType", + "mappings": [ + { + "source": "componentTypeId", + "target": "ComponentTypeId" + }, + { + "source": "compositeComponentTypes", + "target": "CompositeComponentTypes" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "extendsFrom", + "target": "ExtendsFrom" + }, + { + "source": "functions", + "target": "Functions" + }, + { + "source": "isSingleton", + "target": "IsSingleton" + }, + { + "source": "propertyDefinitions", + "target": "PropertyDefinitions" + }, + { + "source": "propertyGroups", + "target": "PropertyGroups" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workspaceId", + "target": "WorkspaceId" + } + ], + "operation": "CreateComponentType", + "phase": "create", + "service": "iottwinmaker" + }, + { + "cfn_type": "AWS::IoTTwinMaker::ComponentType", + "mappings": [ + { + "source": "componentTypeId", + "target": "ComponentTypeId" + }, + { + "source": "workspaceId", + "target": "WorkspaceId" + } + ], + "operation": "DeleteComponentType", + "phase": "delete", + "service": "iottwinmaker" + }, + { + "cfn_type": "AWS::IoTTwinMaker::Entity", + "mappings": [ + { + "source": "components", + "target": "Components" + }, + { + "source": "compositeComponents", + "target": "CompositeComponents" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "entityId", + "target": "EntityId" + }, + { + "source": "entityName", + "target": "EntityName" + }, + { + "source": "parentEntityId", + "target": "ParentEntityId" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workspaceId", + "target": "WorkspaceId" + } + ], + "operation": "CreateEntity", + "phase": "create", + "service": "iottwinmaker" + }, + { + "cfn_type": "AWS::IoTTwinMaker::Entity", + "mappings": [ + { + "source": "entityId", + "target": "EntityId" + }, + { + "source": "workspaceId", + "target": "WorkspaceId" + } + ], + "operation": "DeleteEntity", + "phase": "delete", + "service": "iottwinmaker" + }, + { + "cfn_type": "AWS::IoTTwinMaker::Scene", + "mappings": [ + { + "source": "capabilities", + "target": "Capabilities" + }, + { + "source": "contentLocation", + "target": "ContentLocation" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "sceneId", + "target": "SceneId" + }, + { + "source": "sceneMetadata", + "target": "SceneMetadata" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workspaceId", + "target": "WorkspaceId" + } + ], + "operation": "CreateScene", + "phase": "create", + "service": "iottwinmaker" + }, + { + "cfn_type": "AWS::IoTTwinMaker::Scene", + "mappings": [ + { + "source": "sceneId", + "target": "SceneId" + }, + { + "source": "workspaceId", + "target": "WorkspaceId" + } + ], + "operation": "DeleteScene", + "phase": "delete", + "service": "iottwinmaker" + }, + { + "cfn_type": "AWS::IoTTwinMaker::SyncJob", + "mappings": [ + { + "source": "syncRole", + "target": "SyncRole" + }, + { + "source": "syncSource", + "target": "SyncSource" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workspaceId", + "target": "WorkspaceId" + } + ], + "operation": "CreateSyncJob", + "phase": "create", + "service": "iottwinmaker" + }, + { + "cfn_type": "AWS::IoTTwinMaker::SyncJob", + "mappings": [ + { + "source": "syncSource", + "target": "SyncSource" + }, + { + "source": "workspaceId", + "target": "WorkspaceId" + } + ], + "operation": "DeleteSyncJob", + "phase": "delete", + "service": "iottwinmaker" + }, + { + "cfn_type": "AWS::IoTTwinMaker::Workspace", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "role", + "target": "Role" + }, + { + "source": "s3Location", + "target": "S3Location" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workspaceId", + "target": "WorkspaceId" + } + ], + "operation": "CreateWorkspace", + "phase": "create", + "service": "iottwinmaker" + }, + { + "cfn_type": "AWS::IoTTwinMaker::Workspace", + "mappings": [ + { + "source": "workspaceId", + "target": "WorkspaceId" + } + ], + "operation": "DeleteWorkspace", + "phase": "delete", + "service": "iottwinmaker" + }, + { + "cfn_type": "AWS::IoTWireless::Destination", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Expression", + "target": "Expression" + }, + { + "source": "ExpressionType", + "target": "ExpressionType" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDestination", + "phase": "create", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::Destination", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteDestination", + "phase": "delete", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::DeviceProfile", + "mappings": [ + { + "source": "LoRaWAN", + "target": "LoRaWAN" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDeviceProfile", + "phase": "create", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::DeviceProfile", + "mappings": [], + "operation": "DeleteDeviceProfile", + "phase": "delete", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::FuotaTask", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "FirmwareUpdateImage", + "target": "FirmwareUpdateImage" + }, + { + "source": "FirmwareUpdateRole", + "target": "FirmwareUpdateRole" + }, + { + "source": "LoRaWAN", + "target": "LoRaWAN" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateFuotaTask", + "phase": "create", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::FuotaTask", + "mappings": [], + "operation": "DeleteFuotaTask", + "phase": "delete", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::MulticastGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "LoRaWAN", + "target": "LoRaWAN" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateMulticastGroup", + "phase": "create", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::MulticastGroup", + "mappings": [], + "operation": "DeleteMulticastGroup", + "phase": "delete", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::NetworkAnalyzerConfiguration", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TraceContent", + "target": "TraceContent" + }, + { + "source": "WirelessDevices", + "target": "WirelessDevices" + }, + { + "source": "WirelessGateways", + "target": "WirelessGateways" + } + ], + "operation": "CreateNetworkAnalyzerConfiguration", + "phase": "create", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::NetworkAnalyzerConfiguration", + "mappings": [], + "operation": "DeleteNetworkAnalyzerConfiguration", + "phase": "delete", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::PartnerAccount", + "mappings": [ + { + "source": "Sidewalk", + "target": "Sidewalk" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "AssociateAwsAccountWithPartnerAccount", + "phase": "create", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::PartnerAccount", + "mappings": [ + { + "source": "PartnerAccountId", + "target": "PartnerAccountId" + }, + { + "source": "PartnerType", + "target": "PartnerType" + } + ], + "operation": "DisassociateAwsAccountFromPartnerAccount", + "phase": "delete", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::ServiceProfile", + "mappings": [ + { + "source": "LoRaWAN", + "target": "LoRaWAN" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateServiceProfile", + "phase": "create", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::ServiceProfile", + "mappings": [], + "operation": "DeleteServiceProfile", + "phase": "delete", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::TaskDefinition", + "mappings": [ + { + "source": "AutoCreateTasks", + "target": "AutoCreateTasks" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Update", + "target": "Update" + } + ], + "operation": "CreateWirelessGatewayTaskDefinition", + "phase": "create", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::TaskDefinition", + "mappings": [], + "operation": "DeleteWirelessGatewayTaskDefinition", + "phase": "delete", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::WirelessDevice", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DestinationName", + "target": "DestinationName" + }, + { + "source": "LoRaWAN", + "target": "LoRaWAN" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Positioning", + "target": "Positioning" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateWirelessDevice", + "phase": "create", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::WirelessDevice", + "mappings": [], + "operation": "DeleteWirelessDevice", + "phase": "delete", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::WirelessDeviceImportTask", + "mappings": [ + { + "source": "DestinationName", + "target": "DestinationName" + }, + { + "source": "Sidewalk", + "target": "Sidewalk" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "StartWirelessDeviceImportTask", + "phase": "create", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::WirelessDeviceImportTask", + "mappings": [], + "operation": "DeleteWirelessDeviceImportTask", + "phase": "delete", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::WirelessGateway", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "LoRaWAN", + "target": "LoRaWAN" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateWirelessGateway", + "phase": "create", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::IoTWireless::WirelessGateway", + "mappings": [], + "operation": "DeleteWirelessGateway", + "phase": "delete", + "service": "iotwireless" + }, + { + "cfn_type": "AWS::KMS::Alias", + "mappings": [ + { + "source": "AliasName", + "target": "AliasName" + }, + { + "source": "TargetKeyId", + "target": "TargetKeyId" + } + ], + "operation": "CreateAlias", + "phase": "create", + "service": "kms" + }, + { + "cfn_type": "AWS::KMS::Alias", + "mappings": [ + { + "source": "AliasName", + "target": "AliasName" + } + ], + "operation": "DeleteAlias", + "phase": "delete", + "service": "kms" + }, + { + "cfn_type": "AWS::KMS::Key", + "mappings": [ + { + "source": "BypassPolicyLockoutSafetyCheck", + "target": "BypassPolicyLockoutSafetyCheck" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "KeySpec", + "target": "KeySpec" + }, + { + "source": "KeyUsage", + "target": "KeyUsage" + }, + { + "source": "MultiRegion", + "target": "MultiRegion" + }, + { + "source": "Origin", + "target": "Origin" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateKey", + "phase": "create", + "service": "kms" + }, + { + "cfn_type": "AWS::KafkaConnect::Connector", + "mappings": [ + { + "source": "capacity", + "target": "Capacity" + }, + { + "source": "connectorConfiguration", + "target": "ConnectorConfiguration" + }, + { + "source": "connectorDescription", + "target": "ConnectorDescription" + }, + { + "source": "connectorName", + "target": "ConnectorName" + }, + { + "source": "kafkaCluster", + "target": "KafkaCluster" + }, + { + "source": "kafkaClusterClientAuthentication", + "target": "KafkaClusterClientAuthentication" + }, + { + "source": "kafkaClusterEncryptionInTransit", + "target": "KafkaClusterEncryptionInTransit" + }, + { + "source": "kafkaConnectVersion", + "target": "KafkaConnectVersion" + }, + { + "source": "logDelivery", + "target": "LogDelivery" + }, + { + "source": "plugins", + "target": "Plugins" + }, + { + "source": "serviceExecutionRoleArn", + "target": "ServiceExecutionRoleArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workerConfiguration", + "target": "WorkerConfiguration" + } + ], + "operation": "CreateConnector", + "phase": "create", + "service": "kafkaconnect" + }, + { + "cfn_type": "AWS::KafkaConnect::Connector", + "mappings": [], + "operation": "DeleteConnector", + "phase": "delete", + "service": "kafkaconnect" + }, + { + "cfn_type": "AWS::KafkaConnect::CustomPlugin", + "mappings": [ + { + "source": "contentType", + "target": "ContentType" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "location", + "target": "Location" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCustomPlugin", + "phase": "create", + "service": "kafkaconnect" + }, + { + "cfn_type": "AWS::KafkaConnect::CustomPlugin", + "mappings": [], + "operation": "DeleteCustomPlugin", + "phase": "delete", + "service": "kafkaconnect" + }, + { + "cfn_type": "AWS::KafkaConnect::WorkerConfiguration", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "propertiesFileContent", + "target": "PropertiesFileContent" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateWorkerConfiguration", + "phase": "create", + "service": "kafkaconnect" + }, + { + "cfn_type": "AWS::KafkaConnect::WorkerConfiguration", + "mappings": [], + "operation": "DeleteWorkerConfiguration", + "phase": "delete", + "service": "kafkaconnect" + }, + { + "cfn_type": "AWS::Kendra::DataSource", + "mappings": [ + { + "source": "CustomDocumentEnrichmentConfiguration", + "target": "CustomDocumentEnrichmentConfiguration" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "IndexId", + "target": "IndexId" + }, + { + "source": "LanguageCode", + "target": "LanguageCode" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Schedule", + "target": "Schedule" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateDataSource", + "phase": "create", + "service": "kendra" + }, + { + "cfn_type": "AWS::Kendra::DataSource", + "mappings": [ + { + "source": "IndexId", + "target": "IndexId" + } + ], + "operation": "DeleteDataSource", + "phase": "delete", + "service": "kendra" + }, + { + "cfn_type": "AWS::Kendra::Faq", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "FileFormat", + "target": "FileFormat" + }, + { + "source": "IndexId", + "target": "IndexId" + }, + { + "source": "LanguageCode", + "target": "LanguageCode" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "S3Path", + "target": "S3Path" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateFaq", + "phase": "create", + "service": "kendra" + }, + { + "cfn_type": "AWS::Kendra::Faq", + "mappings": [ + { + "source": "IndexId", + "target": "IndexId" + } + ], + "operation": "DeleteFaq", + "phase": "delete", + "service": "kendra" + }, + { + "cfn_type": "AWS::Kendra::Index", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Edition", + "target": "Edition" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "ServerSideEncryptionConfiguration", + "target": "ServerSideEncryptionConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UserContextPolicy", + "target": "UserContextPolicy" + }, + { + "source": "UserTokenConfigurations", + "target": "UserTokenConfigurations" + } + ], + "operation": "CreateIndex", + "phase": "create", + "service": "kendra" + }, + { + "cfn_type": "AWS::Kendra::Index", + "mappings": [], + "operation": "DeleteIndex", + "phase": "delete", + "service": "kendra" + }, + { + "cfn_type": "AWS::KendraRanking::ExecutionPlan", + "mappings": [ + { + "source": "CapacityUnits", + "target": "CapacityUnits" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRescoreExecutionPlan", + "phase": "create", + "service": "kendra-ranking" + }, + { + "cfn_type": "AWS::KendraRanking::ExecutionPlan", + "mappings": [], + "operation": "DeleteRescoreExecutionPlan", + "phase": "delete", + "service": "kendra-ranking" + }, + { + "cfn_type": "AWS::Kinesis::ResourcePolicy", + "mappings": [ + { + "source": "ResourceARN", + "target": "ResourceArn" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "kinesis" + }, + { + "cfn_type": "AWS::Kinesis::ResourcePolicy", + "mappings": [ + { + "source": "ResourceARN", + "target": "ResourceArn" + } + ], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "kinesis" + }, + { + "cfn_type": "AWS::Kinesis::Stream", + "mappings": [ + { + "source": "ShardCount", + "target": "ShardCount" + }, + { + "source": "StreamModeDetails", + "target": "StreamModeDetails" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateStream", + "phase": "create", + "service": "kinesis" + }, + { + "cfn_type": "AWS::Kinesis::StreamConsumer", + "mappings": [ + { + "source": "ConsumerName", + "target": "ConsumerName" + }, + { + "source": "StreamARN", + "target": "StreamARN" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "RegisterStreamConsumer", + "phase": "create", + "service": "kinesis" + }, + { + "cfn_type": "AWS::Kinesis::StreamConsumer", + "mappings": [ + { + "source": "ConsumerName", + "target": "ConsumerName" + }, + { + "source": "StreamARN", + "target": "StreamARN" + } + ], + "operation": "DeregisterStreamConsumer", + "phase": "delete", + "service": "kinesis" + }, + { + "cfn_type": "AWS::KinesisAnalyticsV2::Application", + "mappings": [ + { + "source": "ApplicationConfiguration", + "target": "ApplicationConfiguration" + }, + { + "source": "ApplicationDescription", + "target": "ApplicationDescription" + }, + { + "source": "ApplicationMode", + "target": "ApplicationMode" + }, + { + "source": "ApplicationName", + "target": "ApplicationName" + }, + { + "source": "RuntimeEnvironment", + "target": "RuntimeEnvironment" + }, + { + "source": "ServiceExecutionRole", + "target": "ServiceExecutionRole" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "kinesisanalyticsv2" + }, + { + "cfn_type": "AWS::KinesisAnalyticsV2::Application", + "mappings": [ + { + "source": "ApplicationName", + "target": "ApplicationName" + } + ], + "operation": "DeleteApplication", + "phase": "delete", + "service": "kinesisanalyticsv2" + }, + { + "cfn_type": "AWS::KinesisFirehose::DeliveryStream", + "mappings": [ + { + "source": "AmazonOpenSearchServerlessDestinationConfiguration", + "target": "AmazonOpenSearchServerlessDestinationConfiguration" + }, + { + "source": "AmazonopensearchserviceDestinationConfiguration", + "target": "AmazonopensearchserviceDestinationConfiguration" + }, + { + "source": "DatabaseSourceConfiguration", + "target": "DatabaseSourceConfiguration" + }, + { + "source": "DeliveryStreamEncryptionConfigurationInput", + "target": "DeliveryStreamEncryptionConfigurationInput" + }, + { + "source": "DeliveryStreamName", + "target": "DeliveryStreamName" + }, + { + "source": "DeliveryStreamType", + "target": "DeliveryStreamType" + }, + { + "source": "DirectPutSourceConfiguration", + "target": "DirectPutSourceConfiguration" + }, + { + "source": "ElasticsearchDestinationConfiguration", + "target": "ElasticsearchDestinationConfiguration" + }, + { + "source": "ExtendedS3DestinationConfiguration", + "target": "ExtendedS3DestinationConfiguration" + }, + { + "source": "HttpEndpointDestinationConfiguration", + "target": "HttpEndpointDestinationConfiguration" + }, + { + "source": "IcebergDestinationConfiguration", + "target": "IcebergDestinationConfiguration" + }, + { + "source": "KinesisStreamSourceConfiguration", + "target": "KinesisStreamSourceConfiguration" + }, + { + "source": "MSKSourceConfiguration", + "target": "MSKSourceConfiguration" + }, + { + "source": "RedshiftDestinationConfiguration", + "target": "RedshiftDestinationConfiguration" + }, + { + "source": "S3DestinationConfiguration", + "target": "S3DestinationConfiguration" + }, + { + "source": "SnowflakeDestinationConfiguration", + "target": "SnowflakeDestinationConfiguration" + }, + { + "source": "SplunkDestinationConfiguration", + "target": "SplunkDestinationConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDeliveryStream", + "phase": "create", + "service": "firehose" + }, + { + "cfn_type": "AWS::KinesisFirehose::DeliveryStream", + "mappings": [ + { + "source": "DeliveryStreamName", + "target": "DeliveryStreamName" + } + ], + "operation": "DeleteDeliveryStream", + "phase": "delete", + "service": "firehose" + }, + { + "cfn_type": "AWS::KinesisVideo::SignalingChannel", + "mappings": [ + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSignalingChannel", + "phase": "create", + "service": "kinesisvideo" + }, + { + "cfn_type": "AWS::KinesisVideo::SignalingChannel", + "mappings": [], + "operation": "DeleteSignalingChannel", + "phase": "delete", + "service": "kinesisvideo" + }, + { + "cfn_type": "AWS::KinesisVideo::Stream", + "mappings": [ + { + "source": "DataRetentionInHours", + "target": "DataRetentionInHours" + }, + { + "source": "DeviceName", + "target": "DeviceName" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "MediaType", + "target": "MediaType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateStream", + "phase": "create", + "service": "kinesisvideo" + }, + { + "cfn_type": "AWS::KinesisVideo::Stream", + "mappings": [], + "operation": "DeleteStream", + "phase": "delete", + "service": "kinesisvideo" + }, + { + "cfn_type": "AWS::LakeFormation::DataCellsFilter", + "mappings": [ + { + "source": "DatabaseName", + "target": "DatabaseName" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "TableCatalogId", + "target": "TableCatalogId" + }, + { + "source": "TableName", + "target": "TableName" + } + ], + "operation": "DeleteDataCellsFilter", + "phase": "delete", + "service": "lakeformation" + }, + { + "cfn_type": "AWS::LakeFormation::PrincipalPermissions", + "mappings": [ + { + "source": "Permissions", + "target": "Permissions" + }, + { + "source": "PermissionsWithGrantOption", + "target": "PermissionsWithGrantOption" + }, + { + "source": "Principal", + "target": "Principal" + }, + { + "source": "Resource", + "target": "Resource" + } + ], + "operation": "GrantPermissions", + "phase": "create", + "service": "lakeformation" + }, + { + "cfn_type": "AWS::LakeFormation::PrincipalPermissions", + "mappings": [ + { + "source": "Permissions", + "target": "Permissions" + }, + { + "source": "PermissionsWithGrantOption", + "target": "PermissionsWithGrantOption" + }, + { + "source": "Principal", + "target": "Principal" + }, + { + "source": "Resource", + "target": "Resource" + } + ], + "operation": "RevokePermissions", + "phase": "delete", + "service": "lakeformation" + }, + { + "cfn_type": "AWS::LakeFormation::Tag", + "mappings": [ + { + "source": "CatalogId", + "target": "CatalogId" + }, + { + "source": "TagKey", + "target": "TagKey" + }, + { + "source": "TagValues", + "target": "TagValues" + } + ], + "operation": "CreateLFTag", + "phase": "create", + "service": "lakeformation" + }, + { + "cfn_type": "AWS::LakeFormation::Tag", + "mappings": [ + { + "source": "CatalogId", + "target": "CatalogId" + }, + { + "source": "TagKey", + "target": "TagKey" + } + ], + "operation": "DeleteLFTag", + "phase": "delete", + "service": "lakeformation" + }, + { + "cfn_type": "AWS::LakeFormation::TagAssociation", + "mappings": [ + { + "source": "LFTags", + "target": "LFTags" + }, + { + "source": "Resource", + "target": "Resource" + } + ], + "operation": "AddLFTagsToResource", + "phase": "create", + "service": "lakeformation" + }, + { + "cfn_type": "AWS::LakeFormation::TagAssociation", + "mappings": [ + { + "source": "LFTags", + "target": "LFTags" + }, + { + "source": "Resource", + "target": "Resource" + } + ], + "operation": "RemoveLFTagsFromResource", + "phase": "delete", + "service": "lakeformation" + }, + { + "cfn_type": "AWS::Lambda::Alias", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "FunctionName", + "target": "FunctionName" + }, + { + "source": "FunctionVersion", + "target": "FunctionVersion" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoutingConfig", + "target": "RoutingConfig" + } + ], + "operation": "CreateAlias", + "phase": "create", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::Alias", + "mappings": [ + { + "source": "FunctionName", + "target": "FunctionName" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteAlias", + "phase": "delete", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::CodeSigningConfig", + "mappings": [ + { + "source": "AllowedPublishers", + "target": "AllowedPublishers" + }, + { + "source": "CodeSigningPolicies", + "target": "CodeSigningPolicies" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCodeSigningConfig", + "phase": "create", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::CodeSigningConfig", + "mappings": [], + "operation": "DeleteCodeSigningConfig", + "phase": "delete", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::EventInvokeConfig", + "mappings": [ + { + "source": "DestinationConfig", + "target": "DestinationConfig" + }, + { + "source": "FunctionName", + "target": "FunctionName" + }, + { + "source": "MaximumEventAgeInSeconds", + "target": "MaximumEventAgeInSeconds" + }, + { + "source": "MaximumRetryAttempts", + "target": "MaximumRetryAttempts" + }, + { + "source": "Qualifier", + "target": "Qualifier" + } + ], + "operation": "PutFunctionEventInvokeConfig", + "phase": "create", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::EventInvokeConfig", + "mappings": [ + { + "source": "FunctionName", + "target": "FunctionName" + }, + { + "source": "Qualifier", + "target": "Qualifier" + } + ], + "operation": "DeleteFunctionEventInvokeConfig", + "phase": "delete", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::EventSourceMapping", + "mappings": [ + { + "source": "AmazonManagedKafkaEventSourceConfig", + "target": "AmazonManagedKafkaEventSourceConfig" + }, + { + "source": "BatchSize", + "target": "BatchSize" + }, + { + "source": "BisectBatchOnFunctionError", + "target": "BisectBatchOnFunctionError" + }, + { + "source": "DestinationConfig", + "target": "DestinationConfig" + }, + { + "source": "DocumentDBEventSourceConfig", + "target": "DocumentDBEventSourceConfig" + }, + { + "source": "Enabled", + "target": "Enabled" + }, + { + "source": "EventSourceArn", + "target": "EventSourceArn" + }, + { + "source": "FilterCriteria", + "target": "FilterCriteria" + }, + { + "source": "FunctionName", + "target": "FunctionName" + }, + { + "source": "FunctionResponseTypes", + "target": "FunctionResponseTypes" + }, + { + "source": "KMSKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "MaximumBatchingWindowInSeconds", + "target": "MaximumBatchingWindowInSeconds" + }, + { + "source": "MaximumRecordAgeInSeconds", + "target": "MaximumRecordAgeInSeconds" + }, + { + "source": "MaximumRetryAttempts", + "target": "MaximumRetryAttempts" + }, + { + "source": "MetricsConfig", + "target": "MetricsConfig" + }, + { + "source": "ParallelizationFactor", + "target": "ParallelizationFactor" + }, + { + "source": "ProvisionedPollerConfig", + "target": "ProvisionedPollerConfig" + }, + { + "source": "Queues", + "target": "Queues" + }, + { + "source": "ScalingConfig", + "target": "ScalingConfig" + }, + { + "source": "SelfManagedEventSource", + "target": "SelfManagedEventSource" + }, + { + "source": "SelfManagedKafkaEventSourceConfig", + "target": "SelfManagedKafkaEventSourceConfig" + }, + { + "source": "SourceAccessConfigurations", + "target": "SourceAccessConfigurations" + }, + { + "source": "StartingPosition", + "target": "StartingPosition" + }, + { + "source": "StartingPositionTimestamp", + "target": "StartingPositionTimestamp" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Topics", + "target": "Topics" + }, + { + "source": "TumblingWindowInSeconds", + "target": "TumblingWindowInSeconds" + } + ], + "operation": "CreateEventSourceMapping", + "phase": "create", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::EventSourceMapping", + "mappings": [], + "operation": "DeleteEventSourceMapping", + "phase": "delete", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::Function", + "mappings": [ + { + "source": "Architectures", + "target": "Architectures" + }, + { + "source": "Code", + "target": "Code" + }, + { + "source": "CodeSigningConfigArn", + "target": "CodeSigningConfigArn" + }, + { + "source": "DeadLetterConfig", + "target": "DeadLetterConfig" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Environment", + "target": "Environment" + }, + { + "source": "EphemeralStorage", + "target": "EphemeralStorage" + }, + { + "source": "FileSystemConfigs", + "target": "FileSystemConfigs" + }, + { + "source": "FunctionName", + "target": "FunctionName" + }, + { + "source": "Handler", + "target": "Handler" + }, + { + "source": "ImageConfig", + "target": "ImageConfig" + }, + { + "source": "KMSKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "Layers", + "target": "Layers" + }, + { + "source": "LoggingConfig", + "target": "LoggingConfig" + }, + { + "source": "MemorySize", + "target": "MemorySize" + }, + { + "source": "PackageType", + "target": "PackageType" + }, + { + "source": "Role", + "target": "Role" + }, + { + "source": "Runtime", + "target": "Runtime" + }, + { + "source": "SnapStart", + "target": "SnapStart" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Timeout", + "target": "Timeout" + }, + { + "source": "TracingConfig", + "target": "TracingConfig" + }, + { + "source": "VpcConfig", + "target": "VpcConfig" + } + ], + "operation": "CreateFunction", + "phase": "create", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::Function", + "mappings": [ + { + "source": "FunctionName", + "target": "FunctionName" + } + ], + "operation": "DeleteFunction", + "phase": "delete", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::Function", + "mappings": [ + { + "source": "Runtime", + "target": "Runtime" + }, + { + "source": "Role", + "target": "Role" + }, + { + "source": "Handler", + "target": "Handler" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Timeout", + "target": "Timeout" + }, + { + "source": "MemorySize", + "target": "MemorySize" + } + ], + "operation": "UpdateFunctionConfiguration", + "phase": "update", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::LayerVersion", + "mappings": [ + { + "source": "CompatibleArchitectures", + "target": "CompatibleArchitectures" + }, + { + "source": "CompatibleRuntimes", + "target": "CompatibleRuntimes" + }, + { + "source": "Content", + "target": "Content" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "LayerName", + "target": "LayerName" + }, + { + "source": "LicenseInfo", + "target": "LicenseInfo" + } + ], + "operation": "PublishLayerVersion", + "phase": "create", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::LayerVersion", + "mappings": [ + { + "source": "LayerName", + "target": "LayerName" + } + ], + "operation": "DeleteLayerVersion", + "phase": "delete", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::LayerVersionPermission", + "mappings": [ + { + "source": "Action", + "target": "Action" + }, + { + "source": "OrganizationId", + "target": "OrganizationId" + }, + { + "source": "Principal", + "target": "Principal" + } + ], + "operation": "AddLayerVersionPermission", + "phase": "create", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::LayerVersionPermission", + "mappings": [], + "operation": "RemoveLayerVersionPermission", + "phase": "delete", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::Permission", + "mappings": [ + { + "source": "Action", + "target": "Action" + }, + { + "source": "EventSourceToken", + "target": "EventSourceToken" + }, + { + "source": "FunctionName", + "target": "FunctionName" + }, + { + "source": "FunctionUrlAuthType", + "target": "FunctionUrlAuthType" + }, + { + "source": "Principal", + "target": "Principal" + }, + { + "source": "PrincipalOrgID", + "target": "PrincipalOrgID" + }, + { + "source": "SourceAccount", + "target": "SourceAccount" + }, + { + "source": "SourceArn", + "target": "SourceArn" + } + ], + "operation": "AddPermission", + "phase": "create", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::Permission", + "mappings": [ + { + "source": "FunctionName", + "target": "FunctionName" + } + ], + "operation": "RemovePermission", + "phase": "delete", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::Url", + "mappings": [ + { + "source": "AuthType", + "target": "AuthType" + }, + { + "source": "Cors", + "target": "Cors" + }, + { + "source": "InvokeMode", + "target": "InvokeMode" + }, + { + "source": "Qualifier", + "target": "Qualifier" + } + ], + "operation": "CreateFunctionUrlConfig", + "phase": "create", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::Version", + "mappings": [ + { + "source": "CodeSha256", + "target": "CodeSha256" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "FunctionName", + "target": "FunctionName" + } + ], + "operation": "PublishVersion", + "phase": "create", + "service": "lambda" + }, + { + "cfn_type": "AWS::LaunchWizard::Deployment", + "mappings": [ + { + "source": "deploymentPatternName", + "target": "DeploymentPatternName" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "specifications", + "target": "Specifications" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workloadName", + "target": "WorkloadName" + } + ], + "operation": "CreateDeployment", + "phase": "create", + "service": "launch-wizard" + }, + { + "cfn_type": "AWS::LaunchWizard::Deployment", + "mappings": [], + "operation": "DeleteDeployment", + "phase": "delete", + "service": "launch-wizard" + }, + { + "cfn_type": "AWS::Lex::Bot", + "mappings": [ + { + "source": "botMembers", + "target": "BotMembers" + }, + { + "source": "botTags", + "target": "BotTags" + }, + { + "source": "botType", + "target": "BotType" + }, + { + "source": "dataPrivacy", + "target": "DataPrivacy" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "errorLogSettings", + "target": "ErrorLogSettings" + }, + { + "source": "idleSessionTTLInSeconds", + "target": "IdleSessionTTLInSeconds" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "testBotAliasTags", + "target": "TestBotAliasTags" + } + ], + "operation": "CreateBot", + "phase": "create", + "service": "lexv2-models" + }, + { + "cfn_type": "AWS::Lex::Bot", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteBot", + "phase": "delete", + "service": "lex-models" + }, + { + "cfn_type": "AWS::Lex::BotAlias", + "mappings": [ + { + "source": "botAliasLocaleSettings", + "target": "BotAliasLocaleSettings" + }, + { + "source": "botAliasName", + "target": "BotAliasName" + }, + { + "source": "botId", + "target": "BotId" + }, + { + "source": "botVersion", + "target": "BotVersion" + }, + { + "source": "conversationLogSettings", + "target": "ConversationLogSettings" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "sentimentAnalysisSettings", + "target": "SentimentAnalysisSettings" + } + ], + "operation": "CreateBotAlias", + "phase": "create", + "service": "lexv2-models" + }, + { + "cfn_type": "AWS::Lex::BotAlias", + "mappings": [ + { + "source": "name", + "target": "BotAliasName" + } + ], + "operation": "DeleteBotAlias", + "phase": "delete", + "service": "lex-models" + }, + { + "cfn_type": "AWS::Lex::BotVersion", + "mappings": [ + { + "source": "botId", + "target": "BotId" + }, + { + "source": "botVersionLocaleSpecification", + "target": "BotVersionLocaleSpecification" + }, + { + "source": "description", + "target": "Description" + } + ], + "operation": "CreateBotVersion", + "phase": "create", + "service": "lexv2-models" + }, + { + "cfn_type": "AWS::Lex::BotVersion", + "mappings": [ + { + "source": "botId", + "target": "BotId" + } + ], + "operation": "DeleteBotVersion", + "phase": "delete", + "service": "lexv2-models" + }, + { + "cfn_type": "AWS::Lex::ResourcePolicy", + "mappings": [ + { + "source": "policy", + "target": "Policy" + }, + { + "source": "resourceArn", + "target": "ResourceArn" + } + ], + "operation": "CreateResourcePolicy", + "phase": "create", + "service": "lexv2-models" + }, + { + "cfn_type": "AWS::Lex::ResourcePolicy", + "mappings": [ + { + "source": "resourceArn", + "target": "ResourceArn" + } + ], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "lexv2-models" + }, + { + "cfn_type": "AWS::LicenseManager::Grant", + "mappings": [ + { + "source": "AllowedOperations", + "target": "AllowedOperations" + }, + { + "source": "GrantName", + "target": "GrantName" + }, + { + "source": "HomeRegion", + "target": "HomeRegion" + }, + { + "source": "LicenseArn", + "target": "LicenseArn" + }, + { + "source": "Principals", + "target": "Principals" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateGrant", + "phase": "create", + "service": "license-manager" + }, + { + "cfn_type": "AWS::LicenseManager::Grant", + "mappings": [], + "operation": "DeleteGrant", + "phase": "delete", + "service": "license-manager" + }, + { + "cfn_type": "AWS::LicenseManager::License", + "mappings": [ + { + "source": "Beneficiary", + "target": "Beneficiary" + }, + { + "source": "ConsumptionConfiguration", + "target": "ConsumptionConfiguration" + }, + { + "source": "Entitlements", + "target": "Entitlements" + }, + { + "source": "HomeRegion", + "target": "HomeRegion" + }, + { + "source": "Issuer", + "target": "Issuer" + }, + { + "source": "LicenseMetadata", + "target": "LicenseMetadata" + }, + { + "source": "LicenseName", + "target": "LicenseName" + }, + { + "source": "ProductName", + "target": "ProductName" + }, + { + "source": "ProductSKU", + "target": "ProductSKU" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Validity", + "target": "Validity" + } + ], + "operation": "CreateLicense", + "phase": "create", + "service": "license-manager" + }, + { + "cfn_type": "AWS::LicenseManager::License", + "mappings": [], + "operation": "DeleteLicense", + "phase": "delete", + "service": "license-manager" + }, + { + "cfn_type": "AWS::Lightsail::Alarm", + "mappings": [ + { + "source": "alarmName", + "target": "AlarmName" + }, + { + "source": "comparisonOperator", + "target": "ComparisonOperator" + }, + { + "source": "contactProtocols", + "target": "ContactProtocols" + }, + { + "source": "datapointsToAlarm", + "target": "DatapointsToAlarm" + }, + { + "source": "evaluationPeriods", + "target": "EvaluationPeriods" + }, + { + "source": "metricName", + "target": "MetricName" + }, + { + "source": "monitoredResourceName", + "target": "MonitoredResourceName" + }, + { + "source": "notificationEnabled", + "target": "NotificationEnabled" + }, + { + "source": "notificationTriggers", + "target": "NotificationTriggers" + }, + { + "source": "threshold", + "target": "Threshold" + }, + { + "source": "treatMissingData", + "target": "TreatMissingData" + } + ], + "operation": "PutAlarm", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Alarm", + "mappings": [ + { + "source": "alarmName", + "target": "AlarmName" + } + ], + "operation": "DeleteAlarm", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Bucket", + "mappings": [ + { + "source": "bucketName", + "target": "BucketName" + }, + { + "source": "bundleId", + "target": "BundleId" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateBucket", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Bucket", + "mappings": [ + { + "source": "bucketName", + "target": "BucketName" + } + ], + "operation": "DeleteBucket", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Certificate", + "mappings": [ + { + "source": "certificateName", + "target": "CertificateName" + }, + { + "source": "domainName", + "target": "DomainName" + }, + { + "source": "subjectAlternativeNames", + "target": "SubjectAlternativeNames" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCertificate", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Certificate", + "mappings": [ + { + "source": "certificateName", + "target": "CertificateName" + } + ], + "operation": "DeleteCertificate", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Container", + "mappings": [ + { + "source": "power", + "target": "Power" + }, + { + "source": "privateRegistryAccess", + "target": "PrivateRegistryAccess" + }, + { + "source": "publicDomainNames", + "target": "PublicDomainNames" + }, + { + "source": "scale", + "target": "Scale" + }, + { + "source": "serviceName", + "target": "ServiceName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateContainerService", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Database", + "mappings": [ + { + "source": "availabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "masterDatabaseName", + "target": "MasterDatabaseName" + }, + { + "source": "masterUserPassword", + "target": "MasterUserPassword" + }, + { + "source": "masterUsername", + "target": "MasterUsername" + }, + { + "source": "preferredBackupWindow", + "target": "PreferredBackupWindow" + }, + { + "source": "preferredMaintenanceWindow", + "target": "PreferredMaintenanceWindow" + }, + { + "source": "publiclyAccessible", + "target": "PubliclyAccessible" + }, + { + "source": "relationalDatabaseBlueprintId", + "target": "RelationalDatabaseBlueprintId" + }, + { + "source": "relationalDatabaseBundleId", + "target": "RelationalDatabaseBundleId" + }, + { + "source": "relationalDatabaseName", + "target": "RelationalDatabaseName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRelationalDatabase", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Database", + "mappings": [ + { + "source": "relationalDatabaseName", + "target": "RelationalDatabaseName" + } + ], + "operation": "DeleteRelationalDatabase", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::DatabaseSnapshot", + "mappings": [ + { + "source": "relationalDatabaseName", + "target": "RelationalDatabaseName" + }, + { + "source": "relationalDatabaseSnapshotName", + "target": "RelationalDatabaseSnapshotName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRelationalDatabaseSnapshot", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::DatabaseSnapshot", + "mappings": [ + { + "source": "relationalDatabaseSnapshotName", + "target": "RelationalDatabaseSnapshotName" + } + ], + "operation": "DeleteRelationalDatabaseSnapshot", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Disk", + "mappings": [ + { + "source": "addOns", + "target": "AddOns" + }, + { + "source": "availabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "diskName", + "target": "DiskName" + }, + { + "source": "sizeInGb", + "target": "SizeInGb" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDisk", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Disk", + "mappings": [ + { + "source": "diskName", + "target": "DiskName" + } + ], + "operation": "DeleteDisk", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::DiskSnapshot", + "mappings": [ + { + "source": "diskName", + "target": "DiskName" + }, + { + "source": "diskSnapshotName", + "target": "DiskSnapshotName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDiskSnapshot", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::DiskSnapshot", + "mappings": [ + { + "source": "diskSnapshotName", + "target": "DiskSnapshotName" + } + ], + "operation": "DeleteDiskSnapshot", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Distribution", + "mappings": [ + { + "source": "bundleId", + "target": "BundleId" + }, + { + "source": "cacheBehaviorSettings", + "target": "CacheBehaviorSettings" + }, + { + "source": "cacheBehaviors", + "target": "CacheBehaviors" + }, + { + "source": "certificateName", + "target": "CertificateName" + }, + { + "source": "defaultCacheBehavior", + "target": "DefaultCacheBehavior" + }, + { + "source": "distributionName", + "target": "DistributionName" + }, + { + "source": "ipAddressType", + "target": "IpAddressType" + }, + { + "source": "origin", + "target": "Origin" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDistribution", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Distribution", + "mappings": [ + { + "source": "distributionName", + "target": "DistributionName" + } + ], + "operation": "DeleteDistribution", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Domain", + "mappings": [ + { + "source": "domainName", + "target": "DomainName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDomain", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Domain", + "mappings": [ + { + "source": "domainName", + "target": "DomainName" + } + ], + "operation": "DeleteDomain", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Instance", + "mappings": [ + { + "source": "addOns", + "target": "AddOns" + }, + { + "source": "availabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "blueprintId", + "target": "BlueprintId" + }, + { + "source": "bundleId", + "target": "BundleId" + }, + { + "source": "keyPairName", + "target": "KeyPairName" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "userData", + "target": "UserData" + } + ], + "operation": "CreateInstances", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::Instance", + "mappings": [ + { + "source": "instanceName", + "target": "InstanceName" + } + ], + "operation": "DeleteInstance", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::InstanceSnapshot", + "mappings": [ + { + "source": "instanceName", + "target": "InstanceName" + }, + { + "source": "instanceSnapshotName", + "target": "InstanceSnapshotName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateInstanceSnapshot", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::InstanceSnapshot", + "mappings": [ + { + "source": "instanceSnapshotName", + "target": "InstanceSnapshotName" + } + ], + "operation": "DeleteInstanceSnapshot", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::LoadBalancer", + "mappings": [ + { + "source": "healthCheckPath", + "target": "HealthCheckPath" + }, + { + "source": "instancePort", + "target": "InstancePort" + }, + { + "source": "ipAddressType", + "target": "IpAddressType" + }, + { + "source": "loadBalancerName", + "target": "LoadBalancerName" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "tlsPolicyName", + "target": "TlsPolicyName" + } + ], + "operation": "CreateLoadBalancer", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::LoadBalancer", + "mappings": [ + { + "source": "loadBalancerName", + "target": "LoadBalancerName" + } + ], + "operation": "DeleteLoadBalancer", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::LoadBalancerTlsCertificate", + "mappings": [ + { + "source": "certificateAlternativeNames", + "target": "CertificateAlternativeNames" + }, + { + "source": "certificateDomainName", + "target": "CertificateDomainName" + }, + { + "source": "certificateName", + "target": "CertificateName" + }, + { + "source": "loadBalancerName", + "target": "LoadBalancerName" + } + ], + "operation": "CreateLoadBalancerTlsCertificate", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::LoadBalancerTlsCertificate", + "mappings": [ + { + "source": "certificateName", + "target": "CertificateName" + }, + { + "source": "loadBalancerName", + "target": "LoadBalancerName" + } + ], + "operation": "DeleteLoadBalancerTlsCertificate", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::StaticIp", + "mappings": [ + { + "source": "staticIpName", + "target": "StaticIpName" + } + ], + "operation": "AllocateStaticIp", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::StaticIp", + "mappings": [ + { + "source": "staticIpName", + "target": "StaticIpName" + } + ], + "operation": "ReleaseStaticIp", + "phase": "delete", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Location::APIKey", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "ExpireTime", + "target": "ExpireTime" + }, + { + "source": "KeyName", + "target": "KeyName" + }, + { + "source": "NoExpiry", + "target": "NoExpiry" + }, + { + "source": "Restrictions", + "target": "Restrictions" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateKey", + "phase": "create", + "service": "location" + }, + { + "cfn_type": "AWS::Location::APIKey", + "mappings": [ + { + "source": "ForceDelete", + "target": "ForceDelete" + }, + { + "source": "KeyName", + "target": "KeyName" + } + ], + "operation": "DeleteKey", + "phase": "delete", + "service": "location" + }, + { + "cfn_type": "AWS::Location::GeofenceCollection", + "mappings": [ + { + "source": "CollectionName", + "target": "CollectionName" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "PricingPlan", + "target": "PricingPlan" + }, + { + "source": "PricingPlanDataSource", + "target": "PricingPlanDataSource" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateGeofenceCollection", + "phase": "create", + "service": "location" + }, + { + "cfn_type": "AWS::Location::GeofenceCollection", + "mappings": [ + { + "source": "CollectionName", + "target": "CollectionName" + } + ], + "operation": "DeleteGeofenceCollection", + "phase": "delete", + "service": "location" + }, + { + "cfn_type": "AWS::Location::Map", + "mappings": [ + { + "source": "Configuration", + "target": "Configuration" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "MapName", + "target": "MapName" + }, + { + "source": "PricingPlan", + "target": "PricingPlan" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateMap", + "phase": "create", + "service": "location" + }, + { + "cfn_type": "AWS::Location::Map", + "mappings": [ + { + "source": "MapName", + "target": "MapName" + } + ], + "operation": "DeleteMap", + "phase": "delete", + "service": "location" + }, + { + "cfn_type": "AWS::Location::PlaceIndex", + "mappings": [ + { + "source": "DataSource", + "target": "DataSource" + }, + { + "source": "DataSourceConfiguration", + "target": "DataSourceConfiguration" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "IndexName", + "target": "IndexName" + }, + { + "source": "PricingPlan", + "target": "PricingPlan" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePlaceIndex", + "phase": "create", + "service": "location" + }, + { + "cfn_type": "AWS::Location::PlaceIndex", + "mappings": [ + { + "source": "IndexName", + "target": "IndexName" + } + ], + "operation": "DeletePlaceIndex", + "phase": "delete", + "service": "location" + }, + { + "cfn_type": "AWS::Location::RouteCalculator", + "mappings": [ + { + "source": "CalculatorName", + "target": "CalculatorName" + }, + { + "source": "DataSource", + "target": "DataSource" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "PricingPlan", + "target": "PricingPlan" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRouteCalculator", + "phase": "create", + "service": "location" + }, + { + "cfn_type": "AWS::Location::RouteCalculator", + "mappings": [ + { + "source": "CalculatorName", + "target": "CalculatorName" + } + ], + "operation": "DeleteRouteCalculator", + "phase": "delete", + "service": "location" + }, + { + "cfn_type": "AWS::Location::Tracker", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "EventBridgeEnabled", + "target": "EventBridgeEnabled" + }, + { + "source": "KmsKeyEnableGeospatialQueries", + "target": "KmsKeyEnableGeospatialQueries" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "PositionFiltering", + "target": "PositionFiltering" + }, + { + "source": "PricingPlan", + "target": "PricingPlan" + }, + { + "source": "PricingPlanDataSource", + "target": "PricingPlanDataSource" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TrackerName", + "target": "TrackerName" + } + ], + "operation": "CreateTracker", + "phase": "create", + "service": "location" + }, + { + "cfn_type": "AWS::Location::Tracker", + "mappings": [ + { + "source": "TrackerName", + "target": "TrackerName" + } + ], + "operation": "DeleteTracker", + "phase": "delete", + "service": "location" + }, + { + "cfn_type": "AWS::Location::TrackerConsumer", + "mappings": [ + { + "source": "ConsumerArn", + "target": "ConsumerArn" + }, + { + "source": "TrackerName", + "target": "TrackerName" + } + ], + "operation": "AssociateTrackerConsumer", + "phase": "create", + "service": "location" + }, + { + "cfn_type": "AWS::Location::TrackerConsumer", + "mappings": [ + { + "source": "ConsumerArn", + "target": "ConsumerArn" + }, + { + "source": "TrackerName", + "target": "TrackerName" + } + ], + "operation": "DisassociateTrackerConsumer", + "phase": "delete", + "service": "location" + }, + { + "cfn_type": "AWS::Logs::AccountPolicy", + "mappings": [ + { + "source": "policyDocument", + "target": "PolicyDocument" + }, + { + "source": "policyName", + "target": "PolicyName" + }, + { + "source": "policyType", + "target": "PolicyType" + }, + { + "source": "scope", + "target": "Scope" + }, + { + "source": "selectionCriteria", + "target": "SelectionCriteria" + } + ], + "operation": "PutAccountPolicy", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::AccountPolicy", + "mappings": [ + { + "source": "policyName", + "target": "PolicyName" + }, + { + "source": "policyType", + "target": "PolicyType" + } + ], + "operation": "DeleteAccountPolicy", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::Delivery", + "mappings": [ + { + "source": "deliveryDestinationArn", + "target": "DeliveryDestinationArn" + }, + { + "source": "deliverySourceName", + "target": "DeliverySourceName" + }, + { + "source": "fieldDelimiter", + "target": "FieldDelimiter" + }, + { + "source": "recordFields", + "target": "RecordFields" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDelivery", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::Delivery", + "mappings": [], + "operation": "DeleteDelivery", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::DeliveryDestination", + "mappings": [ + { + "source": "deliveryDestinationType", + "target": "DeliveryDestinationType" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "outputFormat", + "target": "OutputFormat" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "PutDeliveryDestination", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::DeliveryDestination", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteDeliveryDestination", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::DeliverySource", + "mappings": [ + { + "source": "logType", + "target": "LogType" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "resourceArn", + "target": "ResourceArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "PutDeliverySource", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::DeliverySource", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteDeliverySource", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::Destination", + "mappings": [ + { + "source": "destinationName", + "target": "DestinationName" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetArn", + "target": "TargetArn" + } + ], + "operation": "PutDestination", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::Destination", + "mappings": [ + { + "source": "destinationName", + "target": "DestinationName" + } + ], + "operation": "DeleteDestination", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::Integration", + "mappings": [ + { + "source": "integrationName", + "target": "IntegrationName" + }, + { + "source": "integrationType", + "target": "IntegrationType" + }, + { + "source": "resourceConfig", + "target": "ResourceConfig" + } + ], + "operation": "PutIntegration", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::Integration", + "mappings": [ + { + "source": "integrationName", + "target": "IntegrationName" + } + ], + "operation": "DeleteIntegration", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::LogAnomalyDetector", + "mappings": [ + { + "source": "anomalyVisibilityTime", + "target": "AnomalyVisibilityTime" + }, + { + "source": "detectorName", + "target": "DetectorName" + }, + { + "source": "evaluationFrequency", + "target": "EvaluationFrequency" + }, + { + "source": "filterPattern", + "target": "FilterPattern" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "logGroupArnList", + "target": "LogGroupArnList" + } + ], + "operation": "CreateLogAnomalyDetector", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::LogAnomalyDetector", + "mappings": [], + "operation": "DeleteLogAnomalyDetector", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::LogGroup", + "mappings": [ + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "logGroupClass", + "target": "LogGroupClass" + }, + { + "source": "logGroupName", + "target": "LogGroupName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateLogGroup", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::LogGroup", + "mappings": [ + { + "source": "logGroupName", + "target": "LogGroupName" + } + ], + "operation": "DeleteLogGroup", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::LogStream", + "mappings": [ + { + "source": "logGroupName", + "target": "LogGroupName" + }, + { + "source": "logStreamName", + "target": "LogStreamName" + } + ], + "operation": "CreateLogStream", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::LogStream", + "mappings": [ + { + "source": "logGroupName", + "target": "LogGroupName" + }, + { + "source": "logStreamName", + "target": "LogStreamName" + } + ], + "operation": "DeleteLogStream", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::MetricFilter", + "mappings": [ + { + "source": "applyOnTransformedLogs", + "target": "ApplyOnTransformedLogs" + }, + { + "source": "filterName", + "target": "FilterName" + }, + { + "source": "filterPattern", + "target": "FilterPattern" + }, + { + "source": "logGroupName", + "target": "LogGroupName" + }, + { + "source": "metricTransformations", + "target": "MetricTransformations" + } + ], + "operation": "PutMetricFilter", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::MetricFilter", + "mappings": [ + { + "source": "filterName", + "target": "FilterName" + }, + { + "source": "logGroupName", + "target": "LogGroupName" + } + ], + "operation": "DeleteMetricFilter", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::QueryDefinition", + "mappings": [ + { + "source": "logGroupNames", + "target": "LogGroupNames" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "queryLanguage", + "target": "QueryLanguage" + }, + { + "source": "queryString", + "target": "QueryString" + } + ], + "operation": "PutQueryDefinition", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::QueryDefinition", + "mappings": [], + "operation": "DeleteQueryDefinition", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::ResourcePolicy", + "mappings": [ + { + "source": "policyDocument", + "target": "PolicyDocument" + }, + { + "source": "policyName", + "target": "PolicyName" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::ResourcePolicy", + "mappings": [ + { + "source": "policyName", + "target": "PolicyName" + } + ], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::ScheduledQuery", + "mappings": [ + { + "source": "logGroupIdentifiers", + "target": "LogGroupIdentifiers" + }, + { + "source": "queryLanguage", + "target": "QueryLanguage" + }, + { + "source": "queryString", + "target": "QueryString" + } + ], + "operation": "StartQuery", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::SubscriptionFilter", + "mappings": [ + { + "source": "applyOnTransformedLogs", + "target": "ApplyOnTransformedLogs" + }, + { + "source": "destinationArn", + "target": "DestinationArn" + }, + { + "source": "distribution", + "target": "Distribution" + }, + { + "source": "filterName", + "target": "FilterName" + }, + { + "source": "filterPattern", + "target": "FilterPattern" + }, + { + "source": "logGroupName", + "target": "LogGroupName" + }, + { + "source": "roleArn", + "target": "RoleArn" + } + ], + "operation": "PutSubscriptionFilter", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::SubscriptionFilter", + "mappings": [ + { + "source": "filterName", + "target": "FilterName" + }, + { + "source": "logGroupName", + "target": "LogGroupName" + } + ], + "operation": "DeleteSubscriptionFilter", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::Transformer", + "mappings": [ + { + "source": "logGroupIdentifier", + "target": "LogGroupIdentifier" + }, + { + "source": "transformerConfig", + "target": "TransformerConfig" + } + ], + "operation": "PutTransformer", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::Transformer", + "mappings": [ + { + "source": "logGroupIdentifier", + "target": "LogGroupIdentifier" + } + ], + "operation": "DeleteTransformer", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::LookoutEquipment::InferenceScheduler", + "mappings": [ + { + "source": "DataDelayOffsetInMinutes", + "target": "DataDelayOffsetInMinutes" + }, + { + "source": "DataInputConfiguration", + "target": "DataInputConfiguration" + }, + { + "source": "DataOutputConfiguration", + "target": "DataOutputConfiguration" + }, + { + "source": "DataUploadFrequency", + "target": "DataUploadFrequency" + }, + { + "source": "InferenceSchedulerName", + "target": "InferenceSchedulerName" + }, + { + "source": "ModelName", + "target": "ModelName" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "ServerSideKmsKeyId", + "target": "ServerSideKmsKeyId" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateInferenceScheduler", + "phase": "create", + "service": "lookoutequipment" + }, + { + "cfn_type": "AWS::LookoutEquipment::InferenceScheduler", + "mappings": [ + { + "source": "InferenceSchedulerName", + "target": "InferenceSchedulerName" + } + ], + "operation": "DeleteInferenceScheduler", + "phase": "delete", + "service": "lookoutequipment" + }, + { + "cfn_type": "AWS::LookoutVision::Project", + "mappings": [ + { + "source": "ProjectName", + "target": "ProjectName" + } + ], + "operation": "CreateProject", + "phase": "create", + "service": "lookoutvision" + }, + { + "cfn_type": "AWS::LookoutVision::Project", + "mappings": [ + { + "source": "ProjectName", + "target": "ProjectName" + } + ], + "operation": "DeleteProject", + "phase": "delete", + "service": "lookoutvision" + }, + { + "cfn_type": "AWS::M2::Application", + "mappings": [ + { + "source": "definition", + "target": "Definition" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "engineType", + "target": "EngineType" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "m2" + }, + { + "cfn_type": "AWS::M2::Application", + "mappings": [], + "operation": "DeleteApplication", + "phase": "delete", + "service": "m2" + }, + { + "cfn_type": "AWS::M2::Deployment", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + }, + { + "source": "applicationVersion", + "target": "ApplicationVersion" + }, + { + "source": "environmentId", + "target": "EnvironmentId" + } + ], + "operation": "CreateDeployment", + "phase": "create", + "service": "m2" + }, + { + "cfn_type": "AWS::M2::Deployment", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + }, + { + "source": "environmentId", + "target": "EnvironmentId" + } + ], + "operation": "DeleteApplicationFromEnvironment", + "phase": "delete", + "service": "m2" + }, + { + "cfn_type": "AWS::M2::Environment", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "engineType", + "target": "EngineType" + }, + { + "source": "engineVersion", + "target": "EngineVersion" + }, + { + "source": "highAvailabilityConfig", + "target": "HighAvailabilityConfig" + }, + { + "source": "instanceType", + "target": "InstanceType" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "networkType", + "target": "NetworkType" + }, + { + "source": "preferredMaintenanceWindow", + "target": "PreferredMaintenanceWindow" + }, + { + "source": "publiclyAccessible", + "target": "PubliclyAccessible" + }, + { + "source": "securityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "storageConfigurations", + "target": "StorageConfigurations" + }, + { + "source": "subnetIds", + "target": "SubnetIds" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateEnvironment", + "phase": "create", + "service": "m2" + }, + { + "cfn_type": "AWS::M2::Environment", + "mappings": [], + "operation": "DeleteEnvironment", + "phase": "delete", + "service": "m2" + }, + { + "cfn_type": "AWS::MPA::ApprovalTeam", + "mappings": [ + { + "source": "ApprovalStrategy", + "target": "ApprovalStrategy" + }, + { + "source": "Approvers", + "target": "Approvers" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Policies", + "target": "Policies" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateApprovalTeam", + "phase": "create", + "service": "mpa" + }, + { + "cfn_type": "AWS::MPA::IdentitySource", + "mappings": [ + { + "source": "IdentitySourceParameters", + "target": "IdentitySourceParameters" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateIdentitySource", + "phase": "create", + "service": "mpa" + }, + { + "cfn_type": "AWS::MPA::IdentitySource", + "mappings": [], + "operation": "DeleteIdentitySource", + "phase": "delete", + "service": "mpa" + }, + { + "cfn_type": "AWS::MSK::Cluster", + "mappings": [ + { + "source": "BrokerNodeGroupInfo", + "target": "BrokerNodeGroupInfo" + }, + { + "source": "ClientAuthentication", + "target": "ClientAuthentication" + }, + { + "source": "ClusterName", + "target": "ClusterName" + }, + { + "source": "ConfigurationInfo", + "target": "ConfigurationInfo" + }, + { + "source": "EncryptionInfo", + "target": "EncryptionInfo" + }, + { + "source": "EnhancedMonitoring", + "target": "EnhancedMonitoring" + }, + { + "source": "KafkaVersion", + "target": "KafkaVersion" + }, + { + "source": "LoggingInfo", + "target": "LoggingInfo" + }, + { + "source": "NumberOfBrokerNodes", + "target": "NumberOfBrokerNodes" + }, + { + "source": "OpenMonitoring", + "target": "OpenMonitoring" + }, + { + "source": "StorageMode", + "target": "StorageMode" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCluster", + "phase": "create", + "service": "kafka" + }, + { + "cfn_type": "AWS::MSK::Cluster", + "mappings": [], + "operation": "DeleteCluster", + "phase": "delete", + "service": "kafka" + }, + { + "cfn_type": "AWS::MSK::ClusterPolicy", + "mappings": [ + { + "source": "ClusterArn", + "target": "ClusterArn" + }, + { + "source": "Policy", + "target": "Policy" + } + ], + "operation": "PutClusterPolicy", + "phase": "create", + "service": "kafka" + }, + { + "cfn_type": "AWS::MSK::ClusterPolicy", + "mappings": [ + { + "source": "ClusterArn", + "target": "ClusterArn" + } + ], + "operation": "DeleteClusterPolicy", + "phase": "delete", + "service": "kafka" + }, + { + "cfn_type": "AWS::MSK::Configuration", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ServerProperties", + "target": "ServerProperties" + } + ], + "operation": "CreateConfiguration", + "phase": "create", + "service": "kafka" + }, + { + "cfn_type": "AWS::MSK::Configuration", + "mappings": [], + "operation": "DeleteConfiguration", + "phase": "delete", + "service": "kafka" + }, + { + "cfn_type": "AWS::MSK::Replicator", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "KafkaClusters", + "target": "KafkaClusters" + }, + { + "source": "ReplicationInfoList", + "target": "ReplicationInfoList" + }, + { + "source": "ReplicatorName", + "target": "ReplicatorName" + }, + { + "source": "ServiceExecutionRoleArn", + "target": "ServiceExecutionRoleArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateReplicator", + "phase": "create", + "service": "kafka" + }, + { + "cfn_type": "AWS::MSK::Replicator", + "mappings": [], + "operation": "DeleteReplicator", + "phase": "delete", + "service": "kafka" + }, + { + "cfn_type": "AWS::MSK::ServerlessCluster", + "mappings": [ + { + "source": "ClusterName", + "target": "ClusterName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateClusterV2", + "phase": "create", + "service": "kafka" + }, + { + "cfn_type": "AWS::MSK::VpcConnection", + "mappings": [ + { + "source": "Authentication", + "target": "Authentication" + }, + { + "source": "ClientSubnets", + "target": "ClientSubnets" + }, + { + "source": "SecurityGroups", + "target": "SecurityGroups" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetClusterArn", + "target": "TargetClusterArn" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateVpcConnection", + "phase": "create", + "service": "kafka" + }, + { + "cfn_type": "AWS::MSK::VpcConnection", + "mappings": [], + "operation": "DeleteVpcConnection", + "phase": "delete", + "service": "kafka" + }, + { + "cfn_type": "AWS::MWAA::Environment", + "mappings": [ + { + "source": "AirflowConfigurationOptions", + "target": "AirflowConfigurationOptions" + }, + { + "source": "AirflowVersion", + "target": "AirflowVersion" + }, + { + "source": "DagS3Path", + "target": "DagS3Path" + }, + { + "source": "EndpointManagement", + "target": "EndpointManagement" + }, + { + "source": "EnvironmentClass", + "target": "EnvironmentClass" + }, + { + "source": "ExecutionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "KmsKey", + "target": "KmsKey" + }, + { + "source": "LoggingConfiguration", + "target": "LoggingConfiguration" + }, + { + "source": "MaxWebservers", + "target": "MaxWebservers" + }, + { + "source": "MaxWorkers", + "target": "MaxWorkers" + }, + { + "source": "MinWebservers", + "target": "MinWebservers" + }, + { + "source": "MinWorkers", + "target": "MinWorkers" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NetworkConfiguration", + "target": "NetworkConfiguration" + }, + { + "source": "PluginsS3ObjectVersion", + "target": "PluginsS3ObjectVersion" + }, + { + "source": "PluginsS3Path", + "target": "PluginsS3Path" + }, + { + "source": "RequirementsS3ObjectVersion", + "target": "RequirementsS3ObjectVersion" + }, + { + "source": "RequirementsS3Path", + "target": "RequirementsS3Path" + }, + { + "source": "Schedulers", + "target": "Schedulers" + }, + { + "source": "SourceBucketArn", + "target": "SourceBucketArn" + }, + { + "source": "StartupScriptS3ObjectVersion", + "target": "StartupScriptS3ObjectVersion" + }, + { + "source": "StartupScriptS3Path", + "target": "StartupScriptS3Path" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "WebserverAccessMode", + "target": "WebserverAccessMode" + }, + { + "source": "WeeklyMaintenanceWindowStart", + "target": "WeeklyMaintenanceWindowStart" + } + ], + "operation": "CreateEnvironment", + "phase": "create", + "service": "mwaa" + }, + { + "cfn_type": "AWS::MWAA::Environment", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteEnvironment", + "phase": "delete", + "service": "mwaa" + }, + { + "cfn_type": "AWS::Macie::AllowList", + "mappings": [ + { + "source": "criteria", + "target": "Criteria" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAllowList", + "phase": "create", + "service": "macie2" + }, + { + "cfn_type": "AWS::Macie::AllowList", + "mappings": [], + "operation": "DeleteAllowList", + "phase": "delete", + "service": "macie2" + }, + { + "cfn_type": "AWS::Macie::CustomDataIdentifier", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "ignoreWords", + "target": "IgnoreWords" + }, + { + "source": "keywords", + "target": "Keywords" + }, + { + "source": "maximumMatchDistance", + "target": "MaximumMatchDistance" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "regex", + "target": "Regex" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCustomDataIdentifier", + "phase": "create", + "service": "macie2" + }, + { + "cfn_type": "AWS::Macie::CustomDataIdentifier", + "mappings": [], + "operation": "DeleteCustomDataIdentifier", + "phase": "delete", + "service": "macie2" + }, + { + "cfn_type": "AWS::Macie::FindingsFilter", + "mappings": [ + { + "source": "action", + "target": "Action" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "findingCriteria", + "target": "FindingCriteria" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "position", + "target": "Position" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateFindingsFilter", + "phase": "create", + "service": "macie2" + }, + { + "cfn_type": "AWS::Macie::FindingsFilter", + "mappings": [], + "operation": "DeleteFindingsFilter", + "phase": "delete", + "service": "macie2" + }, + { + "cfn_type": "AWS::Macie::Session", + "mappings": [ + { + "source": "findingPublishingFrequency", + "target": "FindingPublishingFrequency" + }, + { + "source": "status", + "target": "Status" + } + ], + "operation": "EnableMacie", + "phase": "create", + "service": "macie2" + }, + { + "cfn_type": "AWS::ManagedBlockchain::Accessor", + "mappings": [ + { + "source": "AccessorType", + "target": "AccessorType" + }, + { + "source": "NetworkType", + "target": "NetworkType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAccessor", + "phase": "create", + "service": "managedblockchain" + }, + { + "cfn_type": "AWS::ManagedBlockchain::Accessor", + "mappings": [], + "operation": "DeleteAccessor", + "phase": "delete", + "service": "managedblockchain" + }, + { + "cfn_type": "AWS::MediaConnect::Bridge", + "mappings": [ + { + "source": "EgressGatewayBridge", + "target": "EgressGatewayBridge" + }, + { + "source": "IngressGatewayBridge", + "target": "IngressGatewayBridge" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Outputs", + "target": "Outputs" + }, + { + "source": "PlacementArn", + "target": "PlacementArn" + }, + { + "source": "SourceFailoverConfig", + "target": "SourceFailoverConfig" + }, + { + "source": "Sources", + "target": "Sources" + } + ], + "operation": "CreateBridge", + "phase": "create", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::Bridge", + "mappings": [], + "operation": "DeleteBridge", + "phase": "delete", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::BridgeOutput", + "mappings": [ + { + "source": "BridgeArn", + "target": "BridgeArn" + } + ], + "operation": "AddBridgeOutputs", + "phase": "create", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::BridgeOutput", + "mappings": [ + { + "source": "BridgeArn", + "target": "BridgeArn" + } + ], + "operation": "RemoveBridgeOutput", + "phase": "delete", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::BridgeSource", + "mappings": [ + { + "source": "BridgeArn", + "target": "BridgeArn" + } + ], + "operation": "AddBridgeSources", + "phase": "create", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::BridgeSource", + "mappings": [ + { + "source": "BridgeArn", + "target": "BridgeArn" + } + ], + "operation": "RemoveBridgeSource", + "phase": "delete", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::Flow", + "mappings": [ + { + "source": "AvailabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "FlowSize", + "target": "FlowSize" + }, + { + "source": "Maintenance", + "target": "Maintenance" + }, + { + "source": "MediaStreams", + "target": "MediaStreams" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NdiConfig", + "target": "NdiConfig" + }, + { + "source": "Source", + "target": "Source" + }, + { + "source": "SourceFailoverConfig", + "target": "SourceFailoverConfig" + }, + { + "source": "SourceMonitoringConfig", + "target": "SourceMonitoringConfig" + }, + { + "source": "VpcInterfaces", + "target": "VpcInterfaces" + } + ], + "operation": "CreateFlow", + "phase": "create", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::Flow", + "mappings": [], + "operation": "DeleteFlow", + "phase": "delete", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::FlowEntitlement", + "mappings": [ + { + "source": "FlowArn", + "target": "FlowArn" + } + ], + "operation": "GrantFlowEntitlements", + "phase": "create", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::FlowEntitlement", + "mappings": [ + { + "source": "FlowArn", + "target": "FlowArn" + } + ], + "operation": "RevokeFlowEntitlement", + "phase": "delete", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::FlowOutput", + "mappings": [ + { + "source": "FlowArn", + "target": "FlowArn" + } + ], + "operation": "AddFlowOutputs", + "phase": "create", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::FlowOutput", + "mappings": [ + { + "source": "FlowArn", + "target": "FlowArn" + } + ], + "operation": "RemoveFlowOutput", + "phase": "delete", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::FlowSource", + "mappings": [ + { + "source": "FlowArn", + "target": "FlowArn" + } + ], + "operation": "AddFlowSources", + "phase": "create", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::FlowSource", + "mappings": [ + { + "source": "FlowArn", + "target": "FlowArn" + } + ], + "operation": "RemoveFlowSource", + "phase": "delete", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::FlowVpcInterface", + "mappings": [ + { + "source": "FlowArn", + "target": "FlowArn" + } + ], + "operation": "AddFlowVpcInterfaces", + "phase": "create", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::FlowVpcInterface", + "mappings": [ + { + "source": "FlowArn", + "target": "FlowArn" + } + ], + "operation": "RemoveFlowVpcInterface", + "phase": "delete", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::Gateway", + "mappings": [ + { + "source": "EgressCidrBlocks", + "target": "EgressCidrBlocks" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Networks", + "target": "Networks" + } + ], + "operation": "CreateGateway", + "phase": "create", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::Gateway", + "mappings": [], + "operation": "DeleteGateway", + "phase": "delete", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConvert::Preset", + "mappings": [ + { + "source": "Category", + "target": "Category" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePreset", + "phase": "create", + "service": "mediaconvert" + }, + { + "cfn_type": "AWS::MediaConvert::Preset", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeletePreset", + "phase": "delete", + "service": "mediaconvert" + }, + { + "cfn_type": "AWS::MediaLive::ChannelPlacementGroup", + "mappings": [ + { + "source": "ClusterId", + "target": "ClusterId" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Nodes", + "target": "Nodes" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateChannelPlacementGroup", + "phase": "create", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::ChannelPlacementGroup", + "mappings": [ + { + "source": "ClusterId", + "target": "ClusterId" + } + ], + "operation": "DeleteChannelPlacementGroup", + "phase": "delete", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::CloudWatchAlarmTemplate", + "mappings": [ + { + "source": "ComparisonOperator", + "target": "ComparisonOperator" + }, + { + "source": "DatapointsToAlarm", + "target": "DatapointsToAlarm" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EvaluationPeriods", + "target": "EvaluationPeriods" + }, + { + "source": "GroupIdentifier", + "target": "GroupIdentifier" + }, + { + "source": "MetricName", + "target": "MetricName" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Period", + "target": "Period" + }, + { + "source": "Statistic", + "target": "Statistic" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetResourceType", + "target": "TargetResourceType" + }, + { + "source": "Threshold", + "target": "Threshold" + }, + { + "source": "TreatMissingData", + "target": "TreatMissingData" + } + ], + "operation": "CreateCloudWatchAlarmTemplate", + "phase": "create", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::CloudWatchAlarmTemplate", + "mappings": [], + "operation": "DeleteCloudWatchAlarmTemplate", + "phase": "delete", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::CloudWatchAlarmTemplateGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCloudWatchAlarmTemplateGroup", + "phase": "create", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::CloudWatchAlarmTemplateGroup", + "mappings": [], + "operation": "DeleteCloudWatchAlarmTemplateGroup", + "phase": "delete", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::Cluster", + "mappings": [ + { + "source": "ClusterType", + "target": "ClusterType" + }, + { + "source": "InstanceRoleArn", + "target": "InstanceRoleArn" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NetworkSettings", + "target": "NetworkSettings" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCluster", + "phase": "create", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::Cluster", + "mappings": [], + "operation": "DeleteCluster", + "phase": "delete", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::EventBridgeRuleTemplate", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "EventTargets", + "target": "EventTargets" + }, + { + "source": "EventType", + "target": "EventType" + }, + { + "source": "GroupIdentifier", + "target": "GroupIdentifier" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEventBridgeRuleTemplate", + "phase": "create", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::EventBridgeRuleTemplate", + "mappings": [], + "operation": "DeleteEventBridgeRuleTemplate", + "phase": "delete", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::EventBridgeRuleTemplateGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEventBridgeRuleTemplateGroup", + "phase": "create", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::EventBridgeRuleTemplateGroup", + "mappings": [], + "operation": "DeleteEventBridgeRuleTemplateGroup", + "phase": "delete", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::Multiplex", + "mappings": [ + { + "source": "AvailabilityZones", + "target": "AvailabilityZones" + }, + { + "source": "MultiplexSettings", + "target": "MultiplexSettings" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateMultiplex", + "phase": "create", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::Multiplex", + "mappings": [], + "operation": "DeleteMultiplex", + "phase": "delete", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::Multiplexprogram", + "mappings": [ + { + "source": "MultiplexId", + "target": "MultiplexId" + }, + { + "source": "MultiplexProgramSettings", + "target": "MultiplexProgramSettings" + }, + { + "source": "ProgramName", + "target": "ProgramName" + } + ], + "operation": "CreateMultiplexProgram", + "phase": "create", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::Multiplexprogram", + "mappings": [ + { + "source": "MultiplexId", + "target": "MultiplexId" + }, + { + "source": "ProgramName", + "target": "ProgramName" + } + ], + "operation": "DeleteMultiplexProgram", + "phase": "delete", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::Network", + "mappings": [ + { + "source": "IpPools", + "target": "IpPools" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Routes", + "target": "Routes" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateNetwork", + "phase": "create", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::Network", + "mappings": [], + "operation": "DeleteNetwork", + "phase": "delete", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::Node", + "mappings": [ + { + "source": "ClusterId", + "target": "ClusterId" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NodeInterfaceMappings", + "target": "NodeInterfaceMappings" + }, + { + "source": "Role", + "target": "Role" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateNode", + "phase": "create", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::Node", + "mappings": [ + { + "source": "ClusterId", + "target": "ClusterId" + } + ], + "operation": "DeleteNode", + "phase": "delete", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::SdiSource", + "mappings": [ + { + "source": "Mode", + "target": "Mode" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateSdiSource", + "phase": "create", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::SdiSource", + "mappings": [], + "operation": "DeleteSdiSource", + "phase": "delete", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::SignalMap", + "mappings": [ + { + "source": "CloudWatchAlarmTemplateGroupIdentifiers", + "target": "CloudWatchAlarmTemplateGroupIdentifiers" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DiscoveryEntryPointArn", + "target": "DiscoveryEntryPointArn" + }, + { + "source": "EventBridgeRuleTemplateGroupIdentifiers", + "target": "EventBridgeRuleTemplateGroupIdentifiers" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSignalMap", + "phase": "create", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaLive::SignalMap", + "mappings": [], + "operation": "DeleteSignalMap", + "phase": "delete", + "service": "medialive" + }, + { + "cfn_type": "AWS::MediaPackage::Asset", + "mappings": [ + { + "source": "Id", + "target": "Id" + }, + { + "source": "PackagingGroupId", + "target": "PackagingGroupId" + }, + { + "source": "ResourceId", + "target": "ResourceId" + }, + { + "source": "SourceArn", + "target": "SourceArn" + }, + { + "source": "SourceRoleArn", + "target": "SourceRoleArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAsset", + "phase": "create", + "service": "mediapackage-vod" + }, + { + "cfn_type": "AWS::MediaPackage::Asset", + "mappings": [ + { + "source": "Id", + "target": "Id" + } + ], + "operation": "DeleteAsset", + "phase": "delete", + "service": "mediapackage-vod" + }, + { + "cfn_type": "AWS::MediaPackage::Channel", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Id", + "target": "Id" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateChannel", + "phase": "create", + "service": "mediapackage" + }, + { + "cfn_type": "AWS::MediaPackage::Channel", + "mappings": [ + { + "source": "Id", + "target": "Id" + } + ], + "operation": "DeleteChannel", + "phase": "delete", + "service": "mediapackage" + }, + { + "cfn_type": "AWS::MediaPackage::OriginEndpoint", + "mappings": [ + { + "source": "Authorization", + "target": "Authorization" + }, + { + "source": "ChannelId", + "target": "ChannelId" + }, + { + "source": "CmafPackage", + "target": "CmafPackage" + }, + { + "source": "DashPackage", + "target": "DashPackage" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "HlsPackage", + "target": "HlsPackage" + }, + { + "source": "Id", + "target": "Id" + }, + { + "source": "ManifestName", + "target": "ManifestName" + }, + { + "source": "MssPackage", + "target": "MssPackage" + }, + { + "source": "Origination", + "target": "Origination" + }, + { + "source": "StartoverWindowSeconds", + "target": "StartoverWindowSeconds" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TimeDelaySeconds", + "target": "TimeDelaySeconds" + }, + { + "source": "Whitelist", + "target": "Whitelist" + } + ], + "operation": "CreateOriginEndpoint", + "phase": "create", + "service": "mediapackage" + }, + { + "cfn_type": "AWS::MediaPackage::OriginEndpoint", + "mappings": [ + { + "source": "Id", + "target": "Id" + } + ], + "operation": "DeleteOriginEndpoint", + "phase": "delete", + "service": "mediapackage" + }, + { + "cfn_type": "AWS::MediaPackage::PackagingConfiguration", + "mappings": [ + { + "source": "CmafPackage", + "target": "CmafPackage" + }, + { + "source": "DashPackage", + "target": "DashPackage" + }, + { + "source": "HlsPackage", + "target": "HlsPackage" + }, + { + "source": "Id", + "target": "Id" + }, + { + "source": "MssPackage", + "target": "MssPackage" + }, + { + "source": "PackagingGroupId", + "target": "PackagingGroupId" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePackagingConfiguration", + "phase": "create", + "service": "mediapackage-vod" + }, + { + "cfn_type": "AWS::MediaPackage::PackagingConfiguration", + "mappings": [ + { + "source": "Id", + "target": "Id" + } + ], + "operation": "DeletePackagingConfiguration", + "phase": "delete", + "service": "mediapackage-vod" + }, + { + "cfn_type": "AWS::MediaPackage::PackagingGroup", + "mappings": [ + { + "source": "Authorization", + "target": "Authorization" + }, + { + "source": "EgressAccessLogs", + "target": "EgressAccessLogs" + }, + { + "source": "Id", + "target": "Id" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePackagingGroup", + "phase": "create", + "service": "mediapackage-vod" + }, + { + "cfn_type": "AWS::MediaPackage::PackagingGroup", + "mappings": [ + { + "source": "Id", + "target": "Id" + } + ], + "operation": "DeletePackagingGroup", + "phase": "delete", + "service": "mediapackage-vod" + }, + { + "cfn_type": "AWS::MediaPackageV2::Channel", + "mappings": [ + { + "source": "ChannelGroupName", + "target": "ChannelGroupName" + }, + { + "source": "ChannelName", + "target": "ChannelName" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "InputSwitchConfiguration", + "target": "InputSwitchConfiguration" + }, + { + "source": "InputType", + "target": "InputType" + }, + { + "source": "OutputHeaderConfiguration", + "target": "OutputHeaderConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateChannel", + "phase": "create", + "service": "mediapackagev2" + }, + { + "cfn_type": "AWS::MediaPackageV2::Channel", + "mappings": [ + { + "source": "ChannelGroupName", + "target": "ChannelGroupName" + }, + { + "source": "ChannelName", + "target": "ChannelName" + } + ], + "operation": "DeleteChannel", + "phase": "delete", + "service": "mediapackagev2" + }, + { + "cfn_type": "AWS::MediaPackageV2::ChannelGroup", + "mappings": [ + { + "source": "ChannelGroupName", + "target": "ChannelGroupName" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateChannelGroup", + "phase": "create", + "service": "mediapackagev2" + }, + { + "cfn_type": "AWS::MediaPackageV2::ChannelGroup", + "mappings": [ + { + "source": "ChannelGroupName", + "target": "ChannelGroupName" + } + ], + "operation": "DeleteChannelGroup", + "phase": "delete", + "service": "mediapackagev2" + }, + { + "cfn_type": "AWS::MediaPackageV2::ChannelPolicy", + "mappings": [ + { + "source": "ChannelGroupName", + "target": "ChannelGroupName" + }, + { + "source": "ChannelName", + "target": "ChannelName" + }, + { + "source": "Policy", + "target": "Policy" + } + ], + "operation": "PutChannelPolicy", + "phase": "create", + "service": "mediapackagev2" + }, + { + "cfn_type": "AWS::MediaPackageV2::ChannelPolicy", + "mappings": [ + { + "source": "ChannelGroupName", + "target": "ChannelGroupName" + }, + { + "source": "ChannelName", + "target": "ChannelName" + } + ], + "operation": "DeleteChannelPolicy", + "phase": "delete", + "service": "mediapackagev2" + }, + { + "cfn_type": "AWS::MediaPackageV2::OriginEndpoint", + "mappings": [ + { + "source": "ChannelGroupName", + "target": "ChannelGroupName" + }, + { + "source": "ChannelName", + "target": "ChannelName" + }, + { + "source": "ContainerType", + "target": "ContainerType" + }, + { + "source": "DashManifests", + "target": "DashManifests" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "ForceEndpointErrorConfiguration", + "target": "ForceEndpointErrorConfiguration" + }, + { + "source": "HlsManifests", + "target": "HlsManifests" + }, + { + "source": "LowLatencyHlsManifests", + "target": "LowLatencyHlsManifests" + }, + { + "source": "MssManifests", + "target": "MssManifests" + }, + { + "source": "OriginEndpointName", + "target": "OriginEndpointName" + }, + { + "source": "Segment", + "target": "Segment" + }, + { + "source": "StartoverWindowSeconds", + "target": "StartoverWindowSeconds" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateOriginEndpoint", + "phase": "create", + "service": "mediapackagev2" + }, + { + "cfn_type": "AWS::MediaPackageV2::OriginEndpoint", + "mappings": [ + { + "source": "ChannelGroupName", + "target": "ChannelGroupName" + }, + { + "source": "ChannelName", + "target": "ChannelName" + }, + { + "source": "OriginEndpointName", + "target": "OriginEndpointName" + } + ], + "operation": "DeleteOriginEndpoint", + "phase": "delete", + "service": "mediapackagev2" + }, + { + "cfn_type": "AWS::MediaPackageV2::OriginEndpointPolicy", + "mappings": [ + { + "source": "CdnAuthConfiguration", + "target": "CdnAuthConfiguration" + }, + { + "source": "ChannelGroupName", + "target": "ChannelGroupName" + }, + { + "source": "ChannelName", + "target": "ChannelName" + }, + { + "source": "OriginEndpointName", + "target": "OriginEndpointName" + }, + { + "source": "Policy", + "target": "Policy" + } + ], + "operation": "PutOriginEndpointPolicy", + "phase": "create", + "service": "mediapackagev2" + }, + { + "cfn_type": "AWS::MediaPackageV2::OriginEndpointPolicy", + "mappings": [ + { + "source": "ChannelGroupName", + "target": "ChannelGroupName" + }, + { + "source": "ChannelName", + "target": "ChannelName" + }, + { + "source": "OriginEndpointName", + "target": "OriginEndpointName" + } + ], + "operation": "DeleteOriginEndpointPolicy", + "phase": "delete", + "service": "mediapackagev2" + }, + { + "cfn_type": "AWS::MediaTailor::Channel", + "mappings": [ + { + "source": "Audiences", + "target": "Audiences" + }, + { + "source": "ChannelName", + "target": "ChannelName" + }, + { + "source": "FillerSlate", + "target": "FillerSlate" + }, + { + "source": "Outputs", + "target": "Outputs" + }, + { + "source": "PlaybackMode", + "target": "PlaybackMode" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Tier", + "target": "Tier" + }, + { + "source": "TimeShiftConfiguration", + "target": "TimeShiftConfiguration" + } + ], + "operation": "CreateChannel", + "phase": "create", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::Channel", + "mappings": [ + { + "source": "ChannelName", + "target": "ChannelName" + } + ], + "operation": "DeleteChannel", + "phase": "delete", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::ChannelPolicy", + "mappings": [ + { + "source": "ChannelName", + "target": "ChannelName" + }, + { + "source": "Policy", + "target": "Policy" + } + ], + "operation": "PutChannelPolicy", + "phase": "create", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::ChannelPolicy", + "mappings": [ + { + "source": "ChannelName", + "target": "ChannelName" + } + ], + "operation": "DeleteChannelPolicy", + "phase": "delete", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::LiveSource", + "mappings": [ + { + "source": "HttpPackageConfigurations", + "target": "HttpPackageConfigurations" + }, + { + "source": "LiveSourceName", + "target": "LiveSourceName" + }, + { + "source": "SourceLocationName", + "target": "SourceLocationName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLiveSource", + "phase": "create", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::LiveSource", + "mappings": [ + { + "source": "LiveSourceName", + "target": "LiveSourceName" + }, + { + "source": "SourceLocationName", + "target": "SourceLocationName" + } + ], + "operation": "DeleteLiveSource", + "phase": "delete", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::PlaybackConfiguration", + "mappings": [ + { + "source": "AdConditioningConfiguration", + "target": "AdConditioningConfiguration" + }, + { + "source": "AdDecisionServerUrl", + "target": "AdDecisionServerUrl" + }, + { + "source": "AvailSuppression", + "target": "AvailSuppression" + }, + { + "source": "Bumper", + "target": "Bumper" + }, + { + "source": "CdnConfiguration", + "target": "CdnConfiguration" + }, + { + "source": "ConfigurationAliases", + "target": "ConfigurationAliases" + }, + { + "source": "DashConfiguration", + "target": "DashConfiguration" + }, + { + "source": "InsertionMode", + "target": "InsertionMode" + }, + { + "source": "LivePreRollConfiguration", + "target": "LivePreRollConfiguration" + }, + { + "source": "ManifestProcessingRules", + "target": "ManifestProcessingRules" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "PersonalizationThresholdSeconds", + "target": "PersonalizationThresholdSeconds" + }, + { + "source": "SlateAdUrl", + "target": "SlateAdUrl" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TranscodeProfileName", + "target": "TranscodeProfileName" + }, + { + "source": "VideoContentSourceUrl", + "target": "VideoContentSourceUrl" + } + ], + "operation": "PutPlaybackConfiguration", + "phase": "create", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::PlaybackConfiguration", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeletePlaybackConfiguration", + "phase": "delete", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::SourceLocation", + "mappings": [ + { + "source": "AccessConfiguration", + "target": "AccessConfiguration" + }, + { + "source": "DefaultSegmentDeliveryConfiguration", + "target": "DefaultSegmentDeliveryConfiguration" + }, + { + "source": "HttpConfiguration", + "target": "HttpConfiguration" + }, + { + "source": "SegmentDeliveryConfigurations", + "target": "SegmentDeliveryConfigurations" + }, + { + "source": "SourceLocationName", + "target": "SourceLocationName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSourceLocation", + "phase": "create", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::SourceLocation", + "mappings": [ + { + "source": "SourceLocationName", + "target": "SourceLocationName" + } + ], + "operation": "DeleteSourceLocation", + "phase": "delete", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::VodSource", + "mappings": [ + { + "source": "HttpPackageConfigurations", + "target": "HttpPackageConfigurations" + }, + { + "source": "SourceLocationName", + "target": "SourceLocationName" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VodSourceName", + "target": "VodSourceName" + } + ], + "operation": "CreateVodSource", + "phase": "create", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::VodSource", + "mappings": [ + { + "source": "SourceLocationName", + "target": "SourceLocationName" + }, + { + "source": "VodSourceName", + "target": "VodSourceName" + } + ], + "operation": "DeleteVodSource", + "phase": "delete", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MemoryDB::ACL", + "mappings": [ + { + "source": "ACLName", + "target": "ACLName" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UserNames", + "target": "UserNames" + } + ], + "operation": "CreateACL", + "phase": "create", + "service": "memorydb" + }, + { + "cfn_type": "AWS::MemoryDB::ACL", + "mappings": [ + { + "source": "ACLName", + "target": "ACLName" + } + ], + "operation": "DeleteACL", + "phase": "delete", + "service": "memorydb" + }, + { + "cfn_type": "AWS::MemoryDB::Cluster", + "mappings": [ + { + "source": "ACLName", + "target": "ACLName" + }, + { + "source": "AutoMinorVersionUpgrade", + "target": "AutoMinorVersionUpgrade" + }, + { + "source": "ClusterName", + "target": "ClusterName" + }, + { + "source": "DataTiering", + "target": "DataTiering" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "IpDiscovery", + "target": "IpDiscovery" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "MaintenanceWindow", + "target": "MaintenanceWindow" + }, + { + "source": "MultiRegionClusterName", + "target": "MultiRegionClusterName" + }, + { + "source": "NetworkType", + "target": "NetworkType" + }, + { + "source": "NodeType", + "target": "NodeType" + }, + { + "source": "NumReplicasPerShard", + "target": "NumReplicasPerShard" + }, + { + "source": "NumShards", + "target": "NumShards" + }, + { + "source": "ParameterGroupName", + "target": "ParameterGroupName" + }, + { + "source": "Port", + "target": "Port" + }, + { + "source": "SecurityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "SnapshotArns", + "target": "SnapshotArns" + }, + { + "source": "SnapshotName", + "target": "SnapshotName" + }, + { + "source": "SnapshotRetentionLimit", + "target": "SnapshotRetentionLimit" + }, + { + "source": "SnapshotWindow", + "target": "SnapshotWindow" + }, + { + "source": "SnsTopicArn", + "target": "SnsTopicArn" + }, + { + "source": "SubnetGroupName", + "target": "SubnetGroupName" + }, + { + "source": "TLSEnabled", + "target": "TLSEnabled" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCluster", + "phase": "create", + "service": "memorydb" + }, + { + "cfn_type": "AWS::MemoryDB::Cluster", + "mappings": [ + { + "source": "ClusterName", + "target": "ClusterName" + }, + { + "source": "FinalSnapshotName", + "target": "FinalSnapshotName" + }, + { + "source": "MultiRegionClusterName", + "target": "MultiRegionClusterName" + } + ], + "operation": "DeleteCluster", + "phase": "delete", + "service": "memorydb" + }, + { + "cfn_type": "AWS::MemoryDB::MultiRegionCluster", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "MultiRegionClusterNameSuffix", + "target": "MultiRegionClusterNameSuffix" + }, + { + "source": "MultiRegionParameterGroupName", + "target": "MultiRegionParameterGroupName" + }, + { + "source": "NodeType", + "target": "NodeType" + }, + { + "source": "NumShards", + "target": "NumShards" + }, + { + "source": "TLSEnabled", + "target": "TLSEnabled" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateMultiRegionCluster", + "phase": "create", + "service": "memorydb" + }, + { + "cfn_type": "AWS::MemoryDB::MultiRegionCluster", + "mappings": [], + "operation": "DeleteMultiRegionCluster", + "phase": "delete", + "service": "memorydb" + }, + { + "cfn_type": "AWS::MemoryDB::ParameterGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Family", + "target": "Family" + }, + { + "source": "ParameterGroupName", + "target": "ParameterGroupName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateParameterGroup", + "phase": "create", + "service": "memorydb" + }, + { + "cfn_type": "AWS::MemoryDB::ParameterGroup", + "mappings": [ + { + "source": "ParameterGroupName", + "target": "ParameterGroupName" + } + ], + "operation": "DeleteParameterGroup", + "phase": "delete", + "service": "memorydb" + }, + { + "cfn_type": "AWS::MemoryDB::SubnetGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "SubnetGroupName", + "target": "SubnetGroupName" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSubnetGroup", + "phase": "create", + "service": "memorydb" + }, + { + "cfn_type": "AWS::MemoryDB::SubnetGroup", + "mappings": [ + { + "source": "SubnetGroupName", + "target": "SubnetGroupName" + } + ], + "operation": "DeleteSubnetGroup", + "phase": "delete", + "service": "memorydb" + }, + { + "cfn_type": "AWS::MemoryDB::User", + "mappings": [ + { + "source": "AccessString", + "target": "AccessString" + }, + { + "source": "AuthenticationMode", + "target": "AuthenticationMode" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UserName", + "target": "UserName" + } + ], + "operation": "CreateUser", + "phase": "create", + "service": "memorydb" + }, + { + "cfn_type": "AWS::MemoryDB::User", + "mappings": [ + { + "source": "UserName", + "target": "UserName" + } + ], + "operation": "DeleteUser", + "phase": "delete", + "service": "memorydb" + }, + { + "cfn_type": "AWS::Neptune::DBCluster", + "mappings": [ + { + "source": "AvailabilityZones", + "target": "AvailabilityZones" + }, + { + "source": "BackupRetentionPeriod", + "target": "BackupRetentionPeriod" + }, + { + "source": "CopyTagsToSnapshot", + "target": "CopyTagsToSnapshot" + }, + { + "source": "DBClusterIdentifier", + "target": "DBClusterIdentifier" + }, + { + "source": "DBClusterParameterGroupName", + "target": "DBClusterParameterGroupName" + }, + { + "source": "DBSubnetGroupName", + "target": "DBSubnetGroupName" + }, + { + "source": "DeletionProtection", + "target": "DeletionProtection" + }, + { + "source": "EnableCloudwatchLogsExports", + "target": "EnableCloudwatchLogsExports" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "GlobalClusterIdentifier", + "target": "GlobalClusterIdentifier" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "PreferredBackupWindow", + "target": "PreferredBackupWindow" + }, + { + "source": "PreferredMaintenanceWindow", + "target": "PreferredMaintenanceWindow" + }, + { + "source": "StorageEncrypted", + "target": "StorageEncrypted" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcSecurityGroupIds", + "target": "VpcSecurityGroupIds" + } + ], + "operation": "CreateDBCluster", + "phase": "create", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::DBCluster", + "mappings": [ + { + "source": "DBClusterIdentifier", + "target": "DBClusterIdentifier" + } + ], + "operation": "DeleteDBCluster", + "phase": "delete", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::DBClusterParameterGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDBClusterParameterGroup", + "phase": "create", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::DBClusterParameterGroup", + "mappings": [], + "operation": "DeleteDBClusterParameterGroup", + "phase": "delete", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::DBInstance", + "mappings": [ + { + "source": "AutoMinorVersionUpgrade", + "target": "AutoMinorVersionUpgrade" + }, + { + "source": "AvailabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "DBClusterIdentifier", + "target": "DBClusterIdentifier" + }, + { + "source": "DBInstanceClass", + "target": "DBInstanceClass" + }, + { + "source": "DBInstanceIdentifier", + "target": "DBInstanceIdentifier" + }, + { + "source": "DBParameterGroupName", + "target": "DBParameterGroupName" + }, + { + "source": "DBSubnetGroupName", + "target": "DBSubnetGroupName" + }, + { + "source": "PreferredMaintenanceWindow", + "target": "PreferredMaintenanceWindow" + }, + { + "source": "PubliclyAccessible", + "target": "PubliclyAccessible" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDBInstance", + "phase": "create", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::DBInstance", + "mappings": [ + { + "source": "DBInstanceIdentifier", + "target": "DBInstanceIdentifier" + } + ], + "operation": "DeleteDBInstance", + "phase": "delete", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::DBParameterGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDBParameterGroup", + "phase": "create", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::DBParameterGroup", + "mappings": [], + "operation": "DeleteDBParameterGroup", + "phase": "delete", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::DBSubnetGroup", + "mappings": [ + { + "source": "DBSubnetGroupDescription", + "target": "DBSubnetGroupDescription" + }, + { + "source": "DBSubnetGroupName", + "target": "DBSubnetGroupName" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDBSubnetGroup", + "phase": "create", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::DBSubnetGroup", + "mappings": [ + { + "source": "DBSubnetGroupName", + "target": "DBSubnetGroupName" + } + ], + "operation": "DeleteDBSubnetGroup", + "phase": "delete", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::EventSubscription", + "mappings": [ + { + "source": "Enabled", + "target": "Enabled" + }, + { + "source": "EventCategories", + "target": "EventCategories" + }, + { + "source": "SnsTopicArn", + "target": "SnsTopicArn" + }, + { + "source": "SourceIds", + "target": "SourceIds" + }, + { + "source": "SourceType", + "target": "SourceType" + }, + { + "source": "SubscriptionName", + "target": "SubscriptionName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEventSubscription", + "phase": "create", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::EventSubscription", + "mappings": [ + { + "source": "SubscriptionName", + "target": "SubscriptionName" + } + ], + "operation": "DeleteEventSubscription", + "phase": "delete", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::GlobalCluster", + "mappings": [ + { + "source": "DeletionProtection", + "target": "DeletionProtection" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "GlobalClusterIdentifier", + "target": "GlobalClusterIdentifier" + }, + { + "source": "SourceDBClusterIdentifier", + "target": "SourceDBClusterIdentifier" + }, + { + "source": "StorageEncrypted", + "target": "StorageEncrypted" + } + ], + "operation": "CreateGlobalCluster", + "phase": "create", + "service": "neptune" + }, + { + "cfn_type": "AWS::Neptune::GlobalCluster", + "mappings": [ + { + "source": "GlobalClusterIdentifier", + "target": "GlobalClusterIdentifier" + } + ], + "operation": "DeleteGlobalCluster", + "phase": "delete", + "service": "neptune" + }, + { + "cfn_type": "AWS::NeptuneGraph::Graph", + "mappings": [ + { + "source": "deletionProtection", + "target": "DeletionProtection" + }, + { + "source": "graphName", + "target": "GraphName" + }, + { + "source": "kmsKeyIdentifier", + "target": "KmsKeyIdentifier" + }, + { + "source": "provisionedMemory", + "target": "ProvisionedMemory" + }, + { + "source": "publicConnectivity", + "target": "PublicConnectivity" + }, + { + "source": "replicaCount", + "target": "ReplicaCount" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "vectorSearchConfiguration", + "target": "VectorSearchConfiguration" + } + ], + "operation": "CreateGraph", + "phase": "create", + "service": "neptune-graph" + }, + { + "cfn_type": "AWS::NeptuneGraph::Graph", + "mappings": [], + "operation": "DeleteGraph", + "phase": "delete", + "service": "neptune-graph" + }, + { + "cfn_type": "AWS::NeptuneGraph::GraphSnapshot", + "mappings": [ + { + "source": "graphIdentifier", + "target": "GraphIdentifier" + }, + { + "source": "snapshotName", + "target": "SnapshotName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateGraphSnapshot", + "phase": "create", + "service": "neptune-graph" + }, + { + "cfn_type": "AWS::NeptuneGraph::GraphSnapshot", + "mappings": [], + "operation": "DeleteGraphSnapshot", + "phase": "delete", + "service": "neptune-graph" + }, + { + "cfn_type": "AWS::NeptuneGraph::PrivateGraphEndpoint", + "mappings": [ + { + "source": "graphIdentifier", + "target": "GraphIdentifier" + }, + { + "source": "subnetIds", + "target": "SubnetIds" + }, + { + "source": "vpcId", + "target": "VpcId" + } + ], + "operation": "CreatePrivateGraphEndpoint", + "phase": "create", + "service": "neptune-graph" + }, + { + "cfn_type": "AWS::NeptuneGraph::PrivateGraphEndpoint", + "mappings": [ + { + "source": "graphIdentifier", + "target": "GraphIdentifier" + }, + { + "source": "vpcId", + "target": "VpcId" + } + ], + "operation": "DeletePrivateGraphEndpoint", + "phase": "delete", + "service": "neptune-graph" + }, + { + "cfn_type": "AWS::NetworkFirewall::Firewall", + "mappings": [ + { + "source": "AvailabilityZoneChangeProtection", + "target": "AvailabilityZoneChangeProtection" + }, + { + "source": "AvailabilityZoneMappings", + "target": "AvailabilityZoneMappings" + }, + { + "source": "DeleteProtection", + "target": "DeleteProtection" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EnabledAnalysisTypes", + "target": "EnabledAnalysisTypes" + }, + { + "source": "FirewallName", + "target": "FirewallName" + }, + { + "source": "FirewallPolicyArn", + "target": "FirewallPolicyArn" + }, + { + "source": "FirewallPolicyChangeProtection", + "target": "FirewallPolicyChangeProtection" + }, + { + "source": "SubnetChangeProtection", + "target": "SubnetChangeProtection" + }, + { + "source": "SubnetMappings", + "target": "SubnetMappings" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TransitGatewayId", + "target": "TransitGatewayId" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateFirewall", + "phase": "create", + "service": "network-firewall" + }, + { + "cfn_type": "AWS::NetworkFirewall::Firewall", + "mappings": [ + { + "source": "FirewallName", + "target": "FirewallName" + } + ], + "operation": "DeleteFirewall", + "phase": "delete", + "service": "network-firewall" + }, + { + "cfn_type": "AWS::NetworkFirewall::FirewallPolicy", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "FirewallPolicy", + "target": "FirewallPolicy" + }, + { + "source": "FirewallPolicyName", + "target": "FirewallPolicyName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateFirewallPolicy", + "phase": "create", + "service": "network-firewall" + }, + { + "cfn_type": "AWS::NetworkFirewall::FirewallPolicy", + "mappings": [ + { + "source": "FirewallPolicyName", + "target": "FirewallPolicyName" + } + ], + "operation": "DeleteFirewallPolicy", + "phase": "delete", + "service": "network-firewall" + }, + { + "cfn_type": "AWS::NetworkFirewall::RuleGroup", + "mappings": [ + { + "source": "Capacity", + "target": "Capacity" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "RuleGroup", + "target": "RuleGroup" + }, + { + "source": "RuleGroupName", + "target": "RuleGroupName" + }, + { + "source": "SummaryConfiguration", + "target": "SummaryConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateRuleGroup", + "phase": "create", + "service": "network-firewall" + }, + { + "cfn_type": "AWS::NetworkFirewall::RuleGroup", + "mappings": [ + { + "source": "RuleGroupName", + "target": "RuleGroupName" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "DeleteRuleGroup", + "phase": "delete", + "service": "network-firewall" + }, + { + "cfn_type": "AWS::NetworkFirewall::TLSInspectionConfiguration", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "TLSInspectionConfiguration", + "target": "TLSInspectionConfiguration" + }, + { + "source": "TLSInspectionConfigurationName", + "target": "TLSInspectionConfigurationName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateTLSInspectionConfiguration", + "phase": "create", + "service": "network-firewall" + }, + { + "cfn_type": "AWS::NetworkFirewall::TLSInspectionConfiguration", + "mappings": [ + { + "source": "TLSInspectionConfigurationName", + "target": "TLSInspectionConfigurationName" + } + ], + "operation": "DeleteTLSInspectionConfiguration", + "phase": "delete", + "service": "network-firewall" + }, + { + "cfn_type": "AWS::NetworkFirewall::VpcEndpointAssociation", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "FirewallArn", + "target": "FirewallArn" + }, + { + "source": "SubnetMapping", + "target": "SubnetMapping" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateVpcEndpointAssociation", + "phase": "create", + "service": "network-firewall" + }, + { + "cfn_type": "AWS::NetworkFirewall::VpcEndpointAssociation", + "mappings": [], + "operation": "DeleteVpcEndpointAssociation", + "phase": "delete", + "service": "network-firewall" + }, + { + "cfn_type": "AWS::NetworkFlowMonitor::Monitor", + "mappings": [ + { + "source": "localResources", + "target": "LocalResources" + }, + { + "source": "monitorName", + "target": "MonitorName" + }, + { + "source": "remoteResources", + "target": "RemoteResources" + }, + { + "source": "scopeArn", + "target": "ScopeArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateMonitor", + "phase": "create", + "service": "networkflowmonitor" + }, + { + "cfn_type": "AWS::NetworkFlowMonitor::Monitor", + "mappings": [ + { + "source": "monitorName", + "target": "MonitorName" + } + ], + "operation": "DeleteMonitor", + "phase": "delete", + "service": "networkflowmonitor" + }, + { + "cfn_type": "AWS::NetworkManager::ConnectAttachment", + "mappings": [ + { + "source": "CoreNetworkId", + "target": "CoreNetworkId" + }, + { + "source": "EdgeLocation", + "target": "EdgeLocation" + }, + { + "source": "Options", + "target": "Options" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TransportAttachmentId", + "target": "TransportAttachmentId" + } + ], + "operation": "CreateConnectAttachment", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::ConnectPeer", + "mappings": [ + { + "source": "BgpOptions", + "target": "BgpOptions" + }, + { + "source": "ConnectAttachmentId", + "target": "ConnectAttachmentId" + }, + { + "source": "CoreNetworkAddress", + "target": "CoreNetworkAddress" + }, + { + "source": "InsideCidrBlocks", + "target": "InsideCidrBlocks" + }, + { + "source": "PeerAddress", + "target": "PeerAddress" + }, + { + "source": "SubnetArn", + "target": "SubnetArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateConnectPeer", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::ConnectPeer", + "mappings": [], + "operation": "DeleteConnectPeer", + "phase": "delete", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::CoreNetwork", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + }, + { + "source": "PolicyDocument", + "target": "PolicyDocument" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCoreNetwork", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::CoreNetwork", + "mappings": [], + "operation": "DeleteCoreNetwork", + "phase": "delete", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::CustomerGatewayAssociation", + "mappings": [ + { + "source": "CustomerGatewayArn", + "target": "CustomerGatewayArn" + }, + { + "source": "DeviceId", + "target": "DeviceId" + }, + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + }, + { + "source": "LinkId", + "target": "LinkId" + } + ], + "operation": "AssociateCustomerGateway", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::CustomerGatewayAssociation", + "mappings": [ + { + "source": "CustomerGatewayArn", + "target": "CustomerGatewayArn" + }, + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + } + ], + "operation": "DisassociateCustomerGateway", + "phase": "delete", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::Device", + "mappings": [ + { + "source": "AWSLocation", + "target": "AWSLocation" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + }, + { + "source": "Location", + "target": "Location" + }, + { + "source": "Model", + "target": "Model" + }, + { + "source": "SerialNumber", + "target": "SerialNumber" + }, + { + "source": "SiteId", + "target": "SiteId" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + }, + { + "source": "Vendor", + "target": "Vendor" + } + ], + "operation": "CreateDevice", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::Device", + "mappings": [ + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + } + ], + "operation": "DeleteDevice", + "phase": "delete", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::DirectConnectGatewayAttachment", + "mappings": [ + { + "source": "CoreNetworkId", + "target": "CoreNetworkId" + }, + { + "source": "DirectConnectGatewayArn", + "target": "DirectConnectGatewayArn" + }, + { + "source": "EdgeLocations", + "target": "EdgeLocations" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDirectConnectGatewayAttachment", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::GlobalNetwork", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateGlobalNetwork", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::GlobalNetwork", + "mappings": [], + "operation": "DeleteGlobalNetwork", + "phase": "delete", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::Link", + "mappings": [ + { + "source": "Bandwidth", + "target": "Bandwidth" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + }, + { + "source": "Provider", + "target": "Provider" + }, + { + "source": "SiteId", + "target": "SiteId" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateLink", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::Link", + "mappings": [ + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + } + ], + "operation": "DeleteLink", + "phase": "delete", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::LinkAssociation", + "mappings": [ + { + "source": "DeviceId", + "target": "DeviceId" + }, + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + }, + { + "source": "LinkId", + "target": "LinkId" + } + ], + "operation": "AssociateLink", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::LinkAssociation", + "mappings": [ + { + "source": "DeviceId", + "target": "DeviceId" + }, + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + }, + { + "source": "LinkId", + "target": "LinkId" + } + ], + "operation": "DisassociateLink", + "phase": "delete", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::Site", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + }, + { + "source": "Location", + "target": "Location" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSite", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::Site", + "mappings": [ + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + } + ], + "operation": "DeleteSite", + "phase": "delete", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::SiteToSiteVpnAttachment", + "mappings": [ + { + "source": "CoreNetworkId", + "target": "CoreNetworkId" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpnConnectionArn", + "target": "VpnConnectionArn" + } + ], + "operation": "CreateSiteToSiteVpnAttachment", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::TransitGatewayPeering", + "mappings": [ + { + "source": "CoreNetworkId", + "target": "CoreNetworkId" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TransitGatewayArn", + "target": "TransitGatewayArn" + } + ], + "operation": "CreateTransitGatewayPeering", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::TransitGatewayRegistration", + "mappings": [ + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + }, + { + "source": "TransitGatewayArn", + "target": "TransitGatewayArn" + } + ], + "operation": "RegisterTransitGateway", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::TransitGatewayRegistration", + "mappings": [ + { + "source": "GlobalNetworkId", + "target": "GlobalNetworkId" + }, + { + "source": "TransitGatewayArn", + "target": "TransitGatewayArn" + } + ], + "operation": "DeregisterTransitGateway", + "phase": "delete", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::TransitGatewayRouteTableAttachment", + "mappings": [ + { + "source": "PeeringId", + "target": "PeeringId" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TransitGatewayRouteTableArn", + "target": "TransitGatewayRouteTableArn" + } + ], + "operation": "CreateTransitGatewayRouteTableAttachment", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::VpcAttachment", + "mappings": [ + { + "source": "CoreNetworkId", + "target": "CoreNetworkId" + }, + { + "source": "Options", + "target": "Options" + }, + { + "source": "SubnetArns", + "target": "SubnetArns" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcArn", + "target": "VpcArn" + } + ], + "operation": "CreateVpcAttachment", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::Notifications::ChannelAssociation", + "mappings": [ + { + "source": "arn", + "target": "Arn" + }, + { + "source": "notificationConfigurationArn", + "target": "NotificationConfigurationArn" + } + ], + "operation": "AssociateChannel", + "phase": "create", + "service": "notifications" + }, + { + "cfn_type": "AWS::Notifications::ChannelAssociation", + "mappings": [ + { + "source": "arn", + "target": "Arn" + }, + { + "source": "notificationConfigurationArn", + "target": "NotificationConfigurationArn" + } + ], + "operation": "DisassociateChannel", + "phase": "delete", + "service": "notifications" + }, + { + "cfn_type": "AWS::Notifications::EventRule", + "mappings": [ + { + "source": "eventPattern", + "target": "EventPattern" + }, + { + "source": "eventType", + "target": "EventType" + }, + { + "source": "notificationConfigurationArn", + "target": "NotificationConfigurationArn" + }, + { + "source": "regions", + "target": "Regions" + }, + { + "source": "source", + "target": "Source" + } + ], + "operation": "CreateEventRule", + "phase": "create", + "service": "notifications" + }, + { + "cfn_type": "AWS::Notifications::EventRule", + "mappings": [], + "operation": "DeleteEventRule", + "phase": "delete", + "service": "notifications" + }, + { + "cfn_type": "AWS::Notifications::ManagedNotificationAccountContactAssociation", + "mappings": [ + { + "source": "contactIdentifier", + "target": "ContactIdentifier" + }, + { + "source": "managedNotificationConfigurationArn", + "target": "ManagedNotificationConfigurationArn" + } + ], + "operation": "AssociateManagedNotificationAccountContact", + "phase": "create", + "service": "notifications" + }, + { + "cfn_type": "AWS::Notifications::ManagedNotificationAccountContactAssociation", + "mappings": [ + { + "source": "contactIdentifier", + "target": "ContactIdentifier" + }, + { + "source": "managedNotificationConfigurationArn", + "target": "ManagedNotificationConfigurationArn" + } + ], + "operation": "DisassociateManagedNotificationAccountContact", + "phase": "delete", + "service": "notifications" + }, + { + "cfn_type": "AWS::Notifications::ManagedNotificationAdditionalChannelAssociation", + "mappings": [ + { + "source": "channelArn", + "target": "ChannelArn" + }, + { + "source": "managedNotificationConfigurationArn", + "target": "ManagedNotificationConfigurationArn" + } + ], + "operation": "AssociateManagedNotificationAdditionalChannel", + "phase": "create", + "service": "notifications" + }, + { + "cfn_type": "AWS::Notifications::ManagedNotificationAdditionalChannelAssociation", + "mappings": [ + { + "source": "channelArn", + "target": "ChannelArn" + }, + { + "source": "managedNotificationConfigurationArn", + "target": "ManagedNotificationConfigurationArn" + } + ], + "operation": "DisassociateManagedNotificationAdditionalChannel", + "phase": "delete", + "service": "notifications" + }, + { + "cfn_type": "AWS::Notifications::NotificationConfiguration", + "mappings": [ + { + "source": "aggregationDuration", + "target": "AggregationDuration" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateNotificationConfiguration", + "phase": "create", + "service": "notifications" + }, + { + "cfn_type": "AWS::Notifications::NotificationConfiguration", + "mappings": [], + "operation": "DeleteNotificationConfiguration", + "phase": "delete", + "service": "notifications" + }, + { + "cfn_type": "AWS::Notifications::NotificationHub", + "mappings": [], + "operation": "DeregisterNotificationHub", + "phase": "delete", + "service": "notifications" + }, + { + "cfn_type": "AWS::NotificationsContacts::EmailContact", + "mappings": [ + { + "source": "emailAddress", + "target": "EmailAddress" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateEmailContact", + "phase": "create", + "service": "notificationscontacts" + }, + { + "cfn_type": "AWS::NotificationsContacts::EmailContact", + "mappings": [], + "operation": "DeleteEmailContact", + "phase": "delete", + "service": "notificationscontacts" + }, + { + "cfn_type": "AWS::ODB::CloudAutonomousVmCluster", + "mappings": [ + { + "source": "autonomousDataStorageSizeInTBs", + "target": "AutonomousDataStorageSizeInTBs" + }, + { + "source": "cloudExadataInfrastructureId", + "target": "CloudExadataInfrastructureId" + }, + { + "source": "cpuCoreCountPerNode", + "target": "CpuCoreCountPerNode" + }, + { + "source": "dbServers", + "target": "DbServers" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "isMtlsEnabledVmCluster", + "target": "IsMtlsEnabledVmCluster" + }, + { + "source": "licenseModel", + "target": "LicenseModel" + }, + { + "source": "maintenanceWindow", + "target": "MaintenanceWindow" + }, + { + "source": "memoryPerOracleComputeUnitInGBs", + "target": "MemoryPerOracleComputeUnitInGBs" + }, + { + "source": "odbNetworkId", + "target": "OdbNetworkId" + }, + { + "source": "scanListenerPortNonTls", + "target": "ScanListenerPortNonTls" + }, + { + "source": "scanListenerPortTls", + "target": "ScanListenerPortTls" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "timeZone", + "target": "TimeZone" + }, + { + "source": "totalContainerDatabases", + "target": "TotalContainerDatabases" + } + ], + "operation": "CreateCloudAutonomousVmCluster", + "phase": "create", + "service": "odb" + }, + { + "cfn_type": "AWS::ODB::CloudAutonomousVmCluster", + "mappings": [], + "operation": "DeleteCloudAutonomousVmCluster", + "phase": "delete", + "service": "odb" + }, + { + "cfn_type": "AWS::ODB::CloudExadataInfrastructure", + "mappings": [ + { + "source": "availabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "availabilityZoneId", + "target": "AvailabilityZoneId" + }, + { + "source": "computeCount", + "target": "ComputeCount" + }, + { + "source": "customerContactsToSendToOCI", + "target": "CustomerContactsToSendToOCI" + }, + { + "source": "databaseServerType", + "target": "DatabaseServerType" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "maintenanceWindow", + "target": "MaintenanceWindow" + }, + { + "source": "shape", + "target": "Shape" + }, + { + "source": "storageCount", + "target": "StorageCount" + }, + { + "source": "storageServerType", + "target": "StorageServerType" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCloudExadataInfrastructure", + "phase": "create", + "service": "odb" + }, + { + "cfn_type": "AWS::ODB::CloudExadataInfrastructure", + "mappings": [], + "operation": "DeleteCloudExadataInfrastructure", + "phase": "delete", + "service": "odb" + }, + { + "cfn_type": "AWS::ODB::CloudVmCluster", + "mappings": [ + { + "source": "cloudExadataInfrastructureId", + "target": "CloudExadataInfrastructureId" + }, + { + "source": "clusterName", + "target": "ClusterName" + }, + { + "source": "cpuCoreCount", + "target": "CpuCoreCount" + }, + { + "source": "dataCollectionOptions", + "target": "DataCollectionOptions" + }, + { + "source": "dataStorageSizeInTBs", + "target": "DataStorageSizeInTBs" + }, + { + "source": "dbNodeStorageSizeInGBs", + "target": "DbNodeStorageSizeInGBs" + }, + { + "source": "dbServers", + "target": "DbServers" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "giVersion", + "target": "GiVersion" + }, + { + "source": "hostname", + "target": "Hostname" + }, + { + "source": "isLocalBackupEnabled", + "target": "IsLocalBackupEnabled" + }, + { + "source": "isSparseDiskgroupEnabled", + "target": "IsSparseDiskgroupEnabled" + }, + { + "source": "licenseModel", + "target": "LicenseModel" + }, + { + "source": "memorySizeInGBs", + "target": "MemorySizeInGBs" + }, + { + "source": "odbNetworkId", + "target": "OdbNetworkId" + }, + { + "source": "scanListenerPortTcp", + "target": "ScanListenerPortTcp" + }, + { + "source": "sshPublicKeys", + "target": "SshPublicKeys" + }, + { + "source": "systemVersion", + "target": "SystemVersion" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "timeZone", + "target": "TimeZone" + } + ], + "operation": "CreateCloudVmCluster", + "phase": "create", + "service": "odb" + }, + { + "cfn_type": "AWS::ODB::CloudVmCluster", + "mappings": [], + "operation": "DeleteCloudVmCluster", + "phase": "delete", + "service": "odb" + }, + { + "cfn_type": "AWS::ODB::OdbNetwork", + "mappings": [ + { + "source": "availabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "availabilityZoneId", + "target": "AvailabilityZoneId" + }, + { + "source": "backupSubnetCidr", + "target": "BackupSubnetCidr" + }, + { + "source": "clientSubnetCidr", + "target": "ClientSubnetCidr" + }, + { + "source": "customDomainName", + "target": "CustomDomainName" + }, + { + "source": "defaultDnsPrefix", + "target": "DefaultDnsPrefix" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "s3Access", + "target": "S3Access" + }, + { + "source": "s3PolicyDocument", + "target": "S3PolicyDocument" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "zeroEtlAccess", + "target": "ZeroEtlAccess" + } + ], + "operation": "CreateOdbNetwork", + "phase": "create", + "service": "odb" + }, + { + "cfn_type": "AWS::ODB::OdbNetwork", + "mappings": [ + { + "source": "deleteAssociatedResources", + "target": "DeleteAssociatedResources" + } + ], + "operation": "DeleteOdbNetwork", + "phase": "delete", + "service": "odb" + }, + { + "cfn_type": "AWS::ODB::OdbPeeringConnection", + "mappings": [ + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "odbNetworkId", + "target": "OdbNetworkId" + }, + { + "source": "peerNetworkId", + "target": "PeerNetworkId" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateOdbPeeringConnection", + "phase": "create", + "service": "odb" + }, + { + "cfn_type": "AWS::ODB::OdbPeeringConnection", + "mappings": [], + "operation": "DeleteOdbPeeringConnection", + "phase": "delete", + "service": "odb" + }, + { + "cfn_type": "AWS::OSIS::Pipeline", + "mappings": [ + { + "source": "BufferOptions", + "target": "BufferOptions" + }, + { + "source": "EncryptionAtRestOptions", + "target": "EncryptionAtRestOptions" + }, + { + "source": "LogPublishingOptions", + "target": "LogPublishingOptions" + }, + { + "source": "MaxUnits", + "target": "MaxUnits" + }, + { + "source": "MinUnits", + "target": "MinUnits" + }, + { + "source": "PipelineConfigurationBody", + "target": "PipelineConfigurationBody" + }, + { + "source": "PipelineName", + "target": "PipelineName" + }, + { + "source": "PipelineRoleArn", + "target": "PipelineRoleArn" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcOptions", + "target": "VpcOptions" + } + ], + "operation": "CreatePipeline", + "phase": "create", + "service": "osis" + }, + { + "cfn_type": "AWS::OSIS::Pipeline", + "mappings": [ + { + "source": "PipelineName", + "target": "PipelineName" + } + ], + "operation": "DeletePipeline", + "phase": "delete", + "service": "osis" + }, + { + "cfn_type": "AWS::Oam::Link", + "mappings": [ + { + "source": "LabelTemplate", + "target": "LabelTemplate" + }, + { + "source": "LinkConfiguration", + "target": "LinkConfiguration" + }, + { + "source": "ResourceTypes", + "target": "ResourceTypes" + }, + { + "source": "SinkIdentifier", + "target": "SinkIdentifier" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLink", + "phase": "create", + "service": "oam" + }, + { + "cfn_type": "AWS::Oam::Link", + "mappings": [], + "operation": "DeleteLink", + "phase": "delete", + "service": "oam" + }, + { + "cfn_type": "AWS::Oam::Sink", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSink", + "phase": "create", + "service": "oam" + }, + { + "cfn_type": "AWS::Oam::Sink", + "mappings": [], + "operation": "DeleteSink", + "phase": "delete", + "service": "oam" + }, + { + "cfn_type": "AWS::ObservabilityAdmin::OrganizationTelemetryRule", + "mappings": [ + { + "source": "Rule", + "target": "Rule" + }, + { + "source": "RuleName", + "target": "RuleName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateTelemetryRuleForOrganization", + "phase": "create", + "service": "observabilityadmin" + }, + { + "cfn_type": "AWS::ObservabilityAdmin::TelemetryRule", + "mappings": [ + { + "source": "Rule", + "target": "Rule" + }, + { + "source": "RuleName", + "target": "RuleName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateTelemetryRule", + "phase": "create", + "service": "observabilityadmin" + }, + { + "cfn_type": "AWS::ObservabilityAdmin::TelemetryRule", + "mappings": [], + "operation": "DeleteTelemetryRule", + "phase": "delete", + "service": "observabilityadmin" + }, + { + "cfn_type": "AWS::Omics::AnnotationStore", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "reference", + "target": "Reference" + }, + { + "source": "sseConfig", + "target": "SseConfig" + }, + { + "source": "storeFormat", + "target": "StoreFormat" + }, + { + "source": "storeOptions", + "target": "StoreOptions" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAnnotationStore", + "phase": "create", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::AnnotationStore", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteAnnotationStore", + "phase": "delete", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::ReferenceStore", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "sseConfig", + "target": "SseConfig" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateReferenceStore", + "phase": "create", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::ReferenceStore", + "mappings": [], + "operation": "DeleteReferenceStore", + "phase": "delete", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::RunGroup", + "mappings": [ + { + "source": "maxCpus", + "target": "MaxCpus" + }, + { + "source": "maxDuration", + "target": "MaxDuration" + }, + { + "source": "maxGpus", + "target": "MaxGpus" + }, + { + "source": "maxRuns", + "target": "MaxRuns" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRunGroup", + "phase": "create", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::RunGroup", + "mappings": [], + "operation": "DeleteRunGroup", + "phase": "delete", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::SequenceStore", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "eTagAlgorithmFamily", + "target": "ETagAlgorithmFamily" + }, + { + "source": "fallbackLocation", + "target": "FallbackLocation" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "propagatedSetLevelTags", + "target": "PropagatedSetLevelTags" + }, + { + "source": "sseConfig", + "target": "SseConfig" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSequenceStore", + "phase": "create", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::SequenceStore", + "mappings": [], + "operation": "DeleteSequenceStore", + "phase": "delete", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::VariantStore", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "reference", + "target": "Reference" + }, + { + "source": "sseConfig", + "target": "SseConfig" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateVariantStore", + "phase": "create", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::VariantStore", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteVariantStore", + "phase": "delete", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::Workflow", + "mappings": [ + { + "source": "accelerators", + "target": "Accelerators" + }, + { + "source": "definitionRepository", + "target": "DefinitionRepository" + }, + { + "source": "definitionUri", + "target": "DefinitionUri" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "engine", + "target": "Engine" + }, + { + "source": "main", + "target": "Main" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "parameterTemplate", + "target": "ParameterTemplate" + }, + { + "source": "parameterTemplatePath", + "target": "ParameterTemplatePath" + }, + { + "source": "readmeMarkdown", + "target": "readmeMarkdown" + }, + { + "source": "readmePath", + "target": "readmePath" + }, + { + "source": "readmeUri", + "target": "readmeUri" + }, + { + "source": "storageCapacity", + "target": "StorageCapacity" + }, + { + "source": "storageType", + "target": "StorageType" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workflowBucketOwnerId", + "target": "WorkflowBucketOwnerId" + } + ], + "operation": "CreateWorkflow", + "phase": "create", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::Workflow", + "mappings": [], + "operation": "DeleteWorkflow", + "phase": "delete", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::WorkflowVersion", + "mappings": [ + { + "source": "accelerators", + "target": "Accelerators" + }, + { + "source": "definitionRepository", + "target": "DefinitionRepository" + }, + { + "source": "definitionUri", + "target": "DefinitionUri" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "engine", + "target": "Engine" + }, + { + "source": "main", + "target": "Main" + }, + { + "source": "parameterTemplate", + "target": "ParameterTemplate" + }, + { + "source": "parameterTemplatePath", + "target": "ParameterTemplatePath" + }, + { + "source": "readmeMarkdown", + "target": "readmeMarkdown" + }, + { + "source": "readmePath", + "target": "readmePath" + }, + { + "source": "readmeUri", + "target": "readmeUri" + }, + { + "source": "storageCapacity", + "target": "StorageCapacity" + }, + { + "source": "storageType", + "target": "StorageType" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "versionName", + "target": "VersionName" + }, + { + "source": "workflowBucketOwnerId", + "target": "WorkflowBucketOwnerId" + }, + { + "source": "workflowId", + "target": "WorkflowId" + } + ], + "operation": "CreateWorkflowVersion", + "phase": "create", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::WorkflowVersion", + "mappings": [ + { + "source": "versionName", + "target": "VersionName" + }, + { + "source": "workflowId", + "target": "WorkflowId" + } + ], + "operation": "DeleteWorkflowVersion", + "phase": "delete", + "service": "omics" + }, + { + "cfn_type": "AWS::OpenSearchServerless::AccessPolicy", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "policy", + "target": "Policy" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateAccessPolicy", + "phase": "create", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::AccessPolicy", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "DeleteAccessPolicy", + "phase": "delete", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::Collection", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "standbyReplicas", + "target": "StandbyReplicas" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateCollection", + "phase": "create", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::Collection", + "mappings": [], + "operation": "DeleteCollection", + "phase": "delete", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::CollectionIndex", + "mappings": [ + { + "source": "id", + "target": "Id" + }, + { + "source": "indexName", + "target": "IndexName" + }, + { + "source": "indexSchema", + "target": "IndexSchema" + } + ], + "operation": "CreateIndex", + "phase": "create", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::CollectionIndex", + "mappings": [ + { + "source": "id", + "target": "Id" + }, + { + "source": "indexName", + "target": "IndexName" + } + ], + "operation": "DeleteIndex", + "phase": "delete", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::LifecyclePolicy", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "policy", + "target": "Policy" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateLifecyclePolicy", + "phase": "create", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::LifecyclePolicy", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "DeleteLifecyclePolicy", + "phase": "delete", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::SecurityConfig", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "iamFederationOptions", + "target": "IamFederationOptions" + }, + { + "source": "iamIdentityCenterOptions", + "target": "IamIdentityCenterOptions" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "samlOptions", + "target": "SamlOptions" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateSecurityConfig", + "phase": "create", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::SecurityConfig", + "mappings": [], + "operation": "DeleteSecurityConfig", + "phase": "delete", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::SecurityPolicy", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "policy", + "target": "Policy" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateSecurityPolicy", + "phase": "create", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::SecurityPolicy", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "DeleteSecurityPolicy", + "phase": "delete", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::VpcEndpoint", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "securityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "subnetIds", + "target": "SubnetIds" + }, + { + "source": "vpcId", + "target": "VpcId" + } + ], + "operation": "CreateVpcEndpoint", + "phase": "create", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::VpcEndpoint", + "mappings": [], + "operation": "DeleteVpcEndpoint", + "phase": "delete", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchService::Application", + "mappings": [], + "operation": "DeleteApplication", + "phase": "delete", + "service": "opensearch" + }, + { + "cfn_type": "AWS::OpenSearchService::Domain", + "mappings": [ + { + "source": "DomainName", + "target": "DomainName" + } + ], + "operation": "DeleteDomain", + "phase": "delete", + "service": "opensearch" + }, + { + "cfn_type": "AWS::Organizations::Account", + "mappings": [ + { + "source": "AccountName", + "target": "AccountName" + }, + { + "source": "Email", + "target": "Email" + }, + { + "source": "RoleName", + "target": "RoleName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAccount", + "phase": "create", + "service": "organizations" + }, + { + "cfn_type": "AWS::Organizations::Organization", + "mappings": [ + { + "source": "FeatureSet", + "target": "FeatureSet" + } + ], + "operation": "CreateOrganization", + "phase": "create", + "service": "organizations" + }, + { + "cfn_type": "AWS::Organizations::Organization", + "mappings": [], + "operation": "DeleteOrganization", + "phase": "delete", + "service": "organizations" + }, + { + "cfn_type": "AWS::Organizations::OrganizationalUnit", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "ParentId", + "target": "ParentId" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateOrganizationalUnit", + "phase": "create", + "service": "organizations" + }, + { + "cfn_type": "AWS::Organizations::OrganizationalUnit", + "mappings": [], + "operation": "DeleteOrganizationalUnit", + "phase": "delete", + "service": "organizations" + }, + { + "cfn_type": "AWS::Organizations::Policy", + "mappings": [ + { + "source": "Content", + "target": "Content" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreatePolicy", + "phase": "create", + "service": "organizations" + }, + { + "cfn_type": "AWS::Organizations::ResourcePolicy", + "mappings": [ + { + "source": "Content", + "target": "Content" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "organizations" + }, + { + "cfn_type": "AWS::Organizations::ResourcePolicy", + "mappings": [], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "organizations" + }, + { + "cfn_type": "AWS::Outposts::Site", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Notes", + "target": "Notes" + }, + { + "source": "OperatingAddress", + "target": "OperatingAddress" + }, + { + "source": "RackPhysicalProperties", + "target": "RackPhysicalProperties" + }, + { + "source": "ShippingAddress", + "target": "ShippingAddress" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSite", + "phase": "create", + "service": "outposts" + }, + { + "cfn_type": "AWS::Outposts::Site", + "mappings": [], + "operation": "DeleteSite", + "phase": "delete", + "service": "outposts" + }, + { + "cfn_type": "AWS::PCAConnectorAD::Connector", + "mappings": [ + { + "source": "CertificateAuthorityArn", + "target": "CertificateAuthorityArn" + }, + { + "source": "DirectoryId", + "target": "DirectoryId" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcInformation", + "target": "VpcInformation" + } + ], + "operation": "CreateConnector", + "phase": "create", + "service": "pca-connector-ad" + }, + { + "cfn_type": "AWS::PCAConnectorAD::Connector", + "mappings": [], + "operation": "DeleteConnector", + "phase": "delete", + "service": "pca-connector-ad" + }, + { + "cfn_type": "AWS::PCAConnectorAD::DirectoryRegistration", + "mappings": [ + { + "source": "DirectoryId", + "target": "DirectoryId" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDirectoryRegistration", + "phase": "create", + "service": "pca-connector-ad" + }, + { + "cfn_type": "AWS::PCAConnectorAD::DirectoryRegistration", + "mappings": [], + "operation": "DeleteDirectoryRegistration", + "phase": "delete", + "service": "pca-connector-ad" + }, + { + "cfn_type": "AWS::PCAConnectorAD::ServicePrincipalName", + "mappings": [ + { + "source": "ConnectorArn", + "target": "ConnectorArn" + }, + { + "source": "DirectoryRegistrationArn", + "target": "DirectoryRegistrationArn" + } + ], + "operation": "CreateServicePrincipalName", + "phase": "create", + "service": "pca-connector-ad" + }, + { + "cfn_type": "AWS::PCAConnectorAD::ServicePrincipalName", + "mappings": [ + { + "source": "ConnectorArn", + "target": "ConnectorArn" + }, + { + "source": "DirectoryRegistrationArn", + "target": "DirectoryRegistrationArn" + } + ], + "operation": "DeleteServicePrincipalName", + "phase": "delete", + "service": "pca-connector-ad" + }, + { + "cfn_type": "AWS::PCAConnectorAD::Template", + "mappings": [ + { + "source": "ConnectorArn", + "target": "ConnectorArn" + }, + { + "source": "Definition", + "target": "Definition" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateTemplate", + "phase": "create", + "service": "pca-connector-ad" + }, + { + "cfn_type": "AWS::PCAConnectorAD::Template", + "mappings": [], + "operation": "DeleteTemplate", + "phase": "delete", + "service": "pca-connector-ad" + }, + { + "cfn_type": "AWS::PCAConnectorAD::TemplateGroupAccessControlEntry", + "mappings": [ + { + "source": "AccessRights", + "target": "AccessRights" + }, + { + "source": "GroupDisplayName", + "target": "GroupDisplayName" + }, + { + "source": "GroupSecurityIdentifier", + "target": "GroupSecurityIdentifier" + }, + { + "source": "TemplateArn", + "target": "TemplateArn" + } + ], + "operation": "CreateTemplateGroupAccessControlEntry", + "phase": "create", + "service": "pca-connector-ad" + }, + { + "cfn_type": "AWS::PCAConnectorAD::TemplateGroupAccessControlEntry", + "mappings": [ + { + "source": "GroupSecurityIdentifier", + "target": "GroupSecurityIdentifier" + }, + { + "source": "TemplateArn", + "target": "TemplateArn" + } + ], + "operation": "DeleteTemplateGroupAccessControlEntry", + "phase": "delete", + "service": "pca-connector-ad" + }, + { + "cfn_type": "AWS::PCAConnectorSCEP::Challenge", + "mappings": [ + { + "source": "ConnectorArn", + "target": "ConnectorArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateChallenge", + "phase": "create", + "service": "pca-connector-scep" + }, + { + "cfn_type": "AWS::PCAConnectorSCEP::Challenge", + "mappings": [], + "operation": "DeleteChallenge", + "phase": "delete", + "service": "pca-connector-scep" + }, + { + "cfn_type": "AWS::PCAConnectorSCEP::Connector", + "mappings": [ + { + "source": "CertificateAuthorityArn", + "target": "CertificateAuthorityArn" + }, + { + "source": "MobileDeviceManagement", + "target": "MobileDeviceManagement" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateConnector", + "phase": "create", + "service": "pca-connector-scep" + }, + { + "cfn_type": "AWS::PCAConnectorSCEP::Connector", + "mappings": [], + "operation": "DeleteConnector", + "phase": "delete", + "service": "pca-connector-scep" + }, + { + "cfn_type": "AWS::PCS::Cluster", + "mappings": [ + { + "source": "networking", + "target": "Networking" + }, + { + "source": "scheduler", + "target": "Scheduler" + }, + { + "source": "size", + "target": "Size" + }, + { + "source": "slurmConfiguration", + "target": "SlurmConfiguration" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateCluster", + "phase": "create", + "service": "pcs" + }, + { + "cfn_type": "AWS::PCS::Cluster", + "mappings": [], + "operation": "DeleteCluster", + "phase": "delete", + "service": "pcs" + }, + { + "cfn_type": "AWS::PCS::ComputeNodeGroup", + "mappings": [ + { + "source": "amiId", + "target": "AmiId" + }, + { + "source": "customLaunchTemplate", + "target": "CustomLaunchTemplate" + }, + { + "source": "iamInstanceProfileArn", + "target": "IamInstanceProfileArn" + }, + { + "source": "instanceConfigs", + "target": "InstanceConfigs" + }, + { + "source": "purchaseOption", + "target": "PurchaseOption" + }, + { + "source": "scalingConfiguration", + "target": "ScalingConfiguration" + }, + { + "source": "slurmConfiguration", + "target": "SlurmConfiguration" + }, + { + "source": "spotOptions", + "target": "SpotOptions" + }, + { + "source": "subnetIds", + "target": "SubnetIds" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateComputeNodeGroup", + "phase": "create", + "service": "pcs" + }, + { + "cfn_type": "AWS::PCS::ComputeNodeGroup", + "mappings": [], + "operation": "DeleteComputeNodeGroup", + "phase": "delete", + "service": "pcs" + }, + { + "cfn_type": "AWS::PCS::Queue", + "mappings": [ + { + "source": "computeNodeGroupConfigurations", + "target": "ComputeNodeGroupConfigurations" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateQueue", + "phase": "create", + "service": "pcs" + }, + { + "cfn_type": "AWS::PCS::Queue", + "mappings": [], + "operation": "DeleteQueue", + "phase": "delete", + "service": "pcs" + }, + { + "cfn_type": "AWS::Panorama::ApplicationInstance", + "mappings": [ + { + "source": "ApplicationInstanceIdToReplace", + "target": "ApplicationInstanceIdToReplace" + }, + { + "source": "DefaultRuntimeContextDevice", + "target": "DefaultRuntimeContextDevice" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "ManifestOverridesPayload", + "target": "ManifestOverridesPayload" + }, + { + "source": "ManifestPayload", + "target": "ManifestPayload" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RuntimeRoleArn", + "target": "RuntimeRoleArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateApplicationInstance", + "phase": "create", + "service": "panorama" + }, + { + "cfn_type": "AWS::Panorama::ApplicationInstance", + "mappings": [], + "operation": "RemoveApplicationInstance", + "phase": "delete", + "service": "panorama" + }, + { + "cfn_type": "AWS::Panorama::Package", + "mappings": [ + { + "source": "PackageName", + "target": "PackageName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePackage", + "phase": "create", + "service": "panorama" + }, + { + "cfn_type": "AWS::Panorama::Package", + "mappings": [], + "operation": "DeletePackage", + "phase": "delete", + "service": "panorama" + }, + { + "cfn_type": "AWS::Panorama::PackageVersion", + "mappings": [ + { + "source": "MarkLatest", + "target": "MarkLatest" + }, + { + "source": "OwnerAccount", + "target": "OwnerAccount" + }, + { + "source": "PackageId", + "target": "PackageId" + }, + { + "source": "PackageVersion", + "target": "PackageVersion" + }, + { + "source": "PatchVersion", + "target": "PatchVersion" + } + ], + "operation": "RegisterPackageVersion", + "phase": "create", + "service": "panorama" + }, + { + "cfn_type": "AWS::Panorama::PackageVersion", + "mappings": [ + { + "source": "OwnerAccount", + "target": "OwnerAccount" + }, + { + "source": "PackageId", + "target": "PackageId" + }, + { + "source": "PackageVersion", + "target": "PackageVersion" + }, + { + "source": "PatchVersion", + "target": "PatchVersion" + }, + { + "source": "UpdatedLatestPatchVersion", + "target": "UpdatedLatestPatchVersion" + } + ], + "operation": "DeregisterPackageVersion", + "phase": "delete", + "service": "panorama" + }, + { + "cfn_type": "AWS::PaymentCryptography::Alias", + "mappings": [ + { + "source": "AliasName", + "target": "AliasName" + }, + { + "source": "KeyArn", + "target": "KeyArn" + } + ], + "operation": "CreateAlias", + "phase": "create", + "service": "payment-cryptography" + }, + { + "cfn_type": "AWS::PaymentCryptography::Alias", + "mappings": [ + { + "source": "AliasName", + "target": "AliasName" + } + ], + "operation": "DeleteAlias", + "phase": "delete", + "service": "payment-cryptography" + }, + { + "cfn_type": "AWS::PaymentCryptography::Key", + "mappings": [ + { + "source": "DeriveKeyUsage", + "target": "DeriveKeyUsage" + }, + { + "source": "Enabled", + "target": "Enabled" + }, + { + "source": "Exportable", + "target": "Exportable" + }, + { + "source": "KeyAttributes", + "target": "KeyAttributes" + }, + { + "source": "KeyCheckValueAlgorithm", + "target": "KeyCheckValueAlgorithm" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateKey", + "phase": "create", + "service": "payment-cryptography" + }, + { + "cfn_type": "AWS::PaymentCryptography::Key", + "mappings": [], + "operation": "DeleteKey", + "phase": "delete", + "service": "payment-cryptography" + }, + { + "cfn_type": "AWS::Personalize::Dataset", + "mappings": [ + { + "source": "datasetGroupArn", + "target": "DatasetGroupArn" + }, + { + "source": "datasetType", + "target": "DatasetType" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "schemaArn", + "target": "SchemaArn" + } + ], + "operation": "CreateDataset", + "phase": "create", + "service": "personalize" + }, + { + "cfn_type": "AWS::Personalize::Dataset", + "mappings": [], + "operation": "DeleteDataset", + "phase": "delete", + "service": "personalize" + }, + { + "cfn_type": "AWS::Personalize::DatasetGroup", + "mappings": [ + { + "source": "domain", + "target": "Domain" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "roleArn", + "target": "RoleArn" + } + ], + "operation": "CreateDatasetGroup", + "phase": "create", + "service": "personalize" + }, + { + "cfn_type": "AWS::Personalize::DatasetGroup", + "mappings": [], + "operation": "DeleteDatasetGroup", + "phase": "delete", + "service": "personalize" + }, + { + "cfn_type": "AWS::Personalize::Schema", + "mappings": [ + { + "source": "domain", + "target": "Domain" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "schema", + "target": "Schema" + } + ], + "operation": "CreateSchema", + "phase": "create", + "service": "personalize" + }, + { + "cfn_type": "AWS::Personalize::Schema", + "mappings": [], + "operation": "DeleteSchema", + "phase": "delete", + "service": "personalize" + }, + { + "cfn_type": "AWS::Personalize::Solution", + "mappings": [ + { + "source": "datasetGroupArn", + "target": "DatasetGroupArn" + }, + { + "source": "eventType", + "target": "EventType" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "performAutoML", + "target": "PerformAutoML" + }, + { + "source": "performHPO", + "target": "PerformHPO" + }, + { + "source": "recipeArn", + "target": "RecipeArn" + }, + { + "source": "solutionConfig", + "target": "SolutionConfig" + } + ], + "operation": "CreateSolution", + "phase": "create", + "service": "personalize" + }, + { + "cfn_type": "AWS::Personalize::Solution", + "mappings": [], + "operation": "DeleteSolution", + "phase": "delete", + "service": "personalize" + }, + { + "cfn_type": "AWS::Pinpoint::InAppTemplate", + "mappings": [ + { + "source": "TemplateName", + "target": "TemplateName" + } + ], + "operation": "CreateInAppTemplate", + "phase": "create", + "service": "pinpoint" + }, + { + "cfn_type": "AWS::Pinpoint::InAppTemplate", + "mappings": [ + { + "source": "TemplateName", + "target": "TemplateName" + } + ], + "operation": "DeleteInAppTemplate", + "phase": "delete", + "service": "pinpoint" + }, + { + "cfn_type": "AWS::Pipes::Pipe", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DesiredState", + "target": "DesiredState" + }, + { + "source": "Enrichment", + "target": "Enrichment" + }, + { + "source": "EnrichmentParameters", + "target": "EnrichmentParameters" + }, + { + "source": "KmsKeyIdentifier", + "target": "KmsKeyIdentifier" + }, + { + "source": "LogConfiguration", + "target": "LogConfiguration" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Source", + "target": "Source" + }, + { + "source": "SourceParameters", + "target": "SourceParameters" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Target", + "target": "Target" + }, + { + "source": "TargetParameters", + "target": "TargetParameters" + } + ], + "operation": "CreatePipe", + "phase": "create", + "service": "pipes" + }, + { + "cfn_type": "AWS::Pipes::Pipe", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeletePipe", + "phase": "delete", + "service": "pipes" + }, + { + "cfn_type": "AWS::Proton::EnvironmentAccountConnection", + "mappings": [ + { + "source": "codebuildRoleArn", + "target": "CodebuildRoleArn" + }, + { + "source": "componentRoleArn", + "target": "ComponentRoleArn" + }, + { + "source": "environmentName", + "target": "EnvironmentName" + }, + { + "source": "managementAccountId", + "target": "ManagementAccountId" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateEnvironmentAccountConnection", + "phase": "create", + "service": "proton" + }, + { + "cfn_type": "AWS::Proton::EnvironmentAccountConnection", + "mappings": [], + "operation": "DeleteEnvironmentAccountConnection", + "phase": "delete", + "service": "proton" + }, + { + "cfn_type": "AWS::Proton::EnvironmentTemplate", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "encryptionKey", + "target": "EncryptionKey" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "provisioning", + "target": "Provisioning" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateEnvironmentTemplate", + "phase": "create", + "service": "proton" + }, + { + "cfn_type": "AWS::Proton::EnvironmentTemplate", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteEnvironmentTemplate", + "phase": "delete", + "service": "proton" + }, + { + "cfn_type": "AWS::Proton::ServiceTemplate", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "encryptionKey", + "target": "EncryptionKey" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "pipelineProvisioning", + "target": "PipelineProvisioning" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateServiceTemplate", + "phase": "create", + "service": "proton" + }, + { + "cfn_type": "AWS::Proton::ServiceTemplate", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteServiceTemplate", + "phase": "delete", + "service": "proton" + }, + { + "cfn_type": "AWS::QBusiness::Application", + "mappings": [ + { + "source": "attachmentsConfiguration", + "target": "AttachmentsConfiguration" + }, + { + "source": "clientIdsForOIDC", + "target": "ClientIdsForOIDC" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "encryptionConfiguration", + "target": "EncryptionConfiguration" + }, + { + "source": "iamIdentityProviderArn", + "target": "IamIdentityProviderArn" + }, + { + "source": "identityCenterInstanceArn", + "target": "IdentityCenterInstanceArn" + }, + { + "source": "identityType", + "target": "IdentityType" + }, + { + "source": "personalizationConfiguration", + "target": "PersonalizationConfiguration" + }, + { + "source": "qAppsConfiguration", + "target": "QAppsConfiguration" + }, + { + "source": "quickSightConfiguration", + "target": "QuickSightConfiguration" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::Application", + "mappings": [], + "operation": "DeleteApplication", + "phase": "delete", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::DataAccessor", + "mappings": [ + { + "source": "actionConfigurations", + "target": "ActionConfigurations" + }, + { + "source": "applicationId", + "target": "ApplicationId" + }, + { + "source": "authenticationDetail", + "target": "AuthenticationDetail" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "principal", + "target": "Principal" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDataAccessor", + "phase": "create", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::DataAccessor", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + } + ], + "operation": "DeleteDataAccessor", + "phase": "delete", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::DataSource", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + }, + { + "source": "configuration", + "target": "Configuration" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "documentEnrichmentConfiguration", + "target": "DocumentEnrichmentConfiguration" + }, + { + "source": "indexId", + "target": "IndexId" + }, + { + "source": "mediaExtractionConfiguration", + "target": "MediaExtractionConfiguration" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "syncSchedule", + "target": "SyncSchedule" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "vpcConfiguration", + "target": "VpcConfiguration" + } + ], + "operation": "CreateDataSource", + "phase": "create", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::DataSource", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + }, + { + "source": "indexId", + "target": "IndexId" + } + ], + "operation": "DeleteDataSource", + "phase": "delete", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::Index", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + }, + { + "source": "capacityConfiguration", + "target": "CapacityConfiguration" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateIndex", + "phase": "create", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::Index", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + } + ], + "operation": "DeleteIndex", + "phase": "delete", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::Permission", + "mappings": [ + { + "source": "actions", + "target": "Actions" + }, + { + "source": "applicationId", + "target": "ApplicationId" + }, + { + "source": "conditions", + "target": "Conditions" + }, + { + "source": "principal", + "target": "Principal" + }, + { + "source": "statementId", + "target": "StatementId" + } + ], + "operation": "AssociatePermission", + "phase": "create", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::Permission", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + }, + { + "source": "statementId", + "target": "StatementId" + } + ], + "operation": "DisassociatePermission", + "phase": "delete", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::Plugin", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + }, + { + "source": "authConfiguration", + "target": "AuthConfiguration" + }, + { + "source": "customPluginConfiguration", + "target": "CustomPluginConfiguration" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "serverUrl", + "target": "ServerUrl" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreatePlugin", + "phase": "create", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::Plugin", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + } + ], + "operation": "DeletePlugin", + "phase": "delete", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::Retriever", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + }, + { + "source": "configuration", + "target": "Configuration" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateRetriever", + "phase": "create", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::Retriever", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + } + ], + "operation": "DeleteRetriever", + "phase": "delete", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::WebExperience", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + }, + { + "source": "browserExtensionConfiguration", + "target": "BrowserExtensionConfiguration" + }, + { + "source": "customizationConfiguration", + "target": "CustomizationConfiguration" + }, + { + "source": "identityProviderConfiguration", + "target": "IdentityProviderConfiguration" + }, + { + "source": "origins", + "target": "Origins" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "samplePromptsControlMode", + "target": "SamplePromptsControlMode" + }, + { + "source": "subtitle", + "target": "Subtitle" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "title", + "target": "Title" + }, + { + "source": "welcomeMessage", + "target": "WelcomeMessage" + } + ], + "operation": "CreateWebExperience", + "phase": "create", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QBusiness::WebExperience", + "mappings": [ + { + "source": "applicationId", + "target": "ApplicationId" + } + ], + "operation": "DeleteWebExperience", + "phase": "delete", + "service": "qbusiness" + }, + { + "cfn_type": "AWS::QLDB::Stream", + "mappings": [ + { + "source": "LedgerName", + "target": "LedgerName" + } + ], + "operation": "CancelJournalKinesisStream", + "phase": "delete", + "service": "qldb" + }, + { + "cfn_type": "AWS::QuickSight::Analysis", + "mappings": [ + { + "source": "AnalysisId", + "target": "AnalysisId" + }, + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "Definition", + "target": "Definition" + }, + { + "source": "FolderArns", + "target": "FolderArns" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Parameters", + "target": "Parameters" + }, + { + "source": "Permissions", + "target": "Permissions" + }, + { + "source": "SourceEntity", + "target": "SourceEntity" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "ThemeArn", + "target": "ThemeArn" + }, + { + "source": "ValidationStrategy", + "target": "ValidationStrategy" + } + ], + "operation": "CreateAnalysis", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Analysis", + "mappings": [ + { + "source": "AnalysisId", + "target": "AnalysisId" + }, + { + "source": "AwsAccountId", + "target": "AwsAccountId" + } + ], + "operation": "DeleteAnalysis", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::CustomPermissions", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "Capabilities", + "target": "Capabilities" + }, + { + "source": "CustomPermissionsName", + "target": "CustomPermissionsName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCustomPermissions", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::CustomPermissions", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "CustomPermissionsName", + "target": "CustomPermissionsName" + } + ], + "operation": "DeleteCustomPermissions", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Dashboard", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "DashboardId", + "target": "DashboardId" + }, + { + "source": "DashboardPublishOptions", + "target": "DashboardPublishOptions" + }, + { + "source": "Definition", + "target": "Definition" + }, + { + "source": "FolderArns", + "target": "FolderArns" + }, + { + "source": "LinkEntities", + "target": "LinkEntities" + }, + { + "source": "LinkSharingConfiguration", + "target": "LinkSharingConfiguration" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Parameters", + "target": "Parameters" + }, + { + "source": "Permissions", + "target": "Permissions" + }, + { + "source": "SourceEntity", + "target": "SourceEntity" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "ThemeArn", + "target": "ThemeArn" + }, + { + "source": "ValidationStrategy", + "target": "ValidationStrategy" + }, + { + "source": "VersionDescription", + "target": "VersionDescription" + } + ], + "operation": "CreateDashboard", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Dashboard", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "DashboardId", + "target": "DashboardId" + } + ], + "operation": "DeleteDashboard", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::DataSet", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "ColumnGroups", + "target": "ColumnGroups" + }, + { + "source": "ColumnLevelPermissionRules", + "target": "ColumnLevelPermissionRules" + }, + { + "source": "DataSetId", + "target": "DataSetId" + }, + { + "source": "DataSetUsageConfiguration", + "target": "DataSetUsageConfiguration" + }, + { + "source": "DatasetParameters", + "target": "DatasetParameters" + }, + { + "source": "FieldFolders", + "target": "FieldFolders" + }, + { + "source": "FolderArns", + "target": "FolderArns" + }, + { + "source": "ImportMode", + "target": "ImportMode" + }, + { + "source": "LogicalTableMap", + "target": "LogicalTableMap" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "PerformanceConfiguration", + "target": "PerformanceConfiguration" + }, + { + "source": "Permissions", + "target": "Permissions" + }, + { + "source": "PhysicalTableMap", + "target": "PhysicalTableMap" + }, + { + "source": "RowLevelPermissionDataSet", + "target": "RowLevelPermissionDataSet" + }, + { + "source": "RowLevelPermissionTagConfiguration", + "target": "RowLevelPermissionTagConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UseAs", + "target": "UseAs" + } + ], + "operation": "CreateDataSet", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::DataSet", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "DataSetId", + "target": "DataSetId" + } + ], + "operation": "DeleteDataSet", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::DataSource", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "Credentials", + "target": "Credentials" + }, + { + "source": "DataSourceId", + "target": "DataSourceId" + }, + { + "source": "DataSourceParameters", + "target": "DataSourceParameters" + }, + { + "source": "FolderArns", + "target": "FolderArns" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Permissions", + "target": "Permissions" + }, + { + "source": "SslProperties", + "target": "SslProperties" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + }, + { + "source": "VpcConnectionProperties", + "target": "VpcConnectionProperties" + } + ], + "operation": "CreateDataSource", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::DataSource", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "DataSourceId", + "target": "DataSourceId" + } + ], + "operation": "DeleteDataSource", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Folder", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "FolderId", + "target": "FolderId" + }, + { + "source": "FolderType", + "target": "FolderType" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ParentFolderArn", + "target": "ParentFolderArn" + }, + { + "source": "Permissions", + "target": "Permissions" + }, + { + "source": "SharingModel", + "target": "SharingModel" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateFolder", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Folder", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "FolderId", + "target": "FolderId" + } + ], + "operation": "DeleteFolder", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::RefreshSchedule", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "DataSetId", + "target": "DataSetId" + }, + { + "source": "Schedule", + "target": "Schedule" + } + ], + "operation": "CreateRefreshSchedule", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::RefreshSchedule", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "DataSetId", + "target": "DataSetId" + } + ], + "operation": "DeleteRefreshSchedule", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Template", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "Definition", + "target": "Definition" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Permissions", + "target": "Permissions" + }, + { + "source": "SourceEntity", + "target": "SourceEntity" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TemplateId", + "target": "TemplateId" + }, + { + "source": "ValidationStrategy", + "target": "ValidationStrategy" + }, + { + "source": "VersionDescription", + "target": "VersionDescription" + } + ], + "operation": "CreateTemplate", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Template", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "TemplateId", + "target": "TemplateId" + } + ], + "operation": "DeleteTemplate", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Theme", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "BaseThemeId", + "target": "BaseThemeId" + }, + { + "source": "Configuration", + "target": "Configuration" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Permissions", + "target": "Permissions" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "ThemeId", + "target": "ThemeId" + }, + { + "source": "VersionDescription", + "target": "VersionDescription" + } + ], + "operation": "CreateTheme", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Theme", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "ThemeId", + "target": "ThemeId" + } + ], + "operation": "DeleteTheme", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Topic", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "CustomInstructions", + "target": "CustomInstructions" + }, + { + "source": "FolderArns", + "target": "FolderArns" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TopicId", + "target": "TopicId" + } + ], + "operation": "CreateTopic", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Topic", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "TopicId", + "target": "TopicId" + } + ], + "operation": "DeleteTopic", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::VPCConnection", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "DnsResolvers", + "target": "DnsResolvers" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "SecurityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VPCConnectionId", + "target": "VPCConnectionId" + } + ], + "operation": "CreateVPCConnection", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::VPCConnection", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "VPCConnectionId", + "target": "VPCConnectionId" + } + ], + "operation": "DeleteVPCConnection", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::RAM::Permission", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "policyTemplate", + "target": "PolicyTemplate" + }, + { + "source": "resourceType", + "target": "ResourceType" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePermission", + "phase": "create", + "service": "ram" + }, + { + "cfn_type": "AWS::RAM::Permission", + "mappings": [], + "operation": "DeletePermission", + "phase": "delete", + "service": "ram" + }, + { + "cfn_type": "AWS::RAM::ResourceShare", + "mappings": [ + { + "source": "allowExternalPrincipals", + "target": "AllowExternalPrincipals" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "permissionArns", + "target": "PermissionArns" + }, + { + "source": "principals", + "target": "Principals" + }, + { + "source": "resourceArns", + "target": "ResourceArns" + }, + { + "source": "sources", + "target": "Sources" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateResourceShare", + "phase": "create", + "service": "ram" + }, + { + "cfn_type": "AWS::RAM::ResourceShare", + "mappings": [], + "operation": "DeleteResourceShare", + "phase": "delete", + "service": "ram" + }, + { + "cfn_type": "AWS::RDS::CustomDBEngineVersion", + "mappings": [ + { + "source": "DatabaseInstallationFilesS3BucketName", + "target": "DatabaseInstallationFilesS3BucketName" + }, + { + "source": "DatabaseInstallationFilesS3Prefix", + "target": "DatabaseInstallationFilesS3Prefix" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "ImageId", + "target": "ImageId" + }, + { + "source": "KMSKeyId", + "target": "KMSKeyId" + }, + { + "source": "Manifest", + "target": "Manifest" + }, + { + "source": "SourceCustomDbEngineVersionIdentifier", + "target": "SourceCustomDbEngineVersionIdentifier" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UseAwsProvidedLatestImage", + "target": "UseAwsProvidedLatestImage" + } + ], + "operation": "CreateCustomDBEngineVersion", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::CustomDBEngineVersion", + "mappings": [ + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + } + ], + "operation": "DeleteCustomDBEngineVersion", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBCluster", + "mappings": [ + { + "source": "AllocatedStorage", + "target": "AllocatedStorage" + }, + { + "source": "AutoMinorVersionUpgrade", + "target": "AutoMinorVersionUpgrade" + }, + { + "source": "AvailabilityZones", + "target": "AvailabilityZones" + }, + { + "source": "BacktrackWindow", + "target": "BacktrackWindow" + }, + { + "source": "BackupRetentionPeriod", + "target": "BackupRetentionPeriod" + }, + { + "source": "ClusterScalabilityType", + "target": "ClusterScalabilityType" + }, + { + "source": "CopyTagsToSnapshot", + "target": "CopyTagsToSnapshot" + }, + { + "source": "DBClusterIdentifier", + "target": "DBClusterIdentifier" + }, + { + "source": "DBClusterInstanceClass", + "target": "DBClusterInstanceClass" + }, + { + "source": "DBClusterParameterGroupName", + "target": "DBClusterParameterGroupName" + }, + { + "source": "DBSubnetGroupName", + "target": "DBSubnetGroupName" + }, + { + "source": "DBSystemId", + "target": "DBSystemId" + }, + { + "source": "DatabaseInsightsMode", + "target": "DatabaseInsightsMode" + }, + { + "source": "DatabaseName", + "target": "DatabaseName" + }, + { + "source": "DeletionProtection", + "target": "DeletionProtection" + }, + { + "source": "Domain", + "target": "Domain" + }, + { + "source": "DomainIAMRoleName", + "target": "DomainIAMRoleName" + }, + { + "source": "EnableCloudwatchLogsExports", + "target": "EnableCloudwatchLogsExports" + }, + { + "source": "EnableGlobalWriteForwarding", + "target": "EnableGlobalWriteForwarding" + }, + { + "source": "EnableHttpEndpoint", + "target": "EnableHttpEndpoint" + }, + { + "source": "EnableIAMDatabaseAuthentication", + "target": "EnableIAMDatabaseAuthentication" + }, + { + "source": "EnableLocalWriteForwarding", + "target": "EnableLocalWriteForwarding" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "EngineLifecycleSupport", + "target": "EngineLifecycleSupport" + }, + { + "source": "EngineMode", + "target": "EngineMode" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "GlobalClusterIdentifier", + "target": "GlobalClusterIdentifier" + }, + { + "source": "Iops", + "target": "Iops" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "ManageMasterUserPassword", + "target": "ManageMasterUserPassword" + }, + { + "source": "MasterUserPassword", + "target": "MasterUserPassword" + }, + { + "source": "MasterUsername", + "target": "MasterUsername" + }, + { + "source": "MonitoringInterval", + "target": "MonitoringInterval" + }, + { + "source": "MonitoringRoleArn", + "target": "MonitoringRoleArn" + }, + { + "source": "NetworkType", + "target": "NetworkType" + }, + { + "source": "PerformanceInsightsKMSKeyId", + "target": "PerformanceInsightsKmsKeyId" + }, + { + "source": "PerformanceInsightsRetentionPeriod", + "target": "PerformanceInsightsRetentionPeriod" + }, + { + "source": "Port", + "target": "Port" + }, + { + "source": "PreferredBackupWindow", + "target": "PreferredBackupWindow" + }, + { + "source": "PreferredMaintenanceWindow", + "target": "PreferredMaintenanceWindow" + }, + { + "source": "PubliclyAccessible", + "target": "PubliclyAccessible" + }, + { + "source": "ReplicationSourceIdentifier", + "target": "ReplicationSourceIdentifier" + }, + { + "source": "ScalingConfiguration", + "target": "ScalingConfiguration" + }, + { + "source": "ServerlessV2ScalingConfiguration", + "target": "ServerlessV2ScalingConfiguration" + }, + { + "source": "SourceRegion", + "target": "SourceRegion" + }, + { + "source": "StorageEncrypted", + "target": "StorageEncrypted" + }, + { + "source": "StorageType", + "target": "StorageType" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcSecurityGroupIds", + "target": "VpcSecurityGroupIds" + } + ], + "operation": "CreateDBCluster", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBCluster", + "mappings": [ + { + "source": "DBClusterIdentifier", + "target": "DBClusterIdentifier" + }, + { + "source": "DeleteAutomatedBackups", + "target": "DeleteAutomatedBackups" + } + ], + "operation": "DeleteDBCluster", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBClusterParameterGroup", + "mappings": [ + { + "source": "DBClusterParameterGroupName", + "target": "DBClusterParameterGroupName" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDBClusterParameterGroup", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBClusterParameterGroup", + "mappings": [ + { + "source": "DBClusterParameterGroupName", + "target": "DBClusterParameterGroupName" + } + ], + "operation": "DeleteDBClusterParameterGroup", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBInstance", + "mappings": [ + { + "source": "AllocatedStorage", + "target": "AllocatedStorage" + }, + { + "source": "AutoMinorVersionUpgrade", + "target": "AutoMinorVersionUpgrade" + }, + { + "source": "AvailabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "BackupRetentionPeriod", + "target": "BackupRetentionPeriod" + }, + { + "source": "BackupTarget", + "target": "BackupTarget" + }, + { + "source": "CACertificateIdentifier", + "target": "CACertificateIdentifier" + }, + { + "source": "CharacterSetName", + "target": "CharacterSetName" + }, + { + "source": "CopyTagsToSnapshot", + "target": "CopyTagsToSnapshot" + }, + { + "source": "CustomIamInstanceProfile", + "target": "CustomIAMInstanceProfile" + }, + { + "source": "DBClusterIdentifier", + "target": "DBClusterIdentifier" + }, + { + "source": "DBInstanceClass", + "target": "DBInstanceClass" + }, + { + "source": "DBInstanceIdentifier", + "target": "DBInstanceIdentifier" + }, + { + "source": "DBName", + "target": "DBName" + }, + { + "source": "DBParameterGroupName", + "target": "DBParameterGroupName" + }, + { + "source": "DBSecurityGroups", + "target": "DBSecurityGroups" + }, + { + "source": "DBSubnetGroupName", + "target": "DBSubnetGroupName" + }, + { + "source": "DBSystemId", + "target": "DBSystemId" + }, + { + "source": "DatabaseInsightsMode", + "target": "DatabaseInsightsMode" + }, + { + "source": "DedicatedLogVolume", + "target": "DedicatedLogVolume" + }, + { + "source": "DeletionProtection", + "target": "DeletionProtection" + }, + { + "source": "Domain", + "target": "Domain" + }, + { + "source": "DomainAuthSecretArn", + "target": "DomainAuthSecretArn" + }, + { + "source": "DomainDnsIps", + "target": "DomainDnsIps" + }, + { + "source": "DomainFqdn", + "target": "DomainFqdn" + }, + { + "source": "DomainIAMRoleName", + "target": "DomainIAMRoleName" + }, + { + "source": "DomainOu", + "target": "DomainOu" + }, + { + "source": "EnableCloudwatchLogsExports", + "target": "EnableCloudwatchLogsExports" + }, + { + "source": "EnableIAMDatabaseAuthentication", + "target": "EnableIAMDatabaseAuthentication" + }, + { + "source": "EnablePerformanceInsights", + "target": "EnablePerformanceInsights" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "EngineLifecycleSupport", + "target": "EngineLifecycleSupport" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "Iops", + "target": "Iops" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "LicenseModel", + "target": "LicenseModel" + }, + { + "source": "ManageMasterUserPassword", + "target": "ManageMasterUserPassword" + }, + { + "source": "MasterUserPassword", + "target": "MasterUserPassword" + }, + { + "source": "MasterUsername", + "target": "MasterUsername" + }, + { + "source": "MaxAllocatedStorage", + "target": "MaxAllocatedStorage" + }, + { + "source": "MonitoringInterval", + "target": "MonitoringInterval" + }, + { + "source": "MonitoringRoleArn", + "target": "MonitoringRoleArn" + }, + { + "source": "MultiAZ", + "target": "MultiAZ" + }, + { + "source": "NcharCharacterSetName", + "target": "NcharCharacterSetName" + }, + { + "source": "NetworkType", + "target": "NetworkType" + }, + { + "source": "OptionGroupName", + "target": "OptionGroupName" + }, + { + "source": "PerformanceInsightsKMSKeyId", + "target": "PerformanceInsightsKMSKeyId" + }, + { + "source": "PerformanceInsightsRetentionPeriod", + "target": "PerformanceInsightsRetentionPeriod" + }, + { + "source": "Port", + "target": "Port" + }, + { + "source": "PreferredBackupWindow", + "target": "PreferredBackupWindow" + }, + { + "source": "PreferredMaintenanceWindow", + "target": "PreferredMaintenanceWindow" + }, + { + "source": "ProcessorFeatures", + "target": "ProcessorFeatures" + }, + { + "source": "PromotionTier", + "target": "PromotionTier" + }, + { + "source": "PubliclyAccessible", + "target": "PubliclyAccessible" + }, + { + "source": "StorageEncrypted", + "target": "StorageEncrypted" + }, + { + "source": "StorageThroughput", + "target": "StorageThroughput" + }, + { + "source": "StorageType", + "target": "StorageType" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TdeCredentialArn", + "target": "TdeCredentialArn" + }, + { + "source": "TdeCredentialPassword", + "target": "TdeCredentialPassword" + }, + { + "source": "Timezone", + "target": "Timezone" + } + ], + "operation": "CreateDBInstance", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBInstance", + "mappings": [ + { + "source": "DBInstanceIdentifier", + "target": "DBInstanceIdentifier" + }, + { + "source": "DeleteAutomatedBackups", + "target": "DeleteAutomatedBackups" + } + ], + "operation": "DeleteDBInstance", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBParameterGroup", + "mappings": [ + { + "source": "DBParameterGroupName", + "target": "DBParameterGroupName" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDBParameterGroup", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBParameterGroup", + "mappings": [ + { + "source": "DBParameterGroupName", + "target": "DBParameterGroupName" + } + ], + "operation": "DeleteDBParameterGroup", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBProxy", + "mappings": [ + { + "source": "Auth", + "target": "Auth" + }, + { + "source": "DBProxyName", + "target": "DBProxyName" + }, + { + "source": "DebugLogging", + "target": "DebugLogging" + }, + { + "source": "EngineFamily", + "target": "EngineFamily" + }, + { + "source": "IdleClientTimeout", + "target": "IdleClientTimeout" + }, + { + "source": "RequireTLS", + "target": "RequireTLS" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcSecurityGroupIds", + "target": "VpcSecurityGroupIds" + }, + { + "source": "VpcSubnetIds", + "target": "VpcSubnetIds" + } + ], + "operation": "CreateDBProxy", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBProxy", + "mappings": [ + { + "source": "DBProxyName", + "target": "DBProxyName" + } + ], + "operation": "DeleteDBProxy", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBProxyEndpoint", + "mappings": [ + { + "source": "DBProxyEndpointName", + "target": "DBProxyEndpointName" + }, + { + "source": "DBProxyName", + "target": "DBProxyName" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetRole", + "target": "TargetRole" + }, + { + "source": "VpcSecurityGroupIds", + "target": "VpcSecurityGroupIds" + }, + { + "source": "VpcSubnetIds", + "target": "VpcSubnetIds" + } + ], + "operation": "CreateDBProxyEndpoint", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBProxyEndpoint", + "mappings": [ + { + "source": "DBProxyEndpointName", + "target": "DBProxyEndpointName" + } + ], + "operation": "DeleteDBProxyEndpoint", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBProxyTargetGroup", + "mappings": [ + { + "source": "DBClusterIdentifiers", + "target": "DBClusterIdentifiers" + }, + { + "source": "DBInstanceIdentifiers", + "target": "DBInstanceIdentifiers" + }, + { + "source": "DBProxyName", + "target": "DBProxyName" + }, + { + "source": "TargetGroupName", + "target": "TargetGroupName" + } + ], + "operation": "RegisterDBProxyTargets", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBProxyTargetGroup", + "mappings": [ + { + "source": "DBClusterIdentifiers", + "target": "DBClusterIdentifiers" + }, + { + "source": "DBInstanceIdentifiers", + "target": "DBInstanceIdentifiers" + }, + { + "source": "DBProxyName", + "target": "DBProxyName" + }, + { + "source": "TargetGroupName", + "target": "TargetGroupName" + } + ], + "operation": "DeregisterDBProxyTargets", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBShardGroup", + "mappings": [ + { + "source": "ComputeRedundancy", + "target": "ComputeRedundancy" + }, + { + "source": "DBClusterIdentifier", + "target": "DBClusterIdentifier" + }, + { + "source": "DBShardGroupIdentifier", + "target": "DBShardGroupIdentifier" + }, + { + "source": "MaxACU", + "target": "MaxACU" + }, + { + "source": "MinACU", + "target": "MinACU" + }, + { + "source": "PubliclyAccessible", + "target": "PubliclyAccessible" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDBShardGroup", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBShardGroup", + "mappings": [ + { + "source": "DBShardGroupIdentifier", + "target": "DBShardGroupIdentifier" + } + ], + "operation": "DeleteDBShardGroup", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBSubnetGroup", + "mappings": [ + { + "source": "DBSubnetGroupDescription", + "target": "DBSubnetGroupDescription" + }, + { + "source": "DBSubnetGroupName", + "target": "DBSubnetGroupName" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDBSubnetGroup", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBSubnetGroup", + "mappings": [ + { + "source": "DBSubnetGroupName", + "target": "DBSubnetGroupName" + } + ], + "operation": "DeleteDBSubnetGroup", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::EventSubscription", + "mappings": [ + { + "source": "Enabled", + "target": "Enabled" + }, + { + "source": "EventCategories", + "target": "EventCategories" + }, + { + "source": "SnsTopicArn", + "target": "SnsTopicArn" + }, + { + "source": "SourceIds", + "target": "SourceIds" + }, + { + "source": "SourceType", + "target": "SourceType" + }, + { + "source": "SubscriptionName", + "target": "SubscriptionName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEventSubscription", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::EventSubscription", + "mappings": [ + { + "source": "SubscriptionName", + "target": "SubscriptionName" + } + ], + "operation": "DeleteEventSubscription", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::GlobalCluster", + "mappings": [ + { + "source": "DeletionProtection", + "target": "DeletionProtection" + }, + { + "source": "Engine", + "target": "Engine" + }, + { + "source": "EngineLifecycleSupport", + "target": "EngineLifecycleSupport" + }, + { + "source": "EngineVersion", + "target": "EngineVersion" + }, + { + "source": "GlobalClusterIdentifier", + "target": "GlobalClusterIdentifier" + }, + { + "source": "SourceDBClusterIdentifier", + "target": "SourceDBClusterIdentifier" + }, + { + "source": "StorageEncrypted", + "target": "StorageEncrypted" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateGlobalCluster", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::GlobalCluster", + "mappings": [ + { + "source": "GlobalClusterIdentifier", + "target": "GlobalClusterIdentifier" + } + ], + "operation": "DeleteGlobalCluster", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::Integration", + "mappings": [ + { + "source": "AdditionalEncryptionContext", + "target": "AdditionalEncryptionContext" + }, + { + "source": "DataFilter", + "target": "DataFilter" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "IntegrationName", + "target": "IntegrationName" + }, + { + "source": "KMSKeyId", + "target": "KMSKeyId" + }, + { + "source": "SourceArn", + "target": "SourceArn" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetArn", + "target": "TargetArn" + } + ], + "operation": "CreateIntegration", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::Integration", + "mappings": [], + "operation": "DeleteIntegration", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::OptionGroup", + "mappings": [ + { + "source": "EngineName", + "target": "EngineName" + }, + { + "source": "MajorEngineVersion", + "target": "MajorEngineVersion" + }, + { + "source": "OptionGroupDescription", + "target": "OptionGroupDescription" + }, + { + "source": "OptionGroupName", + "target": "OptionGroupName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateOptionGroup", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::OptionGroup", + "mappings": [ + { + "source": "OptionGroupName", + "target": "OptionGroupName" + } + ], + "operation": "DeleteOptionGroup", + "phase": "delete", + "service": "rds" + }, + { + "cfn_type": "AWS::RUM::AppMonitor", + "mappings": [ + { + "source": "AppMonitorConfiguration", + "target": "AppMonitorConfiguration" + }, + { + "source": "CustomEvents", + "target": "CustomEvents" + }, + { + "source": "CwLogEnabled", + "target": "CwLogEnabled" + }, + { + "source": "DeobfuscationConfiguration", + "target": "DeobfuscationConfiguration" + }, + { + "source": "Domain", + "target": "Domain" + }, + { + "source": "DomainList", + "target": "DomainList" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAppMonitor", + "phase": "create", + "service": "rum" + }, + { + "cfn_type": "AWS::RUM::AppMonitor", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteAppMonitor", + "phase": "delete", + "service": "rum" + }, + { + "cfn_type": "AWS::Rbin::Rule", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "ExcludeResourceTags", + "target": "ExcludeResourceTags" + }, + { + "source": "LockConfiguration", + "target": "LockConfiguration" + }, + { + "source": "ResourceTags", + "target": "ResourceTags" + }, + { + "source": "ResourceType", + "target": "ResourceType" + }, + { + "source": "RetentionPeriod", + "target": "RetentionPeriod" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRule", + "phase": "create", + "service": "rbin" + }, + { + "cfn_type": "AWS::Rbin::Rule", + "mappings": [], + "operation": "DeleteRule", + "phase": "delete", + "service": "rbin" + }, + { + "cfn_type": "AWS::Redshift::Cluster", + "mappings": [ + { + "source": "AllowVersionUpgrade", + "target": "AllowVersionUpgrade" + }, + { + "source": "AquaConfigurationStatus", + "target": "AquaConfigurationStatus" + }, + { + "source": "AutomatedSnapshotRetentionPeriod", + "target": "AutomatedSnapshotRetentionPeriod" + }, + { + "source": "AvailabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "AvailabilityZoneRelocation", + "target": "AvailabilityZoneRelocation" + }, + { + "source": "ClusterIdentifier", + "target": "ClusterIdentifier" + }, + { + "source": "ClusterParameterGroupName", + "target": "ClusterParameterGroupName" + }, + { + "source": "ClusterSecurityGroups", + "target": "ClusterSecurityGroups" + }, + { + "source": "ClusterSubnetGroupName", + "target": "ClusterSubnetGroupName" + }, + { + "source": "ClusterType", + "target": "ClusterType" + }, + { + "source": "ClusterVersion", + "target": "ClusterVersion" + }, + { + "source": "DBName", + "target": "DBName" + }, + { + "source": "ElasticIp", + "target": "ElasticIp" + }, + { + "source": "Encrypted", + "target": "Encrypted" + }, + { + "source": "EnhancedVpcRouting", + "target": "EnhancedVpcRouting" + }, + { + "source": "HsmClientCertificateIdentifier", + "target": "HsmClientCertificateIdentifier" + }, + { + "source": "HsmConfigurationIdentifier", + "target": "HsmConfigurationIdentifier" + }, + { + "source": "IamRoles", + "target": "IamRoles" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "MaintenanceTrackName", + "target": "MaintenanceTrackName" + }, + { + "source": "ManageMasterPassword", + "target": "ManageMasterPassword" + }, + { + "source": "ManualSnapshotRetentionPeriod", + "target": "ManualSnapshotRetentionPeriod" + }, + { + "source": "MasterPasswordSecretKmsKeyId", + "target": "MasterPasswordSecretKmsKeyId" + }, + { + "source": "MasterUserPassword", + "target": "MasterUserPassword" + }, + { + "source": "MasterUsername", + "target": "MasterUsername" + }, + { + "source": "MultiAZ", + "target": "MultiAZ" + }, + { + "source": "NodeType", + "target": "NodeType" + }, + { + "source": "NumberOfNodes", + "target": "NumberOfNodes" + }, + { + "source": "Port", + "target": "Port" + }, + { + "source": "PreferredMaintenanceWindow", + "target": "PreferredMaintenanceWindow" + }, + { + "source": "PubliclyAccessible", + "target": "PubliclyAccessible" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcSecurityGroupIds", + "target": "VpcSecurityGroupIds" + } + ], + "operation": "CreateCluster", + "phase": "create", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::Cluster", + "mappings": [ + { + "source": "ClusterIdentifier", + "target": "ClusterIdentifier" + } + ], + "operation": "DeleteCluster", + "phase": "delete", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::ClusterParameterGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "ParameterGroupFamily", + "target": "ParameterGroupFamily" + }, + { + "source": "ParameterGroupName", + "target": "ParameterGroupName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateClusterParameterGroup", + "phase": "create", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::ClusterParameterGroup", + "mappings": [ + { + "source": "ParameterGroupName", + "target": "ParameterGroupName" + } + ], + "operation": "DeleteClusterParameterGroup", + "phase": "delete", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::ClusterSubnetGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateClusterSubnetGroup", + "phase": "create", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::ClusterSubnetGroup", + "mappings": [], + "operation": "DeleteClusterSubnetGroup", + "phase": "delete", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::EndpointAccess", + "mappings": [ + { + "source": "ClusterIdentifier", + "target": "ClusterIdentifier" + }, + { + "source": "EndpointName", + "target": "EndpointName" + }, + { + "source": "ResourceOwner", + "target": "ResourceOwner" + }, + { + "source": "SubnetGroupName", + "target": "SubnetGroupName" + }, + { + "source": "VpcSecurityGroupIds", + "target": "VpcSecurityGroupIds" + } + ], + "operation": "CreateEndpointAccess", + "phase": "create", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::EndpointAccess", + "mappings": [ + { + "source": "EndpointName", + "target": "EndpointName" + } + ], + "operation": "DeleteEndpointAccess", + "phase": "delete", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::EndpointAuthorization", + "mappings": [ + { + "source": "Account", + "target": "Account" + }, + { + "source": "ClusterIdentifier", + "target": "ClusterIdentifier" + }, + { + "source": "Force", + "target": "Force" + }, + { + "source": "VpcIds", + "target": "VpcIds" + } + ], + "operation": "RevokeEndpointAccess", + "phase": "delete", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::EventSubscription", + "mappings": [ + { + "source": "Enabled", + "target": "Enabled" + }, + { + "source": "EventCategories", + "target": "EventCategories" + }, + { + "source": "Severity", + "target": "Severity" + }, + { + "source": "SnsTopicArn", + "target": "SnsTopicArn" + }, + { + "source": "SourceIds", + "target": "SourceIds" + }, + { + "source": "SourceType", + "target": "SourceType" + }, + { + "source": "SubscriptionName", + "target": "SubscriptionName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEventSubscription", + "phase": "create", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::EventSubscription", + "mappings": [ + { + "source": "SubscriptionName", + "target": "SubscriptionName" + } + ], + "operation": "DeleteEventSubscription", + "phase": "delete", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::Integration", + "mappings": [ + { + "source": "AdditionalEncryptionContext", + "target": "AdditionalEncryptionContext" + }, + { + "source": "IntegrationName", + "target": "IntegrationName" + }, + { + "source": "KMSKeyId", + "target": "KMSKeyId" + }, + { + "source": "SourceArn", + "target": "SourceArn" + }, + { + "source": "TargetArn", + "target": "TargetArn" + } + ], + "operation": "CreateIntegration", + "phase": "create", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::Integration", + "mappings": [], + "operation": "DeleteIntegration", + "phase": "delete", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::ScheduledAction", + "mappings": [ + { + "source": "Enable", + "target": "Enable" + }, + { + "source": "EndTime", + "target": "EndTime" + }, + { + "source": "IamRole", + "target": "IamRole" + }, + { + "source": "Schedule", + "target": "Schedule" + }, + { + "source": "ScheduledActionDescription", + "target": "ScheduledActionDescription" + }, + { + "source": "ScheduledActionName", + "target": "ScheduledActionName" + }, + { + "source": "StartTime", + "target": "StartTime" + }, + { + "source": "TargetAction", + "target": "TargetAction" + } + ], + "operation": "CreateScheduledAction", + "phase": "create", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::ScheduledAction", + "mappings": [ + { + "source": "ScheduledActionName", + "target": "ScheduledActionName" + } + ], + "operation": "DeleteScheduledAction", + "phase": "delete", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::SnapshotSchedule", + "mappings": [ + { + "source": "ScheduleDefinitions", + "target": "ScheduleDefinitions" + }, + { + "source": "ScheduleDescription", + "target": "ScheduleDescription" + }, + { + "source": "ScheduleIdentifier", + "target": "ScheduleIdentifier" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSnapshotSchedule", + "phase": "create", + "service": "redshift" + }, + { + "cfn_type": "AWS::Redshift::SnapshotSchedule", + "mappings": [ + { + "source": "ScheduleIdentifier", + "target": "ScheduleIdentifier" + } + ], + "operation": "DeleteSnapshotSchedule", + "phase": "delete", + "service": "redshift" + }, + { + "cfn_type": "AWS::RedshiftServerless::Namespace", + "mappings": [ + { + "source": "adminPasswordSecretKmsKeyId", + "target": "AdminPasswordSecretKmsKeyId" + }, + { + "source": "adminUserPassword", + "target": "AdminUserPassword" + }, + { + "source": "adminUsername", + "target": "AdminUsername" + }, + { + "source": "dbName", + "target": "DbName" + }, + { + "source": "defaultIamRoleArn", + "target": "DefaultIamRoleArn" + }, + { + "source": "iamRoles", + "target": "IamRoles" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "logExports", + "target": "LogExports" + }, + { + "source": "manageAdminPassword", + "target": "ManageAdminPassword" + }, + { + "source": "namespaceName", + "target": "NamespaceName" + }, + { + "source": "redshiftIdcApplicationArn", + "target": "RedshiftIdcApplicationArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateNamespace", + "phase": "create", + "service": "redshift-serverless" + }, + { + "cfn_type": "AWS::RedshiftServerless::Namespace", + "mappings": [ + { + "source": "finalSnapshotName", + "target": "FinalSnapshotName" + }, + { + "source": "finalSnapshotRetentionPeriod", + "target": "FinalSnapshotRetentionPeriod" + }, + { + "source": "namespaceName", + "target": "NamespaceName" + } + ], + "operation": "DeleteNamespace", + "phase": "delete", + "service": "redshift-serverless" + }, + { + "cfn_type": "AWS::RedshiftServerless::Snapshot", + "mappings": [ + { + "source": "namespaceName", + "target": "NamespaceName" + }, + { + "source": "retentionPeriod", + "target": "RetentionPeriod" + }, + { + "source": "snapshotName", + "target": "SnapshotName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSnapshot", + "phase": "create", + "service": "redshift-serverless" + }, + { + "cfn_type": "AWS::RedshiftServerless::Snapshot", + "mappings": [ + { + "source": "snapshotName", + "target": "SnapshotName" + } + ], + "operation": "DeleteSnapshot", + "phase": "delete", + "service": "redshift-serverless" + }, + { + "cfn_type": "AWS::RedshiftServerless::Workgroup", + "mappings": [ + { + "source": "baseCapacity", + "target": "BaseCapacity" + }, + { + "source": "configParameters", + "target": "ConfigParameters" + }, + { + "source": "enhancedVpcRouting", + "target": "EnhancedVpcRouting" + }, + { + "source": "maxCapacity", + "target": "MaxCapacity" + }, + { + "source": "namespaceName", + "target": "NamespaceName" + }, + { + "source": "port", + "target": "Port" + }, + { + "source": "pricePerformanceTarget", + "target": "PricePerformanceTarget" + }, + { + "source": "publiclyAccessible", + "target": "PubliclyAccessible" + }, + { + "source": "securityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "subnetIds", + "target": "SubnetIds" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "trackName", + "target": "TrackName" + }, + { + "source": "workgroupName", + "target": "WorkgroupName" + } + ], + "operation": "CreateWorkgroup", + "phase": "create", + "service": "redshift-serverless" + }, + { + "cfn_type": "AWS::RedshiftServerless::Workgroup", + "mappings": [ + { + "source": "workgroupName", + "target": "WorkgroupName" + } + ], + "operation": "DeleteWorkgroup", + "phase": "delete", + "service": "redshift-serverless" + }, + { + "cfn_type": "AWS::RefactorSpaces::Application", + "mappings": [ + { + "source": "ApiGatewayProxy", + "target": "ApiGatewayProxy" + }, + { + "source": "EnvironmentIdentifier", + "target": "EnvironmentIdentifier" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ProxyType", + "target": "ProxyType" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "migration-hub-refactor-spaces" + }, + { + "cfn_type": "AWS::RefactorSpaces::Application", + "mappings": [ + { + "source": "EnvironmentIdentifier", + "target": "EnvironmentIdentifier" + } + ], + "operation": "DeleteApplication", + "phase": "delete", + "service": "migration-hub-refactor-spaces" + }, + { + "cfn_type": "AWS::RefactorSpaces::Environment", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NetworkFabricType", + "target": "NetworkFabricType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEnvironment", + "phase": "create", + "service": "migration-hub-refactor-spaces" + }, + { + "cfn_type": "AWS::RefactorSpaces::Environment", + "mappings": [], + "operation": "DeleteEnvironment", + "phase": "delete", + "service": "migration-hub-refactor-spaces" + }, + { + "cfn_type": "AWS::RefactorSpaces::Route", + "mappings": [ + { + "source": "ApplicationIdentifier", + "target": "ApplicationIdentifier" + }, + { + "source": "DefaultRoute", + "target": "DefaultRoute" + }, + { + "source": "EnvironmentIdentifier", + "target": "EnvironmentIdentifier" + }, + { + "source": "RouteType", + "target": "RouteType" + }, + { + "source": "ServiceIdentifier", + "target": "ServiceIdentifier" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UriPathRoute", + "target": "UriPathRoute" + } + ], + "operation": "CreateRoute", + "phase": "create", + "service": "migration-hub-refactor-spaces" + }, + { + "cfn_type": "AWS::RefactorSpaces::Route", + "mappings": [ + { + "source": "ApplicationIdentifier", + "target": "ApplicationIdentifier" + }, + { + "source": "EnvironmentIdentifier", + "target": "EnvironmentIdentifier" + } + ], + "operation": "DeleteRoute", + "phase": "delete", + "service": "migration-hub-refactor-spaces" + }, + { + "cfn_type": "AWS::RefactorSpaces::Service", + "mappings": [ + { + "source": "ApplicationIdentifier", + "target": "ApplicationIdentifier" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EndpointType", + "target": "EndpointType" + }, + { + "source": "EnvironmentIdentifier", + "target": "EnvironmentIdentifier" + }, + { + "source": "LambdaEndpoint", + "target": "LambdaEndpoint" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UrlEndpoint", + "target": "UrlEndpoint" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateService", + "phase": "create", + "service": "migration-hub-refactor-spaces" + }, + { + "cfn_type": "AWS::RefactorSpaces::Service", + "mappings": [ + { + "source": "ApplicationIdentifier", + "target": "ApplicationIdentifier" + }, + { + "source": "EnvironmentIdentifier", + "target": "EnvironmentIdentifier" + } + ], + "operation": "DeleteService", + "phase": "delete", + "service": "migration-hub-refactor-spaces" + }, + { + "cfn_type": "AWS::Rekognition::Collection", + "mappings": [ + { + "source": "CollectionId", + "target": "CollectionId" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCollection", + "phase": "create", + "service": "rekognition" + }, + { + "cfn_type": "AWS::Rekognition::Collection", + "mappings": [ + { + "source": "CollectionId", + "target": "CollectionId" + } + ], + "operation": "DeleteCollection", + "phase": "delete", + "service": "rekognition" + }, + { + "cfn_type": "AWS::Rekognition::Project", + "mappings": [ + { + "source": "ProjectName", + "target": "ProjectName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateProject", + "phase": "create", + "service": "rekognition" + }, + { + "cfn_type": "AWS::Rekognition::Project", + "mappings": [], + "operation": "DeleteProject", + "phase": "delete", + "service": "rekognition" + }, + { + "cfn_type": "AWS::Rekognition::StreamProcessor", + "mappings": [ + { + "source": "DataSharingPreference", + "target": "DataSharingPreference" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NotificationChannel", + "target": "NotificationChannel" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateStreamProcessor", + "phase": "create", + "service": "rekognition" + }, + { + "cfn_type": "AWS::Rekognition::StreamProcessor", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteStreamProcessor", + "phase": "delete", + "service": "rekognition" + }, + { + "cfn_type": "AWS::ResilienceHub::App", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "eventSubscriptions", + "target": "EventSubscriptions" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "permissionModel", + "target": "PermissionModel" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateApp", + "phase": "create", + "service": "resiliencehub" + }, + { + "cfn_type": "AWS::ResilienceHub::App", + "mappings": [], + "operation": "DeleteApp", + "phase": "delete", + "service": "resiliencehub" + }, + { + "cfn_type": "AWS::ResilienceHub::ResiliencyPolicy", + "mappings": [ + { + "source": "dataLocationConstraint", + "target": "DataLocationConstraint" + }, + { + "source": "policy", + "target": "Policy" + }, + { + "source": "policyDescription", + "target": "PolicyDescription" + }, + { + "source": "policyName", + "target": "PolicyName" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "tier", + "target": "Tier" + } + ], + "operation": "CreateResiliencyPolicy", + "phase": "create", + "service": "resiliencehub" + }, + { + "cfn_type": "AWS::ResilienceHub::ResiliencyPolicy", + "mappings": [], + "operation": "DeleteResiliencyPolicy", + "phase": "delete", + "service": "resiliencehub" + }, + { + "cfn_type": "AWS::ResourceExplorer2::Index", + "mappings": [ + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateIndex", + "phase": "create", + "service": "resource-explorer-2" + }, + { + "cfn_type": "AWS::ResourceExplorer2::Index", + "mappings": [], + "operation": "DeleteIndex", + "phase": "delete", + "service": "resource-explorer-2" + }, + { + "cfn_type": "AWS::ResourceExplorer2::View", + "mappings": [ + { + "source": "Filters", + "target": "Filters" + }, + { + "source": "IncludedProperties", + "target": "IncludedProperties" + }, + { + "source": "Scope", + "target": "Scope" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "ViewName", + "target": "ViewName" + } + ], + "operation": "CreateView", + "phase": "create", + "service": "resource-explorer-2" + }, + { + "cfn_type": "AWS::ResourceExplorer2::View", + "mappings": [], + "operation": "DeleteView", + "phase": "delete", + "service": "resource-explorer-2" + }, + { + "cfn_type": "AWS::ResourceGroups::Group", + "mappings": [ + { + "source": "Configuration", + "target": "Configuration" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ResourceQuery", + "target": "ResourceQuery" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateGroup", + "phase": "create", + "service": "resource-groups" + }, + { + "cfn_type": "AWS::ResourceGroups::Group", + "mappings": [], + "operation": "DeleteGroup", + "phase": "delete", + "service": "resource-groups" + }, + { + "cfn_type": "AWS::ResourceGroups::TagSyncTask", + "mappings": [ + { + "source": "Group", + "target": "Group" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "TagKey", + "target": "TagKey" + }, + { + "source": "TagValue", + "target": "TagValue" + } + ], + "operation": "StartTagSyncTask", + "phase": "create", + "service": "resource-groups" + }, + { + "cfn_type": "AWS::ResourceGroups::TagSyncTask", + "mappings": [], + "operation": "CancelTagSyncTask", + "phase": "delete", + "service": "resource-groups" + }, + { + "cfn_type": "AWS::RoboMaker::Fleet", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateFleet", + "phase": "create", + "service": "robomaker" + }, + { + "cfn_type": "AWS::RoboMaker::Fleet", + "mappings": [], + "operation": "DeleteFleet", + "phase": "delete", + "service": "robomaker" + }, + { + "cfn_type": "AWS::RoboMaker::Robot", + "mappings": [ + { + "source": "architecture", + "target": "Architecture" + }, + { + "source": "greengrassGroupId", + "target": "GreengrassGroupId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRobot", + "phase": "create", + "service": "robomaker" + }, + { + "cfn_type": "AWS::RoboMaker::Robot", + "mappings": [ + { + "source": "fleet", + "target": "Fleet" + } + ], + "operation": "DeregisterRobot", + "phase": "delete", + "service": "robomaker" + }, + { + "cfn_type": "AWS::RoboMaker::RobotApplication", + "mappings": [ + { + "source": "environment", + "target": "Environment" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "robotSoftwareSuite", + "target": "RobotSoftwareSuite" + }, + { + "source": "sources", + "target": "Sources" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRobotApplication", + "phase": "create", + "service": "robomaker" + }, + { + "cfn_type": "AWS::RoboMaker::RobotApplication", + "mappings": [], + "operation": "DeleteRobotApplication", + "phase": "delete", + "service": "robomaker" + }, + { + "cfn_type": "AWS::RoboMaker::RobotApplicationVersion", + "mappings": [ + { + "source": "application", + "target": "Application" + }, + { + "source": "currentRevisionId", + "target": "CurrentRevisionId" + } + ], + "operation": "CreateRobotApplicationVersion", + "phase": "create", + "service": "robomaker" + }, + { + "cfn_type": "AWS::RoboMaker::SimulationApplication", + "mappings": [ + { + "source": "environment", + "target": "Environment" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "renderingEngine", + "target": "RenderingEngine" + }, + { + "source": "robotSoftwareSuite", + "target": "RobotSoftwareSuite" + }, + { + "source": "simulationSoftwareSuite", + "target": "SimulationSoftwareSuite" + }, + { + "source": "sources", + "target": "Sources" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSimulationApplication", + "phase": "create", + "service": "robomaker" + }, + { + "cfn_type": "AWS::RoboMaker::SimulationApplication", + "mappings": [], + "operation": "DeleteSimulationApplication", + "phase": "delete", + "service": "robomaker" + }, + { + "cfn_type": "AWS::RoboMaker::SimulationApplicationVersion", + "mappings": [ + { + "source": "application", + "target": "Application" + }, + { + "source": "currentRevisionId", + "target": "CurrentRevisionId" + } + ], + "operation": "CreateSimulationApplicationVersion", + "phase": "create", + "service": "robomaker" + }, + { + "cfn_type": "AWS::RolesAnywhere::CRL", + "mappings": [ + { + "source": "crlData", + "target": "CrlData" + }, + { + "source": "enabled", + "target": "Enabled" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "trustAnchorArn", + "target": "TrustAnchorArn" + } + ], + "operation": "ImportCrl", + "phase": "create", + "service": "rolesanywhere" + }, + { + "cfn_type": "AWS::RolesAnywhere::CRL", + "mappings": [], + "operation": "DeleteCrl", + "phase": "delete", + "service": "rolesanywhere" + }, + { + "cfn_type": "AWS::RolesAnywhere::Profile", + "mappings": [ + { + "source": "acceptRoleSessionName", + "target": "AcceptRoleSessionName" + }, + { + "source": "durationSeconds", + "target": "DurationSeconds" + }, + { + "source": "enabled", + "target": "Enabled" + }, + { + "source": "managedPolicyArns", + "target": "ManagedPolicyArns" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "requireInstanceProperties", + "target": "RequireInstanceProperties" + }, + { + "source": "roleArns", + "target": "RoleArns" + }, + { + "source": "sessionPolicy", + "target": "SessionPolicy" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateProfile", + "phase": "create", + "service": "rolesanywhere" + }, + { + "cfn_type": "AWS::RolesAnywhere::Profile", + "mappings": [], + "operation": "DeleteProfile", + "phase": "delete", + "service": "rolesanywhere" + }, + { + "cfn_type": "AWS::RolesAnywhere::TrustAnchor", + "mappings": [ + { + "source": "enabled", + "target": "Enabled" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "notificationSettings", + "target": "NotificationSettings" + }, + { + "source": "source", + "target": "Source" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateTrustAnchor", + "phase": "create", + "service": "rolesanywhere" + }, + { + "cfn_type": "AWS::RolesAnywhere::TrustAnchor", + "mappings": [], + "operation": "DeleteTrustAnchor", + "phase": "delete", + "service": "rolesanywhere" + }, + { + "cfn_type": "AWS::Route53::CidrCollection", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateCidrCollection", + "phase": "create", + "service": "route53" + }, + { + "cfn_type": "AWS::Route53::CidrCollection", + "mappings": [], + "operation": "DeleteCidrCollection", + "phase": "delete", + "service": "route53" + }, + { + "cfn_type": "AWS::Route53::DNSSEC", + "mappings": [ + { + "source": "HostedZoneId", + "target": "HostedZoneId" + } + ], + "operation": "EnableHostedZoneDNSSEC", + "phase": "create", + "service": "route53" + }, + { + "cfn_type": "AWS::Route53::HealthCheck", + "mappings": [ + { + "source": "HealthCheckConfig", + "target": "HealthCheckConfig" + } + ], + "operation": "CreateHealthCheck", + "phase": "create", + "service": "route53" + }, + { + "cfn_type": "AWS::Route53::HealthCheck", + "mappings": [], + "operation": "DeleteHealthCheck", + "phase": "delete", + "service": "route53" + }, + { + "cfn_type": "AWS::Route53::HostedZone", + "mappings": [ + { + "source": "HostedZoneConfig", + "target": "HostedZoneConfig" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateHostedZone", + "phase": "create", + "service": "route53" + }, + { + "cfn_type": "AWS::Route53::HostedZone", + "mappings": [], + "operation": "DeleteHostedZone", + "phase": "delete", + "service": "route53" + }, + { + "cfn_type": "AWS::Route53::KeySigningKey", + "mappings": [ + { + "source": "HostedZoneId", + "target": "HostedZoneId" + }, + { + "source": "KeyManagementServiceArn", + "target": "KeyManagementServiceArn" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Status", + "target": "Status" + } + ], + "operation": "CreateKeySigningKey", + "phase": "create", + "service": "route53" + }, + { + "cfn_type": "AWS::Route53::KeySigningKey", + "mappings": [ + { + "source": "HostedZoneId", + "target": "HostedZoneId" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteKeySigningKey", + "phase": "delete", + "service": "route53" + }, + { + "cfn_type": "AWS::Route53Profiles::Profile", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateProfile", + "phase": "create", + "service": "route53profiles" + }, + { + "cfn_type": "AWS::Route53Profiles::Profile", + "mappings": [], + "operation": "DeleteProfile", + "phase": "delete", + "service": "route53profiles" + }, + { + "cfn_type": "AWS::Route53Profiles::ProfileAssociation", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "ProfileId", + "target": "ProfileId" + }, + { + "source": "ResourceId", + "target": "ResourceId" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "AssociateProfile", + "phase": "create", + "service": "route53profiles" + }, + { + "cfn_type": "AWS::Route53Profiles::ProfileAssociation", + "mappings": [ + { + "source": "ProfileId", + "target": "ProfileId" + }, + { + "source": "ResourceId", + "target": "ResourceId" + } + ], + "operation": "DisassociateProfile", + "phase": "delete", + "service": "route53profiles" + }, + { + "cfn_type": "AWS::Route53Profiles::ProfileResourceAssociation", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "ProfileId", + "target": "ProfileId" + }, + { + "source": "ResourceArn", + "target": "ResourceArn" + }, + { + "source": "ResourceProperties", + "target": "ResourceProperties" + } + ], + "operation": "AssociateResourceToProfile", + "phase": "create", + "service": "route53profiles" + }, + { + "cfn_type": "AWS::Route53Profiles::ProfileResourceAssociation", + "mappings": [ + { + "source": "ProfileId", + "target": "ProfileId" + }, + { + "source": "ResourceArn", + "target": "ResourceArn" + } + ], + "operation": "DisassociateResourceFromProfile", + "phase": "delete", + "service": "route53profiles" + }, + { + "cfn_type": "AWS::Route53RecoveryControl::Cluster", + "mappings": [ + { + "source": "NetworkType", + "target": "NetworkType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCluster", + "phase": "create", + "service": "route53-recovery-control-config" + }, + { + "cfn_type": "AWS::Route53RecoveryControl::Cluster", + "mappings": [], + "operation": "DeleteCluster", + "phase": "delete", + "service": "route53-recovery-control-config" + }, + { + "cfn_type": "AWS::Route53RecoveryControl::ControlPanel", + "mappings": [ + { + "source": "ClusterArn", + "target": "ClusterArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateControlPanel", + "phase": "create", + "service": "route53-recovery-control-config" + }, + { + "cfn_type": "AWS::Route53RecoveryControl::ControlPanel", + "mappings": [], + "operation": "DeleteControlPanel", + "phase": "delete", + "service": "route53-recovery-control-config" + }, + { + "cfn_type": "AWS::Route53RecoveryControl::RoutingControl", + "mappings": [ + { + "source": "ClusterArn", + "target": "ClusterArn" + }, + { + "source": "ControlPanelArn", + "target": "ControlPanelArn" + } + ], + "operation": "CreateRoutingControl", + "phase": "create", + "service": "route53-recovery-control-config" + }, + { + "cfn_type": "AWS::Route53RecoveryControl::RoutingControl", + "mappings": [], + "operation": "DeleteRoutingControl", + "phase": "delete", + "service": "route53-recovery-control-config" + }, + { + "cfn_type": "AWS::Route53RecoveryControl::SafetyRule", + "mappings": [ + { + "source": "AssertionRule", + "target": "AssertionRule" + }, + { + "source": "GatingRule", + "target": "GatingRule" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSafetyRule", + "phase": "create", + "service": "route53-recovery-control-config" + }, + { + "cfn_type": "AWS::Route53RecoveryControl::SafetyRule", + "mappings": [], + "operation": "DeleteSafetyRule", + "phase": "delete", + "service": "route53-recovery-control-config" + }, + { + "cfn_type": "AWS::Route53RecoveryReadiness::Cell", + "mappings": [ + { + "source": "CellName", + "target": "CellName" + }, + { + "source": "Cells", + "target": "Cells" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCell", + "phase": "create", + "service": "route53-recovery-readiness" + }, + { + "cfn_type": "AWS::Route53RecoveryReadiness::Cell", + "mappings": [ + { + "source": "CellName", + "target": "CellName" + } + ], + "operation": "DeleteCell", + "phase": "delete", + "service": "route53-recovery-readiness" + }, + { + "cfn_type": "AWS::Route53RecoveryReadiness::ReadinessCheck", + "mappings": [ + { + "source": "ReadinessCheckName", + "target": "ReadinessCheckName" + }, + { + "source": "ResourceSetName", + "target": "ResourceSetName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateReadinessCheck", + "phase": "create", + "service": "route53-recovery-readiness" + }, + { + "cfn_type": "AWS::Route53RecoveryReadiness::ReadinessCheck", + "mappings": [ + { + "source": "ReadinessCheckName", + "target": "ReadinessCheckName" + } + ], + "operation": "DeleteReadinessCheck", + "phase": "delete", + "service": "route53-recovery-readiness" + }, + { + "cfn_type": "AWS::Route53RecoveryReadiness::RecoveryGroup", + "mappings": [ + { + "source": "Cells", + "target": "Cells" + }, + { + "source": "RecoveryGroupName", + "target": "RecoveryGroupName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRecoveryGroup", + "phase": "create", + "service": "route53-recovery-readiness" + }, + { + "cfn_type": "AWS::Route53RecoveryReadiness::RecoveryGroup", + "mappings": [ + { + "source": "RecoveryGroupName", + "target": "RecoveryGroupName" + } + ], + "operation": "DeleteRecoveryGroup", + "phase": "delete", + "service": "route53-recovery-readiness" + }, + { + "cfn_type": "AWS::Route53RecoveryReadiness::ResourceSet", + "mappings": [ + { + "source": "ResourceSetName", + "target": "ResourceSetName" + }, + { + "source": "ResourceSetType", + "target": "ResourceSetType" + }, + { + "source": "Resources", + "target": "Resources" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateResourceSet", + "phase": "create", + "service": "route53-recovery-readiness" + }, + { + "cfn_type": "AWS::Route53RecoveryReadiness::ResourceSet", + "mappings": [ + { + "source": "ResourceSetName", + "target": "ResourceSetName" + } + ], + "operation": "DeleteResourceSet", + "phase": "delete", + "service": "route53-recovery-readiness" + }, + { + "cfn_type": "AWS::Route53Resolver::FirewallDomainList", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateFirewallDomainList", + "phase": "create", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::FirewallDomainList", + "mappings": [], + "operation": "DeleteFirewallDomainList", + "phase": "delete", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::FirewallRuleGroup", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateFirewallRuleGroup", + "phase": "create", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::FirewallRuleGroup", + "mappings": [], + "operation": "DeleteFirewallRuleGroup", + "phase": "delete", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::FirewallRuleGroupAssociation", + "mappings": [ + { + "source": "FirewallRuleGroupId", + "target": "FirewallRuleGroupId" + }, + { + "source": "MutationProtection", + "target": "MutationProtection" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Priority", + "target": "Priority" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "AssociateFirewallRuleGroup", + "phase": "create", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::OutpostResolver", + "mappings": [ + { + "source": "InstanceCount", + "target": "InstanceCount" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "OutpostArn", + "target": "OutpostArn" + }, + { + "source": "PreferredInstanceType", + "target": "PreferredInstanceType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateOutpostResolver", + "phase": "create", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::OutpostResolver", + "mappings": [], + "operation": "DeleteOutpostResolver", + "phase": "delete", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::ResolverEndpoint", + "mappings": [ + { + "source": "Direction", + "target": "Direction" + }, + { + "source": "IpAddresses", + "target": "IpAddresses" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "OutpostArn", + "target": "OutpostArn" + }, + { + "source": "PreferredInstanceType", + "target": "PreferredInstanceType" + }, + { + "source": "Protocols", + "target": "Protocols" + }, + { + "source": "ResolverEndpointType", + "target": "ResolverEndpointType" + }, + { + "source": "SecurityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateResolverEndpoint", + "phase": "create", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::ResolverEndpoint", + "mappings": [], + "operation": "DeleteResolverEndpoint", + "phase": "delete", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::ResolverQueryLoggingConfig", + "mappings": [ + { + "source": "DestinationArn", + "target": "DestinationArn" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateResolverQueryLogConfig", + "phase": "create", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::ResolverQueryLoggingConfigAssociation", + "mappings": [ + { + "source": "ResolverQueryLogConfigId", + "target": "ResolverQueryLogConfigId" + }, + { + "source": "ResourceId", + "target": "ResourceId" + } + ], + "operation": "AssociateResolverQueryLogConfig", + "phase": "create", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::ResolverQueryLoggingConfigAssociation", + "mappings": [ + { + "source": "ResolverQueryLogConfigId", + "target": "ResolverQueryLogConfigId" + }, + { + "source": "ResourceId", + "target": "ResourceId" + } + ], + "operation": "DisassociateResolverQueryLogConfig", + "phase": "delete", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::ResolverRule", + "mappings": [ + { + "source": "DelegationRecord", + "target": "DelegationRecord" + }, + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ResolverEndpointId", + "target": "ResolverEndpointId" + }, + { + "source": "RuleType", + "target": "RuleType" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetIps", + "target": "TargetIps" + } + ], + "operation": "CreateResolverRule", + "phase": "create", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::ResolverRule", + "mappings": [], + "operation": "DeleteResolverRule", + "phase": "delete", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::ResolverRuleAssociation", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "ResolverRuleId", + "target": "ResolverRuleId" + }, + { + "source": "VPCId", + "target": "VPCId" + } + ], + "operation": "AssociateResolverRule", + "phase": "create", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::Route53Resolver::ResolverRuleAssociation", + "mappings": [ + { + "source": "ResolverRuleId", + "target": "ResolverRuleId" + }, + { + "source": "VPCId", + "target": "VPCId" + } + ], + "operation": "DisassociateResolverRule", + "phase": "delete", + "service": "route53resolver" + }, + { + "cfn_type": "AWS::S3::AccessGrant", + "mappings": [ + { + "source": "AccessGrantsLocationConfiguration", + "target": "AccessGrantsLocationConfiguration" + }, + { + "source": "AccessGrantsLocationId", + "target": "AccessGrantsLocationId" + }, + { + "source": "ApplicationArn", + "target": "ApplicationArn" + }, + { + "source": "Grantee", + "target": "Grantee" + }, + { + "source": "Permission", + "target": "Permission" + }, + { + "source": "S3PrefixType", + "target": "S3PrefixType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAccessGrant", + "phase": "create", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3::AccessGrant", + "mappings": [], + "operation": "DeleteAccessGrant", + "phase": "delete", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3::AccessGrantsInstance", + "mappings": [ + { + "source": "IdentityCenterArn", + "target": "IdentityCenterArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAccessGrantsInstance", + "phase": "create", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3::AccessGrantsInstance", + "mappings": [], + "operation": "DeleteAccessGrantsInstance", + "phase": "delete", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3::AccessGrantsLocation", + "mappings": [ + { + "source": "IAMRoleArn", + "target": "IamRoleArn" + }, + { + "source": "LocationScope", + "target": "LocationScope" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAccessGrantsLocation", + "phase": "create", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3::AccessGrantsLocation", + "mappings": [], + "operation": "DeleteAccessGrantsLocation", + "phase": "delete", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3::Bucket", + "mappings": [ + { + "source": "Bucket", + "target": "BucketName" + } + ], + "operation": "CreateBucket", + "phase": "create", + "service": "s3" + }, + { + "cfn_type": "AWS::S3::Bucket", + "mappings": [ + { + "source": "Bucket", + "target": "BucketName" + } + ], + "operation": "DeleteBucket", + "phase": "delete", + "service": "s3" + }, + { + "cfn_type": "AWS::S3::BucketPolicy", + "mappings": [ + { + "source": "Bucket", + "target": "Bucket" + } + ], + "operation": "PutBucketPolicy", + "phase": "create", + "service": "s3" + }, + { + "cfn_type": "AWS::S3::BucketPolicy", + "mappings": [ + { + "source": "Bucket", + "target": "Bucket" + } + ], + "operation": "DeleteBucketPolicy", + "phase": "delete", + "service": "s3" + }, + { + "cfn_type": "AWS::S3::MultiRegionAccessPoint", + "mappings": [], + "operation": "DeleteMultiRegionAccessPoint", + "phase": "delete", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3::StorageLens", + "mappings": [ + { + "source": "StorageLensConfiguration", + "target": "StorageLensConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "PutStorageLensConfiguration", + "phase": "create", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3::StorageLensGroup", + "mappings": [ + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateStorageLensGroup", + "phase": "create", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3::StorageLensGroup", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteStorageLensGroup", + "phase": "delete", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3Outposts::Bucket", + "mappings": [ + { + "source": "Bucket", + "target": "BucketName" + }, + { + "source": "OutpostId", + "target": "OutpostId" + } + ], + "operation": "CreateBucket", + "phase": "create", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3Outposts::Endpoint", + "mappings": [ + { + "source": "AccessType", + "target": "AccessType" + }, + { + "source": "CustomerOwnedIpv4Pool", + "target": "CustomerOwnedIpv4Pool" + }, + { + "source": "OutpostId", + "target": "OutpostId" + }, + { + "source": "SecurityGroupId", + "target": "SecurityGroupId" + }, + { + "source": "SubnetId", + "target": "SubnetId" + } + ], + "operation": "CreateEndpoint", + "phase": "create", + "service": "s3outposts" + }, + { + "cfn_type": "AWS::S3Outposts::Endpoint", + "mappings": [ + { + "source": "OutpostId", + "target": "OutpostId" + } + ], + "operation": "DeleteEndpoint", + "phase": "delete", + "service": "s3outposts" + }, + { + "cfn_type": "AWS::S3Tables::Namespace", + "mappings": [ + { + "source": "namespace", + "target": "Namespace" + }, + { + "source": "tableBucketARN", + "target": "TableBucketARN" + } + ], + "operation": "CreateNamespace", + "phase": "create", + "service": "s3tables" + }, + { + "cfn_type": "AWS::S3Tables::Namespace", + "mappings": [ + { + "source": "namespace", + "target": "Namespace" + }, + { + "source": "tableBucketARN", + "target": "TableBucketARN" + } + ], + "operation": "DeleteNamespace", + "phase": "delete", + "service": "s3tables" + }, + { + "cfn_type": "AWS::S3Tables::Table", + "mappings": [ + { + "source": "name", + "target": "TableName" + }, + { + "source": "namespace", + "target": "Namespace" + }, + { + "source": "tableBucketARN", + "target": "TableBucketARN" + } + ], + "operation": "CreateTable", + "phase": "create", + "service": "s3tables" + }, + { + "cfn_type": "AWS::S3Tables::Table", + "mappings": [ + { + "source": "name", + "target": "TableName" + }, + { + "source": "namespace", + "target": "Namespace" + }, + { + "source": "tableBucketARN", + "target": "TableBucketARN" + } + ], + "operation": "DeleteTable", + "phase": "delete", + "service": "s3tables" + }, + { + "cfn_type": "AWS::S3Tables::TableBucket", + "mappings": [ + { + "source": "encryptionConfiguration", + "target": "EncryptionConfiguration" + }, + { + "source": "name", + "target": "TableBucketName" + } + ], + "operation": "CreateTableBucket", + "phase": "create", + "service": "s3tables" + }, + { + "cfn_type": "AWS::S3Tables::TableBucket", + "mappings": [], + "operation": "DeleteTableBucket", + "phase": "delete", + "service": "s3tables" + }, + { + "cfn_type": "AWS::S3Tables::TableBucketPolicy", + "mappings": [ + { + "source": "resourcePolicy", + "target": "ResourcePolicy" + }, + { + "source": "tableBucketARN", + "target": "TableBucketARN" + } + ], + "operation": "PutTableBucketPolicy", + "phase": "create", + "service": "s3tables" + }, + { + "cfn_type": "AWS::S3Tables::TableBucketPolicy", + "mappings": [ + { + "source": "tableBucketARN", + "target": "TableBucketARN" + } + ], + "operation": "DeleteTableBucketPolicy", + "phase": "delete", + "service": "s3tables" + }, + { + "cfn_type": "AWS::S3Tables::TablePolicy", + "mappings": [ + { + "source": "resourcePolicy", + "target": "ResourcePolicy" + } + ], + "operation": "PutTablePolicy", + "phase": "create", + "service": "s3tables" + }, + { + "cfn_type": "AWS::S3Tables::TablePolicy", + "mappings": [], + "operation": "DeleteTablePolicy", + "phase": "delete", + "service": "s3tables" + }, + { + "cfn_type": "AWS::S3Vectors::Index", + "mappings": [ + { + "source": "dataType", + "target": "DataType" + }, + { + "source": "dimension", + "target": "Dimension" + }, + { + "source": "distanceMetric", + "target": "DistanceMetric" + }, + { + "source": "indexName", + "target": "IndexName" + }, + { + "source": "metadataConfiguration", + "target": "MetadataConfiguration" + }, + { + "source": "vectorBucketArn", + "target": "VectorBucketArn" + }, + { + "source": "vectorBucketName", + "target": "VectorBucketName" + } + ], + "operation": "CreateIndex", + "phase": "create", + "service": "s3vectors" + }, + { + "cfn_type": "AWS::S3Vectors::Index", + "mappings": [ + { + "source": "indexName", + "target": "IndexName" + }, + { + "source": "vectorBucketName", + "target": "VectorBucketName" + } + ], + "operation": "DeleteIndex", + "phase": "delete", + "service": "s3vectors" + }, + { + "cfn_type": "AWS::S3Vectors::VectorBucket", + "mappings": [ + { + "source": "encryptionConfiguration", + "target": "EncryptionConfiguration" + }, + { + "source": "vectorBucketName", + "target": "VectorBucketName" + } + ], + "operation": "CreateVectorBucket", + "phase": "create", + "service": "s3vectors" + }, + { + "cfn_type": "AWS::S3Vectors::VectorBucket", + "mappings": [ + { + "source": "vectorBucketName", + "target": "VectorBucketName" + } + ], + "operation": "DeleteVectorBucket", + "phase": "delete", + "service": "s3vectors" + }, + { + "cfn_type": "AWS::S3Vectors::VectorBucketPolicy", + "mappings": [ + { + "source": "policy", + "target": "Policy" + }, + { + "source": "vectorBucketArn", + "target": "VectorBucketArn" + }, + { + "source": "vectorBucketName", + "target": "VectorBucketName" + } + ], + "operation": "PutVectorBucketPolicy", + "phase": "create", + "service": "s3vectors" + }, + { + "cfn_type": "AWS::S3Vectors::VectorBucketPolicy", + "mappings": [ + { + "source": "vectorBucketArn", + "target": "VectorBucketArn" + }, + { + "source": "vectorBucketName", + "target": "VectorBucketName" + } + ], + "operation": "DeleteVectorBucketPolicy", + "phase": "delete", + "service": "s3vectors" + }, + { + "cfn_type": "AWS::SCN::Dataset", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "instanceId", + "target": "InstanceId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "namespace", + "target": "Namespace" + }, + { + "source": "partitionSpec", + "target": "PartitionSpec" + }, + { + "source": "schema", + "target": "Schema" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDataLakeDataset", + "phase": "create", + "service": "supplychain" + }, + { + "cfn_type": "AWS::SCN::Dataset", + "mappings": [ + { + "source": "instanceId", + "target": "InstanceId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "namespace", + "target": "Namespace" + } + ], + "operation": "DeleteDataLakeDataset", + "phase": "delete", + "service": "supplychain" + }, + { + "cfn_type": "AWS::SCN::Namespace", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "instanceId", + "target": "InstanceId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDataLakeNamespace", + "phase": "create", + "service": "supplychain" + }, + { + "cfn_type": "AWS::SCN::Namespace", + "mappings": [ + { + "source": "instanceId", + "target": "InstanceId" + }, + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteDataLakeNamespace", + "phase": "delete", + "service": "supplychain" + }, + { + "cfn_type": "AWS::SES::ConfigurationSet", + "mappings": [], + "operation": "DeleteConfigurationSet", + "phase": "delete", + "service": "ses" + }, + { + "cfn_type": "AWS::SES::ConfigurationSetEventDestination", + "mappings": [ + { + "source": "ConfigurationSetName", + "target": "ConfigurationSetName" + }, + { + "source": "EventDestination", + "target": "EventDestination" + } + ], + "operation": "CreateConfigurationSetEventDestination", + "phase": "create", + "service": "ses" + }, + { + "cfn_type": "AWS::SES::ConfigurationSetEventDestination", + "mappings": [ + { + "source": "ConfigurationSetName", + "target": "ConfigurationSetName" + } + ], + "operation": "DeleteConfigurationSetEventDestination", + "phase": "delete", + "service": "ses" + }, + { + "cfn_type": "AWS::SES::ContactList", + "mappings": [ + { + "source": "ContactListName", + "target": "ContactListName" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Topics", + "target": "Topics" + } + ], + "operation": "CreateContactList", + "phase": "create", + "service": "sesv2" + }, + { + "cfn_type": "AWS::SES::ContactList", + "mappings": [ + { + "source": "ContactListName", + "target": "ContactListName" + } + ], + "operation": "DeleteContactList", + "phase": "delete", + "service": "sesv2" + }, + { + "cfn_type": "AWS::SES::CustomVerificationEmailTemplate", + "mappings": [ + { + "source": "FailureRedirectionURL", + "target": "FailureRedirectionURL" + }, + { + "source": "FromEmailAddress", + "target": "FromEmailAddress" + }, + { + "source": "SuccessRedirectionURL", + "target": "SuccessRedirectionURL" + }, + { + "source": "TemplateContent", + "target": "TemplateContent" + }, + { + "source": "TemplateName", + "target": "TemplateName" + }, + { + "source": "TemplateSubject", + "target": "TemplateSubject" + } + ], + "operation": "CreateCustomVerificationEmailTemplate", + "phase": "create", + "service": "ses" + }, + { + "cfn_type": "AWS::SES::CustomVerificationEmailTemplate", + "mappings": [ + { + "source": "TemplateName", + "target": "TemplateName" + } + ], + "operation": "DeleteCustomVerificationEmailTemplate", + "phase": "delete", + "service": "ses" + }, + { + "cfn_type": "AWS::SES::DedicatedIpPool", + "mappings": [ + { + "source": "PoolName", + "target": "PoolName" + }, + { + "source": "ScalingMode", + "target": "ScalingMode" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDedicatedIpPool", + "phase": "create", + "service": "sesv2" + }, + { + "cfn_type": "AWS::SES::EmailIdentity", + "mappings": [ + { + "source": "DkimSigningAttributes", + "target": "DkimSigningAttributes" + }, + { + "source": "EmailIdentity", + "target": "EmailIdentity" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEmailIdentity", + "phase": "create", + "service": "sesv2" + }, + { + "cfn_type": "AWS::SES::MailManagerAddonInstance", + "mappings": [ + { + "source": "AddonSubscriptionId", + "target": "AddonSubscriptionId" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAddonInstance", + "phase": "create", + "service": "mailmanager" + }, + { + "cfn_type": "AWS::SES::MailManagerAddonSubscription", + "mappings": [ + { + "source": "AddonName", + "target": "AddonName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAddonSubscription", + "phase": "create", + "service": "mailmanager" + }, + { + "cfn_type": "AWS::SES::MailManagerAddressList", + "mappings": [ + { + "source": "AddressListName", + "target": "AddressListName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAddressList", + "phase": "create", + "service": "mailmanager" + }, + { + "cfn_type": "AWS::SES::MailManagerArchive", + "mappings": [ + { + "source": "ArchiveName", + "target": "ArchiveName" + }, + { + "source": "KmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "Retention", + "target": "Retention" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateArchive", + "phase": "create", + "service": "mailmanager" + }, + { + "cfn_type": "AWS::SES::MailManagerIngressPoint", + "mappings": [ + { + "source": "IngressPointConfiguration", + "target": "IngressPointConfiguration" + }, + { + "source": "IngressPointName", + "target": "IngressPointName" + }, + { + "source": "NetworkConfiguration", + "target": "NetworkConfiguration" + }, + { + "source": "RuleSetId", + "target": "RuleSetId" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TrafficPolicyId", + "target": "TrafficPolicyId" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateIngressPoint", + "phase": "create", + "service": "mailmanager" + }, + { + "cfn_type": "AWS::SES::MailManagerRelay", + "mappings": [ + { + "source": "Authentication", + "target": "Authentication" + }, + { + "source": "RelayName", + "target": "RelayName" + }, + { + "source": "ServerName", + "target": "ServerName" + }, + { + "source": "ServerPort", + "target": "ServerPort" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRelay", + "phase": "create", + "service": "mailmanager" + }, + { + "cfn_type": "AWS::SES::MailManagerRuleSet", + "mappings": [ + { + "source": "RuleSetName", + "target": "RuleSetName" + }, + { + "source": "Rules", + "target": "Rules" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRuleSet", + "phase": "create", + "service": "mailmanager" + }, + { + "cfn_type": "AWS::SES::MailManagerTrafficPolicy", + "mappings": [ + { + "source": "DefaultAction", + "target": "DefaultAction" + }, + { + "source": "MaxMessageSizeBytes", + "target": "MaxMessageSizeBytes" + }, + { + "source": "PolicyStatements", + "target": "PolicyStatements" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TrafficPolicyName", + "target": "TrafficPolicyName" + } + ], + "operation": "CreateTrafficPolicy", + "phase": "create", + "service": "mailmanager" + }, + { + "cfn_type": "AWS::SES::MultiRegionEndpoint", + "mappings": [ + { + "source": "Details", + "target": "Details" + }, + { + "source": "EndpointName", + "target": "EndpointName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateMultiRegionEndpoint", + "phase": "create", + "service": "sesv2" + }, + { + "cfn_type": "AWS::SES::MultiRegionEndpoint", + "mappings": [ + { + "source": "EndpointName", + "target": "EndpointName" + } + ], + "operation": "DeleteMultiRegionEndpoint", + "phase": "delete", + "service": "sesv2" + }, + { + "cfn_type": "AWS::SES::ReceiptRule", + "mappings": [ + { + "source": "After", + "target": "After" + }, + { + "source": "Rule", + "target": "Rule" + }, + { + "source": "RuleSetName", + "target": "RuleSetName" + } + ], + "operation": "CreateReceiptRule", + "phase": "create", + "service": "ses" + }, + { + "cfn_type": "AWS::SES::ReceiptRule", + "mappings": [ + { + "source": "RuleSetName", + "target": "RuleSetName" + } + ], + "operation": "DeleteReceiptRule", + "phase": "delete", + "service": "ses" + }, + { + "cfn_type": "AWS::SES::ReceiptRuleSet", + "mappings": [ + { + "source": "RuleSetName", + "target": "RuleSetName" + } + ], + "operation": "CreateReceiptRuleSet", + "phase": "create", + "service": "ses" + }, + { + "cfn_type": "AWS::SES::ReceiptRuleSet", + "mappings": [ + { + "source": "RuleSetName", + "target": "RuleSetName" + } + ], + "operation": "DeleteReceiptRuleSet", + "phase": "delete", + "service": "ses" + }, + { + "cfn_type": "AWS::SES::Template", + "mappings": [ + { + "source": "Template", + "target": "Template" + } + ], + "operation": "CreateTemplate", + "phase": "create", + "service": "ses" + }, + { + "cfn_type": "AWS::SES::Template", + "mappings": [], + "operation": "DeleteTemplate", + "phase": "delete", + "service": "ses" + }, + { + "cfn_type": "AWS::SES::Tenant", + "mappings": [ + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TenantName", + "target": "TenantName" + } + ], + "operation": "CreateTenant", + "phase": "create", + "service": "sesv2" + }, + { + "cfn_type": "AWS::SES::Tenant", + "mappings": [ + { + "source": "TenantName", + "target": "TenantName" + } + ], + "operation": "DeleteTenant", + "phase": "delete", + "service": "sesv2" + }, + { + "cfn_type": "AWS::SMSVOICE::ConfigurationSet", + "mappings": [ + { + "source": "ConfigurationSetName", + "target": "ConfigurationSetName" + } + ], + "operation": "CreateConfigurationSet", + "phase": "create", + "service": "sms-voice" + }, + { + "cfn_type": "AWS::SMSVOICE::ConfigurationSet", + "mappings": [ + { + "source": "ConfigurationSetName", + "target": "ConfigurationSetName" + } + ], + "operation": "DeleteConfigurationSet", + "phase": "delete", + "service": "sms-voice" + }, + { + "cfn_type": "AWS::SMSVOICE::OptOutList", + "mappings": [ + { + "source": "OptOutListName", + "target": "OptOutListName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateOptOutList", + "phase": "create", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::OptOutList", + "mappings": [ + { + "source": "OptOutListName", + "target": "OptOutListName" + } + ], + "operation": "DeleteOptOutList", + "phase": "delete", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::PhoneNumber", + "mappings": [ + { + "source": "DeletionProtectionEnabled", + "target": "DeletionProtectionEnabled" + }, + { + "source": "IsoCountryCode", + "target": "IsoCountryCode" + }, + { + "source": "NumberCapabilities", + "target": "NumberCapabilities" + }, + { + "source": "NumberType", + "target": "NumberType" + }, + { + "source": "OptOutListName", + "target": "OptOutListName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "RequestPhoneNumber", + "phase": "create", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::PhoneNumber", + "mappings": [], + "operation": "ReleasePhoneNumber", + "phase": "delete", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::Pool", + "mappings": [ + { + "source": "DeletionProtectionEnabled", + "target": "DeletionProtectionEnabled" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePool", + "phase": "create", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::Pool", + "mappings": [], + "operation": "DeletePool", + "phase": "delete", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::ProtectConfiguration", + "mappings": [ + { + "source": "DeletionProtectionEnabled", + "target": "DeletionProtectionEnabled" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateProtectConfiguration", + "phase": "create", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::ProtectConfiguration", + "mappings": [], + "operation": "DeleteProtectConfiguration", + "phase": "delete", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::Registration", + "mappings": [ + { + "source": "RegistrationType", + "target": "RegistrationType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRegistration", + "phase": "create", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::Registration", + "mappings": [], + "operation": "DeleteRegistration", + "phase": "delete", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::ResourcePolicy", + "mappings": [ + { + "source": "ResourceArn", + "target": "ResourceArn" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::ResourcePolicy", + "mappings": [ + { + "source": "ResourceArn", + "target": "ResourceArn" + } + ], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::SenderId", + "mappings": [ + { + "source": "DeletionProtectionEnabled", + "target": "DeletionProtectionEnabled" + }, + { + "source": "IsoCountryCode", + "target": "IsoCountryCode" + }, + { + "source": "SenderId", + "target": "SenderId" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "RequestSenderId", + "phase": "create", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SMSVOICE::SenderId", + "mappings": [ + { + "source": "IsoCountryCode", + "target": "IsoCountryCode" + }, + { + "source": "SenderId", + "target": "SenderId" + } + ], + "operation": "ReleaseSenderId", + "phase": "delete", + "service": "pinpoint-sms-voice-v2" + }, + { + "cfn_type": "AWS::SNS::Subscription", + "mappings": [ + { + "source": "Endpoint", + "target": "Endpoint" + }, + { + "source": "Protocol", + "target": "Protocol" + }, + { + "source": "TopicArn", + "target": "TopicArn" + } + ], + "operation": "Subscribe", + "phase": "create", + "service": "sns" + }, + { + "cfn_type": "AWS::SNS::Topic", + "mappings": [ + { + "source": "DataProtectionPolicy", + "target": "DataProtectionPolicy" + }, + { + "source": "Name", + "target": "TopicName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateTopic", + "phase": "create", + "service": "sns" + }, + { + "cfn_type": "AWS::SNS::Topic", + "mappings": [], + "operation": "DeleteTopic", + "phase": "delete", + "service": "sns" + }, + { + "cfn_type": "AWS::SQS::Queue", + "mappings": [ + { + "source": "QueueName", + "target": "QueueName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateQueue", + "phase": "create", + "service": "sqs" + }, + { + "cfn_type": "AWS::SQS::Queue", + "mappings": [], + "operation": "DeleteQueue", + "phase": "delete", + "service": "sqs" + }, + { + "cfn_type": "AWS::SSM::Association", + "mappings": [ + { + "source": "ApplyOnlyAtCronInterval", + "target": "ApplyOnlyAtCronInterval" + }, + { + "source": "AssociationName", + "target": "AssociationName" + }, + { + "source": "AutomationTargetParameterName", + "target": "AutomationTargetParameterName" + }, + { + "source": "CalendarNames", + "target": "CalendarNames" + }, + { + "source": "ComplianceSeverity", + "target": "ComplianceSeverity" + }, + { + "source": "DocumentVersion", + "target": "DocumentVersion" + }, + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "MaxConcurrency", + "target": "MaxConcurrency" + }, + { + "source": "MaxErrors", + "target": "MaxErrors" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "OutputLocation", + "target": "OutputLocation" + }, + { + "source": "Parameters", + "target": "Parameters" + }, + { + "source": "ScheduleExpression", + "target": "ScheduleExpression" + }, + { + "source": "ScheduleOffset", + "target": "ScheduleOffset" + }, + { + "source": "SyncCompliance", + "target": "SyncCompliance" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Targets", + "target": "Targets" + } + ], + "operation": "CreateAssociation", + "phase": "create", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::Association", + "mappings": [ + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteAssociation", + "phase": "delete", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::Document", + "mappings": [ + { + "source": "Attachments", + "target": "Attachments" + }, + { + "source": "Content", + "target": "Content" + }, + { + "source": "DocumentFormat", + "target": "DocumentFormat" + }, + { + "source": "DocumentType", + "target": "DocumentType" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Requires", + "target": "Requires" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetType", + "target": "TargetType" + }, + { + "source": "VersionName", + "target": "VersionName" + } + ], + "operation": "CreateDocument", + "phase": "create", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::Document", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "VersionName", + "target": "VersionName" + } + ], + "operation": "DeleteDocument", + "phase": "delete", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::MaintenanceWindow", + "mappings": [ + { + "source": "AllowUnassociatedTargets", + "target": "AllowUnassociatedTargets" + }, + { + "source": "Cutoff", + "target": "Cutoff" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Duration", + "target": "Duration" + }, + { + "source": "EndDate", + "target": "EndDate" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Schedule", + "target": "Schedule" + }, + { + "source": "ScheduleOffset", + "target": "ScheduleOffset" + }, + { + "source": "ScheduleTimezone", + "target": "ScheduleTimezone" + }, + { + "source": "StartDate", + "target": "StartDate" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateMaintenanceWindow", + "phase": "create", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::MaintenanceWindow", + "mappings": [], + "operation": "DeleteMaintenanceWindow", + "phase": "delete", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::MaintenanceWindowTarget", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "OwnerInformation", + "target": "OwnerInformation" + }, + { + "source": "ResourceType", + "target": "ResourceType" + }, + { + "source": "Targets", + "target": "Targets" + }, + { + "source": "WindowId", + "target": "WindowId" + } + ], + "operation": "RegisterTargetWithMaintenanceWindow", + "phase": "create", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::MaintenanceWindowTask", + "mappings": [ + { + "source": "CutoffBehavior", + "target": "CutoffBehavior" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "LoggingInfo", + "target": "LoggingInfo" + }, + { + "source": "MaxConcurrency", + "target": "MaxConcurrency" + }, + { + "source": "MaxErrors", + "target": "MaxErrors" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Priority", + "target": "Priority" + }, + { + "source": "ServiceRoleArn", + "target": "ServiceRoleArn" + }, + { + "source": "Targets", + "target": "Targets" + }, + { + "source": "TaskArn", + "target": "TaskArn" + }, + { + "source": "TaskInvocationParameters", + "target": "TaskInvocationParameters" + }, + { + "source": "TaskParameters", + "target": "TaskParameters" + }, + { + "source": "TaskType", + "target": "TaskType" + }, + { + "source": "WindowId", + "target": "WindowId" + } + ], + "operation": "RegisterTaskWithMaintenanceWindow", + "phase": "create", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::OpsItem", + "mappings": [ + { + "source": "Category", + "target": "Category" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Priority", + "target": "Priority" + }, + { + "source": "Severity", + "target": "Severity" + }, + { + "source": "Source", + "target": "Source" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Title", + "target": "Title" + } + ], + "operation": "CreateOpsItem", + "phase": "create", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::OpsItem", + "mappings": [], + "operation": "DeleteOpsItem", + "phase": "delete", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::Parameter", + "mappings": [ + { + "source": "AllowedPattern", + "target": "AllowedPattern" + }, + { + "source": "DataType", + "target": "DataType" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Policies", + "target": "Policies" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Tier", + "target": "Tier" + }, + { + "source": "Type", + "target": "Type" + }, + { + "source": "Value", + "target": "Value" + } + ], + "operation": "PutParameter", + "phase": "create", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::Parameter", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteParameter", + "phase": "delete", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::PatchBaseline", + "mappings": [ + { + "source": "ApprovalRules", + "target": "ApprovalRules" + }, + { + "source": "ApprovedPatches", + "target": "ApprovedPatches" + }, + { + "source": "ApprovedPatchesComplianceLevel", + "target": "ApprovedPatchesComplianceLevel" + }, + { + "source": "ApprovedPatchesEnableNonSecurity", + "target": "ApprovedPatchesEnableNonSecurity" + }, + { + "source": "AvailableSecurityUpdatesComplianceStatus", + "target": "AvailableSecurityUpdatesComplianceStatus" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "GlobalFilters", + "target": "GlobalFilters" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "OperatingSystem", + "target": "OperatingSystem" + }, + { + "source": "RejectedPatches", + "target": "RejectedPatches" + }, + { + "source": "RejectedPatchesAction", + "target": "RejectedPatchesAction" + }, + { + "source": "Sources", + "target": "Sources" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePatchBaseline", + "phase": "create", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::PatchBaseline", + "mappings": [], + "operation": "DeletePatchBaseline", + "phase": "delete", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::ResourceDataSync", + "mappings": [ + { + "source": "S3Destination", + "target": "S3Destination" + }, + { + "source": "SyncName", + "target": "SyncName" + }, + { + "source": "SyncSource", + "target": "SyncSource" + }, + { + "source": "SyncType", + "target": "SyncType" + } + ], + "operation": "CreateResourceDataSync", + "phase": "create", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::ResourceDataSync", + "mappings": [ + { + "source": "SyncName", + "target": "SyncName" + }, + { + "source": "SyncType", + "target": "SyncType" + } + ], + "operation": "DeleteResourceDataSync", + "phase": "delete", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::ResourcePolicy", + "mappings": [ + { + "source": "Policy", + "target": "Policy" + }, + { + "source": "ResourceArn", + "target": "ResourceArn" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::ResourcePolicy", + "mappings": [ + { + "source": "ResourceArn", + "target": "ResourceArn" + } + ], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSMContacts::Contact", + "mappings": [ + { + "source": "Alias", + "target": "Alias" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "Plan", + "target": "Plan" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateContact", + "phase": "create", + "service": "ssm-contacts" + }, + { + "cfn_type": "AWS::SSMContacts::Contact", + "mappings": [], + "operation": "DeleteContact", + "phase": "delete", + "service": "ssm-contacts" + }, + { + "cfn_type": "AWS::SSMContacts::ContactChannel", + "mappings": [ + { + "source": "ContactId", + "target": "ContactId" + }, + { + "source": "DeferActivation", + "target": "DeferActivation" + } + ], + "operation": "CreateContactChannel", + "phase": "create", + "service": "ssm-contacts" + }, + { + "cfn_type": "AWS::SSMContacts::ContactChannel", + "mappings": [], + "operation": "DeleteContactChannel", + "phase": "delete", + "service": "ssm-contacts" + }, + { + "cfn_type": "AWS::SSMContacts::Rotation", + "mappings": [ + { + "source": "ContactIds", + "target": "ContactIds" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Recurrence", + "target": "Recurrence" + }, + { + "source": "StartTime", + "target": "StartTime" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TimeZoneId", + "target": "TimeZoneId" + } + ], + "operation": "CreateRotation", + "phase": "create", + "service": "ssm-contacts" + }, + { + "cfn_type": "AWS::SSMContacts::Rotation", + "mappings": [], + "operation": "DeleteRotation", + "phase": "delete", + "service": "ssm-contacts" + }, + { + "cfn_type": "AWS::SSMGuiConnect::Preferences", + "mappings": [], + "operation": "DeleteConnectionRecordingPreferences", + "phase": "delete", + "service": "ssm-guiconnect" + }, + { + "cfn_type": "AWS::SSMIncidents::ReplicationSet", + "mappings": [ + { + "source": "regions", + "target": "Regions" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateReplicationSet", + "phase": "create", + "service": "ssm-incidents" + }, + { + "cfn_type": "AWS::SSMIncidents::ReplicationSet", + "mappings": [], + "operation": "DeleteReplicationSet", + "phase": "delete", + "service": "ssm-incidents" + }, + { + "cfn_type": "AWS::SSMIncidents::ResponsePlan", + "mappings": [ + { + "source": "actions", + "target": "Actions" + }, + { + "source": "chatChannel", + "target": "ChatChannel" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "engagements", + "target": "Engagements" + }, + { + "source": "incidentTemplate", + "target": "IncidentTemplate" + }, + { + "source": "integrations", + "target": "Integrations" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateResponsePlan", + "phase": "create", + "service": "ssm-incidents" + }, + { + "cfn_type": "AWS::SSMIncidents::ResponsePlan", + "mappings": [], + "operation": "DeleteResponsePlan", + "phase": "delete", + "service": "ssm-incidents" + }, + { + "cfn_type": "AWS::SSMQuickSetup::ConfigurationManager", + "mappings": [ + { + "source": "ConfigurationDefinitions", + "target": "ConfigurationDefinitions" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateConfigurationManager", + "phase": "create", + "service": "ssm-quicksetup" + }, + { + "cfn_type": "AWS::SSMQuickSetup::ConfigurationManager", + "mappings": [], + "operation": "DeleteConfigurationManager", + "phase": "delete", + "service": "ssm-quicksetup" + }, + { + "cfn_type": "AWS::SSO::Application", + "mappings": [ + { + "source": "ApplicationProviderArn", + "target": "ApplicationProviderArn" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "InstanceArn", + "target": "InstanceArn" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "PortalOptions", + "target": "PortalOptions" + }, + { + "source": "Status", + "target": "Status" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "sso-admin" + }, + { + "cfn_type": "AWS::SSO::Application", + "mappings": [], + "operation": "DeleteApplication", + "phase": "delete", + "service": "sso-admin" + }, + { + "cfn_type": "AWS::SSO::ApplicationAssignment", + "mappings": [ + { + "source": "ApplicationArn", + "target": "ApplicationArn" + }, + { + "source": "PrincipalId", + "target": "PrincipalId" + }, + { + "source": "PrincipalType", + "target": "PrincipalType" + } + ], + "operation": "CreateApplicationAssignment", + "phase": "create", + "service": "sso-admin" + }, + { + "cfn_type": "AWS::SSO::ApplicationAssignment", + "mappings": [ + { + "source": "ApplicationArn", + "target": "ApplicationArn" + }, + { + "source": "PrincipalId", + "target": "PrincipalId" + }, + { + "source": "PrincipalType", + "target": "PrincipalType" + } + ], + "operation": "DeleteApplicationAssignment", + "phase": "delete", + "service": "sso-admin" + }, + { + "cfn_type": "AWS::SSO::Assignment", + "mappings": [ + { + "source": "InstanceArn", + "target": "InstanceArn" + }, + { + "source": "PermissionSetArn", + "target": "PermissionSetArn" + }, + { + "source": "PrincipalId", + "target": "PrincipalId" + }, + { + "source": "PrincipalType", + "target": "PrincipalType" + }, + { + "source": "TargetId", + "target": "TargetId" + }, + { + "source": "TargetType", + "target": "TargetType" + } + ], + "operation": "CreateAccountAssignment", + "phase": "create", + "service": "sso-admin" + }, + { + "cfn_type": "AWS::SSO::Assignment", + "mappings": [ + { + "source": "InstanceArn", + "target": "InstanceArn" + }, + { + "source": "PermissionSetArn", + "target": "PermissionSetArn" + }, + { + "source": "PrincipalId", + "target": "PrincipalId" + }, + { + "source": "PrincipalType", + "target": "PrincipalType" + }, + { + "source": "TargetId", + "target": "TargetId" + }, + { + "source": "TargetType", + "target": "TargetType" + } + ], + "operation": "DeleteAccountAssignment", + "phase": "delete", + "service": "sso-admin" + }, + { + "cfn_type": "AWS::SSO::Instance", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateInstance", + "phase": "create", + "service": "sso-admin" + }, + { + "cfn_type": "AWS::SSO::Instance", + "mappings": [], + "operation": "DeleteInstance", + "phase": "delete", + "service": "sso-admin" + }, + { + "cfn_type": "AWS::SSO::InstanceAccessControlAttributeConfiguration", + "mappings": [ + { + "source": "InstanceAccessControlAttributeConfiguration", + "target": "InstanceAccessControlAttributeConfiguration" + }, + { + "source": "InstanceArn", + "target": "InstanceArn" + } + ], + "operation": "CreateInstanceAccessControlAttributeConfiguration", + "phase": "create", + "service": "sso-admin" + }, + { + "cfn_type": "AWS::SSO::InstanceAccessControlAttributeConfiguration", + "mappings": [ + { + "source": "InstanceArn", + "target": "InstanceArn" + } + ], + "operation": "DeleteInstanceAccessControlAttributeConfiguration", + "phase": "delete", + "service": "sso-admin" + }, + { + "cfn_type": "AWS::SSO::PermissionSet", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "InstanceArn", + "target": "InstanceArn" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "SessionDuration", + "target": "SessionDuration" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePermissionSet", + "phase": "create", + "service": "sso-admin" + }, + { + "cfn_type": "AWS::SSO::PermissionSet", + "mappings": [ + { + "source": "InstanceArn", + "target": "InstanceArn" + } + ], + "operation": "DeletePermissionSet", + "phase": "delete", + "service": "sso-admin" + }, + { + "cfn_type": "AWS::SageMaker::Action", + "mappings": [ + { + "source": "ActionName", + "target": "ActionName" + }, + { + "source": "ActionType", + "target": "ActionType" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "MetadataProperties", + "target": "MetadataProperties" + }, + { + "source": "Properties", + "target": "Properties" + }, + { + "source": "Source", + "target": "Source" + }, + { + "source": "Status", + "target": "Status" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAction", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Action", + "mappings": [ + { + "source": "ActionName", + "target": "ActionName" + } + ], + "operation": "DeleteAction", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Algorithm", + "mappings": [ + { + "source": "AlgorithmDescription", + "target": "AlgorithmDescription" + }, + { + "source": "AlgorithmName", + "target": "AlgorithmName" + }, + { + "source": "CertifyForMarketplace", + "target": "CertifyForMarketplace" + }, + { + "source": "InferenceSpecification", + "target": "InferenceSpecification" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TrainingSpecification", + "target": "TrainingSpecification" + } + ], + "operation": "CreateAlgorithm", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Algorithm", + "mappings": [ + { + "source": "AlgorithmName", + "target": "AlgorithmName" + } + ], + "operation": "DeleteAlgorithm", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::App", + "mappings": [ + { + "source": "AppName", + "target": "AppName" + }, + { + "source": "AppType", + "target": "AppType" + }, + { + "source": "DomainId", + "target": "DomainId" + }, + { + "source": "RecoveryMode", + "target": "RecoveryMode" + }, + { + "source": "ResourceSpec", + "target": "ResourceSpec" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UserProfileName", + "target": "UserProfileName" + } + ], + "operation": "CreateApp", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::App", + "mappings": [ + { + "source": "AppName", + "target": "AppName" + }, + { + "source": "AppType", + "target": "AppType" + }, + { + "source": "DomainId", + "target": "DomainId" + }, + { + "source": "UserProfileName", + "target": "UserProfileName" + } + ], + "operation": "DeleteApp", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::AppImageConfig", + "mappings": [ + { + "source": "AppImageConfigName", + "target": "AppImageConfigName" + }, + { + "source": "CodeEditorAppImageConfig", + "target": "CodeEditorAppImageConfig" + }, + { + "source": "JupyterLabAppImageConfig", + "target": "JupyterLabAppImageConfig" + }, + { + "source": "KernelGatewayImageConfig", + "target": "KernelGatewayImageConfig" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAppImageConfig", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::AppImageConfig", + "mappings": [ + { + "source": "AppImageConfigName", + "target": "AppImageConfigName" + } + ], + "operation": "DeleteAppImageConfig", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Artifact", + "mappings": [ + { + "source": "ArtifactName", + "target": "ArtifactName" + }, + { + "source": "ArtifactType", + "target": "ArtifactType" + }, + { + "source": "MetadataProperties", + "target": "MetadataProperties" + }, + { + "source": "Properties", + "target": "Properties" + }, + { + "source": "Source", + "target": "Source" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateArtifact", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Artifact", + "mappings": [ + { + "source": "Source", + "target": "Source" + } + ], + "operation": "DeleteArtifact", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Cluster", + "mappings": [ + { + "source": "ClusterName", + "target": "ClusterName" + }, + { + "source": "InstanceGroups", + "target": "InstanceGroups" + }, + { + "source": "NodeProvisioningMode", + "target": "NodeProvisioningMode" + }, + { + "source": "NodeRecovery", + "target": "NodeRecovery" + }, + { + "source": "Orchestrator", + "target": "Orchestrator" + }, + { + "source": "RestrictedInstanceGroups", + "target": "RestrictedInstanceGroups" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcConfig", + "target": "VpcConfig" + } + ], + "operation": "CreateCluster", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Cluster", + "mappings": [ + { + "source": "ClusterName", + "target": "ClusterName" + } + ], + "operation": "DeleteCluster", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Context", + "mappings": [ + { + "source": "ContextName", + "target": "ContextName" + }, + { + "source": "ContextType", + "target": "ContextType" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Properties", + "target": "Properties" + }, + { + "source": "Source", + "target": "Source" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateContext", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Context", + "mappings": [ + { + "source": "ContextName", + "target": "ContextName" + } + ], + "operation": "DeleteContext", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::DataQualityJobDefinition", + "mappings": [ + { + "source": "DataQualityAppSpecification", + "target": "DataQualityAppSpecification" + }, + { + "source": "DataQualityBaselineConfig", + "target": "DataQualityBaselineConfig" + }, + { + "source": "DataQualityJobInput", + "target": "DataQualityJobInput" + }, + { + "source": "DataQualityJobOutputConfig", + "target": "DataQualityJobOutputConfig" + }, + { + "source": "JobDefinitionName", + "target": "JobDefinitionName" + }, + { + "source": "JobResources", + "target": "JobResources" + }, + { + "source": "NetworkConfig", + "target": "NetworkConfig" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "StoppingCondition", + "target": "StoppingCondition" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDataQualityJobDefinition", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::DataQualityJobDefinition", + "mappings": [ + { + "source": "JobDefinitionName", + "target": "JobDefinitionName" + } + ], + "operation": "DeleteDataQualityJobDefinition", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Device", + "mappings": [ + { + "source": "DeviceFleetName", + "target": "DeviceFleetName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "RegisterDevices", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Device", + "mappings": [ + { + "source": "DeviceFleetName", + "target": "DeviceFleetName" + } + ], + "operation": "DeregisterDevices", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::DeviceFleet", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DeviceFleetName", + "target": "DeviceFleetName" + }, + { + "source": "OutputConfig", + "target": "OutputConfig" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDeviceFleet", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::DeviceFleet", + "mappings": [ + { + "source": "DeviceFleetName", + "target": "DeviceFleetName" + } + ], + "operation": "DeleteDeviceFleet", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Domain", + "mappings": [ + { + "source": "AppNetworkAccessType", + "target": "AppNetworkAccessType" + }, + { + "source": "AppSecurityGroupManagement", + "target": "AppSecurityGroupManagement" + }, + { + "source": "AuthMode", + "target": "AuthMode" + }, + { + "source": "DefaultSpaceSettings", + "target": "DefaultSpaceSettings" + }, + { + "source": "DefaultUserSettings", + "target": "DefaultUserSettings" + }, + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "DomainSettings", + "target": "DomainSettings" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "SubnetIds", + "target": "SubnetIds" + }, + { + "source": "TagPropagation", + "target": "TagPropagation" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateDomain", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Domain", + "mappings": [], + "operation": "DeleteDomain", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Endpoint", + "mappings": [ + { + "source": "DeploymentConfig", + "target": "DeploymentConfig" + }, + { + "source": "EndpointConfigName", + "target": "EndpointConfigName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEndpoint", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Endpoint", + "mappings": [], + "operation": "DeleteEndpoint", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Experiment", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "ExperimentName", + "target": "ExperimentName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateExperiment", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Experiment", + "mappings": [ + { + "source": "ExperimentName", + "target": "ExperimentName" + } + ], + "operation": "DeleteExperiment", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::FeatureGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "EventTimeFeatureName", + "target": "EventTimeFeatureName" + }, + { + "source": "FeatureDefinitions", + "target": "FeatureDefinitions" + }, + { + "source": "FeatureGroupName", + "target": "FeatureGroupName" + }, + { + "source": "OfflineStoreConfig", + "target": "OfflineStoreConfig" + }, + { + "source": "OnlineStoreConfig", + "target": "OnlineStoreConfig" + }, + { + "source": "RecordIdentifierFeatureName", + "target": "RecordIdentifierFeatureName" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "ThroughputConfig", + "target": "ThroughputConfig" + } + ], + "operation": "CreateFeatureGroup", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::FeatureGroup", + "mappings": [ + { + "source": "FeatureGroupName", + "target": "FeatureGroupName" + } + ], + "operation": "DeleteFeatureGroup", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Hub", + "mappings": [ + { + "source": "HubDescription", + "target": "HubDescription" + }, + { + "source": "HubDisplayName", + "target": "HubDisplayName" + }, + { + "source": "HubName", + "target": "HubName" + }, + { + "source": "HubSearchKeywords", + "target": "HubSearchKeywords" + }, + { + "source": "S3StorageConfig", + "target": "S3StorageConfig" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateHub", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Hub", + "mappings": [ + { + "source": "HubName", + "target": "HubName" + } + ], + "operation": "DeleteHub", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Image", + "mappings": [ + { + "source": "ImageName", + "target": "ImageName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateImage", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Image", + "mappings": [ + { + "source": "ImageName", + "target": "ImageName" + } + ], + "operation": "DeleteImage", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ImageVersion", + "mappings": [ + { + "source": "Aliases", + "target": "Aliases" + }, + { + "source": "BaseImage", + "target": "BaseImage" + }, + { + "source": "Horovod", + "target": "Horovod" + }, + { + "source": "ImageName", + "target": "ImageName" + }, + { + "source": "JobType", + "target": "JobType" + }, + { + "source": "MLFramework", + "target": "MLFramework" + }, + { + "source": "Processor", + "target": "Processor" + }, + { + "source": "ProgrammingLang", + "target": "ProgrammingLang" + }, + { + "source": "ReleaseNotes", + "target": "ReleaseNotes" + }, + { + "source": "VendorGuidance", + "target": "VendorGuidance" + } + ], + "operation": "CreateImageVersion", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ImageVersion", + "mappings": [ + { + "source": "Alias", + "target": "Alias" + }, + { + "source": "ImageName", + "target": "ImageName" + } + ], + "operation": "DeleteImageVersion", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::InferenceComponent", + "mappings": [ + { + "source": "EndpointName", + "target": "EndpointName" + }, + { + "source": "InferenceComponentName", + "target": "InferenceComponentName" + }, + { + "source": "RuntimeConfig", + "target": "RuntimeConfig" + }, + { + "source": "Specification", + "target": "Specification" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VariantName", + "target": "VariantName" + } + ], + "operation": "CreateInferenceComponent", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::InferenceComponent", + "mappings": [ + { + "source": "InferenceComponentName", + "target": "InferenceComponentName" + } + ], + "operation": "DeleteInferenceComponent", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::InferenceExperiment", + "mappings": [ + { + "source": "DataStorageConfig", + "target": "DataStorageConfig" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EndpointName", + "target": "EndpointName" + }, + { + "source": "KmsKey", + "target": "KmsKey" + }, + { + "source": "ModelVariants", + "target": "ModelVariants" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Schedule", + "target": "Schedule" + }, + { + "source": "ShadowModeConfig", + "target": "ShadowModeConfig" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateInferenceExperiment", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::InferenceExperiment", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteInferenceExperiment", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::MlflowTrackingServer", + "mappings": [ + { + "source": "ArtifactStoreUri", + "target": "ArtifactStoreUri" + }, + { + "source": "AutomaticModelRegistration", + "target": "AutomaticModelRegistration" + }, + { + "source": "MlflowVersion", + "target": "MlflowVersion" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TrackingServerName", + "target": "TrackingServerName" + }, + { + "source": "TrackingServerSize", + "target": "TrackingServerSize" + }, + { + "source": "WeeklyMaintenanceWindowStart", + "target": "WeeklyMaintenanceWindowStart" + } + ], + "operation": "CreateMlflowTrackingServer", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::MlflowTrackingServer", + "mappings": [ + { + "source": "TrackingServerName", + "target": "TrackingServerName" + } + ], + "operation": "DeleteMlflowTrackingServer", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Model", + "mappings": [ + { + "source": "Containers", + "target": "Containers" + }, + { + "source": "EnableNetworkIsolation", + "target": "EnableNetworkIsolation" + }, + { + "source": "ExecutionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "InferenceExecutionConfig", + "target": "InferenceExecutionConfig" + }, + { + "source": "ModelName", + "target": "ModelName" + }, + { + "source": "PrimaryContainer", + "target": "PrimaryContainer" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcConfig", + "target": "VpcConfig" + } + ], + "operation": "CreateModel", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Model", + "mappings": [ + { + "source": "ModelName", + "target": "ModelName" + } + ], + "operation": "DeleteModel", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ModelBiasJobDefinition", + "mappings": [ + { + "source": "JobDefinitionName", + "target": "JobDefinitionName" + }, + { + "source": "JobResources", + "target": "JobResources" + }, + { + "source": "ModelBiasAppSpecification", + "target": "ModelBiasAppSpecification" + }, + { + "source": "ModelBiasBaselineConfig", + "target": "ModelBiasBaselineConfig" + }, + { + "source": "ModelBiasJobInput", + "target": "ModelBiasJobInput" + }, + { + "source": "ModelBiasJobOutputConfig", + "target": "ModelBiasJobOutputConfig" + }, + { + "source": "NetworkConfig", + "target": "NetworkConfig" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "StoppingCondition", + "target": "StoppingCondition" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateModelBiasJobDefinition", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ModelBiasJobDefinition", + "mappings": [ + { + "source": "JobDefinitionName", + "target": "JobDefinitionName" + } + ], + "operation": "DeleteModelBiasJobDefinition", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ModelCard", + "mappings": [ + { + "source": "Content", + "target": "Content" + }, + { + "source": "ModelCardName", + "target": "ModelCardName" + }, + { + "source": "ModelCardStatus", + "target": "ModelCardStatus" + }, + { + "source": "SecurityConfig", + "target": "SecurityConfig" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateModelCard", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ModelCard", + "mappings": [ + { + "source": "ModelCardName", + "target": "ModelCardName" + } + ], + "operation": "DeleteModelCard", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ModelExplainabilityJobDefinition", + "mappings": [ + { + "source": "JobDefinitionName", + "target": "JobDefinitionName" + }, + { + "source": "JobResources", + "target": "JobResources" + }, + { + "source": "ModelExplainabilityAppSpecification", + "target": "ModelExplainabilityAppSpecification" + }, + { + "source": "ModelExplainabilityBaselineConfig", + "target": "ModelExplainabilityBaselineConfig" + }, + { + "source": "ModelExplainabilityJobInput", + "target": "ModelExplainabilityJobInput" + }, + { + "source": "ModelExplainabilityJobOutputConfig", + "target": "ModelExplainabilityJobOutputConfig" + }, + { + "source": "NetworkConfig", + "target": "NetworkConfig" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "StoppingCondition", + "target": "StoppingCondition" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateModelExplainabilityJobDefinition", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ModelExplainabilityJobDefinition", + "mappings": [ + { + "source": "JobDefinitionName", + "target": "JobDefinitionName" + } + ], + "operation": "DeleteModelExplainabilityJobDefinition", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ModelPackage", + "mappings": [ + { + "source": "AdditionalInferenceSpecifications", + "target": "AdditionalInferenceSpecifications" + }, + { + "source": "CertifyForMarketplace", + "target": "CertifyForMarketplace" + }, + { + "source": "ClientToken", + "target": "ClientToken" + }, + { + "source": "CustomerMetadataProperties", + "target": "CustomerMetadataProperties" + }, + { + "source": "Domain", + "target": "Domain" + }, + { + "source": "DriftCheckBaselines", + "target": "DriftCheckBaselines" + }, + { + "source": "InferenceSpecification", + "target": "InferenceSpecification" + }, + { + "source": "MetadataProperties", + "target": "MetadataProperties" + }, + { + "source": "ModelApprovalStatus", + "target": "ModelApprovalStatus" + }, + { + "source": "ModelCard", + "target": "ModelCard" + }, + { + "source": "ModelMetrics", + "target": "ModelMetrics" + }, + { + "source": "ModelPackageDescription", + "target": "ModelPackageDescription" + }, + { + "source": "ModelPackageGroupName", + "target": "ModelPackageGroupName" + }, + { + "source": "ModelPackageName", + "target": "ModelPackageName" + }, + { + "source": "SamplePayloadUrl", + "target": "SamplePayloadUrl" + }, + { + "source": "SecurityConfig", + "target": "SecurityConfig" + }, + { + "source": "SkipModelValidation", + "target": "SkipModelValidation" + }, + { + "source": "SourceAlgorithmSpecification", + "target": "SourceAlgorithmSpecification" + }, + { + "source": "SourceUri", + "target": "SourceUri" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Task", + "target": "Task" + }, + { + "source": "ValidationSpecification", + "target": "ValidationSpecification" + } + ], + "operation": "CreateModelPackage", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ModelPackage", + "mappings": [ + { + "source": "ModelPackageName", + "target": "ModelPackageName" + } + ], + "operation": "DeleteModelPackage", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ModelPackageGroup", + "mappings": [ + { + "source": "ModelPackageGroupDescription", + "target": "ModelPackageGroupDescription" + }, + { + "source": "ModelPackageGroupName", + "target": "ModelPackageGroupName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateModelPackageGroup", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ModelPackageGroup", + "mappings": [ + { + "source": "ModelPackageGroupName", + "target": "ModelPackageGroupName" + } + ], + "operation": "DeleteModelPackageGroup", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ModelQualityJobDefinition", + "mappings": [ + { + "source": "JobDefinitionName", + "target": "JobDefinitionName" + }, + { + "source": "JobResources", + "target": "JobResources" + }, + { + "source": "ModelQualityAppSpecification", + "target": "ModelQualityAppSpecification" + }, + { + "source": "ModelQualityBaselineConfig", + "target": "ModelQualityBaselineConfig" + }, + { + "source": "ModelQualityJobInput", + "target": "ModelQualityJobInput" + }, + { + "source": "ModelQualityJobOutputConfig", + "target": "ModelQualityJobOutputConfig" + }, + { + "source": "NetworkConfig", + "target": "NetworkConfig" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "StoppingCondition", + "target": "StoppingCondition" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateModelQualityJobDefinition", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ModelQualityJobDefinition", + "mappings": [ + { + "source": "JobDefinitionName", + "target": "JobDefinitionName" + } + ], + "operation": "DeleteModelQualityJobDefinition", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::MonitoringSchedule", + "mappings": [ + { + "source": "MonitoringScheduleConfig", + "target": "MonitoringScheduleConfig" + }, + { + "source": "MonitoringScheduleName", + "target": "MonitoringScheduleName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateMonitoringSchedule", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::MonitoringSchedule", + "mappings": [ + { + "source": "MonitoringScheduleName", + "target": "MonitoringScheduleName" + } + ], + "operation": "DeleteMonitoringSchedule", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::PartnerApp", + "mappings": [ + { + "source": "ApplicationConfig", + "target": "ApplicationConfig" + }, + { + "source": "AuthType", + "target": "AuthType" + }, + { + "source": "ClientToken", + "target": "ClientToken" + }, + { + "source": "EnableIamSessionBasedIdentity", + "target": "EnableIamSessionBasedIdentity" + }, + { + "source": "ExecutionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "MaintenanceConfig", + "target": "MaintenanceConfig" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Tier", + "target": "Tier" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreatePartnerApp", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::PartnerApp", + "mappings": [ + { + "source": "ClientToken", + "target": "ClientToken" + } + ], + "operation": "DeletePartnerApp", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Pipeline", + "mappings": [ + { + "source": "ParallelismConfiguration", + "target": "ParallelismConfiguration" + }, + { + "source": "PipelineDefinition", + "target": "PipelineDefinition" + }, + { + "source": "PipelineDescription", + "target": "PipelineDescription" + }, + { + "source": "PipelineDisplayName", + "target": "PipelineDisplayName" + }, + { + "source": "PipelineName", + "target": "PipelineName" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePipeline", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Pipeline", + "mappings": [ + { + "source": "PipelineName", + "target": "PipelineName" + } + ], + "operation": "DeletePipeline", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::ProcessingJob", + "mappings": [ + { + "source": "AppSpecification", + "target": "AppSpecification" + }, + { + "source": "Environment", + "target": "Environment" + }, + { + "source": "ExperimentConfig", + "target": "ExperimentConfig" + }, + { + "source": "NetworkConfig", + "target": "NetworkConfig" + }, + { + "source": "ProcessingInputs", + "target": "ProcessingInputs" + }, + { + "source": "ProcessingJobName", + "target": "ProcessingJobName" + }, + { + "source": "ProcessingOutputConfig", + "target": "ProcessingOutputConfig" + }, + { + "source": "ProcessingResources", + "target": "ProcessingResources" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "StoppingCondition", + "target": "StoppingCondition" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateProcessingJob", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Project", + "mappings": [ + { + "source": "ProjectDescription", + "target": "ProjectDescription" + }, + { + "source": "ProjectName", + "target": "ProjectName" + }, + { + "source": "ServiceCatalogProvisioningDetails", + "target": "ServiceCatalogProvisioningDetails" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateProject", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Project", + "mappings": [ + { + "source": "ProjectName", + "target": "ProjectName" + } + ], + "operation": "DeleteProject", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Space", + "mappings": [ + { + "source": "DomainId", + "target": "DomainId" + }, + { + "source": "OwnershipSettings", + "target": "OwnershipSettings" + }, + { + "source": "SpaceDisplayName", + "target": "SpaceDisplayName" + }, + { + "source": "SpaceName", + "target": "SpaceName" + }, + { + "source": "SpaceSettings", + "target": "SpaceSettings" + }, + { + "source": "SpaceSharingSettings", + "target": "SpaceSharingSettings" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSpace", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Space", + "mappings": [ + { + "source": "DomainId", + "target": "DomainId" + }, + { + "source": "SpaceName", + "target": "SpaceName" + } + ], + "operation": "DeleteSpace", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::StudioLifecycleConfig", + "mappings": [ + { + "source": "StudioLifecycleConfigAppType", + "target": "StudioLifecycleConfigAppType" + }, + { + "source": "StudioLifecycleConfigContent", + "target": "StudioLifecycleConfigContent" + }, + { + "source": "StudioLifecycleConfigName", + "target": "StudioLifecycleConfigName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateStudioLifecycleConfig", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::StudioLifecycleConfig", + "mappings": [ + { + "source": "StudioLifecycleConfigName", + "target": "StudioLifecycleConfigName" + } + ], + "operation": "DeleteStudioLifecycleConfig", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::TrialComponent", + "mappings": [ + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "InputArtifacts", + "target": "InputArtifacts" + }, + { + "source": "MetadataProperties", + "target": "MetadataProperties" + }, + { + "source": "OutputArtifacts", + "target": "OutputArtifacts" + }, + { + "source": "Parameters", + "target": "Parameters" + }, + { + "source": "Status", + "target": "Status" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TrialComponentName", + "target": "TrialComponentName" + } + ], + "operation": "CreateTrialComponent", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::TrialComponent", + "mappings": [ + { + "source": "TrialComponentName", + "target": "TrialComponentName" + } + ], + "operation": "DeleteTrialComponent", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::UserProfile", + "mappings": [ + { + "source": "DomainId", + "target": "DomainId" + }, + { + "source": "SingleSignOnUserIdentifier", + "target": "SingleSignOnUserIdentifier" + }, + { + "source": "SingleSignOnUserValue", + "target": "SingleSignOnUserValue" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UserProfileName", + "target": "UserProfileName" + }, + { + "source": "UserSettings", + "target": "UserSettings" + } + ], + "operation": "CreateUserProfile", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::UserProfile", + "mappings": [ + { + "source": "DomainId", + "target": "DomainId" + }, + { + "source": "UserProfileName", + "target": "UserProfileName" + } + ], + "operation": "DeleteUserProfile", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::Scheduler::Schedule", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "EndDate", + "target": "EndDate" + }, + { + "source": "FlexibleTimeWindow", + "target": "FlexibleTimeWindow" + }, + { + "source": "GroupName", + "target": "GroupName" + }, + { + "source": "KmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ScheduleExpression", + "target": "ScheduleExpression" + }, + { + "source": "ScheduleExpressionTimezone", + "target": "ScheduleExpressionTimezone" + }, + { + "source": "StartDate", + "target": "StartDate" + }, + { + "source": "State", + "target": "State" + }, + { + "source": "Target", + "target": "Target" + } + ], + "operation": "CreateSchedule", + "phase": "create", + "service": "scheduler" + }, + { + "cfn_type": "AWS::Scheduler::Schedule", + "mappings": [ + { + "source": "GroupName", + "target": "GroupName" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteSchedule", + "phase": "delete", + "service": "scheduler" + }, + { + "cfn_type": "AWS::Scheduler::ScheduleGroup", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateScheduleGroup", + "phase": "create", + "service": "scheduler" + }, + { + "cfn_type": "AWS::Scheduler::ScheduleGroup", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteScheduleGroup", + "phase": "delete", + "service": "scheduler" + }, + { + "cfn_type": "AWS::SecretsManager::ResourcePolicy", + "mappings": [ + { + "source": "BlockPublicPolicy", + "target": "BlockPublicPolicy" + }, + { + "source": "ResourcePolicy", + "target": "ResourcePolicy" + }, + { + "source": "SecretId", + "target": "SecretId" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "secretsmanager" + }, + { + "cfn_type": "AWS::SecretsManager::ResourcePolicy", + "mappings": [ + { + "source": "SecretId", + "target": "SecretId" + } + ], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "secretsmanager" + }, + { + "cfn_type": "AWS::SecretsManager::Secret", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "SecretString", + "target": "SecretString" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSecret", + "phase": "create", + "service": "secretsmanager" + }, + { + "cfn_type": "AWS::SecretsManager::Secret", + "mappings": [], + "operation": "DeleteSecret", + "phase": "delete", + "service": "secretsmanager" + }, + { + "cfn_type": "AWS::SecurityHub::AggregatorV2", + "mappings": [ + { + "source": "LinkedRegions", + "target": "LinkedRegions" + }, + { + "source": "RegionLinkingMode", + "target": "RegionLinkingMode" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAggregatorV2", + "phase": "create", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::AggregatorV2", + "mappings": [], + "operation": "DeleteAggregatorV2", + "phase": "delete", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::AutomationRule", + "mappings": [ + { + "source": "Actions", + "target": "Actions" + }, + { + "source": "Criteria", + "target": "Criteria" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "IsTerminal", + "target": "IsTerminal" + }, + { + "source": "RuleName", + "target": "RuleName" + }, + { + "source": "RuleOrder", + "target": "RuleOrder" + }, + { + "source": "RuleStatus", + "target": "RuleStatus" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAutomationRule", + "phase": "create", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::AutomationRuleV2", + "mappings": [ + { + "source": "Actions", + "target": "Actions" + }, + { + "source": "Criteria", + "target": "Criteria" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "RuleName", + "target": "RuleName" + }, + { + "source": "RuleOrder", + "target": "RuleOrder" + }, + { + "source": "RuleStatus", + "target": "RuleStatus" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAutomationRuleV2", + "phase": "create", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::AutomationRuleV2", + "mappings": [], + "operation": "DeleteAutomationRuleV2", + "phase": "delete", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::ConfigurationPolicy", + "mappings": [ + { + "source": "ConfigurationPolicy", + "target": "ConfigurationPolicy" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateConfigurationPolicy", + "phase": "create", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::ConfigurationPolicy", + "mappings": [], + "operation": "DeleteConfigurationPolicy", + "phase": "delete", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::ConnectorV2", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "KmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Provider", + "target": "Provider" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateConnectorV2", + "phase": "create", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::ConnectorV2", + "mappings": [], + "operation": "DeleteConnectorV2", + "phase": "delete", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::FindingAggregator", + "mappings": [ + { + "source": "RegionLinkingMode", + "target": "RegionLinkingMode" + }, + { + "source": "Regions", + "target": "Regions" + } + ], + "operation": "CreateFindingAggregator", + "phase": "create", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::FindingAggregator", + "mappings": [], + "operation": "DeleteFindingAggregator", + "phase": "delete", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::Hub", + "mappings": [ + { + "source": "ControlFindingGenerator", + "target": "ControlFindingGenerator" + }, + { + "source": "EnableDefaultStandards", + "target": "EnableDefaultStandards" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "EnableSecurityHub", + "phase": "create", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::HubV2", + "mappings": [ + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "EnableSecurityHubV2", + "phase": "create", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::Insight", + "mappings": [ + { + "source": "Filters", + "target": "Filters" + }, + { + "source": "GroupByAttribute", + "target": "GroupByAttribute" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateInsight", + "phase": "create", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::Insight", + "mappings": [], + "operation": "DeleteInsight", + "phase": "delete", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityLake::AwsLogSource", + "mappings": [], + "operation": "DeleteAwsLogSource", + "phase": "delete", + "service": "securitylake" + }, + { + "cfn_type": "AWS::SecurityLake::DataLake", + "mappings": [ + { + "source": "metaStoreManagerRoleArn", + "target": "MetaStoreManagerRoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDataLake", + "phase": "create", + "service": "securitylake" + }, + { + "cfn_type": "AWS::SecurityLake::DataLake", + "mappings": [], + "operation": "DeleteDataLake", + "phase": "delete", + "service": "securitylake" + }, + { + "cfn_type": "AWS::SecurityLake::Subscriber", + "mappings": [ + { + "source": "accessTypes", + "target": "AccessTypes" + }, + { + "source": "sources", + "target": "Sources" + }, + { + "source": "subscriberDescription", + "target": "SubscriberDescription" + }, + { + "source": "subscriberIdentity", + "target": "SubscriberIdentity" + }, + { + "source": "subscriberName", + "target": "SubscriberName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSubscriber", + "phase": "create", + "service": "securitylake" + }, + { + "cfn_type": "AWS::SecurityLake::Subscriber", + "mappings": [], + "operation": "DeleteSubscriber", + "phase": "delete", + "service": "securitylake" + }, + { + "cfn_type": "AWS::SecurityLake::SubscriberNotification", + "mappings": [], + "operation": "DeleteSubscriberNotification", + "phase": "delete", + "service": "securitylake" + }, + { + "cfn_type": "AWS::ServiceCatalog::CloudFormationProduct", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Distributor", + "target": "Distributor" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Owner", + "target": "Owner" + }, + { + "source": "ProductType", + "target": "ProductType" + }, + { + "source": "ProvisioningArtifactParameters", + "target": "ProvisioningArtifactParameters" + }, + { + "source": "SourceConnection", + "target": "SourceConnection" + }, + { + "source": "SupportDescription", + "target": "SupportDescription" + }, + { + "source": "SupportEmail", + "target": "SupportEmail" + }, + { + "source": "SupportUrl", + "target": "SupportUrl" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateProduct", + "phase": "create", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::CloudFormationProvisionedProduct", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + }, + { + "source": "NotificationArns", + "target": "NotificationArns" + }, + { + "source": "PathId", + "target": "PathId" + }, + { + "source": "PathName", + "target": "PathName" + }, + { + "source": "ProductId", + "target": "ProductId" + }, + { + "source": "ProductName", + "target": "ProductName" + }, + { + "source": "ProvisionedProductName", + "target": "ProvisionedProductName" + }, + { + "source": "ProvisioningArtifactId", + "target": "ProvisioningArtifactId" + }, + { + "source": "ProvisioningArtifactName", + "target": "ProvisioningArtifactName" + }, + { + "source": "ProvisioningParameters", + "target": "ProvisioningParameters" + }, + { + "source": "ProvisioningPreferences", + "target": "ProvisioningPreferences" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "ProvisionProduct", + "phase": "create", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::CloudFormationProvisionedProduct", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + }, + { + "source": "ProvisionedProductName", + "target": "ProvisionedProductName" + } + ], + "operation": "TerminateProvisionedProduct", + "phase": "delete", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::Portfolio", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "ProviderName", + "target": "ProviderName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePortfolio", + "phase": "create", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::Portfolio", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + } + ], + "operation": "DeletePortfolio", + "phase": "delete", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::PortfolioPrincipalAssociation", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + }, + { + "source": "PortfolioId", + "target": "PortfolioId" + }, + { + "source": "PrincipalARN", + "target": "PrincipalARN" + }, + { + "source": "PrincipalType", + "target": "PrincipalType" + } + ], + "operation": "AssociatePrincipalWithPortfolio", + "phase": "create", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::PortfolioPrincipalAssociation", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + }, + { + "source": "PortfolioId", + "target": "PortfolioId" + }, + { + "source": "PrincipalARN", + "target": "PrincipalARN" + }, + { + "source": "PrincipalType", + "target": "PrincipalType" + } + ], + "operation": "DisassociatePrincipalFromPortfolio", + "phase": "delete", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::PortfolioProductAssociation", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + }, + { + "source": "PortfolioId", + "target": "PortfolioId" + }, + { + "source": "ProductId", + "target": "ProductId" + }, + { + "source": "SourcePortfolioId", + "target": "SourcePortfolioId" + } + ], + "operation": "AssociateProductWithPortfolio", + "phase": "create", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::PortfolioProductAssociation", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + }, + { + "source": "PortfolioId", + "target": "PortfolioId" + }, + { + "source": "ProductId", + "target": "ProductId" + } + ], + "operation": "DisassociateProductFromPortfolio", + "phase": "delete", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::PortfolioShare", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + }, + { + "source": "AccountId", + "target": "AccountId" + }, + { + "source": "PortfolioId", + "target": "PortfolioId" + }, + { + "source": "ShareTagOptions", + "target": "ShareTagOptions" + } + ], + "operation": "CreatePortfolioShare", + "phase": "create", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::PortfolioShare", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + }, + { + "source": "AccountId", + "target": "AccountId" + }, + { + "source": "PortfolioId", + "target": "PortfolioId" + } + ], + "operation": "DeletePortfolioShare", + "phase": "delete", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::ServiceAction", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + }, + { + "source": "Definition", + "target": "Definition" + }, + { + "source": "DefinitionType", + "target": "DefinitionType" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateServiceAction", + "phase": "create", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::ServiceAction", + "mappings": [ + { + "source": "AcceptLanguage", + "target": "AcceptLanguage" + } + ], + "operation": "DeleteServiceAction", + "phase": "delete", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::ServiceActionAssociation", + "mappings": [ + { + "source": "ProductId", + "target": "ProductId" + }, + { + "source": "ProvisioningArtifactId", + "target": "ProvisioningArtifactId" + }, + { + "source": "ServiceActionId", + "target": "ServiceActionId" + } + ], + "operation": "AssociateServiceActionWithProvisioningArtifact", + "phase": "create", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::ServiceActionAssociation", + "mappings": [ + { + "source": "ProductId", + "target": "ProductId" + }, + { + "source": "ProvisioningArtifactId", + "target": "ProvisioningArtifactId" + }, + { + "source": "ServiceActionId", + "target": "ServiceActionId" + } + ], + "operation": "DisassociateServiceActionFromProvisioningArtifact", + "phase": "delete", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::TagOption", + "mappings": [ + { + "source": "Key", + "target": "Key" + }, + { + "source": "Value", + "target": "Value" + } + ], + "operation": "CreateTagOption", + "phase": "create", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::TagOption", + "mappings": [], + "operation": "DeleteTagOption", + "phase": "delete", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::TagOptionAssociation", + "mappings": [ + { + "source": "ResourceId", + "target": "ResourceId" + }, + { + "source": "TagOptionId", + "target": "TagOptionId" + } + ], + "operation": "AssociateTagOptionWithResource", + "phase": "create", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalog::TagOptionAssociation", + "mappings": [ + { + "source": "ResourceId", + "target": "ResourceId" + }, + { + "source": "TagOptionId", + "target": "TagOptionId" + } + ], + "operation": "DisassociateTagOptionFromResource", + "phase": "delete", + "service": "servicecatalog" + }, + { + "cfn_type": "AWS::ServiceCatalogAppRegistry::Application", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "servicecatalog-appregistry" + }, + { + "cfn_type": "AWS::ServiceCatalogAppRegistry::Application", + "mappings": [], + "operation": "DeleteApplication", + "phase": "delete", + "service": "servicecatalog-appregistry" + }, + { + "cfn_type": "AWS::ServiceCatalogAppRegistry::AttributeGroup", + "mappings": [ + { + "source": "attributes", + "target": "Attributes" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAttributeGroup", + "phase": "create", + "service": "servicecatalog-appregistry" + }, + { + "cfn_type": "AWS::ServiceCatalogAppRegistry::AttributeGroup", + "mappings": [], + "operation": "DeleteAttributeGroup", + "phase": "delete", + "service": "servicecatalog-appregistry" + }, + { + "cfn_type": "AWS::ServiceCatalogAppRegistry::AttributeGroupAssociation", + "mappings": [ + { + "source": "application", + "target": "Application" + }, + { + "source": "attributeGroup", + "target": "AttributeGroup" + } + ], + "operation": "AssociateAttributeGroup", + "phase": "create", + "service": "servicecatalog-appregistry" + }, + { + "cfn_type": "AWS::ServiceCatalogAppRegistry::AttributeGroupAssociation", + "mappings": [ + { + "source": "application", + "target": "Application" + }, + { + "source": "attributeGroup", + "target": "AttributeGroup" + } + ], + "operation": "DisassociateAttributeGroup", + "phase": "delete", + "service": "servicecatalog-appregistry" + }, + { + "cfn_type": "AWS::ServiceCatalogAppRegistry::ResourceAssociation", + "mappings": [ + { + "source": "application", + "target": "Application" + }, + { + "source": "resource", + "target": "Resource" + }, + { + "source": "resourceType", + "target": "ResourceType" + } + ], + "operation": "AssociateResource", + "phase": "create", + "service": "servicecatalog-appregistry" + }, + { + "cfn_type": "AWS::ServiceCatalogAppRegistry::ResourceAssociation", + "mappings": [ + { + "source": "application", + "target": "Application" + }, + { + "source": "resource", + "target": "Resource" + }, + { + "source": "resourceType", + "target": "ResourceType" + } + ], + "operation": "DisassociateResource", + "phase": "delete", + "service": "servicecatalog-appregistry" + }, + { + "cfn_type": "AWS::ServiceDiscovery::PublicDnsNamespace", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Properties", + "target": "Properties" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePublicDnsNamespace", + "phase": "create", + "service": "servicediscovery" + }, + { + "cfn_type": "AWS::ServiceDiscovery::Service", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DnsConfig", + "target": "DnsConfig" + }, + { + "source": "HealthCheckConfig", + "target": "HealthCheckConfig" + }, + { + "source": "HealthCheckCustomConfig", + "target": "HealthCheckCustomConfig" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NamespaceId", + "target": "NamespaceId" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateService", + "phase": "create", + "service": "servicediscovery" + }, + { + "cfn_type": "AWS::ServiceDiscovery::Service", + "mappings": [], + "operation": "DeleteService", + "phase": "delete", + "service": "servicediscovery" + }, + { + "cfn_type": "AWS::Shield::Protection", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "ResourceArn", + "target": "ResourceArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateProtection", + "phase": "create", + "service": "shield" + }, + { + "cfn_type": "AWS::Shield::Protection", + "mappings": [], + "operation": "DeleteProtection", + "phase": "delete", + "service": "shield" + }, + { + "cfn_type": "AWS::Shield::ProtectionGroup", + "mappings": [ + { + "source": "Aggregation", + "target": "Aggregation" + }, + { + "source": "Members", + "target": "Members" + }, + { + "source": "Pattern", + "target": "Pattern" + }, + { + "source": "ProtectionGroupId", + "target": "ProtectionGroupId" + }, + { + "source": "ResourceType", + "target": "ResourceType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateProtectionGroup", + "phase": "create", + "service": "shield" + }, + { + "cfn_type": "AWS::Shield::ProtectionGroup", + "mappings": [ + { + "source": "ProtectionGroupId", + "target": "ProtectionGroupId" + } + ], + "operation": "DeleteProtectionGroup", + "phase": "delete", + "service": "shield" + }, + { + "cfn_type": "AWS::Signer::ProfilePermission", + "mappings": [ + { + "source": "action", + "target": "Action" + }, + { + "source": "principal", + "target": "Principal" + }, + { + "source": "profileName", + "target": "ProfileName" + }, + { + "source": "profileVersion", + "target": "ProfileVersion" + }, + { + "source": "statementId", + "target": "StatementId" + } + ], + "operation": "AddProfilePermission", + "phase": "create", + "service": "signer" + }, + { + "cfn_type": "AWS::Signer::ProfilePermission", + "mappings": [ + { + "source": "profileName", + "target": "ProfileName" + }, + { + "source": "statementId", + "target": "StatementId" + } + ], + "operation": "RemoveProfilePermission", + "phase": "delete", + "service": "signer" + }, + { + "cfn_type": "AWS::Signer::SigningProfile", + "mappings": [ + { + "source": "platformId", + "target": "PlatformId" + }, + { + "source": "signatureValidityPeriod", + "target": "SignatureValidityPeriod" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "PutSigningProfile", + "phase": "create", + "service": "signer" + }, + { + "cfn_type": "AWS::Signer::SigningProfile", + "mappings": [], + "operation": "CancelSigningProfile", + "phase": "delete", + "service": "signer" + }, + { + "cfn_type": "AWS::SimSpaceWeaver::Simulation", + "mappings": [ + { + "source": "MaximumDuration", + "target": "MaximumDuration" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "SchemaS3Location", + "target": "SchemaS3Location" + }, + { + "source": "SnapshotS3Location", + "target": "SnapshotS3Location" + } + ], + "operation": "StartSimulation", + "phase": "create", + "service": "simspaceweaver" + }, + { + "cfn_type": "AWS::SimSpaceWeaver::Simulation", + "mappings": [], + "operation": "DeleteSimulation", + "phase": "delete", + "service": "simspaceweaver" + }, + { + "cfn_type": "AWS::StepFunctions::Activity", + "mappings": [ + { + "source": "encryptionConfiguration", + "target": "EncryptionConfiguration" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateActivity", + "phase": "create", + "service": "stepfunctions" + }, + { + "cfn_type": "AWS::StepFunctions::Activity", + "mappings": [], + "operation": "DeleteActivity", + "phase": "delete", + "service": "stepfunctions" + }, + { + "cfn_type": "AWS::StepFunctions::StateMachine", + "mappings": [ + { + "source": "definition", + "target": "Definition" + }, + { + "source": "encryptionConfiguration", + "target": "EncryptionConfiguration" + }, + { + "source": "loggingConfiguration", + "target": "LoggingConfiguration" + }, + { + "source": "name", + "target": "StateMachineName" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "tracingConfiguration", + "target": "TracingConfiguration" + } + ], + "operation": "CreateStateMachine", + "phase": "create", + "service": "stepfunctions" + }, + { + "cfn_type": "AWS::StepFunctions::StateMachine", + "mappings": [], + "operation": "DeleteStateMachine", + "phase": "delete", + "service": "stepfunctions" + }, + { + "cfn_type": "AWS::StepFunctions::StateMachineAlias", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "routingConfiguration", + "target": "RoutingConfiguration" + } + ], + "operation": "CreateStateMachineAlias", + "phase": "create", + "service": "stepfunctions" + }, + { + "cfn_type": "AWS::StepFunctions::StateMachineAlias", + "mappings": [], + "operation": "DeleteStateMachineAlias", + "phase": "delete", + "service": "stepfunctions" + }, + { + "cfn_type": "AWS::StepFunctions::StateMachineVersion", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "stateMachineArn", + "target": "StateMachineArn" + } + ], + "operation": "PublishStateMachineVersion", + "phase": "create", + "service": "stepfunctions" + }, + { + "cfn_type": "AWS::StepFunctions::StateMachineVersion", + "mappings": [], + "operation": "DeleteStateMachineVersion", + "phase": "delete", + "service": "stepfunctions" + }, + { + "cfn_type": "AWS::StorageGateway::TapePool", + "mappings": [ + { + "source": "PoolName", + "target": "PoolName" + }, + { + "source": "RetentionLockTimeInDays", + "target": "RetentionLockTimeInDays" + }, + { + "source": "RetentionLockType", + "target": "RetentionLockType" + }, + { + "source": "StorageClass", + "target": "StorageClass" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateTapePool", + "phase": "create", + "service": "storagegateway" + }, + { + "cfn_type": "AWS::StorageGateway::TapePool", + "mappings": [], + "operation": "DeleteTapePool", + "phase": "delete", + "service": "storagegateway" + }, + { + "cfn_type": "AWS::SupportApp::AccountAlias", + "mappings": [ + { + "source": "accountAlias", + "target": "AccountAlias" + } + ], + "operation": "PutAccountAlias", + "phase": "create", + "service": "support-app" + }, + { + "cfn_type": "AWS::SupportApp::AccountAlias", + "mappings": [], + "operation": "DeleteAccountAlias", + "phase": "delete", + "service": "support-app" + }, + { + "cfn_type": "AWS::SupportApp::SlackChannelConfiguration", + "mappings": [ + { + "source": "channelId", + "target": "ChannelId" + }, + { + "source": "channelName", + "target": "ChannelName" + }, + { + "source": "channelRoleArn", + "target": "ChannelRoleArn" + }, + { + "source": "notifyOnAddCorrespondenceToCase", + "target": "NotifyOnAddCorrespondenceToCase" + }, + { + "source": "notifyOnCaseSeverity", + "target": "NotifyOnCaseSeverity" + }, + { + "source": "notifyOnCreateOrReopenCase", + "target": "NotifyOnCreateOrReopenCase" + }, + { + "source": "notifyOnResolveCase", + "target": "NotifyOnResolveCase" + }, + { + "source": "teamId", + "target": "TeamId" + } + ], + "operation": "CreateSlackChannelConfiguration", + "phase": "create", + "service": "support-app" + }, + { + "cfn_type": "AWS::SupportApp::SlackChannelConfiguration", + "mappings": [ + { + "source": "channelId", + "target": "ChannelId" + }, + { + "source": "teamId", + "target": "TeamId" + } + ], + "operation": "DeleteSlackChannelConfiguration", + "phase": "delete", + "service": "support-app" + }, + { + "cfn_type": "AWS::SupportApp::SlackWorkspaceConfiguration", + "mappings": [ + { + "source": "teamId", + "target": "TeamId" + } + ], + "operation": "DeleteSlackWorkspaceConfiguration", + "phase": "delete", + "service": "support-app" + }, + { + "cfn_type": "AWS::Synthetics::Canary", + "mappings": [ + { + "source": "ArtifactConfig", + "target": "ArtifactConfig" + }, + { + "source": "ArtifactS3Location", + "target": "ArtifactS3Location" + }, + { + "source": "Code", + "target": "Code" + }, + { + "source": "ExecutionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ProvisionedResourceCleanup", + "target": "ProvisionedResourceCleanup" + }, + { + "source": "ResourcesToReplicateTags", + "target": "ResourcesToReplicateTags" + }, + { + "source": "RunConfig", + "target": "RunConfig" + }, + { + "source": "RuntimeVersion", + "target": "RuntimeVersion" + }, + { + "source": "Schedule", + "target": "Schedule" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VpcConfig", + "target": "VPCConfig" + } + ], + "operation": "CreateCanary", + "phase": "create", + "service": "synthetics" + }, + { + "cfn_type": "AWS::Synthetics::Canary", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteCanary", + "phase": "delete", + "service": "synthetics" + }, + { + "cfn_type": "AWS::Synthetics::Group", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateGroup", + "phase": "create", + "service": "synthetics" + }, + { + "cfn_type": "AWS::Synthetics::Group", + "mappings": [], + "operation": "DeleteGroup", + "phase": "delete", + "service": "synthetics" + }, + { + "cfn_type": "AWS::Timestream::Database", + "mappings": [ + { + "source": "DatabaseName", + "target": "DatabaseName" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDatabase", + "phase": "create", + "service": "timestream-write" + }, + { + "cfn_type": "AWS::Timestream::Database", + "mappings": [ + { + "source": "DatabaseName", + "target": "DatabaseName" + } + ], + "operation": "DeleteDatabase", + "phase": "delete", + "service": "timestream-write" + }, + { + "cfn_type": "AWS::Timestream::InfluxDBCluster", + "mappings": [ + { + "source": "allocatedStorage", + "target": "AllocatedStorage" + }, + { + "source": "bucket", + "target": "Bucket" + }, + { + "source": "dbInstanceType", + "target": "DbInstanceType" + }, + { + "source": "dbParameterGroupIdentifier", + "target": "DbParameterGroupIdentifier" + }, + { + "source": "dbStorageType", + "target": "DbStorageType" + }, + { + "source": "deploymentType", + "target": "DeploymentType" + }, + { + "source": "failoverMode", + "target": "FailoverMode" + }, + { + "source": "logDeliveryConfiguration", + "target": "LogDeliveryConfiguration" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "networkType", + "target": "NetworkType" + }, + { + "source": "organization", + "target": "Organization" + }, + { + "source": "password", + "target": "Password" + }, + { + "source": "port", + "target": "Port" + }, + { + "source": "publiclyAccessible", + "target": "PubliclyAccessible" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "username", + "target": "Username" + }, + { + "source": "vpcSecurityGroupIds", + "target": "VpcSecurityGroupIds" + }, + { + "source": "vpcSubnetIds", + "target": "VpcSubnetIds" + } + ], + "operation": "CreateDbCluster", + "phase": "create", + "service": "timestream-influxdb" + }, + { + "cfn_type": "AWS::Timestream::InfluxDBInstance", + "mappings": [ + { + "source": "allocatedStorage", + "target": "AllocatedStorage" + }, + { + "source": "bucket", + "target": "Bucket" + }, + { + "source": "dbInstanceType", + "target": "DbInstanceType" + }, + { + "source": "dbParameterGroupIdentifier", + "target": "DbParameterGroupIdentifier" + }, + { + "source": "dbStorageType", + "target": "DbStorageType" + }, + { + "source": "deploymentType", + "target": "DeploymentType" + }, + { + "source": "logDeliveryConfiguration", + "target": "LogDeliveryConfiguration" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "networkType", + "target": "NetworkType" + }, + { + "source": "organization", + "target": "Organization" + }, + { + "source": "password", + "target": "Password" + }, + { + "source": "port", + "target": "Port" + }, + { + "source": "publiclyAccessible", + "target": "PubliclyAccessible" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "username", + "target": "Username" + }, + { + "source": "vpcSecurityGroupIds", + "target": "VpcSecurityGroupIds" + }, + { + "source": "vpcSubnetIds", + "target": "VpcSubnetIds" + } + ], + "operation": "CreateDbInstance", + "phase": "create", + "service": "timestream-influxdb" + }, + { + "cfn_type": "AWS::Timestream::ScheduledQuery", + "mappings": [ + { + "source": "ClientToken", + "target": "ClientToken" + }, + { + "source": "ErrorReportConfiguration", + "target": "ErrorReportConfiguration" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "Name", + "target": "ScheduledQueryName" + }, + { + "source": "NotificationConfiguration", + "target": "NotificationConfiguration" + }, + { + "source": "QueryString", + "target": "QueryString" + }, + { + "source": "ScheduleConfiguration", + "target": "ScheduleConfiguration" + }, + { + "source": "ScheduledQueryExecutionRoleArn", + "target": "ScheduledQueryExecutionRoleArn" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TargetConfiguration", + "target": "TargetConfiguration" + } + ], + "operation": "CreateScheduledQuery", + "phase": "create", + "service": "timestream-query" + }, + { + "cfn_type": "AWS::Timestream::ScheduledQuery", + "mappings": [], + "operation": "DeleteScheduledQuery", + "phase": "delete", + "service": "timestream-query" + }, + { + "cfn_type": "AWS::Timestream::Table", + "mappings": [ + { + "source": "DatabaseName", + "target": "DatabaseName" + }, + { + "source": "MagneticStoreWriteProperties", + "target": "MagneticStoreWriteProperties" + }, + { + "source": "RetentionProperties", + "target": "RetentionProperties" + }, + { + "source": "Schema", + "target": "Schema" + }, + { + "source": "TableName", + "target": "TableName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateTable", + "phase": "create", + "service": "timestream-write" + }, + { + "cfn_type": "AWS::Timestream::Table", + "mappings": [ + { + "source": "DatabaseName", + "target": "DatabaseName" + }, + { + "source": "TableName", + "target": "TableName" + } + ], + "operation": "DeleteTable", + "phase": "delete", + "service": "timestream-write" + }, + { + "cfn_type": "AWS::Transcribe::VocabularyFilter", + "mappings": [ + { + "source": "DataAccessRoleArn", + "target": "DataAccessRoleArn" + }, + { + "source": "LanguageCode", + "target": "LanguageCode" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VocabularyFilterFileUri", + "target": "VocabularyFilterFileUri" + }, + { + "source": "VocabularyFilterName", + "target": "VocabularyFilterName" + }, + { + "source": "Words", + "target": "Words" + } + ], + "operation": "CreateVocabularyFilter", + "phase": "create", + "service": "transcribe" + }, + { + "cfn_type": "AWS::Transcribe::VocabularyFilter", + "mappings": [ + { + "source": "VocabularyFilterName", + "target": "VocabularyFilterName" + } + ], + "operation": "DeleteVocabularyFilter", + "phase": "delete", + "service": "transcribe" + }, + { + "cfn_type": "AWS::Transfer::Agreement", + "mappings": [ + { + "source": "AccessRole", + "target": "AccessRole" + }, + { + "source": "BaseDirectory", + "target": "BaseDirectory" + }, + { + "source": "CustomDirectories", + "target": "CustomDirectories" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "EnforceMessageSigning", + "target": "EnforceMessageSigning" + }, + { + "source": "LocalProfileId", + "target": "LocalProfileId" + }, + { + "source": "PartnerProfileId", + "target": "PartnerProfileId" + }, + { + "source": "PreserveFilename", + "target": "PreserveFilename" + }, + { + "source": "ServerId", + "target": "ServerId" + }, + { + "source": "Status", + "target": "Status" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAgreement", + "phase": "create", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::Agreement", + "mappings": [ + { + "source": "ServerId", + "target": "ServerId" + } + ], + "operation": "DeleteAgreement", + "phase": "delete", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::Certificate", + "mappings": [ + { + "source": "ActiveDate", + "target": "ActiveDate" + }, + { + "source": "Certificate", + "target": "Certificate" + }, + { + "source": "CertificateChain", + "target": "CertificateChain" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "InactiveDate", + "target": "InactiveDate" + }, + { + "source": "PrivateKey", + "target": "PrivateKey" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Usage", + "target": "Usage" + } + ], + "operation": "ImportCertificate", + "phase": "create", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::Certificate", + "mappings": [], + "operation": "DeleteCertificate", + "phase": "delete", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::Connector", + "mappings": [ + { + "source": "AccessRole", + "target": "AccessRole" + }, + { + "source": "As2Config", + "target": "As2Config" + }, + { + "source": "LoggingRole", + "target": "LoggingRole" + }, + { + "source": "SecurityPolicyName", + "target": "SecurityPolicyName" + }, + { + "source": "SftpConfig", + "target": "SftpConfig" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Url", + "target": "Url" + } + ], + "operation": "CreateConnector", + "phase": "create", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::Connector", + "mappings": [], + "operation": "DeleteConnector", + "phase": "delete", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::Profile", + "mappings": [ + { + "source": "As2Id", + "target": "As2Id" + }, + { + "source": "CertificateIds", + "target": "CertificateIds" + }, + { + "source": "ProfileType", + "target": "ProfileType" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateProfile", + "phase": "create", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::Profile", + "mappings": [], + "operation": "DeleteProfile", + "phase": "delete", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::Server", + "mappings": [ + { + "source": "Certificate", + "target": "Certificate" + }, + { + "source": "Domain", + "target": "Domain" + }, + { + "source": "EndpointDetails", + "target": "EndpointDetails" + }, + { + "source": "EndpointType", + "target": "EndpointType" + }, + { + "source": "IdentityProviderDetails", + "target": "IdentityProviderDetails" + }, + { + "source": "IdentityProviderType", + "target": "IdentityProviderType" + }, + { + "source": "IpAddressType", + "target": "IpAddressType" + }, + { + "source": "LoggingRole", + "target": "LoggingRole" + }, + { + "source": "PostAuthenticationLoginBanner", + "target": "PostAuthenticationLoginBanner" + }, + { + "source": "PreAuthenticationLoginBanner", + "target": "PreAuthenticationLoginBanner" + }, + { + "source": "ProtocolDetails", + "target": "ProtocolDetails" + }, + { + "source": "Protocols", + "target": "Protocols" + }, + { + "source": "S3StorageOptions", + "target": "S3StorageOptions" + }, + { + "source": "SecurityPolicyName", + "target": "SecurityPolicyName" + }, + { + "source": "StructuredLogDestinations", + "target": "StructuredLogDestinations" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "WorkflowDetails", + "target": "WorkflowDetails" + } + ], + "operation": "CreateServer", + "phase": "create", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::Server", + "mappings": [], + "operation": "DeleteServer", + "phase": "delete", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::User", + "mappings": [ + { + "source": "HomeDirectory", + "target": "HomeDirectory" + }, + { + "source": "HomeDirectoryMappings", + "target": "HomeDirectoryMappings" + }, + { + "source": "HomeDirectoryType", + "target": "HomeDirectoryType" + }, + { + "source": "Policy", + "target": "Policy" + }, + { + "source": "PosixProfile", + "target": "PosixProfile" + }, + { + "source": "Role", + "target": "Role" + }, + { + "source": "ServerId", + "target": "ServerId" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UserName", + "target": "UserName" + } + ], + "operation": "CreateUser", + "phase": "create", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::User", + "mappings": [ + { + "source": "ServerId", + "target": "ServerId" + }, + { + "source": "UserName", + "target": "UserName" + } + ], + "operation": "DeleteUser", + "phase": "delete", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::WebApp", + "mappings": [ + { + "source": "AccessEndpoint", + "target": "AccessEndpoint" + }, + { + "source": "IdentityProviderDetails", + "target": "IdentityProviderDetails" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "WebAppEndpointPolicy", + "target": "WebAppEndpointPolicy" + }, + { + "source": "WebAppUnits", + "target": "WebAppUnits" + } + ], + "operation": "CreateWebApp", + "phase": "create", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::WebApp", + "mappings": [], + "operation": "DeleteWebApp", + "phase": "delete", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::Workflow", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "OnExceptionSteps", + "target": "OnExceptionSteps" + }, + { + "source": "Steps", + "target": "Steps" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateWorkflow", + "phase": "create", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::Workflow", + "mappings": [], + "operation": "DeleteWorkflow", + "phase": "delete", + "service": "transfer" + }, + { + "cfn_type": "AWS::VerifiedPermissions::IdentitySource", + "mappings": [ + { + "source": "configuration", + "target": "Configuration" + }, + { + "source": "policyStoreId", + "target": "PolicyStoreId" + }, + { + "source": "principalEntityType", + "target": "PrincipalEntityType" + } + ], + "operation": "CreateIdentitySource", + "phase": "create", + "service": "verifiedpermissions" + }, + { + "cfn_type": "AWS::VerifiedPermissions::IdentitySource", + "mappings": [ + { + "source": "policyStoreId", + "target": "PolicyStoreId" + } + ], + "operation": "DeleteIdentitySource", + "phase": "delete", + "service": "verifiedpermissions" + }, + { + "cfn_type": "AWS::VerifiedPermissions::Policy", + "mappings": [ + { + "source": "definition", + "target": "Definition" + }, + { + "source": "policyStoreId", + "target": "PolicyStoreId" + } + ], + "operation": "CreatePolicy", + "phase": "create", + "service": "verifiedpermissions" + }, + { + "cfn_type": "AWS::VerifiedPermissions::Policy", + "mappings": [ + { + "source": "policyStoreId", + "target": "PolicyStoreId" + } + ], + "operation": "DeletePolicy", + "phase": "delete", + "service": "verifiedpermissions" + }, + { + "cfn_type": "AWS::VerifiedPermissions::PolicyStore", + "mappings": [ + { + "source": "deletionProtection", + "target": "DeletionProtection" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "validationSettings", + "target": "ValidationSettings" + } + ], + "operation": "CreatePolicyStore", + "phase": "create", + "service": "verifiedpermissions" + }, + { + "cfn_type": "AWS::VerifiedPermissions::PolicyStore", + "mappings": [], + "operation": "DeletePolicyStore", + "phase": "delete", + "service": "verifiedpermissions" + }, + { + "cfn_type": "AWS::VerifiedPermissions::PolicyTemplate", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "policyStoreId", + "target": "PolicyStoreId" + }, + { + "source": "statement", + "target": "Statement" + } + ], + "operation": "CreatePolicyTemplate", + "phase": "create", + "service": "verifiedpermissions" + }, + { + "cfn_type": "AWS::VerifiedPermissions::PolicyTemplate", + "mappings": [ + { + "source": "policyStoreId", + "target": "PolicyStoreId" + } + ], + "operation": "DeletePolicyTemplate", + "phase": "delete", + "service": "verifiedpermissions" + }, + { + "cfn_type": "AWS::VoiceID::Domain", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ServerSideEncryptionConfiguration", + "target": "ServerSideEncryptionConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDomain", + "phase": "create", + "service": "voice-id" + }, + { + "cfn_type": "AWS::VoiceID::Domain", + "mappings": [], + "operation": "DeleteDomain", + "phase": "delete", + "service": "voice-id" + }, + { + "cfn_type": "AWS::VpcLattice::AccessLogSubscription", + "mappings": [ + { + "source": "destinationArn", + "target": "DestinationArn" + }, + { + "source": "resourceIdentifier", + "target": "ResourceIdentifier" + }, + { + "source": "serviceNetworkLogType", + "target": "ServiceNetworkLogType" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAccessLogSubscription", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::AccessLogSubscription", + "mappings": [], + "operation": "DeleteAccessLogSubscription", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::AuthPolicy", + "mappings": [ + { + "source": "policy", + "target": "Policy" + }, + { + "source": "resourceIdentifier", + "target": "ResourceIdentifier" + } + ], + "operation": "PutAuthPolicy", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::AuthPolicy", + "mappings": [ + { + "source": "resourceIdentifier", + "target": "ResourceIdentifier" + } + ], + "operation": "DeleteAuthPolicy", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::Listener", + "mappings": [ + { + "source": "defaultAction", + "target": "DefaultAction" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "port", + "target": "Port" + }, + { + "source": "protocol", + "target": "Protocol" + }, + { + "source": "serviceIdentifier", + "target": "ServiceIdentifier" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateListener", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::Listener", + "mappings": [ + { + "source": "serviceIdentifier", + "target": "ServiceIdentifier" + } + ], + "operation": "DeleteListener", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ResourceConfiguration", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "portRanges", + "target": "PortRanges" + }, + { + "source": "resourceConfigurationDefinition", + "target": "ResourceConfigurationDefinition" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateResourceConfiguration", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ResourceConfiguration", + "mappings": [], + "operation": "DeleteResourceConfiguration", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ResourceGateway", + "mappings": [ + { + "source": "ipAddressType", + "target": "IpAddressType" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "securityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "subnetIds", + "target": "SubnetIds" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "vpcIdentifier", + "target": "VpcIdentifier" + } + ], + "operation": "CreateResourceGateway", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ResourceGateway", + "mappings": [], + "operation": "DeleteResourceGateway", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ResourcePolicy", + "mappings": [ + { + "source": "policy", + "target": "Policy" + }, + { + "source": "resourceArn", + "target": "ResourceArn" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ResourcePolicy", + "mappings": [ + { + "source": "resourceArn", + "target": "ResourceArn" + } + ], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::Rule", + "mappings": [ + { + "source": "action", + "target": "Action" + }, + { + "source": "listenerIdentifier", + "target": "ListenerIdentifier" + }, + { + "source": "match", + "target": "Match" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "priority", + "target": "Priority" + }, + { + "source": "serviceIdentifier", + "target": "ServiceIdentifier" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRule", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::Rule", + "mappings": [ + { + "source": "listenerIdentifier", + "target": "ListenerIdentifier" + }, + { + "source": "serviceIdentifier", + "target": "ServiceIdentifier" + } + ], + "operation": "DeleteRule", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::Service", + "mappings": [ + { + "source": "authType", + "target": "AuthType" + }, + { + "source": "certificateArn", + "target": "CertificateArn" + }, + { + "source": "customDomainName", + "target": "CustomDomainName" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateService", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::Service", + "mappings": [], + "operation": "DeleteService", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ServiceNetwork", + "mappings": [ + { + "source": "authType", + "target": "AuthType" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "sharingConfig", + "target": "SharingConfig" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateServiceNetwork", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ServiceNetwork", + "mappings": [], + "operation": "DeleteServiceNetwork", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ServiceNetworkResourceAssociation", + "mappings": [ + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateServiceNetworkResourceAssociation", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ServiceNetworkResourceAssociation", + "mappings": [], + "operation": "DeleteServiceNetworkResourceAssociation", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ServiceNetworkServiceAssociation", + "mappings": [ + { + "source": "serviceIdentifier", + "target": "ServiceIdentifier" + }, + { + "source": "serviceNetworkIdentifier", + "target": "ServiceNetworkIdentifier" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateServiceNetworkServiceAssociation", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ServiceNetworkServiceAssociation", + "mappings": [], + "operation": "DeleteServiceNetworkServiceAssociation", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ServiceNetworkVpcAssociation", + "mappings": [ + { + "source": "securityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "serviceNetworkIdentifier", + "target": "ServiceNetworkIdentifier" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "vpcIdentifier", + "target": "VpcIdentifier" + } + ], + "operation": "CreateServiceNetworkVpcAssociation", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::ServiceNetworkVpcAssociation", + "mappings": [], + "operation": "DeleteServiceNetworkVpcAssociation", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::TargetGroup", + "mappings": [ + { + "source": "config", + "target": "Config" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateTargetGroup", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::TargetGroup", + "mappings": [], + "operation": "DeleteTargetGroup", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::WAFv2::IPSet", + "mappings": [ + { + "source": "Addresses", + "target": "Addresses" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "IPAddressVersion", + "target": "IPAddressVersion" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Scope", + "target": "Scope" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateIPSet", + "phase": "create", + "service": "wafv2" + }, + { + "cfn_type": "AWS::WAFv2::IPSet", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Scope", + "target": "Scope" + } + ], + "operation": "DeleteIPSet", + "phase": "delete", + "service": "wafv2" + }, + { + "cfn_type": "AWS::WAFv2::LoggingConfiguration", + "mappings": [ + { + "source": "ResourceArn", + "target": "ResourceArn" + } + ], + "operation": "DeleteLoggingConfiguration", + "phase": "delete", + "service": "wafv2" + }, + { + "cfn_type": "AWS::WAFv2::RegexPatternSet", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RegularExpressionList", + "target": "RegularExpressionList" + }, + { + "source": "Scope", + "target": "Scope" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRegexPatternSet", + "phase": "create", + "service": "wafv2" + }, + { + "cfn_type": "AWS::WAFv2::RegexPatternSet", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Scope", + "target": "Scope" + } + ], + "operation": "DeleteRegexPatternSet", + "phase": "delete", + "service": "wafv2" + }, + { + "cfn_type": "AWS::WAFv2::RuleGroup", + "mappings": [ + { + "source": "Capacity", + "target": "Capacity" + }, + { + "source": "CustomResponseBodies", + "target": "CustomResponseBodies" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Rules", + "target": "Rules" + }, + { + "source": "Scope", + "target": "Scope" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "VisibilityConfig", + "target": "VisibilityConfig" + } + ], + "operation": "CreateRuleGroup", + "phase": "create", + "service": "wafv2" + }, + { + "cfn_type": "AWS::WAFv2::RuleGroup", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Scope", + "target": "Scope" + } + ], + "operation": "DeleteRuleGroup", + "phase": "delete", + "service": "wafv2" + }, + { + "cfn_type": "AWS::WAFv2::WebACL", + "mappings": [ + { + "source": "ApplicationConfig", + "target": "ApplicationConfig" + }, + { + "source": "AssociationConfig", + "target": "AssociationConfig" + }, + { + "source": "CaptchaConfig", + "target": "CaptchaConfig" + }, + { + "source": "ChallengeConfig", + "target": "ChallengeConfig" + }, + { + "source": "CustomResponseBodies", + "target": "CustomResponseBodies" + }, + { + "source": "DataProtectionConfig", + "target": "DataProtectionConfig" + }, + { + "source": "DefaultAction", + "target": "DefaultAction" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "OnSourceDDoSProtectionConfig", + "target": "OnSourceDDoSProtectionConfig" + }, + { + "source": "Rules", + "target": "Rules" + }, + { + "source": "Scope", + "target": "Scope" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TokenDomains", + "target": "TokenDomains" + }, + { + "source": "VisibilityConfig", + "target": "VisibilityConfig" + } + ], + "operation": "CreateWebACL", + "phase": "create", + "service": "wafv2" + }, + { + "cfn_type": "AWS::WAFv2::WebACL", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "Scope", + "target": "Scope" + } + ], + "operation": "DeleteWebACL", + "phase": "delete", + "service": "wafv2" + }, + { + "cfn_type": "AWS::WAFv2::WebACLAssociation", + "mappings": [ + { + "source": "ResourceArn", + "target": "ResourceArn" + }, + { + "source": "WebACLArn", + "target": "WebACLArn" + } + ], + "operation": "AssociateWebACL", + "phase": "create", + "service": "wafv2" + }, + { + "cfn_type": "AWS::WellArchitected::Lens", + "mappings": [ + { + "source": "JSONString", + "target": "JSONString" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "ImportLens", + "phase": "create", + "service": "wellarchitected" + }, + { + "cfn_type": "AWS::WellArchitected::Lens", + "mappings": [], + "operation": "DeleteLens", + "phase": "delete", + "service": "wellarchitected" + }, + { + "cfn_type": "AWS::WellArchitected::Profile", + "mappings": [ + { + "source": "ProfileDescription", + "target": "ProfileDescription" + }, + { + "source": "ProfileName", + "target": "ProfileName" + }, + { + "source": "ProfileQuestions", + "target": "ProfileQuestions" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateProfile", + "phase": "create", + "service": "wellarchitected" + }, + { + "cfn_type": "AWS::WellArchitected::Profile", + "mappings": [], + "operation": "DeleteProfile", + "phase": "delete", + "service": "wellarchitected" + }, + { + "cfn_type": "AWS::WellArchitected::ReviewTemplate", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Lenses", + "target": "Lenses" + }, + { + "source": "Notes", + "target": "Notes" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TemplateName", + "target": "TemplateName" + } + ], + "operation": "CreateReviewTemplate", + "phase": "create", + "service": "wellarchitected" + }, + { + "cfn_type": "AWS::WellArchitected::ReviewTemplate", + "mappings": [], + "operation": "DeleteReviewTemplate", + "phase": "delete", + "service": "wellarchitected" + }, + { + "cfn_type": "AWS::Wisdom::AIAgent", + "mappings": [ + { + "source": "assistantId", + "target": "AssistantId" + }, + { + "source": "configuration", + "target": "Configuration" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateAIAgent", + "phase": "create", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::AIAgent", + "mappings": [ + { + "source": "assistantId", + "target": "AssistantId" + } + ], + "operation": "DeleteAIAgent", + "phase": "delete", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::AIAgentVersion", + "mappings": [ + { + "source": "aiAgentId", + "target": "AIAgentId" + }, + { + "source": "assistantId", + "target": "AssistantId" + } + ], + "operation": "CreateAIAgentVersion", + "phase": "create", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::AIAgentVersion", + "mappings": [ + { + "source": "aiAgentId", + "target": "AIAgentId" + }, + { + "source": "assistantId", + "target": "AssistantId" + } + ], + "operation": "DeleteAIAgentVersion", + "phase": "delete", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::AIGuardrail", + "mappings": [ + { + "source": "assistantId", + "target": "AssistantId" + }, + { + "source": "blockedInputMessaging", + "target": "BlockedInputMessaging" + }, + { + "source": "blockedOutputsMessaging", + "target": "BlockedOutputsMessaging" + }, + { + "source": "contentPolicyConfig", + "target": "ContentPolicyConfig" + }, + { + "source": "contextualGroundingPolicyConfig", + "target": "ContextualGroundingPolicyConfig" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "sensitiveInformationPolicyConfig", + "target": "SensitiveInformationPolicyConfig" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "topicPolicyConfig", + "target": "TopicPolicyConfig" + }, + { + "source": "wordPolicyConfig", + "target": "WordPolicyConfig" + } + ], + "operation": "CreateAIGuardrail", + "phase": "create", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::AIGuardrail", + "mappings": [ + { + "source": "assistantId", + "target": "AssistantId" + } + ], + "operation": "DeleteAIGuardrail", + "phase": "delete", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::AIGuardrailVersion", + "mappings": [ + { + "source": "aiGuardrailId", + "target": "AIGuardrailId" + }, + { + "source": "assistantId", + "target": "AssistantId" + } + ], + "operation": "CreateAIGuardrailVersion", + "phase": "create", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::AIGuardrailVersion", + "mappings": [ + { + "source": "aiGuardrailId", + "target": "AIGuardrailId" + }, + { + "source": "assistantId", + "target": "AssistantId" + } + ], + "operation": "DeleteAIGuardrailVersion", + "phase": "delete", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::AIPrompt", + "mappings": [ + { + "source": "apiFormat", + "target": "ApiFormat" + }, + { + "source": "assistantId", + "target": "AssistantId" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "modelId", + "target": "ModelId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "templateConfiguration", + "target": "TemplateConfiguration" + }, + { + "source": "templateType", + "target": "TemplateType" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateAIPrompt", + "phase": "create", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::AIPrompt", + "mappings": [ + { + "source": "assistantId", + "target": "AssistantId" + } + ], + "operation": "DeleteAIPrompt", + "phase": "delete", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::AIPromptVersion", + "mappings": [ + { + "source": "aiPromptId", + "target": "AIPromptId" + }, + { + "source": "assistantId", + "target": "AssistantId" + } + ], + "operation": "CreateAIPromptVersion", + "phase": "create", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::AIPromptVersion", + "mappings": [ + { + "source": "aiPromptId", + "target": "AIPromptId" + }, + { + "source": "assistantId", + "target": "AssistantId" + } + ], + "operation": "DeleteAIPromptVersion", + "phase": "delete", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::Assistant", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "serverSideEncryptionConfiguration", + "target": "ServerSideEncryptionConfiguration" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateAssistant", + "phase": "create", + "service": "wisdom" + }, + { + "cfn_type": "AWS::Wisdom::Assistant", + "mappings": [], + "operation": "DeleteAssistant", + "phase": "delete", + "service": "wisdom" + }, + { + "cfn_type": "AWS::Wisdom::AssistantAssociation", + "mappings": [ + { + "source": "assistantId", + "target": "AssistantId" + }, + { + "source": "association", + "target": "Association" + }, + { + "source": "associationType", + "target": "AssociationType" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAssistantAssociation", + "phase": "create", + "service": "wisdom" + }, + { + "cfn_type": "AWS::Wisdom::AssistantAssociation", + "mappings": [ + { + "source": "assistantId", + "target": "AssistantId" + } + ], + "operation": "DeleteAssistantAssociation", + "phase": "delete", + "service": "wisdom" + }, + { + "cfn_type": "AWS::Wisdom::KnowledgeBase", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "knowledgeBaseType", + "target": "KnowledgeBaseType" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "renderingConfiguration", + "target": "RenderingConfiguration" + }, + { + "source": "serverSideEncryptionConfiguration", + "target": "ServerSideEncryptionConfiguration" + }, + { + "source": "sourceConfiguration", + "target": "SourceConfiguration" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateKnowledgeBase", + "phase": "create", + "service": "wisdom" + }, + { + "cfn_type": "AWS::Wisdom::KnowledgeBase", + "mappings": [], + "operation": "DeleteKnowledgeBase", + "phase": "delete", + "service": "wisdom" + }, + { + "cfn_type": "AWS::Wisdom::MessageTemplate", + "mappings": [ + { + "source": "channelSubtype", + "target": "ChannelSubtype" + }, + { + "source": "content", + "target": "Content" + }, + { + "source": "defaultAttributes", + "target": "DefaultAttributes" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "groupingConfiguration", + "target": "GroupingConfiguration" + }, + { + "source": "language", + "target": "Language" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateMessageTemplate", + "phase": "create", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::MessageTemplate", + "mappings": [], + "operation": "DeleteMessageTemplate", + "phase": "delete", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::MessageTemplateVersion", + "mappings": [ + { + "source": "messageTemplateContentSha256", + "target": "MessageTemplateContentSha256" + } + ], + "operation": "CreateMessageTemplateVersion", + "phase": "create", + "service": "qconnect" + }, + { + "cfn_type": "AWS::Wisdom::QuickResponse", + "mappings": [ + { + "source": "channels", + "target": "Channels" + }, + { + "source": "content", + "target": "Content" + }, + { + "source": "contentType", + "target": "ContentType" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "groupingConfiguration", + "target": "GroupingConfiguration" + }, + { + "source": "isActive", + "target": "IsActive" + }, + { + "source": "language", + "target": "Language" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "shortcutKey", + "target": "ShortcutKey" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateQuickResponse", + "phase": "create", + "service": "wisdom" + }, + { + "cfn_type": "AWS::Wisdom::QuickResponse", + "mappings": [], + "operation": "DeleteQuickResponse", + "phase": "delete", + "service": "wisdom" + }, + { + "cfn_type": "AWS::WorkSpaces::ConnectionAlias", + "mappings": [ + { + "source": "ConnectionString", + "target": "ConnectionString" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateConnectionAlias", + "phase": "create", + "service": "workspaces" + }, + { + "cfn_type": "AWS::WorkSpaces::ConnectionAlias", + "mappings": [], + "operation": "DeleteConnectionAlias", + "phase": "delete", + "service": "workspaces" + }, + { + "cfn_type": "AWS::WorkSpaces::Workspace", + "mappings": [], + "operation": "TerminateWorkspaces", + "phase": "delete", + "service": "workspaces" + }, + { + "cfn_type": "AWS::WorkSpaces::WorkspaceIpGroup", + "mappings": [ + { + "source": "GroupDesc", + "target": "GroupDesc" + }, + { + "source": "GroupName", + "target": "GroupName" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "UserRules", + "target": "UserRules" + } + ], + "operation": "CreateIpGroup", + "phase": "create", + "service": "workspaces" + }, + { + "cfn_type": "AWS::WorkSpaces::WorkspacesPool", + "mappings": [ + { + "source": "ApplicationSettings", + "target": "ApplicationSettings" + }, + { + "source": "BundleId", + "target": "BundleId" + }, + { + "source": "Capacity", + "target": "Capacity" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DirectoryId", + "target": "DirectoryId" + }, + { + "source": "PoolName", + "target": "PoolName" + }, + { + "source": "RunningMode", + "target": "RunningMode" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TimeoutSettings", + "target": "TimeoutSettings" + } + ], + "operation": "CreateWorkspacesPool", + "phase": "create", + "service": "workspaces" + }, + { + "cfn_type": "AWS::WorkSpaces::WorkspacesPool", + "mappings": [], + "operation": "TerminateWorkspacesPool", + "phase": "delete", + "service": "workspaces" + }, + { + "cfn_type": "AWS::WorkSpacesThinClient::Environment", + "mappings": [ + { + "source": "desiredSoftwareSetId", + "target": "DesiredSoftwareSetId" + }, + { + "source": "desktopArn", + "target": "DesktopArn" + }, + { + "source": "desktopEndpoint", + "target": "DesktopEndpoint" + }, + { + "source": "deviceCreationTags", + "target": "DeviceCreationTags" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "maintenanceWindow", + "target": "MaintenanceWindow" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "softwareSetUpdateMode", + "target": "SoftwareSetUpdateMode" + }, + { + "source": "softwareSetUpdateSchedule", + "target": "SoftwareSetUpdateSchedule" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateEnvironment", + "phase": "create", + "service": "workspaces-thin-client" + }, + { + "cfn_type": "AWS::WorkSpacesThinClient::Environment", + "mappings": [], + "operation": "DeleteEnvironment", + "phase": "delete", + "service": "workspaces-thin-client" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::BrowserSettings", + "mappings": [ + { + "source": "additionalEncryptionContext", + "target": "AdditionalEncryptionContext" + }, + { + "source": "browserPolicy", + "target": "BrowserPolicy" + }, + { + "source": "customerManagedKey", + "target": "CustomerManagedKey" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateBrowserSettings", + "phase": "create", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::BrowserSettings", + "mappings": [], + "operation": "DeleteBrowserSettings", + "phase": "delete", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::DataProtectionSettings", + "mappings": [ + { + "source": "additionalEncryptionContext", + "target": "AdditionalEncryptionContext" + }, + { + "source": "customerManagedKey", + "target": "CustomerManagedKey" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "inlineRedactionConfiguration", + "target": "InlineRedactionConfiguration" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDataProtectionSettings", + "phase": "create", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::DataProtectionSettings", + "mappings": [], + "operation": "DeleteDataProtectionSettings", + "phase": "delete", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::IdentityProvider", + "mappings": [ + { + "source": "identityProviderDetails", + "target": "IdentityProviderDetails" + }, + { + "source": "identityProviderName", + "target": "IdentityProviderName" + }, + { + "source": "identityProviderType", + "target": "IdentityProviderType" + }, + { + "source": "portalArn", + "target": "PortalArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateIdentityProvider", + "phase": "create", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::IdentityProvider", + "mappings": [], + "operation": "DeleteIdentityProvider", + "phase": "delete", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::IpAccessSettings", + "mappings": [ + { + "source": "additionalEncryptionContext", + "target": "AdditionalEncryptionContext" + }, + { + "source": "customerManagedKey", + "target": "CustomerManagedKey" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "ipRules", + "target": "IpRules" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateIpAccessSettings", + "phase": "create", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::IpAccessSettings", + "mappings": [], + "operation": "DeleteIpAccessSettings", + "phase": "delete", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::NetworkSettings", + "mappings": [ + { + "source": "securityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "subnetIds", + "target": "SubnetIds" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "vpcId", + "target": "VpcId" + } + ], + "operation": "CreateNetworkSettings", + "phase": "create", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::NetworkSettings", + "mappings": [], + "operation": "DeleteNetworkSettings", + "phase": "delete", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::Portal", + "mappings": [ + { + "source": "additionalEncryptionContext", + "target": "AdditionalEncryptionContext" + }, + { + "source": "authenticationType", + "target": "AuthenticationType" + }, + { + "source": "customerManagedKey", + "target": "CustomerManagedKey" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "instanceType", + "target": "InstanceType" + }, + { + "source": "maxConcurrentSessions", + "target": "MaxConcurrentSessions" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePortal", + "phase": "create", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::Portal", + "mappings": [], + "operation": "DeletePortal", + "phase": "delete", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::SessionLogger", + "mappings": [ + { + "source": "additionalEncryptionContext", + "target": "AdditionalEncryptionContext" + }, + { + "source": "customerManagedKey", + "target": "CustomerManagedKey" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "eventFilter", + "target": "EventFilter" + }, + { + "source": "logConfiguration", + "target": "LogConfiguration" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSessionLogger", + "phase": "create", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::SessionLogger", + "mappings": [], + "operation": "DeleteSessionLogger", + "phase": "delete", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::TrustStore", + "mappings": [ + { + "source": "certificateList", + "target": "CertificateList" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateTrustStore", + "phase": "create", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::TrustStore", + "mappings": [], + "operation": "DeleteTrustStore", + "phase": "delete", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::UserAccessLoggingSettings", + "mappings": [ + { + "source": "kinesisStreamArn", + "target": "KinesisStreamArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateUserAccessLoggingSettings", + "phase": "create", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::UserAccessLoggingSettings", + "mappings": [], + "operation": "DeleteUserAccessLoggingSettings", + "phase": "delete", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::UserSettings", + "mappings": [ + { + "source": "additionalEncryptionContext", + "target": "AdditionalEncryptionContext" + }, + { + "source": "cookieSynchronizationConfiguration", + "target": "CookieSynchronizationConfiguration" + }, + { + "source": "copyAllowed", + "target": "CopyAllowed" + }, + { + "source": "customerManagedKey", + "target": "CustomerManagedKey" + }, + { + "source": "deepLinkAllowed", + "target": "DeepLinkAllowed" + }, + { + "source": "disconnectTimeoutInMinutes", + "target": "DisconnectTimeoutInMinutes" + }, + { + "source": "downloadAllowed", + "target": "DownloadAllowed" + }, + { + "source": "idleDisconnectTimeoutInMinutes", + "target": "IdleDisconnectTimeoutInMinutes" + }, + { + "source": "pasteAllowed", + "target": "PasteAllowed" + }, + { + "source": "printAllowed", + "target": "PrintAllowed" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "toolbarConfiguration", + "target": "ToolbarConfiguration" + }, + { + "source": "uploadAllowed", + "target": "UploadAllowed" + } + ], + "operation": "CreateUserSettings", + "phase": "create", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkSpacesWeb::UserSettings", + "mappings": [], + "operation": "DeleteUserSettings", + "phase": "delete", + "service": "workspaces-web" + }, + { + "cfn_type": "AWS::WorkspacesInstances::Volume", + "mappings": [ + { + "source": "AvailabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "Encrypted", + "target": "Encrypted" + }, + { + "source": "Iops", + "target": "Iops" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "SizeInGB", + "target": "SizeInGB" + }, + { + "source": "SnapshotId", + "target": "SnapshotId" + }, + { + "source": "TagSpecifications", + "target": "TagSpecifications" + }, + { + "source": "Throughput", + "target": "Throughput" + }, + { + "source": "VolumeType", + "target": "VolumeType" + } + ], + "operation": "CreateVolume", + "phase": "create", + "service": "workspaces-instances" + }, + { + "cfn_type": "AWS::WorkspacesInstances::Volume", + "mappings": [], + "operation": "DeleteVolume", + "phase": "delete", + "service": "workspaces-instances" + }, + { + "cfn_type": "AWS::WorkspacesInstances::VolumeAssociation", + "mappings": [ + { + "source": "Device", + "target": "Device" + }, + { + "source": "VolumeId", + "target": "VolumeId" + }, + { + "source": "WorkspaceInstanceId", + "target": "WorkspaceInstanceId" + } + ], + "operation": "AssociateVolume", + "phase": "create", + "service": "workspaces-instances" + }, + { + "cfn_type": "AWS::WorkspacesInstances::VolumeAssociation", + "mappings": [ + { + "source": "Device", + "target": "Device" + }, + { + "source": "DisassociateMode", + "target": "DisassociateMode" + }, + { + "source": "VolumeId", + "target": "VolumeId" + }, + { + "source": "WorkspaceInstanceId", + "target": "WorkspaceInstanceId" + } + ], + "operation": "DisassociateVolume", + "phase": "delete", + "service": "workspaces-instances" + }, + { + "cfn_type": "AWS::WorkspacesInstances::WorkspaceInstance", + "mappings": [ + { + "source": "ManagedInstance", + "target": "ManagedInstance" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateWorkspaceInstance", + "phase": "create", + "service": "workspaces-instances" + }, + { + "cfn_type": "AWS::WorkspacesInstances::WorkspaceInstance", + "mappings": [], + "operation": "DeleteWorkspaceInstance", + "phase": "delete", + "service": "workspaces-instances" + }, + { + "cfn_type": "AWS::XRay::Group", + "mappings": [ + { + "source": "FilterExpression", + "target": "FilterExpression" + }, + { + "source": "GroupName", + "target": "GroupName" + }, + { + "source": "InsightsConfiguration", + "target": "InsightsConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateGroup", + "phase": "create", + "service": "xray" + }, + { + "cfn_type": "AWS::XRay::Group", + "mappings": [ + { + "source": "GroupName", + "target": "GroupName" + } + ], + "operation": "DeleteGroup", + "phase": "delete", + "service": "xray" + }, + { + "cfn_type": "AWS::XRay::ResourcePolicy", + "mappings": [ + { + "source": "BypassPolicyLockoutCheck", + "target": "BypassPolicyLockoutCheck" + }, + { + "source": "PolicyDocument", + "target": "PolicyDocument" + }, + { + "source": "PolicyName", + "target": "PolicyName" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "xray" + }, + { + "cfn_type": "AWS::XRay::ResourcePolicy", + "mappings": [ + { + "source": "PolicyName", + "target": "PolicyName" + } + ], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "xray" + }, + { + "cfn_type": "AWS::XRay::SamplingRule", + "mappings": [ + { + "source": "SamplingRule", + "target": "SamplingRule" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateSamplingRule", + "phase": "create", + "service": "xray" + }, + { + "cfn_type": "AWS::XRay::SamplingRule", + "mappings": [ + { + "source": "RuleName", + "target": "RuleName" + } + ], + "operation": "DeleteSamplingRule", + "phase": "delete", + "service": "xray" + } + ], + "format_version": 1 +} diff --git a/src/data-source/scripts/generate_aws_api_catalog.py b/src/data-source/scripts/generate_aws_api_catalog.py new file mode 100644 index 00000000..f157a4a1 --- /dev/null +++ b/src/data-source/scripts/generate_aws_api_catalog.py @@ -0,0 +1,673 @@ +#!/usr/bin/env python3 +"""Generate the AWS API operation adapter catalog for validation-engine. + +Derives CloudFormation-type -> API-operation adapters from two public sources: + +1. CloudFormation resource provider schemas WITH handler metadata + (https://github.com/aws-cloudformation/resource-provider-enhanced-schemas + releases, ``schemas-standard.zip``). Each type's own create/delete handler + permissions contain the type's canonical lifecycle API actions. +2. Botocore service models (importable ``botocore``), which resolve IAM action + prefixes to real services and operations and provide exact input shapes. + +Derivation direction is type -> operation, scoped to one type's own handler +permissions at a time. The global inverse (operation -> type by name) is +unsafe and is never used. Every candidate must pass all of: + +- service identity tiering: the action must belong to the type's own service + (botocore service name match beats IAM-prefix match beats signing-identity + match beats substring; lower tiers are dropped when a higher tier exists) +- lifecycle verb family match for the handler role +- structural verification: operation input members must map onto writable + properties of the type in the validator's OWN compiled schemas (verbatim, + or via the reviewed identifier-rename rules below) +- noun agreement or property-overlap thresholds; ties are dropped entirely +- global reverse uniqueness: one (service, operation) key maps to exactly one + catalog entry; unresolvable collisions are dropped entirely + +Types or operations that fail any gate are omitted: an uncovered operation is +validated as SKIPPED at runtime, never guessed. + +Usage: + PYTHONPATH= python3 generate_aws_api_catalog.py \ + --provider-schemas schemas-standard.zip \ + --compiled-schemas ../generated/schema-validator/compiled_schemas.json \ + --output ../generated/data/aws_api_operation_catalog.json +""" + +import argparse +import hashlib +import json +import sys +import zipfile +from collections import defaultdict +from pathlib import Path + +import botocore +import botocore.session + +FORMAT_VERSION = 1 + +# Multiple provider types can list the same underlying operation. Keep a +# collision only when the API action itself names one uniquely correct type; +# every unreviewed or representation-version collision is dropped. +COLLISION_PREFERENCES = { + ('dynamodb', 'CreateTable'): 'AWS::DynamoDB::Table', + ('ec2', 'CreateTransitGatewayVpcAttachment'): + 'AWS::EC2::TransitGatewayVpcAttachment', + ('eks', 'CreateAccessEntry'): 'AWS::EKS::AccessEntry', +} + +CREATE_VERBS = ( + 'create', 'put', 'register', 'add', 'allocate', 'provision', 'launch', + 'run', 'import', 'request', 'publish', 'set', 'establish', + 'associate', 'attach', 'enable', 'deploy', 'subscribe', 'purchase', + 'copy', 'initialize', 'define', 'build', 'issue', 'schedule', 'submit', + 'grant', 'start', +) +DELETE_VERBS = ( + 'delete', 'remove', 'deregister', 'release', 'terminate', 'cancel', + 'disassociate', 'detach', 'revoke', 'deprovision', 'destroy', + 'unsubscribe', 'purge', +) + +# CFN service segments whose IAM/service identity differs beyond casing. +SEGMENT_ALIASES = { + 'msk': 'kafka', + 'opensearchservice': 'es', + 'certificatemanager': 'acm', + 'elasticloadbalancingv2': 'elasticloadbalancing', + 'ses': 'sesv2', +} + +# Services handled by dedicated validation paths; adapters must not shadow them. +EXCLUDED_SERVICES = frozenset({'cloudformation', 'cloudcontrol'}) + +# Hand-reviewed update adapters. Update APIs carry partial state, so update +# entries are curated rather than derived; each is verified like derived ones. +CURATED_UPDATE_ADAPTERS = [ + { + 'cfn_type': 'AWS::Lambda::Function', + 'service': 'lambda', + 'operation': 'UpdateFunctionConfiguration', + 'phase': 'update', + 'mappings': [ + {'source': 'Runtime', 'target': 'Runtime'}, + {'source': 'Role', 'target': 'Role'}, + {'source': 'Handler', 'target': 'Handler'}, + {'source': 'Description', 'target': 'Description'}, + {'source': 'Timeout', 'target': 'Timeout'}, + {'source': 'MemorySize', 'target': 'MemorySize'}, + ], + }, +] + +# Operations that mutate runtime state without representing desired-state +# creation. The generator fails if a derivation ever selects one of these. +FORBIDDEN_OPERATIONS = frozenset({ + ('ecs', 'RunTask'), + ('ec2', 'StartInstances'), + ('ec2', 'StopInstances'), + ('ec2', 'RebootInstances'), + ('iot', 'StartThingRegistrationTask'), + ('lambda', 'Invoke'), + ('sns', 'Publish'), + ('sqs', 'SendMessage'), + ('s3', 'PutObject'), + ('dynamodb', 'PutItem'), + ('logs', 'StartQuery'), + ('acm', 'RemoveTagsFromCertificate'), + ('robomaker', 'DeregisterRobot'), + ('quicksight', 'CreateTopic'), + ('quicksight', 'DeleteTopic'), +}) + +# Known-good pairs the derivation must reproduce exactly; guards regressions +# in the derivation rules themselves. +EXPECTED_PAIRS = { + 'AWS::S3::Bucket': ('s3', 'CreateBucket'), + 'AWS::DynamoDB::Table': ('dynamodb', 'CreateTable'), + 'AWS::IAM::Role': ('iam', 'CreateRole'), + 'AWS::Lambda::Function': ('lambda', 'CreateFunction'), + 'AWS::SNS::Topic': ('sns', 'CreateTopic'), + 'AWS::SQS::Queue': ('sqs', 'CreateQueue'), + 'AWS::EC2::Instance': ('ec2', 'RunInstances'), + 'AWS::EC2::VPC': ('ec2', 'CreateVpc'), + 'AWS::KMS::Key': ('kms', 'CreateKey'), + 'AWS::Logs::LogGroup': ('logs', 'CreateLogGroup'), + 'AWS::CloudWatch::Alarm': ('cloudwatch', 'PutMetricAlarm'), + 'AWS::StepFunctions::StateMachine': ('stepfunctions', 'CreateStateMachine'), + 'AWS::Kinesis::Stream': ('kinesis', 'CreateStream'), + 'AWS::SecretsManager::Secret': ('secretsmanager', 'CreateSecret'), + 'AWS::ElasticLoadBalancingV2::LoadBalancer': ('elbv2', 'CreateLoadBalancer'), +} + + +def _parse_args(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--provider-schemas', required=True, type=Path) + parser.add_argument('--compiled-schemas', required=True, type=Path) + parser.add_argument('--output', required=True, type=Path) + return parser.parse_args() + + +def _normalize(value): + return ''.join(c for c in value.lower() if c.isalnum()) + + +class BotocoreIndex: + """Resolves IAM action prefixes to concrete botocore operations.""" + + def __init__(self): + self._session = botocore.session.Session() + self._identities = {} + self._operations = {} + self._by_identity = defaultdict(set) + for service in self._session.get_available_services(): + model = self._session.get_service_model(service) + identities = { + _normalize(value) + for value in ( + service, + model.endpoint_prefix or '', + model.signing_name or '', + str(getattr(model, 'service_id', '') or ''), + ) + if value + } + self._identities[service] = identities + for identity in identities: + self._by_identity[identity].add(service) + self._operations[service] = { + op.lower(): op for op in model.operation_names + } + + @property + def service_count(self): + return len(self._operations) + + def input_members(self, service, operation): + model = self._session.get_service_model(service) + shape = model.operation_model(operation).input_shape + return dict(shape.members) if shape else {} + + def resolve(self, action_prefix, action_name): + """Every (service, operation) the action can denote.""" + resolved = set() + for service in self._by_identity.get(action_prefix, ()): + operation = self._operations[service].get(action_name.lower()) + if operation: + resolved.add((service, operation)) + if resolved: + return resolved + for identity, services in self._by_identity.items(): + if action_prefix in identity or identity in action_prefix: + for service in services: + operation = self._operations[service].get( + action_name.lower() + ) + if operation: + resolved.add((service, operation)) + return resolved + + def identity_tier(self, action_prefix, service, segment_aliases): + """Lower is a stronger identity match; None means unrelated.""" + if _normalize(service) in segment_aliases: + return 0 + if action_prefix in segment_aliases: + return 1 + if self._identities[service] & segment_aliases: + return 2 + if any( + action_prefix in alias or alias in action_prefix + for alias in segment_aliases + ): + return 3 + return None + + +def _verb_rank(operation, verbs): + lowered = operation.lower() + for index, verb in enumerate(verbs): + if lowered.startswith(verb): + return index + return None + + +def _noun_matches(operation, resource_segment): + normalized = _normalize(operation) + if normalized.endswith(resource_segment): + return True + if normalized.endswith(resource_segment + 's'): + return True + if resource_segment.endswith('y') and normalized.endswith( + resource_segment[:-1] + 'ies' + ): + return True + return False + + +def _source_sha256(source_path): + digest = hashlib.sha256() + if source_path.is_file(): + digest.update(source_path.read_bytes()) + return digest.hexdigest() + for path in sorted(source_path.rglob('*.json')): + digest.update(path.relative_to(source_path).as_posix().encode()) + digest.update(b'\0') + digest.update(path.read_bytes()) + return digest.hexdigest() + + +def _load_provider_schemas(source_path): + schemas = {} + if source_path.is_dir(): + documents = ( + (path.as_posix(), path.read_bytes()) + for path in sorted(source_path.rglob('*.json')) + ) + else: + archive = zipfile.ZipFile(source_path) + documents = ( + (name, archive.read(name)) + for name in sorted(archive.namelist()) + if name.endswith('.json') + ) + try: + for _, contents in documents: + try: + schema = json.loads(contents) + except (json.JSONDecodeError, UnicodeDecodeError): + continue + type_name = schema.get('typeName') if isinstance(schema, dict) else None + if type_name and type_name.startswith('AWS::'): + schemas[type_name] = schema + finally: + if not source_path.is_dir(): + archive.close() + return schemas + + +def _compiled_constraints(compiled_schemas, type_name): + schema = compiled_schemas.get(type_name) + if not isinstance(schema, dict): + return None + property_schemas = schema.get('properties') or {} + read_only = set(schema.get('read_only_properties') or []) + primary = set(schema.get('primary_identifier') or []) + definitions = schema.get('definitions') or {} + return property_schemas, read_only, primary, definitions + + +def _resolve_schema_node(node, definitions, seen=frozenset()): + if not isinstance(node, dict): + return {} + reference = node.get('ref_name') + if reference and reference not in seen: + return _resolve_schema_node( + definitions.get(reference), definitions, seen | {reference} + ) + return node + + +def _schema_types(node, definitions): + node = _resolve_schema_node(node, definitions) + schema_type = node.get('type') + if isinstance(schema_type, str): + types = {schema_type} + elif isinstance(schema_type, list): + types = {value for value in schema_type if isinstance(value, str)} + else: + types = set() + for alternatives in ('any_of', 'one_of'): + for alternative in node.get(alternatives) or []: + types.update(_schema_types(alternative, definitions)) + return types + + +def _schema_node_for_type(node, definitions, expected_type): + node = _resolve_schema_node(node, definitions) + if expected_type in _schema_types(node, definitions): + if expected_type in _schema_types( + {key: value for key, value in node.items() + if key not in ('any_of', 'one_of')}, definitions + ): + return node + for alternatives in ('any_of', 'one_of'): + for alternative in node.get(alternatives) or []: + selected = _schema_node_for_type( + alternative, definitions, expected_type + ) + if selected: + return selected + return None + + +def _is_key_value_tag_array(target_schema, definitions): + array_schema = _schema_node_for_type( + target_schema, definitions, 'array' + ) + if not array_schema: + return False + item_schema = _resolve_schema_node( + array_schema.get('items') or {}, definitions + ) + alternatives = [item_schema] + for key in ('any_of', 'one_of'): + alternatives.extend( + _resolve_schema_node(option, definitions) + for option in item_schema.get(key) or [] + ) + return any( + {'Key', 'Value'} <= set(option.get('properties') or {}) + for option in alternatives + ) + + +def _is_runtime_safe_mapping(source_shape, target_schema, definitions, target): + source_type = source_shape.type_name + target_types = _schema_types(target_schema, definitions) + if source_type in ('string', 'boolean'): + return source_type in target_types + if source_type in ('integer', 'long'): + return bool({'integer', 'number'} & target_types) + if source_type in ('float', 'double'): + return 'number' in target_types + if source_type == 'list' and source_shape.member.type_name in ( + 'string', 'boolean', 'integer', 'long', 'float', 'double' + ): + array_schema = _schema_node_for_type( + target_schema, definitions, 'array' + ) + return bool( + array_schema + and _is_runtime_safe_mapping( + source_shape.member, + array_schema.get('items') or {}, + definitions, + target, + ) + ) + if source_type == 'map' and target == 'Tags': + return ( + source_shape.value.type_name == 'string' + and _is_key_value_tag_array(target_schema, definitions) + ) + return False + + +def _property_mappings( + members, property_schemas, writable_by_lower, resource_segment, definitions +): + """Return mappings the runtime can serialize without nested rewriting.""" + mappings = [] + for member in sorted(members): + lowered = member.lower() + target = None + if lowered in writable_by_lower: + target = writable_by_lower[lowered] + elif lowered + 'name' in writable_by_lower: + target = writable_by_lower[lowered + 'name'] + elif lowered == 'name' and resource_segment + 'name' in writable_by_lower: + target = writable_by_lower[resource_segment + 'name'] + if target and _is_runtime_safe_mapping( + members[member], property_schemas[target], definitions, target + ): + mappings.append((member, target)) + return mappings + + +def _derive_role(role, verbs, provider_schemas, compiled_schemas, index, require_mappings): + adapters = {} + counters = defaultdict(int) + for type_name, schema in sorted(provider_schemas.items()): + constraints = _compiled_constraints(compiled_schemas, type_name) + if constraints is None: + counters['type_not_compiled'] += 1 + continue + property_schemas, read_only, primary, definitions = constraints + handlers = schema.get('handlers') + handler = handlers.get(role) if isinstance(handlers, dict) else None + if not isinstance(handler, dict): + counters['no_handler'] += 1 + continue + _, service_segment, resource_segment = type_name.split('::', 2) + service_segment = _normalize(service_segment) + resource_segment = _normalize(resource_segment) + if service_segment in EXCLUDED_SERVICES: + counters['excluded_service'] += 1 + continue + segment_aliases = {service_segment} + if service_segment in SEGMENT_ALIASES: + segment_aliases.add(SEGMENT_ALIASES[service_segment]) + candidates = set() + has_unavailable_exact_lifecycle_operation = False + for action in handler.get('permissions') or []: + if not isinstance(action, str) or ':' not in action: + continue + prefix, action_name = action.split(':', 1) + rank = _verb_rank(action_name, verbs) + if rank is None: + continue + prefix = _normalize(prefix) + resolved_actions = index.resolve(prefix, action_name) + related_actions = { + (service, operation) + for service, operation in resolved_actions + if index.identity_tier(prefix, service, segment_aliases) + is not None + } + if ( + prefix in segment_aliases + and _noun_matches(action_name, resource_segment) + and not related_actions + ): + has_unavailable_exact_lifecycle_operation = True + for service, operation in related_actions: + if ( + _normalize(service) in EXCLUDED_SERVICES + or (service, operation) in FORBIDDEN_OPERATIONS + ): + continue + tier = index.identity_tier(prefix, service, segment_aliases) + candidates.add((tier, rank, service, operation)) + if not candidates: + counters['no_candidates'] += 1 + continue + best_tier = min(candidate[0] for candidate in candidates) + candidates = {c for c in candidates if c[0] == best_tier} + writable_by_lower = { + p.lower(): p for p in set(property_schemas) - read_only + } + scored = [] + for _, rank, service, operation in candidates: + members = index.input_members(service, operation) + mappings = _property_mappings( + members, property_schemas, writable_by_lower, + resource_segment, definitions + ) + precision = len(mappings) / len(members) if members else 0.0 + noun = _noun_matches(operation, resource_segment) + scored.append(( + 0 if noun else 1, + rank, + -len(mappings), + -precision, + service, + operation, + mappings, + noun, + )) + scored.sort() + top = scored[0] + noun, mappings = top[7], top[6] + precision = -top[3] + accepted = (noun and (mappings or not require_mappings)) or ( + len(mappings) >= 2 and precision >= 0.3 + ) + if has_unavailable_exact_lifecycle_operation and not noun: + accepted = False + counters['stale_model_rejected'] += 1 + if not accepted: + counters['rejected'] += 1 + continue + tied = [ + entry + for entry in scored[1:] + if entry[0] == top[0] + and entry[1] == top[1] + and entry[2] == top[2] + and abs(entry[3] - top[3]) < 1e-9 + and (entry[4], entry[5]) != (top[4], top[5]) + ] + if tied: + counters['tied_rejected'] += 1 + continue + adapters[type_name] = { + 'cfn_type': type_name, + 'service': top[4], + 'operation': top[5], + 'phase': role, + 'mappings': [ + {'source': source, 'target': target} + for source, target in mappings + ], + 'noun_matched': noun, + } + counters['verified'] += 1 + return adapters, counters + + +def _enforce_global_uniqueness(adapters): + """One (service, operation) key -> exactly one adapter, or none at all.""" + by_key = defaultdict(list) + for adapter in adapters: + by_key[(adapter['service'].lower(), adapter['operation'])].append(adapter) + kept, dropped = [], [] + for _, group in sorted(by_key.items()): + if len(group) == 1: + kept.append(group[0]) + continue + key = (group[0]['service'].lower(), group[0]['operation']) + preferred_type = COLLISION_PREFERENCES.get(key) + preferred = [ + adapter for adapter in group + if adapter['cfn_type'] == preferred_type + ] + if len(preferred) == 1: + kept.append(preferred[0]) + dropped.extend( + adapter for adapter in group if adapter is not preferred[0] + ) + else: + dropped.extend(group) + return kept, dropped + + +def _verify_curated_updates(compiled_schemas, index): + for adapter in CURATED_UPDATE_ADAPTERS: + constraints = _compiled_constraints(compiled_schemas, adapter['cfn_type']) + if constraints is None: + raise SystemExit( + f"curated update adapter references unknown type {adapter['cfn_type']}" + ) + property_schemas, read_only, primary, definitions = constraints + members = index.input_members(adapter['service'], adapter['operation']) + for mapping in adapter['mappings']: + if mapping['source'] not in members: + raise SystemExit( + f"curated mapping source {mapping['source']} is not an input of " + f"{adapter['service']}:{adapter['operation']}" + ) + target = mapping['target'] + if target not in property_schemas or target in read_only or target in primary: + raise SystemExit( + f"curated mapping target {target} is invalid for {adapter['cfn_type']}" + ) + if not _is_runtime_safe_mapping( + members[mapping['source']], property_schemas[target], + definitions, target + ): + raise SystemExit( + f"curated mapping {mapping['source']} -> {target} is not " + "runtime shape-compatible" + ) + + +def main(): + args = _parse_args() + compiled_schemas = json.loads(args.compiled_schemas.read_text()) + provider_schemas = _load_provider_schemas(args.provider_schemas) + index = BotocoreIndex() + + creates, create_counters = _derive_role( + 'create', CREATE_VERBS, provider_schemas, compiled_schemas, index, True + ) + deletes, delete_counters = _derive_role( + 'delete', DELETE_VERBS, provider_schemas, compiled_schemas, index, False + ) + _verify_curated_updates(compiled_schemas, index) + + all_adapters = ( + list(creates.values()) + + list(deletes.values()) + + [dict(adapter) for adapter in CURATED_UPDATE_ADAPTERS] + ) + unique_adapters, dropped = _enforce_global_uniqueness(all_adapters) + + for adapter in unique_adapters: + key = (adapter['service'], adapter['operation']) + if key in FORBIDDEN_OPERATIONS: + raise SystemExit(f'forbidden operation selected: {key} for {adapter["cfn_type"]}') + + final_creates = { + a['cfn_type']: a for a in unique_adapters if a['phase'] == 'create' + } + for type_name, expected in sorted(EXPECTED_PAIRS.items()): + actual = final_creates.get(type_name) + if actual is None: + raise SystemExit(f'expected pair missing after uniqueness: {type_name}') + if (actual['service'], actual['operation']) != expected: + raise SystemExit( + f'expected pair mismatch for {type_name}: ' + f"got {(actual['service'], actual['operation'])}, want {expected}" + ) + + for adapter in unique_adapters: + adapter.pop('noun_matched', None) + unique_adapters.sort(key=lambda a: (a['cfn_type'], a['phase'])) + document = { + 'format_version': FORMAT_VERSION, + 'source': { + 'provider_schemas_sha256': _source_sha256( + args.provider_schemas + ), + 'compiled_schemas_sha256': _source_sha256( + args.compiled_schemas + ), + 'botocore_version': botocore.__version__, + 'botocore_service_count': index.service_count, + 'provider_type_count': len(provider_schemas), + 'compiled_type_count': len(compiled_schemas), + }, + 'adapters': unique_adapters, + } + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(json.dumps(document, indent=1, sort_keys=True) + '\n') + + phases = defaultdict(int) + for adapter in unique_adapters: + phases[adapter['phase']] += 1 + print(f'create derivation: {dict(create_counters)}') + print(f'delete derivation: {dict(delete_counters)}') + print(f'uniqueness dropped: {len(dropped)}') + print( + f"catalog: {len(unique_adapters)} adapters " + f"({phases['create']} create, {phases['update']} update, " + f"{phases['delete']} delete) -> {args.output}" + ) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/data-source/scripts/test_generate_aws_api_catalog.py b/src/data-source/scripts/test_generate_aws_api_catalog.py new file mode 100644 index 00000000..7e67d801 --- /dev/null +++ b/src/data-source/scripts/test_generate_aws_api_catalog.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +import json +import tempfile +import unittest +from pathlib import Path + +import generate_aws_api_catalog as catalog + + +class Shape: + def __init__(self, type_name, *, member=None, value=None): + self.type_name = type_name + self.member = member + self.value = value + + +class CatalogGeneratorTest(unittest.TestCase): + def test_unreviewed_collision_is_dropped(self): + adapters = [ + { + "service": "quicksight", + "operation": "CreateTopic", + "cfn_type": "AWS::QuickSight::Topic", + }, + { + "service": "quicksight", + "operation": "CreateTopic", + "cfn_type": "AWS::QuickSight::TopicV2", + }, + ] + + kept, dropped = catalog._enforce_global_uniqueness(adapters) + + self.assertEqual([], kept) + self.assertEqual(2, len(dropped)) + + def test_reviewed_collision_keeps_only_preferred_type(self): + adapters = [ + { + "service": "dynamodb", + "operation": "CreateTable", + "cfn_type": "AWS::DynamoDB::GlobalTable", + }, + { + "service": "dynamodb", + "operation": "CreateTable", + "cfn_type": "AWS::DynamoDB::Table", + }, + ] + + kept, dropped = catalog._enforce_global_uniqueness(adapters) + + self.assertEqual(["AWS::DynamoDB::Table"], [entry["cfn_type"] for entry in kept]) + self.assertEqual(["AWS::DynamoDB::GlobalTable"], [entry["cfn_type"] for entry in dropped]) + + def test_runtime_safe_mapping_accepts_only_serializable_shapes(self): + definitions = { + "Tag": { + "type": "object", + "properties": {"Key": {"type": "string"}, "Value": {"type": "string"}}, + } + } + tags = {"type": "array", "items": {"ref_name": "Tag"}} + strings = {"type": "array", "items": {"type": "string"}} + + self.assertTrue( + catalog._is_runtime_safe_mapping(Shape("string"), {"type": "string"}, {}, "Name") + ) + self.assertTrue( + catalog._is_runtime_safe_mapping( + Shape("list", member=Shape("string")), strings, {}, "Names" + ) + ) + self.assertTrue( + catalog._is_runtime_safe_mapping( + Shape("map", value=Shape("string")), tags, definitions, "Tags" + ) + ) + self.assertFalse( + catalog._is_runtime_safe_mapping(Shape("structure"), {"type": "object"}, {}, "Config") + ) + self.assertFalse( + catalog._is_runtime_safe_mapping( + Shape("list", member=Shape("structure")), + {"type": "array", "items": {"type": "object"}}, + {}, + "Configs", + ) + ) + + def test_provider_schema_directory_is_loaded_deterministically(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + (root / "b.json").write_text(json.dumps({"typeName": "AWS::Test::B"})) + (root / "a.json").write_text(json.dumps({"typeName": "AWS::Test::A"})) + (root / "ignored.json").write_text(json.dumps({"notTypeName": "AWS::Test::Ignored"})) + + schemas = catalog._load_provider_schemas(root) + first_hash = catalog._source_sha256(root) + second_hash = catalog._source_sha256(root) + + self.assertEqual(["AWS::Test::A", "AWS::Test::B"], sorted(schemas)) + self.assertEqual(first_hash, second_hash) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/validation-engine/API.md b/src/validation-engine/API.md index ab3599c6..57143aff 100644 --- a/src/validation-engine/API.md +++ b/src/validation-engine/API.md @@ -36,8 +36,8 @@ On parse failure, `validate_bytes_with_path` returns `Ok(report)` with a synthet ## Validating an AWS API Request `validate_aws_api_request` accepts raw service, operation, HTTP, trait, and request-parameter context. It owns operation -classification, CloudFormation resource-type selection, request-to-template modeling, schema-backed property mapping, -and partial-update diagnostic scoping: +classification, deterministic CloudFormation resource-type selection, request-to-template modeling, schema-backed property +mapping, and diagnostic scoping to explicitly modeled properties: ```rust use rego_engine::RegoEngine; @@ -53,11 +53,6 @@ let request = AwsApiRequest::new( "CreateBucket", [ ("Bucket".into(), AwsApiValue::String { value: "example-bucket".into() }), - ("Tags".into(), AwsApiValue::Object { - entries: [("Team".into(), AwsApiValue::String { value: "Platform".into() })] - .into_iter() - .collect(), - }), ], ) .with_service_prefix("s3") @@ -84,6 +79,22 @@ an operation kind, validation status, optional template source, resource candida modeled template reached the normal validation pipeline; `Skipped` has no report and explains why. Use `validate_aws_api_request_with_path` when the embedding application needs a custom report path. +**Deterministic closed-adapter contract.** Operation-to-resource mapping uses a generated adapter catalog keyed by +case-normalized canonical `service_name` and exact operation name. The catalog is produced by +`data-source/scripts/generate_aws_api_catalog.py` from each resource type's own provider handler metadata, resolved +against botocore service models and structurally verified against the compiled CloudFormation schemas; it covers +create and delete lifecycles for roughly seventy percent of all resource types plus curated update entries. Each +adapter declares one CloudFormation resource type with explicit request-parameter-to-property pairs. Unregistered +operations never receive an *inferred* resource type and are classified as `UnmappedMutation` (or +`DataPlaneMutation` for data-plane verbs) with `Skipped` status. +Cloud Control `UpdateResource` and `DeleteResource` may report a known `TypeName` supplied explicitly by the request, +but they never synthesize state. There is no fuzzy inference, substring matching, or generic property-name guessing. +`TemplateBody` validation is restricted to the closed set of CloudFormation operations that accept it; +`TypeName`+`DesiredState` wrapping is restricted to exact Cloud Control `CreateResource`. `service_name` is the +authoritative mapping identity; the optional signing `service_prefix` cannot override it. Case normalization supports +both CLI names (for example, `s3`) and Java SDK `SERVICE_NAME` values (for example, `S3`) without punctuation or +substring aliases. + ## Constructing an Engine Both engines take a single `EngineConfig` and return `anyhow::Result`: diff --git a/src/validation-engine/src/aws_api.rs b/src/validation-engine/src/aws_api.rs index 88151a83..c97b33f8 100644 --- a/src/validation-engine/src/aws_api.rs +++ b/src/validation-engine/src/aws_api.rs @@ -3,6 +3,7 @@ use rules::Severity; use schema_validator::{PropertyValueType, ResourceSchemaMetadata, SchemaValidator}; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::sync::LazyLock; use crate::{ValidateConfig, ValidationEngine, ValidationError, validate_bytes_with_path}; @@ -143,10 +144,6 @@ impl AwsApiRequestContext { self } - fn effective_service_prefix(&self) -> &str { - self.service_prefix.as_deref().filter(|prefix| !prefix.is_empty()).unwrap_or(&self.service_name) - } - fn default_file_path(&self) -> String { format!("aws-api://{}/{}", self.service_name, self.operation_name) } @@ -266,7 +263,7 @@ pub fn validate_aws_api_request_with_path( config: ValidateConfig, file_path: String, ) -> Result { - let classification = classify_operation(request, schema_validator); + let classification = classify_operation(request, schema_validator)?; let synthesis = synthesize_request(request, &classification, schema_validator)?; let Some(template) = synthesis.template else { return Ok(AwsApiRequestValidation { @@ -281,7 +278,7 @@ pub fn validate_aws_api_request_with_path( let mut report = validate_bytes_with_path(engine, schema_validator, &template, config, file_path)?; if let Some(properties) = synthesis.diagnostic_properties.as_ref() { - scope_partial_update_report(&mut report, properties); + scope_synthesized_report(&mut report, properties); } Ok(AwsApiRequestValidation { operation_kind: classification.kind, @@ -292,7 +289,133 @@ pub fn validate_aws_api_request_with_path( report: Some(report), }) } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +enum AdapterPhase { + Create, + Update, + Delete, +} + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +struct PropertyMapping { + source: String, + target: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +struct OperationAdapter { + service: String, + operation: String, + phase: AdapterPhase, + cfn_type: String, + mappings: Vec, +} + +#[derive(Debug, Deserialize)] +struct OperationCatalog { + format_version: u32, + adapters: Vec, +} + +// These entries exposed dependent or ambiguous provider permissions in an +// older generated artifact. Filtering them here keeps shipped catalogs +// conservative while the maintenance pipeline catches up. +const REJECTED_CATALOG_OPERATIONS: &[(&str, &str)] = &[ + ("acm", "RemoveTagsFromCertificate"), + ("logs", "StartQuery"), + ("quicksight", "CreateTopic"), + ("quicksight", "DeleteTopic"), + ("robomaker", "DeregisterRobot"), +]; + +fn is_rejected_catalog_operation(service: &str, operation: &str) -> bool { + REJECTED_CATALOG_OPERATIONS.iter().any(|(candidate_service, candidate_operation)| { + *candidate_service == service && *candidate_operation == operation + }) +} + +/// Generated by `data-source/scripts/generate_aws_api_catalog.py`: each entry is +/// derived from the resource type's own provider handler metadata, resolved +/// against botocore service models, and structurally verified against the +/// compiled CloudFormation schemas. Only exact service+operation keys resolve; +/// unregistered operations stay unmapped. +static ADAPTER_REGISTRY: LazyLock, String>> = + LazyLock::new(|| parse_adapter_registry(&data_source::embedded::AWS_API_OPERATION_CATALOG_BYTES)); + +fn parse_adapter_registry(bytes: &[u8]) -> Result, String> { + let catalog: OperationCatalog = serde_json::from_slice(bytes) + .map_err(|error| format!("embedded AWS API operation catalog is invalid: {error}"))?; + if catalog.format_version != 1 { + return Err(format!("unsupported AWS API operation catalog format {}", catalog.format_version)); + } + let mut registry = HashMap::new(); + for adapter in catalog.adapters { + if adapter.service.trim().is_empty() + || adapter.operation.trim().is_empty() + || adapter.cfn_type.trim().is_empty() + { + return Err("AWS API operation catalog identities must not be blank".into()); + } + let key = (normalize_service(&adapter.service), adapter.operation.clone()); + if is_rejected_catalog_operation(&key.0, &key.1) { + continue; + } + if let Some(previous) = registry.insert(key, adapter) { + return Err(format!("duplicate AWS API operation catalog key {}:{}", previous.service, previous.operation)); + } + } + Ok(registry) +} + +fn adapter_registry() -> Result<&'static HashMap<(String, String), OperationAdapter>, ValidationError> { + ADAPTER_REGISTRY.as_ref().map_err(|message| ValidationError::Engine(message.clone())) +} + +/// CloudFormation operations that accept TemplateBody per botocore service +/// definitions. Only these exact service+operation pairs treat a TemplateBody +/// parameter as a CloudFormation template. +const TEMPLATE_BODY_OPERATIONS: &[(&str, &str)] = &[ + ("cloudformation", "CreateChangeSet"), + ("cloudformation", "CreateStack"), + ("cloudformation", "CreateStackSet"), + ("cloudformation", "EstimateTemplateCost"), + ("cloudformation", "GetTemplateSummary"), + ("cloudformation", "UpdateStack"), + ("cloudformation", "UpdateStackSet"), + ("cloudformation", "ValidateTemplate"), +]; + +/// CLI and Java SDK service names differ only in casing for the supported adapters. +fn normalize_service(name: &str) -> String { + name.to_ascii_lowercase() +} + +fn lookup_adapter(service: &str, operation: &str) -> Result, ValidationError> { + let key = (normalize_service(service), operation.to_string()); + Ok(adapter_registry()?.get(&key)) +} +fn is_template_body_operation(service: &str, operation: &str) -> bool { + let normalized = normalize_service(service); + TEMPLATE_BODY_OPERATIONS.iter().any(|(s, o)| normalize_service(s) == normalized && *o == operation) +} + +fn template_body_operation_kind(service: &str, operation: &str) -> Option { + if normalize_service(service) != "cloudformation" { + return None; + } + match operation { + "CreateChangeSet" | "CreateStack" | "CreateStackSet" => Some(AwsApiOperationKind::CloudFormationCreate), + "UpdateStack" | "UpdateStackSet" => Some(AwsApiOperationKind::CloudFormationUpdate), + "EstimateTemplateCost" | "GetTemplateSummary" | "ValidateTemplate" => Some(AwsApiOperationKind::ReadOnly), + _ => None, + } +} + +fn is_cloud_control_service(service: &str) -> bool { + normalize_service(service) == "cloudcontrol" +} #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum OperationPhase { Read, @@ -306,11 +429,9 @@ enum OperationPhase { #[derive(Debug, Clone)] struct Classification { kind: AwsApiOperationKind, - phase: OperationPhase, candidates: Vec, } -const MODIFIER_PREFIXES: &[&str] = &["Admin", "Batch", "Bulk", "Transact"]; const READ_VERBS: &[&str] = &[ "Calculate", "Check", @@ -343,6 +464,123 @@ const READ_VERBS: &[&str] = &[ "Verify", "View", ]; +const DATA_PLANE_VERBS: &[&str] = &[ + "Analyze", + "Chat", + "Complete", + "Convert", + "Converse", + "Decrypt", + "Deliver", + "Encrypt", + "Execute", + "Generate", + "Infer", + "Invoke", + "Meter", + "Notify", + "Post", + "Predict", + "Publish", + "Receive", + "Recognize", + "Render", + "Respond", + "Send", + "Signal", + "Sign", + "Stream", + "Synthesize", + "Test", + "Translate", + "Upload", + "Write", +]; + +const MODIFIER_PREFIXES: &[&str] = &["Admin", "Batch", "Bulk", "Transact"]; + +fn classify_operation( + request: &AwsApiRequest, + schema_validator: &SchemaValidator, +) -> Result { + if let Some(kind) = template_body_operation_kind(&request.service_name, &request.operation_name) { + return Ok(Classification { kind, candidates: Vec::new() }); + } + + // The modeled read-only trait is authoritative even over a registered adapter. + if request.is_read_only == Some(true) { + return Ok(Classification { kind: AwsApiOperationKind::ReadOnly, candidates: Vec::new() }); + } + + if let Some(adapter) = lookup_adapter(&request.service_name, &request.operation_name)? { + let kind = match adapter.phase { + AdapterPhase::Create => AwsApiOperationKind::CloudFormationCreate, + AdapterPhase::Update => AwsApiOperationKind::CloudFormationUpdate, + AdapterPhase::Delete => AwsApiOperationKind::CloudFormationDelete, + }; + return Ok(Classification { kind, candidates: vec![adapter.cfn_type.clone()] }); + } + + let words = operation_words(&request.operation_name); + let verb = effective_verb(&words); + let phase = operation_phase(request, verb); + + if phase == OperationPhase::Read { + return Ok(Classification { kind: AwsApiOperationKind::ReadOnly, candidates: Vec::new() }); + } + if phase == OperationPhase::Data { + return Ok(Classification { kind: AwsApiOperationKind::DataPlaneMutation, candidates: Vec::new() }); + } + + if is_cloud_control_service(&request.service_name) { + let is_resource_op = + matches!(request.operation_name.as_str(), "CreateResource" | "UpdateResource" | "DeleteResource"); + if is_resource_op && let Some(type_name) = explicit_valid_type_name(request, schema_validator) { + let kind = match request.operation_name.as_str() { + "CreateResource" => AwsApiOperationKind::CloudFormationCreate, + "DeleteResource" => AwsApiOperationKind::CloudFormationDelete, + _ => AwsApiOperationKind::UnmappedMutation, + }; + return Ok(Classification { kind, candidates: vec![type_name] }); + } + } + + // Unknown mutation: classify by verb family but never assign resource types. + let kind = if DATA_PLANE_IF_UNMAPPED_VERBS.contains(&verb) { + AwsApiOperationKind::DataPlaneMutation + } else { + AwsApiOperationKind::UnmappedMutation + }; + Ok(Classification { kind, candidates: Vec::new() }) +} + +const DATA_PLANE_IF_UNMAPPED_VERBS: &[&str] = + &["Execute", "Invoke", "Post", "Publish", "Put", "Send", "Upload", "Write"]; + +fn operation_phase(request: &AwsApiRequest, verb: &str) -> OperationPhase { + if request.is_read_only == Some(true) || READ_VERBS.contains(&verb) { + return OperationPhase::Read; + } + if DATA_PLANE_VERBS.contains(&verb) { + return OperationPhase::Data; + } + if CREATE_VERBS.contains(&verb) { + return OperationPhase::Create; + } + if UPDATE_VERBS.contains(&verb) { + return OperationPhase::Update; + } + if DELETE_VERBS.contains(&verb) { + return OperationPhase::Delete; + } + match request.http_method.as_deref().map(str::to_ascii_uppercase).as_deref() { + Some("GET" | "HEAD") => return OperationPhase::Read, + Some("DELETE") => return OperationPhase::Delete, + _ => {} + } + OperationPhase::Unknown +} + const CREATE_VERBS: &[&str] = &[ "Add", "Allocate", @@ -449,106 +687,6 @@ const DELETE_VERBS: &[&str] = &[ "Terminate", "Unregister", ]; -const DATA_PLANE_VERBS: &[&str] = &[ - "Analyze", - "Chat", - "Complete", - "Convert", - "Converse", - "Decrypt", - "Deliver", - "Encrypt", - "Execute", - "Generate", - "Infer", - "Invoke", - "Meter", - "Notify", - "Post", - "Predict", - "Publish", - "Receive", - "Recognize", - "Render", - "Respond", - "Send", - "Signal", - "Sign", - "Stream", - "Synthesize", - "Test", - "Translate", - "Upload", - "Write", -]; -const DATA_PLANE_IF_UNMAPPED_VERBS: &[&str] = - &["Execute", "Invoke", "Post", "Publish", "Put", "Send", "Upload", "Write"]; - -fn classify_operation(request: &AwsApiRequest, schema_validator: &SchemaValidator) -> Classification { - let prefix = request.effective_service_prefix(); - let words = operation_words(&request.operation_name); - let verb = effective_verb(&words); - let phase = operation_phase(request, verb); - - match phase { - OperationPhase::Read => Classification { kind: AwsApiOperationKind::ReadOnly, phase, candidates: Vec::new() }, - OperationPhase::Data => { - Classification { kind: AwsApiOperationKind::DataPlaneMutation, phase, candidates: Vec::new() } - } - OperationPhase::Create | OperationPhase::Update | OperationPhase::Delete => { - let candidates = explicit_resource_type(request, schema_validator) - .map(|type_name| vec![type_name]) - .unwrap_or_else(|| candidate_types(schema_validator, prefix, operation_noun(&words))); - if candidates.is_empty() { - let is_data_plane = DATA_PLANE_IF_UNMAPPED_VERBS.contains(&verb); - Classification { - kind: if is_data_plane { - AwsApiOperationKind::DataPlaneMutation - } else { - AwsApiOperationKind::UnmappedMutation - }, - phase: if is_data_plane { OperationPhase::Data } else { phase }, - candidates, - } - } else { - let kind = match phase { - OperationPhase::Create => AwsApiOperationKind::CloudFormationCreate, - OperationPhase::Update => AwsApiOperationKind::CloudFormationUpdate, - OperationPhase::Delete => AwsApiOperationKind::CloudFormationDelete, - _ => AwsApiOperationKind::UnmappedMutation, - }; - Classification { kind, phase, candidates } - } - } - OperationPhase::Unknown => { - Classification { kind: AwsApiOperationKind::UnmappedMutation, phase, candidates: Vec::new() } - } - } -} - -fn operation_phase(request: &AwsApiRequest, verb: &str) -> OperationPhase { - if request.is_read_only == Some(true) || READ_VERBS.contains(&verb) { - return OperationPhase::Read; - } - if DATA_PLANE_VERBS.contains(&verb) { - return OperationPhase::Data; - } - if CREATE_VERBS.contains(&verb) { - return OperationPhase::Create; - } - if UPDATE_VERBS.contains(&verb) { - return OperationPhase::Update; - } - if DELETE_VERBS.contains(&verb) { - return OperationPhase::Delete; - } - match request.http_method.as_deref().map(str::to_ascii_uppercase).as_deref() { - Some("GET" | "HEAD") => return OperationPhase::Read, - Some("DELETE") => return OperationPhase::Delete, - _ => {} - } - OperationPhase::Unknown -} fn operation_words(operation_name: &str) -> Vec { let characters: Vec = operation_name.chars().collect(); @@ -584,84 +722,12 @@ fn effective_verb(words: &[String]) -> &str { } } -fn operation_noun(words: &[String]) -> String { - let words = - if words.first().is_some_and(|word| MODIFIER_PREFIXES.contains(&word.as_str())) { &words[1..] } else { words }; - words.iter().skip(1).map(String::as_str).collect() -} - -fn explicit_resource_type(request: &AwsApiRequest, schema_validator: &SchemaValidator) -> Option { +fn explicit_valid_type_name(request: &AwsApiRequest, schema_validator: &SchemaValidator) -> Option { match request.parameters.get("TypeName") { Some(AwsApiValue::String { value }) if schema_validator.has_resource_type(value) => Some(value.clone()), _ => None, } } - -fn candidate_types(schema_validator: &SchemaValidator, prefix: &str, noun: String) -> Vec { - let resource_candidates: BTreeSet = schema_validator - .resource_type_names() - .filter(|type_name| score_candidate(type_name, prefix, &noun) > 0) - .map(str::to_string) - .collect(); - let scores: BTreeMap = resource_candidates - .into_iter() - .map(|type_name| { - let score = score_candidate(&type_name, prefix, &noun); - (type_name, score) - }) - .collect(); - let best_score = scores.values().copied().max().unwrap_or(0); - if best_score < 120 { - return Vec::new(); - } - scores.into_iter().filter_map(|(type_name, score)| (score == best_score).then_some(type_name)).collect() -} - -fn normalize(value: &str) -> String { - value.chars().filter(char::is_ascii_alphanumeric).flat_map(char::to_lowercase).collect() -} - -fn namespace_score(namespace: &str, prefix: &str) -> u32 { - let namespace = normalize(namespace); - let prefix = normalize(prefix); - if namespace == prefix { - 100 - } else if !namespace.is_empty() - && !prefix.is_empty() - && (namespace.contains(&prefix) || prefix.contains(&namespace)) - { - 70 - } else { - 0 - } -} - -fn resource_score(resource_name: &str, noun: &str) -> u32 { - let resource = normalize(resource_name); - let noun = normalize(noun); - if resource.is_empty() || noun.is_empty() { - 0 - } else if resource == noun { - 100 - } else if noun.contains(&resource) { - 40 + (40 * resource.len() / noun.len()) as u32 - } else if resource.contains(&noun) { - 30 + (30 * noun.len() / resource.len()) as u32 - } else { - 0 - } -} - -fn score_candidate(type_name: &str, prefix: &str, noun: &str) -> u32 { - let parts: Vec<&str> = type_name.split("::").collect(); - if parts.len() != 3 { - return 0; - } - let namespace = namespace_score(parts[1], prefix); - let resource = resource_score(parts[2], noun); - if namespace == 0 || resource == 0 { 0 } else { namespace + resource } -} - struct Synthesis { template: Option>, source: Option, @@ -681,25 +747,45 @@ fn synthesize_request( classification: &Classification, schema_validator: &SchemaValidator, ) -> Result { + let is_cfn_op = is_template_body_operation(&request.service_name, &request.operation_name); + + if is_cfn_op { + if let Some(template) = template_body_bytes(request.parameters.get("TemplateBody")) { + return Ok(Synthesis { + template: Some(template), + source: Some(AwsApiTemplateSource::TemplateBody), + reason: "using exact request TemplateBody".into(), + resource_types: Vec::new(), + diagnostic_properties: None, + }); + } + if request.parameters.contains_key("TemplateURL") { + return Ok(Synthesis::skipped("TemplateURL content is unavailable to the offline validator", Vec::new())); + } + } + if classification.kind == AwsApiOperationKind::ReadOnly { return Ok(Synthesis::skipped("read-only calls do not need validation", Vec::new())); } - if let Some(template) = template_body_bytes(request.parameters.get("TemplateBody")) { - return Ok(Synthesis { - template: Some(template), - source: Some(AwsApiTemplateSource::TemplateBody), - reason: "using exact request TemplateBody".into(), - resource_types: Vec::new(), - diagnostic_properties: None, - }); - } - if request.parameters.contains_key("TemplateURL") { - return Ok(Synthesis::skipped("TemplateURL content is unavailable to the offline validator", Vec::new())); - } - if request.parameters.contains_key("TypeName") && request.parameters.contains_key("DesiredState") { + + let is_cloud_control = is_cloud_control_service(&request.service_name); + if is_cloud_control + && request.operation_name == "CreateResource" + && request.parameters.contains_key("TypeName") + && request.parameters.contains_key("DesiredState") + { return desired_state_template(request, schema_validator); } - generic_template(request, classification, schema_validator) + + if is_cloud_control && request.operation_name == "UpdateResource" { + let type_names = explicit_valid_type_name(request, schema_validator).map(|t| vec![t]).unwrap_or_default(); + return Ok(Synthesis::skipped( + "Cloud Control UpdateResource uses PatchDocument and cannot be synthesized", + type_names, + )); + } + + adapter_template(request, classification, schema_validator) } fn template_body_bytes(value: Option<&AwsApiValue>) -> Option> { @@ -744,7 +830,7 @@ fn desired_state_template( }) } -fn generic_template( +fn adapter_template( request: &AwsApiRequest, classification: &Classification, schema_validator: &SchemaValidator, @@ -760,33 +846,44 @@ fn generic_template( } if classification.candidates.len() != 1 { return Ok(Synthesis::skipped( - "CloudFormation resource candidate is ambiguous", + "no adapter maps this operation to a CloudFormation resource", classification.candidates.clone(), )); } + let type_name = &classification.candidates[0]; let Some(schema) = schema_validator.resource_schema_metadata(type_name) else { - return Ok(Synthesis::skipped("CloudFormation resource candidate is unknown", vec![type_name.clone()])); + return Ok(Synthesis::skipped("CloudFormation resource type is unknown", vec![type_name.clone()])); }; - let properties = match map_properties(&request.parameters, &schema, classification.phase) { - Ok(properties) => properties, - Err(reason) => return Ok(Synthesis::skipped(reason, vec![type_name.clone()])), + + let adapter = lookup_adapter(&request.service_name, &request.operation_name)?; + let Some(adapter) = adapter else { + return Ok(Synthesis::skipped( + "no adapter maps this operation to a CloudFormation resource", + vec![type_name.clone()], + )); }; - let diagnostic_properties = - (classification.phase == OperationPhase::Update).then(|| properties.keys().cloned().collect::>()); - let source = if classification.phase == OperationPhase::Update { + + let properties = map_adapter_properties(&request.parameters, &schema, adapter)?; + + if properties.is_empty() { + return Ok(Synthesis::skipped("no request parameters map to resource properties", vec![type_name.clone()])); + } + + let diagnostic_properties = Some(properties.keys().cloned().collect::>()); + let source = if adapter.phase == AdapterPhase::Update { AwsApiTemplateSource::SynthesizedUpdate } else { AwsApiTemplateSource::SynthesizedCreate }; - let reason = if classification.phase == OperationPhase::Update { + let reason = if adapter.phase == AdapterPhase::Update { "synthesized explicitly updated CloudFormation properties" } else { "synthesized one unambiguous CloudFormation resource" }; - let properties: serde_json::Map = properties.into_iter().collect(); + let template_properties: serde_json::Map = properties.into_iter().collect(); Ok(Synthesis { - template: Some(resource_template(type_name, &properties)?), + template: Some(resource_template(type_name, &template_properties)?), source: Some(source), reason: reason.into(), resource_types: vec![type_name.clone()], @@ -794,82 +891,68 @@ fn generic_template( }) } -fn resource_template( - type_name: &str, - properties: &serde_json::Map, -) -> Result, ValidationError> { - serde_json::to_vec(&serde_json::json!({ - "AWSTemplateFormatVersion": "2010-09-09", - "Resources": { - "Resource": { - "Type": type_name, - "Properties": properties, - } - } - })) - .map_err(|error| ValidationError::Engine(format!("failed to serialize synthesized template: {error}"))) -} - -fn map_properties( +fn map_adapter_properties( parameters: &HashMap, schema: &ResourceSchemaMetadata, - phase: OperationPhase, -) -> Result, String> { - let resource_name = schema.type_name.rsplit("::").next().unwrap_or(&schema.type_name); + adapter: &OperationAdapter, +) -> Result, ValidationError> { let mut excluded = schema.read_only_properties.clone(); - if phase == OperationPhase::Update { + if adapter.phase == AdapterPhase::Update { excluded.extend(schema.primary_identifier_properties.iter().cloned()); } + + let mut sources = BTreeSet::new(); + let mut targets = BTreeSet::new(); let mut mapped = BTreeMap::new(); - let mut parameters: Vec<(&String, &AwsApiValue)> = parameters.iter().collect(); - parameters.sort_by_key(|(name, _)| name.as_str()); - - for (parameter_name, value) in parameters { - let matches: Vec<&String> = schema - .property_types - .keys() - .filter(|property_name| { - !excluded.contains(*property_name) && property_matches(parameter_name, property_name, resource_name) - }) - .collect(); - if matches.len() > 1 { - return Err(format!("parameter {parameter_name} maps to multiple resource properties")); + for mapping in &adapter.mappings { + if !sources.insert(mapping.source.as_str()) { + return Err(ValidationError::Engine(format!( + "adapter {}:{} has duplicate source parameter '{}'", + adapter.service, adapter.operation, mapping.source + ))); } - let Some(property_name) = matches.first().copied() else { - continue; - }; - if mapped.contains_key(property_name) { - return Err(format!("multiple parameters map to {property_name}")); + if !targets.insert(mapping.target.as_str()) { + return Err(ValidationError::Engine(format!( + "adapter {}:{} has duplicate target property '{}'", + adapter.service, adapter.operation, mapping.target + ))); } - let accepted_types = &schema.property_types[property_name]; - if let Some(value) = mapped_value(value, accepted_types, property_name) { - mapped.insert(property_name.clone(), value); + let Some(accepted_types) = schema.property_types.get(&mapping.target) else { + return Err(ValidationError::Engine(format!( + "adapter {}:{} targets property '{}' which does not exist on {}", + adapter.service, adapter.operation, mapping.target, adapter.cfn_type + ))); + }; + if excluded.contains(&mapping.target) { + return Err(ValidationError::Engine(format!( + "adapter {}:{} targets excluded property '{}' on {}", + adapter.service, adapter.operation, mapping.target, adapter.cfn_type + ))); } - } - - if mapped.is_empty() { - return Err("no request parameters map to resource properties".into()); - } - if phase == OperationPhase::Create { - let mapped_properties: BTreeSet = mapped.keys().cloned().collect(); - let missing: Vec<&String> = schema.required_properties.difference(&mapped_properties).collect(); - if !missing.is_empty() { - return Err(format!( - "required resource properties are absent: {}", - missing.into_iter().map(String::as_str).collect::>().join(", ") - )); + let Some(value) = parameters.get(&mapping.source) else { + continue; + }; + if let Some(json_value) = mapped_value(value, accepted_types, &mapping.target) { + mapped.insert(mapping.target.clone(), json_value); } } Ok(mapped) } -fn property_matches(parameter_name: &str, property_name: &str, resource_name: &str) -> bool { - let parameter = normalize(parameter_name); - let property = normalize(property_name); - let resource = normalize(resource_name); - parameter == property - || property == format!("{resource}{parameter}") - || (property == format!("{parameter}name") && parameter == resource) +fn resource_template( + type_name: &str, + properties: &serde_json::Map, +) -> Result, ValidationError> { + serde_json::to_vec(&serde_json::json!({ + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "Resource": { + "Type": type_name, + "Properties": properties, + } + } + })) + .map_err(|error| ValidationError::Engine(format!("failed to serialize synthesized template: {error}"))) } fn mapped_value( @@ -877,9 +960,8 @@ fn mapped_value( accepted_types: &BTreeSet, property_name: &str, ) -> Option { - if value_matches_types(value, accepted_types) { - return value.json_value(); - } + // Many AWS APIs use string maps for tags, while CloudFormation uses + // Key/Value object arrays for the same resource state. if property_name == "Tags" && accepts_type(accepted_types, PropertyValueType::Array) && let AwsApiValue::Object { entries } = value @@ -896,15 +978,8 @@ fn mapped_value( .collect(), )); } - if accepts_type(accepted_types, PropertyValueType::String) { - let value = match value { - AwsApiValue::Boolean { value } => Some(value.to_string()), - AwsApiValue::Integer { value } => Some(value.to_string()), - AwsApiValue::UnsignedInteger { value } => Some(value.to_string()), - AwsApiValue::Number { value } => serde_json::Number::from_f64(*value).map(|value| value.to_string()), - _ => None, - }; - return value.map(serde_json::Value::String); + if value_matches_types(value, accepted_types) { + return value.json_value(); } None } @@ -914,23 +989,33 @@ fn accepts_type(types: &BTreeSet, expected: PropertyValueType } fn value_matches_types(value: &AwsApiValue, types: &BTreeSet) -> bool { - if types.contains(&PropertyValueType::Any) { - return !matches!(value, AwsApiValue::Null | AwsApiValue::Bytes { .. } | AwsApiValue::Unsupported { .. }); - } + let accepts_any = types.contains(&PropertyValueType::Any); match value { - AwsApiValue::Array { .. } => types.contains(&PropertyValueType::Array), - AwsApiValue::Object { .. } => types.contains(&PropertyValueType::Object), - AwsApiValue::Boolean { .. } => types.contains(&PropertyValueType::Boolean), + AwsApiValue::Array { items } => { + (accepts_any || types.contains(&PropertyValueType::Array)) && items.iter().all(is_scalar_api_value) + } + AwsApiValue::Object { .. } => false, + AwsApiValue::Boolean { .. } => accepts_any || types.contains(&PropertyValueType::Boolean), AwsApiValue::Integer { .. } | AwsApiValue::UnsignedInteger { .. } => { - types.contains(&PropertyValueType::Integer) || types.contains(&PropertyValueType::Number) + accepts_any || types.contains(&PropertyValueType::Integer) || types.contains(&PropertyValueType::Number) } - AwsApiValue::Number { .. } => types.contains(&PropertyValueType::Number), - AwsApiValue::String { .. } => types.contains(&PropertyValueType::String), + AwsApiValue::Number { .. } => accepts_any || types.contains(&PropertyValueType::Number), + AwsApiValue::String { .. } => accepts_any || types.contains(&PropertyValueType::String), AwsApiValue::Null | AwsApiValue::Bytes { .. } | AwsApiValue::Unsupported { .. } => false, } } -fn scope_partial_update_report(report: &mut ValidationReport, properties: &BTreeSet) { +fn is_scalar_api_value(value: &AwsApiValue) -> bool { + matches!( + value, + AwsApiValue::Boolean { .. } + | AwsApiValue::Integer { .. } + | AwsApiValue::UnsignedInteger { .. } + | AwsApiValue::Number { .. } + | AwsApiValue::String { .. } + ) +} +fn scope_synthesized_report(report: &mut ValidationReport, properties: &BTreeSet) { let before = report.diagnostics.len(); report.diagnostics.retain(|diagnostic| { diagnostic.property_path.as_deref().is_some_and(|path| diagnostic_in_scope(path, properties)) @@ -960,7 +1045,6 @@ fn summarize_diagnostics(diagnostics: &[diagnostics::Diagnostic]) -> Summary { let informational = diagnostics.len() as u32 - fatal - errors - warnings - debug; Summary { fatal, errors, warnings, informational, debug } } - #[cfg(test)] mod tests { use super::*; @@ -1021,83 +1105,524 @@ mod tests { .iter() .map(|(name, value)| (name.clone(), AwsApiValue::from_json(value.clone()))) .collect(); - AwsApiRequest::new(service, operation, parameters).with_service_prefix(service).with_http_method("POST") + AwsApiRequest::new(service, operation, parameters).with_http_method("POST") } fn synthesized_json(request: &AwsApiRequest) -> (Classification, Synthesis, serde_json::Value) { let schema_validator = SchemaValidator::default(); - let classification = classify_operation(request, &schema_validator); + let classification = classify_operation(request, &schema_validator).expect("classification succeeds"); let synthesis = synthesize_request(request, &classification, &schema_validator).expect("synthesis succeeds"); let template = synthesis.template.as_ref().expect("request must synthesize"); let document = serde_json::from_slice(template).expect("template must be JSON"); (classification, synthesis, document) } + fn mapping(source: &str, target: &str) -> PropertyMapping { + PropertyMapping { source: source.into(), target: target.into() } + } + + fn malformed_adapter_error(mappings: Vec) -> String { + let schema_validator = SchemaValidator::default(); + let schema = schema_validator.resource_schema_metadata("AWS::S3::Bucket").expect("S3 bucket schema must exist"); + let adapter = OperationAdapter { + service: "s3".into(), + operation: "CreateBucket".into(), + phase: AdapterPhase::Create, + cfn_type: "AWS::S3::Bucket".into(), + mappings, + }; + match map_adapter_properties(&HashMap::new(), &schema, &adapter) + .expect_err("malformed adapter must return an error") + { + ValidationError::Engine(message) => message, + error => panic!("expected engine error, got {error:?}"), + } + } #[test] - fn operation_words_preserve_acronyms_for_noun_matching() { - assert_eq!(operation_words("BatchCreateDB2Cluster"), ["Batch", "Create", "DB", "2", "Cluster"]); - assert_eq!(effective_verb(&operation_words("BatchCreateDB2Cluster")), "Create"); - assert_eq!(operation_noun(&operation_words("BatchCreateDB2Cluster")), "DB2Cluster"); + fn catalog_parser_rejects_invalid_formats_and_normalized_duplicates() { + assert!(parse_adapter_registry(b"not json").expect_err("invalid JSON must fail").contains("invalid")); + assert!( + parse_adapter_registry(br#"{"format_version":2,"adapters":[]}"#) + .expect_err("unsupported format must fail") + .contains("unsupported") + ); + let duplicate = br#"{ + "format_version": 1, + "adapters": [ + {"service":"S3","operation":"CreateBucket","phase":"create","cfn_type":"AWS::S3::Bucket","mappings":[]}, + {"service":"s3","operation":"CreateBucket","phase":"create","cfn_type":"AWS::S3::Bucket","mappings":[]} + ] + }"#; + assert!( + parse_adapter_registry(duplicate).expect_err("case-normalized duplicate must fail").contains("duplicate") + ); + let blank = br#"{ + "format_version": 1, + "adapters": [ + {"service":"","operation":"CreateBucket","phase":"create","cfn_type":"AWS::S3::Bucket","mappings":[]} + ] + }"#; + assert!(parse_adapter_registry(blank).expect_err("blank identity must fail").contains("blank")); + } + + #[test] + fn catalog_covers_the_generated_resource_universe() { + let registry = adapter_registry().expect("catalog loads"); + let creates = registry.values().filter(|a| a.phase == AdapterPhase::Create).count(); + let deletes = registry.values().filter(|a| a.phase == AdapterPhase::Delete).count(); + assert!(registry.len() >= 2000, "catalog unexpectedly small: {}", registry.len()); + assert!(creates >= 1000, "create adapters unexpectedly few: {creates}"); + assert!(deletes >= 900, "delete adapters unexpectedly few: {deletes}"); } #[test] - fn representative_operations_have_closed_classifications() { + fn catalog_operations_synthesize_beyond_the_original_services() { + let cases = [ + ("ec2", "RunInstances", "AWS::EC2::Instance"), + ("kms", "CreateKey", "AWS::KMS::Key"), + ("logs", "CreateLogGroup", "AWS::Logs::LogGroup"), + ("stepfunctions", "CreateStateMachine", "AWS::StepFunctions::StateMachine"), + ("cloudwatch", "PutMetricAlarm", "AWS::CloudWatch::Alarm"), + ("secretsmanager", "CreateSecret", "AWS::SecretsManager::Secret"), + ]; let schema_validator = SchemaValidator::default(); - for (service, operation, expected, candidate) in [ - ("s3", "CreateBucket", AwsApiOperationKind::CloudFormationCreate, Some("AWS::S3::Bucket")), - ("dynamodb", "CreateTable", AwsApiOperationKind::CloudFormationCreate, Some("AWS::DynamoDB::Table")), - ("iam", "GetRole", AwsApiOperationKind::ReadOnly, None), - ("lambda", "Invoke", AwsApiOperationKind::DataPlaneMutation, None), - ("s3", "DeleteBucket", AwsApiOperationKind::CloudFormationDelete, Some("AWS::S3::Bucket")), - ] { - let classification = - classify_operation(&request(service, operation, serde_json::json!({})), &schema_validator); - assert_eq!(classification.kind, expected, "{service}:{operation}"); - if let Some(candidate) = candidate { - assert_eq!(classification.candidates, [candidate], "{service}:{operation}"); + for (service, operation, expected_type) in cases { + let req = request(service, operation, serde_json::json!({})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + assert_eq!(classification.candidates, [expected_type], "{service}:{operation} must map to {expected_type}"); + } + } + + #[test] + fn catalog_never_contains_forbidden_or_ambiguous_operations() { + let forbidden = [ + ("ecs", "RunTask"), + ("ec2", "StartInstances"), + ("ec2", "StopInstances"), + ("iot", "StartThingRegistrationTask"), + ("lambda", "Invoke"), + ("sns", "Publish"), + ("sqs", "SendMessage"), + ("s3", "PutObject"), + ("dynamodb", "PutItem"), + ("logs", "StartQuery"), + ("acm", "RemoveTagsFromCertificate"), + ("robomaker", "DeregisterRobot"), + ("quicksight", "CreateTopic"), + ("quicksight", "DeleteTopic"), + ]; + let registry = adapter_registry().expect("catalog loads"); + for (service, operation) in forbidden { + let key = (service.to_string(), operation.to_string()); + assert!(!registry.contains_key(&key), "{service}:{operation} must never be a registered adapter"); + } + } + + #[test] + fn every_catalog_adapter_maps_cleanly_with_empty_requests() { + let schema_validator = SchemaValidator::default(); + let empty = HashMap::new(); + for adapter in adapter_registry().expect("catalog loads").values() { + let schema = schema_validator + .resource_schema_metadata(&adapter.cfn_type) + .unwrap_or_else(|| panic!("{} missing schema metadata", adapter.cfn_type)); + map_adapter_properties(&empty, &schema, adapter).unwrap_or_else(|error| { + panic!("adapter {}:{} violates registry invariants: {error}", adapter.service, adapter.operation) + }); + } + } + + #[test] + fn rejected_catalog_operations_never_report_resource_types() { + let schema_validator = SchemaValidator::default(); + for (service, operation) in REJECTED_CATALOG_OPERATIONS { + let request = request(service, operation, serde_json::json!({})); + let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); + assert!( + classification.candidates.is_empty(), + "{service}:{operation} must not report a CloudFormation type" + ); + let synthesis = + synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none(), "{service}:{operation} must not synthesize state"); + assert!(synthesis.resource_types.is_empty(), "{service}:{operation} must not report resource types"); + } + } + + #[test] + fn nested_values_are_omitted_without_recursive_shape_mappings() { + let object_types = BTreeSet::from([PropertyValueType::Object]); + let array_types = BTreeSet::from([PropertyValueType::Array]); + let object = AwsApiValue::from_json(serde_json::json!({"lowerCamel": "value"})); + let object_array = AwsApiValue::from_json(serde_json::json!([{"lowerCamel": "value"}])); + let scalar_array = AwsApiValue::from_json(serde_json::json!(["one", "two"])); + + assert!(mapped_value(&object, &object_types, "Configuration").is_none()); + assert!(mapped_value(&object_array, &array_types, "Configurations").is_none()); + assert_eq!(mapped_value(&scalar_array, &array_types, "Names"), Some(serde_json::json!(["one", "two"]))); + } + + #[test] + fn registry_has_unique_service_operation_keys() { + let mut seen = BTreeSet::new(); + for adapter in adapter_registry().expect("catalog loads").values() { + let key = (normalize_service(&adapter.service), adapter.operation.clone()); + assert!(seen.insert(key.clone()), "duplicate adapter key: {}:{}", key.0, key.1); + } + } + + #[test] + fn registry_types_exist_in_schema_validator() { + let schema_validator = SchemaValidator::default(); + for adapter in adapter_registry().expect("catalog loads").values() { + assert!( + schema_validator.has_resource_type(&adapter.cfn_type), + "adapter {}:{} references unknown type {}", + adapter.service, + adapter.operation, + adapter.cfn_type + ); + } + } + + #[test] + fn registry_property_mappings_target_real_properties() { + let schema_validator = SchemaValidator::default(); + for adapter in adapter_registry().expect("catalog loads").values() { + let Some(schema) = schema_validator.resource_schema_metadata(&adapter.cfn_type) else { + continue; + }; + for mapping in &adapter.mappings { + assert!( + schema.property_types.contains_key(&mapping.target), + "adapter {}:{} maps to non-existent property {}.{}", + adapter.service, + adapter.operation, + adapter.cfn_type, + mapping.target + ); } } } #[test] - fn explicit_readonly_and_http_get_are_authoritative_read_signals() { + fn registry_has_no_read_only_property_mappings() { let schema_validator = SchemaValidator::default(); - let mut explicitly_readonly = request("test", "CreateThing", serde_json::json!({})); - explicitly_readonly.is_read_only = Some(true); - assert_eq!(classify_operation(&explicitly_readonly, &schema_validator).kind, AwsApiOperationKind::ReadOnly); - let mut get_request = request("test", "FrobnicateThing", serde_json::json!({})); - get_request.http_method = Some("GET".into()); - assert_eq!(classify_operation(&get_request, &schema_validator).kind, AwsApiOperationKind::ReadOnly); + for adapter in adapter_registry().expect("catalog loads").values() { + let Some(schema) = schema_validator.resource_schema_metadata(&adapter.cfn_type) else { + continue; + }; + for mapping in &adapter.mappings { + assert!( + !schema.read_only_properties.contains(&mapping.target), + "adapter {}:{} maps to read-only property {}.{}", + adapter.service, + adapter.operation, + adapter.cfn_type, + mapping.target + ); + } + } } #[test] - fn exact_template_body_bytes_are_not_rewritten() { + fn registry_update_mappings_exclude_primary_identifiers() { let schema_validator = SchemaValidator::default(); - let mut request = request("cloudformation", "CreateChangeSet", serde_json::json!({})); - let template = br#"{"Resources":{}}"#.to_vec(); - request.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: template.clone() }); - let classification = classify_operation(&request, &schema_validator); + for adapter in adapter_registry().expect("catalog loads").values() { + if adapter.phase != AdapterPhase::Update { + continue; + } + let Some(schema) = schema_validator.resource_schema_metadata(&adapter.cfn_type) else { + continue; + }; + for mapping in &adapter.mappings { + assert!( + !schema.primary_identifier_properties.contains(&mapping.target), + "update adapter {}:{} maps to primary identifier property {}.{}", + adapter.service, + adapter.operation, + adapter.cfn_type, + mapping.target + ); + } + } + } + + #[test] + fn registry_has_no_duplicate_source_or_target_mappings() { + for adapter in adapter_registry().expect("catalog loads").values() { + let mut sources = BTreeSet::new(); + let mut targets = BTreeSet::new(); + for mapping in &adapter.mappings { + assert!( + sources.insert(mapping.source.as_str()), + "adapter {}:{} has duplicate source mapping: {}", + adapter.service, + adapter.operation, + mapping.source + ); + assert!( + targets.insert(mapping.target.as_str()), + "adapter {}:{} has duplicate target mapping: {}", + adapter.service, + adapter.operation, + mapping.target + ); + } + } + } + + #[test] + fn malformed_adapter_mappings_fail_without_request_values() { + assert!(malformed_adapter_error(vec![mapping("Bucket", "NotAProperty")]).contains("does not exist")); + assert!(malformed_adapter_error(vec![mapping("Bucket", "Arn")]).contains("excluded property")); + assert!( + malformed_adapter_error(vec![mapping("Bucket", "BucketName"), mapping("Bucket", "Tags")]) + .contains("duplicate source parameter") + ); + assert!( + malformed_adapter_error(vec![mapping("Bucket", "BucketName"), mapping("OtherBucket", "BucketName")]) + .contains("duplicate target property") + ); + } + + #[test] + fn s3_create_bucket_synthesizes_with_explicit_mappings() { + let request = request("s3", "CreateBucket", serde_json::json!({"Bucket": "synthetic-bucket"})); + let (classification, synthesis, document) = synthesized_json(&request); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.candidates, ["AWS::S3::Bucket"]); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(document["Resources"]["Resource"]["Properties"]["BucketName"], "synthetic-bucket"); + } + + #[test] + fn s3_delete_bucket_identifies_type_without_synthesizing() { + let schema_validator = SchemaValidator::default(); + let request = request("s3", "DeleteBucket", serde_json::json!({"Bucket": "synthetic-bucket"})); + let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationDelete); + assert_eq!(classification.candidates, ["AWS::S3::Bucket"]); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::TemplateBody)); - assert_eq!(synthesis.template, Some(template)); + assert!(synthesis.template.is_none()); + } + + #[test] + fn dynamodb_create_table_synthesizes_with_explicit_mappings() { + let request = request( + "dynamodb", + "CreateTable", + serde_json::json!({ + "TableName": "Synthetic", + "KeySchema": [{"AttributeName": "id", "KeyType": "HASH"}], + "AttributeDefinitions": [{"AttributeName": "id", "AttributeType": "S"}], + "BillingMode": "PAY_PER_REQUEST" + }), + ); + let (classification, synthesis, document) = synthesized_json(&request); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.candidates, ["AWS::DynamoDB::Table"]); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(document["Resources"]["Resource"]["Properties"]["TableName"], "Synthetic"); + assert_eq!(document["Resources"]["Resource"]["Properties"]["BillingMode"], "PAY_PER_REQUEST"); + } + + #[test] + fn dynamodb_delete_table_identifies_type_without_synthesizing() { + let schema_validator = SchemaValidator::default(); + let request = request("dynamodb", "DeleteTable", serde_json::json!({"TableName": "Synthetic"})); + let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationDelete); + assert_eq!(classification.candidates, ["AWS::DynamoDB::Table"]); + let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none()); + } + + #[test] + fn iam_create_role_synthesizes_with_explicit_mappings() { + let request = request( + "iam", + "CreateRole", + serde_json::json!({ + "RoleName": "Synthetic", + "AssumeRolePolicyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[]}", + "Tags": {"Team": "CLI"} + }), + ); + let (classification, synthesis, document) = synthesized_json(&request); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.candidates, ["AWS::IAM::Role"]); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(document["Resources"]["Resource"]["Properties"]["RoleName"], "Synthetic"); } #[test] - fn template_url_is_not_fetched() { + fn iam_delete_role_identifies_type_without_synthesizing() { let schema_validator = SchemaValidator::default(); + let request = request("iam", "DeleteRole", serde_json::json!({"RoleName": "Synthetic"})); + let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationDelete); + assert_eq!(classification.candidates, ["AWS::IAM::Role"]); + let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none()); + } + + #[test] + fn lambda_create_function_synthesizes_partial_and_scopes_diagnostics() { + let request = + request("lambda", "CreateFunction", serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 128})); + let (classification, synthesis, document) = synthesized_json(&request); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.candidates, ["AWS::Lambda::Function"]); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(synthesis.diagnostic_properties, Some(BTreeSet::from(["FunctionName".into(), "MemorySize".into()]))); + assert_eq!(document["Resources"]["Resource"]["Properties"]["FunctionName"], "Synthetic"); + assert_eq!(document["Resources"]["Resource"]["Properties"]["MemorySize"], 128); + } + + #[test] + fn lambda_update_function_configuration_synthesizes_partial_update() { let request = request( + "lambda", + "UpdateFunctionConfiguration", + serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 128}), + ); + let (classification, synthesis, document) = synthesized_json(&request); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationUpdate); + assert_eq!(classification.candidates, ["AWS::Lambda::Function"]); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedUpdate)); + assert_eq!(document["Resources"]["Resource"]["Properties"], serde_json::json!({"MemorySize": 128})); + assert_eq!(synthesis.diagnostic_properties, Some(BTreeSet::from(["MemorySize".into()]))); + } + + #[test] + fn lambda_delete_function_identifies_type_without_synthesizing() { + let schema_validator = SchemaValidator::default(); + let request = request("lambda", "DeleteFunction", serde_json::json!({"FunctionName": "Synthetic"})); + let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationDelete); + assert_eq!(classification.candidates, ["AWS::Lambda::Function"]); + let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none()); + } + + #[test] + fn sns_create_topic_synthesizes_with_explicit_mappings() { + let request = request("sns", "CreateTopic", serde_json::json!({"Name": "Synthetic", "Tags": {"Team": "CLI"}})); + let (classification, synthesis, document) = synthesized_json(&request); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.candidates, ["AWS::SNS::Topic"]); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(document["Resources"]["Resource"]["Properties"]["TopicName"], "Synthetic"); + } + + #[test] + fn sns_delete_topic_identifies_type_without_synthesizing() { + let schema_validator = SchemaValidator::default(); + let request = request("sns", "DeleteTopic", serde_json::json!({"TopicArn": "arn:aws:sns:us-east-1:123:Topic"})); + let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationDelete); + assert_eq!(classification.candidates, ["AWS::SNS::Topic"]); + let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none()); + } + + #[test] + fn sqs_create_queue_synthesizes_with_explicit_mappings() { + let request = + request("sqs", "CreateQueue", serde_json::json!({"QueueName": "Synthetic", "tags": {"Team": "CLI"}})); + let (classification, synthesis, document) = synthesized_json(&request); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.candidates, ["AWS::SQS::Queue"]); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(document["Resources"]["Resource"]["Properties"]["QueueName"], "Synthetic"); + } + + #[test] + fn sqs_delete_queue_identifies_type_without_synthesizing() { + let schema_validator = SchemaValidator::default(); + let request = + request("sqs", "DeleteQueue", serde_json::json!({"QueueUrl": "https://sqs.us-east-1.amazonaws.com/123/Q"})); + let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationDelete); + assert_eq!(classification.candidates, ["AWS::SQS::Queue"]); + let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none()); + } + #[test] + fn java_sdk_service_name_casing_resolves_adapters() { + let schema_validator = SchemaValidator::default(); + for (service, operation, expected_type) in [ + ("S3", "CreateBucket", "AWS::S3::Bucket"), + ("DynamoDb", "CreateTable", "AWS::DynamoDB::Table"), + ("Iam", "CreateRole", "AWS::IAM::Role"), + ("Lambda", "CreateFunction", "AWS::Lambda::Function"), + ("Sns", "CreateTopic", "AWS::SNS::Topic"), + ("Sqs", "CreateQueue", "AWS::SQS::Queue"), + ] { + let req = request(service, operation, serde_json::json!({})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + assert_eq!( + classification.candidates, + [expected_type], + "Java SDK casing {service}:{operation} should resolve to {expected_type}" + ); + } + } + #[test] + fn template_body_is_accepted_only_for_cloudformation_operations() { + let schema_validator = SchemaValidator::default(); + let template = br#"{"Resources":{}}"#.to_vec(); + + let mut cfn_request = request("cloudformation", "CreateChangeSet", serde_json::json!({})); + cfn_request.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: template.clone() }); + let classification = classify_operation(&cfn_request, &schema_validator).expect("classification succeeds"); + let synthesis = + synthesize_request(&cfn_request, &classification, &schema_validator).expect("synthesis succeeds"); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::TemplateBody)); + assert_eq!(synthesis.template, Some(template.clone())); + + let mut s3_request = request("s3", "PutObject", serde_json::json!({})); + s3_request.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: template.clone() }); + let classification = classify_operation(&s3_request, &schema_validator).expect("classification succeeds"); + let synthesis = + synthesize_request(&s3_request, &classification, &schema_validator).expect("synthesis succeeds"); + assert_ne!(synthesis.source, Some(AwsApiTemplateSource::TemplateBody)); + } + + #[test] + fn template_url_skip_only_for_cloudformation_operations() { + let schema_validator = SchemaValidator::default(); + let cfn_request = request( "cloudformation", "CreateStack", serde_json::json!({"TemplateURL": "https://example.com/template.json"}), ); - let classification = classify_operation(&request, &schema_validator); - let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + let classification = classify_operation(&cfn_request, &schema_validator).expect("classification succeeds"); + let synthesis = + synthesize_request(&cfn_request, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); assert!(synthesis.reason.contains("unavailable")); } #[test] - fn desired_state_wraps_any_known_type_and_rejects_unknown_types() { + fn all_closed_template_body_operations_are_accepted() { + let schema_validator = SchemaValidator::default(); + let template = br#"{"Resources":{}}"#.to_vec(); + for (service, operation) in TEMPLATE_BODY_OPERATIONS { + let mut req = request(service, operation, serde_json::json!({})); + req.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: template.clone() }); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); + assert_eq!( + synthesis.source, + Some(AwsApiTemplateSource::TemplateBody), + "{service}:{operation} should accept TemplateBody" + ); + } + } + #[test] + fn cloud_control_create_resource_wraps_desired_state() { let known = request( "cloudcontrol", "CreateResource", @@ -1108,82 +1633,183 @@ mod tests { assert_eq!(classification.candidates, ["AWS::SNS::Topic"]); assert_eq!(synthesis.source, Some(AwsApiTemplateSource::CloudControlDesiredState)); assert_eq!(document["Resources"]["Resource"]["Properties"]["TopicName"], "Synthetic"); + } + #[test] + fn cloud_control_with_signing_prefix_cloudcontrolapi() { + let parameters: HashMap = serde_json::json!({ + "TypeName": "AWS::SNS::Topic", + "DesiredState": "{\"TopicName\":\"Synthetic\"}" + }) + .as_object() + .unwrap() + .iter() + .map(|(k, v)| (k.clone(), AwsApiValue::from_json(v.clone()))) + .collect(); + let req = + AwsApiRequest::new("cloudcontrol", "CreateResource", parameters).with_service_prefix("cloudcontrolapi"); + let (classification, synthesis, document) = synthesized_json(&req); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.candidates, ["AWS::SNS::Topic"]); + assert_eq!(synthesis.source, Some(AwsApiTemplateSource::CloudControlDesiredState)); + assert_eq!(document["Resources"]["Resource"]["Properties"]["TopicName"], "Synthetic"); + } + + #[test] + fn cloud_control_rejects_unknown_type_name() { let schema_validator = SchemaValidator::default(); let unknown = request( "cloudcontrol", "CreateResource", serde_json::json!({"TypeName": "AWS::Unknown::Type", "DesiredState": "{}"}), ); - let classification = classify_operation(&unknown, &schema_validator); + let classification = classify_operation(&unknown, &schema_validator).expect("classification succeeds"); let synthesis = synthesize_request(&unknown, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); assert!(synthesis.reason.contains("known CloudFormation TypeName")); } #[test] - fn generic_create_maps_aliases_and_tag_objects() { - let request = - request("s3", "CreateBucket", serde_json::json!({"Bucket": "synthetic-bucket", "Tags": {"Team": "CLI"}})); - let (classification, synthesis, document) = synthesized_json(&request); - assert_eq!(classification.candidates, ["AWS::S3::Bucket"]); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); - assert_eq!(document["Resources"]["Resource"]["Properties"]["BucketName"], "synthetic-bucket"); + fn cloud_control_update_resource_reports_type_but_does_not_synthesize() { + let schema_validator = SchemaValidator::default(); + let update = request( + "cloudcontrol", + "UpdateResource", + serde_json::json!({"TypeName": "AWS::SNS::Topic", "PatchDocument": "[{\"op\":\"replace\"}]"}), + ); + let classification = classify_operation(&update, &schema_validator).expect("classification succeeds"); + assert_eq!(classification.kind, AwsApiOperationKind::UnmappedMutation); + assert_eq!(classification.candidates, ["AWS::SNS::Topic"]); + let synthesis = synthesize_request(&update, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none()); + assert!(synthesis.reason.contains("PatchDocument")); + assert_eq!(synthesis.resource_types, ["AWS::SNS::Topic"]); + } + #[test] + fn explicit_readonly_and_http_get_are_authoritative_read_signals() { + let schema_validator = SchemaValidator::default(); + let mut explicitly_readonly = request("test", "CreateThing", serde_json::json!({})); + explicitly_readonly.is_read_only = Some(true); + assert_eq!( + classify_operation(&explicitly_readonly, &schema_validator).expect("classification succeeds").kind, + AwsApiOperationKind::ReadOnly + ); + let mut get_request = request("test", "FrobnicateThing", serde_json::json!({})); + get_request.http_method = Some("GET".into()); assert_eq!( - document["Resources"]["Resource"]["Properties"]["Tags"], - serde_json::json!([{"Key": "Team", "Value": "CLI"}]) + classify_operation(&get_request, &schema_validator).expect("classification succeeds").kind, + AwsApiOperationKind::ReadOnly ); } #[test] - fn generic_create_requires_complete_resource_state() { + fn data_plane_verbs_are_classified_correctly() { let schema_validator = SchemaValidator::default(); - let request = - request("lambda", "CreateFunction", serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 128})); - let classification = classify_operation(&request, &schema_validator); - let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + let lambda_invoke = request("lambda", "Invoke", serde_json::json!({})); + assert_eq!( + classify_operation(&lambda_invoke, &schema_validator).expect("classification succeeds").kind, + AwsApiOperationKind::DataPlaneMutation + ); + } + #[test] + fn ecs_run_task_never_maps_to_resource() { + let schema_validator = SchemaValidator::default(); + let req = request("ecs", "RunTask", serde_json::json!({"TaskDefinition": "my-task"})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + assert!(classification.candidates.is_empty(), "ecs:RunTask must not map to any resource type"); + assert_ne!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); - assert!(synthesis.reason.contains("Code, Role"), "{}", synthesis.reason); } #[test] - fn generic_update_excludes_primary_identifier_and_tracks_changed_properties() { - let request = request( - "lambda", - "UpdateFunctionConfiguration", - serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 128}), + fn ec2_start_instances_never_maps_to_resource() { + let schema_validator = SchemaValidator::default(); + let req = request("ec2", "StartInstances", serde_json::json!({"InstanceIds": ["i-12345"]})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + assert!(classification.candidates.is_empty(), "ec2:StartInstances must not map to any resource type"); + assert_ne!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none()); + } + + #[test] + fn iot_start_thing_registration_task_never_maps_to_resource() { + let schema_validator = SchemaValidator::default(); + let req = request("iot", "StartThingRegistrationTask", serde_json::json!({"TemplateBody": "{}"})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + assert!( + classification.candidates.is_empty(), + "iot:StartThingRegistrationTask must not map to any resource type" ); - let (_, synthesis, document) = synthesized_json(&request); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedUpdate)); - assert_eq!(document["Resources"]["Resource"]["Properties"], serde_json::json!({"MemorySize": 128})); - assert_eq!(synthesis.diagnostic_properties, Some(BTreeSet::from(["MemorySize".into()]))); + let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); + assert_ne!(synthesis.source, Some(AwsApiTemplateSource::TemplateBody)); } #[test] - fn incompatible_optional_property_is_omitted() { - let request = - request("s3", "CreateBucket", serde_json::json!({"Bucket": "synthetic-bucket", "Tags": {"Key": 42}})); - let (_, _, document) = synthesized_json(&request); - assert_eq!( - document["Resources"]["Resource"]["Properties"], - serde_json::json!({"BucketName": "synthetic-bucket"}) + fn wrong_service_template_body_is_not_treated_as_cfn_template() { + let schema_validator = SchemaValidator::default(); + let template = br#"{"Resources":{}}"#.to_vec(); + for service in ["s3", "lambda", "iot", "dynamodb"] { + let mut req = request(service, "SomeOperation", serde_json::json!({})); + req.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: template.clone() }); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); + assert_ne!( + synthesis.source, + Some(AwsApiTemplateSource::TemplateBody), + "{service}:SomeOperation should not treat TemplateBody as CFN template" + ); + } + } + + #[test] + fn wrong_service_type_name_desired_state_is_not_wrapped() { + let schema_validator = SchemaValidator::default(); + let req = request( + "s3", + "CreateResource", + serde_json::json!({"TypeName": "AWS::SNS::Topic", "DesiredState": "{\"TopicName\":\"Synthetic\"}"}), ); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); + assert_ne!(synthesis.source, Some(AwsApiTemplateSource::CloudControlDesiredState)); } #[test] - fn data_plane_and_delete_requests_do_not_fabricate_resource_state() { + fn near_match_operation_names_never_map_or_synthesize() { let schema_validator = SchemaValidator::default(); - for request in [ - request("dynamodb", "PutItem", serde_json::json!({"TableName": "Synthetic"})), - request("s3", "DeleteBucket", serde_json::json!({"Bucket": "synthetic-bucket"})), + for (service, operation) in [ + ("s3", "CreateBuckets"), + ("s3", "createBucket"), + ("dynamodb", "CreateTables"), + ("lambda", "CreateFunctions"), + ("lambda", "UpdateFunctionConfigurations"), ] { - let classification = classify_operation(&request, &schema_validator); - let synthesis = - synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); - assert!(synthesis.template.is_none()); - assert!(synthesis.reason.contains("representable resource state")); + let req = request(service, operation, serde_json::json!({})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + assert!( + classification.candidates.is_empty(), + "{service}:{operation} near-match must not resolve to any adapter" + ); } } + #[test] + fn incompatible_optional_property_is_omitted() { + let request = request( + "iam", + "CreateRole", + serde_json::json!({ + "RoleName": "Synthetic", + "AssumeRolePolicyDocument": "{}", + "Tags": {"Key": 42} + }), + ); + let (_, _, document) = synthesized_json(&request); + let properties = &document["Resources"]["Resource"]["Properties"]; + assert_eq!(properties["RoleName"], "Synthetic"); + assert!(properties.get("Tags").is_none() || properties["Tags"].is_null()); + } #[test] fn high_level_api_validates_exact_template_and_reports_skips() { @@ -1238,8 +1864,10 @@ mod tests { ("AttributeDefinitions".into(), value(serde_json::json!([{"AttributeName": "id", "AttributeType": "S"}]))), ]); let original = parameters.clone(); - let request = AwsApiRequest::new("dynamodb", "CreateTable", parameters).with_service_prefix("dynamodb"); - let _ = synthesized_json(&request); + let request = AwsApiRequest::new("dynamodb", "CreateTable", parameters); + let schema_validator = SchemaValidator::default(); + let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); + let _ = synthesize_request(&request, &classification, &schema_validator); assert_eq!(request.parameters, original); } @@ -1249,4 +1877,164 @@ mod tests { assert!(AwsApiValue::Number { value: f64::NAN }.to_json().is_err()); assert!(AwsApiValue::Unsupported { type_name: "timestamp".into() }.to_json().is_err()); } + + #[test] + fn operation_words_preserve_acronyms_for_verb_classification() { + assert_eq!(operation_words("BatchCreateDB2Cluster"), ["Batch", "Create", "DB", "2", "Cluster"]); + assert_eq!(effective_verb(&operation_words("BatchCreateDB2Cluster")), "Create"); + } + + #[test] + fn normalize_service_changes_ascii_case_only() { + assert_eq!(normalize_service("s3"), "s3"); + assert_eq!(normalize_service("S3"), "s3"); + assert_eq!(normalize_service("DynamoDb"), "dynamodb"); + assert_eq!(normalize_service("dynamodb"), "dynamodb"); + assert_eq!(normalize_service("cloud-control"), "cloud-control"); + assert_eq!(normalize_service("CloudControl"), "cloudcontrol"); + assert_eq!(normalize_service("cloudcontrolapi"), "cloudcontrolapi"); + } + + #[test] + fn conflicting_service_prefix_does_not_map_adapter() { + let schema_validator = SchemaValidator::default(); + let parameters: HashMap = serde_json::json!({"Bucket": "test"}) + .as_object() + .unwrap() + .iter() + .map(|(k, v)| (k.clone(), AwsApiValue::from_json(v.clone()))) + .collect(); + let req = AwsApiRequest::new("ecs", "CreateBucket", parameters).with_service_prefix("s3"); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + assert!( + classification.candidates.is_empty(), + "service_name=ecs with service_prefix=s3 must not map CreateBucket" + ); + + let punctuated = request("s-3", "CreateBucket", serde_json::json!({"Bucket": "test"})); + let classification = classify_operation(&punctuated, &schema_validator).expect("classification succeeds"); + assert!(classification.candidates.is_empty(), "punctuated service names must not map adapters"); + } + + #[test] + fn conflicting_service_prefix_does_not_validate_template_body() { + let schema_validator = SchemaValidator::default(); + let template = br#"{"Resources":{}}"#.to_vec(); + let parameters: HashMap = + [("TemplateBody".to_string(), AwsApiValue::Bytes { value: template })].into_iter().collect(); + let req = AwsApiRequest::new("iot", "CreateStack", parameters).with_service_prefix("cloudformation"); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); + assert_ne!( + synthesis.source, + Some(AwsApiTemplateSource::TemplateBody), + "service_name=iot with service_prefix=cloudformation must not validate TemplateBody" + ); + } + + #[test] + fn arbitrary_cloudcontrol_operation_with_valid_type_name_has_no_candidates() { + let schema_validator = SchemaValidator::default(); + let req = request("cloudcontrol", "ListResources", serde_json::json!({"TypeName": "AWS::SNS::Topic"})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + assert!( + classification.candidates.is_empty(), + "arbitrary cloudcontrol operations must not produce resource_types even with valid TypeName" + ); + } + + #[test] + fn lambda_create_partial_scopes_to_mapped_properties_only() { + let engine = NoopEngine::default(); + let schema_validator = SchemaValidator::default(); + let req = request("lambda", "CreateFunction", serde_json::json!({"MemorySize": 0})); + let validation = validate_aws_api_request(&engine, &schema_validator, &req, ValidateConfig::default()) + .expect("validation succeeds"); + assert_eq!(validation.status, AwsApiRequestValidationStatus::Validated); + assert_eq!(validation.template_source, Some(AwsApiTemplateSource::SynthesizedCreate)); + let report = validation.report.expect("create is validated"); + for diagnostic in &report.diagnostics { + assert!( + diagnostic.property_path.as_deref().is_some_and(|p| p.contains("MemorySize")), + "diagnostic must be scoped to MemorySize, got: {:?}", + diagnostic.property_path + ); + } + let counts = &report.metadata.counts; + assert_eq!( + report.diagnostics.len() as u32, + counts.fatal + counts.errors + counts.warnings + counts.informational + counts.debug, + ); + } + + #[test] + fn template_body_create_operations_have_cloud_formation_create_kind() { + let schema_validator = SchemaValidator::default(); + for operation in ["CreateChangeSet", "CreateStack", "CreateStackSet"] { + let req = request("cloudformation", operation, serde_json::json!({})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + assert_eq!( + classification.kind, + AwsApiOperationKind::CloudFormationCreate, + "cloudformation:{operation} must be CloudFormationCreate" + ); + } + } + + #[test] + fn template_body_update_operations_have_cloud_formation_update_kind() { + let schema_validator = SchemaValidator::default(); + for operation in ["UpdateStack", "UpdateStackSet"] { + let req = request("cloudformation", operation, serde_json::json!({})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + assert_eq!( + classification.kind, + AwsApiOperationKind::CloudFormationUpdate, + "cloudformation:{operation} must be CloudFormationUpdate" + ); + } + } + + #[test] + fn template_body_readonly_operations_have_readonly_kind() { + let schema_validator = SchemaValidator::default(); + for operation in ["EstimateTemplateCost", "GetTemplateSummary", "ValidateTemplate"] { + let req = request("cloudformation", operation, serde_json::json!({})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + assert_eq!( + classification.kind, + AwsApiOperationKind::ReadOnly, + "cloudformation:{operation} must be ReadOnly" + ); + } + } + + #[test] + fn template_body_readonly_operations_still_validate_payload() { + let engine = NoopEngine::default(); + let schema_validator = SchemaValidator::default(); + let template = br#"{"Resources":{}}"#.to_vec(); + for operation in ["EstimateTemplateCost", "GetTemplateSummary", "ValidateTemplate"] { + let parameters: HashMap = + [("TemplateBody".to_string(), AwsApiValue::Bytes { value: template.clone() })].into_iter().collect(); + let req = AwsApiRequest::new("cloudformation", operation, parameters); + let validation = validate_aws_api_request(&engine, &schema_validator, &req, ValidateConfig::default()) + .expect("validation succeeds"); + assert_eq!( + validation.status, + AwsApiRequestValidationStatus::Validated, + "cloudformation:{operation} with TemplateBody must still validate" + ); + assert_eq!(validation.template_source, Some(AwsApiTemplateSource::TemplateBody)); + } + } + + #[test] + fn unknown_cloudformation_verbs_remain_unmapped() { + let schema_validator = SchemaValidator::default(); + let req = request("cloudformation", "DeleteChangeSet", serde_json::json!({})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + assert_eq!(classification.kind, AwsApiOperationKind::UnmappedMutation); + assert!(classification.candidates.is_empty()); + } } From 20af1464af067e9d9f3f46dc77e7096dd24f0a66 Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Tue, 18 Aug 2026 11:48:05 -0400 Subject: [PATCH 04/19] update api spec --- src/bindings-go/README.md | 64 +++++ src/bindings-go/go/cfnvalidate.go | 196 +++++++++++++ src/bindings-go/go/cfnvalidate_test.go | 76 +++++ src/bindings-go/go/types.go | 54 ++++ src/bindings-go/src/lib.rs | 187 +++++++++++++ src/bindings-go/tests/run.sh | 3 + src/bindings-go/tests/smoke_test.go | 263 ++++++++++++++++++ src/bindings-jvm/README.md | 7 +- src/bindings-jvm/src/lib.rs | 31 +-- .../amazon/cloudformation/validate/Api.kt | 37 +-- .../tests/kotlin/src/test/kotlin/SmokeTest.kt | 3 - src/bindings-python/README.md | 4 +- .../cloudformation_validate/__init__.py | 29 +- src/bindings-python/src/lib.rs | 31 +-- src/bindings-python/tests/smoke_test.py | 3 - src/validation-engine/API.md | 10 +- src/validation-engine/src/aws_api.rs | 67 +---- src/validation-engine/src/lib.rs | 3 +- 18 files changed, 888 insertions(+), 180 deletions(-) create mode 100644 src/bindings-go/go/cfnvalidate_test.go diff --git a/src/bindings-go/README.md b/src/bindings-go/README.md index c6b151ba..e08f8517 100644 --- a/src/bindings-go/README.md +++ b/src/bindings-go/README.md @@ -61,6 +61,7 @@ diagnostics for the same template and config. A `nil` config uses only the built | `ValidateStandardFile(path string, config *ValidateConfig)` | `(*StandardReport, error)` | Reads a template from disk, then validates it | | `ValidateDetailed(template []byte, config *ValidateConfig, filePath string)` | `(*DetailedReport, error)` | Validates bytes with documentation URLs, rule descriptions, phase tags, and `ViolationContext` | | `ValidateDetailedFile(path string, config *ValidateConfig)` | `(*DetailedReport, error)` | Reads a template from disk, then validates it (detailed) | +| `ValidateAWSAPIRequest(request AWSAPIRequest, config *ValidateConfig)` | `(*AWSAPIRequestValidation, error)` | Classifies and validates an AWS API request offline | | `ListRules()` | `([]RuleInfo, error)` | Returns metadata for every built-in and loaded custom rule | | `EngineName()` | `string` | `"rego"` or `"cel"` | | `Destroy()` | - | Releases the native engine; the engine must not be used afterwards | @@ -194,6 +195,69 @@ type PseudoParameterOverrides struct { } ``` +## AWS API Request Validation + +Validates an AWS API request by classifying the operation, inferring the CloudFormation resource type, and running +schema and rule validation against a synthesized template - entirely offline. The method returns classification +metadata and an optional `StandardReport` when the request was validated (not skipped for read-only operations). + +```go +engine, _ := cfnvalidate.NewRegoEngine(nil) +defer engine.Destroy() + +result, err := engine.ValidateAWSAPIRequest(cfnvalidate.AWSAPIRequest{ + ServiceName: "s3", + OperationName: "CreateBucket", + Parameters: map[string]any{"Bucket": "my-bucket"}, + HTTPMethod: "PUT", +}, nil) +if err != nil { + log.Fatal(err) +} +fmt.Printf("Kind: %s Status: %s Types: %v\n", + result.OperationKind, result.Status, result.ResourceTypes) +if result.Report != nil { + for _, d := range result.Report.Diagnostics { + fmt.Printf(" [%s] %s: %s\n", d.Severity, d.RuleID, d.Message) + } +} +``` + +### AWSAPIRequest + +```go +type AWSAPIRequest struct { + ServiceName string // AWS service (e.g. "s3", "DynamoDb") - case-insensitive + OperationName string // operation name (e.g. "CreateBucket") - case-sensitive + Parameters map[string]any // request parameters: strings, numbers, booleans, []byte, maps, slices, nil + ServicePrefix string // optional signing prefix (e.g. "cloudcontrolapi") + HTTPMethod string // optional HTTP method hint for classification + IsReadOnly *bool // explicit read-only flag - skips validation when true +} +``` + +`Parameters` values are recursively encoded into the core's tagged value representation. Supported Go types: `nil`, +`bool`, all signed/unsigned integer widths, `float32`/`float64` (finite only), `string`, `[]byte` (as byte arrays), +`time.Time` (as an RFC 3339 UTC string), `json.Number`, slices/arrays, and `map[string]any`. Integer-valued +`json.Number` inputs are preserved across the full signed and unsigned 64-bit range; integer literals outside that range +are represented as unsupported rather than rounded through `float64`. SDK-defined type aliases (e.g. +`types.InstanceType` which is `type InstanceType string`) are handled transparently via their underlying kind. +Non-finite floats, maps with non-string keys, and unsupported types are represented as `UNSUPPORTED` rather than +coerced. + +### AWSAPIRequestValidation + +```go +type AWSAPIRequestValidation struct { + OperationKind AWSAPIOperationKind // READ_ONLY, CLOUD_FORMATION_CREATE, etc. + Status AWSAPIRequestValidationStatus // VALIDATED or SKIPPED + TemplateSource *AWSAPITemplateSource // TEMPLATE_BODY, SYNTHESIZED_CREATE, etc. + ResourceTypes []string // inferred CloudFormation resource types + Reason string // human-readable explanation + Report *StandardReport // present only when Status is VALIDATED +} +``` + ## TemplateModel Parses a template into the resolved `SemanticModel` for direct inspection - the same model the engines evaluate rules diff --git a/src/bindings-go/go/cfnvalidate.go b/src/bindings-go/go/cfnvalidate.go index 3a2e0d2c..887fdf37 100644 --- a/src/bindings-go/go/cfnvalidate.go +++ b/src/bindings-go/go/cfnvalidate.go @@ -21,7 +21,12 @@ package cfnvalidate import ( "encoding/json" "fmt" + "math" "os" + "reflect" + "strconv" + "strings" + "time" bindings "github.com/aws-cloudformation/cloudformation-validate/src/bindings-go/go/internal/bindings_go" ) @@ -77,6 +82,7 @@ func decodeInto[T any](data string, what string) (*T, error) { type nativeEngine interface { ValidateStandardJson(template []byte, optionsJson string, filePath string) (string, error) ValidateDetailedJson(template []byte, optionsJson string, filePath string) (string, error) + ValidateAwsApiRequestJson(requestJson string, optionsJson string) (string, error) ListRulesJson() (string, error) EngineName() string Destroy() @@ -195,6 +201,196 @@ func (e *Engine) Destroy() { e.inner.Destroy() } +// ValidateAWSAPIRequest classifies and validates an AWS API request against +// CloudFormation schemas and rules entirely offline. The result contains +// operation classification, resource type inference, and an optional +// StandardReport when the request was validated (not skipped). +func (e *Engine) ValidateAWSAPIRequest(request AWSAPIRequest, config *ValidateConfig) (*AWSAPIRequestValidation, error) { + optionsJSON, err := validateConfigJSON(config) + if err != nil { + return nil, err + } + requestJSON, err := marshalAWSAPIRequest(request) + if err != nil { + return nil, err + } + data, err := e.inner.ValidateAwsApiRequestJson(requestJSON, optionsJSON) + if err != nil { + return nil, err + } + return decodeInto[AWSAPIRequestValidation](data, "AWS API request validation") +} + +// marshalAWSAPIRequest encodes an AWSAPIRequest into the wire JSON that the +// Rust side expects, converting Go parameter values into tagged AwsApiValue +// objects. +func marshalAWSAPIRequest(request AWSAPIRequest) (string, error) { + wire := awsApiRequestWire{ + ServiceName: request.ServiceName, + OperationName: request.OperationName, + Parameters: make(map[string]awsApiValue, len(request.Parameters)), + ServicePrefix: nilIfEmpty(request.ServicePrefix), + HTTPMethod: nilIfEmpty(request.HTTPMethod), + IsReadOnly: request.IsReadOnly, + } + for key, value := range request.Parameters { + wire.Parameters[key] = encodeAwsApiValue(value, 0) + } + data, err := json.Marshal(wire) + if err != nil { + return "", fmt.Errorf("cfnvalidate: encoding AWS API request: %w", err) + } + return string(data), nil +} + +func nilIfEmpty(s string) *string { + if s == "" { + return nil + } + return &s +} + +// awsApiRequestWire is the JSON structure consumed by the Rust wire parser. +type awsApiRequestWire struct { + ServiceName string `json:"serviceName"` + OperationName string `json:"operationName"` + Parameters map[string]awsApiValue `json:"parameters"` + ServicePrefix *string `json:"servicePrefix,omitempty"` + HTTPMethod *string `json:"httpMethod,omitempty"` + IsReadOnly *bool `json:"isReadOnly,omitempty"` +} + +// awsApiValue is the tagged union wire format matching the core AwsApiValue +// serde representation (tag = "type", rename_all = "SCREAMING_SNAKE_CASE"). +// Items and Entries use pointer fields so that empty slices/maps serialize as +// their JSON zero ([] / {}) while remaining absent for unrelated variants. +type awsApiValue struct { + Type string `json:"type"` + Value any `json:"value,omitempty"` + Items *[]awsApiValue `json:"items,omitempty"` + Entries *map[string]awsApiValue `json:"entries,omitempty"` + TypeName string `json:"type_name,omitempty"` +} + +// maxEncodeDepth prevents stack overflow on cyclic or deeply nested structures. +const maxEncodeDepth = 64 + +// encodeAwsApiValue recursively converts a Go value into the tagged wire +// format. It is non-mutating: no pointer is followed through a write path. +// Unsupported types are represented as UNSUPPORTED rather than coerced. +// +// SDK-defined type aliases (e.g. types.InstanceType is a named string) are +// handled via reflect.Kind after concrete type checks, so any alias of a +// scalar kind is encoded correctly without enumerating every SDK type. +func encodeAwsApiValue(v any, depth int) awsApiValue { + if depth > maxEncodeDepth { + return awsApiValue{Type: "UNSUPPORTED", TypeName: "recursion depth exceeded"} + } + if v == nil { + return awsApiValue{Type: "NULL"} + } + + // Unwrap interface and pointer layers. Count indirections separately because + // a pointer-to-interface cycle can otherwise loop before recursive + // collection encoding reaches the depth guard. + rv := reflect.ValueOf(v) + indirections := 0 + for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface { + if depth+indirections > maxEncodeDepth { + return awsApiValue{Type: "UNSUPPORTED", TypeName: "recursion depth exceeded"} + } + if rv.IsNil() { + return awsApiValue{Type: "NULL"} + } + rv = rv.Elem() + indirections++ + } + v = rv.Interface() + + // Concrete type checks for stdlib types that carry semantics beyond their + // underlying kind (time.Time and json.Number). + switch val := v.(type) { + case time.Time: + return awsApiValue{Type: "STRING", Value: val.UTC().Format(time.RFC3339Nano)} + + case json.Number: + text := string(val) + if i, err := val.Int64(); err == nil { + return awsApiValue{Type: "INTEGER", Value: i} + } + if u, err := strconv.ParseUint(text, 10, 64); err == nil { + return awsApiValue{Type: "UNSIGNED_INTEGER", Value: u} + } + if !strings.ContainsAny(text, ".eE") && json.Valid([]byte(text)) { + return awsApiValue{Type: "UNSUPPORTED", TypeName: "integer outside 64-bit range"} + } + if f, err := val.Float64(); err == nil { + if math.IsInf(f, 0) || math.IsNaN(f) { + return awsApiValue{Type: "UNSUPPORTED", TypeName: "non-finite floating-point number"} + } + return awsApiValue{Type: "NUMBER", Value: f} + } + return awsApiValue{Type: "UNSUPPORTED", TypeName: "unparseable json.Number"} + } + + // Kind-based handling covers both built-in types and SDK-defined aliases + // (e.g. types.InstanceType is `type InstanceType string`). + switch rv.Kind() { + case reflect.Bool: + return awsApiValue{Type: "BOOLEAN", Value: rv.Bool()} + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return awsApiValue{Type: "INTEGER", Value: rv.Int()} + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return awsApiValue{Type: "UNSIGNED_INTEGER", Value: rv.Uint()} + + case reflect.Float32, reflect.Float64: + f := rv.Float() + if math.IsInf(f, 0) || math.IsNaN(f) { + return awsApiValue{Type: "UNSUPPORTED", TypeName: "non-finite floating-point number"} + } + return awsApiValue{Type: "NUMBER", Value: f} + + case reflect.String: + return awsApiValue{Type: "STRING", Value: rv.String()} + + case reflect.Slice, reflect.Array: + // []byte / [N]byte → BYTES, encoded as a JSON integer array so the + // Rust side receives Vec from serde (encoding/json marshals + // []byte as base64 which is incompatible with serde's Vec). + if rv.Type().Elem().Kind() == reflect.Uint8 { + ints := make([]int, rv.Len()) + for i := range ints { + ints[i] = int(rv.Index(i).Uint()) + } + return awsApiValue{Type: "BYTES", Value: ints} + } + items := make([]awsApiValue, rv.Len()) + for i := range items { + items[i] = encodeAwsApiValue(rv.Index(i).Interface(), depth+1) + } + return awsApiValue{Type: "ARRAY", Items: &items} + + case reflect.Map: + if rv.Type().Key().Kind() != reflect.String { + return awsApiValue{Type: "UNSUPPORTED", TypeName: "mapping with non-string keys"} + } + entries := make(map[string]awsApiValue, rv.Len()) + iter := rv.MapRange() + for iter.Next() { + entries[iter.Key().String()] = encodeAwsApiValue(iter.Value().Interface(), depth+1) + } + return awsApiValue{Type: "OBJECT", Entries: &entries} + + case reflect.Struct: + return awsApiValue{Type: "UNSUPPORTED", TypeName: rv.Type().String()} + + default: + return awsApiValue{Type: "UNSUPPORTED", TypeName: rv.Type().String()} + } +} + // SchemaValidator validates resources against the compiled CloudFormation // provider schemas. type SchemaValidator struct { diff --git a/src/bindings-go/go/cfnvalidate_test.go b/src/bindings-go/go/cfnvalidate_test.go new file mode 100644 index 00000000..5feb33f7 --- /dev/null +++ b/src/bindings-go/go/cfnvalidate_test.go @@ -0,0 +1,76 @@ +package cfnvalidate + +import ( + "encoding/json" + "strings" + "testing" + "time" +) + +func TestMarshalAWSAPIRequestFormatsTimeAsRFC3339UTC(t *testing.T) { + timestamp := time.Date(2025, time.January, 2, 3, 4, 5, 123456789, time.FixedZone("UTC+2", 2*60*60)) + + encoded := encodedAWSAPIParameter(t, timestamp) + + if got := encoded["type"]; got != "STRING" { + t.Fatalf("type = %v, want STRING", got) + } + if got := encoded["value"]; got != "2025-01-02T01:04:05.123456789Z" { + t.Errorf("value = %v, want RFC3339 UTC timestamp", got) + } +} + +func TestMarshalAWSAPIRequestPreservesUnsignedJSONNumber(t *testing.T) { + encoded := encodedAWSAPIParameter(t, json.Number("18446744073709551615")) + + if got := encoded["type"]; got != "UNSIGNED_INTEGER" { + t.Fatalf("type = %v, want UNSIGNED_INTEGER", got) + } + value, ok := encoded["value"].(json.Number) + if !ok { + t.Fatalf("value type = %T, want json.Number", encoded["value"]) + } + if got := value.String(); got != "18446744073709551615" { + t.Errorf("value = %s, want exact uint64 maximum", got) + } +} + +func TestMarshalAWSAPIRequestMarksOutOfRangeIntegerUnsupported(t *testing.T) { + encoded := encodedAWSAPIParameter(t, json.Number("18446744073709551616")) + + if got := encoded["type"]; got != "UNSUPPORTED" { + t.Fatalf("type = %v, want UNSUPPORTED", got) + } + if got := encoded["type_name"]; got != "integer outside 64-bit range" { + t.Errorf("type_name = %v, want integer outside 64-bit range", got) + } + if _, ok := encoded["value"]; ok { + t.Error("UNSUPPORTED value must not contain a numeric value") + } +} + +func encodedAWSAPIParameter(t *testing.T, value any) map[string]any { + t.Helper() + requestJSON, err := marshalAWSAPIRequest(AWSAPIRequest{ + ServiceName: "test", + OperationName: "TestOperation", + Parameters: map[string]any{"Value": value}, + }) + if err != nil { + t.Fatalf("marshalAWSAPIRequest failed: %v", err) + } + + decoder := json.NewDecoder(strings.NewReader(requestJSON)) + decoder.UseNumber() + var wire struct { + Parameters map[string]map[string]any `json:"parameters"` + } + if err := decoder.Decode(&wire); err != nil { + t.Fatalf("decoding request wire JSON failed: %v", err) + } + encoded, ok := wire.Parameters["Value"] + if !ok { + t.Fatal("encoded request is missing the Value parameter") + } + return encoded +} diff --git a/src/bindings-go/go/types.go b/src/bindings-go/go/types.go index 0262cf49..ced2af66 100644 --- a/src/bindings-go/go/types.go +++ b/src/bindings-go/go/types.go @@ -287,3 +287,57 @@ type ValidateConfig struct { Strict *bool `json:"strict,omitempty"` DisableBuiltinRules *bool `json:"disableBuiltinRules,omitempty"` } + +// AWSAPIRequest holds an AWS API service call for offline CloudFormation +// validation. ServiceName and OperationName identify the API; Parameters carry +// the request values (maps, strings, numbers, booleans, byte slices, etc.). +type AWSAPIRequest struct { + ServiceName string `json:"serviceName"` + OperationName string `json:"operationName"` + Parameters map[string]any `json:"parameters"` + ServicePrefix string `json:"servicePrefix,omitempty"` + HTTPMethod string `json:"httpMethod,omitempty"` + IsReadOnly *bool `json:"isReadOnly,omitempty"` +} + +// AWSAPIOperationKind classifies an AWS API operation. +type AWSAPIOperationKind string + +const ( + AWSAPIOperationKindReadOnly AWSAPIOperationKind = "READ_ONLY" + AWSAPIOperationKindCloudFormationCreate AWSAPIOperationKind = "CLOUD_FORMATION_CREATE" + AWSAPIOperationKindCloudFormationUpdate AWSAPIOperationKind = "CLOUD_FORMATION_UPDATE" + AWSAPIOperationKindCloudFormationDelete AWSAPIOperationKind = "CLOUD_FORMATION_DELETE" + AWSAPIOperationKindDataPlaneMutation AWSAPIOperationKind = "DATA_PLANE_MUTATION" + AWSAPIOperationKindUnmappedMutation AWSAPIOperationKind = "UNMAPPED_MUTATION" +) + +// AWSAPIRequestValidationStatus indicates whether validation ran or was skipped. +type AWSAPIRequestValidationStatus string + +const ( + AWSAPIRequestValidationStatusValidated AWSAPIRequestValidationStatus = "VALIDATED" + AWSAPIRequestValidationStatusSkipped AWSAPIRequestValidationStatus = "SKIPPED" +) + +// AWSAPITemplateSource identifies the provenance of the template validated for +// an API request. +type AWSAPITemplateSource string + +const ( + AWSAPITemplateSourceTemplateBody AWSAPITemplateSource = "TEMPLATE_BODY" + AWSAPITemplateSourceCloudControlDesiredState AWSAPITemplateSource = "CLOUD_CONTROL_DESIRED_STATE" + AWSAPITemplateSourceSynthesizedCreate AWSAPITemplateSource = "SYNTHESIZED_CREATE" + AWSAPITemplateSourceSynthesizedUpdate AWSAPITemplateSource = "SYNTHESIZED_UPDATE" +) + +// AWSAPIRequestValidation is the canonical result of validating an AWS API +// request. Report is present only when Status is VALIDATED. +type AWSAPIRequestValidation struct { + OperationKind AWSAPIOperationKind `json:"operationKind"` + Status AWSAPIRequestValidationStatus `json:"status"` + TemplateSource *AWSAPITemplateSource `json:"templateSource,omitempty"` + ResourceTypes []string `json:"resourceTypes"` + Reason string `json:"reason"` + Report *StandardReport `json:"report,omitempty"` +} diff --git a/src/bindings-go/src/lib.rs b/src/bindings-go/src/lib.rs index 00cbbb87..3c48712b 100644 --- a/src/bindings-go/src/lib.rs +++ b/src/bindings-go/src/lib.rs @@ -177,6 +177,42 @@ fn to_json(value: &T) -> Result { serde_json::to_string(value).map_err(|e| ValidationError::new(format!("failed to serialize result: {e}"))) } +/// Wire struct for an AWS API request received from Go as JSON. +/// +/// Field names match the Go `AWSAPIRequest` struct's `json` tags exactly. +/// Unknown fields are rejected so a drifted field name surfaces as an error +/// instead of silently ignoring the caller's intent. +#[derive(Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct AwsApiRequestWire { + service_name: String, + operation_name: String, + parameters: HashMap, + #[serde(default)] + service_prefix: Option, + #[serde(default)] + http_method: Option, + #[serde(default)] + is_read_only: Option, +} + +impl AwsApiRequestWire { + fn parse(json: &str) -> Result { + serde_json::from_str(json).map_err(|e| ValidationError::new(format!("invalid AWS API request JSON: {e}"))) + } + + fn into_context(self) -> validation_engine::AwsApiRequestContext { + validation_engine::AwsApiRequestContext { + service_name: self.service_name, + operation_name: self.operation_name, + parameters: self.parameters, + service_prefix: self.service_prefix, + http_method: self.http_method, + is_read_only: self.is_read_only, + } + } +} + #[derive(uniffi::Object)] pub struct GoSchemaValidator { inner: schema_validator::SchemaValidator, @@ -312,6 +348,29 @@ macro_rules! impl_go_engine { ) } + /// Validates an AWS API request and returns the canonical result as JSON. + pub fn validate_aws_api_request_json( + &self, + request_json: String, + options_json: String, + ) -> Result { + catch_panics( + || { + let request = AwsApiRequestWire::parse(&request_json)?.into_context(); + let config = ValidateOptions::parse(&options_json)?.to_core(DetailLevel::Standard); + let result = validation_engine::validate_aws_api_request( + &self.engine, + &self.schema_validator, + &request, + config, + ) + .map_err(ValidationError::new)?; + to_json(&result) + }, + panic_to_error, + ) + } + /// Returns the engine's rules as a JSON array of rule infos. pub fn list_rules_json(&self) -> Result { catch_panics(|| to_json(&self.engine.list_rules()), panic_to_error) @@ -616,4 +675,132 @@ mod tests { "error must identify the failing input: {error}" ); } + + #[test] + fn aws_api_request_parses_valid_minimal_request() { + let wire = AwsApiRequestWire::parse( + r#"{"serviceName":"s3","operationName":"CreateBucket","parameters":{"Bucket":{"type":"STRING","value":"test"}}}"#, + ) + .expect("valid minimal request must parse"); + + assert_eq!(wire.service_name, "s3"); + assert_eq!(wire.operation_name, "CreateBucket"); + assert!(wire.parameters.contains_key("Bucket")); + assert_eq!(wire.service_prefix, None); + assert_eq!(wire.http_method, None); + assert_eq!(wire.is_read_only, None); + } + + #[test] + fn aws_api_request_parses_nested_values_and_bytes() { + let wire = AwsApiRequestWire::parse( + r#"{ + "serviceName": "cloudformation", + "operationName": "CreateStack", + "parameters": { + "TemplateBody": {"type": "BYTES", "value": [123, 125]}, + "Tags": {"type": "ARRAY", "items": [ + {"type": "OBJECT", "entries": {"Key": {"type": "STRING", "value": "env"}}} + ]}, + "Count": {"type": "INTEGER", "value": 42} + }, + "servicePrefix": "cloudformation", + "httpMethod": "POST", + "isReadOnly": false + }"#, + ) + .expect("nested request must parse"); + + assert_eq!(wire.service_name, "cloudformation"); + assert_eq!(wire.service_prefix, Some("cloudformation".to_string())); + assert_eq!(wire.http_method, Some("POST".to_string())); + assert_eq!(wire.is_read_only, Some(false)); + + let context = wire.into_context(); + match context.parameters.get("TemplateBody") { + Some(validation_engine::AwsApiValue::Bytes { value }) => assert_eq!(value, &[123, 125]), + other => panic!("expected Bytes, got {other:?}"), + } + match context.parameters.get("Count") { + Some(validation_engine::AwsApiValue::Integer { value }) => assert_eq!(*value, 42), + other => panic!("expected Integer, got {other:?}"), + } + } + + #[test] + fn aws_api_request_rejects_malformed_json() { + let error = AwsApiRequestWire::parse("not json").expect_err("malformed JSON must fail"); + assert!( + error.to_string().contains("invalid AWS API request JSON"), + "error must identify the failing input: {error}" + ); + } + + #[test] + fn aws_api_request_rejects_unknown_fields() { + let error = AwsApiRequestWire::parse( + r#"{"serviceName":"s3","operationName":"CreateBucket","parameters":{},"unknownField":"x"}"#, + ) + .expect_err("unknown field must fail"); + assert!(error.to_string().contains("unknownField"), "error must name the offending key: {error}"); + } + + #[test] + fn aws_api_request_rejects_missing_required_fields() { + let error = + AwsApiRequestWire::parse(r#"{"serviceName":"s3"}"#).expect_err("missing required operationName must fail"); + assert!(error.to_string().contains("operationName"), "error must name the missing field: {error}"); + } + + #[test] + fn aws_api_value_unsupported_uses_type_name_field() { + let json = r#"{"type":"UNSUPPORTED","type_name":"non-finite floating-point number"}"#; + let value: validation_engine::AwsApiValue = + serde_json::from_str(json).expect("UNSUPPORTED with type_name must parse"); + match value { + validation_engine::AwsApiValue::Unsupported { type_name } => { + assert_eq!(type_name, "non-finite floating-point number"); + } + other => panic!("expected Unsupported, got {other:?}"), + } + } + + #[test] + fn aws_api_value_bytes_parses_integer_array() { + let json = r#"{"type":"BYTES","value":[72,101,108,108,111]}"#; + let value: validation_engine::AwsApiValue = + serde_json::from_str(json).expect("BYTES with integer array must parse"); + match value { + validation_engine::AwsApiValue::Bytes { value } => { + assert_eq!(value, vec![72, 101, 108, 108, 111]); + } + other => panic!("expected Bytes, got {other:?}"), + } + } + + #[test] + fn aws_api_value_empty_array_has_items_field() { + let json = r#"{"type":"ARRAY","items":[]}"#; + let value: validation_engine::AwsApiValue = + serde_json::from_str(json).expect("ARRAY with empty items must parse"); + match value { + validation_engine::AwsApiValue::Array { items } => { + assert!(items.is_empty()); + } + other => panic!("expected Array, got {other:?}"), + } + } + + #[test] + fn aws_api_value_empty_object_has_entries_field() { + let json = r#"{"type":"OBJECT","entries":{}}"#; + let value: validation_engine::AwsApiValue = + serde_json::from_str(json).expect("OBJECT with empty entries must parse"); + match value { + validation_engine::AwsApiValue::Object { entries } => { + assert!(entries.is_empty()); + } + other => panic!("expected Object, got {other:?}"), + } + } } diff --git a/src/bindings-go/tests/run.sh b/src/bindings-go/tests/run.sh index 5c356895..dab2274f 100755 --- a/src/bindings-go/tests/run.sh +++ b/src/bindings-go/tests/run.sh @@ -9,6 +9,9 @@ GO_MODULE="github.com/aws-cloudformation/cloudformation-validate/src/bindings-go [ -d "$GO_DIR/internal/bindings_go" ] && compgen -G "$GO_DIR/libs/*/libbindings_go.a" >/dev/null \ || { echo "Error: generated bindings or static library missing - run build.sh first" >&2; exit 1; } +echo "Running Go module unit tests..." +(cd "$GO_DIR" && go test ./...) + echo "Running smoke tests with coverage..." cd "$SCRIPT_DIR" go test -v -covermode=atomic -coverpkg="$GO_MODULE" -coverprofile="$SCRIPT_DIR/coverage.out" ./... diff --git a/src/bindings-go/tests/smoke_test.go b/src/bindings-go/tests/smoke_test.go index bba97c13..9eeb4e18 100644 --- a/src/bindings-go/tests/smoke_test.go +++ b/src/bindings-go/tests/smoke_test.go @@ -9,6 +9,7 @@ package cfnvalidate_test import ( "encoding/json" "fmt" + "math" "os" "path/filepath" "regexp" @@ -480,3 +481,265 @@ func TestErrorsSurfaceAsGoErrors(t *testing.T) { t.Error("invalid custom rule must fail engine construction") } } + +// --- AWS API Request Validation tests --- + +func TestAWSAPIRequestS3CreateWithBothEnginesAndParity(t *testing.T) { + request := cfnvalidate.AWSAPIRequest{ + ServiceName: "s3", + OperationName: "CreateBucket", + Parameters: map[string]any{"Bucket": "my-test-bucket"}, + HTTPMethod: "PUT", + } + originalParams := map[string]any{"Bucket": "my-test-bucket"} + + results := map[string]*cfnvalidate.AWSAPIRequestValidation{} + for name, engine := range bothEngines(t) { + result, err := engine.ValidateAWSAPIRequest(request, nil) + if err != nil { + t.Fatalf("%s: ValidateAWSAPIRequest failed: %v", name, err) + } + results[name] = result + + if result.OperationKind != cfnvalidate.AWSAPIOperationKindCloudFormationCreate { + t.Errorf("%s: operationKind = %s, want CLOUD_FORMATION_CREATE", name, result.OperationKind) + } + if result.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { + t.Errorf("%s: status = %s, want VALIDATED", name, result.Status) + } + if len(result.ResourceTypes) == 0 || result.ResourceTypes[0] != "AWS::S3::Bucket" { + t.Errorf("%s: resourceTypes = %v, want [AWS::S3::Bucket]", name, result.ResourceTypes) + } + if result.Report == nil { + t.Fatalf("%s: report must be present for a validated request", name) + } + if result.Report.Status != cfnvalidate.StatusOK { + t.Errorf("%s: report.Status = %s, want OK", name, result.Report.Status) + } + } + + // Verify engine parity. + if results["rego"] != nil && results["cel"] != nil { + regoKeys := diagnosticKeys(results["rego"].Report) + celKeys := diagnosticKeys(results["cel"].Report) + if !equalStrings(regoKeys, celKeys) { + t.Errorf("engines disagree on AWS API diagnostics:\nrego: %v\ncel: %v", regoKeys, celKeys) + } + } + + // Verify input non-mutation. + if len(request.Parameters) != len(originalParams) { + t.Errorf("request.Parameters mutated: len changed from %d to %d", len(originalParams), len(request.Parameters)) + } + for key, want := range originalParams { + if got, ok := request.Parameters[key]; !ok || got != want { + t.Errorf("request.Parameters[%q] mutated: got %v, want %v", key, got, want) + } + } +} + +func TestAWSAPIRequestCloudFormationTemplateBodyBytes(t *testing.T) { + template := []byte(`{"AWSTemplateFormatVersion":"2010-09-09","Resources":{"Bucket":{"Type":"AWS::S3::Bucket","Properties":{"BucketName":"exact-body"}}}}`) + request := cfnvalidate.AWSAPIRequest{ + ServiceName: "cloudformation", + OperationName: "CreateStack", + Parameters: map[string]any{"TemplateBody": template}, + HTTPMethod: "POST", + } + + engine := mustEngine(t, cfnvalidate.NewRegoEngine, nil) + result, err := engine.ValidateAWSAPIRequest(request, nil) + if err != nil { + t.Fatalf("ValidateAWSAPIRequest failed: %v", err) + } + + if result.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { + t.Errorf("status = %s, want VALIDATED", result.Status) + } + if result.TemplateSource == nil || *result.TemplateSource != cfnvalidate.AWSAPITemplateSourceTemplateBody { + t.Errorf("templateSource = %v, want TEMPLATE_BODY", result.TemplateSource) + } + if result.Report == nil { + t.Fatal("report must be present for a validated template body") + } + if result.Report.Status != cfnvalidate.StatusOK { + t.Errorf("report.Status = %s, want OK", result.Report.Status) + } +} + +func TestAWSAPIRequestReadOnlySkips(t *testing.T) { + readOnly := true + request := cfnvalidate.AWSAPIRequest{ + ServiceName: "iam", + OperationName: "GetRole", + Parameters: map[string]any{"RoleName": "test-role"}, + IsReadOnly: &readOnly, + } + + engine := mustEngine(t, cfnvalidate.NewCelEngine, nil) + result, err := engine.ValidateAWSAPIRequest(request, nil) + if err != nil { + t.Fatalf("ValidateAWSAPIRequest failed: %v", err) + } + + if result.OperationKind != cfnvalidate.AWSAPIOperationKindReadOnly { + t.Errorf("operationKind = %s, want READ_ONLY", result.OperationKind) + } + if result.Status != cfnvalidate.AWSAPIRequestValidationStatusSkipped { + t.Errorf("status = %s, want SKIPPED", result.Status) + } + if result.Report != nil { + t.Errorf("report must be nil for a skipped request, got %+v", result.Report) + } +} + +func TestAWSAPIRequestUnregisteredOperationNoInferredTypes(t *testing.T) { + request := cfnvalidate.AWSAPIRequest{ + ServiceName: "customservice", + OperationName: "DoSomethingUnknown", + Parameters: map[string]any{"Key": "value"}, + HTTPMethod: "POST", + } + + engine := mustEngine(t, cfnvalidate.NewRegoEngine, nil) + result, err := engine.ValidateAWSAPIRequest(request, nil) + if err != nil { + t.Fatalf("ValidateAWSAPIRequest failed: %v", err) + } + + if len(result.ResourceTypes) != 0 { + t.Errorf("resourceTypes = %v, want empty for unregistered operation", result.ResourceTypes) + } + if result.Report != nil { + t.Errorf("report must be nil for an unmapped operation that cannot synthesize, got non-nil") + } +} + +func TestAWSAPIRequestSDKServiceCasing(t *testing.T) { + request := cfnvalidate.AWSAPIRequest{ + ServiceName: "DynamoDb", + OperationName: "CreateTable", + Parameters: map[string]any{ + "TableName": "PascalCased", + "KeySchema": []any{map[string]any{"AttributeName": "id", "KeyType": "HASH"}}, + "AttributeDefinitions": []any{map[string]any{"AttributeName": "id", "AttributeType": "S"}}, + "BillingMode": "PAY_PER_REQUEST", + }, + HTTPMethod: "POST", + } + + engine := mustEngine(t, cfnvalidate.NewCelEngine, nil) + result, err := engine.ValidateAWSAPIRequest(request, nil) + if err != nil { + t.Fatalf("ValidateAWSAPIRequest failed: %v", err) + } + + if result.OperationKind != cfnvalidate.AWSAPIOperationKindCloudFormationCreate { + t.Errorf("operationKind = %s, want CLOUD_FORMATION_CREATE", result.OperationKind) + } + if len(result.ResourceTypes) == 0 || result.ResourceTypes[0] != "AWS::DynamoDB::Table" { + t.Errorf("resourceTypes = %v, want [AWS::DynamoDB::Table]", result.ResourceTypes) + } +} + +func TestAWSAPIRequestEC2MappedDiagnostic(t *testing.T) { + // Use a defined string alias to exercise the scalar alias encoding path. + // InstanceInitiatedShutdownBehavior has a real enum constraint in the + // EC2 schema, so an invalid value triggers a diagnostic reliably. + type ShutdownBehavior string + request := cfnvalidate.AWSAPIRequest{ + ServiceName: "ec2", + OperationName: "RunInstances", + Parameters: map[string]any{ + "ImageId": "ami-12345678", + "InstanceInitiatedShutdownBehavior": ShutdownBehavior("invalid"), + }, + HTTPMethod: "POST", + } + + for name, engine := range bothEngines(t) { + result, err := engine.ValidateAWSAPIRequest(request, nil) + if err != nil { + t.Fatalf("%s: ValidateAWSAPIRequest failed: %v", name, err) + } + if result.OperationKind != cfnvalidate.AWSAPIOperationKindCloudFormationCreate { + t.Errorf("%s: operationKind = %s, want CLOUD_FORMATION_CREATE", name, result.OperationKind) + } + if len(result.ResourceTypes) == 0 || result.ResourceTypes[0] != "AWS::EC2::Instance" { + t.Errorf("%s: resourceTypes = %v, want [AWS::EC2::Instance]", name, result.ResourceTypes) + } + if result.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { + t.Errorf("%s: status = %s, want VALIDATED", name, result.Status) + } + if result.Report == nil { + t.Fatalf("%s: report must be present", name) + } + hasDiagnostic := false + for _, d := range result.Report.Diagnostics { + if strings.Contains(d.Message, "InstanceInitiatedShutdownBehavior") || (d.PropertyPath != nil && strings.Contains(*d.PropertyPath, "InstanceInitiatedShutdownBehavior")) { + hasDiagnostic = true + break + } + } + if !hasDiagnostic { + t.Errorf("%s: expected at least one diagnostic about InstanceInitiatedShutdownBehavior, got %d total diagnostics", name, len(result.Report.Diagnostics)) + } + } +} + +func TestAWSAPIRequestEmptyArrayAndObjectParsed(t *testing.T) { + // Empty ARRAY must serialize items:[] and empty OBJECT must serialize + // entries:{} — the Rust parser rejects their absence. + request := cfnvalidate.AWSAPIRequest{ + ServiceName: "ec2", + OperationName: "RunInstances", + Parameters: map[string]any{ + "ImageId": "ami-12345678", + "TagSpecifications": []any{}, + "TagSet": map[string]any{}, + }, + HTTPMethod: "POST", + } + + engine := mustEngine(t, cfnvalidate.NewRegoEngine, nil) + result, err := engine.ValidateAWSAPIRequest(request, nil) + if err != nil { + t.Fatalf("ValidateAWSAPIRequest failed with empty array/object parameters: %v", err) + } + // The request must not cause a JSON parse failure on the Rust side — it + // should be accepted and processed (status VALIDATED, not an error). + if result.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { + t.Errorf("status = %s, want VALIDATED", result.Status) + } +} + +func TestAWSAPIRequestUnsupportedValueDoesNotCauseParseFailure(t *testing.T) { + // Non-finite floating-point values, non-string-key maps, and cyclic + // indirection are conservatively represented as UNSUPPORTED rather than + // causing a JSON parse failure or looping in the Go encoder. + var cyclic any + cyclic = &cyclic + request := cfnvalidate.AWSAPIRequest{ + ServiceName: "ec2", + OperationName: "RunInstances", + Parameters: map[string]any{ + "ImageId": "ami-12345678", + "BadFloat": math.Inf(1), + "BadMapKey": map[int]string{1: "one"}, + "Cyclic": cyclic, + }, + HTTPMethod: "POST", + } + + engine := mustEngine(t, cfnvalidate.NewCelEngine, nil) + result, err := engine.ValidateAWSAPIRequest(request, nil) + if err != nil { + t.Fatalf("ValidateAWSAPIRequest must not fail for unsupported values: %v", err) + } + // The request crosses the FFI boundary without a JSON parse error — + // unsupported values are encoded as {"type":"UNSUPPORTED","type_name":"..."} + // which the Rust parser accepts. + if result.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { + t.Errorf("status = %s, want VALIDATED (unsupported values are carried, not rejected)", result.Status) + } +} diff --git a/src/bindings-jvm/README.md b/src/bindings-jvm/README.md index 1a0c107b..117d7666 100644 --- a/src/bindings-jvm/README.md +++ b/src/bindings-jvm/README.md @@ -103,8 +103,7 @@ result.report?.diagnostics?.forEach { diagnostic -> `AwsApiRequest.parameters` accepts nested maps, iterables and arrays, scalars, byte arrays, and Java temporal values without mutating the supplied map. `TemplateBody` bytes are validated exactly; `TemplateURL` is skipped because the validator does not perform network requests. Every result reports `status`, `operationKind`, `templateSource`, -`resourceTypes`, and `reason`; skipped requests have a null `report`. `validateAwsApiRequestStandard` returns standard -diagnostics, while `validateAwsApiRequestDetailed` and its `validateAwsApiRequest` alias return detailed diagnostics. +`resourceTypes`, and `reason`; skipped requests have a null `report`. The same classes and methods are callable from Java with conventional generated getters. Operation-to-resource mapping uses a deterministic closed adapter catalog generated from each resource type's own @@ -137,14 +136,14 @@ import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.cloudformation.validate.AwsApiRequest; import software.amazon.cloudformation.validate.RegoEngine; import software.amazon.cloudformation.validate.ValidateConfig; -import software.amazon.cloudformation.validate.engine.DetailedAwsApiRequestValidation; +import software.amazon.cloudformation.validate.engine.AwsApiRequestValidation; public final class CloudFormationValidationInterceptor implements ExecutionInterceptor { private final RegoEngine engine = new RegoEngine(); @Override public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes attributes) { - DetailedAwsApiRequestValidation result = engine.validateAwsApiRequest( + AwsApiRequestValidation result = engine.validateAwsApiRequest( new AwsApiRequest( attributes.getAttribute(SdkExecutionAttribute.SERVICE_NAME), attributes.getAttribute(SdkExecutionAttribute.OPERATION_NAME), diff --git a/src/bindings-jvm/src/lib.rs b/src/bindings-jvm/src/lib.rs index e3508e94..84c24622 100644 --- a/src/bindings-jvm/src/lib.rs +++ b/src/bindings-jvm/src/lib.rs @@ -23,8 +23,8 @@ pub use template_model::model::{ pub use template_model::resolver::{MapEntry, ParameterInfo, RefKind, ResolvedValue}; pub use template_model::{JsonValue, PseudoParameterOverrides, SourceSpan}; pub use validation_engine::{ - AwsApiOperationKind, AwsApiRequestContext, AwsApiRequestValidationStatus, AwsApiTemplateSource, AwsApiValue, - DetailedAwsApiRequestValidation, EngineConfig, EngineType, ExternalRuleSource, StandardAwsApiRequestValidation, + AwsApiOperationKind, AwsApiRequestContext, AwsApiRequestValidation, AwsApiRequestValidationStatus, + AwsApiTemplateSource, AwsApiValue, EngineConfig, EngineType, ExternalRuleSource, }; pub use schema_validator::SchemaValidatorConfig; @@ -200,11 +200,11 @@ macro_rules! impl_jvm_engine { ) } - pub fn validate_aws_api_request_standard( + pub fn validate_aws_api_request( &self, request: AwsApiRequestContext, config: ValidateConfig, - ) -> Result { + ) -> Result { validation_engine::catch_panics( || { let core_config = config.to_core(DetailLevel::Standard); @@ -215,28 +215,7 @@ macro_rules! impl_jvm_engine { core_config, ) .map_err(|e| ValidationError::Engine { msg: e.to_string() })?; - Ok(validation.to_standard()) - }, - panic_to_error, - ) - } - - pub fn validate_aws_api_request_detailed( - &self, - request: AwsApiRequestContext, - config: ValidateConfig, - ) -> Result { - validation_engine::catch_panics( - || { - let core_config = config.to_core(DetailLevel::Detailed); - let validation = validation_engine::validate_aws_api_request( - &self.engine, - &self.schema_validator, - &request, - core_config, - ) - .map_err(|e| ValidationError::Engine { msg: e.to_string() })?; - Ok(validation.to_detailed()) + Ok(validation) }, panic_to_error, ) diff --git a/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt b/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt index ee054564..0ba69f66 100644 --- a/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt +++ b/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt @@ -5,11 +5,10 @@ import software.amazon.cloudformation.validate.diagnostics.DetailedReport import software.amazon.cloudformation.validate.diagnostics.StandardDiagnostic import software.amazon.cloudformation.validate.diagnostics.StandardReport import software.amazon.cloudformation.validate.engine.AwsApiRequestContext as NativeAwsApiRequest +import software.amazon.cloudformation.validate.engine.AwsApiRequestValidation import software.amazon.cloudformation.validate.engine.AwsApiValue as NativeAwsApiValue -import software.amazon.cloudformation.validate.engine.DetailedAwsApiRequestValidation import software.amazon.cloudformation.validate.engine.EngineConfig import software.amazon.cloudformation.validate.engine.ExternalRuleSource -import software.amazon.cloudformation.validate.engine.StandardAwsApiRequestValidation import software.amazon.cloudformation.validate.rules.RuleInfo import software.amazon.cloudformation.validate.schemavalidator.SchemaValidatorConfig import java.io.File @@ -20,21 +19,7 @@ interface Engine { fun validateAwsApiRequest( request: AwsApiRequest, config: ValidateConfig = ValidateConfig(), - ): DetailedAwsApiRequestValidation = validateAwsApiRequestDetailed(request, config) - fun validateAwsApiRequestStandard( - request: AwsApiRequest, - config: ValidateConfig = ValidateConfig(), - ): StandardAwsApiRequestValidation = - throw UnsupportedOperationException( - "AWS API request validation is not supported by this Engine implementation", - ) - fun validateAwsApiRequestDetailed( - request: AwsApiRequest, - config: ValidateConfig = ValidateConfig(), - ): DetailedAwsApiRequestValidation = - throw UnsupportedOperationException( - "AWS API request validation is not supported by this Engine implementation", - ) + ): AwsApiRequestValidation fun listRules(): List fun engineName(): String } @@ -161,15 +146,10 @@ class RegoEngine( override fun validateDetailed(template: File, config: ValidateConfig): DetailedReport = inner.validateDetailed(template.readBytes(), config, template.path) - override fun validateAwsApiRequestStandard( + override fun validateAwsApiRequest( request: AwsApiRequest, config: ValidateConfig, - ): StandardAwsApiRequestValidation = inner.validateAwsApiRequestStandard(request.toNative(), config) - - override fun validateAwsApiRequestDetailed( - request: AwsApiRequest, - config: ValidateConfig, - ): DetailedAwsApiRequestValidation = inner.validateAwsApiRequestDetailed(request.toNative(), config) + ): AwsApiRequestValidation = inner.validateAwsApiRequest(request.toNative(), config) override fun listRules(): List = inner.listRules() override fun engineName(): String = inner.engineName() @@ -186,15 +166,10 @@ class CelEngine( override fun validateDetailed(template: File, config: ValidateConfig): DetailedReport = inner.validateDetailed(template.readBytes(), config, template.path) - override fun validateAwsApiRequestStandard( - request: AwsApiRequest, - config: ValidateConfig, - ): StandardAwsApiRequestValidation = inner.validateAwsApiRequestStandard(request.toNative(), config) - - override fun validateAwsApiRequestDetailed( + override fun validateAwsApiRequest( request: AwsApiRequest, config: ValidateConfig, - ): DetailedAwsApiRequestValidation = inner.validateAwsApiRequestDetailed(request.toNative(), config) + ): AwsApiRequestValidation = inner.validateAwsApiRequest(request.toNative(), config) override fun listRules(): List = inner.listRules() override fun engineName(): String = inner.engineName() diff --git a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt index 87472947..395ac217 100644 --- a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt +++ b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt @@ -163,9 +163,6 @@ class SmokeTest { assertNotNull(result.report) } assertEquals(gson.toJson(results[0].report?.diagnostics), gson.toJson(results[1].report?.diagnostics)) - val standard = REGO.validateAwsApiRequestStandard(request, defaultConfig()) - assertEquals(AwsApiRequestValidationStatus.VALIDATED, standard.status) - assertNotNull(standard.report) assertEquals( linkedMapOf("Bucket" to "synthetic-bucket"), parameters, diff --git a/src/bindings-python/README.md b/src/bindings-python/README.md index 21701bf5..d6959db2 100644 --- a/src/bindings-python/README.md +++ b/src/bindings-python/README.md @@ -87,9 +87,7 @@ else: `AwsApiRequest.parameters` accepts nested mappings and sequences, scalars, `bytes`, and `datetime.datetime` values without mutating the supplied mapping. `TemplateBody` bytes are validated exactly; `TemplateURL` is skipped because the validator does not perform network requests. The result always reports `status`, `operation_kind`, `template_source`, -`resource_types`, and `reason`; skipped requests have `report is None`. Use -`validate_aws_api_request_standard` for standard diagnostics or `validate_aws_api_request_detailed` (also exposed as -`validate_aws_api_request`) for detailed diagnostics. +`resource_types`, and `reason`; skipped requests have `report is None`. Operation-to-resource mapping uses a deterministic closed adapter catalog generated from each resource type's own provider handler metadata and verified against botocore models and the compiled CloudFormation schemas: only diff --git a/src/bindings-python/python/cloudformation_validate/__init__.py b/src/bindings-python/python/cloudformation_validate/__init__.py index 499eb6ad..a70bedd6 100644 --- a/src/bindings-python/python/cloudformation_validate/__init__.py +++ b/src/bindings-python/python/cloudformation_validate/__init__.py @@ -96,27 +96,26 @@ from .validation_engine import ( AwsApiOperationKind, AwsApiRequestContext as _NativeAwsApiRequest, + AwsApiRequestValidation, AwsApiRequestValidationStatus, AwsApiTemplateSource, AwsApiValue as _NativeAwsApiValue, - DetailedAwsApiRequestValidation, EngineConfig, EngineType, ExternalRuleSource, - StandardAwsApiRequestValidation, ) __all__ = [ "AdditionalSchemaSource", "AwsApiOperationKind", "AwsApiRequest", + "AwsApiRequestValidation", "AwsApiRequestValidationStatus", "AwsApiTemplateSource", "CelEngine", "ConditionalNull", "ConditionalNullEntry", "DetailLevel", - "DetailedAwsApiRequestValidation", "DetailedDiagnostic", "DetailedReport", "DiagnosticCondition", @@ -172,7 +171,6 @@ "ServiceFilter", "Severity", "SourceSpan", - "StandardAwsApiRequestValidation", "StandardDiagnostic", "StandardReport", "Summary", @@ -328,31 +326,14 @@ def validate_detailed(self, template: Template, config: typing.Optional[Validate def validate_aws_api_request( self, request: AwsApiRequest, config: typing.Optional[ValidateConfig] = None - ) -> DetailedAwsApiRequestValidation: + ) -> AwsApiRequestValidation: """Classifies, models, and validates an AWS API request. - This detailed variant is the primary integration entry point. A skipped - request has ``report is None`` and an explicit status and reason. + A skipped request has ``report is None`` and an explicit status and reason. """ - return self.validate_aws_api_request_detailed(request, config) - - def validate_aws_api_request_standard( - self, request: AwsApiRequest, config: typing.Optional[ValidateConfig] = None - ) -> StandardAwsApiRequestValidation: - """Validates an AWS API request and returns standard diagnostics.""" - if not isinstance(request, AwsApiRequest): - raise TypeError("request must be an AwsApiRequest") - return self._inner.validate_aws_api_request_standard( - request._to_native(), config if config is not None else ValidateConfig() - ) - - def validate_aws_api_request_detailed( - self, request: AwsApiRequest, config: typing.Optional[ValidateConfig] = None - ) -> DetailedAwsApiRequestValidation: - """Validates an AWS API request and returns detailed diagnostics.""" if not isinstance(request, AwsApiRequest): raise TypeError("request must be an AwsApiRequest") - return self._inner.validate_aws_api_request_detailed( + return self._inner.validate_aws_api_request( request._to_native(), config if config is not None else ValidateConfig() ) diff --git a/src/bindings-python/src/lib.rs b/src/bindings-python/src/lib.rs index e9da2086..bc8a9b51 100644 --- a/src/bindings-python/src/lib.rs +++ b/src/bindings-python/src/lib.rs @@ -23,8 +23,8 @@ pub use template_model::model::{ pub use template_model::resolver::{MapEntry, ParameterInfo, RefKind, ResolvedValue}; pub use template_model::{JsonValue, PseudoParameterOverrides, SourceSpan}; pub use validation_engine::{ - AwsApiOperationKind, AwsApiRequestContext, AwsApiRequestValidationStatus, AwsApiTemplateSource, AwsApiValue, - DetailedAwsApiRequestValidation, EngineConfig, EngineType, ExternalRuleSource, StandardAwsApiRequestValidation, + AwsApiOperationKind, AwsApiRequestContext, AwsApiRequestValidation, AwsApiRequestValidationStatus, + AwsApiTemplateSource, AwsApiValue, EngineConfig, EngineType, ExternalRuleSource, }; pub use schema_validator::SchemaValidatorConfig; @@ -200,11 +200,11 @@ macro_rules! impl_py_engine { ) } - pub fn validate_aws_api_request_standard( + pub fn validate_aws_api_request( &self, request: AwsApiRequestContext, config: ValidateConfig, - ) -> Result { + ) -> Result { validation_engine::catch_panics( || { let core_config = config.to_core(DetailLevel::Standard); @@ -215,28 +215,7 @@ macro_rules! impl_py_engine { core_config, ) .map_err(|e| ValidationError::Engine { msg: e.to_string() })?; - Ok(validation.to_standard()) - }, - panic_to_error, - ) - } - - pub fn validate_aws_api_request_detailed( - &self, - request: AwsApiRequestContext, - config: ValidateConfig, - ) -> Result { - validation_engine::catch_panics( - || { - let core_config = config.to_core(DetailLevel::Detailed); - let validation = validation_engine::validate_aws_api_request( - &self.engine, - &self.schema_validator, - &request, - core_config, - ) - .map_err(|e| ValidationError::Engine { msg: e.to_string() })?; - Ok(validation.to_detailed()) + Ok(validation) }, panic_to_error, ) diff --git a/src/bindings-python/tests/smoke_test.py b/src/bindings-python/tests/smoke_test.py index 37c45071..613fcea4 100644 --- a/src/bindings-python/tests/smoke_test.py +++ b/src/bindings-python/tests/smoke_test.py @@ -204,9 +204,6 @@ def test_synthesized_create_validates_with_both_engines(self): self.assertEqual(["AWS::S3::Bucket"], result.resource_types) self.assertIsNotNone(result.report) self.assertEqual(diagnostic_keys(results[0].report), diagnostic_keys(results[1].report)) - standard = REGO.validate_aws_api_request_standard(request) - self.assertEqual(AwsApiRequestValidationStatus.VALIDATED, standard.status) - self.assertIsNotNone(standard.report) self.assertEqual({"Bucket": "synthetic-bucket"}, parameters) def test_template_body_bytes_are_validated_exactly(self): diff --git a/src/validation-engine/API.md b/src/validation-engine/API.md index 57143aff..ae72ee9f 100644 --- a/src/validation-engine/API.md +++ b/src/validation-engine/API.md @@ -64,8 +64,8 @@ let result = validate_aws_api_request( &request, ValidateConfig::default(), )?; -if let Some(report) = result.report { - for diagnostic in report.diagnostics { +if let Some(report) = &result.report { + for diagnostic in &report.diagnostics { println!("{}: {}", diagnostic.rule_id, diagnostic.message); } } else { @@ -76,8 +76,10 @@ if let Some(report) = result.report { `AwsApiValue` preserves bytes and 64-bit integer widths and explicitly marks unsupported values. Exact `TemplateBody` bytes are validated without rewriting; `TemplateURL` is skipped because validation is offline. Every result includes an operation kind, validation status, optional template source, resource candidates, and reason. `Validated` means the -modeled template reached the normal validation pipeline; `Skipped` has no report and explains why. Use -`validate_aws_api_request_with_path` when the embedding application needs a custom report path. +modeled template reached the normal validation pipeline; `Skipped` has no report and explains why. +`AwsApiRequestValidation` contains an `Option` directly — detailed enrichment is not supported for +synthesized API-request templates because there is no user-authored source to annotate with context. +Use `validate_aws_api_request_with_path` when the embedding application needs a custom report path. **Deterministic closed-adapter contract.** Operation-to-resource mapping uses a generated adapter catalog keyed by case-normalized canonical `service_name` and exact operation name. The catalog is produced by diff --git a/src/validation-engine/src/aws_api.rs b/src/validation-engine/src/aws_api.rs index c97b33f8..6e2189d9 100644 --- a/src/validation-engine/src/aws_api.rs +++ b/src/validation-engine/src/aws_api.rs @@ -1,4 +1,4 @@ -use diagnostics::{DetailedReport, StandardReport, Summary, ValidationReport}; +use diagnostics::{DetailLevel, StandardReport, Summary, ValidationReport}; use rules::Severity; use schema_validator::{PropertyValueType, ResourceSchemaMetadata, SchemaValidator}; use serde::{Deserialize, Serialize}; @@ -182,23 +182,16 @@ pub enum AwsApiTemplateSource { SynthesizedUpdate, } -/// Full Rust result for AWS API request validation. -#[derive(Debug, Clone)] -#[must_use] -pub struct AwsApiRequestValidation { - pub operation_kind: AwsApiOperationKind, - pub status: AwsApiRequestValidationStatus, - pub template_source: Option, - pub resource_types: Vec, - pub reason: String, - pub report: Option, -} - -/// AWS API request result containing standard diagnostics. +/// Canonical result for AWS API request validation. +/// +/// Contains standard diagnostics only — detailed enrichment is not meaningful +/// for synthesized API-request templates because there is no user-authored +/// source to annotate with context. #[derive(Debug, Clone, Serialize)] #[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))] #[serde(rename_all = "camelCase")] -pub struct StandardAwsApiRequestValidation { +#[must_use] +pub struct AwsApiRequestValidation { pub operation_kind: AwsApiOperationKind, pub status: AwsApiRequestValidationStatus, pub template_source: Option, @@ -207,43 +200,6 @@ pub struct StandardAwsApiRequestValidation { pub report: Option, } -/// AWS API request result containing detailed diagnostics and context. -#[derive(Debug, Clone, Serialize)] -#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))] -#[serde(rename_all = "camelCase")] -pub struct DetailedAwsApiRequestValidation { - pub operation_kind: AwsApiOperationKind, - pub status: AwsApiRequestValidationStatus, - pub template_source: Option, - pub resource_types: Vec, - pub reason: String, - pub report: Option, -} - -impl AwsApiRequestValidation { - pub fn to_standard(&self) -> StandardAwsApiRequestValidation { - StandardAwsApiRequestValidation { - operation_kind: self.operation_kind, - status: self.status, - template_source: self.template_source, - resource_types: self.resource_types.clone(), - reason: self.reason.clone(), - report: self.report.as_ref().map(ValidationReport::to_standard), - } - } - - pub fn to_detailed(&self) -> DetailedAwsApiRequestValidation { - DetailedAwsApiRequestValidation { - operation_kind: self.operation_kind, - status: self.status, - template_source: self.template_source, - resource_types: self.resource_types.clone(), - reason: self.reason.clone(), - report: self.report.as_ref().map(ValidationReport::to_detailed), - } - } -} - /// Classifies, models, and validates one AWS API request entirely offline. pub fn validate_aws_api_request( engine: &dyn ValidationEngine, @@ -276,7 +232,10 @@ pub fn validate_aws_api_request_with_path( }); }; - let mut report = validate_bytes_with_path(engine, schema_validator, &template, config, file_path)?; + // Force standard detail level — detailed enrichment is not meaningful for + // synthesized API-request templates (no user-authored source to annotate). + let standard_config = ValidateConfig { detail_level: DetailLevel::Standard, ..config }; + let mut report = validate_bytes_with_path(engine, schema_validator, &template, standard_config, file_path)?; if let Some(properties) = synthesis.diagnostic_properties.as_ref() { scope_synthesized_report(&mut report, properties); } @@ -286,7 +245,7 @@ pub fn validate_aws_api_request_with_path( template_source: synthesis.source, resource_types: synthesis.resource_types, reason: synthesis.reason, - report: Some(report), + report: Some(report.to_standard()), }) } #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)] diff --git a/src/validation-engine/src/lib.rs b/src/validation-engine/src/lib.rs index 9e9d43a3..b7697c72 100644 --- a/src/validation-engine/src/lib.rs +++ b/src/validation-engine/src/lib.rs @@ -8,8 +8,7 @@ pub(crate) mod step_functions; pub use aws_api::{ AwsApiOperationKind, AwsApiRequest, AwsApiRequestContext, AwsApiRequestValidation, AwsApiRequestValidationStatus, - AwsApiTemplateSource, AwsApiValue, DetailedAwsApiRequestValidation, StandardAwsApiRequestValidation, - validate_aws_api_request, validate_aws_api_request_with_path, + AwsApiTemplateSource, AwsApiValue, validate_aws_api_request, validate_aws_api_request_with_path, }; pub use engine::{ EngineConfig, EngineType, ExternalRuleSource, ValidateConfig, ValidationEngine, ValidationError, build_rule_list, From d06ada785cbd746b0e383f36d9f17a4a83c2ad7a Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Tue, 18 Aug 2026 13:48:06 -0400 Subject: [PATCH 05/19] add design --- aws-api-request-validation-design.md | 85 ++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 aws-api-request-validation-design.md diff --git a/aws-api-request-validation-design.md b/aws-api-request-validation-design.md new file mode 100644 index 00000000..c5302a52 --- /dev/null +++ b/aws-api-request-validation-design.md @@ -0,0 +1,85 @@ +# Validating AWS API Requests + +## Summary + +`cloudformation-validate` normally receives a CloudFormation template. A direct AWS API request carries resource configuration as operation parameters, so it has no template to validate. This feature accepts the request context and determines whether the operation represents CloudFormation resource state. It validates supplied `TemplateBody` bytes or builds a temporary, one-resource template from mapped request fields. A generated operation catalog supplies the service-operation, resource-type, and field mappings. Requests that produce no template return `SKIPPED` with a reason. + +## Request and result + +An Amazon Web Services (AWS) application programming interface (API) request enters this feature. Required fields are service name, operation name, and parameters. Three optional fields carry the request method, read-only status, and request-signing service prefix. + +Parameter values use a tagged type. Variants represent null, Boolean, signed integer, unsigned integer, number, string, bytes, array, object, and unsupported values. This representation keeps `TemplateBody` bytes and 64-bit integer values intact. + +The result contains an operation class, `VALIDATED` or `SKIPPED` status, template source, matched resource types, reason, and optional `cloudformation-validate` report. Template source is one of `TEMPLATE_BODY`, `CLOUD_CONTROL_DESIRED_STATE`, `SYNTHESIZED_CREATE`, or `SYNTHESIZED_UPDATE`. + +## Operation catalog format and loading + +The embedded catalog contains adapter rows with: + +- `service`: canonical AWS service name. +- `operation`: exact API operation name. +- `phase`: `create`, `update`, or `delete`. +- `cfn_type`: CloudFormation resource type. +- `mappings`: request-field and CloudFormation-property pairs named `source` and `target`. + +At startup, the catalog loader checks non-empty identities and unique service-operation keys. It lowercases the service portion of each key and keeps the operation name unchanged. The resulting map supports exact lookup by `(service, operation)`. + +The data build compresses the catalog into the `cloudformation-validate` artifact. Runtime request processing does not load service models or catalog files. + +## Catalog generation + +The generator combines three sources. + +1. **Enhanced CloudFormation provider schemas** supply each resource type and its create and delete handler permissions. +2. **Botocore service models** resolve permission actions to exact services and operations. They also supply operation input fields and types. +3. **CloudFormation property definitions** supply writable property names, read-only properties, identifiers, and accepted value types. + +The generator applies five stages: + +1. **Find candidate operations.** For one resource type, read its create or delete handler permissions. Keep actions whose verb matches that lifecycle phase. +2. **Resolve the API operation.** Match each permission against Botocore service identities and operation names. Known service aliases handle naming differences between CloudFormation and AWS APIs. +3. **Map request fields to properties.** Compare Botocore input fields with writable CloudFormation properties. Accept equal names and narrow renames such as `Bucket` to `BucketName`. Record mappings whose value types align. String maps targeting `Tags` use the tag conversion. +4. **Choose one adapter.** Rank candidates by resource-name agreement, lifecycle verb, mapped-field count, and mapping coverage. A candidate without name agreement needs two mappings covering 30 percent of its inputs. Equal-ranked candidates and unresolved service-operation collisions produce no adapter. Known data operations are excluded. +5. **Write the catalog.** Add hand-authored update adapters and remove generation-only fields. Sort entries, then write each adapter’s service, operation, phase, resource type, and mappings. + +The S3 bucket example has four links. + +`AWS::S3::Bucket` → create permission `s3:CreateBucket` → Botocore input `Bucket` → CloudFormation property `BucketName`. + +It emits service `s3`, operation `CreateBucket`, phase `create`, resource type `AWS::S3::Bucket`, and mapping `Bucket → BucketName`. + +Create and delete adapters come from provider handler permissions. Update adapters remain hand-authored because update requests contain changed fields rather than complete resource state. + +## Operation classification + +Each request receives one of six classes: `READ_ONLY`, `CLOUD_FORMATION_CREATE`, `CLOUD_FORMATION_UPDATE`, `CLOUD_FORMATION_DELETE`, `DATA_PLANE_MUTATION`, or `UNMAPPED_MUTATION`. + +The classifier applies these rules in order: + +1. Known CloudFormation operations receive fixed classes. Stack and change-set creation operations use `CLOUD_FORMATION_CREATE`; stack updates use `CLOUD_FORMATION_UPDATE`; template inspection uses `READ_ONLY`. +2. An explicit read-only status produces `READ_ONLY`. +3. An exact catalog match uses the adapter phase and resource type. +4. The classifier splits an uncataloged operation name into words and selects its effective verb. Modifier prefixes such as `Admin`, `Batch`, `Bulk`, and `Transact` move the effective verb to the next word. +5. Read verbs or `GET` and `HEAD` produce `READ_ONLY`. Data verbs produce `DATA_PLANE_MUTATION`. +6. Exact Cloud Control `CreateResource`, `UpdateResource`, and `DeleteResource` calls read `TypeName` when it names a known resource type. Create and delete receive lifecycle classes. Update receives `UNMAPPED_MUTATION`. +7. Remaining writes use `DATA_PLANE_MUTATION` for the configured content-changing verb set and `UNMAPPED_MUTATION` otherwise. These classes contain no inferred resource type. + +The canonical service name controls catalog and special-operation lookup. The service prefix does not replace it. Service matching changes ASCII letter case only; operation matching remains exact. + +## Template construction and validation + +Template construction follows the classification result. + +1. A recognized CloudFormation operation with a non-empty `TemplateBody` string or byte sequence sends those bytes directly to `cloudformation-validate`. +2. A recognized CloudFormation operation containing `TemplateURL` returns `SKIPPED` because no template bytes are present. +3. A `READ_ONLY` request without a direct `TemplateBody` path returns `SKIPPED`. +4. Cloud Control `CreateResource` with `TypeName` and `DesiredState` parses the state string or bytes as a JSON object. It uses `TypeName` as the resource type. Cloud Control `UpdateResource` returns `SKIPPED` because it carries `PatchDocument`. +5. A cataloged create or update loads the mapped resource’s CloudFormation property definitions. Delete and other classes return `SKIPPED` before field mapping. +6. For every adapter mapping, the runtime confirms that the target property exists. It excludes read-only properties and, for updates, identifying properties. Missing source fields are ignored. +7. Strings, Booleans, integers, numbers, and lists of simple values map when their types match the target property. A string map targeting `Tags` becomes a sorted array of `{Key, Value}` objects. Other object values, bytes, nulls, and unsupported values do not enter the temporary template. +8. If no field maps, the request returns `SKIPPED`. Otherwise, the runtime creates one resource named `Resource` with the matched type and mapped properties. +9. `cloudformation-validate` processes the temporary template. For catalog-generated templates, the result retains findings only for properties inserted during field mapping and recalculates report counts. + +The final response returns the operation class, status, template source, resource types, reason, and validation report when validation ran. + + From 21d1bd33839e620839d0c1b43bc3ebdaef74fd3e Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Wed, 19 Aug 2026 01:10:52 -0400 Subject: [PATCH 06/19] update validate impl --- aws-api-request-validation-design.md | 85 ------ src/bindings-go/tests/smoke_test.go | 263 ------------------ .../tests/kotlin/src/test/kotlin/SmokeTest.kt | 120 -------- src/bindings-python/README.md | 5 +- .../cloudformation_validate/__init__.py | 7 +- src/bindings-python/tests/smoke_test.py | 109 -------- src/data-source/README.md | 32 +-- .../scripts/generate_aws_api_catalog.py | 180 +++++++++++- .../scripts/test_generate_aws_api_catalog.py | 223 ++++++++++++++- src/validation-engine/API.md | 11 + src/validation-engine/src/aws_api.rs | 244 +++++++++++++++- 11 files changed, 651 insertions(+), 628 deletions(-) delete mode 100644 aws-api-request-validation-design.md diff --git a/aws-api-request-validation-design.md b/aws-api-request-validation-design.md deleted file mode 100644 index c5302a52..00000000 --- a/aws-api-request-validation-design.md +++ /dev/null @@ -1,85 +0,0 @@ -# Validating AWS API Requests - -## Summary - -`cloudformation-validate` normally receives a CloudFormation template. A direct AWS API request carries resource configuration as operation parameters, so it has no template to validate. This feature accepts the request context and determines whether the operation represents CloudFormation resource state. It validates supplied `TemplateBody` bytes or builds a temporary, one-resource template from mapped request fields. A generated operation catalog supplies the service-operation, resource-type, and field mappings. Requests that produce no template return `SKIPPED` with a reason. - -## Request and result - -An Amazon Web Services (AWS) application programming interface (API) request enters this feature. Required fields are service name, operation name, and parameters. Three optional fields carry the request method, read-only status, and request-signing service prefix. - -Parameter values use a tagged type. Variants represent null, Boolean, signed integer, unsigned integer, number, string, bytes, array, object, and unsupported values. This representation keeps `TemplateBody` bytes and 64-bit integer values intact. - -The result contains an operation class, `VALIDATED` or `SKIPPED` status, template source, matched resource types, reason, and optional `cloudformation-validate` report. Template source is one of `TEMPLATE_BODY`, `CLOUD_CONTROL_DESIRED_STATE`, `SYNTHESIZED_CREATE`, or `SYNTHESIZED_UPDATE`. - -## Operation catalog format and loading - -The embedded catalog contains adapter rows with: - -- `service`: canonical AWS service name. -- `operation`: exact API operation name. -- `phase`: `create`, `update`, or `delete`. -- `cfn_type`: CloudFormation resource type. -- `mappings`: request-field and CloudFormation-property pairs named `source` and `target`. - -At startup, the catalog loader checks non-empty identities and unique service-operation keys. It lowercases the service portion of each key and keeps the operation name unchanged. The resulting map supports exact lookup by `(service, operation)`. - -The data build compresses the catalog into the `cloudformation-validate` artifact. Runtime request processing does not load service models or catalog files. - -## Catalog generation - -The generator combines three sources. - -1. **Enhanced CloudFormation provider schemas** supply each resource type and its create and delete handler permissions. -2. **Botocore service models** resolve permission actions to exact services and operations. They also supply operation input fields and types. -3. **CloudFormation property definitions** supply writable property names, read-only properties, identifiers, and accepted value types. - -The generator applies five stages: - -1. **Find candidate operations.** For one resource type, read its create or delete handler permissions. Keep actions whose verb matches that lifecycle phase. -2. **Resolve the API operation.** Match each permission against Botocore service identities and operation names. Known service aliases handle naming differences between CloudFormation and AWS APIs. -3. **Map request fields to properties.** Compare Botocore input fields with writable CloudFormation properties. Accept equal names and narrow renames such as `Bucket` to `BucketName`. Record mappings whose value types align. String maps targeting `Tags` use the tag conversion. -4. **Choose one adapter.** Rank candidates by resource-name agreement, lifecycle verb, mapped-field count, and mapping coverage. A candidate without name agreement needs two mappings covering 30 percent of its inputs. Equal-ranked candidates and unresolved service-operation collisions produce no adapter. Known data operations are excluded. -5. **Write the catalog.** Add hand-authored update adapters and remove generation-only fields. Sort entries, then write each adapter’s service, operation, phase, resource type, and mappings. - -The S3 bucket example has four links. - -`AWS::S3::Bucket` → create permission `s3:CreateBucket` → Botocore input `Bucket` → CloudFormation property `BucketName`. - -It emits service `s3`, operation `CreateBucket`, phase `create`, resource type `AWS::S3::Bucket`, and mapping `Bucket → BucketName`. - -Create and delete adapters come from provider handler permissions. Update adapters remain hand-authored because update requests contain changed fields rather than complete resource state. - -## Operation classification - -Each request receives one of six classes: `READ_ONLY`, `CLOUD_FORMATION_CREATE`, `CLOUD_FORMATION_UPDATE`, `CLOUD_FORMATION_DELETE`, `DATA_PLANE_MUTATION`, or `UNMAPPED_MUTATION`. - -The classifier applies these rules in order: - -1. Known CloudFormation operations receive fixed classes. Stack and change-set creation operations use `CLOUD_FORMATION_CREATE`; stack updates use `CLOUD_FORMATION_UPDATE`; template inspection uses `READ_ONLY`. -2. An explicit read-only status produces `READ_ONLY`. -3. An exact catalog match uses the adapter phase and resource type. -4. The classifier splits an uncataloged operation name into words and selects its effective verb. Modifier prefixes such as `Admin`, `Batch`, `Bulk`, and `Transact` move the effective verb to the next word. -5. Read verbs or `GET` and `HEAD` produce `READ_ONLY`. Data verbs produce `DATA_PLANE_MUTATION`. -6. Exact Cloud Control `CreateResource`, `UpdateResource`, and `DeleteResource` calls read `TypeName` when it names a known resource type. Create and delete receive lifecycle classes. Update receives `UNMAPPED_MUTATION`. -7. Remaining writes use `DATA_PLANE_MUTATION` for the configured content-changing verb set and `UNMAPPED_MUTATION` otherwise. These classes contain no inferred resource type. - -The canonical service name controls catalog and special-operation lookup. The service prefix does not replace it. Service matching changes ASCII letter case only; operation matching remains exact. - -## Template construction and validation - -Template construction follows the classification result. - -1. A recognized CloudFormation operation with a non-empty `TemplateBody` string or byte sequence sends those bytes directly to `cloudformation-validate`. -2. A recognized CloudFormation operation containing `TemplateURL` returns `SKIPPED` because no template bytes are present. -3. A `READ_ONLY` request without a direct `TemplateBody` path returns `SKIPPED`. -4. Cloud Control `CreateResource` with `TypeName` and `DesiredState` parses the state string or bytes as a JSON object. It uses `TypeName` as the resource type. Cloud Control `UpdateResource` returns `SKIPPED` because it carries `PatchDocument`. -5. A cataloged create or update loads the mapped resource’s CloudFormation property definitions. Delete and other classes return `SKIPPED` before field mapping. -6. For every adapter mapping, the runtime confirms that the target property exists. It excludes read-only properties and, for updates, identifying properties. Missing source fields are ignored. -7. Strings, Booleans, integers, numbers, and lists of simple values map when their types match the target property. A string map targeting `Tags` becomes a sorted array of `{Key, Value}` objects. Other object values, bytes, nulls, and unsupported values do not enter the temporary template. -8. If no field maps, the request returns `SKIPPED`. Otherwise, the runtime creates one resource named `Resource` with the matched type and mapped properties. -9. `cloudformation-validate` processes the temporary template. For catalog-generated templates, the result retains findings only for properties inserted during field mapping and recalculates report counts. - -The final response returns the operation class, status, template source, resource types, reason, and validation report when validation ran. - - diff --git a/src/bindings-go/tests/smoke_test.go b/src/bindings-go/tests/smoke_test.go index 9eeb4e18..bba97c13 100644 --- a/src/bindings-go/tests/smoke_test.go +++ b/src/bindings-go/tests/smoke_test.go @@ -9,7 +9,6 @@ package cfnvalidate_test import ( "encoding/json" "fmt" - "math" "os" "path/filepath" "regexp" @@ -481,265 +480,3 @@ func TestErrorsSurfaceAsGoErrors(t *testing.T) { t.Error("invalid custom rule must fail engine construction") } } - -// --- AWS API Request Validation tests --- - -func TestAWSAPIRequestS3CreateWithBothEnginesAndParity(t *testing.T) { - request := cfnvalidate.AWSAPIRequest{ - ServiceName: "s3", - OperationName: "CreateBucket", - Parameters: map[string]any{"Bucket": "my-test-bucket"}, - HTTPMethod: "PUT", - } - originalParams := map[string]any{"Bucket": "my-test-bucket"} - - results := map[string]*cfnvalidate.AWSAPIRequestValidation{} - for name, engine := range bothEngines(t) { - result, err := engine.ValidateAWSAPIRequest(request, nil) - if err != nil { - t.Fatalf("%s: ValidateAWSAPIRequest failed: %v", name, err) - } - results[name] = result - - if result.OperationKind != cfnvalidate.AWSAPIOperationKindCloudFormationCreate { - t.Errorf("%s: operationKind = %s, want CLOUD_FORMATION_CREATE", name, result.OperationKind) - } - if result.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { - t.Errorf("%s: status = %s, want VALIDATED", name, result.Status) - } - if len(result.ResourceTypes) == 0 || result.ResourceTypes[0] != "AWS::S3::Bucket" { - t.Errorf("%s: resourceTypes = %v, want [AWS::S3::Bucket]", name, result.ResourceTypes) - } - if result.Report == nil { - t.Fatalf("%s: report must be present for a validated request", name) - } - if result.Report.Status != cfnvalidate.StatusOK { - t.Errorf("%s: report.Status = %s, want OK", name, result.Report.Status) - } - } - - // Verify engine parity. - if results["rego"] != nil && results["cel"] != nil { - regoKeys := diagnosticKeys(results["rego"].Report) - celKeys := diagnosticKeys(results["cel"].Report) - if !equalStrings(regoKeys, celKeys) { - t.Errorf("engines disagree on AWS API diagnostics:\nrego: %v\ncel: %v", regoKeys, celKeys) - } - } - - // Verify input non-mutation. - if len(request.Parameters) != len(originalParams) { - t.Errorf("request.Parameters mutated: len changed from %d to %d", len(originalParams), len(request.Parameters)) - } - for key, want := range originalParams { - if got, ok := request.Parameters[key]; !ok || got != want { - t.Errorf("request.Parameters[%q] mutated: got %v, want %v", key, got, want) - } - } -} - -func TestAWSAPIRequestCloudFormationTemplateBodyBytes(t *testing.T) { - template := []byte(`{"AWSTemplateFormatVersion":"2010-09-09","Resources":{"Bucket":{"Type":"AWS::S3::Bucket","Properties":{"BucketName":"exact-body"}}}}`) - request := cfnvalidate.AWSAPIRequest{ - ServiceName: "cloudformation", - OperationName: "CreateStack", - Parameters: map[string]any{"TemplateBody": template}, - HTTPMethod: "POST", - } - - engine := mustEngine(t, cfnvalidate.NewRegoEngine, nil) - result, err := engine.ValidateAWSAPIRequest(request, nil) - if err != nil { - t.Fatalf("ValidateAWSAPIRequest failed: %v", err) - } - - if result.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { - t.Errorf("status = %s, want VALIDATED", result.Status) - } - if result.TemplateSource == nil || *result.TemplateSource != cfnvalidate.AWSAPITemplateSourceTemplateBody { - t.Errorf("templateSource = %v, want TEMPLATE_BODY", result.TemplateSource) - } - if result.Report == nil { - t.Fatal("report must be present for a validated template body") - } - if result.Report.Status != cfnvalidate.StatusOK { - t.Errorf("report.Status = %s, want OK", result.Report.Status) - } -} - -func TestAWSAPIRequestReadOnlySkips(t *testing.T) { - readOnly := true - request := cfnvalidate.AWSAPIRequest{ - ServiceName: "iam", - OperationName: "GetRole", - Parameters: map[string]any{"RoleName": "test-role"}, - IsReadOnly: &readOnly, - } - - engine := mustEngine(t, cfnvalidate.NewCelEngine, nil) - result, err := engine.ValidateAWSAPIRequest(request, nil) - if err != nil { - t.Fatalf("ValidateAWSAPIRequest failed: %v", err) - } - - if result.OperationKind != cfnvalidate.AWSAPIOperationKindReadOnly { - t.Errorf("operationKind = %s, want READ_ONLY", result.OperationKind) - } - if result.Status != cfnvalidate.AWSAPIRequestValidationStatusSkipped { - t.Errorf("status = %s, want SKIPPED", result.Status) - } - if result.Report != nil { - t.Errorf("report must be nil for a skipped request, got %+v", result.Report) - } -} - -func TestAWSAPIRequestUnregisteredOperationNoInferredTypes(t *testing.T) { - request := cfnvalidate.AWSAPIRequest{ - ServiceName: "customservice", - OperationName: "DoSomethingUnknown", - Parameters: map[string]any{"Key": "value"}, - HTTPMethod: "POST", - } - - engine := mustEngine(t, cfnvalidate.NewRegoEngine, nil) - result, err := engine.ValidateAWSAPIRequest(request, nil) - if err != nil { - t.Fatalf("ValidateAWSAPIRequest failed: %v", err) - } - - if len(result.ResourceTypes) != 0 { - t.Errorf("resourceTypes = %v, want empty for unregistered operation", result.ResourceTypes) - } - if result.Report != nil { - t.Errorf("report must be nil for an unmapped operation that cannot synthesize, got non-nil") - } -} - -func TestAWSAPIRequestSDKServiceCasing(t *testing.T) { - request := cfnvalidate.AWSAPIRequest{ - ServiceName: "DynamoDb", - OperationName: "CreateTable", - Parameters: map[string]any{ - "TableName": "PascalCased", - "KeySchema": []any{map[string]any{"AttributeName": "id", "KeyType": "HASH"}}, - "AttributeDefinitions": []any{map[string]any{"AttributeName": "id", "AttributeType": "S"}}, - "BillingMode": "PAY_PER_REQUEST", - }, - HTTPMethod: "POST", - } - - engine := mustEngine(t, cfnvalidate.NewCelEngine, nil) - result, err := engine.ValidateAWSAPIRequest(request, nil) - if err != nil { - t.Fatalf("ValidateAWSAPIRequest failed: %v", err) - } - - if result.OperationKind != cfnvalidate.AWSAPIOperationKindCloudFormationCreate { - t.Errorf("operationKind = %s, want CLOUD_FORMATION_CREATE", result.OperationKind) - } - if len(result.ResourceTypes) == 0 || result.ResourceTypes[0] != "AWS::DynamoDB::Table" { - t.Errorf("resourceTypes = %v, want [AWS::DynamoDB::Table]", result.ResourceTypes) - } -} - -func TestAWSAPIRequestEC2MappedDiagnostic(t *testing.T) { - // Use a defined string alias to exercise the scalar alias encoding path. - // InstanceInitiatedShutdownBehavior has a real enum constraint in the - // EC2 schema, so an invalid value triggers a diagnostic reliably. - type ShutdownBehavior string - request := cfnvalidate.AWSAPIRequest{ - ServiceName: "ec2", - OperationName: "RunInstances", - Parameters: map[string]any{ - "ImageId": "ami-12345678", - "InstanceInitiatedShutdownBehavior": ShutdownBehavior("invalid"), - }, - HTTPMethod: "POST", - } - - for name, engine := range bothEngines(t) { - result, err := engine.ValidateAWSAPIRequest(request, nil) - if err != nil { - t.Fatalf("%s: ValidateAWSAPIRequest failed: %v", name, err) - } - if result.OperationKind != cfnvalidate.AWSAPIOperationKindCloudFormationCreate { - t.Errorf("%s: operationKind = %s, want CLOUD_FORMATION_CREATE", name, result.OperationKind) - } - if len(result.ResourceTypes) == 0 || result.ResourceTypes[0] != "AWS::EC2::Instance" { - t.Errorf("%s: resourceTypes = %v, want [AWS::EC2::Instance]", name, result.ResourceTypes) - } - if result.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { - t.Errorf("%s: status = %s, want VALIDATED", name, result.Status) - } - if result.Report == nil { - t.Fatalf("%s: report must be present", name) - } - hasDiagnostic := false - for _, d := range result.Report.Diagnostics { - if strings.Contains(d.Message, "InstanceInitiatedShutdownBehavior") || (d.PropertyPath != nil && strings.Contains(*d.PropertyPath, "InstanceInitiatedShutdownBehavior")) { - hasDiagnostic = true - break - } - } - if !hasDiagnostic { - t.Errorf("%s: expected at least one diagnostic about InstanceInitiatedShutdownBehavior, got %d total diagnostics", name, len(result.Report.Diagnostics)) - } - } -} - -func TestAWSAPIRequestEmptyArrayAndObjectParsed(t *testing.T) { - // Empty ARRAY must serialize items:[] and empty OBJECT must serialize - // entries:{} — the Rust parser rejects their absence. - request := cfnvalidate.AWSAPIRequest{ - ServiceName: "ec2", - OperationName: "RunInstances", - Parameters: map[string]any{ - "ImageId": "ami-12345678", - "TagSpecifications": []any{}, - "TagSet": map[string]any{}, - }, - HTTPMethod: "POST", - } - - engine := mustEngine(t, cfnvalidate.NewRegoEngine, nil) - result, err := engine.ValidateAWSAPIRequest(request, nil) - if err != nil { - t.Fatalf("ValidateAWSAPIRequest failed with empty array/object parameters: %v", err) - } - // The request must not cause a JSON parse failure on the Rust side — it - // should be accepted and processed (status VALIDATED, not an error). - if result.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { - t.Errorf("status = %s, want VALIDATED", result.Status) - } -} - -func TestAWSAPIRequestUnsupportedValueDoesNotCauseParseFailure(t *testing.T) { - // Non-finite floating-point values, non-string-key maps, and cyclic - // indirection are conservatively represented as UNSUPPORTED rather than - // causing a JSON parse failure or looping in the Go encoder. - var cyclic any - cyclic = &cyclic - request := cfnvalidate.AWSAPIRequest{ - ServiceName: "ec2", - OperationName: "RunInstances", - Parameters: map[string]any{ - "ImageId": "ami-12345678", - "BadFloat": math.Inf(1), - "BadMapKey": map[int]string{1: "one"}, - "Cyclic": cyclic, - }, - HTTPMethod: "POST", - } - - engine := mustEngine(t, cfnvalidate.NewCelEngine, nil) - result, err := engine.ValidateAWSAPIRequest(request, nil) - if err != nil { - t.Fatalf("ValidateAWSAPIRequest must not fail for unsupported values: %v", err) - } - // The request crosses the FFI boundary without a JSON parse error — - // unsupported values are encoded as {"type":"UNSUPPORTED","type_name":"..."} - // which the Rust parser accepts. - if result.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { - t.Errorf("status = %s, want VALIDATED (unsupported values are carried, not rejected)", result.Status) - } -} diff --git a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt index 395ac217..e664eb92 100644 --- a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt +++ b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt @@ -140,126 +140,6 @@ class SmokeTest { } } - @Test - fun synthesizedAwsApiCreateValidatesWithBothEnginesWithoutMutatingInput() { - val parameters = linkedMapOf( - "Bucket" to "synthetic-bucket", - ) - val request = AwsApiRequest( - serviceName = "s3", - operationName = "CreateBucket", - parameters = parameters, - servicePrefix = "s3", - httpMethod = "POST", - ) - - val results = listOf(REGO, CEL).map { it.validateAwsApiRequest(request, defaultConfig()) } - - for (result in results) { - assertEquals(AwsApiRequestValidationStatus.VALIDATED, result.status) - assertEquals(AwsApiOperationKind.CLOUD_FORMATION_CREATE, result.operationKind) - assertEquals(AwsApiTemplateSource.SYNTHESIZED_CREATE, result.templateSource) - assertEquals(listOf("AWS::S3::Bucket"), result.resourceTypes) - assertNotNull(result.report) - } - assertEquals(gson.toJson(results[0].report?.diagnostics), gson.toJson(results[1].report?.diagnostics)) - assertEquals( - linkedMapOf("Bucket" to "synthetic-bucket"), - parameters, - ) - } - - @Test - fun awsApiTemplateBodyPreservesBytesAndReadOnlyRequestsReportSkips() { - val templateResult = REGO.validateAwsApiRequest( - AwsApiRequest( - serviceName = "cloudformation", - operationName = "CreateChangeSet", - parameters = mapOf("TemplateBody" to "{\"Resources\":{}}".toByteArray()), - servicePrefix = "cloudformation", - httpMethod = "POST", - ), - defaultConfig(), - ) - assertEquals(AwsApiRequestValidationStatus.VALIDATED, templateResult.status) - assertEquals(AwsApiTemplateSource.TEMPLATE_BODY, templateResult.templateSource) - assertEquals(ReportStatus.OK, templateResult.report?.status) - - val readResult = REGO.validateAwsApiRequest( - AwsApiRequest( - serviceName = "iam", - operationName = "GetRole", - parameters = mapOf("RoleName" to "Synthetic"), - servicePrefix = "iam", - httpMethod = "POST", - ), - defaultConfig(), - ) - assertEquals(AwsApiRequestValidationStatus.SKIPPED, readResult.status) - assertEquals(AwsApiOperationKind.READ_ONLY, readResult.operationKind) - assertNull(readResult.report) - assertTrue(readResult.reason.contains("read-only")) - } - - @Test - fun awsApiPartialUpdateScopesDiagnosticsAndKeepsCountsConsistent() { - val result = REGO.validateAwsApiRequest( - AwsApiRequest( - serviceName = "lambda", - operationName = "UpdateFunctionConfiguration", - parameters = mapOf("FunctionName" to "Synthetic", "MemorySize" to 0), - servicePrefix = "lambda", - httpMethod = "POST", - ), - defaultConfig(), - ) - val report = result.report ?: fail("synthesized update must return a report") - - assertEquals(AwsApiTemplateSource.SYNTHESIZED_UPDATE, result.templateSource) - assertTrue( - report.diagnostics.all { it.propertyPath?.contains("MemorySize") == true }, - report.diagnostics.toString(), - ) - val counts = report.metadata.counts - assertEquals( - report.diagnostics.size.toUInt(), - counts.fatal + counts.errors + counts.warnings + counts.informational + counts.debug, - ) - } - - @Test - fun awsApiJavaSdkServiceNameCasingResolvesAdapter() { - val result = REGO.validateAwsApiRequest( - AwsApiRequest( - serviceName = "S3", - operationName = "CreateBucket", - parameters = mapOf("Bucket" to "synthetic-bucket"), - servicePrefix = "S3", - httpMethod = "PUT", - ), - defaultConfig(), - ) - assertEquals(AwsApiOperationKind.CLOUD_FORMATION_CREATE, result.operationKind) - assertEquals(listOf("AWS::S3::Bucket"), result.resourceTypes) - } - - @Test - fun awsApiUnregisteredOperationNeverMapsToResourceType() { - val result = REGO.validateAwsApiRequest( - AwsApiRequest( - serviceName = "ecs", - operationName = "RunTask", - parameters = mapOf("TaskDefinition" to "my-task"), - servicePrefix = "ecs", - httpMethod = "POST", - ), - defaultConfig(), - ) - assertEquals(AwsApiRequestValidationStatus.SKIPPED, result.status) - assertTrue(result.resourceTypes.isEmpty(), "unregistered operation must not produce resource types") - assertNull(result.report) - } - // ── SchemaValidator ────────────────────────────────────────────────────── @Test diff --git a/src/bindings-python/README.md b/src/bindings-python/README.md index d6959db2..26c4be48 100644 --- a/src/bindings-python/README.md +++ b/src/bindings-python/README.md @@ -87,7 +87,10 @@ else: `AwsApiRequest.parameters` accepts nested mappings and sequences, scalars, `bytes`, and `datetime.datetime` values without mutating the supplied mapping. `TemplateBody` bytes are validated exactly; `TemplateURL` is skipped because the validator does not perform network requests. The result always reports `status`, `operation_kind`, `template_source`, -`resource_types`, and `reason`; skipped requests have `report is None`. +`resource_types`, and `reason`; skipped requests have `report is None`. The `template` field carries the exact bytes +validated — the caller's original `TemplateBody` without reserializing, or the synthesized JSON for adapter-mapped +requests — so consumers can display the modeled template that produced the diagnostics. Skipped requests have +`template is None`. Operation-to-resource mapping uses a deterministic closed adapter catalog generated from each resource type's own provider handler metadata and verified against botocore models and the compiled CloudFormation schemas: only diff --git a/src/bindings-python/python/cloudformation_validate/__init__.py b/src/bindings-python/python/cloudformation_validate/__init__.py index a70bedd6..e7cc29a4 100644 --- a/src/bindings-python/python/cloudformation_validate/__init__.py +++ b/src/bindings-python/python/cloudformation_validate/__init__.py @@ -227,7 +227,10 @@ class AwsApiRequest: ``parameters`` accepts the same Python values used by botocore request dictionaries, including nested mappings/sequences, ``bytes``, and ``datetime.datetime``. Values that cannot be represented are carried as an - explicit unsupported marker and are conservatively omitted during synthesis. + explicit unsupported marker. Synthesis enforces all-or-nothing semantics: + if any supplied non-control resource-state field lacks a lossless mapping, + the entire synthesis/validation is skipped with a reason naming the + offending parameter — no parameter is ever silently omitted. """ def __init__( @@ -330,6 +333,8 @@ def validate_aws_api_request( """Classifies, models, and validates an AWS API request. A skipped request has ``report is None`` and an explicit status and reason. + The ``template`` field carries the exact bytes validated (the caller's + original ``TemplateBody`` or the synthesized JSON), or ``None`` when skipped. """ if not isinstance(request, AwsApiRequest): raise TypeError("request must be an AwsApiRequest") diff --git a/src/bindings-python/tests/smoke_test.py b/src/bindings-python/tests/smoke_test.py index 613fcea4..058a60e7 100644 --- a/src/bindings-python/tests/smoke_test.py +++ b/src/bindings-python/tests/smoke_test.py @@ -15,10 +15,6 @@ import cloudformation_validate._native as native_loader from cloudformation_validate import ( AdditionalSchemaSource, - AwsApiOperationKind, - AwsApiRequest, - AwsApiRequestValidationStatus, - AwsApiTemplateSource, CelEngine, EngineConfig, EntityType, @@ -185,111 +181,6 @@ def test_unparseable_template_reports_error_status(self): self.assertTrue(report.diagnostics, "parse failure must surface as a diagnostic") -class AwsApiRequestValidationTest(unittest.TestCase): - def test_synthesized_create_validates_with_both_engines(self): - parameters = {"Bucket": "synthetic-bucket"} - request = AwsApiRequest( - "s3", - "CreateBucket", - parameters, - service_prefix="s3", - http_method="POST", - ) - results = [engine.validate_aws_api_request(request) for engine in (REGO, CEL)] - - for result in results: - self.assertEqual(AwsApiRequestValidationStatus.VALIDATED, result.status) - self.assertEqual(AwsApiOperationKind.CLOUD_FORMATION_CREATE, result.operation_kind) - self.assertEqual(AwsApiTemplateSource.SYNTHESIZED_CREATE, result.template_source) - self.assertEqual(["AWS::S3::Bucket"], result.resource_types) - self.assertIsNotNone(result.report) - self.assertEqual(diagnostic_keys(results[0].report), diagnostic_keys(results[1].report)) - self.assertEqual({"Bucket": "synthetic-bucket"}, parameters) - - def test_template_body_bytes_are_validated_exactly(self): - request = AwsApiRequest( - "cloudformation", - "CreateChangeSet", - {"TemplateBody": b'{"Resources":{}}'}, - service_prefix="cloudformation", - http_method="POST", - ) - - result = REGO.validate_aws_api_request(request) - - self.assertEqual(AwsApiRequestValidationStatus.VALIDATED, result.status) - self.assertEqual(AwsApiTemplateSource.TEMPLATE_BODY, result.template_source) - self.assertEqual(ReportStatus.OK, result.report.status) - - def test_read_only_request_reports_explicit_skip(self): - request = AwsApiRequest( - "iam", - "GetRole", - {"RoleName": "Synthetic"}, - service_prefix="iam", - http_method="POST", - ) - - result = REGO.validate_aws_api_request(request) - - self.assertEqual(AwsApiRequestValidationStatus.SKIPPED, result.status) - self.assertEqual(AwsApiOperationKind.READ_ONLY, result.operation_kind) - self.assertIsNone(result.report) - self.assertIn("read-only", result.reason) - - def test_partial_update_diagnostics_are_scoped_and_counts_match(self): - request = AwsApiRequest( - "lambda", - "UpdateFunctionConfiguration", - {"FunctionName": "Synthetic", "MemorySize": 0}, - service_prefix="lambda", - http_method="POST", - ) - - result = REGO.validate_aws_api_request(request) - report = result.report - - self.assertEqual(AwsApiTemplateSource.SYNTHESIZED_UPDATE, result.template_source) - self.assertTrue( - all(d.property_path and "MemorySize" in d.property_path for d in report.diagnostics), - report.diagnostics, - ) - counts = report.metadata.counts - self.assertEqual( - len(report.diagnostics), - counts.fatal + counts.errors + counts.warnings + counts.informational + counts.debug, - ) - - def test_unregistered_operation_never_maps_to_resource_type(self): - request = AwsApiRequest( - "ecs", - "RunTask", - {"TaskDefinition": "my-task"}, - service_prefix="ecs", - http_method="POST", - ) - - result = REGO.validate_aws_api_request(request) - - self.assertEqual(AwsApiRequestValidationStatus.SKIPPED, result.status) - self.assertEqual([], result.resource_types) - self.assertIsNone(result.report) - - def test_java_sdk_service_name_casing_resolves_adapter(self): - request = AwsApiRequest( - "S3", - "CreateBucket", - {"Bucket": "synthetic-bucket"}, - service_prefix="S3", - http_method="PUT", - ) - - result = REGO.validate_aws_api_request(request) - - self.assertEqual(AwsApiOperationKind.CLOUD_FORMATION_CREATE, result.operation_kind) - self.assertEqual(["AWS::S3::Bucket"], result.resource_types) - - class AdditionalSchemasTest(unittest.TestCase): def test_additional_schemas_apply_through_the_public_config_on_both_engines(self): from cloudformation_validate import SchemaValidatorConfig diff --git a/src/data-source/README.md b/src/data-source/README.md index f0e6f0e0..0b016850 100644 --- a/src/data-source/README.md +++ b/src/data-source/README.md @@ -13,6 +13,13 @@ cargo run -p data-source --features maintenance --example generate # Refresh all upstream sources, then generate every output (cfn-lint root is required) cargo run -p data-source --features maintenance --example sync -- --cfn-lint-root + +# Generate the AWS API operation catalog; unit tests run first +PYTHONPATH= \ + python3 data-source/scripts/generate_aws_api_catalog.py \ + --provider-schemas data-source/upstream/schemas \ + --compiled-schemas data-source/generated/schema-validator/compiled_schemas.json \ + --output data-source/generated/data/aws_api_operation_catalog.json ``` The `generate` and `sync` examples require the `maintenance` feature, which enables dependencies used only by the @@ -38,28 +45,3 @@ data-source/ ├── cel-rules/ # CEL rule descriptors └── schema-validator/ # Compiled schemas for schema-validator ``` - -## AWS API operation catalog - -`generated/data/aws_api_operation_catalog.json` is produced by -`scripts/generate_aws_api_catalog.py`. The generator derives create and delete adapters from each resource type's own -handler permissions in the public -[`resource-provider-enhanced-schemas`](https://github.com/aws-cloudformation/resource-provider-enhanced-schemas) -release, resolves actions against a pinned botocore checkout, and verifies writable mappings against this repository's -compiled schemas. It rejects unavailable lifecycle operations, unsafe nested shapes, unreviewed operation collisions, -and known data-plane actions. Curated update adapters remain explicit because update requests carry partial state. - -Maintainers regenerate it only after the compiled schemas have been generated: - -```bash -PYTHONPATH= \ -python3 scripts/generate_aws_api_catalog.py \ - --provider-schemas \ - --compiled-schemas generated/schema-validator/compiled_schemas.json \ - --output generated/data/aws_api_operation_catalog.json -``` - -The output records SHA-256 hashes for both schema inputs, the botocore version and service count, and source/type counts. -Use the same enhanced-schema release, compiled-schema artifact, and botocore version to reproduce a catalog byte for -byte. Run `python3 -m unittest scripts/test_generate_aws_api_catalog.py` with that botocore checkout on `PYTHONPATH` -before committing a maintainer-generated artifact. diff --git a/src/data-source/scripts/generate_aws_api_catalog.py b/src/data-source/scripts/generate_aws_api_catalog.py index f157a4a1..a986b754 100644 --- a/src/data-source/scripts/generate_aws_api_catalog.py +++ b/src/data-source/scripts/generate_aws_api_catalog.py @@ -38,6 +38,7 @@ import argparse import hashlib import json +import subprocess import sys import zipfile from collections import defaultdict @@ -99,6 +100,7 @@ {'source': 'Timeout', 'target': 'Timeout'}, {'source': 'MemorySize', 'target': 'MemorySize'}, ], + 'ignored_inputs': ['FunctionName'], }, ] @@ -122,6 +124,41 @@ ('quicksight', 'DeleteTopic'), }) +# Explicit input member names safe to ignore during all-or-nothing synthesis. +# These are request-control fields that do not represent desired resource state. +# Detection: by exact name match from this curated set, or botocore shape +# metadata (idempotencyToken trait). +IGNORED_INPUT_NAMES = frozenset({ + 'ClientToken', + 'ClientRequestToken', + 'IdempotencyToken', + 'RequestToken', + 'DryRun', +}) + + +def _ignored_inputs_for_operation(members, phase, service, operation): + """Determine which input members are safe to ignore. + + Returns a sorted list of member names that the runtime can discard without + affecting state validation. Only exact name matches against the curated + request-control set and botocore idempotency-token metadata qualify. + """ + ignored = set() + for name, shape in members.items(): + if name in IGNORED_INPUT_NAMES: + ignored.add(name) + elif getattr(shape, 'metadata', None) and shape.metadata.get( + 'idempotencyToken' + ): + ignored.add(name) + elif hasattr(shape, 'serialization') and isinstance( + shape.serialization, dict + ) and shape.serialization.get('idempotencyToken'): + ignored.add(name) + return sorted(ignored) + + # Known-good pairs the derivation must reproduce exactly; guards regressions # in the derivation rules themselves. EXPECTED_PAIRS = { @@ -186,6 +223,11 @@ def __init__(self): def service_count(self): return len(self._operations) + @property + def operation_count(self): + """Total number of operations across all services.""" + return sum(len(ops) for ops in self._operations.values()) + def input_members(self, service, operation): model = self._session.get_service_model(service) shape = model.operation_model(operation).input_shape @@ -532,6 +574,9 @@ def _derive_role(role, verbs, provider_schemas, compiled_schemas, index, require {'source': source, 'target': target} for source, target in mappings ], + 'ignored_inputs': _ignored_inputs_for_operation( + index.input_members(top[4], top[5]), role, top[4], top[5] + ), 'noun_matched': noun, } counters['verified'] += 1 @@ -573,12 +618,14 @@ def _verify_curated_updates(compiled_schemas, index): ) property_schemas, read_only, primary, definitions = constraints members = index.input_members(adapter['service'], adapter['operation']) + mapping_sources = set() for mapping in adapter['mappings']: if mapping['source'] not in members: raise SystemExit( f"curated mapping source {mapping['source']} is not an input of " f"{adapter['service']}:{adapter['operation']}" ) + mapping_sources.add(mapping['source']) target = mapping['target'] if target not in property_schemas or target in read_only or target in primary: raise SystemExit( @@ -592,10 +639,129 @@ def _verify_curated_updates(compiled_schemas, index): f"curated mapping {mapping['source']} -> {target} is not " "runtime shape-compatible" ) + for ignored_name in adapter.get('ignored_inputs', []): + if ignored_name not in members: + raise SystemExit( + f"curated ignored_inputs entry '{ignored_name}' is not an input of " + f"{adapter['service']}:{adapter['operation']}" + ) + if ignored_name in mapping_sources: + raise SystemExit( + f"curated ignored_inputs entry '{ignored_name}' overlaps a mapping " + f"source in {adapter['service']}:{adapter['operation']}" + ) + + +def _compute_coverage(unique_adapters, index, compiled_schemas): + """Compute catalog and state-validation coverage metrics. + + Catalog coverage counts all adapters regardless of phase. + State-validation coverage counts only create/update adapters with at least + one property mapping. + + Denominators: + services — botocore available services (index.service_count) + resources — compiled CloudFormation schema types (len(compiled_schemas)) + commands — total botocore operations (index.operation_count) + writable_properties — unique (type, property) pairs across all compiled + schemas excluding read-only properties + """ + botocore_services = index.service_count + botocore_operations = index.operation_count + compiled_types = len(compiled_schemas) + + writable_pairs = set() + for type_name, schema in compiled_schemas.items(): + if not isinstance(schema, dict): + continue + properties = schema.get('properties') or {} + read_only = set(schema.get('read_only_properties') or []) + for prop in set(properties) - read_only: + writable_pairs.add((type_name, prop)) + + state_adapters = [ + a for a in unique_adapters + if a['phase'] in ('create', 'update') and len(a.get('mappings', [])) > 0 + ] + + covered_writable_pairs = set() + for adapter in state_adapters: + for mapping in adapter.get('mappings', []): + covered_writable_pairs.add((adapter['cfn_type'], mapping['target'])) + + phases = defaultdict(int) + for adapter in unique_adapters: + phases[adapter['phase']] += 1 + + return { + 'catalog_services': { + 'covered': len({a['service'] for a in unique_adapters}), + 'total': botocore_services, + }, + 'catalog_resources': { + 'covered': len({a['cfn_type'] for a in unique_adapters}), + 'total': compiled_types, + }, + 'catalog_commands': { + 'covered': len(unique_adapters), + 'total': botocore_operations, + }, + 'state_services': { + 'covered': len({a['service'] for a in state_adapters}), + 'total': botocore_services, + }, + 'state_resources': { + 'covered': len({a['cfn_type'] for a in state_adapters}), + 'total': compiled_types, + }, + 'state_commands': { + 'covered': len(state_adapters), + 'total': botocore_operations, + }, + 'writable_properties': { + 'covered': len(covered_writable_pairs), + 'total': len(writable_pairs), + }, + 'lifecycle_adapters': dict(phases), + } + + +def _render_coverage(coverage): + """Render coverage metrics as human-readable lines.""" + lines = [] + for label in ( + 'catalog_services', 'catalog_resources', 'catalog_commands', + 'state_services', 'state_resources', 'state_commands', + 'writable_properties', + ): + entry = coverage[label] + covered = entry['covered'] + total = entry['total'] + percent = (covered / total * 100) if total > 0 else 0.0 + lines.append(f'{label}: {covered}/{total} ({percent:.1f}%)') + lifecycle = coverage.get('lifecycle_adapters', {}) + parts = ', '.join(f'{k} {v}' for k, v in sorted(lifecycle.items())) + lines.append(f'lifecycle_adapters: {parts}') + return lines + + +def _run_unit_tests(): + test_file = Path(__file__).with_name('test_generate_aws_api_catalog.py') + completed = subprocess.run( + [sys.executable, '-m', 'unittest', '-v', test_file.stem], + cwd=test_file.parent, + check=False, + ) + if completed.returncode != 0: + raise SystemExit( + 'catalog generator unit tests failed with exit code ' + f'{completed.returncode}' + ) def main(): args = _parse_args() + _run_unit_tests() compiled_schemas = json.loads(args.compiled_schemas.read_text()) provider_schemas = _load_provider_schemas(args.provider_schemas) index = BotocoreIndex() @@ -635,6 +801,8 @@ def main(): for adapter in unique_adapters: adapter.pop('noun_matched', None) + if not adapter.get('ignored_inputs'): + adapter.pop('ignored_inputs', None) unique_adapters.sort(key=lambda a: (a['cfn_type'], a['phase'])) document = { 'format_version': FORMAT_VERSION, @@ -655,17 +823,13 @@ def main(): args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(json.dumps(document, indent=1, sort_keys=True) + '\n') - phases = defaultdict(int) - for adapter in unique_adapters: - phases[adapter['phase']] += 1 + coverage = _compute_coverage(unique_adapters, index, compiled_schemas) print(f'create derivation: {dict(create_counters)}') print(f'delete derivation: {dict(delete_counters)}') print(f'uniqueness dropped: {len(dropped)}') - print( - f"catalog: {len(unique_adapters)} adapters " - f"({phases['create']} create, {phases['update']} update, " - f"{phases['delete']} delete) -> {args.output}" - ) + for line in _render_coverage(coverage): + print(line) + print(f"catalog: {len(unique_adapters)} adapters -> {args.output}") return 0 diff --git a/src/data-source/scripts/test_generate_aws_api_catalog.py b/src/data-source/scripts/test_generate_aws_api_catalog.py index 7e67d801..aa4bf722 100644 --- a/src/data-source/scripts/test_generate_aws_api_catalog.py +++ b/src/data-source/scripts/test_generate_aws_api_catalog.py @@ -8,10 +8,13 @@ class Shape: - def __init__(self, type_name, *, member=None, value=None): + def __init__(self, type_name, *, member=None, value=None, metadata=None, + serialization=None): self.type_name = type_name self.member = member self.value = value + self.metadata = metadata or {} + self.serialization = serialization or {} class CatalogGeneratorTest(unittest.TestCase): @@ -103,5 +106,223 @@ def test_provider_schema_directory_is_loaded_deterministically(self): self.assertEqual(first_hash, second_hash) +class IgnoredInputsTest(unittest.TestCase): + """Tests for _ignored_inputs_for_operation.""" + + def test_curated_name_is_ignored(self): + members = { + 'ClientToken': Shape('string'), + 'BucketName': Shape('string'), + } + result = catalog._ignored_inputs_for_operation(members, 'create', 's3', 'CreateBucket') + self.assertEqual(result, ['ClientToken']) + + def test_dry_run_is_ignored(self): + members = { + 'DryRun': Shape('boolean'), + 'InstanceId': Shape('string'), + } + result = catalog._ignored_inputs_for_operation(members, 'create', 'ec2', 'RunInstances') + self.assertEqual(result, ['DryRun']) + + def test_idempotency_token_metadata_is_detected(self): + members = { + 'Token': Shape('string', metadata={'idempotencyToken': True}), + 'Name': Shape('string'), + } + result = catalog._ignored_inputs_for_operation(members, 'create', 'test', 'CreateThing') + self.assertEqual(result, ['Token']) + + def test_idempotency_token_serialization_is_detected(self): + members = { + 'RequestId': Shape('string', serialization={'idempotencyToken': True}), + 'Data': Shape('string'), + } + result = catalog._ignored_inputs_for_operation(members, 'create', 'test', 'CreateThing') + self.assertEqual(result, ['RequestId']) + + def test_update_phase_does_not_add_curated_identifiers(self): + """_ignored_inputs_for_operation derives only request-control fields.""" + members = { + 'FunctionName': Shape('string'), + 'MemorySize': Shape('integer'), + } + result = catalog._ignored_inputs_for_operation( + members, 'update', 'lambda', 'UpdateFunctionConfiguration' + ) + self.assertNotIn('FunctionName', result) + + def test_no_heuristic_detection(self): + """Members not in the curated set or metadata are never ignored.""" + members = { + 'TokenValue': Shape('string'), + 'RequestId': Shape('string'), + 'Nonce': Shape('string'), + } + result = catalog._ignored_inputs_for_operation(members, 'create', 'test', 'CreateThing') + self.assertEqual(result, []) + + def test_returns_sorted(self): + members = { + 'DryRun': Shape('boolean'), + 'ClientToken': Shape('string'), + 'BucketName': Shape('string'), + } + result = catalog._ignored_inputs_for_operation(members, 'create', 's3', 'CreateBucket') + self.assertEqual(result, sorted(result)) + + +class CoverageMetricsTest(unittest.TestCase): + """Tests for _compute_coverage and _render_coverage with synthetic data.""" + + def _synthetic_coverage(self, adapters, botocore_operations=100, + botocore_services=10, compiled_schemas=None): + """Build a synthetic coverage computation.""" + if compiled_schemas is None: + compiled_schemas = { + 'AWS::Test::Type': { + 'properties': {'Name': {}, 'Arn': {}, 'Id': {}}, + 'read_only_properties': ['Arn'], + }, + } + + class FakeIndex: + def __init__(self, services, operations): + self.service_count = services + self.operation_count = operations + + index = FakeIndex(botocore_services, botocore_operations) + return catalog._compute_coverage(adapters, index, compiled_schemas) + + def test_zero_adapters_yields_zero_coverage(self): + coverage = self._synthetic_coverage([]) + self.assertEqual(coverage['catalog_services']['covered'], 0) + self.assertEqual(coverage['catalog_resources']['covered'], 0) + self.assertEqual(coverage['writable_properties']['covered'], 0) + self.assertEqual(coverage['catalog_commands']['covered'], 0) + self.assertEqual(coverage['state_commands']['covered'], 0) + + def test_single_create_adapter_coverage(self): + adapters = [{ + 'service': 'test', + 'operation': 'CreateThing', + 'cfn_type': 'AWS::Test::Type', + 'phase': 'create', + 'mappings': [{'source': 'Name', 'target': 'Name'}], + }] + coverage = self._synthetic_coverage(adapters) + self.assertEqual(coverage['catalog_services']['covered'], 1) + self.assertEqual(coverage['catalog_services']['total'], 10) + self.assertEqual(coverage['catalog_resources']['covered'], 1) + self.assertEqual(coverage['catalog_commands']['covered'], 1) + self.assertEqual(coverage['catalog_commands']['total'], 100) + self.assertEqual(coverage['state_commands']['covered'], 1) + self.assertEqual(coverage['state_services']['covered'], 1) + self.assertEqual(coverage['state_resources']['covered'], 1) + self.assertEqual(coverage['writable_properties']['covered'], 1) + # Total writable is 2 (Name + Id; Arn is read-only) + self.assertEqual(coverage['writable_properties']['total'], 2) + + def test_delete_adapter_does_not_count_as_state_validation(self): + adapters = [{ + 'service': 'test', + 'operation': 'DeleteThing', + 'cfn_type': 'AWS::Test::Type', + 'phase': 'delete', + 'mappings': [], + }] + coverage = self._synthetic_coverage(adapters) + self.assertEqual(coverage['state_commands']['covered'], 0) + self.assertEqual(coverage['catalog_commands']['covered'], 1) + self.assertEqual(coverage['lifecycle_adapters'], {'delete': 1}) + + def test_writable_properties_are_deduplicated_across_adapters(self): + """Two adapters mapping to the same (cfn_type, target) count once.""" + adapters = [ + { + 'service': 'test', + 'operation': 'Create', + 'cfn_type': 'AWS::Test::Type', + 'phase': 'create', + 'mappings': [{'source': 'Name', 'target': 'Name'}], + }, + { + 'service': 'test', + 'operation': 'Update', + 'cfn_type': 'AWS::Test::Type', + 'phase': 'update', + 'mappings': [{'source': 'Name', 'target': 'Name'}], + }, + ] + coverage = self._synthetic_coverage(adapters) + self.assertEqual(coverage['writable_properties']['covered'], 1) + + def test_render_coverage_formats_percentages(self): + coverage = { + 'catalog_services': {'covered': 3, 'total': 10}, + 'catalog_resources': {'covered': 5, 'total': 20}, + 'catalog_commands': {'covered': 7, 'total': 50}, + 'state_services': {'covered': 2, 'total': 10}, + 'state_resources': {'covered': 4, 'total': 20}, + 'state_commands': {'covered': 4, 'total': 50}, + 'writable_properties': {'covered': 15, 'total': 100}, + 'lifecycle_adapters': {'create': 4, 'delete': 3}, + } + lines = catalog._render_coverage(coverage) + self.assertIn('catalog_services: 3/10 (30.0%)', lines) + self.assertIn('catalog_resources: 5/20 (25.0%)', lines) + self.assertIn('catalog_commands: 7/50 (14.0%)', lines) + self.assertIn('state_services: 2/10 (20.0%)', lines) + self.assertIn('state_resources: 4/20 (20.0%)', lines) + self.assertIn('state_commands: 4/50 (8.0%)', lines) + self.assertIn('writable_properties: 15/100 (15.0%)', lines) + self.assertIn('lifecycle_adapters: create 4, delete 3', lines) + + def test_zero_total_does_not_divide_by_zero(self): + coverage = { + 'catalog_services': {'covered': 0, 'total': 0}, + 'catalog_resources': {'covered': 0, 'total': 0}, + 'catalog_commands': {'covered': 0, 'total': 0}, + 'state_services': {'covered': 0, 'total': 0}, + 'state_resources': {'covered': 0, 'total': 0}, + 'state_commands': {'covered': 0, 'total': 0}, + 'writable_properties': {'covered': 0, 'total': 0}, + 'lifecycle_adapters': {}, + } + lines = catalog._render_coverage(coverage) + self.assertTrue(all('0.0%' in line for line in lines if '/' in line)) + + def test_exact_percentage_calculation(self): + adapters = [ + { + 'service': 'svc0', + 'operation': 'Create', + 'cfn_type': 'AWS::A::B', + 'phase': 'create', + 'mappings': [{'source': 'X', 'target': 'Y'}], + }, + { + 'service': 'svc1', + 'operation': 'Update', + 'cfn_type': 'AWS::A::B', + 'phase': 'update', + 'mappings': [{'source': 'Z', 'target': 'W'}], + }, + ] + # 2 services out of 4, 2 commands out of 8 + compiled = {'AWS::A::B': {'properties': {'Y': {}, 'W': {}}, 'read_only_properties': []}} + coverage = self._synthetic_coverage( + adapters, botocore_operations=8, botocore_services=4, + compiled_schemas=compiled, + ) + self.assertEqual(coverage['catalog_services']['covered'], 2) + self.assertEqual(coverage['catalog_services']['total'], 4) + self.assertEqual(coverage['catalog_commands']['covered'], 2) + self.assertEqual(coverage['catalog_commands']['total'], 8) + self.assertEqual(coverage['state_commands']['covered'], 2) + self.assertEqual(coverage['writable_properties']['covered'], 2) + self.assertEqual(coverage['writable_properties']['total'], 2) + + if __name__ == "__main__": unittest.main() diff --git a/src/validation-engine/API.md b/src/validation-engine/API.md index ae72ee9f..9bda1ccb 100644 --- a/src/validation-engine/API.md +++ b/src/validation-engine/API.md @@ -79,6 +79,9 @@ an operation kind, validation status, optional template source, resource candida modeled template reached the normal validation pipeline; `Skipped` has no report and explains why. `AwsApiRequestValidation` contains an `Option` directly — detailed enrichment is not supported for synthesized API-request templates because there is no user-authored source to annotate with context. +The `template` field carries the exact bytes that were validated — the caller's original `TemplateBody` without +reserializing, or the synthesized JSON template for adapter-mapped requests — so consumers can display the modeled +template that produced the diagnostics. It is `None` when the request was skipped. Use `validate_aws_api_request_with_path` when the embedding application needs a custom report path. **Deterministic closed-adapter contract.** Operation-to-resource mapping uses a generated adapter catalog keyed by @@ -89,6 +92,14 @@ create and delete lifecycles for roughly seventy percent of all resource types p adapter declares one CloudFormation resource type with explicit request-parameter-to-property pairs. Unregistered operations never receive an *inferred* resource type and are classified as `UnmappedMutation` (or `DataPlaneMutation` for data-plane verbs) with `Skipped` status. + +**Strict all-supplied-state mapping.** Template synthesis is all-or-nothing: every request parameter the caller +supplies must either (a) map to a resource property with a representable value, or (b) be an explicitly safe-to-ignore +field (idempotency tokens, DryRun, or a declared primary identifier on update operations). If any supplied parameter +fails both conditions — because it has no mapping, or its value cannot be type-matched to the target property — +synthesis is SKIPPED and the reason names the offending parameter. This guarantees that validated templates faithfully +represent the full caller-supplied state: no parameter is ever silently omitted from the synthesized template. + Cloud Control `UpdateResource` and `DeleteResource` may report a known `TypeName` supplied explicitly by the request, but they never synthesize state. There is no fuzzy inference, substring matching, or generic property-name guessing. `TemplateBody` validation is restricted to the closed set of CloudFormation operations that accept it; diff --git a/src/validation-engine/src/aws_api.rs b/src/validation-engine/src/aws_api.rs index 6e2189d9..56108e1e 100644 --- a/src/validation-engine/src/aws_api.rs +++ b/src/validation-engine/src/aws_api.rs @@ -198,6 +198,14 @@ pub struct AwsApiRequestValidation { pub resource_types: Vec, pub reason: String, pub report: Option, + /// The exact template bytes that were validated, or `None` when the request + /// was skipped. For `TemplateBody` requests, this is the caller's original + /// bytes without reserializing. For synthesized requests, this is the + /// generated JSON template. Consumers can display this to show the modeled + /// template that produced the diagnostics. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[cfg_attr(feature = "uniffi-bindings", uniffi(default))] + pub template: Option>, } /// Classifies, models, and validates one AWS API request entirely offline. @@ -229,6 +237,7 @@ pub fn validate_aws_api_request_with_path( resource_types: synthesis.resource_types, reason: synthesis.reason, report: None, + template: None, }); }; @@ -246,6 +255,7 @@ pub fn validate_aws_api_request_with_path( resource_types: synthesis.resource_types, reason: synthesis.reason, report: Some(report.to_standard()), + template: Some(template), }) } #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)] @@ -269,6 +279,9 @@ struct OperationAdapter { phase: AdapterPhase, cfn_type: String, mappings: Vec, + /// Request-control fields safe to ignore during all-or-nothing synthesis. + #[serde(default)] + ignored_inputs: Vec, } #[derive(Debug, Deserialize)] @@ -823,7 +836,12 @@ fn adapter_template( )); }; - let properties = map_adapter_properties(&request.parameters, &schema, adapter)?; + let properties = match map_adapter_properties(&request.parameters, &schema, adapter)? { + AdapterMappingResult::Mapped(properties) => properties, + AdapterMappingResult::Skip(reason) => { + return Ok(Synthesis::skipped(reason, vec![type_name.clone()])); + } + }; if properties.is_empty() { return Ok(Synthesis::skipped("no request parameters map to resource properties", vec![type_name.clone()])); @@ -850,11 +868,17 @@ fn adapter_template( }) } +#[derive(Debug)] +enum AdapterMappingResult { + Mapped(BTreeMap), + Skip(String), +} + fn map_adapter_properties( parameters: &HashMap, schema: &ResourceSchemaMetadata, adapter: &OperationAdapter, -) -> Result, ValidationError> { +) -> Result { let mut excluded = schema.read_only_properties.clone(); if adapter.phase == AdapterPhase::Update { excluded.extend(schema.primary_identifier_properties.iter().cloned()); @@ -862,6 +886,7 @@ fn map_adapter_properties( let mut sources = BTreeSet::new(); let mut targets = BTreeSet::new(); + let mut mapped_sources: BTreeSet<&str> = BTreeSet::new(); let mut mapped = BTreeMap::new(); for mapping in &adapter.mappings { if !sources.insert(mapping.source.as_str()) { @@ -891,11 +916,42 @@ fn map_adapter_properties( let Some(value) = parameters.get(&mapping.source) else { continue; }; - if let Some(json_value) = mapped_value(value, accepted_types, &mapping.target) { - mapped.insert(mapping.target.clone(), json_value); + mapped_sources.insert(&mapping.source); + match mapped_value(value, accepted_types, &mapping.target) { + Some(json_value) => { + mapped.insert(mapping.target.clone(), json_value); + } + None => { + return Ok(AdapterMappingResult::Skip(format!( + "parameter '{}' cannot be represented as property '{}' on {}", + mapping.source, mapping.target, adapter.cfn_type + ))); + } + } + } + + // Build the set of parameters that are safe to ignore: explicitly declared + // ignored_inputs, plus primary identifier properties for update adapters. + let mut ignored: BTreeSet<&str> = adapter.ignored_inputs.iter().map(String::as_str).collect(); + if adapter.phase == AdapterPhase::Update { + ignored.extend(schema.primary_identifier_properties.iter().map(String::as_str)); + } + + // All-or-nothing: every supplied parameter must either be mapped or + // in the ignored set. + for param_name in parameters.keys() { + if mapped_sources.contains(param_name.as_str()) { + continue; } + if ignored.contains(param_name.as_str()) { + continue; + } + return Ok(AdapterMappingResult::Skip(format!( + "parameter '{}' has no mapping to a property on {}", + param_name, adapter.cfn_type + ))); } - Ok(mapped) + Ok(AdapterMappingResult::Mapped(mapped)) } fn resource_template( @@ -1089,6 +1145,7 @@ mod tests { phase: AdapterPhase::Create, cfn_type: "AWS::S3::Bucket".into(), mappings, + ignored_inputs: Vec::new(), }; match map_adapter_properties(&HashMap::new(), &schema, &adapter) .expect_err("malformed adapter must return an error") @@ -1185,9 +1242,16 @@ mod tests { let schema = schema_validator .resource_schema_metadata(&adapter.cfn_type) .unwrap_or_else(|| panic!("{} missing schema metadata", adapter.cfn_type)); - map_adapter_properties(&empty, &schema, adapter).unwrap_or_else(|error| { + let result = map_adapter_properties(&empty, &schema, adapter).unwrap_or_else(|error| { panic!("adapter {}:{} violates registry invariants: {error}", adapter.service, adapter.operation) }); + // Empty parameters always produce Mapped (no supplied params to conflict). + assert!( + matches!(result, AdapterMappingResult::Mapped(_)), + "adapter {}:{} must accept empty parameters", + adapter.service, + adapter.operation + ); } } @@ -1209,7 +1273,7 @@ mod tests { } #[test] - fn nested_values_are_omitted_without_recursive_shape_mappings() { + fn nested_values_are_rejected_without_recursive_shape_mappings() { let object_types = BTreeSet::from([PropertyValueType::Object]); let array_types = BTreeSet::from([PropertyValueType::Array]); let object = AwsApiValue::from_json(serde_json::json!({"lowerCamel": "value"})); @@ -1367,7 +1431,8 @@ mod tests { } #[test] - fn dynamodb_create_table_synthesizes_with_explicit_mappings() { + fn dynamodb_create_table_skips_when_nested_values_are_unrepresentable() { + let schema_validator = SchemaValidator::default(); let request = request( "dynamodb", "CreateTable", @@ -1378,6 +1443,25 @@ mod tests { "BillingMode": "PAY_PER_REQUEST" }), ); + let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); + assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.candidates, ["AWS::DynamoDB::Table"]); + let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none(), "nested struct arrays must skip synthesis"); + assert!( + synthesis.reason.contains("cannot be represented"), + "reason must explain the type mismatch: {}", + synthesis.reason + ); + } + + #[test] + fn dynamodb_create_table_synthesizes_with_scalar_only_parameters() { + let request = request( + "dynamodb", + "CreateTable", + serde_json::json!({"TableName": "Synthetic", "BillingMode": "PAY_PER_REQUEST"}), + ); let (classification, synthesis, document) = synthesized_json(&request); assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); assert_eq!(classification.candidates, ["AWS::DynamoDB::Table"]); @@ -1427,7 +1511,7 @@ mod tests { } #[test] - fn lambda_create_function_synthesizes_partial_and_scopes_diagnostics() { + fn lambda_create_function_synthesizes_all_supplied_scalar_properties() { let request = request("lambda", "CreateFunction", serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 128})); let (classification, synthesis, document) = synthesized_json(&request); @@ -1440,7 +1524,7 @@ mod tests { } #[test] - fn lambda_update_function_configuration_synthesizes_partial_update() { + fn lambda_update_function_configuration_maps_all_supplied_mutable_properties() { let request = request( "lambda", "UpdateFunctionConfiguration", @@ -1754,7 +1838,8 @@ mod tests { } } #[test] - fn incompatible_optional_property_is_omitted() { + fn incompatible_property_value_skips_synthesis() { + let schema_validator = SchemaValidator::default(); let request = request( "iam", "CreateRole", @@ -1764,10 +1849,14 @@ mod tests { "Tags": {"Key": 42} }), ); - let (_, _, document) = synthesized_json(&request); - let properties = &document["Resources"]["Resource"]["Properties"]; - assert_eq!(properties["RoleName"], "Synthetic"); - assert!(properties.get("Tags").is_none() || properties["Tags"].is_null()); + let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); + let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none(), "incompatible value must skip synthesis"); + assert!( + synthesis.reason.contains("cannot be represented"), + "reason must explain the type mismatch: {}", + synthesis.reason + ); } #[test] @@ -1781,6 +1870,11 @@ mod tests { assert_eq!(validation.status, AwsApiRequestValidationStatus::Validated); assert_eq!(validation.template_source, Some(AwsApiTemplateSource::TemplateBody)); assert!(validation.report.is_some()); + assert_eq!( + validation.template, + Some(br#"{"Resources":{}}"#.to_vec()), + "exact TemplateBody bytes must be preserved without reserializing" + ); let read = request("iam", "GetRole", serde_json::json!({"RoleName": "Synthetic"})); let validation = validate_aws_api_request(&engine, &schema_validator, &read, ValidateConfig::default()) @@ -1788,6 +1882,7 @@ mod tests { assert_eq!(validation.status, AwsApiRequestValidationStatus::Skipped); assert_eq!(validation.operation_kind, AwsApiOperationKind::ReadOnly); assert!(validation.report.is_none()); + assert_eq!(validation.template, None, "skipped requests must have template=None"); } #[test] @@ -1924,6 +2019,12 @@ mod tests { report.diagnostics.len() as u32, counts.fatal + counts.errors + counts.warnings + counts.informational + counts.debug, ); + // The template field carries the synthesized JSON used for validation. + let template_bytes = validation.template.expect("validated requests carry template bytes"); + let template_json: serde_json::Value = + serde_json::from_slice(&template_bytes).expect("template must be valid JSON"); + assert_eq!(template_json["Resources"]["Resource"]["Type"], "AWS::Lambda::Function"); + assert_eq!(template_json["Resources"]["Resource"]["Properties"]["MemorySize"], 0); } #[test] @@ -1996,4 +2097,117 @@ mod tests { assert_eq!(classification.kind, AwsApiOperationKind::UnmappedMutation); assert!(classification.candidates.is_empty()); } + + #[test] + fn unmapped_parameter_skips_synthesis_with_reason() { + let schema_validator = SchemaValidator::default(); + let req = request("s3", "CreateBucket", serde_json::json!({"Bucket": "test-bucket", "UnknownParam": "value"})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_none(), "unmapped parameter must skip synthesis"); + assert!( + synthesis.reason.contains("UnknownParam") && synthesis.reason.contains("no mapping"), + "reason must name the unmapped parameter: {}", + synthesis.reason + ); + } + + #[test] + fn ignored_input_does_not_block_synthesis() { + let schema_validator = SchemaValidator::default(); + let schema = schema_validator.resource_schema_metadata("AWS::S3::Bucket").expect("S3 bucket schema must exist"); + let adapter = OperationAdapter { + service: "s3".into(), + operation: "CreateBucket".into(), + phase: AdapterPhase::Create, + cfn_type: "AWS::S3::Bucket".into(), + mappings: vec![mapping("Bucket", "BucketName")], + ignored_inputs: vec!["ClientToken".into()], + }; + let parameters: HashMap = [ + ("Bucket".into(), AwsApiValue::String { value: "test".into() }), + ("ClientToken".into(), AwsApiValue::String { value: "idempotent-token".into() }), + ] + .into_iter() + .collect(); + let result = map_adapter_properties(¶meters, &schema, &adapter).expect("mapping must succeed"); + match result { + AdapterMappingResult::Mapped(properties) => { + assert_eq!(properties.len(), 1); + assert_eq!(properties["BucketName"], serde_json::json!("test")); + } + AdapterMappingResult::Skip(reason) => { + panic!("ignored input should not skip synthesis: {reason}"); + } + } + } + + #[test] + fn update_adapter_ignores_primary_identifier_parameters() { + let schema_validator = SchemaValidator::default(); + let schema = schema_validator + .resource_schema_metadata("AWS::Lambda::Function") + .expect("Lambda function schema must exist"); + let adapter = OperationAdapter { + service: "lambda".into(), + operation: "UpdateFunctionConfiguration".into(), + phase: AdapterPhase::Update, + cfn_type: "AWS::Lambda::Function".into(), + mappings: vec![mapping("MemorySize", "MemorySize")], + ignored_inputs: Vec::new(), + }; + // FunctionName is a primary identifier for AWS::Lambda::Function + let parameters: HashMap = [ + ("MemorySize".into(), AwsApiValue::Integer { value: 256 }), + ("FunctionName".into(), AwsApiValue::String { value: "my-func".into() }), + ] + .into_iter() + .collect(); + let result = map_adapter_properties(¶meters, &schema, &adapter).expect("mapping must succeed"); + match result { + AdapterMappingResult::Mapped(properties) => { + assert_eq!(properties.len(), 1); + assert_eq!(properties["MemorySize"], serde_json::json!(256)); + } + AdapterMappingResult::Skip(reason) => { + panic!("primary identifier on update must be ignored: {reason}"); + } + } + } + + #[test] + fn all_mapped_parameters_produce_successful_synthesis() { + let schema_validator = SchemaValidator::default(); + let req = request("s3", "CreateBucket", serde_json::json!({"Bucket": "all-mapped"})); + let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); + let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); + assert!(synthesis.template.is_some(), "all-mapped parameters must synthesize"); + } + + #[test] + fn catalog_ignored_inputs_deserialize_from_missing_field() { + let json = br#"{ + "format_version": 1, + "adapters": [ + {"service":"test","operation":"Create","phase":"create","cfn_type":"AWS::Test::Type","mappings":[]} + ] + }"#; + let registry = parse_adapter_registry(json).expect("catalog without ignored_inputs must parse"); + let adapter = registry.get(&("test".to_string(), "Create".to_string())).expect("adapter must exist"); + assert!(adapter.ignored_inputs.is_empty(), "missing field defaults to empty"); + } + + #[test] + fn catalog_ignored_inputs_deserialize_from_explicit_field() { + let json = br#"{ + "format_version": 1, + "adapters": [ + {"service":"test","operation":"Create","phase":"create","cfn_type":"AWS::Test::Type", + "mappings":[],"ignored_inputs":["ClientToken","DryRun"]} + ] + }"#; + let registry = parse_adapter_registry(json).expect("catalog with ignored_inputs must parse"); + let adapter = registry.get(&("test".to_string(), "Create".to_string())).expect("adapter must exist"); + assert_eq!(adapter.ignored_inputs, vec!["ClientToken", "DryRun"]); + } } From f7384010a851885e22ee718970667a8ff60343cf Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Wed, 19 Aug 2026 03:31:08 -0400 Subject: [PATCH 07/19] update catalog generation tool --- src/data-source/README.md | 26 +- .../data/aws_api_operation_catalog.json | 16697 +++++++--------- .../scripts/generate_aws_api_catalog.py | 196 +- .../scripts/test_generate_aws_api_catalog.py | 130 +- src/data-source/src/generate.rs | 2 +- src/data-source/src/lib.rs | 102 + src/data-source/src/sync.rs | 19 +- 7 files changed, 7884 insertions(+), 9288 deletions(-) diff --git a/src/data-source/README.md b/src/data-source/README.md index 0b016850..6284ed0a 100644 --- a/src/data-source/README.md +++ b/src/data-source/README.md @@ -8,26 +8,26 @@ compile time. Everything compiles into the binary - no runtime fetching. ## Commands ```bash -# Generate from existing upstream data +# Generate schema and rule artifacts from existing upstream data cargo run -p data-source --features maintenance --example generate -# Refresh all upstream sources, then generate every output (cfn-lint root is required) -cargo run -p data-source --features maintenance --example sync -- --cfn-lint-root - -# Generate the AWS API operation catalog; unit tests run first -PYTHONPATH= \ - python3 data-source/scripts/generate_aws_api_catalog.py \ - --provider-schemas data-source/upstream/schemas \ - --compiled-schemas data-source/generated/schema-validator/compiled_schemas.json \ - --output data-source/generated/data/aws_api_operation_catalog.json +# Refresh every upstream source and generate every output, including the AWS API operation catalog +cargo run -p data-source --features maintenance --example sync -- \ + --cfn-lint-root \ + --aws-cli-root ``` The `generate` and `sync` examples require the `maintenance` feature, which enables dependencies used only by the data maintenance pipeline. `sync` is the complete workflow: it refreshes every upstream source, records source -versions, and generates all outputs. `generate` reruns code generation from the existing upstream data without network -access. +versions, generates the schema and rule outputs, then generates and verifies the AWS API operation catalog. Pass the +AWS CLI checkout root through `--aws-cli-root`; sync derives its bundled botocore package path, and the catalog +generator runs its unit tests before generating the catalog. + +`generate` reruns schema and rule generation from existing upstream data without network access. It does not rebuild +the AWS API operation catalog because that step requires botocore service models and is owned by the complete `sync` +workflow. -`--cfn-lint-root` is required by `sync`, which fails before starting work when it is absent. +`--cfn-lint-root` and `--aws-cli-root` are required by `sync`, which fails before starting work when either is absent. A successful sync records both strict, source-qualified values together only after all source processing succeeds. ## Directory Structure diff --git a/src/data-source/generated/data/aws_api_operation_catalog.json b/src/data-source/generated/data/aws_api_operation_catalog.json index 5416d8b7..22a69e0d 100644 --- a/src/data-source/generated/data/aws_api_operation_catalog.json +++ b/src/data-source/generated/data/aws_api_operation_catalog.json @@ -2,11 +2,10 @@ "adapters": [ { "cfn_type": "AWS::ACMPCA::Certificate", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ - { - "source": "ApiPassthrough", - "target": "ApiPassthrough" - }, { "source": "CertificateAuthorityArn", "target": "CertificateAuthorityArn" @@ -18,14 +17,6 @@ { "source": "TemplateArn", "target": "TemplateArn" - }, - { - "source": "Validity", - "target": "Validity" - }, - { - "source": "ValidityNotBefore", - "target": "ValidityNotBefore" } ], "operation": "IssueCertificate", @@ -34,19 +25,14 @@ }, { "cfn_type": "AWS::ACMPCA::CertificateAuthority", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "KeyStorageSecurityStandard", "target": "KeyStorageSecurityStandard" }, - { - "source": "RevocationConfiguration", - "target": "RevocationConfiguration" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UsageMode", "target": "UsageMode" @@ -63,26 +49,6 @@ "phase": "delete", "service": "acm-pca" }, - { - "cfn_type": "AWS::ACMPCA::CertificateAuthorityActivation", - "mappings": [ - { - "source": "Certificate", - "target": "Certificate" - }, - { - "source": "CertificateAuthorityArn", - "target": "CertificateAuthorityArn" - }, - { - "source": "CertificateChain", - "target": "CertificateChain" - } - ], - "operation": "ImportCertificateAuthorityCertificate", - "phase": "create", - "service": "acm-pca" - }, { "cfn_type": "AWS::ACMPCA::Permission", "mappings": [ @@ -130,10 +96,6 @@ { "cfn_type": "AWS::AIOps::InvestigationGroup", "mappings": [ - { - "source": "crossAccountConfigurations", - "target": "CrossAccountConfigurations" - }, { "source": "isCloudTrailEventHistoryEnabled", "target": "IsCloudTrailEventHistoryEnabled" @@ -171,12 +133,69 @@ "service": "aiops" }, { - "cfn_type": "AWS::APS::RuleGroupsNamespace", + "cfn_type": "AWS::APS::AnomalyDetector", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "data", - "target": "Data" + "source": "alias", + "target": "Alias" + }, + { + "source": "evaluationIntervalInSeconds", + "target": "EvaluationIntervalInSeconds" }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAnomalyDetector", + "phase": "create", + "service": "amp" + }, + { + "cfn_type": "AWS::APS::AnomalyDetector", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [], + "operation": "DeleteAnomalyDetector", + "phase": "delete", + "service": "amp" + }, + { + "cfn_type": "AWS::APS::ResourcePolicy", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "policyDocument", + "target": "PolicyDocument" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "amp" + }, + { + "cfn_type": "AWS::APS::ResourcePolicy", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "amp" + }, + { + "cfn_type": "AWS::APS::RuleGroupsNamespace", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ { "source": "name", "target": "Name" @@ -192,6 +211,9 @@ }, { "cfn_type": "AWS::APS::RuleGroupsNamespace", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "name", @@ -204,27 +226,14 @@ }, { "cfn_type": "AWS::APS::Scraper", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "alias", "target": "Alias" }, - { - "source": "destination", - "target": "Destination" - }, - { - "source": "roleConfiguration", - "target": "RoleConfiguration" - }, - { - "source": "scrapeConfiguration", - "target": "ScrapeConfiguration" - }, - { - "source": "source", - "target": "Source" - }, { "source": "tags", "target": "Tags" @@ -236,6 +245,9 @@ }, { "cfn_type": "AWS::APS::Scraper", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteScraper", "phase": "delete", @@ -243,6 +255,9 @@ }, { "cfn_type": "AWS::APS::Workspace", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "alias", @@ -263,6 +278,9 @@ }, { "cfn_type": "AWS::APS::Workspace", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteWorkspace", "phase": "delete", @@ -271,10 +289,6 @@ { "cfn_type": "AWS::ARCRegionSwitch::Plan", "mappings": [ - { - "source": "associatedAlarms", - "target": "AssociatedAlarms" - }, { "source": "description", "target": "Description" @@ -302,18 +316,6 @@ { "source": "regions", "target": "Regions" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "triggers", - "target": "Triggers" - }, - { - "source": "workflows", - "target": "Workflows" } ], "operation": "CreatePlan", @@ -329,15 +331,14 @@ }, { "cfn_type": "AWS::AccessAnalyzer::Analyzer", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "analyzerName", "target": "AnalyzerName" }, - { - "source": "archiveRules", - "target": "ArchiveRules" - }, { "source": "tags", "target": "Tags" @@ -353,6 +354,9 @@ }, { "cfn_type": "AWS::AccessAnalyzer::Analyzer", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "analyzerName", @@ -363,8 +367,130 @@ "phase": "delete", "service": "accessanalyzer" }, + { + "cfn_type": "AWS::AccessAnalyzer::ArchiveRule", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "analyzerName", + "target": "AnalyzerName" + }, + { + "source": "ruleName", + "target": "RuleName" + } + ], + "operation": "CreateArchiveRule", + "phase": "create", + "service": "accessanalyzer" + }, + { + "cfn_type": "AWS::AccessAnalyzer::ArchiveRule", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "analyzerName", + "target": "AnalyzerName" + }, + { + "source": "ruleName", + "target": "RuleName" + } + ], + "operation": "DeleteArchiveRule", + "phase": "delete", + "service": "accessanalyzer" + }, + { + "cfn_type": "AWS::AgentRegistry::Registry", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRegistry", + "phase": "create", + "service": "agent-registry-control" + }, + { + "cfn_type": "AWS::AgentRegistry::Registry", + "mappings": [], + "operation": "DeleteRegistry", + "phase": "delete", + "service": "agent-registry-control" + }, + { + "cfn_type": "AWS::AgentRegistry::RegistryRecord", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "recordType", + "target": "RecordType" + }, + { + "source": "recordVersion", + "target": "RecordVersion" + }, + { + "source": "registryId", + "target": "RegistryId" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRegistryRecord", + "phase": "create", + "service": "agent-registry-control" + }, + { + "cfn_type": "AWS::AgentRegistry::RegistryRecord", + "mappings": [ + { + "source": "registryId", + "target": "RegistryId" + } + ], + "operation": "DeleteRegistryRecord", + "phase": "delete", + "service": "agent-registry-control" + }, { "cfn_type": "AWS::AmazonMQ::Broker", + "ignored_inputs": [ + "CreatorRequestId" + ], "mappings": [ { "source": "AuthenticationStrategy", @@ -378,10 +504,6 @@ "source": "BrokerName", "target": "BrokerName" }, - { - "source": "Configuration", - "target": "Configuration" - }, { "source": "DataReplicationMode", "target": "DataReplicationMode" @@ -394,10 +516,6 @@ "source": "DeploymentMode", "target": "DeploymentMode" }, - { - "source": "EncryptionOptions", - "target": "EncryptionOptions" - }, { "source": "EngineType", "target": "EngineType" @@ -410,18 +528,6 @@ "source": "HostInstanceType", "target": "HostInstanceType" }, - { - "source": "LdapServerMetadata", - "target": "LdapServerMetadata" - }, - { - "source": "Logs", - "target": "Logs" - }, - { - "source": "MaintenanceWindowStartTime", - "target": "MaintenanceWindowStartTime" - }, { "source": "PubliclyAccessible", "target": "PubliclyAccessible" @@ -430,6 +536,10 @@ "source": "SecurityGroups", "target": "SecurityGroups" }, + { + "source": "StorageSize", + "target": "StorageSize" + }, { "source": "StorageType", "target": "StorageType" @@ -441,10 +551,6 @@ { "source": "Tags", "target": "Tags" - }, - { - "source": "Users", - "target": "Users" } ], "operation": "CreateBroker", @@ -500,18 +606,10 @@ "source": "accessToken", "target": "AccessToken" }, - { - "source": "autoBranchCreationConfig", - "target": "AutoBranchCreationConfig" - }, { "source": "buildSpec", "target": "BuildSpec" }, - { - "source": "cacheConfig", - "target": "CacheConfig" - }, { "source": "computeRoleArn", "target": "ComputeRoleArn" @@ -520,10 +618,6 @@ "source": "customHeaders", "target": "CustomHeaders" }, - { - "source": "customRules", - "target": "CustomRules" - }, { "source": "description", "target": "Description" @@ -532,14 +626,6 @@ "source": "enableBranchAutoDeletion", "target": "EnableBranchAutoDeletion" }, - { - "source": "environmentVariables", - "target": "EnvironmentVariables" - }, - { - "source": "jobConfig", - "target": "JobConfig" - }, { "source": "name", "target": "Name" @@ -579,10 +665,6 @@ "source": "appId", "target": "AppId" }, - { - "source": "backend", - "target": "Backend" - }, { "source": "branchName", "target": "BranchName" @@ -615,10 +697,6 @@ "source": "enableSkewProtection", "target": "EnableSkewProtection" }, - { - "source": "environmentVariables", - "target": "EnvironmentVariables" - }, { "source": "framework", "target": "Framework" @@ -671,10 +749,6 @@ "source": "autoSubDomainIAMRole", "target": "AutoSubDomainIAMRole" }, - { - "source": "certificateSettings", - "target": "CertificateSettings" - }, { "source": "domainName", "target": "DomainName" @@ -682,10 +756,6 @@ { "source": "enableAutoSubDomain", "target": "EnableAutoSubDomain" - }, - { - "source": "subDomainSettings", - "target": "SubDomainSettings" } ], "operation": "CreateDomainAssociation", @@ -710,6 +780,9 @@ }, { "cfn_type": "AWS::AmplifyUIBuilder::Component", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "appId", @@ -742,6 +815,9 @@ }, { "cfn_type": "AWS::AmplifyUIBuilder::Form", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "appId", @@ -774,6 +850,9 @@ }, { "cfn_type": "AWS::AmplifyUIBuilder::Theme", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "appId", @@ -805,16 +884,35 @@ "service": "amplifyuibuilder" }, { - "cfn_type": "AWS::ApiGatewayV2::RoutingRule", + "cfn_type": "AWS::ApiGatewayV2::PortalProduct", "mappings": [ { - "source": "Actions", - "target": "Actions" + "source": "Description", + "target": "Description" }, { - "source": "Conditions", - "target": "Conditions" + "source": "DisplayName", + "target": "DisplayName" }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePortalProduct", + "phase": "create", + "service": "apigatewayv2" + }, + { + "cfn_type": "AWS::ApiGatewayV2::PortalProduct", + "mappings": [], + "operation": "DeletePortalProduct", + "phase": "delete", + "service": "apigatewayv2" + }, + { + "cfn_type": "AWS::ApiGatewayV2::RoutingRule", + "mappings": [ { "source": "Priority", "target": "Priority" @@ -892,10 +990,6 @@ { "source": "Type", "target": "Type" - }, - { - "source": "Validators", - "target": "Validators" } ], "operation": "CreateConfigurationProfile", @@ -941,10 +1035,6 @@ "source": "Description", "target": "Description" }, - { - "source": "DynamicExtensionParameters", - "target": "DynamicExtensionParameters" - }, { "source": "EnvironmentId", "target": "EnvironmentId" @@ -1020,10 +1110,6 @@ "source": "Description", "target": "Description" }, - { - "source": "Monitors", - "target": "Monitors" - }, { "source": "Name", "target": "Name" @@ -1054,12 +1140,68 @@ "service": "appconfig" }, { - "cfn_type": "AWS::AppConfig::Extension", + "cfn_type": "AWS::AppConfig::ExperimentDefinition", "mappings": [ { - "source": "Actions", - "target": "Actions" + "source": "ApplicationIdentifier", + "target": "ApplicationIdentifier" + }, + { + "source": "AudienceDescription", + "target": "AudienceDescription" + }, + { + "source": "AudienceRule", + "target": "AudienceRule" + }, + { + "source": "ConfigurationProfileIdentifier", + "target": "ConfigurationProfileIdentifier" }, + { + "source": "EnvironmentIdentifier", + "target": "EnvironmentIdentifier" + }, + { + "source": "FlagKey", + "target": "FlagKey" + }, + { + "source": "Hypothesis", + "target": "Hypothesis" + }, + { + "source": "LaunchCriteria", + "target": "LaunchCriteria" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateExperimentDefinition", + "phase": "create", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::ExperimentDefinition", + "mappings": [ + { + "source": "ApplicationIdentifier", + "target": "ApplicationIdentifier" + } + ], + "operation": "DeleteExperimentDefinition", + "phase": "delete", + "service": "appconfig" + }, + { + "cfn_type": "AWS::AppConfig::Extension", + "mappings": [ { "source": "Description", "target": "Description" @@ -1072,10 +1214,6 @@ "source": "Name", "target": "Name" }, - { - "source": "Parameters", - "target": "Parameters" - }, { "source": "Tags", "target": "Tags" @@ -1103,10 +1241,6 @@ "source": "ExtensionVersionNumber", "target": "ExtensionVersionNumber" }, - { - "source": "Parameters", - "target": "Parameters" - }, { "source": "ResourceIdentifier", "target": "ResourceIdentifier" @@ -1138,10 +1272,6 @@ "source": "ConfigurationProfileId", "target": "ConfigurationProfileId" }, - { - "source": "Content", - "target": "Content" - }, { "source": "ContentType", "target": "ContentType" @@ -1181,15 +1311,14 @@ }, { "cfn_type": "AWS::AppFlow::Connector", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "connectorLabel", "target": "ConnectorLabel" }, - { - "source": "connectorProvisioningConfig", - "target": "ConnectorProvisioningConfig" - }, { "source": "connectorProvisioningType", "target": "ConnectorProvisioningType" @@ -1205,6 +1334,9 @@ }, { "cfn_type": "AWS::AppFlow::ConnectorProfile", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "connectionMode", @@ -1214,10 +1346,6 @@ "source": "connectorLabel", "target": "ConnectorLabel" }, - { - "source": "connectorProfileConfig", - "target": "ConnectorProfileConfig" - }, { "source": "connectorProfileName", "target": "ConnectorProfileName" @@ -1249,15 +1377,14 @@ }, { "cfn_type": "AWS::AppFlow::Flow", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", "target": "Description" }, - { - "source": "destinationFlowConfigList", - "target": "DestinationFlowConfigList" - }, { "source": "flowName", "target": "FlowName" @@ -1266,25 +1393,9 @@ "source": "kmsArn", "target": "KMSArn" }, - { - "source": "metadataCatalogConfig", - "target": "MetadataCatalogConfig" - }, - { - "source": "sourceFlowConfig", - "target": "SourceFlowConfig" - }, { "source": "tags", "target": "Tags" - }, - { - "source": "tasks", - "target": "Tasks" - }, - { - "source": "triggerConfig", - "target": "TriggerConfig" } ], "operation": "CreateFlow", @@ -1305,23 +1416,18 @@ }, { "cfn_type": "AWS::AppIntegrations::Application", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { - "source": "ApplicationConfig", - "target": "ApplicationConfig" - }, - { - "source": "ApplicationSourceConfig", - "target": "ApplicationSourceConfig" + "source": "ApplicationType", + "target": "ApplicationType" }, { "source": "Description", "target": "Description" }, - { - "source": "IframeConfig", - "target": "IframeConfig" - }, { "source": "InitializationTimeout", "target": "InitializationTimeout" @@ -1360,15 +1466,14 @@ }, { "cfn_type": "AWS::AppIntegrations::DataIntegration", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", "target": "Description" }, - { - "source": "FileConfiguration", - "target": "FileConfiguration" - }, { "source": "KmsKey", "target": "KmsKey" @@ -1377,14 +1482,6 @@ "source": "Name", "target": "Name" }, - { - "source": "ObjectConfiguration", - "target": "ObjectConfiguration" - }, - { - "source": "ScheduleConfig", - "target": "ScheduleConfig" - }, { "source": "SourceURI", "target": "SourceURI" @@ -1407,6 +1504,9 @@ }, { "cfn_type": "AWS::AppIntegrations::EventIntegration", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -1416,10 +1516,6 @@ "source": "EventBridgeBus", "target": "EventBridgeBus" }, - { - "source": "EventFilter", - "target": "EventFilter" - }, { "source": "Name", "target": "Name" @@ -1463,10 +1559,6 @@ { "source": "MinSize", "target": "MinSize" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAutoScalingConfiguration", @@ -1486,14 +1578,6 @@ { "source": "ObservabilityConfigurationName", "target": "ObservabilityConfigurationName" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "TraceConfiguration", - "target": "TraceConfiguration" } ], "operation": "CreateObservabilityConfiguration", @@ -1514,37 +1598,9 @@ "source": "AutoScalingConfigurationArn", "target": "AutoScalingConfigurationArn" }, - { - "source": "EncryptionConfiguration", - "target": "EncryptionConfiguration" - }, - { - "source": "HealthCheckConfiguration", - "target": "HealthCheckConfiguration" - }, - { - "source": "InstanceConfiguration", - "target": "InstanceConfiguration" - }, - { - "source": "NetworkConfiguration", - "target": "NetworkConfiguration" - }, - { - "source": "ObservabilityConfiguration", - "target": "ObservabilityConfiguration" - }, { "source": "ServiceName", "target": "ServiceName" - }, - { - "source": "SourceConfiguration", - "target": "SourceConfiguration" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateService", @@ -1569,10 +1625,6 @@ "source": "Subnets", "target": "Subnets" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VpcConnectorName", "target": "VpcConnectorName" @@ -1592,18 +1644,10 @@ { "cfn_type": "AWS::AppRunner::VpcIngressConnection", "mappings": [ - { - "source": "IngressVpcConfiguration", - "target": "IngressVpcConfiguration" - }, { "source": "ServiceArn", "target": "ServiceArn" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VpcIngressConnectionName", "target": "VpcIngressConnectionName" @@ -1639,18 +1683,6 @@ "source": "PackagingType", "target": "PackagingType" }, - { - "source": "PostSetupScriptDetails", - "target": "PostSetupScriptDetails" - }, - { - "source": "SetupScriptDetails", - "target": "SetupScriptDetails" - }, - { - "source": "SourceS3Location", - "target": "SourceS3Location" - }, { "source": "Tags", "target": "Tags" @@ -1675,10 +1707,6 @@ { "cfn_type": "AWS::AppStream::AppBlockBuilder", "mappings": [ - { - "source": "AccessEndpoints", - "target": "AccessEndpoints" - }, { "source": "Description", "target": "Description" @@ -1710,10 +1738,6 @@ { "source": "Tags", "target": "Tags" - }, - { - "source": "VpcConfig", - "target": "VpcConfig" } ], "operation": "CreateAppBlockBuilder", @@ -1747,10 +1771,6 @@ "source": "DisplayName", "target": "DisplayName" }, - { - "source": "IconS3Location", - "target": "IconS3Location" - }, { "source": "InstanceFamilies", "target": "InstanceFamilies" @@ -1871,10 +1891,6 @@ { "cfn_type": "AWS::AppStream::DirectoryConfig", "mappings": [ - { - "source": "CertificateBasedAuthProperties", - "target": "CertificateBasedAuthProperties" - }, { "source": "DirectoryName", "target": "DirectoryName" @@ -1882,10 +1898,6 @@ { "source": "OrganizationalUnitDistinguishedNames", "target": "OrganizationalUnitDistinguishedNames" - }, - { - "source": "ServiceAccountCredentials", - "target": "ServiceAccountCredentials" } ], "operation": "CreateDirectoryConfig", @@ -1911,10 +1923,6 @@ "source": "AppVisibility", "target": "AppVisibility" }, - { - "source": "Attributes", - "target": "Attributes" - }, { "source": "Description", "target": "Description" @@ -1951,10 +1959,6 @@ { "cfn_type": "AWS::AppStream::ImageBuilder", "mappings": [ - { - "source": "AccessEndpoints", - "target": "AccessEndpoints" - }, { "source": "AppstreamAgentVersion", "target": "AppstreamAgentVersion" @@ -1967,10 +1971,6 @@ "source": "DisplayName", "target": "DisplayName" }, - { - "source": "DomainJoinInfo", - "target": "DomainJoinInfo" - }, { "source": "EnableDefaultInternetAccess", "target": "EnableDefaultInternetAccess" @@ -1996,12 +1996,16 @@ "target": "Name" }, { - "source": "Tags", - "target": "Tags" + "source": "SoftwaresToInstall", + "target": "SoftwaresToInstall" + }, + { + "source": "SoftwaresToUninstall", + "target": "SoftwaresToUninstall" }, { - "source": "VpcConfig", - "target": "VpcConfig" + "source": "Tags", + "target": "Tags" } ], "operation": "CreateImageBuilder", @@ -2023,14 +2027,6 @@ { "cfn_type": "AWS::AppStream::Stack", "mappings": [ - { - "source": "AccessEndpoints", - "target": "AccessEndpoints" - }, - { - "source": "ApplicationSettings", - "target": "ApplicationSettings" - }, { "source": "Description", "target": "Description" @@ -2055,21 +2051,9 @@ "source": "RedirectURL", "target": "RedirectURL" }, - { - "source": "StorageConnectors", - "target": "StorageConnectors" - }, - { - "source": "StreamingExperienceSettings", - "target": "StreamingExperienceSettings" - }, { "source": "Tags", "target": "Tags" - }, - { - "source": "UserSettings", - "target": "UserSettings" } ], "operation": "CreateStack", @@ -2135,10 +2119,6 @@ { "cfn_type": "AWS::AppSync::Api", "mappings": [ - { - "source": "eventConfig", - "target": "EventConfig" - }, { "source": "name", "target": "Name" @@ -2174,22 +2154,10 @@ "source": "codeHandlers", "target": "CodeHandlers" }, - { - "source": "handlerConfigs", - "target": "HandlerConfigs" - }, { "source": "name", "target": "Name" }, - { - "source": "publishAuthModes", - "target": "PublishAuthModes" - }, - { - "source": "subscribeAuthModes", - "target": "SubscribeAuthModes" - }, { "source": "tags", "target": "Tags" @@ -2226,26 +2194,6 @@ "source": "description", "target": "Description" }, - { - "source": "dynamodbConfig", - "target": "DynamoDBConfig" - }, - { - "source": "elasticsearchConfig", - "target": "ElasticsearchConfig" - }, - { - "source": "eventBridgeConfig", - "target": "EventBridgeConfig" - }, - { - "source": "httpConfig", - "target": "HttpConfig" - }, - { - "source": "lambdaConfig", - "target": "LambdaConfig" - }, { "source": "metricsConfig", "target": "MetricsConfig" @@ -2254,14 +2202,6 @@ "source": "name", "target": "Name" }, - { - "source": "openSearchServiceConfig", - "target": "OpenSearchServiceConfig" - }, - { - "source": "relationalDatabaseConfig", - "target": "RelationalDatabaseConfig" - }, { "source": "serviceRoleArn", "target": "ServiceRoleArn" @@ -2381,14 +2321,6 @@ { "source": "responseMappingTemplate", "target": "ResponseMappingTemplate" - }, - { - "source": "runtime", - "target": "Runtime" - }, - { - "source": "syncConfig", - "target": "SyncConfig" } ], "operation": "CreateFunction", @@ -2398,10 +2330,6 @@ { "cfn_type": "AWS::AppSync::GraphQLApi", "mappings": [ - { - "source": "additionalAuthenticationProviders", - "target": "AdditionalAuthenticationProviders" - }, { "source": "apiType", "target": "ApiType" @@ -2410,22 +2338,10 @@ "source": "authenticationType", "target": "AuthenticationType" }, - { - "source": "enhancedMetricsConfig", - "target": "EnhancedMetricsConfig" - }, { "source": "introspectionConfig", "target": "IntrospectionConfig" }, - { - "source": "lambdaAuthorizerConfig", - "target": "LambdaAuthorizerConfig" - }, - { - "source": "logConfig", - "target": "LogConfig" - }, { "source": "mergedApiExecutionRoleArn", "target": "MergedApiExecutionRoleArn" @@ -2434,10 +2350,6 @@ "source": "name", "target": "Name" }, - { - "source": "openIDConnectConfig", - "target": "OpenIDConnectConfig" - }, { "source": "ownerContact", "target": "OwnerContact" @@ -2454,10 +2366,6 @@ "source": "tags", "target": "Tags" }, - { - "source": "userPoolConfig", - "target": "UserPoolConfig" - }, { "source": "visibility", "target": "Visibility" @@ -2485,10 +2393,6 @@ "source": "apiId", "target": "ApiId" }, - { - "source": "cachingConfig", - "target": "CachingConfig" - }, { "source": "code", "target": "Code" @@ -2513,10 +2417,6 @@ "source": "metricsConfig", "target": "MetricsConfig" }, - { - "source": "pipelineConfig", - "target": "PipelineConfig" - }, { "source": "requestMappingTemplate", "target": "RequestMappingTemplate" @@ -2525,14 +2425,6 @@ "source": "responseMappingTemplate", "target": "ResponseMappingTemplate" }, - { - "source": "runtime", - "target": "Runtime" - }, - { - "source": "syncConfig", - "target": "SyncConfig" - }, { "source": "typeName", "target": "TypeName" @@ -2562,37 +2454,6 @@ "phase": "delete", "service": "appsync" }, - { - "cfn_type": "AWS::AppTest::TestCase", - "mappings": [ - { - "source": "description", - "target": "Description" - }, - { - "source": "name", - "target": "Name" - }, - { - "source": "steps", - "target": "Steps" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreateTestCase", - "phase": "create", - "service": "apptest" - }, - { - "cfn_type": "AWS::AppTest::TestCase", - "mappings": [], - "operation": "DeleteTestCase", - "phase": "delete", - "service": "apptest" - }, { "cfn_type": "AWS::ApplicationAutoScaling::ScalableTarget", "mappings": [ @@ -2619,10 +2480,6 @@ { "source": "ServiceNamespace", "target": "ServiceNamespace" - }, - { - "source": "SuspendedState", - "target": "SuspendedState" } ], "operation": "RegisterScalableTarget", @@ -2660,10 +2517,6 @@ "source": "PolicyType", "target": "PolicyType" }, - { - "source": "PredictiveScalingPolicyConfiguration", - "target": "PredictiveScalingPolicyConfiguration" - }, { "source": "ResourceId", "target": "ResourceId" @@ -2675,14 +2528,6 @@ { "source": "ServiceNamespace", "target": "ServiceNamespace" - }, - { - "source": "StepScalingPolicyConfiguration", - "target": "StepScalingPolicyConfiguration" - }, - { - "source": "TargetTrackingScalingPolicyConfiguration", - "target": "TargetTrackingScalingPolicyConfiguration" } ], "operation": "PutScalingPolicy", @@ -2743,10 +2588,6 @@ { "source": "SNSNotificationArn", "target": "SNSNotificationArn" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateApplication", @@ -2765,28 +2606,23 @@ "phase": "delete", "service": "application-insights" }, + { + "cfn_type": "AWS::ApplicationSignals::GroupingConfiguration", + "mappings": [], + "operation": "DeleteGroupingConfiguration", + "phase": "delete", + "service": "application-signals" + }, { "cfn_type": "AWS::ApplicationSignals::ServiceLevelObjective", "mappings": [ - { - "source": "BurnRateConfigurations", - "target": "BurnRateConfigurations" - }, { "source": "Description", "target": "Description" }, - { - "source": "Goal", - "target": "Goal" - }, { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateServiceLevelObjective", @@ -2807,10 +2643,6 @@ "source": "Name", "target": "Name" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TargetDpus", "target": "TargetDpus" @@ -2820,6 +2652,18 @@ "phase": "create", "service": "athena" }, + { + "cfn_type": "AWS::Athena::CapacityReservation", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteCapacityReservation", + "phase": "delete", + "service": "athena" + }, { "cfn_type": "AWS::Athena::DataCatalog", "mappings": [ @@ -2831,14 +2675,6 @@ "source": "Name", "target": "Name" }, - { - "source": "Parameters", - "target": "Parameters" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" @@ -2862,6 +2698,9 @@ }, { "cfn_type": "AWS::Athena::NamedQuery", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "Database", @@ -2890,6 +2729,9 @@ }, { "cfn_type": "AWS::Athena::NamedQuery", + "ignored_inputs": [ + "NamedQueryId" + ], "mappings": [], "operation": "DeleteNamedQuery", "phase": "delete", @@ -2945,10 +2787,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateWorkGroup", @@ -2970,10 +2808,6 @@ { "cfn_type": "AWS::AuditManager::Assessment", "mappings": [ - { - "source": "assessmentReportsDestination", - "target": "AssessmentReportsDestination" - }, { "source": "description", "target": "Description" @@ -2986,14 +2820,6 @@ "source": "name", "target": "Name" }, - { - "source": "roles", - "target": "Roles" - }, - { - "source": "scope", - "target": "Scope" - }, { "source": "tags", "target": "Tags" @@ -3017,10 +2843,6 @@ "source": "complianceType", "target": "ComplianceType" }, - { - "source": "controlSets", - "target": "ControlSets" - }, { "source": "description", "target": "Description" @@ -3056,10 +2878,6 @@ "source": "actionPlanTitle", "target": "ActionPlanTitle" }, - { - "source": "controlMappingSources", - "target": "ControlMappingSources" - }, { "source": "description", "target": "Description" @@ -3096,12 +2914,8 @@ "target": "AutoScalingGroupName" }, { - "source": "AvailabilityZoneDistribution", - "target": "AvailabilityZoneDistribution" - }, - { - "source": "AvailabilityZoneImpairmentPolicy", - "target": "AvailabilityZoneImpairmentPolicy" + "source": "AvailabilityZoneIds", + "target": "AvailabilityZoneIds" }, { "source": "AvailabilityZones", @@ -3111,10 +2925,6 @@ "source": "CapacityRebalance", "target": "CapacityRebalance" }, - { - "source": "CapacityReservationSpecification", - "target": "CapacityReservationSpecification" - }, { "source": "Context", "target": "Context" @@ -3124,8 +2934,8 @@ "target": "DefaultInstanceWarmup" }, { - "source": "DesiredCapacity", - "target": "DesiredCapacity" + "source": "DeletionProtection", + "target": "DeletionProtection" }, { "source": "DesiredCapacityType", @@ -3143,22 +2953,10 @@ "source": "InstanceId", "target": "InstanceId" }, - { - "source": "InstanceMaintenancePolicy", - "target": "InstanceMaintenancePolicy" - }, { "source": "LaunchConfigurationName", "target": "LaunchConfigurationName" }, - { - "source": "LaunchTemplate", - "target": "LaunchTemplate" - }, - { - "source": "LifecycleHookSpecificationList", - "target": "LifecycleHookSpecificationList" - }, { "source": "LoadBalancerNames", "target": "LoadBalancerNames" @@ -3167,18 +2965,6 @@ "source": "MaxInstanceLifetime", "target": "MaxInstanceLifetime" }, - { - "source": "MaxSize", - "target": "MaxSize" - }, - { - "source": "MinSize", - "target": "MinSize" - }, - { - "source": "MixedInstancesPolicy", - "target": "MixedInstancesPolicy" - }, { "source": "NewInstancesProtectedFromScaleIn", "target": "NewInstancesProtectedFromScaleIn" @@ -3195,10 +2981,6 @@ "source": "SkipZonalShiftValidation", "target": "SkipZonalShiftValidation" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TargetGroupARNs", "target": "TargetGroupARNs" @@ -3206,14 +2988,6 @@ { "source": "TerminationPolicies", "target": "TerminationPolicies" - }, - { - "source": "TrafficSources", - "target": "TrafficSources" - }, - { - "source": "VPCZoneIdentifier", - "target": "VPCZoneIdentifier" } ], "operation": "CreateAutoScalingGroup", @@ -3239,10 +3013,6 @@ "source": "AssociatePublicIpAddress", "target": "AssociatePublicIpAddress" }, - { - "source": "BlockDeviceMappings", - "target": "BlockDeviceMappings" - }, { "source": "ClassicLinkVPCId", "target": "ClassicLinkVPCId" @@ -3267,10 +3037,6 @@ "source": "InstanceId", "target": "InstanceId" }, - { - "source": "InstanceMonitoring", - "target": "InstanceMonitoring" - }, { "source": "InstanceType", "target": "InstanceType" @@ -3287,10 +3053,6 @@ "source": "LaunchConfigurationName", "target": "LaunchConfigurationName" }, - { - "source": "MetadataOptions", - "target": "MetadataOptions" - }, { "source": "PlacementTenancy", "target": "PlacementTenancy" @@ -3395,10 +3157,6 @@ "source": "AutoScalingGroupName", "target": "AutoScalingGroupName" }, - { - "source": "Cooldown", - "target": "Cooldown" - }, { "source": "EstimatedInstanceWarmup", "target": "EstimatedInstanceWarmup" @@ -3415,21 +3173,9 @@ "source": "PolicyType", "target": "PolicyType" }, - { - "source": "PredictiveScalingConfiguration", - "target": "PredictiveScalingConfiguration" - }, { "source": "ScalingAdjustment", "target": "ScalingAdjustment" - }, - { - "source": "StepAdjustments", - "target": "StepAdjustments" - }, - { - "source": "TargetTrackingConfiguration", - "target": "TargetTrackingConfiguration" } ], "operation": "PutScalingPolicy", @@ -3447,10 +3193,6 @@ "source": "DesiredCapacity", "target": "DesiredCapacity" }, - { - "source": "EndTime", - "target": "EndTime" - }, { "source": "MaxSize", "target": "MaxSize" @@ -3463,10 +3205,6 @@ "source": "Recurrence", "target": "Recurrence" }, - { - "source": "StartTime", - "target": "StartTime" - }, { "source": "TimeZone", "target": "TimeZone" @@ -3495,10 +3233,6 @@ "source": "AutoScalingGroupName", "target": "AutoScalingGroupName" }, - { - "source": "InstanceReusePolicy", - "target": "InstanceReusePolicy" - }, { "source": "MaxGroupPreparedCapacity", "target": "MaxGroupPreparedCapacity" @@ -3530,23 +3264,14 @@ }, { "cfn_type": "AWS::B2BI::Capability", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "configuration", - "target": "Configuration" - }, - { - "source": "instructionsDocuments", - "target": "InstructionsDocuments" - }, { "source": "name", "target": "Name" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "type", "target": "Type" @@ -3565,15 +3290,14 @@ }, { "cfn_type": "AWS::B2BI::Partnership", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "capabilities", "target": "Capabilities" }, - { - "source": "capabilityOptions", - "target": "CapabilityOptions" - }, { "source": "email", "target": "Email" @@ -3589,10 +3313,6 @@ { "source": "profileId", "target": "ProfileId" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreatePartnership", @@ -3608,6 +3328,9 @@ }, { "cfn_type": "AWS::B2BI::Profile", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "businessName", @@ -3628,10 +3351,6 @@ { "source": "phone", "target": "Phone" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateProfile", @@ -3647,23 +3366,14 @@ }, { "cfn_type": "AWS::B2BI::Transformer", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "ediType", - "target": "EdiType" - }, { "source": "fileFormat", "target": "FileFormat" }, - { - "source": "inputConversion", - "target": "InputConversion" - }, - { - "source": "mapping", - "target": "Mapping" - }, { "source": "mappingTemplate", "target": "MappingTemplate" @@ -3672,21 +3382,9 @@ "source": "name", "target": "Name" }, - { - "source": "outputConversion", - "target": "OutputConversion" - }, { "source": "sampleDocument", "target": "SampleDocument" - }, - { - "source": "sampleDocuments", - "target": "SampleDocuments" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateTransformer", @@ -3701,16 +3399,27 @@ "service": "b2bi" }, { - "cfn_type": "AWS::BCMDataExports::Export", + "cfn_type": "AWS::BCM::Dashboard", "mappings": [ { - "source": "Export", - "target": "Export" + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" } ], - "operation": "CreateExport", + "operation": "CreateDashboard", "phase": "create", - "service": "bcm-data-exports" + "service": "bcm-dashboards" + }, + { + "cfn_type": "AWS::BCM::Dashboard", + "mappings": [], + "operation": "DeleteDashboard", + "phase": "delete", + "service": "bcm-dashboards" }, { "cfn_type": "AWS::BCMDataExports::Export", @@ -3719,22 +3428,6 @@ "phase": "delete", "service": "bcm-data-exports" }, - { - "cfn_type": "AWS::Backup::BackupPlan", - "mappings": [ - { - "source": "BackupPlan", - "target": "BackupPlan" - }, - { - "source": "BackupPlanTags", - "target": "BackupPlanTags" - } - ], - "operation": "CreateBackupPlan", - "phase": "create", - "service": "backup" - }, { "cfn_type": "AWS::Backup::BackupPlan", "mappings": [], @@ -3744,14 +3437,13 @@ }, { "cfn_type": "AWS::Backup::BackupSelection", + "ignored_inputs": [ + "CreatorRequestId" + ], "mappings": [ { "source": "BackupPlanId", "target": "BackupPlanId" - }, - { - "source": "BackupSelection", - "target": "BackupSelection" } ], "operation": "CreateBackupSelection", @@ -3772,15 +3464,14 @@ }, { "cfn_type": "AWS::Backup::BackupVault", + "ignored_inputs": [ + "CreatorRequestId" + ], "mappings": [ { "source": "BackupVaultName", "target": "BackupVaultName" }, - { - "source": "BackupVaultTags", - "target": "BackupVaultTags" - }, { "source": "EncryptionKeyArn", "target": "EncryptionKeyArn" @@ -3804,11 +3495,10 @@ }, { "cfn_type": "AWS::Backup::Framework", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ - { - "source": "FrameworkControls", - "target": "FrameworkControls" - }, { "source": "FrameworkDescription", "target": "FrameworkDescription" @@ -3816,10 +3506,6 @@ { "source": "FrameworkName", "target": "FrameworkName" - }, - { - "source": "FrameworkTags", - "target": "FrameworkTags" } ], "operation": "CreateFramework", @@ -3840,15 +3526,14 @@ }, { "cfn_type": "AWS::Backup::LegalHold", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "Description", "target": "Description" }, - { - "source": "RecoveryPointSelection", - "target": "RecoveryPointSelection" - }, { "source": "Tags", "target": "Tags" @@ -3871,14 +3556,17 @@ }, { "cfn_type": "AWS::Backup::LogicallyAirGappedBackupVault", + "ignored_inputs": [ + "CreatorRequestId" + ], "mappings": [ { "source": "BackupVaultName", "target": "BackupVaultName" }, { - "source": "BackupVaultTags", - "target": "BackupVaultTags" + "source": "EncryptionKeyArn", + "target": "EncryptionKeyArn" }, { "source": "MaxRetentionDays", @@ -3895,11 +3583,10 @@ }, { "cfn_type": "AWS::Backup::ReportPlan", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ - { - "source": "ReportDeliveryChannel", - "target": "ReportDeliveryChannel" - }, { "source": "ReportPlanDescription", "target": "ReportPlanDescription" @@ -3907,14 +3594,6 @@ { "source": "ReportPlanName", "target": "ReportPlanName" - }, - { - "source": "ReportPlanTags", - "target": "ReportPlanTags" - }, - { - "source": "ReportSetting", - "target": "ReportSetting" } ], "operation": "CreateReportPlan", @@ -3936,10 +3615,6 @@ { "cfn_type": "AWS::Backup::RestoreTestingPlan", "mappings": [ - { - "source": "RestoreTestingPlan", - "target": "RestoreTestingPlanName" - }, { "source": "Tags", "target": "Tags" @@ -3967,10 +3642,6 @@ { "source": "RestoreTestingPlanName", "target": "RestoreTestingPlanName" - }, - { - "source": "RestoreTestingSelection", - "target": "RestoreTestingSelectionName" } ], "operation": "CreateRestoreTestingSelection", @@ -3993,6 +3664,18 @@ "phase": "delete", "service": "backup" }, + { + "cfn_type": "AWS::Backup::TieringConfiguration", + "mappings": [ + { + "source": "TieringConfigurationName", + "target": "TieringConfigurationName" + } + ], + "operation": "DeleteTieringConfiguration", + "phase": "delete", + "service": "backup" + }, { "cfn_type": "AWS::BackupGateway::Hypervisor", "mappings": [ @@ -4012,10 +3695,6 @@ "source": "Password", "target": "Password" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Username", "target": "Username" @@ -4039,18 +3718,10 @@ "source": "computeEnvironmentName", "target": "ComputeEnvironmentName" }, - { - "source": "computeResources", - "target": "ComputeResources" - }, { "source": "context", "target": "Context" }, - { - "source": "eksConfiguration", - "target": "EksConfiguration" - }, { "source": "serviceRole", "target": "ServiceRole" @@ -4059,10 +3730,6 @@ "source": "state", "target": "State" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "type", "target": "Type" @@ -4099,10 +3766,6 @@ "source": "resourceType", "target": "ResourceType" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "totalQuantity", "target": "TotalQuantity" @@ -4127,34 +3790,10 @@ { "cfn_type": "AWS::Batch::JobDefinition", "mappings": [ - { - "source": "consumableResourceProperties", - "target": "ConsumableResourceProperties" - }, - { - "source": "containerProperties", - "target": "ContainerProperties" - }, - { - "source": "ecsProperties", - "target": "EcsProperties" - }, - { - "source": "eksProperties", - "target": "EksProperties" - }, { "source": "jobDefinitionName", "target": "JobDefinitionName" }, - { - "source": "nodeProperties", - "target": "NodeProperties" - }, - { - "source": "parameters", - "target": "Parameters" - }, { "source": "platformCapabilities", "target": "PlatformCapabilities" @@ -4163,22 +3802,10 @@ "source": "propagateTags", "target": "PropagateTags" }, - { - "source": "retryStrategy", - "target": "RetryStrategy" - }, { "source": "schedulingPriority", "target": "SchedulingPriority" }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "timeout", - "target": "Timeout" - }, { "source": "type", "target": "Type" @@ -4203,10 +3830,6 @@ { "cfn_type": "AWS::Batch::JobQueue", "mappings": [ - { - "source": "computeEnvironmentOrder", - "target": "ComputeEnvironmentOrder" - }, { "source": "jobQueueName", "target": "JobQueueName" @@ -4215,10 +3838,6 @@ "source": "jobQueueType", "target": "JobQueueType" }, - { - "source": "jobStateTimeLimitActions", - "target": "JobStateTimeLimitActions" - }, { "source": "priority", "target": "Priority" @@ -4227,17 +3846,9 @@ "source": "schedulingPolicyArn", "target": "SchedulingPolicyArn" }, - { - "source": "serviceEnvironmentOrder", - "target": "ServiceEnvironmentOrder" - }, { "source": "state", "target": "State" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateJobQueue", @@ -4257,19 +3868,38 @@ "service": "batch" }, { - "cfn_type": "AWS::Batch::SchedulingPolicy", + "cfn_type": "AWS::Batch::QuotaShare", "mappings": [ { - "source": "fairsharePolicy", - "target": "FairsharePolicy" + "source": "jobQueue", + "target": "JobQueue" }, { - "source": "name", - "target": "Name" + "source": "quotaShareName", + "target": "QuotaShareName" }, { - "source": "tags", - "target": "Tags" + "source": "state", + "target": "State" + } + ], + "operation": "CreateQuotaShare", + "phase": "create", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::QuotaShare", + "mappings": [], + "operation": "DeleteQuotaShare", + "phase": "delete", + "service": "batch" + }, + { + "cfn_type": "AWS::Batch::SchedulingPolicy", + "mappings": [ + { + "source": "name", + "target": "Name" } ], "operation": "CreateSchedulingPolicy", @@ -4286,10 +3916,6 @@ { "cfn_type": "AWS::Batch::ServiceEnvironment", "mappings": [ - { - "source": "capacityLimits", - "target": "CapacityLimits" - }, { "source": "serviceEnvironmentName", "target": "ServiceEnvironmentName" @@ -4301,10 +3927,6 @@ { "source": "state", "target": "State" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateServiceEnvironment", @@ -4325,7 +3947,18 @@ }, { "cfn_type": "AWS::BcmPricingCalculator::BillScenario", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ + { + "source": "costCategoryGroupSharingPreferenceArn", + "target": "CostCategoryGroupSharingPreferenceArn" + }, + { + "source": "groupSharingPreference", + "target": "GroupSharingPreference" + }, { "source": "name", "target": "Name" @@ -4348,6 +3981,9 @@ }, { "cfn_type": "AWS::Bedrock::AgentAlias", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "agentAliasName", @@ -4360,14 +3996,6 @@ { "source": "description", "target": "Description" - }, - { - "source": "routingConfiguration", - "target": "RoutingConfiguration" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateAgentAlias", @@ -4388,6 +4016,9 @@ }, { "cfn_type": "AWS::Bedrock::ApplicationInferenceProfile", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "description", @@ -4396,14 +4027,6 @@ { "source": "inferenceProfileName", "target": "InferenceProfileName" - }, - { - "source": "modelSource", - "target": "ModelSource" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateInferenceProfile", @@ -4412,22 +4035,21 @@ }, { "cfn_type": "AWS::Bedrock::AutomatedReasoningPolicy", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "description", "target": "Description" }, { - "source": "name", - "target": "Name" - }, - { - "source": "policyDefinition", - "target": "PolicyDefinition" + "source": "kmsKeyId", + "target": "KmsKeyId" }, { - "source": "tags", - "target": "Tags" + "source": "name", + "target": "Name" } ], "operation": "CreateAutomatedReasoningPolicy", @@ -4443,6 +4065,9 @@ }, { "cfn_type": "AWS::Bedrock::AutomatedReasoningPolicyVersion", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "lastUpdatedDefinitionHash", @@ -4451,10 +4076,6 @@ { "source": "policyArn", "target": "PolicyArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateAutomatedReasoningPolicyVersion", @@ -4463,19 +4084,14 @@ }, { "cfn_type": "AWS::Bedrock::Blueprint", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "blueprintName", "target": "BlueprintName" }, - { - "source": "schema", - "target": "Schema" - }, - { - "source": "tags", - "target": "Tags" - }, { "source": "type", "target": "Type" @@ -4493,16 +4109,37 @@ "service": "bedrock-data-automation" }, { - "cfn_type": "AWS::Bedrock::DataAutomationProject", + "cfn_type": "AWS::Bedrock::DataAutomationLibrary", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "customOutputConfiguration", - "target": "CustomOutputConfiguration" + "source": "libraryDescription", + "target": "LibraryDescription" }, { - "source": "overrideConfiguration", - "target": "OverrideConfiguration" - }, + "source": "libraryName", + "target": "LibraryName" + } + ], + "operation": "CreateDataAutomationLibrary", + "phase": "create", + "service": "bedrock-data-automation" + }, + { + "cfn_type": "AWS::Bedrock::DataAutomationLibrary", + "mappings": [], + "operation": "DeleteDataAutomationLibrary", + "phase": "delete", + "service": "bedrock-data-automation" + }, + { + "cfn_type": "AWS::Bedrock::DataAutomationProject", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ { "source": "projectDescription", "target": "ProjectDescription" @@ -4512,12 +4149,8 @@ "target": "ProjectName" }, { - "source": "standardOutputConfiguration", - "target": "StandardOutputConfiguration" - }, - { - "source": "tags", - "target": "Tags" + "source": "projectType", + "target": "ProjectType" } ], "operation": "CreateDataAutomationProject", @@ -4533,15 +4166,14 @@ }, { "cfn_type": "AWS::Bedrock::DataSource", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "dataDeletionPolicy", "target": "DataDeletionPolicy" }, - { - "source": "dataSourceConfiguration", - "target": "DataSourceConfiguration" - }, { "source": "description", "target": "Description" @@ -4553,14 +4185,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "serverSideEncryptionConfiguration", - "target": "ServerSideEncryptionConfiguration" - }, - { - "source": "vectorIngestionConfiguration", - "target": "VectorIngestionConfiguration" } ], "operation": "CreateDataSource", @@ -4579,13 +4203,19 @@ "phase": "delete", "service": "bedrock-agent" }, + { + "cfn_type": "AWS::Bedrock::EnforcedGuardrailConfiguration", + "mappings": [], + "operation": "DeleteEnforcedGuardrailConfiguration", + "phase": "delete", + "service": "bedrock" + }, { "cfn_type": "AWS::Bedrock::FlowAlias", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "concurrencyConfiguration", - "target": "ConcurrencyConfiguration" - }, { "source": "description", "target": "Description" @@ -4593,14 +4223,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "routingConfiguration", - "target": "RoutingConfiguration" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateFlowAlias", @@ -4616,11 +4238,10 @@ }, { "cfn_type": "AWS::Bedrock::Guardrail", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ - { - "source": "automatedReasoningPolicyConfig", - "target": "AutomatedReasoningPolicyConfig" - }, { "source": "blockedInputMessaging", "target": "BlockedInputMessaging" @@ -4629,18 +4250,6 @@ "source": "blockedOutputsMessaging", "target": "BlockedOutputsMessaging" }, - { - "source": "contentPolicyConfig", - "target": "ContentPolicyConfig" - }, - { - "source": "contextualGroundingPolicyConfig", - "target": "ContextualGroundingPolicyConfig" - }, - { - "source": "crossRegionConfig", - "target": "CrossRegionConfig" - }, { "source": "description", "target": "Description" @@ -4648,22 +4257,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "sensitiveInformationPolicyConfig", - "target": "SensitiveInformationPolicyConfig" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "topicPolicyConfig", - "target": "TopicPolicyConfig" - }, - { - "source": "wordPolicyConfig", - "target": "WordPolicyConfig" } ], "operation": "CreateGuardrail", @@ -4679,6 +4272,9 @@ }, { "cfn_type": "AWS::Bedrock::GuardrailVersion", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "description", @@ -4693,49 +4289,16 @@ "phase": "create", "service": "bedrock" }, - { - "cfn_type": "AWS::Bedrock::IntelligentPromptRouter", - "mappings": [ - { - "source": "description", - "target": "Description" - }, - { - "source": "fallbackModel", - "target": "FallbackModel" - }, - { - "source": "models", - "target": "Models" - }, - { - "source": "promptRouterName", - "target": "PromptRouterName" - }, - { - "source": "routingCriteria", - "target": "RoutingCriteria" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreatePromptRouter", - "phase": "create", - "service": "bedrock" - }, { "cfn_type": "AWS::Bedrock::KnowledgeBase", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", "target": "Description" }, - { - "source": "knowledgeBaseConfiguration", - "target": "KnowledgeBaseConfiguration" - }, { "source": "name", "target": "Name" @@ -4743,14 +4306,6 @@ { "source": "roleArn", "target": "RoleArn" - }, - { - "source": "storageConfiguration", - "target": "StorageConfiguration" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateKnowledgeBase", @@ -4766,6 +4321,9 @@ }, { "cfn_type": "AWS::Bedrock::Prompt", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "customerEncryptionKeyArn", @@ -4782,14 +4340,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "variants", - "target": "Variants" } ], "operation": "CreatePrompt", @@ -4805,19 +4355,65 @@ }, { "cfn_type": "AWS::Bedrock::PromptVersion", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", "target": "Description" + } + ], + "operation": "CreatePromptVersion", + "phase": "create", + "service": "bedrock-agent" + }, + { + "cfn_type": "AWS::Bedrock::ResourcePolicy", + "mappings": [ + { + "source": "resourceArn", + "target": "ResourceArn" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "bedrock" + }, + { + "cfn_type": "AWS::Bedrock::ResourcePolicy", + "mappings": [ + { + "source": "resourceArn", + "target": "ResourceArn" + } + ], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "bedrock" + }, + { + "cfn_type": "AWS::Bedrock::Session", + "mappings": [ + { + "source": "encryptionKeyArn", + "target": "EncryptionKeyArn" }, { "source": "tags", "target": "Tags" } ], - "operation": "CreatePromptVersion", + "operation": "CreateSession", "phase": "create", - "service": "bedrock-agent" + "service": "bedrock-agent-runtime" + }, + { + "cfn_type": "AWS::Bedrock::Session", + "mappings": [], + "operation": "DeleteSession", + "phase": "delete", + "service": "bedrock-agent-runtime" }, { "cfn_type": "AWS::BedrockAgentCore::ApiKeyCredentialProvider", @@ -4826,9 +4422,17 @@ "source": "apiKey", "target": "ApiKey" }, + { + "source": "apiKeySecretSource", + "target": "ApiKeySecretSource" + }, { "source": "name", "target": "Name" + }, + { + "source": "tags", + "target": "Tags" } ], "operation": "CreateApiKeyCredentialProvider", @@ -4848,31 +4452,72 @@ "service": "bedrock-agentcore-control" }, { - "cfn_type": "AWS::BedrockAgentCore::BrowserCustom", + "cfn_type": "AWS::BedrockAgentCore::BrowserProfile", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", "target": "Description" }, { - "source": "executionRoleArn", - "target": "ExecutionRoleArn" + "source": "name", + "target": "Name" + } + ], + "operation": "CreateBrowserProfile", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::BrowserProfile", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [], + "operation": "DeleteBrowserProfile", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::CapacityProvider", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" }, { "source": "name", "target": "Name" }, { - "source": "networkConfiguration", - "target": "NetworkConfiguration" + "source": "tags", + "target": "Tags" } ], - "operation": "CreateBrowser", + "operation": "CreateCapacityProvider", "phase": "create", "service": "bedrock-agentcore-control" }, + { + "cfn_type": "AWS::BedrockAgentCore::CapacityProvider", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [], + "operation": "DeleteCapacityProvider", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, { "cfn_type": "AWS::BedrockAgentCore::CodeInterpreterCustom", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -4885,23 +4530,136 @@ { "source": "name", "target": "Name" + } + ], + "operation": "CreateCodeInterpreter", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::ConfigurationBundle", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "branchName", + "target": "BranchName" }, { - "source": "networkConfiguration", - "target": "NetworkConfiguration" + "source": "bundleName", + "target": "BundleName" + }, + { + "source": "commitMessage", + "target": "CommitMessage" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "tags", + "target": "Tags" } ], - "operation": "CreateCodeInterpreter", + "operation": "CreateConfigurationBundle", "phase": "create", "service": "bedrock-agentcore-control" }, { - "cfn_type": "AWS::BedrockAgentCore::Gateway", + "cfn_type": "AWS::BedrockAgentCore::ConfigurationBundle", + "mappings": [], + "operation": "DeleteConfigurationBundle", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Dataset", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "datasetName", + "target": "DatasetName" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "schemaType", + "target": "SchemaType" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDataset", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Dataset", + "mappings": [], + "operation": "DeleteDataset", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Evaluator", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "authorizerConfiguration", - "target": "AuthorizerConfiguration" + "source": "description", + "target": "Description" + }, + { + "source": "evaluatorName", + "target": "EvaluatorName" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" }, + { + "source": "level", + "target": "Level" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateEvaluator", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Evaluator", + "mappings": [], + "operation": "DeleteEvaluator", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Gateway", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ { "source": "authorizerType", "target": "AuthorizerType" @@ -4922,14 +4680,6 @@ "source": "name", "target": "Name" }, - { - "source": "protocolConfiguration", - "target": "ProtocolConfiguration" - }, - { - "source": "protocolType", - "target": "ProtocolType" - }, { "source": "roleArn", "target": "RoleArn" @@ -4947,12 +4697,54 @@ "service": "bedrock-agentcore-control" }, { - "cfn_type": "AWS::BedrockAgentCore::GatewayTarget", + "cfn_type": "AWS::BedrockAgentCore::GatewayRateLimit", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "dimensionKeys", + "target": "DimensionKeys" + }, + { + "source": "gatewayIdentifier", + "target": "GatewayIdentifier" + }, + { + "source": "rateLimitId", + "target": "RateLimitId" + } + ], + "operation": "CreateGatewayRateLimit", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::GatewayRateLimit", "mappings": [ { - "source": "credentialProviderConfigurations", - "target": "CredentialProviderConfigurations" + "source": "gatewayIdentifier", + "target": "GatewayIdentifier" }, + { + "source": "rateLimitId", + "target": "RateLimitId" + } + ], + "operation": "DeleteGatewayRateLimit", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::GatewayRule", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ { "source": "description", "target": "Description" @@ -4962,12 +4754,43 @@ "target": "GatewayIdentifier" }, { - "source": "name", - "target": "Name" + "source": "priority", + "target": "Priority" + } + ], + "operation": "CreateGatewayRule", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::GatewayRule", + "mappings": [ + { + "source": "gatewayIdentifier", + "target": "GatewayIdentifier" + } + ], + "operation": "DeleteGatewayRule", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::GatewayTarget", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" }, { - "source": "targetConfiguration", - "target": "TargetConfiguration" + "source": "gatewayIdentifier", + "target": "GatewayIdentifier" + }, + { + "source": "name", + "target": "Name" } ], "operation": "CreateGatewayTarget", @@ -4986,8 +4809,110 @@ "phase": "delete", "service": "bedrock-agentcore-control" }, + { + "cfn_type": "AWS::BedrockAgentCore::Harness", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "allowedTools", + "target": "AllowedTools" + }, + { + "source": "executionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "harnessName", + "target": "HarnessName" + }, + { + "source": "maxIterations", + "target": "MaxIterations" + }, + { + "source": "maxTokens", + "target": "MaxTokens" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "timeoutSeconds", + "target": "TimeoutSeconds" + } + ], + "operation": "CreateHarness", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Harness", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [], + "operation": "DeleteHarness", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::HarnessEndpoint", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "endpointName", + "target": "EndpointName" + }, + { + "source": "harnessId", + "target": "HarnessId" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetVersion", + "target": "TargetVersion" + } + ], + "operation": "CreateHarnessEndpoint", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::HarnessEndpoint", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "endpointName", + "target": "EndpointName" + }, + { + "source": "harnessId", + "target": "HarnessId" + } + ], + "operation": "DeleteHarnessEndpoint", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, { "cfn_type": "AWS::BedrockAgentCore::Memory", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -5005,10 +4930,6 @@ "source": "memoryExecutionRoleArn", "target": "MemoryExecutionRoleArn" }, - { - "source": "memoryStrategies", - "target": "MemoryStrategies" - }, { "source": "name", "target": "Name" @@ -5020,6 +4941,9 @@ }, { "cfn_type": "AWS::BedrockAgentCore::Memory", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteMemory", "phase": "delete", @@ -5037,8 +4961,8 @@ "target": "Name" }, { - "source": "oauth2ProviderConfigInput", - "target": "Oauth2ProviderConfigInput" + "source": "tags", + "target": "Tags" } ], "operation": "CreateOauth2CredentialProvider", @@ -5058,35 +4982,264 @@ "service": "bedrock-agentcore-control" }, { - "cfn_type": "AWS::BedrockAgentCore::Runtime", + "cfn_type": "AWS::BedrockAgentCore::OnlineEvaluationConfig", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "agentRuntimeArtifact", - "target": "AgentRuntimeArtifact" + "source": "description", + "target": "Description" }, { - "source": "agentRuntimeName", - "target": "AgentRuntimeName" + "source": "evaluationExecutionRoleArn", + "target": "EvaluationExecutionRoleArn" }, { - "source": "authorizerConfiguration", - "target": "AuthorizerConfiguration" + "source": "onlineEvaluationConfigName", + "target": "OnlineEvaluationConfigName" }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateOnlineEvaluationConfig", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::OnlineEvaluationConfig", + "mappings": [], + "operation": "DeleteOnlineEvaluationConfig", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::PaymentConnector", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ { "source": "description", "target": "Description" }, { - "source": "environmentVariables", - "target": "EnvironmentVariables" + "source": "paymentManagerId", + "target": "PaymentManagerId" + } + ], + "operation": "CreatePaymentConnector", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::PaymentConnector", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "paymentManagerId", + "target": "PaymentManagerId" + } + ], + "operation": "DeletePaymentConnector", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::PaymentCredentialProvider", + "mappings": [ + { + "source": "credentialProviderVendor", + "target": "CredentialProviderVendor" + }, + { + "source": "name", + "target": "Name" }, { - "source": "networkConfiguration", - "target": "NetworkConfiguration" + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePaymentCredentialProvider", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::PaymentCredentialProvider", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeletePaymentCredentialProvider", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::PaymentManager", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "authorizerType", + "target": "AuthorizerType" }, { - "source": "protocolConfiguration", - "target": "ProtocolConfiguration" + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePaymentManager", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::PaymentManager", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [], + "operation": "DeletePaymentManager", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Policy", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "enforcementMode", + "target": "EnforcementMode" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "policyEngineId", + "target": "PolicyEngineId" + }, + { + "source": "validationMode", + "target": "ValidationMode" + } + ], + "operation": "CreatePolicy", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Policy", + "mappings": [ + { + "source": "policyEngineId", + "target": "PolicyEngineId" + } + ], + "operation": "DeletePolicy", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::PolicyEngine", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "encryptionKeyArn", + "target": "EncryptionKeyArn" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePolicyEngine", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::PolicyEngine", + "mappings": [], + "operation": "DeletePolicyEngine", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::ResourcePolicy", + "mappings": [ + { + "source": "policy", + "target": "Policy" + }, + { + "source": "resourceArn", + "target": "ResourceArn" + } + ], + "operation": "PutResourcePolicy", + "phase": "create", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::ResourcePolicy", + "mappings": [ + { + "source": "resourceArn", + "target": "ResourceArn" + } + ], + "operation": "DeleteResourcePolicy", + "phase": "delete", + "service": "bedrock-agentcore-control" + }, + { + "cfn_type": "AWS::BedrockAgentCore::Runtime", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "agentRuntimeName", + "target": "AgentRuntimeName" + }, + { + "source": "description", + "target": "Description" }, { "source": "roleArn", @@ -5099,6 +5252,9 @@ }, { "cfn_type": "AWS::BedrockAgentCore::Runtime", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteAgentRuntime", "phase": "delete", @@ -5106,6 +5262,9 @@ }, { "cfn_type": "AWS::BedrockAgentCore::RuntimeEndpoint", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "agentRuntimeId", @@ -5130,6 +5289,9 @@ }, { "cfn_type": "AWS::BedrockAgentCore::RuntimeEndpoint", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "agentRuntimeId", @@ -5150,6 +5312,10 @@ { "source": "name", "target": "Name" + }, + { + "source": "tags", + "target": "Tags" } ], "operation": "CreateWorkloadIdentity", @@ -5170,11 +5336,10 @@ }, { "cfn_type": "AWS::Billing::BillingView", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "dataFilterExpression", - "target": "DataFilterExpression" - }, { "source": "description", "target": "Description" @@ -5201,15 +5366,10 @@ }, { "cfn_type": "AWS::BillingConductor::BillingGroup", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "AccountGrouping", - "target": "AccountGrouping" - }, - { - "source": "ComputationPreference", - "target": "ComputationPreference" - }, { "source": "Description", "target": "Description" @@ -5240,6 +5400,9 @@ }, { "cfn_type": "AWS::BillingConductor::CustomLineItem", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "AccountId", @@ -5250,8 +5413,8 @@ "target": "BillingGroupArn" }, { - "source": "BillingPeriodRange", - "target": "BillingPeriodRange" + "source": "ComputationRule", + "target": "ComputationRule" }, { "source": "Description", @@ -5272,18 +5435,16 @@ }, { "cfn_type": "AWS::BillingConductor::CustomLineItem", - "mappings": [ - { - "source": "BillingPeriodRange", - "target": "BillingPeriodRange" - } - ], + "mappings": [], "operation": "DeleteCustomLineItem", "phase": "delete", "service": "billingconductor" }, { "cfn_type": "AWS::BillingConductor::PricingPlan", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -5315,6 +5476,9 @@ }, { "cfn_type": "AWS::BillingConductor::PricingRule", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "BillingEntity", @@ -5348,10 +5512,6 @@ "source": "Tags", "target": "Tags" }, - { - "source": "Tiering", - "target": "Tiering" - }, { "source": "Type", "target": "Type" @@ -5373,12 +5533,38 @@ "service": "billingconductor" }, { - "cfn_type": "AWS::Budgets::BudgetsAction", + "cfn_type": "AWS::Braket::SpendingLimit", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "ActionThreshold", - "target": "ActionThreshold" + "source": "deviceArn", + "target": "DeviceArn" + }, + { + "source": "spendingLimit", + "target": "SpendingLimit" }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSpendingLimit", + "phase": "create", + "service": "braket" + }, + { + "cfn_type": "AWS::Braket::SpendingLimit", + "mappings": [], + "operation": "DeleteSpendingLimit", + "phase": "delete", + "service": "braket" + }, + { + "cfn_type": "AWS::Budgets::BudgetsAction", + "mappings": [ { "source": "ActionType", "target": "ActionType" @@ -5391,10 +5577,6 @@ "source": "BudgetName", "target": "BudgetName" }, - { - "source": "Definition", - "target": "Definition" - }, { "source": "ExecutionRoleArn", "target": "ExecutionRoleArn" @@ -5402,32 +5584,12 @@ { "source": "NotificationType", "target": "NotificationType" - }, - { - "source": "ResourceTags", - "target": "ResourceTags" - }, - { - "source": "Subscribers", - "target": "Subscribers" } ], "operation": "CreateBudgetAction", "phase": "create", "service": "budgets" }, - { - "cfn_type": "AWS::CE::AnomalyMonitor", - "mappings": [ - { - "source": "ResourceTags", - "target": "ResourceTags" - } - ], - "operation": "CreateAnomalyMonitor", - "phase": "create", - "service": "ce" - }, { "cfn_type": "AWS::CE::AnomalyMonitor", "mappings": [], @@ -5435,18 +5597,6 @@ "phase": "delete", "service": "ce" }, - { - "cfn_type": "AWS::CE::AnomalySubscription", - "mappings": [ - { - "source": "ResourceTags", - "target": "ResourceTags" - } - ], - "operation": "CreateAnomalySubscription", - "phase": "create", - "service": "ce" - }, { "cfn_type": "AWS::CE::AnomalySubscription", "mappings": [], @@ -5468,32 +5618,12 @@ { "source": "RuleVersion", "target": "RuleVersion" - }, - { - "source": "Rules", - "target": "Rules" - }, - { - "source": "SplitChargeRules", - "target": "SplitChargeRules" } ], "operation": "CreateCostCategoryDefinition", "phase": "create", "service": "ce" }, - { - "cfn_type": "AWS::CUR::ReportDefinition", - "mappings": [ - { - "source": "Tags", - "target": "Tags" - } - ], - "operation": "PutReportDefinition", - "phase": "create", - "service": "cur" - }, { "cfn_type": "AWS::CUR::ReportDefinition", "mappings": [ @@ -5520,10 +5650,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "rule", - "target": "Rule" } ], "operation": "CreateCaseRule", @@ -5600,10 +5726,6 @@ { "cfn_type": "AWS::Cases::Layout", "mappings": [ - { - "source": "content", - "target": "Content" - }, { "source": "domainId", "target": "DomainId" @@ -5640,22 +5762,10 @@ "source": "domainId", "target": "DomainId" }, - { - "source": "layoutConfiguration", - "target": "LayoutConfiguration" - }, { "source": "name", "target": "Name" }, - { - "source": "requiredFields", - "target": "RequiredFields" - }, - { - "source": "rules", - "target": "Rules" - }, { "source": "status", "target": "Status" @@ -5678,55 +5788,95 @@ "service": "connectcases" }, { - "cfn_type": "AWS::CertificateManager::Certificate", + "cfn_type": "AWS::CertificateManager::AcmeDomainValidation", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { - "source": "CertificateAuthorityArn", - "target": "CertificateAuthorityArn" + "source": "AcmeEndpointArn", + "target": "AcmeEndpointArn" }, { "source": "DomainName", "target": "DomainName" - }, - { - "source": "DomainValidationOptions", - "target": "DomainValidationOptions" - }, - { - "source": "KeyAlgorithm", - "target": "KeyAlgorithm" - }, - { - "source": "SubjectAlternativeNames", - "target": "SubjectAlternativeNames" - }, + } + ], + "operation": "CreateAcmeDomainValidation", + "phase": "create", + "service": "acm" + }, + { + "cfn_type": "AWS::CertificateManager::AcmeDomainValidation", + "mappings": [], + "operation": "DeleteAcmeDomainValidation", + "phase": "delete", + "service": "acm" + }, + { + "cfn_type": "AWS::CertificateManager::AcmeEndpoint", + "ignored_inputs": [ + "IdempotencyToken" + ], + "mappings": [ { - "source": "Tags", - "target": "Tags" + "source": "AuthorizationBehavior", + "target": "AuthorizationBehavior" }, { - "source": "ValidationMethod", - "target": "ValidationMethod" + "source": "Contact", + "target": "Contact" } ], - "operation": "RequestCertificate", + "operation": "CreateAcmeEndpoint", "phase": "create", "service": "acm" }, { - "cfn_type": "AWS::CertificateManager::Certificate", + "cfn_type": "AWS::CertificateManager::AcmeEndpoint", + "mappings": [], + "operation": "DeleteAcmeEndpoint", + "phase": "delete", + "service": "acm" + }, + { + "cfn_type": "AWS::CertificateManager::AcmeExternalAccountBinding", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { - "source": "Tags", - "target": "Tags" + "source": "AcmeEndpointArn", + "target": "AcmeEndpointArn" + }, + { + "source": "RoleArn", + "target": "RoleArn" } ], - "operation": "RemoveTagsFromCertificate", + "operation": "CreateAcmeExternalAccountBinding", + "phase": "create", + "service": "acm" + }, + { + "cfn_type": "AWS::CertificateManager::AcmeExternalAccountBinding", + "mappings": [], + "operation": "DeleteAcmeExternalAccountBinding", + "phase": "delete", + "service": "acm" + }, + { + "cfn_type": "AWS::CertificateManager::Certificate", + "mappings": [], + "operation": "DeleteCertificate", "phase": "delete", "service": "acm" }, { "cfn_type": "AWS::Chatbot::CustomAction", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ActionName", @@ -5735,18 +5885,6 @@ { "source": "AliasName", "target": "AliasName" - }, - { - "source": "Attachments", - "target": "Attachments" - }, - { - "source": "Definition", - "target": "Definition" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateCustomAction", @@ -5779,10 +5917,6 @@ "source": "SnsTopicArns", "target": "SnsTopicArns" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TeamId", "target": "TeamId" @@ -5821,10 +5955,6 @@ { "source": "SnsTopicArns", "target": "SnsTopicArns" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateSlackChannelConfiguration", @@ -5840,6 +5970,9 @@ }, { "cfn_type": "AWS::Chime::AppInstance", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "Metadata", @@ -5848,10 +5981,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAppInstance", @@ -5867,15 +5996,14 @@ }, { "cfn_type": "AWS::Chime::AppInstanceBot", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "AppInstanceArn", "target": "AppInstanceArn" }, - { - "source": "Configuration", - "target": "Configuration" - }, { "source": "Metadata", "target": "Metadata" @@ -5883,10 +6011,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAppInstanceBot", @@ -5902,6 +6026,9 @@ }, { "cfn_type": "AWS::Chime::AppInstanceUser", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "AppInstanceArn", @@ -5911,10 +6038,6 @@ "source": "AppInstanceUserId", "target": "AppInstanceUserId" }, - { - "source": "ExpirationSettings", - "target": "ExpirationSettings" - }, { "source": "Metadata", "target": "Metadata" @@ -5922,10 +6045,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAppInstanceUser", @@ -5940,12 +6059,34 @@ "service": "chime-sdk-identity" }, { - "cfn_type": "AWS::CleanRooms::AnalysisTemplate", + "cfn_type": "AWS::Chime::ChannelFlow", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { - "source": "analysisParameters", - "target": "AnalysisParameters" + "source": "AppInstanceArn", + "target": "AppInstanceArn" }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateChannelFlow", + "phase": "create", + "service": "chime-sdk-messaging" + }, + { + "cfn_type": "AWS::Chime::ChannelFlow", + "mappings": [], + "operation": "DeleteChannelFlow", + "phase": "delete", + "service": "chime-sdk-messaging" + }, + { + "cfn_type": "AWS::CleanRooms::AnalysisTemplate", + "mappings": [ { "source": "description", "target": "Description" @@ -5962,14 +6103,6 @@ "source": "name", "target": "Name" }, - { - "source": "schema", - "target": "Schema" - }, - { - "source": "source", - "target": "Source" - }, { "source": "tags", "target": "Tags" @@ -5994,6 +6127,10 @@ { "cfn_type": "AWS::CleanRooms::Collaboration", "mappings": [ + { + "source": "allowedResultRegions", + "target": "AllowedResultRegions" + }, { "source": "analyticsEngine", "target": "AnalyticsEngine" @@ -6002,33 +6139,21 @@ "source": "creatorDisplayName", "target": "CreatorDisplayName" }, - { - "source": "creatorMLMemberAbilities", - "target": "CreatorMLMemberAbilities" - }, { "source": "creatorMemberAbilities", "target": "CreatorMemberAbilities" }, - { - "source": "creatorPaymentConfiguration", - "target": "CreatorPaymentConfiguration" - }, - { - "source": "dataEncryptionMetadata", - "target": "DataEncryptionMetadata" - }, { "source": "description", "target": "Description" }, { - "source": "jobLogStatus", - "target": "JobLogStatus" + "source": "isMetricsEnabled", + "target": "IsMetricsEnabled" }, { - "source": "members", - "target": "Members" + "source": "jobLogStatus", + "target": "JobLogStatus" }, { "source": "name", @@ -6077,10 +6202,6 @@ "source": "selectedAnalysisMethods", "target": "SelectedAnalysisMethods" }, - { - "source": "tableReference", - "target": "TableReference" - }, { "source": "tags", "target": "Tags" @@ -6148,10 +6269,6 @@ "source": "description", "target": "Description" }, - { - "source": "inputReferenceConfig", - "target": "InputReferenceConfig" - }, { "source": "kmsKeyArn", "target": "KmsKeyArn" @@ -6193,12 +6310,44 @@ "target": "Description" }, { - "source": "idMappingConfig", - "target": "IdMappingConfig" + "source": "membershipIdentifier", + "target": "MembershipIdentifier" }, { - "source": "inputReferenceConfig", - "target": "InputReferenceConfig" + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateIdNamespaceAssociation", + "phase": "create", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::IdNamespaceAssociation", + "mappings": [ + { + "source": "membershipIdentifier", + "target": "MembershipIdentifier" + } + ], + "operation": "DeleteIdNamespaceAssociation", + "phase": "delete", + "service": "cleanrooms" + }, + { + "cfn_type": "AWS::CleanRooms::IntermediateTable", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" }, { "source": "membershipIdentifier", @@ -6213,19 +6362,19 @@ "target": "Tags" } ], - "operation": "CreateIdNamespaceAssociation", + "operation": "CreateIntermediateTable", "phase": "create", "service": "cleanrooms" }, { - "cfn_type": "AWS::CleanRooms::IdNamespaceAssociation", + "cfn_type": "AWS::CleanRooms::IntermediateTable", "mappings": [ { "source": "membershipIdentifier", "target": "MembershipIdentifier" } ], - "operation": "DeleteIdNamespaceAssociation", + "operation": "DeleteIntermediateTable", "phase": "delete", "service": "cleanrooms" }, @@ -6237,21 +6386,13 @@ "target": "CollaborationIdentifier" }, { - "source": "defaultJobResultConfiguration", - "target": "DefaultJobResultConfiguration" - }, - { - "source": "defaultResultConfiguration", - "target": "DefaultResultConfiguration" + "source": "isMetricsEnabled", + "target": "IsMetricsEnabled" }, { "source": "jobLogStatus", "target": "JobLogStatus" }, - { - "source": "paymentConfiguration", - "target": "PaymentConfiguration" - }, { "source": "queryLogStatus", "target": "QueryLogStatus" @@ -6283,10 +6424,6 @@ "source": "membershipIdentifier", "target": "MembershipIdentifier" }, - { - "source": "parameters", - "target": "Parameters" - }, { "source": "privacyBudgetType", "target": "PrivacyBudgetType" @@ -6319,10 +6456,6 @@ "source": "description", "target": "Description" }, - { - "source": "inferenceContainerConfig", - "target": "InferenceContainerConfig" - }, { "source": "kmsKeyArn", "target": "KmsKeyArn" @@ -6338,10 +6471,6 @@ { "source": "tags", "target": "Tags" - }, - { - "source": "trainingContainerConfig", - "target": "TrainingContainerConfig" } ], "operation": "CreateConfiguredModelAlgorithm", @@ -6374,10 +6503,6 @@ "source": "name", "target": "Name" }, - { - "source": "privacyConfiguration", - "target": "PrivacyConfiguration" - }, { "source": "tags", "target": "Tags" @@ -6417,10 +6542,6 @@ { "source": "tags", "target": "Tags" - }, - { - "source": "trainingData", - "target": "TrainingData" } ], "operation": "CreateTrainingDataset", @@ -6437,6 +6558,10 @@ { "cfn_type": "AWS::CloudFront::AnycastIpList", "mappings": [ + { + "source": "IpAddressType", + "target": "IpAddressType" + }, { "source": "IpCount", "target": "IpCount" @@ -6444,10 +6569,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAnycastIpList", @@ -6463,39 +6584,34 @@ }, { "cfn_type": "AWS::CloudFront::CachePolicy", - "mappings": [ - { - "source": "CachePolicyConfig", - "target": "CachePolicyConfig" - } - ], - "operation": "CreateCachePolicy", - "phase": "create", + "mappings": [], + "operation": "DeleteCachePolicy", + "phase": "delete", "service": "cloudfront" }, { - "cfn_type": "AWS::CloudFront::CachePolicy", + "cfn_type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", "mappings": [], - "operation": "DeleteCachePolicy", + "operation": "DeleteCloudFrontOriginAccessIdentity", "phase": "delete", "service": "cloudfront" }, { - "cfn_type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "cfn_type": "AWS::CloudFront::ConnectionFunction", "mappings": [ { - "source": "CloudFrontOriginAccessIdentityConfig", - "target": "CloudFrontOriginAccessIdentityConfig" + "source": "Name", + "target": "Name" } ], - "operation": "CreateCloudFrontOriginAccessIdentity", + "operation": "CreateConnectionFunction", "phase": "create", "service": "cloudfront" }, { - "cfn_type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "cfn_type": "AWS::CloudFront::ConnectionFunction", "mappings": [], - "operation": "DeleteCloudFrontOriginAccessIdentity", + "operation": "DeleteConnectionFunction", "phase": "delete", "service": "cloudfront" }, @@ -6517,10 +6633,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateConnectionGroup", @@ -6534,18 +6646,6 @@ "phase": "delete", "service": "cloudfront" }, - { - "cfn_type": "AWS::CloudFront::ContinuousDeploymentPolicy", - "mappings": [ - { - "source": "ContinuousDeploymentPolicyConfig", - "target": "ContinuousDeploymentPolicyConfig" - } - ], - "operation": "CreateContinuousDeploymentPolicy", - "phase": "create", - "service": "cloudfront" - }, { "cfn_type": "AWS::CloudFront::ContinuousDeploymentPolicy", "mappings": [], @@ -6553,18 +6653,6 @@ "phase": "delete", "service": "cloudfront" }, - { - "cfn_type": "AWS::CloudFront::Distribution", - "mappings": [ - { - "source": "DistributionConfig", - "target": "DistributionConfig" - } - ], - "operation": "CreateDistribution", - "phase": "create", - "service": "cloudfront" - }, { "cfn_type": "AWS::CloudFront::Distribution", "mappings": [], @@ -6579,37 +6667,17 @@ "source": "ConnectionGroupId", "target": "ConnectionGroupId" }, - { - "source": "Customizations", - "target": "Customizations" - }, { "source": "DistributionId", "target": "DistributionId" }, - { - "source": "Domains", - "target": "Domains" - }, { "source": "Enabled", "target": "Enabled" }, - { - "source": "ManagedCertificateRequest", - "target": "ManagedCertificateRequest" - }, { "source": "Name", "target": "Name" - }, - { - "source": "Parameters", - "target": "Parameters" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDistributionTenant", @@ -6626,14 +6694,6 @@ { "cfn_type": "AWS::CloudFront::Function", "mappings": [ - { - "source": "FunctionCode", - "target": "FunctionCode" - }, - { - "source": "FunctionConfig", - "target": "FunctionConfig" - }, { "source": "Name", "target": "Name" @@ -6655,18 +6715,6 @@ "phase": "delete", "service": "cloudfront" }, - { - "cfn_type": "AWS::CloudFront::KeyGroup", - "mappings": [ - { - "source": "KeyGroupConfig", - "target": "KeyGroupConfig" - } - ], - "operation": "CreateKeyGroup", - "phase": "create", - "service": "cloudfront" - }, { "cfn_type": "AWS::CloudFront::KeyGroup", "mappings": [], @@ -6681,10 +6729,6 @@ "source": "Comment", "target": "Comment" }, - { - "source": "ImportSource", - "target": "ImportSource" - }, { "source": "Name", "target": "Name" @@ -6712,10 +6756,6 @@ { "source": "DistributionId", "target": "DistributionId" - }, - { - "source": "MonitoringSubscription", - "target": "MonitoringSubscription" } ], "operation": "CreateMonitoringSubscription", @@ -6734,18 +6774,6 @@ "phase": "delete", "service": "cloudfront" }, - { - "cfn_type": "AWS::CloudFront::OriginAccessControl", - "mappings": [ - { - "source": "OriginAccessControlConfig", - "target": "OriginAccessControlConfig" - } - ], - "operation": "CreateOriginAccessControl", - "phase": "create", - "service": "cloudfront" - }, { "cfn_type": "AWS::CloudFront::OriginAccessControl", "mappings": [], @@ -6753,18 +6781,6 @@ "phase": "delete", "service": "cloudfront" }, - { - "cfn_type": "AWS::CloudFront::OriginRequestPolicy", - "mappings": [ - { - "source": "OriginRequestPolicyConfig", - "target": "OriginRequestPolicyConfig" - } - ], - "operation": "CreateOriginRequestPolicy", - "phase": "create", - "service": "cloudfront" - }, { "cfn_type": "AWS::CloudFront::OriginRequestPolicy", "mappings": [], @@ -6772,18 +6788,6 @@ "phase": "delete", "service": "cloudfront" }, - { - "cfn_type": "AWS::CloudFront::PublicKey", - "mappings": [ - { - "source": "PublicKeyConfig", - "target": "PublicKeyConfig" - } - ], - "operation": "CreatePublicKey", - "phase": "create", - "service": "cloudfront" - }, { "cfn_type": "AWS::CloudFront::PublicKey", "mappings": [], @@ -6794,10 +6798,6 @@ { "cfn_type": "AWS::CloudFront::RealtimeLogConfig", "mappings": [ - { - "source": "EndPoints", - "target": "EndPoints" - }, { "source": "Fields", "target": "Fields" @@ -6829,53 +6829,75 @@ }, { "cfn_type": "AWS::CloudFront::ResponseHeadersPolicy", + "mappings": [], + "operation": "DeleteResponseHeadersPolicy", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudFront::TrustStore", "mappings": [ { - "source": "ResponseHeadersPolicyConfig", - "target": "ResponseHeadersPolicyConfig" + "source": "Name", + "target": "Name" + }, + { + "source": "UseClientCertificateOCSPEndpoint", + "target": "UseClientCertificateOCSPEndpoint" } ], - "operation": "CreateResponseHeadersPolicy", + "operation": "CreateTrustStore", "phase": "create", "service": "cloudfront" }, { - "cfn_type": "AWS::CloudFront::ResponseHeadersPolicy", + "cfn_type": "AWS::CloudFront::TrustStore", "mappings": [], - "operation": "DeleteResponseHeadersPolicy", + "operation": "DeleteTrustStore", "phase": "delete", "service": "cloudfront" }, { "cfn_type": "AWS::CloudFront::VpcOrigin", + "mappings": [], + "operation": "DeleteVpcOrigin", + "phase": "delete", + "service": "cloudfront" + }, + { + "cfn_type": "AWS::CloudHSM::Cluster", "mappings": [ { - "source": "Tags", - "target": "Tags" + "source": "HsmType", + "target": "HsmType" + }, + { + "source": "Mode", + "target": "Mode" + }, + { + "source": "NetworkType", + "target": "NetworkType" }, { - "source": "VpcOriginEndpointConfig", - "target": "VpcOriginEndpointConfig" + "source": "SubnetIds", + "target": "SubnetIds" } ], - "operation": "CreateVpcOrigin", + "operation": "CreateCluster", "phase": "create", - "service": "cloudfront" + "service": "cloudhsmv2" }, { - "cfn_type": "AWS::CloudFront::VpcOrigin", + "cfn_type": "AWS::CloudHSM::Cluster", "mappings": [], - "operation": "DeleteVpcOrigin", + "operation": "DeleteCluster", "phase": "delete", - "service": "cloudfront" + "service": "cloudhsmv2" }, { "cfn_type": "AWS::CloudTrail::Channel", "mappings": [ - { - "source": "Destinations", - "target": "Destinations" - }, { "source": "Name", "target": "Name" @@ -6883,10 +6905,6 @@ { "source": "Source", "target": "Source" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateChannel", @@ -6907,17 +6925,9 @@ "source": "Name", "target": "Name" }, - { - "source": "RefreshSchedule", - "target": "RefreshSchedule" - }, { "source": "TerminationProtectionEnabled", "target": "TerminationProtectionEnabled" - }, - { - "source": "Widgets", - "target": "Widgets" } ], "operation": "CreateDashboard", @@ -6934,10 +6944,6 @@ { "cfn_type": "AWS::CloudTrail::EventDataStore", "mappings": [ - { - "source": "AdvancedEventSelectors", - "target": "AdvancedEventSelectors" - }, { "source": "BillingMode", "target": "BillingMode" @@ -7097,14 +7103,14 @@ "source": "DatapointsToAlarm", "target": "DatapointsToAlarm" }, - { - "source": "Dimensions", - "target": "Dimensions" - }, { "source": "EvaluateLowSampleCountPercentile", "target": "EvaluateLowSampleCountPercentile" }, + { + "source": "EvaluationInterval", + "target": "EvaluationInterval" + }, { "source": "EvaluationPeriods", "target": "EvaluationPeriods" @@ -7121,10 +7127,6 @@ "source": "MetricName", "target": "MetricName" }, - { - "source": "Metrics", - "target": "Metrics" - }, { "source": "Namespace", "target": "Namespace" @@ -7141,10 +7143,6 @@ "source": "Statistic", "target": "Statistic" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Threshold", "target": "Threshold" @@ -7173,6 +7171,29 @@ "phase": "delete", "service": "cloudwatch" }, + { + "cfn_type": "AWS::CloudWatch::AlarmMuteRule", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "PutAlarmMuteRule", + "phase": "create", + "service": "cloudwatch" + }, + { + "cfn_type": "AWS::CloudWatch::AlarmMuteRule", + "mappings": [], + "operation": "DeleteAlarmMuteRule", + "phase": "delete", + "service": "cloudwatch" + }, { "cfn_type": "AWS::CloudWatch::CompositeAlarm", "mappings": [ @@ -7215,10 +7236,6 @@ { "source": "OKActions", "target": "OKActions" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "PutCompositeAlarm", @@ -7262,10 +7279,6 @@ { "source": "RuleState", "target": "RuleState" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "PutInsightRule", @@ -7280,19 +7293,71 @@ "service": "cloudwatch" }, { - "cfn_type": "AWS::CloudWatch::MetricStream", + "cfn_type": "AWS::CloudWatch::LogAlarm", "mappings": [ { - "source": "ExcludeFilters", - "target": "ExcludeFilters" + "source": "ActionLogLineCount", + "target": "ActionLogLineCount" }, { - "source": "FirehoseArn", - "target": "FirehoseArn" + "source": "ActionLogLineRoleArn", + "target": "ActionLogLineRoleArn" + }, + { + "source": "ActionsEnabled", + "target": "ActionsEnabled" + }, + { + "source": "AlarmActions", + "target": "AlarmActions" + }, + { + "source": "AlarmDescription", + "target": "AlarmDescription" }, { - "source": "IncludeFilters", - "target": "IncludeFilters" + "source": "AlarmName", + "target": "AlarmName" + }, + { + "source": "ComparisonOperator", + "target": "ComparisonOperator" + }, + { + "source": "InsufficientDataActions", + "target": "InsufficientDataActions" + }, + { + "source": "OKActions", + "target": "OKActions" + }, + { + "source": "QueryResultsToAlarm", + "target": "QueryResultsToAlarm" + }, + { + "source": "QueryResultsToEvaluate", + "target": "QueryResultsToEvaluate" + }, + { + "source": "Threshold", + "target": "Threshold" + }, + { + "source": "TreatMissingData", + "target": "TreatMissingData" + } + ], + "operation": "PutLogAlarm", + "phase": "create", + "service": "cloudwatch" + }, + { + "cfn_type": "AWS::CloudWatch::MetricStream", + "mappings": [ + { + "source": "FirehoseArn", + "target": "FirehoseArn" }, { "source": "IncludeLinkedAccountsMetrics", @@ -7309,14 +7374,6 @@ { "source": "RoleArn", "target": "RoleArn" - }, - { - "source": "StatisticsConfigurations", - "target": "StatisticsConfigurations" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "PutMetricStream", @@ -7341,10 +7398,6 @@ { "source": "domain", "target": "DomainName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateDomain", @@ -7381,10 +7434,6 @@ { "source": "domainOwner", "target": "DomainOwner" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreatePackageGroup", @@ -7421,14 +7470,6 @@ { "source": "repository", "target": "RepositoryName" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "upstreams", - "target": "Upstreams" } ], "operation": "CreateRepository", @@ -7458,10 +7499,6 @@ "source": "baseCapacity", "target": "BaseCapacity" }, - { - "source": "computeConfiguration", - "target": "ComputeConfiguration" - }, { "source": "computeType", "target": "ComputeType" @@ -7485,14 +7522,6 @@ { "source": "overflowBehavior", "target": "OverflowBehavior" - }, - { - "source": "scalingConfiguration", - "target": "ScalingConfiguration" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateFleet", @@ -7520,10 +7549,6 @@ { "source": "ProviderType", "target": "ProviderType" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateConnection", @@ -7547,10 +7572,6 @@ { "source": "computePlatform", "target": "ComputePlatform" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateApplication", @@ -7579,18 +7600,6 @@ { "source": "deploymentConfigName", "target": "DeploymentConfigName" - }, - { - "source": "minimumHealthyHosts", - "target": "MinimumHealthyHosts" - }, - { - "source": "trafficRoutingConfig", - "target": "TrafficRoutingConfig" - }, - { - "source": "zonalConfig", - "target": "ZonalConfig" } ], "operation": "CreateDeploymentConfig", @@ -7612,26 +7621,14 @@ { "cfn_type": "AWS::CodeDeploy::DeploymentGroup", "mappings": [ - { - "source": "alarmConfiguration", - "target": "AlarmConfiguration" - }, { "source": "applicationName", "target": "ApplicationName" }, - { - "source": "autoRollbackConfiguration", - "target": "AutoRollbackConfiguration" - }, { "source": "autoScalingGroups", "target": "AutoScalingGroups" }, - { - "source": "blueGreenDeploymentConfiguration", - "target": "BlueGreenDeploymentConfiguration" - }, { "source": "deploymentConfigName", "target": "DeploymentConfigName" @@ -7640,34 +7637,6 @@ "source": "deploymentGroupName", "target": "DeploymentGroupName" }, - { - "source": "deploymentStyle", - "target": "DeploymentStyle" - }, - { - "source": "ec2TagFilters", - "target": "Ec2TagFilters" - }, - { - "source": "ec2TagSet", - "target": "Ec2TagSet" - }, - { - "source": "ecsServices", - "target": "ECSServices" - }, - { - "source": "loadBalancerInfo", - "target": "LoadBalancerInfo" - }, - { - "source": "onPremisesInstanceTagFilters", - "target": "OnPremisesInstanceTagFilters" - }, - { - "source": "onPremisesTagSet", - "target": "OnPremisesTagSet" - }, { "source": "outdatedInstancesStrategy", "target": "OutdatedInstancesStrategy" @@ -7676,17 +7645,9 @@ "source": "serviceRoleArn", "target": "ServiceRoleArn" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "terminationHookEnabled", "target": "TerminationHookEnabled" - }, - { - "source": "triggerConfigurations", - "target": "TriggerConfigurations" } ], "operation": "CreateDeploymentGroup", @@ -7711,6 +7672,9 @@ }, { "cfn_type": "AWS::CodeGuruProfiler::ProfilingGroup", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "computePlatform", @@ -7748,30 +7712,10 @@ "source": "category", "target": "Category" }, - { - "source": "configurationProperties", - "target": "ConfigurationProperties" - }, - { - "source": "inputArtifactDetails", - "target": "InputArtifactDetails" - }, - { - "source": "outputArtifactDetails", - "target": "OutputArtifactDetails" - }, { "source": "provider", "target": "Provider" }, - { - "source": "settings", - "target": "Settings" - }, - { - "source": "tags", - "target": "Tags" - }, { "source": "version", "target": "Version" @@ -7801,18 +7745,6 @@ "phase": "delete", "service": "codepipeline" }, - { - "cfn_type": "AWS::CodePipeline::Pipeline", - "mappings": [ - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreatePipeline", - "phase": "create", - "service": "codepipeline" - }, { "cfn_type": "AWS::CodePipeline::Pipeline", "mappings": [ @@ -7825,18 +7757,6 @@ "phase": "delete", "service": "codepipeline" }, - { - "cfn_type": "AWS::CodePipeline::Webhook", - "mappings": [ - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "PutWebhook", - "phase": "create", - "service": "codepipeline" - }, { "cfn_type": "AWS::CodePipeline::Webhook", "mappings": [ @@ -7863,10 +7783,6 @@ { "source": "ProviderType", "target": "ProviderType" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateConnection", @@ -7898,10 +7814,6 @@ { "source": "RepositoryName", "target": "RepositoryName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateRepositoryLink", @@ -7973,6 +7885,9 @@ }, { "cfn_type": "AWS::CodeStarNotifications::NotificationRule", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "DetailType", @@ -7993,14 +7908,6 @@ { "source": "Status", "target": "Status" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "Targets", - "target": "Targets" } ], "operation": "CreateNotificationRule", @@ -8025,10 +7932,6 @@ "source": "AllowUnauthenticatedIdentities", "target": "AllowUnauthenticatedIdentities" }, - { - "source": "CognitoIdentityProviders", - "target": "CognitoIdentityProviders" - }, { "source": "DeveloperProviderName", "target": "DeveloperProviderName" @@ -8037,10 +7940,6 @@ "source": "IdentityPoolName", "target": "IdentityPoolName" }, - { - "source": "IdentityPoolTags", - "target": "IdentityPoolTags" - }, { "source": "OpenIdConnectProviderARNs", "target": "OpenIdConnectProviderARNs" @@ -8048,10 +7947,6 @@ { "source": "SamlProviderARNs", "target": "SamlProviderARNs" - }, - { - "source": "SupportedLoginProviders", - "target": "SupportedLoginProviders" } ], "operation": "CreateIdentityPool", @@ -8076,10 +7971,6 @@ "source": "IdentityProviderName", "target": "IdentityProviderName" }, - { - "source": "PrincipalTags", - "target": "PrincipalTags" - }, { "source": "UseDefaults", "target": "UseDefaults" @@ -8090,38 +7981,34 @@ "service": "cognito-identity" }, { - "cfn_type": "AWS::Cognito::IdentityPoolRoleAttachment", + "cfn_type": "AWS::Cognito::LogDeliveryConfiguration", "mappings": [ { - "source": "IdentityPoolId", - "target": "IdentityPoolId" - }, - { - "source": "RoleMappings", - "target": "RoleMappings" - }, - { - "source": "Roles", - "target": "Roles" + "source": "UserPoolId", + "target": "UserPoolId" } ], - "operation": "SetIdentityPoolRoles", + "operation": "SetLogDeliveryConfiguration", "phase": "create", - "service": "cognito-identity" + "service": "cognito-idp" }, { - "cfn_type": "AWS::Cognito::LogDeliveryConfiguration", + "cfn_type": "AWS::Cognito::ManagedLoginBranding", "mappings": [ { - "source": "LogConfigurations", - "target": "LogConfigurations" + "source": "ClientId", + "target": "ClientId" + }, + { + "source": "UseCognitoProvidedValues", + "target": "UseCognitoProvidedValues" }, { "source": "UserPoolId", "target": "UserPoolId" } ], - "operation": "SetLogDeliveryConfiguration", + "operation": "CreateManagedLoginBranding", "phase": "create", "service": "cognito-idp" }, @@ -8129,53 +8016,57 @@ "cfn_type": "AWS::Cognito::ManagedLoginBranding", "mappings": [ { - "source": "Assets", - "target": "Assets" - }, + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "DeleteManagedLoginBranding", + "phase": "delete", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::Terms", + "mappings": [ { "source": "ClientId", "target": "ClientId" }, { - "source": "Settings", - "target": "Settings" + "source": "Enforcement", + "target": "Enforcement" }, { - "source": "UseCognitoProvidedValues", - "target": "UseCognitoProvidedValues" + "source": "TermsName", + "target": "TermsName" + }, + { + "source": "TermsSource", + "target": "TermsSource" }, { "source": "UserPoolId", "target": "UserPoolId" } ], - "operation": "CreateManagedLoginBranding", + "operation": "CreateTerms", "phase": "create", "service": "cognito-idp" }, { - "cfn_type": "AWS::Cognito::ManagedLoginBranding", + "cfn_type": "AWS::Cognito::Terms", "mappings": [ { "source": "UserPoolId", "target": "UserPoolId" } ], - "operation": "DeleteManagedLoginBranding", + "operation": "DeleteTerms", "phase": "delete", "service": "cognito-idp" }, { "cfn_type": "AWS::Cognito::UserPool", "mappings": [ - { - "source": "AccountRecoverySetting", - "target": "AccountRecoverySetting" - }, - { - "source": "AdminCreateUserConfig", - "target": "AdminCreateUserConfig" - }, { "source": "AliasAttributes", "target": "AliasAttributes" @@ -8188,14 +8079,6 @@ "source": "DeletionProtection", "target": "DeletionProtection" }, - { - "source": "DeviceConfiguration", - "target": "DeviceConfiguration" - }, - { - "source": "EmailConfiguration", - "target": "EmailConfiguration" - }, { "source": "EmailVerificationMessage", "target": "EmailVerificationMessage" @@ -8204,46 +8087,18 @@ "source": "EmailVerificationSubject", "target": "EmailVerificationSubject" }, - { - "source": "LambdaConfig", - "target": "LambdaConfig" - }, { "source": "MfaConfiguration", "target": "MfaConfiguration" }, - { - "source": "Policies", - "target": "Policies" - }, - { - "source": "Schema", - "target": "Schema" - }, { "source": "SmsAuthenticationMessage", "target": "SmsAuthenticationMessage" }, - { - "source": "SmsConfiguration", - "target": "SmsConfiguration" - }, { "source": "SmsVerificationMessage", "target": "SmsVerificationMessage" }, - { - "source": "UserAttributeUpdateSettings", - "target": "UserAttributeUpdateSettings" - }, - { - "source": "UserPoolAddOns", - "target": "UserPoolAddOns" - }, - { - "source": "UserPoolTags", - "target": "UserPoolTags" - }, { "source": "UserPoolTier", "target": "UserPoolTier" @@ -8251,14 +8106,6 @@ { "source": "UsernameAttributes", "target": "UsernameAttributes" - }, - { - "source": "UsernameConfiguration", - "target": "UsernameConfiguration" - }, - { - "source": "VerificationMessageTemplate", - "target": "VerificationMessageTemplate" } ], "operation": "CreateUserPool", @@ -8291,10 +8138,6 @@ "source": "AllowedOAuthScopes", "target": "AllowedOAuthScopes" }, - { - "source": "AnalyticsConfiguration", - "target": "AnalyticsConfiguration" - }, { "source": "AuthSessionValidity", "target": "AuthSessionValidity" @@ -8343,10 +8186,6 @@ "source": "ReadAttributes", "target": "ReadAttributes" }, - { - "source": "RefreshTokenRotation", - "target": "RefreshTokenRotation" - }, { "source": "RefreshTokenValidity", "target": "RefreshTokenValidity" @@ -8355,10 +8194,6 @@ "source": "SupportedIdentityProviders", "target": "SupportedIdentityProviders" }, - { - "source": "TokenValidityUnits", - "target": "TokenValidityUnits" - }, { "source": "UserPoolId", "target": "UserPoolId" @@ -8387,10 +8222,6 @@ { "cfn_type": "AWS::Cognito::UserPoolDomain", "mappings": [ - { - "source": "CustomDomainConfig", - "target": "CustomDomainConfig" - }, { "source": "Domain", "target": "Domain" @@ -8471,18 +8302,10 @@ { "cfn_type": "AWS::Cognito::UserPoolIdentityProvider", "mappings": [ - { - "source": "AttributeMapping", - "target": "AttributeMapping" - }, { "source": "IdpIdentifiers", "target": "IdpIdentifiers" }, - { - "source": "ProviderDetails", - "target": "ProviderDetails" - }, { "source": "ProviderName", "target": "ProviderName" @@ -8516,6 +8339,38 @@ "phase": "delete", "service": "cognito-idp" }, + { + "cfn_type": "AWS::Cognito::UserPoolReplica", + "mappings": [ + { + "source": "RegionName", + "target": "RegionName" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "CreateUserPoolReplica", + "phase": "create", + "service": "cognito-idp" + }, + { + "cfn_type": "AWS::Cognito::UserPoolReplica", + "mappings": [ + { + "source": "RegionName", + "target": "RegionName" + }, + { + "source": "UserPoolId", + "target": "UserPoolId" + } + ], + "operation": "DeleteUserPoolReplica", + "phase": "delete", + "service": "cognito-idp" + }, { "cfn_type": "AWS::Cognito::UserPoolResourceServer", "mappings": [ @@ -8527,10 +8382,6 @@ "source": "Name", "target": "Name" }, - { - "source": "Scopes", - "target": "Scopes" - }, { "source": "UserPoolId", "target": "UserPoolId" @@ -8559,22 +8410,10 @@ { "cfn_type": "AWS::Cognito::UserPoolRiskConfigurationAttachment", "mappings": [ - { - "source": "AccountTakeoverRiskConfiguration", - "target": "AccountTakeoverRiskConfiguration" - }, { "source": "ClientId", "target": "ClientId" }, - { - "source": "CompromisedCredentialsRiskConfiguration", - "target": "CompromisedCredentialsRiskConfiguration" - }, - { - "source": "RiskExceptionConfiguration", - "target": "RiskExceptionConfiguration" - }, { "source": "UserPoolId", "target": "UserPoolId" @@ -8606,6 +8445,9 @@ }, { "cfn_type": "AWS::Comprehend::DocumentClassifier", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "DataAccessRoleArn", @@ -8615,10 +8457,6 @@ "source": "DocumentClassifierName", "target": "DocumentClassifierName" }, - { - "source": "InputDataConfig", - "target": "InputDataConfig" - }, { "source": "LanguageCode", "target": "LanguageCode" @@ -8635,14 +8473,6 @@ "source": "ModelPolicy", "target": "ModelPolicy" }, - { - "source": "OutputDataConfig", - "target": "OutputDataConfig" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VersionName", "target": "VersionName" @@ -8650,10 +8480,6 @@ { "source": "VolumeKmsKeyId", "target": "VolumeKmsKeyId" - }, - { - "source": "VpcConfig", - "target": "VpcConfig" } ], "operation": "CreateDocumentClassifier", @@ -8669,6 +8495,9 @@ }, { "cfn_type": "AWS::Comprehend::Flywheel", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "ActiveModelArn", @@ -8682,10 +8511,6 @@ "source": "DataLakeS3Uri", "target": "DataLakeS3Uri" }, - { - "source": "DataSecurityConfig", - "target": "DataSecurityConfig" - }, { "source": "FlywheelName", "target": "FlywheelName" @@ -8693,14 +8518,6 @@ { "source": "ModelType", "target": "ModelType" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "TaskConfig", - "target": "TaskConfig" } ], "operation": "CreateFlywheel", @@ -8724,10 +8541,6 @@ { "source": "AuthorizedAwsRegion", "target": "AuthorizedAwsRegion" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "PutAggregationAuthorization", @@ -8750,18 +8563,6 @@ "phase": "delete", "service": "config" }, - { - "cfn_type": "AWS::Config::ConfigRule", - "mappings": [ - { - "source": "ConfigRule", - "target": "ConfigRuleName" - } - ], - "operation": "PutConfigRule", - "phase": "create", - "service": "config" - }, { "cfn_type": "AWS::Config::ConfigRule", "mappings": [ @@ -8777,21 +8578,9 @@ { "cfn_type": "AWS::Config::ConfigurationAggregator", "mappings": [ - { - "source": "AccountAggregationSources", - "target": "AccountAggregationSources" - }, { "source": "ConfigurationAggregatorName", "target": "ConfigurationAggregatorName" - }, - { - "source": "OrganizationAggregationSource", - "target": "OrganizationAggregationSource" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "PutConfigurationAggregator", @@ -8813,10 +8602,6 @@ { "cfn_type": "AWS::Config::ConformancePack", "mappings": [ - { - "source": "ConformancePackInputParameters", - "target": "ConformancePackInputParameters" - }, { "source": "ConformancePackName", "target": "ConformancePackName" @@ -8836,10 +8621,6 @@ { "source": "TemplateS3Uri", "target": "TemplateS3Uri" - }, - { - "source": "TemplateSSMDocumentDetails", - "target": "TemplateSSMDocumentDetails" } ], "operation": "PutConformancePack", @@ -8858,13 +8639,16 @@ "phase": "delete", "service": "config" }, + { + "cfn_type": "AWS::Config::Connector", + "mappings": [], + "operation": "DeleteConnector", + "phase": "delete", + "service": "config" + }, { "cfn_type": "AWS::Config::OrganizationConformancePack", "mappings": [ - { - "source": "ConformancePackInputParameters", - "target": "ConformancePackInputParameters" - }, { "source": "DeliveryS3Bucket", "target": "DeliveryS3Bucket" @@ -8922,18 +8706,6 @@ "phase": "delete", "service": "config" }, - { - "cfn_type": "AWS::Config::StoredQuery", - "mappings": [ - { - "source": "Tags", - "target": "Tags" - } - ], - "operation": "PutStoredQuery", - "phase": "create", - "service": "config" - }, { "cfn_type": "AWS::Config::StoredQuery", "mappings": [ @@ -8976,6 +8748,9 @@ }, { "cfn_type": "AWS::Connect::ApprovedOrigin", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "InstanceId", @@ -8992,6 +8767,9 @@ }, { "cfn_type": "AWS::Connect::ApprovedOrigin", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "InstanceId", @@ -9043,6 +8821,9 @@ }, { "cfn_type": "AWS::Connect::ContactFlowModule", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Content", @@ -9056,6 +8837,10 @@ "source": "Name", "target": "Name" }, + { + "source": "Settings", + "target": "Settings" + }, { "source": "Tags", "target": "Tags" @@ -9072,6 +8857,66 @@ "phase": "delete", "service": "connect" }, + { + "cfn_type": "AWS::Connect::ContactFlowModuleAlias", + "mappings": [ + { + "source": "ContactFlowModuleId", + "target": "ContactFlowModuleId" + }, + { + "source": "ContactFlowModuleVersion", + "target": "ContactFlowModuleVersion" + }, + { + "source": "Description", + "target": "Description" + } + ], + "operation": "CreateContactFlowModuleAlias", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ContactFlowModuleAlias", + "mappings": [ + { + "source": "ContactFlowModuleId", + "target": "ContactFlowModuleId" + } + ], + "operation": "DeleteContactFlowModuleAlias", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ContactFlowModuleVersion", + "mappings": [ + { + "source": "ContactFlowModuleId", + "target": "ContactFlowModuleId" + }, + { + "source": "Description", + "target": "Description" + } + ], + "operation": "CreateContactFlowModuleVersion", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::ContactFlowModuleVersion", + "mappings": [ + { + "source": "ContactFlowModuleId", + "target": "ContactFlowModuleId" + } + ], + "operation": "DeleteContactFlowModuleVersion", + "phase": "delete", + "service": "connect" + }, { "cfn_type": "AWS::Connect::ContactFlowVersion", "mappings": [ @@ -9140,8 +8985,81 @@ "phase": "delete", "service": "connect" }, + { + "cfn_type": "AWS::Connect::DataTable", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Status", + "target": "Status" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "TimeZone", + "target": "TimeZone" + }, + { + "source": "ValueLockLevel", + "target": "ValueLockLevel" + } + ], + "operation": "CreateDataTable", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::DataTable", + "mappings": [], + "operation": "DeleteDataTable", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::DataTableAttribute", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Primary", + "target": "Primary" + }, + { + "source": "ValueType", + "target": "ValueType" + } + ], + "operation": "CreateDataTableAttribute", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::DataTableAttribute", + "mappings": [], + "operation": "DeleteDataTableAttribute", + "phase": "delete", + "service": "connect" + }, { "cfn_type": "AWS::Connect::EmailAddress", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -9173,18 +9091,17 @@ }, { "cfn_type": "AWS::Connect::EvaluationForm", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", "target": "Description" }, { - "source": "Items", - "target": "Items" - }, - { - "source": "ScoringStrategy", - "target": "ScoringStrategy" + "source": "Tags", + "target": "Tags" }, { "source": "Title", @@ -9205,10 +9122,6 @@ { "cfn_type": "AWS::Connect::HoursOfOperation", "mappings": [ - { - "source": "Config", - "target": "Config" - }, { "source": "Description", "target": "Description" @@ -9239,6 +9152,9 @@ }, { "cfn_type": "AWS::Connect::Instance", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "DirectoryId", @@ -9263,6 +9179,9 @@ }, { "cfn_type": "AWS::Connect::Instance", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [], "operation": "DeleteInstance", "phase": "delete", @@ -9270,6 +9189,9 @@ }, { "cfn_type": "AWS::Connect::InstanceStorageConfig", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ResourceType", @@ -9282,6 +9204,9 @@ }, { "cfn_type": "AWS::Connect::InstanceStorageConfig", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ResourceType", @@ -9328,8 +9253,41 @@ "phase": "delete", "service": "connect" }, + { + "cfn_type": "AWS::Connect::Notification", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "Priority", + "target": "Priority" + }, + { + "source": "Recipients", + "target": "Recipients" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateNotification", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::Notification", + "mappings": [], + "operation": "DeleteNotification", + "phase": "delete", + "service": "connect" + }, { "cfn_type": "AWS::Connect::PhoneNumber", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "SourcePhoneNumberArn", @@ -9346,6 +9304,9 @@ }, { "cfn_type": "AWS::Connect::PhoneNumber", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [], "operation": "ReleasePhoneNumber", "phase": "delete", @@ -9359,8 +9320,8 @@ "target": "Name" }, { - "source": "Values", - "target": "Values" + "source": "Purposes", + "target": "Purposes" } ], "operation": "CreatePredefinedAttribute", @@ -9425,14 +9386,6 @@ "source": "Name", "target": "Name" }, - { - "source": "OutboundCallerConfig", - "target": "OutboundCallerConfig" - }, - { - "source": "OutboundEmailConfig", - "target": "OutboundEmailConfig" - }, { "source": "Tags", "target": "Tags" @@ -9460,10 +9413,6 @@ "source": "Name", "target": "Name" }, - { - "source": "QuickConnectConfig", - "target": "QuickConnectConfig" - }, { "source": "Tags", "target": "Tags" @@ -9491,18 +9440,10 @@ "source": "Description", "target": "Description" }, - { - "source": "MediaConcurrencies", - "target": "MediaConcurrencies" - }, { "source": "Name", "target": "Name" }, - { - "source": "QueueConfigs", - "target": "QueueConfigs" - }, { "source": "Tags", "target": "Tags" @@ -9521,11 +9462,10 @@ }, { "cfn_type": "AWS::Connect::Rule", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "Actions", - "target": "Actions" - }, { "source": "Function", "target": "Function" @@ -9537,10 +9477,6 @@ { "source": "PublishStatus", "target": "PublishStatus" - }, - { - "source": "TriggerEventSource", - "target": "TriggerEventSource" } ], "operation": "CreateRule", @@ -9556,6 +9492,9 @@ }, { "cfn_type": "AWS::Connect::SecurityKey", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "InstanceId", @@ -9572,6 +9511,9 @@ }, { "cfn_type": "AWS::Connect::SecurityKey", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "InstanceId", @@ -9589,14 +9531,6 @@ "source": "AllowedAccessControlHierarchyGroupId", "target": "AllowedAccessControlHierarchyGroupId" }, - { - "source": "AllowedAccessControlTags", - "target": "AllowedAccessControlTags" - }, - { - "source": "Applications", - "target": "Applications" - }, { "source": "Description", "target": "Description" @@ -9635,26 +9569,52 @@ }, { "cfn_type": "AWS::Connect::TaskTemplate", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ClientToken", "target": "ClientToken" }, { - "source": "Constraints", - "target": "Constraints" + "source": "Description", + "target": "Description" }, { - "source": "Defaults", - "target": "Defaults" + "source": "Name", + "target": "Name" + }, + { + "source": "Status", + "target": "Status" + } + ], + "operation": "CreateTaskTemplate", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::TaskTemplate", + "mappings": [], + "operation": "DeleteTaskTemplate", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::TestCase", + "mappings": [ + { + "source": "Content", + "target": "Content" }, { "source": "Description", "target": "Description" }, { - "source": "Fields", - "target": "Fields" + "source": "InitializationData", + "target": "InitializationData" }, { "source": "Name", @@ -9663,21 +9623,28 @@ { "source": "Status", "target": "Status" + }, + { + "source": "Tags", + "target": "Tags" } ], - "operation": "CreateTaskTemplate", + "operation": "CreateTestCase", "phase": "create", "service": "connect" }, { - "cfn_type": "AWS::Connect::TaskTemplate", + "cfn_type": "AWS::Connect::TestCase", "mappings": [], - "operation": "DeleteTaskTemplate", + "operation": "DeleteTestCase", "phase": "delete", "service": "connect" }, { "cfn_type": "AWS::Connect::TrafficDistributionGroup", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -9710,18 +9677,10 @@ "source": "DirectoryUserId", "target": "DirectoryUserId" }, - { - "source": "IdentityInfo", - "target": "IdentityInfo" - }, { "source": "Password", "target": "Password" }, - { - "source": "PhoneConfig", - "target": "PhoneConfig" - }, { "source": "Tags", "target": "Tags" @@ -9767,6 +9726,9 @@ }, { "cfn_type": "AWS::Connect::View", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -9816,19 +9778,42 @@ "service": "connect" }, { - "cfn_type": "AWS::ConnectCampaigns::Campaign", + "cfn_type": "AWS::Connect::Workspace", "mappings": [ { - "source": "dialerConfig", - "target": "DialerConfig" + "source": "Description", + "target": "Description" }, { - "source": "name", + "source": "Name", "target": "Name" }, { - "source": "outboundCallConfig", - "target": "OutboundCallConfig" + "source": "Tags", + "target": "Tags" + }, + { + "source": "Title", + "target": "Title" + } + ], + "operation": "CreateWorkspace", + "phase": "create", + "service": "connect" + }, + { + "cfn_type": "AWS::Connect::Workspace", + "mappings": [], + "operation": "DeleteWorkspace", + "phase": "delete", + "service": "connect" + }, + { + "cfn_type": "AWS::ConnectCampaigns::Campaign", + "mappings": [ + { + "source": "name", + "target": "Name" }, { "source": "tags", @@ -9849,18 +9834,6 @@ { "cfn_type": "AWS::ConnectCampaignsV2::Campaign", "mappings": [ - { - "source": "channelSubtypeConfig", - "target": "ChannelSubtypeConfig" - }, - { - "source": "communicationLimitsOverride", - "target": "CommunicationLimitsOverride" - }, - { - "source": "communicationTimeConfig", - "target": "CommunicationTimeConfig" - }, { "source": "connectCampaignFlowArn", "target": "ConnectCampaignFlowArn" @@ -9873,17 +9846,13 @@ "source": "name", "target": "Name" }, - { - "source": "schedule", - "target": "Schedule" - }, - { - "source": "source", - "target": "Source" - }, { "source": "tags", "target": "Tags" + }, + { + "source": "type", + "target": "Type" } ], "operation": "CreateCampaign", @@ -9908,10 +9877,6 @@ "source": "baselineVersion", "target": "BaselineVersion" }, - { - "source": "parameters", - "target": "Parameters" - }, { "source": "tags", "target": "Tags" @@ -9932,10 +9897,6 @@ "source": "controlIdentifier", "target": "ControlIdentifier" }, - { - "source": "parameters", - "target": "Parameters" - }, { "source": "tags", "target": "Tags" @@ -9953,8 +9914,8 @@ "cfn_type": "AWS::ControlTower::LandingZone", "mappings": [ { - "source": "manifest", - "target": "Manifest" + "source": "remediationTypes", + "target": "RemediationTypes" }, { "source": "tags", @@ -9979,18 +9940,10 @@ { "cfn_type": "AWS::CustomerProfiles::CalculatedAttributeDefinition", "mappings": [ - { - "source": "AttributeDetails", - "target": "AttributeDetails" - }, { "source": "CalculatedAttributeName", "target": "CalculatedAttributeName" }, - { - "source": "Conditions", - "target": "Conditions" - }, { "source": "Description", "target": "Description" @@ -10056,31 +10009,67 @@ "target": "DomainName" }, { - "source": "Matching", - "target": "Matching" + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDomain", + "phase": "create", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::Domain", + "mappings": [ + { + "source": "DomainName", + "target": "DomainName" + } + ], + "operation": "DeleteDomain", + "phase": "delete", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::DomainObjectType", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "EncryptionKey", + "target": "EncryptionKey" }, { - "source": "RuleBasedMatching", - "target": "RuleBasedMatching" + "source": "ObjectTypeName", + "target": "ObjectTypeName" }, { "source": "Tags", "target": "Tags" } ], - "operation": "CreateDomain", + "operation": "PutDomainObjectType", "phase": "create", "service": "customer-profiles" }, { - "cfn_type": "AWS::CustomerProfiles::Domain", + "cfn_type": "AWS::CustomerProfiles::DomainObjectType", "mappings": [ { "source": "DomainName", "target": "DomainName" + }, + { + "source": "ObjectTypeName", + "target": "ObjectTypeName" } ], - "operation": "DeleteDomain", + "operation": "DeleteDomainObjectType", "phase": "delete", "service": "customer-profiles" }, @@ -10135,14 +10124,6 @@ "source": "DomainName", "target": "DomainName" }, - { - "source": "EventTriggerConditions", - "target": "EventTriggerConditions" - }, - { - "source": "EventTriggerLimits", - "target": "EventTriggerLimits" - }, { "source": "EventTriggerName", "target": "EventTriggerName" @@ -10191,17 +10172,13 @@ "source": "EventTriggerNames", "target": "EventTriggerNames" }, - { - "source": "FlowDefinition", - "target": "FlowDefinition" - }, { "source": "ObjectTypeName", "target": "ObjectTypeName" }, { - "source": "ObjectTypeNames", - "target": "ObjectTypeNames" + "source": "Scope", + "target": "Scope" }, { "source": "Tags", @@ -10255,14 +10232,6 @@ "source": "ExpirationDays", "target": "ExpirationDays" }, - { - "source": "Fields", - "target": "Fields" - }, - { - "source": "Keys", - "target": "Keys" - }, { "source": "MaxProfileObjectCount", "target": "MaxProfileObjectCount" @@ -10275,6 +10244,10 @@ "source": "SourceLastUpdatedTimestampFormat", "target": "SourceLastUpdatedTimestampFormat" }, + { + "source": "SourcePriority", + "target": "SourcePriority" + }, { "source": "Tags", "target": "Tags" @@ -10304,6 +10277,50 @@ "phase": "delete", "service": "customer-profiles" }, + { + "cfn_type": "AWS::CustomerProfiles::Recommender", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "RecommenderName", + "target": "RecommenderName" + }, + { + "source": "RecommenderRecipeName", + "target": "RecommenderRecipeName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRecommender", + "phase": "create", + "service": "customer-profiles" + }, + { + "cfn_type": "AWS::CustomerProfiles::Recommender", + "mappings": [ + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "RecommenderName", + "target": "RecommenderName" + } + ], + "operation": "DeleteRecommender", + "phase": "delete", + "service": "customer-profiles" + }, { "cfn_type": "AWS::CustomerProfiles::SegmentDefinition", "mappings": [ @@ -10324,8 +10341,8 @@ "target": "SegmentDefinitionName" }, { - "source": "SegmentGroups", - "target": "SegmentGroups" + "source": "SegmentSqlQuery", + "target": "SegmentSqlQuery" }, { "source": "Tags", @@ -10362,10 +10379,6 @@ { "source": "CertificatePem", "target": "CertificatePem" - }, - { - "source": "CertificateWallet", - "target": "CertificateWallet" } ], "operation": "ImportCertificate", @@ -10397,14 +10410,6 @@ { "source": "ServiceAccessRoleArn", "target": "ServiceAccessRoleArn" - }, - { - "source": "SourceDataSettings", - "target": "SourceDataSettings" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDataMigration", @@ -10437,14 +10442,6 @@ { "source": "Engine", "target": "Engine" - }, - { - "source": "Settings", - "target": "Settings" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDataProvider", @@ -10474,18 +10471,6 @@ "source": "DatabaseName", "target": "DatabaseName" }, - { - "source": "DocDbSettings", - "target": "DocDbSettings" - }, - { - "source": "DynamoDbSettings", - "target": "DynamoDbSettings" - }, - { - "source": "ElasticsearchSettings", - "target": "ElasticsearchSettings" - }, { "source": "EndpointIdentifier", "target": "EndpointIdentifier" @@ -10502,46 +10487,10 @@ "source": "ExtraConnectionAttributes", "target": "ExtraConnectionAttributes" }, - { - "source": "GcpMySQLSettings", - "target": "GcpMySQLSettings" - }, - { - "source": "IBMDb2Settings", - "target": "IbmDb2Settings" - }, - { - "source": "KafkaSettings", - "target": "KafkaSettings" - }, - { - "source": "KinesisSettings", - "target": "KinesisSettings" - }, { "source": "KmsKeyId", "target": "KmsKeyId" }, - { - "source": "MicrosoftSQLServerSettings", - "target": "MicrosoftSqlServerSettings" - }, - { - "source": "MongoDbSettings", - "target": "MongoDbSettings" - }, - { - "source": "MySQLSettings", - "target": "MySqlSettings" - }, - { - "source": "NeptuneSettings", - "target": "NeptuneSettings" - }, - { - "source": "OracleSettings", - "target": "OracleSettings" - }, { "source": "Password", "target": "Password" @@ -10550,26 +10499,10 @@ "source": "Port", "target": "Port" }, - { - "source": "PostgreSQLSettings", - "target": "PostgreSqlSettings" - }, - { - "source": "RedisSettings", - "target": "RedisSettings" - }, - { - "source": "RedshiftSettings", - "target": "RedshiftSettings" - }, { "source": "ResourceIdentifier", "target": "ResourceIdentifier" }, - { - "source": "S3Settings", - "target": "S3Settings" - }, { "source": "ServerName", "target": "ServerName" @@ -10578,14 +10511,6 @@ "source": "SslMode", "target": "SslMode" }, - { - "source": "SybaseSettings", - "target": "SybaseSettings" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Username", "target": "Username" @@ -10628,10 +10553,6 @@ { "source": "SubscriptionName", "target": "SubscriptionName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateEventSubscription", @@ -10681,10 +10602,6 @@ "source": "SubnetGroupIdentifier", "target": "SubnetGroupIdentifier" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VpcSecurityGroups", "target": "VpcSecurityGroups" @@ -10721,22 +10638,6 @@ "source": "MigrationProjectName", "target": "MigrationProjectName" }, - { - "source": "SchemaConversionApplicationAttributes", - "target": "SchemaConversionApplicationAttributes" - }, - { - "source": "SourceDataProviderDescriptors", - "target": "SourceDataProviderDescriptors" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "TargetDataProviderDescriptors", - "target": "TargetDataProviderDescriptors" - }, { "source": "TransformationRules", "target": "TransformationRules" @@ -10761,10 +10662,6 @@ { "cfn_type": "AWS::DMS::ReplicationConfig", "mappings": [ - { - "source": "ComputeConfig", - "target": "ComputeConfig" - }, { "source": "ReplicationConfigIdentifier", "target": "ReplicationConfigIdentifier" @@ -10793,10 +10690,6 @@ "source": "TableMappings", "target": "TableMappings" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TargetEndpointArn", "target": "TargetEndpointArn" @@ -10827,10 +10720,6 @@ { "source": "SubnetIds", "target": "SubnetIds" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateReplicationSubnetGroup", @@ -10849,8 +10738,42 @@ "phase": "delete", "service": "dms" }, + { + "cfn_type": "AWS::DRS::SourceNetwork", + "mappings": [ + { + "source": "originAccountID", + "target": "OriginAccountID" + }, + { + "source": "originRegion", + "target": "OriginRegion" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "vpcID", + "target": "VpcID" + } + ], + "operation": "CreateSourceNetwork", + "phase": "create", + "service": "drs" + }, + { + "cfn_type": "AWS::DRS::SourceNetwork", + "mappings": [], + "operation": "DeleteSourceNetwork", + "phase": "delete", + "service": "drs" + }, { "cfn_type": "AWS::DSQL::Cluster", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "deletionProtectionEnabled", @@ -10860,10 +10783,6 @@ "source": "kmsEncryptionKey", "target": "KmsEncryptionKey" }, - { - "source": "multiRegionProperties", - "target": "MultiRegionProperties" - }, { "source": "tags", "target": "Tags" @@ -10875,6 +10794,9 @@ }, { "cfn_type": "AWS::DSQL::Cluster", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteCluster", "phase": "delete", @@ -10887,22 +10809,10 @@ "source": "Format", "target": "Format" }, - { - "source": "FormatOptions", - "target": "FormatOptions" - }, - { - "source": "Input", - "target": "Input" - }, { "source": "Name", "target": "Name" }, - { - "source": "PathOptions", - "target": "PathOptions" - }, { "source": "Tags", "target": "Tags" @@ -10927,14 +10837,6 @@ { "cfn_type": "AWS::DataBrew::Job", "mappings": [ - { - "source": "DataCatalogOutputs", - "target": "DataCatalogOutputs" - }, - { - "source": "DatabaseOutputs", - "target": "DatabaseOutputs" - }, { "source": "DatasetName", "target": "DatasetName" @@ -10963,10 +10865,6 @@ "source": "Name", "target": "Name" }, - { - "source": "Outputs", - "target": "Outputs" - }, { "source": "ProjectName", "target": "ProjectName" @@ -11019,10 +10917,6 @@ "source": "RoleArn", "target": "RoleArn" }, - { - "source": "Sample", - "target": "Sample" - }, { "source": "Tags", "target": "Tags" @@ -11055,10 +10949,6 @@ "source": "Name", "target": "Name" }, - { - "source": "Steps", - "target": "Steps" - }, { "source": "Tags", "target": "Tags" @@ -11079,10 +10969,6 @@ "source": "Name", "target": "Name" }, - { - "source": "Rules", - "target": "Rules" - }, { "source": "Tags", "target": "Tags" @@ -11144,6 +11030,37 @@ "phase": "delete", "service": "databrew" }, + { + "cfn_type": "AWS::DataExchange::DataSet", + "mappings": [ + { + "source": "AssetType", + "target": "AssetType" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDataSet", + "phase": "create", + "service": "dataexchange" + }, + { + "cfn_type": "AWS::DataExchange::DataSet", + "mappings": [], + "operation": "DeleteDataSet", + "phase": "delete", + "service": "dataexchange" + }, { "cfn_type": "AWS::DataPipeline::Pipeline", "mappings": [ @@ -11186,10 +11103,6 @@ "source": "SubnetArns", "target": "SubnetArns" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VpcEndpointId", "target": "VpcEndpointId" @@ -11213,21 +11126,9 @@ "source": "AgentArns", "target": "AgentArns" }, - { - "source": "CmkSecretConfig", - "target": "CmkSecretConfig" - }, - { - "source": "CustomSecretConfig", - "target": "CustomSecretConfig" - }, { "source": "Subdirectory", "target": "Subdirectory" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateLocationAzureBlob", @@ -11241,10 +11142,6 @@ "source": "AccessPointArn", "target": "AccessPointArn" }, - { - "source": "Ec2Config", - "target": "Ec2Config" - }, { "source": "EfsFilesystemArn", "target": "EfsFilesystemArn" @@ -11260,10 +11157,6 @@ { "source": "Subdirectory", "target": "Subdirectory" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateLocationEfs", @@ -11284,10 +11177,6 @@ { "source": "Subdirectory", "target": "Subdirectory" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateLocationFsxLustre", @@ -11297,10 +11186,6 @@ { "cfn_type": "AWS::DataSync::LocationFSxONTAP", "mappings": [ - { - "source": "Protocol", - "target": "Protocol" - }, { "source": "SecurityGroupArns", "target": "SecurityGroupArns" @@ -11312,10 +11197,6 @@ { "source": "Subdirectory", "target": "Subdirectory" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateLocationFsxOntap", @@ -11329,10 +11210,6 @@ "source": "FsxFilesystemArn", "target": "FsxFilesystemArn" }, - { - "source": "Protocol", - "target": "Protocol" - }, { "source": "SecurityGroupArns", "target": "SecurityGroupArns" @@ -11340,10 +11217,6 @@ { "source": "Subdirectory", "target": "Subdirectory" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateLocationFsxOpenZfs", @@ -11373,10 +11246,6 @@ "source": "Subdirectory", "target": "Subdirectory" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "User", "target": "User" @@ -11401,14 +11270,6 @@ "source": "BlockSize", "target": "BlockSize" }, - { - "source": "KerberosKeytab", - "target": "KerberosKeytab" - }, - { - "source": "KerberosKrb5Conf", - "target": "KerberosKrb5Conf" - }, { "source": "KerberosPrincipal", "target": "KerberosPrincipal" @@ -11417,14 +11278,6 @@ "source": "KmsKeyProviderUri", "target": "KmsKeyProviderUri" }, - { - "source": "NameNodes", - "target": "NameNodes" - }, - { - "source": "QopConfiguration", - "target": "QopConfiguration" - }, { "source": "ReplicationFactor", "target": "ReplicationFactor" @@ -11436,10 +11289,6 @@ { "source": "Subdirectory", "target": "Subdirectory" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateLocationHdfs", @@ -11449,14 +11298,6 @@ { "cfn_type": "AWS::DataSync::LocationNFS", "mappings": [ - { - "source": "MountOptions", - "target": "MountOptions" - }, - { - "source": "OnPremConfig", - "target": "OnPremConfig" - }, { "source": "ServerHostname", "target": "ServerHostname" @@ -11464,10 +11305,6 @@ { "source": "Subdirectory", "target": "Subdirectory" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateLocationNfs", @@ -11489,22 +11326,10 @@ "source": "BucketName", "target": "BucketName" }, - { - "source": "CmkSecretConfig", - "target": "CmkSecretConfig" - }, - { - "source": "CustomSecretConfig", - "target": "CustomSecretConfig" - }, { "source": "SecretKey", "target": "SecretKey" }, - { - "source": "ServerCertificate", - "target": "ServerCertificate" - }, { "source": "ServerHostname", "target": "ServerHostname" @@ -11520,10 +11345,6 @@ { "source": "Subdirectory", "target": "Subdirectory" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateLocationObjectStorage", @@ -11537,10 +11358,6 @@ "source": "S3BucketArn", "target": "S3BucketArn" }, - { - "source": "S3Config", - "target": "S3Config" - }, { "source": "S3StorageClass", "target": "S3StorageClass" @@ -11548,10 +11365,6 @@ { "source": "Subdirectory", "target": "Subdirectory" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateLocationS3", @@ -11577,22 +11390,10 @@ "source": "Domain", "target": "Domain" }, - { - "source": "KerberosKeytab", - "target": "KerberosKeytab" - }, - { - "source": "KerberosKrb5Conf", - "target": "KerberosKrb5Conf" - }, { "source": "KerberosPrincipal", "target": "KerberosPrincipal" }, - { - "source": "MountOptions", - "target": "MountOptions" - }, { "source": "Password", "target": "Password" @@ -11605,10 +11406,6 @@ "source": "Subdirectory", "target": "Subdirectory" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "User", "target": "User" @@ -11629,45 +11426,17 @@ "source": "DestinationLocationArn", "target": "DestinationLocationArn" }, - { - "source": "Excludes", - "target": "Excludes" - }, - { - "source": "Includes", - "target": "Includes" - }, - { - "source": "ManifestConfig", - "target": "ManifestConfig" - }, { "source": "Name", "target": "Name" }, - { - "source": "Options", - "target": "Options" - }, - { - "source": "Schedule", - "target": "Schedule" - }, { "source": "SourceLocationArn", "target": "SourceLocationArn" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TaskMode", "target": "TaskMode" - }, - { - "source": "TaskReportConfig", - "target": "TaskReportConfig" } ], "operation": "CreateTask", @@ -11683,11 +11452,10 @@ }, { "cfn_type": "AWS::DataZone::Connection", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "awsLocation", - "target": "AwsLocation" - }, { "source": "description", "target": "Description" @@ -11696,6 +11464,10 @@ "source": "domainIdentifier", "target": "DomainIdentifier" }, + { + "source": "enableTrustedIdentityPropagation", + "target": "EnableTrustedIdentityPropagation" + }, { "source": "environmentIdentifier", "target": "EnvironmentIdentifier" @@ -11705,8 +11477,8 @@ "target": "Name" }, { - "source": "props", - "target": "Props" + "source": "scope", + "target": "Scope" } ], "operation": "CreateConnection", @@ -11727,15 +11499,10 @@ }, { "cfn_type": "AWS::DataZone::DataSource", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "assetFormsInput", - "target": "AssetFormsInput" - }, - { - "source": "configuration", - "target": "Configuration" - }, { "source": "connectionIdentifier", "target": "ConnectionIdentifier" @@ -11768,14 +11535,6 @@ "source": "publishOnImport", "target": "PublishOnImport" }, - { - "source": "recommendation", - "target": "Recommendation" - }, - { - "source": "schedule", - "target": "Schedule" - }, { "source": "type", "target": "Type" @@ -11787,6 +11546,9 @@ }, { "cfn_type": "AWS::DataZone::DataSource", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "domainIdentifier", @@ -11799,6 +11561,9 @@ }, { "cfn_type": "AWS::DataZone::Domain", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -11824,10 +11589,6 @@ "source": "serviceRole", "target": "ServiceRole" }, - { - "source": "singleSignOn", - "target": "SingleSignOn" - }, { "source": "tags", "target": "Tags" @@ -11839,6 +11600,9 @@ }, { "cfn_type": "AWS::DataZone::Domain", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteDomain", "phase": "delete", @@ -11846,6 +11610,9 @@ }, { "cfn_type": "AWS::DataZone::DomainUnit", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -11926,10 +11693,6 @@ { "source": "projectIdentifier", "target": "ProjectIdentifier" - }, - { - "source": "userParameters", - "target": "UserParameters" } ], "operation": "CreateEnvironment", @@ -11966,10 +11729,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "parameters", - "target": "Parameters" } ], "operation": "CreateEnvironmentAction", @@ -12019,17 +11778,9 @@ "source": "manageAccessRoleArn", "target": "ManageAccessRoleArn" }, - { - "source": "provisioningConfigurations", - "target": "ProvisioningConfigurations" - }, { "source": "provisioningRoleArn", "target": "ProvisioningRoleArn" - }, - { - "source": "regionalParameters", - "target": "RegionalParameters" } ], "operation": "PutEnvironmentBlueprintConfiguration", @@ -12082,10 +11833,6 @@ { "source": "projectIdentifier", "target": "ProjectIdentifier" - }, - { - "source": "userParameters", - "target": "UserParameters" } ], "operation": "CreateEnvironmentProfile", @@ -12115,10 +11862,6 @@ "source": "domainIdentifier", "target": "DomainIdentifier" }, - { - "source": "model", - "target": "Model" - }, { "source": "name", "target": "Name" @@ -12150,6 +11893,9 @@ }, { "cfn_type": "AWS::DataZone::GroupProfile", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "domainIdentifier", @@ -12158,6 +11904,10 @@ { "source": "groupIdentifier", "target": "GroupIdentifier" + }, + { + "source": "rolePrincipalArn", + "target": "RolePrincipalArn" } ], "operation": "CreateGroupProfile", @@ -12166,6 +11916,9 @@ }, { "cfn_type": "AWS::DataZone::Owner", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "domainIdentifier", @@ -12178,10 +11931,6 @@ { "source": "entityType", "target": "EntityType" - }, - { - "source": "owner", - "target": "Owner" } ], "operation": "AddEntityOwner", @@ -12190,6 +11939,9 @@ }, { "cfn_type": "AWS::DataZone::Owner", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "domainIdentifier", @@ -12202,10 +11954,6 @@ { "source": "entityType", "target": "EntityType" - }, - { - "source": "owner", - "target": "Owner" } ], "operation": "RemoveEntityOwner", @@ -12214,11 +11962,10 @@ }, { "cfn_type": "AWS::DataZone::PolicyGrant", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "detail", - "target": "Detail" - }, { "source": "domainIdentifier", "target": "DomainIdentifier" @@ -12234,10 +11981,6 @@ { "source": "policyType", "target": "PolicyType" - }, - { - "source": "principal", - "target": "Principal" } ], "operation": "AddPolicyGrant", @@ -12246,6 +11989,9 @@ }, { "cfn_type": "AWS::DataZone::PolicyGrant", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "domainIdentifier", @@ -12262,10 +12008,6 @@ { "source": "policyType", "target": "PolicyType" - }, - { - "source": "principal", - "target": "Principal" } ], "operation": "RemovePolicyGrant", @@ -12296,12 +12038,16 @@ "target": "Name" }, { - "source": "projectProfileId", - "target": "ProjectProfileId" + "source": "projectCategory", + "target": "ProjectCategory" }, { - "source": "userParameters", - "target": "UserParameters" + "source": "projectExecutionRole", + "target": "ProjectExecutionRole" + }, + { + "source": "projectProfileId", + "target": "ProjectProfileId" } ], "operation": "CreateProject", @@ -12331,10 +12077,6 @@ "source": "domainIdentifier", "target": "DomainIdentifier" }, - { - "source": "member", - "target": "Member" - }, { "source": "projectIdentifier", "target": "ProjectIdentifier" @@ -12351,10 +12093,6 @@ "source": "domainIdentifier", "target": "DomainIdentifier" }, - { - "source": "member", - "target": "Member" - }, { "source": "projectIdentifier", "target": "ProjectIdentifier" @@ -12367,6 +12105,10 @@ { "cfn_type": "AWS::DataZone::ProjectProfile", "mappings": [ + { + "source": "allowCustomProjectResourceTags", + "target": "AllowCustomProjectResourceTags" + }, { "source": "description", "target": "Description" @@ -12379,14 +12121,14 @@ "source": "domainUnitIdentifier", "target": "DomainUnitIdentifier" }, - { - "source": "environmentConfigurations", - "target": "EnvironmentConfigurations" - }, { "source": "name", "target": "Name" }, + { + "source": "projectResourceTagsDescription", + "target": "ProjectResourceTagsDescription" + }, { "source": "status", "target": "Status" @@ -12410,6 +12152,9 @@ }, { "cfn_type": "AWS::DataZone::SubscriptionTarget", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "applicableAssetTypes", @@ -12439,10 +12184,6 @@ "source": "provider", "target": "Provider" }, - { - "source": "subscriptionTargetConfig", - "target": "SubscriptionTargetConfig" - }, { "source": "type", "target": "Type" @@ -12470,11 +12211,18 @@ }, { "cfn_type": "AWS::DataZone::UserProfile", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "domainIdentifier", "target": "DomainIdentifier" }, + { + "source": "sessionName", + "target": "SessionName" + }, { "source": "userIdentifier", "target": "UserIdentifier" @@ -12490,7 +12238,14 @@ }, { "cfn_type": "AWS::Deadline::Farm", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ + { + "source": "costScaleFactor", + "target": "CostScaleFactor" + }, { "source": "description", "target": "Description" @@ -12521,11 +12276,10 @@ }, { "cfn_type": "AWS::Deadline::Fleet", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "configuration", - "target": "Configuration" - }, { "source": "description", "target": "Description" @@ -12538,10 +12292,6 @@ "source": "farmId", "target": "FarmId" }, - { - "source": "hostConfiguration", - "target": "HostConfiguration" - }, { "source": "maxWorkerCount", "target": "MaxWorkerCount" @@ -12565,6 +12315,9 @@ }, { "cfn_type": "AWS::Deadline::Fleet", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "farmId", @@ -12577,6 +12330,9 @@ }, { "cfn_type": "AWS::Deadline::LicenseEndpoint", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "securityGroupIds", @@ -12608,6 +12364,9 @@ }, { "cfn_type": "AWS::Deadline::Limit", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "amountRequirementName", @@ -12680,6 +12439,9 @@ }, { "cfn_type": "AWS::Deadline::Monitor", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "displayName", @@ -12689,6 +12451,10 @@ "source": "identityCenterInstanceArn", "target": "IdentityCenterInstanceArn" }, + { + "source": "identityCenterRegion", + "target": "IdentityCenterRegion" + }, { "source": "roleArn", "target": "RoleArn" @@ -12715,6 +12481,9 @@ }, { "cfn_type": "AWS::Deadline::Queue", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "allowedStorageProfileIds", @@ -12736,14 +12505,6 @@ "source": "farmId", "target": "FarmId" }, - { - "source": "jobAttachmentSettings", - "target": "JobAttachmentSettings" - }, - { - "source": "jobRunAsUser", - "target": "JobRunAsUser" - }, { "source": "requiredFileSystemLocationNames", "target": "RequiredFileSystemLocationNames" @@ -12775,6 +12536,9 @@ }, { "cfn_type": "AWS::Deadline::QueueEnvironment", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "farmId", @@ -12899,6 +12663,9 @@ }, { "cfn_type": "AWS::Deadline::StorageProfile", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "displayName", @@ -12908,10 +12675,6 @@ "source": "farmId", "target": "FarmId" }, - { - "source": "fileSystemLocations", - "target": "FileSystemLocations" - }, { "source": "osFamily", "target": "OsFamily" @@ -12973,16 +12736,187 @@ "service": "detective" }, { - "cfn_type": "AWS::DevOpsGuru::NotificationChannel", + "cfn_type": "AWS::DevOpsAgent::AgentSpace", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "Config", - "target": "Config" + "source": "description", + "target": "Description" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "locale", + "target": "Locale" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" } ], - "operation": "AddNotificationChannel", + "operation": "CreateAgentSpace", "phase": "create", - "service": "devops-guru" + "service": "devops-agent" + }, + { + "cfn_type": "AWS::DevOpsAgent::AgentSpace", + "mappings": [], + "operation": "DeleteAgentSpace", + "phase": "delete", + "service": "devops-agent" + }, + { + "cfn_type": "AWS::DevOpsAgent::Asset", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "agentSpaceId", + "target": "AgentSpaceId" + }, + { + "source": "assetType", + "target": "AssetType" + } + ], + "operation": "CreateAsset", + "phase": "create", + "service": "devops-agent" + }, + { + "cfn_type": "AWS::DevOpsAgent::Asset", + "mappings": [ + { + "source": "agentSpaceId", + "target": "AgentSpaceId" + } + ], + "operation": "DeleteAsset", + "phase": "delete", + "service": "devops-agent" + }, + { + "cfn_type": "AWS::DevOpsAgent::Association", + "mappings": [ + { + "source": "agentSpaceId", + "target": "AgentSpaceId" + }, + { + "source": "serviceId", + "target": "ServiceId" + } + ], + "operation": "AssociateService", + "phase": "create", + "service": "devops-agent" + }, + { + "cfn_type": "AWS::DevOpsAgent::PrivateConnection", + "mappings": [ + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreatePrivateConnection", + "phase": "create", + "service": "devops-agent" + }, + { + "cfn_type": "AWS::DevOpsAgent::PrivateConnection", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeletePrivateConnection", + "phase": "delete", + "service": "devops-agent" + }, + { + "cfn_type": "AWS::DevOpsAgent::Service", + "mappings": [ + { + "source": "exchangeUrlPrivateConnectionName", + "target": "ExchangeUrlPrivateConnectionName" + }, + { + "source": "kmsKeyArn", + "target": "KmsKeyArn" + }, + { + "source": "privateConnectionName", + "target": "PrivateConnectionName" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetUrlPrivateConnectionName", + "target": "TargetUrlPrivateConnectionName" + } + ], + "operation": "RegisterService", + "phase": "create", + "service": "devops-agent" + }, + { + "cfn_type": "AWS::DevOpsAgent::Service", + "mappings": [], + "operation": "DeregisterService", + "phase": "delete", + "service": "devops-agent" + }, + { + "cfn_type": "AWS::DevOpsAgent::Trigger", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "agentSpaceId", + "target": "AgentSpaceId" + }, + { + "source": "status", + "target": "Status" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateTrigger", + "phase": "create", + "service": "devops-agent" + }, + { + "cfn_type": "AWS::DevOpsAgent::Trigger", + "mappings": [ + { + "source": "agentSpaceId", + "target": "AgentSpaceId" + } + ], + "operation": "DeleteTrigger", + "phase": "delete", + "service": "devops-agent" }, { "cfn_type": "AWS::DevOpsGuru::NotificationChannel", @@ -13009,10 +12943,6 @@ { "source": "projectArn", "target": "ProjectArn" - }, - { - "source": "rules", - "target": "Rules" } ], "operation": "CreateDevicePool", @@ -13128,12 +13058,12 @@ "target": "DefaultJobTimeoutMinutes" }, { - "source": "name", - "target": "Name" + "source": "executionRoleArn", + "target": "ExecutionRoleArn" }, { - "source": "vpcConfig", - "target": "VpcConfig" + "source": "name", + "target": "Name" } ], "operation": "CreateProject", @@ -13157,10 +13087,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "vpcConfig", - "target": "VpcConfig" } ], "operation": "CreateTestGridProject", @@ -13174,6 +13100,37 @@ "phase": "delete", "service": "devicefarm" }, + { + "cfn_type": "AWS::DeviceFarm::Upload", + "mappings": [ + { + "source": "contentType", + "target": "ContentType" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "projectArn", + "target": "ProjectArn" + }, + { + "source": "type", + "target": "Type" + } + ], + "operation": "CreateUpload", + "phase": "create", + "service": "devicefarm" + }, + { + "cfn_type": "AWS::DeviceFarm::Upload", + "mappings": [], + "operation": "DeleteUpload", + "phase": "delete", + "service": "devicefarm" + }, { "cfn_type": "AWS::DeviceFarm::VPCEConfiguration", "mappings": [ @@ -13231,10 +13188,6 @@ { "source": "requestMACSec", "target": "RequestMACSec" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateConnection", @@ -13251,17 +13204,9 @@ { "cfn_type": "AWS::DirectConnect::DirectConnectGateway", "mappings": [ - { - "source": "amazonSideAsn", - "target": "AmazonSideAsn" - }, { "source": "directConnectGatewayName", "target": "DirectConnectGatewayName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateDirectConnectGateway", @@ -13321,10 +13266,6 @@ { "source": "requestMACSec", "target": "RequestMACSec" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateLag", @@ -13396,43 +13337,12 @@ { "source": "Size", "target": "Size" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "VpcSettings", - "target": "VpcSettings" } ], "operation": "CreateDirectory", "phase": "create", "service": "ds" }, - { - "cfn_type": "AWS::DocDB::DBClusterParameterGroup", - "mappings": [ - { - "source": "Description", - "target": "Description" - }, - { - "source": "Tags", - "target": "Tags" - } - ], - "operation": "CreateDBClusterParameterGroup", - "phase": "create", - "service": "docdb" - }, - { - "cfn_type": "AWS::DocDB::DBClusterParameterGroup", - "mappings": [], - "operation": "DeleteDBClusterParameterGroup", - "phase": "delete", - "service": "docdb" - }, { "cfn_type": "AWS::DocDB::DBSubnetGroup", "mappings": [ @@ -13447,10 +13357,6 @@ { "source": "SubnetIds", "target": "SubnetIds" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDBSubnetGroup", @@ -13559,6 +13465,9 @@ }, { "cfn_type": "AWS::DocDBElastic::Cluster", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "adminUserName", @@ -13629,12 +13538,31 @@ "service": "docdb-elastic" }, { - "cfn_type": "AWS::DynamoDB::Table", + "cfn_type": "AWS::DynamoDB::Backup", "mappings": [ { - "source": "AttributeDefinitions", - "target": "AttributeDefinitions" + "source": "BackupName", + "target": "BackupName" }, + { + "source": "TableName", + "target": "TableName" + } + ], + "operation": "CreateBackup", + "phase": "create", + "service": "dynamodb" + }, + { + "cfn_type": "AWS::DynamoDB::Backup", + "mappings": [], + "operation": "DeleteBackup", + "phase": "delete", + "service": "dynamodb" + }, + { + "cfn_type": "AWS::DynamoDB::Table", + "mappings": [ { "source": "BillingMode", "target": "BillingMode" @@ -13643,38 +13571,6 @@ "source": "DeletionProtectionEnabled", "target": "DeletionProtectionEnabled" }, - { - "source": "GlobalSecondaryIndexes", - "target": "GlobalSecondaryIndexes" - }, - { - "source": "KeySchema", - "target": "KeySchema" - }, - { - "source": "LocalSecondaryIndexes", - "target": "LocalSecondaryIndexes" - }, - { - "source": "OnDemandThroughput", - "target": "OnDemandThroughput" - }, - { - "source": "ProvisionedThroughput", - "target": "ProvisionedThroughput" - }, - { - "source": "ResourcePolicy", - "target": "ResourcePolicy" - }, - { - "source": "SSESpecification", - "target": "SSESpecification" - }, - { - "source": "StreamSpecification", - "target": "StreamSpecification" - }, { "source": "TableClass", "target": "TableClass" @@ -13682,14 +13578,6 @@ { "source": "TableName", "target": "TableName" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "WarmThroughput", - "target": "WarmThroughput" } ], "operation": "CreateTable", @@ -13708,8 +13596,50 @@ "phase": "delete", "service": "dynamodb" }, + { + "cfn_type": "AWS::EC2::CapacityManagerDataExport", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], + "mappings": [ + { + "source": "OutputFormat", + "target": "OutputFormat" + }, + { + "source": "S3BucketName", + "target": "S3BucketName" + }, + { + "source": "S3BucketPrefix", + "target": "S3BucketPrefix" + }, + { + "source": "Schedule", + "target": "Schedule" + } + ], + "operation": "CreateCapacityManagerDataExport", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::CapacityManagerDataExport", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [], + "operation": "DeleteCapacityManagerDataExport", + "phase": "delete", + "service": "ec2" + }, { "cfn_type": "AWS::EC2::CapacityReservation", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "AvailabilityZone", @@ -13723,10 +13653,6 @@ "source": "EbsOptimized", "target": "EbsOptimized" }, - { - "source": "EndDate", - "target": "EndDate" - }, { "source": "EndDateType", "target": "EndDateType" @@ -13759,10 +13685,6 @@ "source": "PlacementGroupArn", "target": "PlacementGroupArn" }, - { - "source": "TagSpecifications", - "target": "TagSpecifications" - }, { "source": "Tenancy", "target": "Tenancy" @@ -13774,6 +13696,9 @@ }, { "cfn_type": "AWS::EC2::CapacityReservation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "CancelCapacityReservation", "phase": "delete", @@ -13781,27 +13706,19 @@ }, { "cfn_type": "AWS::EC2::CapacityReservationFleet", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "AllocationStrategy", "target": "AllocationStrategy" }, - { - "source": "EndDate", - "target": "EndDate" - }, { "source": "InstanceMatchCriteria", "target": "InstanceMatchCriteria" }, - { - "source": "InstanceTypeSpecifications", - "target": "InstanceTypeSpecifications" - }, - { - "source": "TagSpecifications", - "target": "TagSpecifications" - }, { "source": "Tenancy", "target": "Tenancy" @@ -13817,6 +13734,9 @@ }, { "cfn_type": "AWS::EC2::CapacityReservationFleet", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "CancelCapacityReservationFleets", "phase": "delete", @@ -13824,6 +13744,10 @@ }, { "cfn_type": "AWS::EC2::CarrierGateway", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "VpcId", @@ -13836,6 +13760,9 @@ }, { "cfn_type": "AWS::EC2::CarrierGateway", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteCarrierGateway", "phase": "delete", @@ -13843,6 +13770,9 @@ }, { "cfn_type": "AWS::EC2::CustomerGateway", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "BgpAsn", @@ -13875,6 +13805,9 @@ }, { "cfn_type": "AWS::EC2::CustomerGateway", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteCustomerGateway", "phase": "delete", @@ -13882,6 +13815,9 @@ }, { "cfn_type": "AWS::EC2::DHCPOptions", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteDhcpOptions", "phase": "delete", @@ -13889,6 +13825,10 @@ }, { "cfn_type": "AWS::EC2::EC2Fleet", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "Context", @@ -13898,30 +13838,10 @@ "source": "ExcessCapacityTerminationPolicy", "target": "ExcessCapacityTerminationPolicy" }, - { - "source": "LaunchTemplateConfigs", - "target": "LaunchTemplateConfigs" - }, - { - "source": "OnDemandOptions", - "target": "OnDemandOptions" - }, { "source": "ReplaceUnhealthyInstances", "target": "ReplaceUnhealthyInstances" }, - { - "source": "SpotOptions", - "target": "SpotOptions" - }, - { - "source": "TagSpecifications", - "target": "TagSpecifications" - }, - { - "source": "TargetCapacitySpecification", - "target": "TargetCapacitySpecification" - }, { "source": "TerminateInstancesWithExpiration", "target": "TerminateInstancesWithExpiration" @@ -13929,50 +13849,17 @@ { "source": "Type", "target": "Type" - }, - { - "source": "ValidFrom", - "target": "ValidFrom" - }, - { - "source": "ValidUntil", - "target": "ValidUntil" } ], "operation": "CreateFleet", "phase": "create", "service": "ec2" }, - { - "cfn_type": "AWS::EC2::EIP", - "mappings": [ - { - "source": "Address", - "target": "Address" - }, - { - "source": "Domain", - "target": "Domain" - }, - { - "source": "IpamPoolId", - "target": "IpamPoolId" - }, - { - "source": "NetworkBorderGroup", - "target": "NetworkBorderGroup" - }, - { - "source": "PublicIpv4Pool", - "target": "PublicIpv4Pool" - } - ], - "operation": "AllocateAddress", - "phase": "create", - "service": "ec2" - }, { "cfn_type": "AWS::EC2::EIPAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "AllocationId", @@ -13997,6 +13884,10 @@ }, { "cfn_type": "AWS::EC2::EgressOnlyInternetGateway", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "VpcId", @@ -14009,6 +13900,9 @@ }, { "cfn_type": "AWS::EC2::EgressOnlyInternetGateway", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteEgressOnlyInternetGateway", "phase": "delete", @@ -14016,6 +13910,9 @@ }, { "cfn_type": "AWS::EC2::EnclaveCertificateIamRoleAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "CertificateArn", @@ -14032,6 +13929,9 @@ }, { "cfn_type": "AWS::EC2::EnclaveCertificateIamRoleAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "CertificateArn", @@ -14048,6 +13948,10 @@ }, { "cfn_type": "AWS::EC2::FlowLog", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "DeliverCrossAccountRole", @@ -14057,10 +13961,6 @@ "source": "DeliverLogsPermissionArn", "target": "DeliverLogsPermissionArn" }, - { - "source": "DestinationOptions", - "target": "DestinationOptions" - }, { "source": "LogDestination", "target": "LogDestination" @@ -14096,6 +13996,9 @@ }, { "cfn_type": "AWS::EC2::FlowLog", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteFlowLogs", "phase": "delete", @@ -14103,6 +14006,9 @@ }, { "cfn_type": "AWS::EC2::Host", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "AutoPlacement", @@ -14146,6 +14052,10 @@ }, { "cfn_type": "AWS::EC2::IPAM", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "Description", @@ -14159,10 +14069,6 @@ "source": "MeteredAccount", "target": "MeteredAccount" }, - { - "source": "OperatingRegions", - "target": "OperatingRegions" - }, { "source": "Tier", "target": "Tier" @@ -14174,6 +14080,9 @@ }, { "cfn_type": "AWS::EC2::IPAM", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteIpam", "phase": "delete", @@ -14181,6 +14090,10 @@ }, { "cfn_type": "AWS::EC2::IPAMAllocation", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "Cidr", @@ -14205,6 +14118,9 @@ }, { "cfn_type": "AWS::EC2::IPAMAllocation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "Cidr", @@ -14221,6 +14137,10 @@ }, { "cfn_type": "AWS::EC2::IPAMPool", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "AddressFamily", @@ -14238,10 +14158,6 @@ "source": "AllocationMinNetmaskLength", "target": "AllocationMinNetmaskLength" }, - { - "source": "AllocationResourceTags", - "target": "AllocationResourceTags" - }, { "source": "AutoImport", "target": "AutoImport" @@ -14273,10 +14189,6 @@ { "source": "SourceIpamPoolId", "target": "SourceIpamPoolId" - }, - { - "source": "SourceResource", - "target": "SourceResource" } ], "operation": "CreateIpamPool", @@ -14285,6 +14197,9 @@ }, { "cfn_type": "AWS::EC2::IPAMPool", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteIpamPool", "phase": "delete", @@ -14292,6 +14207,10 @@ }, { "cfn_type": "AWS::EC2::IPAMPoolCidr", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "Cidr", @@ -14312,6 +14231,9 @@ }, { "cfn_type": "AWS::EC2::IPAMPoolCidr", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "Cidr", @@ -14327,46 +14249,113 @@ "service": "ec2" }, { - "cfn_type": "AWS::EC2::IPAMResourceDiscovery", + "cfn_type": "AWS::EC2::IPAMPrefixListResolver", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ + { + "source": "AddressFamily", + "target": "AddressFamily" + }, { "source": "Description", "target": "Description" }, { - "source": "OperatingRegions", - "target": "OperatingRegions" + "source": "IpamId", + "target": "IpamId" } ], - "operation": "CreateIpamResourceDiscovery", + "operation": "CreateIpamPrefixListResolver", "phase": "create", "service": "ec2" }, { - "cfn_type": "AWS::EC2::IPAMResourceDiscovery", + "cfn_type": "AWS::EC2::IPAMPrefixListResolver", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], - "operation": "DeleteIpamResourceDiscovery", + "operation": "DeleteIpamPrefixListResolver", "phase": "delete", "service": "ec2" }, { - "cfn_type": "AWS::EC2::IPAMResourceDiscoveryAssociation", + "cfn_type": "AWS::EC2::IPAMPrefixListResolverTarget", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { - "source": "IpamId", - "target": "IpamId" + "source": "DesiredVersion", + "target": "DesiredVersion" }, { - "source": "IpamResourceDiscoveryId", - "target": "IpamResourceDiscoveryId" + "source": "IpamPrefixListResolverId", + "target": "IpamPrefixListResolverId" + }, + { + "source": "PrefixListId", + "target": "PrefixListId" + }, + { + "source": "PrefixListRegion", + "target": "PrefixListRegion" + }, + { + "source": "TrackLatestVersion", + "target": "TrackLatestVersion" + } + ], + "operation": "CreateIpamPrefixListResolverTarget", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMPrefixListResolverTarget", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [], + "operation": "DeleteIpamPrefixListResolverTarget", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IPAMResourceDiscovery", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], + "mappings": [ + { + "source": "Description", + "target": "Description" } ], - "operation": "AssociateIpamResourceDiscovery", + "operation": "CreateIpamResourceDiscovery", "phase": "create", "service": "ec2" }, + { + "cfn_type": "AWS::EC2::IPAMResourceDiscovery", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [], + "operation": "DeleteIpamResourceDiscovery", + "phase": "delete", + "service": "ec2" + }, { "cfn_type": "AWS::EC2::IPAMScope", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "Description", @@ -14383,6 +14372,9 @@ }, { "cfn_type": "AWS::EC2::IPAMScope", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteIpamScope", "phase": "delete", @@ -14390,23 +14382,15 @@ }, { "cfn_type": "AWS::EC2::Instance", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "AdditionalInfo", "target": "AdditionalInfo" }, - { - "source": "BlockDeviceMappings", - "target": "BlockDeviceMappings" - }, - { - "source": "CpuOptions", - "target": "CpuOptions" - }, - { - "source": "CreditSpecification", - "target": "CreditSpecification" - }, { "source": "DisableApiTermination", "target": "DisableApiTermination" @@ -14415,22 +14399,6 @@ "source": "EbsOptimized", "target": "EbsOptimized" }, - { - "source": "ElasticInferenceAccelerators", - "target": "ElasticInferenceAccelerators" - }, - { - "source": "EnclaveOptions", - "target": "EnclaveOptions" - }, - { - "source": "HibernationOptions", - "target": "HibernationOptions" - }, - { - "source": "IamInstanceProfile", - "target": "IamInstanceProfile" - }, { "source": "ImageId", "target": "ImageId" @@ -14447,10 +14415,6 @@ "source": "Ipv6AddressCount", "target": "Ipv6AddressCount" }, - { - "source": "Ipv6Addresses", - "target": "Ipv6Addresses" - }, { "source": "KernelId", "target": "KernelId" @@ -14459,30 +14423,6 @@ "source": "KeyName", "target": "KeyName" }, - { - "source": "LaunchTemplate", - "target": "LaunchTemplate" - }, - { - "source": "LicenseSpecifications", - "target": "LicenseSpecifications" - }, - { - "source": "MetadataOptions", - "target": "MetadataOptions" - }, - { - "source": "Monitoring", - "target": "Monitoring" - }, - { - "source": "NetworkInterfaces", - "target": "NetworkInterfaces" - }, - { - "source": "PrivateDnsNameOptions", - "target": "PrivateDnsNameOptions" - }, { "source": "PrivateIpAddress", "target": "PrivateIpAddress" @@ -14514,6 +14454,9 @@ }, { "cfn_type": "AWS::EC2::Instance", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "TerminateInstances", "phase": "delete", @@ -14521,6 +14464,10 @@ }, { "cfn_type": "AWS::EC2::InstanceConnectEndpoint", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "ClientToken", @@ -14545,6 +14492,9 @@ }, { "cfn_type": "AWS::EC2::InstanceConnectEndpoint", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteInstanceConnectEndpoint", "phase": "delete", @@ -14552,6 +14502,9 @@ }, { "cfn_type": "AWS::EC2::InternetGateway", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteInternetGateway", "phase": "delete", @@ -14559,6 +14512,9 @@ }, { "cfn_type": "AWS::EC2::KeyPair", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "KeyFormat", @@ -14579,6 +14535,9 @@ }, { "cfn_type": "AWS::EC2::KeyPair", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "KeyName", @@ -14591,19 +14550,15 @@ }, { "cfn_type": "AWS::EC2::LaunchTemplate", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ - { - "source": "LaunchTemplateData", - "target": "LaunchTemplateData" - }, { "source": "LaunchTemplateName", "target": "LaunchTemplateName" }, - { - "source": "TagSpecifications", - "target": "TagSpecifications" - }, { "source": "VersionDescription", "target": "VersionDescription" @@ -14615,6 +14570,9 @@ }, { "cfn_type": "AWS::EC2::LaunchTemplate", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "LaunchTemplateName", @@ -14627,6 +14585,9 @@ }, { "cfn_type": "AWS::EC2::LocalGatewayRoute", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "DestinationCidrBlock", @@ -14651,6 +14612,9 @@ }, { "cfn_type": "AWS::EC2::LocalGatewayRoute", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "DestinationCidrBlock", @@ -14667,6 +14631,9 @@ }, { "cfn_type": "AWS::EC2::LocalGatewayRouteTable", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "LocalGatewayId", @@ -14683,6 +14650,9 @@ }, { "cfn_type": "AWS::EC2::LocalGatewayRouteTable", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteLocalGatewayRouteTable", "phase": "delete", @@ -14690,6 +14660,9 @@ }, { "cfn_type": "AWS::EC2::LocalGatewayRouteTableVPCAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "LocalGatewayRouteTableId", @@ -14706,6 +14679,9 @@ }, { "cfn_type": "AWS::EC2::LocalGatewayRouteTableVPCAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteLocalGatewayRouteTableVpcAssociation", "phase": "delete", @@ -14713,6 +14689,9 @@ }, { "cfn_type": "AWS::EC2::LocalGatewayRouteTableVirtualInterfaceGroupAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "LocalGatewayRouteTableId", @@ -14729,6 +14708,9 @@ }, { "cfn_type": "AWS::EC2::LocalGatewayRouteTableVirtualInterfaceGroupAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteLocalGatewayRouteTableVirtualInterfaceGroupAssociation", "phase": "delete", @@ -14736,6 +14718,9 @@ }, { "cfn_type": "AWS::EC2::LocalGatewayVirtualInterface", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "LocalAddress", @@ -14772,6 +14757,9 @@ }, { "cfn_type": "AWS::EC2::LocalGatewayVirtualInterface", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteLocalGatewayVirtualInterface", "phase": "delete", @@ -14779,6 +14767,9 @@ }, { "cfn_type": "AWS::EC2::LocalGatewayVirtualInterfaceGroup", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "LocalBgpAsn", @@ -14799,6 +14790,9 @@ }, { "cfn_type": "AWS::EC2::LocalGatewayVirtualInterfaceGroup", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteLocalGatewayVirtualInterfaceGroup", "phase": "delete", @@ -14806,11 +14800,19 @@ }, { "cfn_type": "AWS::EC2::NatGateway", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "AllocationId", "target": "AllocationId" }, + { + "source": "AvailabilityMode", + "target": "AvailabilityMode" + }, { "source": "ConnectivityType", "target": "ConnectivityType" @@ -14834,6 +14836,10 @@ { "source": "SubnetId", "target": "SubnetId" + }, + { + "source": "VpcId", + "target": "VpcId" } ], "operation": "CreateNatGateway", @@ -14842,6 +14848,9 @@ }, { "cfn_type": "AWS::EC2::NatGateway", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteNatGateway", "phase": "delete", @@ -14849,6 +14858,10 @@ }, { "cfn_type": "AWS::EC2::NetworkAcl", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "VpcId", @@ -14861,6 +14874,9 @@ }, { "cfn_type": "AWS::EC2::NetworkAcl", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteNetworkAcl", "phase": "delete", @@ -14868,6 +14884,9 @@ }, { "cfn_type": "AWS::EC2::NetworkAclEntry", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "CidrBlock", @@ -14885,14 +14904,6 @@ "source": "NetworkAclId", "target": "NetworkAclId" }, - { - "source": "PortRange", - "target": "PortRange" - }, - { - "source": "Protocol", - "target": "Protocol" - }, { "source": "RuleAction", "target": "RuleAction" @@ -14908,6 +14919,9 @@ }, { "cfn_type": "AWS::EC2::NetworkAclEntry", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "Egress", @@ -14928,22 +14942,9 @@ }, { "cfn_type": "AWS::EC2::NetworkInsightsAccessScope", - "mappings": [ - { - "source": "ExcludePaths", - "target": "ExcludePaths" - }, - { - "source": "MatchPaths", - "target": "MatchPaths" - } + "ignored_inputs": [ + "DryRun" ], - "operation": "CreateNetworkInsightsAccessScope", - "phase": "create", - "service": "ec2" - }, - { - "cfn_type": "AWS::EC2::NetworkInsightsAccessScope", "mappings": [], "operation": "DeleteNetworkInsightsAccessScope", "phase": "delete", @@ -14951,6 +14952,10 @@ }, { "cfn_type": "AWS::EC2::NetworkInsightsAccessScopeAnalysis", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "NetworkInsightsAccessScopeId", @@ -14963,6 +14968,9 @@ }, { "cfn_type": "AWS::EC2::NetworkInsightsAccessScopeAnalysis", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteNetworkInsightsAccessScopeAnalysis", "phase": "delete", @@ -14970,6 +14978,10 @@ }, { "cfn_type": "AWS::EC2::NetworkInsightsAnalysis", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "AdditionalAccounts", @@ -14994,6 +15006,9 @@ }, { "cfn_type": "AWS::EC2::NetworkInsightsAnalysis", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteNetworkInsightsAnalysis", "phase": "delete", @@ -15001,6 +15016,10 @@ }, { "cfn_type": "AWS::EC2::NetworkInsightsPath", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "Destination", @@ -15014,14 +15033,6 @@ "source": "DestinationPort", "target": "DestinationPort" }, - { - "source": "FilterAtDestination", - "target": "FilterAtDestination" - }, - { - "source": "FilterAtSource", - "target": "FilterAtSource" - }, { "source": "Protocol", "target": "Protocol" @@ -15041,6 +15052,9 @@ }, { "cfn_type": "AWS::EC2::NetworkInsightsPath", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteNetworkInsightsPath", "phase": "delete", @@ -15048,11 +15062,11 @@ }, { "cfn_type": "AWS::EC2::NetworkInterface", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ - { - "source": "ConnectionTrackingSpecification", - "target": "ConnectionTrackingSpecification" - }, { "source": "Description", "target": "Description" @@ -15069,34 +15083,18 @@ "source": "Ipv4PrefixCount", "target": "Ipv4PrefixCount" }, - { - "source": "Ipv4Prefixes", - "target": "Ipv4Prefixes" - }, { "source": "Ipv6AddressCount", "target": "Ipv6AddressCount" }, - { - "source": "Ipv6Addresses", - "target": "Ipv6Addresses" - }, { "source": "Ipv6PrefixCount", "target": "Ipv6PrefixCount" }, - { - "source": "Ipv6Prefixes", - "target": "Ipv6Prefixes" - }, { "source": "PrivateIpAddress", "target": "PrivateIpAddress" }, - { - "source": "PrivateIpAddresses", - "target": "PrivateIpAddresses" - }, { "source": "SecondaryPrivateIpAddressCount", "target": "SecondaryPrivateIpAddressCount" @@ -15112,6 +15110,9 @@ }, { "cfn_type": "AWS::EC2::NetworkInterface", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteNetworkInterface", "phase": "delete", @@ -15119,19 +15120,14 @@ }, { "cfn_type": "AWS::EC2::NetworkInterfaceAttachment", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ - { - "source": "DeviceIndex", - "target": "DeviceIndex" - }, { "source": "EnaQueueCount", "target": "EnaQueueCount" }, - { - "source": "EnaSrdSpecification", - "target": "EnaSrdSpecification" - }, { "source": "InstanceId", "target": "InstanceId" @@ -15147,6 +15143,9 @@ }, { "cfn_type": "AWS::EC2::NetworkPerformanceMetricSubscription", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "Destination", @@ -15171,7 +15170,14 @@ }, { "cfn_type": "AWS::EC2::PlacementGroup", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ + { + "source": "ParentGroupId", + "target": "ParentGroupId" + }, { "source": "PartitionCount", "target": "PartitionCount" @@ -15191,6 +15197,9 @@ }, { "cfn_type": "AWS::EC2::PlacementGroup", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeletePlacementGroup", "phase": "delete", @@ -15198,15 +15207,15 @@ }, { "cfn_type": "AWS::EC2::PrefixList", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "AddressFamily", "target": "AddressFamily" }, - { - "source": "Entries", - "target": "Entries" - }, { "source": "MaxEntries", "target": "MaxEntries" @@ -15222,6 +15231,9 @@ }, { "cfn_type": "AWS::EC2::PrefixList", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteManagedPrefixList", "phase": "delete", @@ -15229,6 +15241,9 @@ }, { "cfn_type": "AWS::EC2::Route", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "CarrierGatewayId", @@ -15301,6 +15316,9 @@ }, { "cfn_type": "AWS::EC2::Route", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "DestinationCidrBlock", @@ -15325,6 +15343,10 @@ }, { "cfn_type": "AWS::EC2::RouteServer", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "AmazonSideAsn", @@ -15349,6 +15371,9 @@ }, { "cfn_type": "AWS::EC2::RouteServer", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteRouteServer", "phase": "delete", @@ -15356,6 +15381,9 @@ }, { "cfn_type": "AWS::EC2::RouteServerAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "RouteServerId", @@ -15372,6 +15400,9 @@ }, { "cfn_type": "AWS::EC2::RouteServerAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "RouteServerId", @@ -15388,6 +15419,10 @@ }, { "cfn_type": "AWS::EC2::RouteServerEndpoint", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "RouteServerId", @@ -15404,6 +15439,9 @@ }, { "cfn_type": "AWS::EC2::RouteServerEndpoint", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteRouteServerEndpoint", "phase": "delete", @@ -15411,11 +15449,10 @@ }, { "cfn_type": "AWS::EC2::RouteServerPeer", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ - { - "source": "BgpOptions", - "target": "BgpOptions" - }, { "source": "PeerAddress", "target": "PeerAddress" @@ -15431,6 +15468,9 @@ }, { "cfn_type": "AWS::EC2::RouteServerPeer", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteRouteServerPeer", "phase": "delete", @@ -15438,6 +15478,9 @@ }, { "cfn_type": "AWS::EC2::RouteServerPropagation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "RouteServerId", @@ -15454,6 +15497,10 @@ }, { "cfn_type": "AWS::EC2::RouteTable", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "VpcId", @@ -15466,6 +15513,9 @@ }, { "cfn_type": "AWS::EC2::RouteTable", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteRouteTable", "phase": "delete", @@ -15473,6 +15523,9 @@ }, { "cfn_type": "AWS::EC2::SecurityGroup", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "GroupName", @@ -15489,6 +15542,9 @@ }, { "cfn_type": "AWS::EC2::SecurityGroup", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "GroupName", @@ -15501,6 +15557,9 @@ }, { "cfn_type": "AWS::EC2::SecurityGroupEgress", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "CidrIp", @@ -15529,6 +15588,9 @@ }, { "cfn_type": "AWS::EC2::SecurityGroupIngress", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "CidrIp", @@ -15569,6 +15631,9 @@ }, { "cfn_type": "AWS::EC2::SecurityGroupVpcAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "GroupId", @@ -15585,6 +15650,9 @@ }, { "cfn_type": "AWS::EC2::SecurityGroupVpcAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "GroupId", @@ -15601,6 +15669,9 @@ }, { "cfn_type": "AWS::EC2::SnapshotBlockPublicAccess", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "State", @@ -15613,6 +15684,9 @@ }, { "cfn_type": "AWS::EC2::Subnet", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "AvailabilityZone", @@ -15665,6 +15739,9 @@ }, { "cfn_type": "AWS::EC2::Subnet", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteSubnet", "phase": "delete", @@ -15703,6 +15780,10 @@ }, { "cfn_type": "AWS::EC2::TrafficMirrorFilter", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "Description", @@ -15715,6 +15796,9 @@ }, { "cfn_type": "AWS::EC2::TrafficMirrorFilter", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteTrafficMirrorFilter", "phase": "delete", @@ -15722,6 +15806,10 @@ }, { "cfn_type": "AWS::EC2::TrafficMirrorFilterRule", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "Description", @@ -15731,10 +15819,6 @@ "source": "DestinationCidrBlock", "target": "DestinationCidrBlock" }, - { - "source": "DestinationPortRange", - "target": "DestinationPortRange" - }, { "source": "Protocol", "target": "Protocol" @@ -15751,10 +15835,6 @@ "source": "SourceCidrBlock", "target": "SourceCidrBlock" }, - { - "source": "SourcePortRange", - "target": "SourcePortRange" - }, { "source": "TrafficDirection", "target": "TrafficDirection" @@ -15770,6 +15850,9 @@ }, { "cfn_type": "AWS::EC2::TrafficMirrorFilterRule", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteTrafficMirrorFilterRule", "phase": "delete", @@ -15777,6 +15860,10 @@ }, { "cfn_type": "AWS::EC2::TrafficMirrorSession", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "Description", @@ -15813,6 +15900,9 @@ }, { "cfn_type": "AWS::EC2::TrafficMirrorSession", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteTrafficMirrorSession", "phase": "delete", @@ -15820,6 +15910,10 @@ }, { "cfn_type": "AWS::EC2::TrafficMirrorTarget", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "Description", @@ -15844,6 +15938,9 @@ }, { "cfn_type": "AWS::EC2::TrafficMirrorTarget", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteTrafficMirrorTarget", "phase": "delete", @@ -15851,6 +15948,9 @@ }, { "cfn_type": "AWS::EC2::TransitGateway", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "Description", @@ -15863,6 +15963,9 @@ }, { "cfn_type": "AWS::EC2::TransitGateway", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteTransitGateway", "phase": "delete", @@ -15870,11 +15973,10 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayConnect", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ - { - "source": "Options", - "target": "Options" - }, { "source": "TransportTransitGatewayAttachmentId", "target": "TransportTransitGatewayAttachmentId" @@ -15886,6 +15988,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayConnect", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteTransitGatewayConnect", "phase": "delete", @@ -15893,6 +15998,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayConnectPeer", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "TransitGatewayAttachmentId", @@ -15905,29 +16013,141 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayConnectPeer", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteTransitGatewayConnectPeer", "phase": "delete", "service": "ec2" }, { - "cfn_type": "AWS::EC2::TransitGatewayMulticastDomain", + "cfn_type": "AWS::EC2::TransitGatewayMeteringPolicy", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { - "source": "Options", - "target": "Options" + "source": "MiddleboxAttachmentIds", + "target": "MiddleboxAttachmentIds" }, { "source": "TransitGatewayId", "target": "TransitGatewayId" } ], + "operation": "CreateTransitGatewayMeteringPolicy", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayMeteringPolicy", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [], + "operation": "DeleteTransitGatewayMeteringPolicy", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayMeteringPolicyEntry", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [ + { + "source": "DestinationCidrBlock", + "target": "DestinationCidrBlock" + }, + { + "source": "DestinationPortRange", + "target": "DestinationPortRange" + }, + { + "source": "DestinationTransitGatewayAttachmentId", + "target": "DestinationTransitGatewayAttachmentId" + }, + { + "source": "DestinationTransitGatewayAttachmentType", + "target": "DestinationTransitGatewayAttachmentType" + }, + { + "source": "MeteredAccount", + "target": "MeteredAccount" + }, + { + "source": "PolicyRuleNumber", + "target": "PolicyRuleNumber" + }, + { + "source": "Protocol", + "target": "Protocol" + }, + { + "source": "SourceCidrBlock", + "target": "SourceCidrBlock" + }, + { + "source": "SourcePortRange", + "target": "SourcePortRange" + }, + { + "source": "SourceTransitGatewayAttachmentId", + "target": "SourceTransitGatewayAttachmentId" + }, + { + "source": "SourceTransitGatewayAttachmentType", + "target": "SourceTransitGatewayAttachmentType" + }, + { + "source": "TransitGatewayMeteringPolicyId", + "target": "TransitGatewayMeteringPolicyId" + } + ], + "operation": "CreateTransitGatewayMeteringPolicyEntry", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayMeteringPolicyEntry", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [ + { + "source": "PolicyRuleNumber", + "target": "PolicyRuleNumber" + }, + { + "source": "TransitGatewayMeteringPolicyId", + "target": "TransitGatewayMeteringPolicyId" + } + ], + "operation": "DeleteTransitGatewayMeteringPolicyEntry", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayMulticastDomain", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [ + { + "source": "TransitGatewayId", + "target": "TransitGatewayId" + } + ], "operation": "CreateTransitGatewayMulticastDomain", "phase": "create", "service": "ec2" }, { "cfn_type": "AWS::EC2::TransitGatewayMulticastDomain", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteTransitGatewayMulticastDomain", "phase": "delete", @@ -15935,6 +16155,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayMulticastDomainAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "TransitGatewayAttachmentId", @@ -15951,6 +16174,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayMulticastDomainAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "TransitGatewayAttachmentId", @@ -15967,6 +16193,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayMulticastGroupMember", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "GroupIpAddress", @@ -15983,6 +16212,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayMulticastGroupMember", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "GroupIpAddress", @@ -15999,6 +16231,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayMulticastGroupSource", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "GroupIpAddress", @@ -16015,6 +16250,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayMulticastGroupSource", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "GroupIpAddress", @@ -16031,6 +16269,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayPeeringAttachment", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "PeerAccountId", @@ -16055,6 +16296,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayPeeringAttachment", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteTransitGatewayPeeringAttachment", "phase": "delete", @@ -16062,6 +16306,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayPolicyTable", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "TransitGatewayId", @@ -16074,6 +16321,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayPolicyTable", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteTransitGatewayPolicyTable", "phase": "delete", @@ -16081,6 +16331,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayPolicyTableAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "TransitGatewayAttachmentId", @@ -16097,6 +16350,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayPolicyTableAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "TransitGatewayAttachmentId", @@ -16111,8 +16367,53 @@ "phase": "delete", "service": "ec2" }, + { + "cfn_type": "AWS::EC2::TransitGatewayPolicyTableEntry", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [ + { + "source": "PolicyRuleNumber", + "target": "PolicyRuleNumber" + }, + { + "source": "TargetRouteTableId", + "target": "TargetRouteTableId" + }, + { + "source": "TransitGatewayPolicyTableId", + "target": "TransitGatewayPolicyTableId" + } + ], + "operation": "CreateTransitGatewayPolicyTableEntry", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::TransitGatewayPolicyTableEntry", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [ + { + "source": "PolicyRuleNumber", + "target": "PolicyRuleNumber" + }, + { + "source": "TransitGatewayPolicyTableId", + "target": "TransitGatewayPolicyTableId" + } + ], + "operation": "DeleteTransitGatewayPolicyTableEntry", + "phase": "delete", + "service": "ec2" + }, { "cfn_type": "AWS::EC2::TransitGatewayRoute", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "Blackhole", @@ -16137,6 +16438,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayRoute", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "DestinationCidrBlock", @@ -16153,6 +16457,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayRouteTable", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "TransitGatewayId", @@ -16163,8 +16470,21 @@ "phase": "create", "service": "ec2" }, + { + "cfn_type": "AWS::EC2::TransitGatewayRouteTable", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [], + "operation": "DeleteTransitGatewayRouteTable", + "phase": "delete", + "service": "ec2" + }, { "cfn_type": "AWS::EC2::TransitGatewayRouteTableAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "TransitGatewayAttachmentId", @@ -16181,6 +16501,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayRouteTableAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "TransitGatewayAttachmentId", @@ -16197,6 +16520,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayRouteTablePropagation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "TransitGatewayAttachmentId", @@ -16213,11 +16539,10 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayVpcAttachment", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ - { - "source": "Options", - "target": "Options" - }, { "source": "SubnetIds", "target": "SubnetIds" @@ -16237,6 +16562,9 @@ }, { "cfn_type": "AWS::EC2::TransitGatewayVpcAttachment", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteTransitGatewayVpcAttachment", "phase": "delete", @@ -16244,6 +16572,9 @@ }, { "cfn_type": "AWS::EC2::VPC", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "CidrBlock", @@ -16268,6 +16599,9 @@ }, { "cfn_type": "AWS::EC2::VPC", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteVpc", "phase": "delete", @@ -16275,6 +16609,9 @@ }, { "cfn_type": "AWS::EC2::VPCBlockPublicAccessExclusion", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "InternetGatewayExclusionMode", @@ -16295,6 +16632,9 @@ }, { "cfn_type": "AWS::EC2::VPCBlockPublicAccessExclusion", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteVpcBlockPublicAccessExclusion", "phase": "delete", @@ -16357,6 +16697,9 @@ }, { "cfn_type": "AWS::EC2::VPCDHCPOptionsAssociation", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "DhcpOptionsId", @@ -16372,12 +16715,37 @@ "service": "ec2" }, { - "cfn_type": "AWS::EC2::VPCEndpoint", + "cfn_type": "AWS::EC2::VPCEncryptionControl", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { - "source": "DnsOptions", - "target": "DnsOptions" - }, + "source": "VpcId", + "target": "VpcId" + } + ], + "operation": "CreateVpcEncryptionControl", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCEncryptionControl", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [], + "operation": "DeleteVpcEncryptionControl", + "phase": "delete", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPCEndpoint", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], + "mappings": [ { "source": "IpAddressType", "target": "IpAddressType" @@ -16433,6 +16801,9 @@ }, { "cfn_type": "AWS::EC2::VPCEndpoint", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteVpcEndpoints", "phase": "delete", @@ -16440,6 +16811,10 @@ }, { "cfn_type": "AWS::EC2::VPCEndpointConnectionNotification", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "ConnectionEvents", @@ -16464,6 +16839,9 @@ }, { "cfn_type": "AWS::EC2::VPCEndpointConnectionNotification", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteVpcEndpointConnectionNotifications", "phase": "delete", @@ -16471,6 +16849,10 @@ }, { "cfn_type": "AWS::EC2::VPCEndpointService", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "AcceptanceRequired", @@ -16499,6 +16881,9 @@ }, { "cfn_type": "AWS::EC2::VPCPeeringConnection", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "PeerOwnerId", @@ -16523,13 +16908,48 @@ }, { "cfn_type": "AWS::EC2::VPCPeeringConnection", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteVpcPeeringConnection", "phase": "delete", "service": "ec2" }, + { + "cfn_type": "AWS::EC2::VPNConcentrator", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [ + { + "source": "TransitGatewayId", + "target": "TransitGatewayId" + }, + { + "source": "Type", + "target": "Type" + } + ], + "operation": "CreateVpnConcentrator", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::VPNConcentrator", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [], + "operation": "DeleteVpnConcentrator", + "phase": "delete", + "service": "ec2" + }, { "cfn_type": "AWS::EC2::VPNConnection", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "CustomerGatewayId", @@ -16547,6 +16967,10 @@ "source": "Type", "target": "Type" }, + { + "source": "VpnConcentratorId", + "target": "VpnConcentratorId" + }, { "source": "VpnGatewayId", "target": "VpnGatewayId" @@ -16558,6 +16982,9 @@ }, { "cfn_type": "AWS::EC2::VPNConnection", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteVpnConnection", "phase": "delete", @@ -16597,6 +17024,9 @@ }, { "cfn_type": "AWS::EC2::VPNGateway", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "AmazonSideAsn", @@ -16613,6 +17043,9 @@ }, { "cfn_type": "AWS::EC2::VPNGateway", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteVpnGateway", "phase": "delete", @@ -16620,6 +17053,10 @@ }, { "cfn_type": "AWS::EC2::VerifiedAccessEndpoint", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "ApplicationDomain", @@ -16629,10 +17066,6 @@ "source": "AttachmentType", "target": "AttachmentType" }, - { - "source": "CidrOptions", - "target": "CidrOptions" - }, { "source": "Description", "target": "Description" @@ -16649,30 +17082,14 @@ "source": "EndpointType", "target": "EndpointType" }, - { - "source": "LoadBalancerOptions", - "target": "LoadBalancerOptions" - }, - { - "source": "NetworkInterfaceOptions", - "target": "NetworkInterfaceOptions" - }, { "source": "PolicyDocument", "target": "PolicyDocument" }, - { - "source": "RdsOptions", - "target": "RdsOptions" - }, { "source": "SecurityGroupIds", "target": "SecurityGroupIds" }, - { - "source": "SseSpecification", - "target": "SseSpecification" - }, { "source": "VerifiedAccessGroupId", "target": "VerifiedAccessGroupId" @@ -16684,6 +17101,10 @@ }, { "cfn_type": "AWS::EC2::VerifiedAccessEndpoint", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [], "operation": "DeleteVerifiedAccessEndpoint", "phase": "delete", @@ -16691,6 +17112,10 @@ }, { "cfn_type": "AWS::EC2::VerifiedAccessGroup", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "Description", @@ -16700,10 +17125,6 @@ "source": "PolicyDocument", "target": "PolicyDocument" }, - { - "source": "SseSpecification", - "target": "SseSpecification" - }, { "source": "VerifiedAccessInstanceId", "target": "VerifiedAccessInstanceId" @@ -16715,6 +17136,10 @@ }, { "cfn_type": "AWS::EC2::VerifiedAccessGroup", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [], "operation": "DeleteVerifiedAccessGroup", "phase": "delete", @@ -16722,6 +17147,10 @@ }, { "cfn_type": "AWS::EC2::VerifiedAccessInstance", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "CidrEndpointsCustomSubDomain", @@ -16742,6 +17171,10 @@ }, { "cfn_type": "AWS::EC2::VerifiedAccessInstance", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [], "operation": "DeleteVerifiedAccessInstance", "phase": "delete", @@ -16749,35 +17182,23 @@ }, { "cfn_type": "AWS::EC2::VerifiedAccessTrustProvider", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "Description", "target": "Description" }, - { - "source": "DeviceOptions", - "target": "DeviceOptions" - }, { "source": "DeviceTrustProviderType", "target": "DeviceTrustProviderType" }, - { - "source": "NativeApplicationOidcOptions", - "target": "NativeApplicationOidcOptions" - }, - { - "source": "OidcOptions", - "target": "OidcOptions" - }, { "source": "PolicyReferenceName", "target": "PolicyReferenceName" }, - { - "source": "SseSpecification", - "target": "SseSpecification" - }, { "source": "TrustProviderType", "target": "TrustProviderType" @@ -16793,6 +17214,10 @@ }, { "cfn_type": "AWS::EC2::VerifiedAccessTrustProvider", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [], "operation": "DeleteVerifiedAccessTrustProvider", "phase": "delete", @@ -16800,11 +17225,19 @@ }, { "cfn_type": "AWS::EC2::Volume", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], "mappings": [ { "source": "AvailabilityZone", "target": "AvailabilityZone" }, + { + "source": "AvailabilityZoneId", + "target": "AvailabilityZoneId" + }, { "source": "Encrypted", "target": "Encrypted" @@ -16852,6 +17285,9 @@ }, { "cfn_type": "AWS::EC2::Volume", + "ignored_inputs": [ + "DryRun" + ], "mappings": [], "operation": "DeleteVolume", "phase": "delete", @@ -16859,11 +17295,18 @@ }, { "cfn_type": "AWS::EC2::VolumeAttachment", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "Device", "target": "Device" }, + { + "source": "EbsCardIndex", + "target": "EbsCardIndex" + }, { "source": "InstanceId", "target": "InstanceId" @@ -16879,6 +17322,9 @@ }, { "cfn_type": "AWS::EC2::VolumeAttachment", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "Device", @@ -16897,22 +17343,6 @@ "phase": "delete", "service": "ec2" }, - { - "cfn_type": "AWS::ECR::PublicRepository", - "mappings": [ - { - "source": "repositoryName", - "target": "RepositoryName" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreateRepository", - "phase": "create", - "service": "ecr-public" - }, { "cfn_type": "AWS::ECR::PullThroughCacheRule", "mappings": [ @@ -16957,6 +17387,30 @@ "phase": "delete", "service": "ecr" }, + { + "cfn_type": "AWS::ECR::PullTimeUpdateExclusion", + "mappings": [ + { + "source": "principalArn", + "target": "PrincipalArn" + } + ], + "operation": "RegisterPullTimeUpdateExclusion", + "phase": "create", + "service": "ecr" + }, + { + "cfn_type": "AWS::ECR::PullTimeUpdateExclusion", + "mappings": [ + { + "source": "principalArn", + "target": "PrincipalArn" + } + ], + "operation": "DeregisterPullTimeUpdateExclusion", + "phase": "delete", + "service": "ecr" + }, { "cfn_type": "AWS::ECR::RegistryPolicy", "mappings": [ @@ -16979,10 +17433,6 @@ { "cfn_type": "AWS::ECR::RegistryScanningConfiguration", "mappings": [ - { - "source": "rules", - "target": "Rules" - }, { "source": "scanType", "target": "ScanType" @@ -16992,44 +17442,16 @@ "phase": "create", "service": "ecr" }, - { - "cfn_type": "AWS::ECR::ReplicationConfiguration", - "mappings": [ - { - "source": "replicationConfiguration", - "target": "ReplicationConfiguration" - } - ], - "operation": "PutReplicationConfiguration", - "phase": "create", - "service": "ecr" - }, { "cfn_type": "AWS::ECR::Repository", "mappings": [ - { - "source": "encryptionConfiguration", - "target": "EncryptionConfiguration" - }, - { - "source": "imageScanningConfiguration", - "target": "ImageScanningConfiguration" - }, { "source": "imageTagMutability", "target": "ImageTagMutability" }, - { - "source": "imageTagMutabilityExclusionFilters", - "target": "ImageTagMutabilityExclusionFilters" - }, { "source": "repositoryName", "target": "RepositoryName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateRepository", @@ -17063,18 +17485,10 @@ "source": "description", "target": "Description" }, - { - "source": "encryptionConfiguration", - "target": "EncryptionConfiguration" - }, { "source": "imageTagMutability", "target": "ImageTagMutability" }, - { - "source": "imageTagMutabilityExclusionFilters", - "target": "ImageTagMutabilityExclusionFilters" - }, { "source": "lifecyclePolicy", "target": "LifecyclePolicy" @@ -17086,10 +17500,6 @@ { "source": "repositoryPolicy", "target": "RepositoryPolicy" - }, - { - "source": "resourceTags", - "target": "ResourceTags" } ], "operation": "CreateRepositoryCreationTemplate", @@ -17108,20 +17518,23 @@ "phase": "delete", "service": "ecr" }, + { + "cfn_type": "AWS::ECR::SigningConfiguration", + "mappings": [], + "operation": "DeleteSigningConfiguration", + "phase": "delete", + "service": "ecr" + }, { "cfn_type": "AWS::ECS::CapacityProvider", "mappings": [ { - "source": "autoScalingGroupProvider", - "target": "AutoScalingGroupProvider" + "source": "cluster", + "target": "ClusterName" }, { "source": "name", "target": "Name" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateCapacityProvider", @@ -17130,7 +17543,12 @@ }, { "cfn_type": "AWS::ECS::CapacityProvider", - "mappings": [], + "mappings": [ + { + "source": "cluster", + "target": "ClusterName" + } + ], "operation": "DeleteCapacityProvider", "phase": "delete", "service": "ecs" @@ -17145,22 +17563,6 @@ { "source": "clusterName", "target": "ClusterName" - }, - { - "source": "configuration", - "target": "Configuration" - }, - { - "source": "defaultCapacityProviderStrategy", - "target": "DefaultCapacityProviderStrategy" - }, - { - "source": "serviceConnectDefaults", - "target": "ServiceConnectDefaults" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateCluster", @@ -17189,38 +17591,159 @@ { "source": "cluster", "target": "Cluster" + } + ], + "operation": "PutClusterCapacityProviders", + "phase": "create", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::Daemon", + "mappings": [ + { + "source": "capacityProviderArns", + "target": "CapacityProviderArns" }, { - "source": "defaultCapacityProviderStrategy", - "target": "DefaultCapacityProviderStrategy" + "source": "clusterArn", + "target": "ClusterArn" + }, + { + "source": "daemonName", + "target": "DaemonName" + }, + { + "source": "daemonTaskDefinitionArn", + "target": "DaemonTaskDefinitionArn" + }, + { + "source": "enableECSManagedTags", + "target": "EnableECSManagedTags" + }, + { + "source": "enableExecuteCommand", + "target": "EnableExecuteCommand" + }, + { + "source": "propagateTags", + "target": "PropagateTags" } ], - "operation": "PutClusterCapacityProviders", + "operation": "CreateDaemon", "phase": "create", "service": "ecs" }, { - "cfn_type": "AWS::ECS::Service", + "cfn_type": "AWS::ECS::Daemon", + "mappings": [], + "operation": "DeleteDaemon", + "phase": "delete", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::DaemonTaskDefinition", "mappings": [ { - "source": "availabilityZoneRebalancing", - "target": "AvailabilityZoneRebalancing" + "source": "cpu", + "target": "Cpu" + }, + { + "source": "executionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "family", + "target": "Family" }, { - "source": "capacityProviderStrategy", - "target": "CapacityProviderStrategy" + "source": "ipcMode", + "target": "IpcMode" }, + { + "source": "memory", + "target": "Memory" + }, + { + "source": "pidMode", + "target": "PidMode" + }, + { + "source": "taskRoleArn", + "target": "TaskRoleArn" + } + ], + "operation": "RegisterDaemonTaskDefinition", + "phase": "create", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::DaemonTaskDefinition", + "mappings": [], + "operation": "DeleteDaemonTaskDefinition", + "phase": "delete", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::ExpressGatewayService", + "mappings": [ { "source": "cluster", "target": "Cluster" }, { - "source": "deploymentConfiguration", - "target": "DeploymentConfiguration" + "source": "cpu", + "target": "Cpu" + }, + { + "source": "executionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "healthCheckPath", + "target": "HealthCheckPath" + }, + { + "source": "infrastructureRoleArn", + "target": "InfrastructureRoleArn" + }, + { + "source": "memory", + "target": "Memory" + }, + { + "source": "serviceName", + "target": "ServiceName" + }, + { + "source": "taskDefinitionArn", + "target": "TaskDefinitionArn" + }, + { + "source": "taskRoleArn", + "target": "TaskRoleArn" + } + ], + "operation": "CreateExpressGatewayService", + "phase": "create", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::ExpressGatewayService", + "mappings": [], + "operation": "DeleteExpressGatewayService", + "phase": "delete", + "service": "ecs" + }, + { + "cfn_type": "AWS::ECS::Service", + "mappings": [ + { + "source": "availabilityZoneRebalancing", + "target": "AvailabilityZoneRebalancing" }, { - "source": "deploymentController", - "target": "DeploymentController" + "source": "cluster", + "target": "Cluster" }, { "source": "desiredCount", @@ -17242,18 +17765,6 @@ "source": "launchType", "target": "LaunchType" }, - { - "source": "loadBalancers", - "target": "LoadBalancers" - }, - { - "source": "networkConfiguration", - "target": "NetworkConfiguration" - }, - { - "source": "placementConstraints", - "target": "PlacementConstraints" - }, { "source": "platformVersion", "target": "PlatformVersion" @@ -17270,33 +17781,13 @@ "source": "schedulingStrategy", "target": "SchedulingStrategy" }, - { - "source": "serviceConnectConfiguration", - "target": "ServiceConnectConfiguration" - }, { "source": "serviceName", "target": "ServiceName" }, - { - "source": "serviceRegistries", - "target": "ServiceRegistries" - }, - { - "source": "tags", - "target": "Tags" - }, { "source": "taskDefinition", "target": "TaskDefinition" - }, - { - "source": "volumeConfigurations", - "target": "VolumeConfigurations" - }, - { - "source": "vpcLatticeConfigurations", - "target": "VpcLatticeConfigurations" } ], "operation": "CreateService", @@ -17322,10 +17813,6 @@ { "cfn_type": "AWS::ECS::TaskDefinition", "mappings": [ - { - "source": "containerDefinitions", - "target": "ContainerDefinitions" - }, { "source": "cpu", "target": "Cpu" @@ -17334,10 +17821,6 @@ "source": "enableFaultInjection", "target": "EnableFaultInjection" }, - { - "source": "ephemeralStorage", - "target": "EphemeralStorage" - }, { "source": "executionRoleArn", "target": "ExecutionRoleArn" @@ -17346,10 +17829,6 @@ "source": "family", "target": "Family" }, - { - "source": "inferenceAccelerators", - "target": "InferenceAccelerators" - }, { "source": "ipcMode", "target": "IpcMode" @@ -17366,33 +17845,13 @@ "source": "pidMode", "target": "PidMode" }, - { - "source": "placementConstraints", - "target": "PlacementConstraints" - }, - { - "source": "proxyConfiguration", - "target": "ProxyConfiguration" - }, { "source": "requiresCompatibilities", "target": "RequiresCompatibilities" }, - { - "source": "runtimePlatform", - "target": "RuntimePlatform" - }, - { - "source": "tags", - "target": "Tags" - }, { "source": "taskRoleArn", "target": "TaskRoleArn" - }, - { - "source": "volumes", - "target": "Volumes" } ], "operation": "RegisterTaskDefinition", @@ -17409,10 +17868,6 @@ { "cfn_type": "AWS::ECS::TaskSet", "mappings": [ - { - "source": "capacityProviderStrategy", - "target": "CapacityProviderStrategy" - }, { "source": "cluster", "target": "Cluster" @@ -17425,34 +17880,14 @@ "source": "launchType", "target": "LaunchType" }, - { - "source": "loadBalancers", - "target": "LoadBalancers" - }, - { - "source": "networkConfiguration", - "target": "NetworkConfiguration" - }, { "source": "platformVersion", "target": "PlatformVersion" }, - { - "source": "scale", - "target": "Scale" - }, { "source": "service", "target": "Service" }, - { - "source": "serviceRegistries", - "target": "ServiceRegistries" - }, - { - "source": "tags", - "target": "Tags" - }, { "source": "taskDefinition", "target": "TaskDefinition" @@ -17480,6 +17915,9 @@ }, { "cfn_type": "AWS::EFS::AccessPoint", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ClientToken", @@ -17488,14 +17926,6 @@ { "source": "FileSystemId", "target": "FileSystemId" - }, - { - "source": "PosixUser", - "target": "PosixUser" - }, - { - "source": "RootDirectory", - "target": "RootDirectory" } ], "operation": "CreateAccessPoint", @@ -17511,6 +17941,9 @@ }, { "cfn_type": "AWS::EFS::FileSystem", + "ignored_inputs": [ + "CreationToken" + ], "mappings": [ { "source": "AvailabilityZoneName", @@ -17589,6 +18022,9 @@ }, { "cfn_type": "AWS::EKS::AccessEntry", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "clusterName", @@ -17637,6 +18073,9 @@ }, { "cfn_type": "AWS::EKS::Addon", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "addonName", @@ -17654,10 +18093,6 @@ "source": "configurationValues", "target": "ConfigurationValues" }, - { - "source": "podIdentityAssociations", - "target": "PodIdentityAssociations" - }, { "source": "resolveConflicts", "target": "ResolveConflicts" @@ -17692,75 +18127,85 @@ "service": "eks" }, { - "cfn_type": "AWS::EKS::Cluster", + "cfn_type": "AWS::EKS::Capability", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { - "source": "accessConfig", - "target": "AccessConfig" + "source": "capabilityName", + "target": "CapabilityName" }, { - "source": "bootstrapSelfManagedAddons", - "target": "BootstrapSelfManagedAddons" + "source": "clusterName", + "target": "ClusterName" }, { - "source": "computeConfig", - "target": "ComputeConfig" + "source": "deletePropagationPolicy", + "target": "DeletePropagationPolicy" }, { - "source": "deletionProtection", - "target": "DeletionProtection" + "source": "roleArn", + "target": "RoleArn" }, { - "source": "encryptionConfig", - "target": "EncryptionConfig" + "source": "tags", + "target": "Tags" }, { - "source": "kubernetesNetworkConfig", - "target": "KubernetesNetworkConfig" - }, + "source": "type", + "target": "Type" + } + ], + "operation": "CreateCapability", + "phase": "create", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::Capability", + "mappings": [ { - "source": "logging", - "target": "Logging" + "source": "capabilityName", + "target": "CapabilityName" }, { - "source": "name", - "target": "Name" - }, + "source": "clusterName", + "target": "ClusterName" + } + ], + "operation": "DeleteCapability", + "phase": "delete", + "service": "eks" + }, + { + "cfn_type": "AWS::EKS::Cluster", + "ignored_inputs": [ + "clientRequestToken" + ], + "mappings": [ { - "source": "outpostConfig", - "target": "OutpostConfig" + "source": "bootstrapSelfManagedAddons", + "target": "BootstrapSelfManagedAddons" }, { - "source": "remoteNetworkConfig", - "target": "RemoteNetworkConfig" + "source": "deletionProtection", + "target": "DeletionProtection" }, { - "source": "resourcesVpcConfig", - "target": "ResourcesVpcConfig" + "source": "name", + "target": "Name" }, { "source": "roleArn", "target": "RoleArn" }, - { - "source": "storageConfig", - "target": "StorageConfig" - }, { "source": "tags", "target": "Tags" }, - { - "source": "upgradePolicy", - "target": "UpgradePolicy" - }, { "source": "version", "target": "Version" - }, - { - "source": "zonalShiftConfig", - "target": "ZonalShiftConfig" } ], "operation": "CreateCluster", @@ -17781,6 +18226,9 @@ }, { "cfn_type": "AWS::EKS::FargateProfile", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "clusterName", @@ -17794,10 +18242,6 @@ "source": "podExecutionRoleArn", "target": "PodExecutionRoleArn" }, - { - "source": "selectors", - "target": "Selectors" - }, { "source": "subnets", "target": "Subnets" @@ -17829,15 +18273,14 @@ }, { "cfn_type": "AWS::EKS::IdentityProviderConfig", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "clusterName", "target": "ClusterName" }, - { - "source": "oidc", - "target": "Oidc" - }, { "source": "tags", "target": "Tags" @@ -17849,14 +18292,13 @@ }, { "cfn_type": "AWS::EKS::IdentityProviderConfig", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "clusterName", "target": "ClusterName" - }, - { - "source": "identityProviderConfig", - "target": "IdentityProviderConfigName" } ], "operation": "DisassociateIdentityProviderConfig", @@ -17865,6 +18307,9 @@ }, { "cfn_type": "AWS::EKS::Nodegroup", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "amiType", @@ -17886,18 +18331,6 @@ "source": "instanceTypes", "target": "InstanceTypes" }, - { - "source": "labels", - "target": "Labels" - }, - { - "source": "launchTemplate", - "target": "LaunchTemplate" - }, - { - "source": "nodeRepairConfig", - "target": "NodeRepairConfig" - }, { "source": "nodeRole", "target": "NodeRole" @@ -17910,30 +18343,10 @@ "source": "releaseVersion", "target": "ReleaseVersion" }, - { - "source": "remoteAccess", - "target": "RemoteAccess" - }, - { - "source": "scalingConfig", - "target": "ScalingConfig" - }, { "source": "subnets", "target": "Subnets" }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "taints", - "target": "Taints" - }, - { - "source": "updateConfig", - "target": "UpdateConfig" - }, { "source": "version", "target": "Version" @@ -17961,6 +18374,9 @@ }, { "cfn_type": "AWS::EKS::PodIdentityAssociation", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "clusterName", @@ -17974,6 +18390,10 @@ "source": "namespace", "target": "Namespace" }, + { + "source": "policy", + "target": "Policy" + }, { "source": "roleArn", "target": "RoleArn" @@ -18023,6 +18443,18 @@ "phase": "create", "service": "emr" }, + { + "cfn_type": "AWS::EMR::SecurityConfiguration", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteSecurityConfiguration", + "phase": "delete", + "service": "emr" + }, { "cfn_type": "AWS::EMR::Step", "mappings": [ @@ -18086,10 +18518,6 @@ "source": "SubnetIds", "target": "SubnetIds" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TrustedIdentityPropagationEnabled", "target": "TrustedIdentityPropagationEnabled" @@ -18164,11 +18592,10 @@ }, { "cfn_type": "AWS::EMRContainers::Endpoint", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "configurationOverrides", - "target": "ConfigurationOverrides" - }, { "source": "executionRoleArn", "target": "ExecutionRoleArn" @@ -18181,6 +18608,10 @@ "source": "releaseLabel", "target": "ReleaseLabel" }, + { + "source": "sessionIdleTimeoutInMinutes", + "target": "SessionIdleTimeoutInMinutes" + }, { "source": "tags", "target": "Tags" @@ -18212,15 +18643,14 @@ }, { "cfn_type": "AWS::EMRContainers::SecurityConfiguration", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "name", "target": "Name" }, - { - "source": "securityConfigurationData", - "target": "SecurityConfigurationData" - }, { "source": "tags", "target": "Tags" @@ -18230,13 +18660,19 @@ "phase": "create", "service": "emr-containers" }, + { + "cfn_type": "AWS::EMRContainers::SecurityConfiguration", + "mappings": [], + "operation": "DeleteSecurityConfiguration", + "phase": "delete", + "service": "emr-containers" + }, { "cfn_type": "AWS::EMRContainers::VirtualCluster", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "containerProvider", - "target": "ContainerProvider" - }, { "source": "name", "target": "Name" @@ -18245,6 +18681,10 @@ "source": "securityConfigurationId", "target": "SecurityConfigurationId" }, + { + "source": "sessionEnabled", + "target": "SessionEnabled" + }, { "source": "tags", "target": "Tags" @@ -18263,63 +18703,22 @@ }, { "cfn_type": "AWS::EMRServerless::Application", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "architecture", "target": "Architecture" }, - { - "source": "autoStartConfiguration", - "target": "AutoStartConfiguration" - }, - { - "source": "autoStopConfiguration", - "target": "AutoStopConfiguration" - }, - { - "source": "identityCenterConfiguration", - "target": "IdentityCenterConfiguration" - }, - { - "source": "imageConfiguration", - "target": "ImageConfiguration" - }, - { - "source": "initialCapacity", - "target": "InitialCapacity" - }, - { - "source": "interactiveConfiguration", - "target": "InteractiveConfiguration" - }, - { - "source": "maximumCapacity", - "target": "MaximumCapacity" - }, - { - "source": "monitoringConfiguration", - "target": "MonitoringConfiguration" - }, { "source": "name", "target": "Name" }, - { - "source": "networkConfiguration", - "target": "NetworkConfiguration" - }, { "source": "releaseLabel", "target": "ReleaseLabel" }, - { - "source": "runtimeConfiguration", - "target": "RuntimeConfiguration" - }, - { - "source": "schedulerConfiguration", - "target": "SchedulerConfiguration" - }, { "source": "tags", "target": "Tags" @@ -18327,10 +18726,6 @@ { "source": "type", "target": "Type" - }, - { - "source": "workerTypeSpecifications", - "target": "WorkerTypeSpecifications" } ], "operation": "CreateApplication", @@ -18346,35 +18741,18 @@ }, { "cfn_type": "AWS::EVS::Environment", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "connectivityInfo", - "target": "ConnectivityInfo" - }, { "source": "environmentName", "target": "EnvironmentName" }, - { - "source": "hosts", - "target": "Hosts" - }, - { - "source": "initialVlans", - "target": "InitialVlans" - }, { "source": "kmsKeyId", "target": "KmsKeyId" }, - { - "source": "licenseInfo", - "target": "LicenseInfo" - }, - { - "source": "serviceAccessSecurityGroups", - "target": "ServiceAccessSecurityGroups" - }, { "source": "serviceAccessSubnetId", "target": "ServiceAccessSubnetId" @@ -18391,10 +18769,6 @@ "source": "termsAccepted", "target": "TermsAccepted" }, - { - "source": "vcfHostnames", - "target": "VcfHostnames" - }, { "source": "vcfVersion", "target": "VcfVersion" @@ -18410,6 +18784,9 @@ }, { "cfn_type": "AWS::EVS::Environment", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteEnvironment", "phase": "delete", @@ -18454,10 +18831,6 @@ "source": "IpDiscovery", "target": "IpDiscovery" }, - { - "source": "LogDeliveryConfigurations", - "target": "LogDeliveryConfigurations" - }, { "source": "NetworkType", "target": "NetworkType" @@ -18502,10 +18875,6 @@ "source": "SnapshotWindow", "target": "SnapshotWindow" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TransitEncryptionEnabled", "target": "TransitEncryptionEnabled" @@ -18538,6 +18907,13 @@ "phase": "create", "service": "elasticache" }, + { + "cfn_type": "AWS::ElastiCache::GlobalReplicationGroup", + "mappings": [], + "operation": "DeleteGlobalReplicationGroup", + "phase": "delete", + "service": "elasticache" + }, { "cfn_type": "AWS::ElastiCache::ParameterGroup", "mappings": [ @@ -18548,10 +18924,6 @@ { "source": "Description", "target": "Description" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateCacheParameterGroup", @@ -18608,6 +18980,10 @@ "source": "DataTieringEnabled", "target": "DataTieringEnabled" }, + { + "source": "Durability", + "target": "Durability" + }, { "source": "Engine", "target": "Engine" @@ -18628,10 +19004,6 @@ "source": "KmsKeyId", "target": "KmsKeyId" }, - { - "source": "LogDeliveryConfigurations", - "target": "LogDeliveryConfigurations" - }, { "source": "MultiAZEnabled", "target": "MultiAZEnabled" @@ -18640,10 +19012,6 @@ "source": "NetworkType", "target": "NetworkType" }, - { - "source": "NodeGroupConfiguration", - "target": "NodeGroupConfiguration" - }, { "source": "NotificationTopicArn", "target": "NotificationTopicArn" @@ -18704,10 +19072,6 @@ "source": "SnapshotWindow", "target": "SnapshotWindow" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TransitEncryptionEnabled", "target": "TransitEncryptionEnabled" @@ -18740,10 +19104,6 @@ { "cfn_type": "AWS::ElastiCache::ServerlessCache", "mappings": [ - { - "source": "CacheUsageLimits", - "target": "CacheUsageLimits" - }, { "source": "DailySnapshotTime", "target": "DailySnapshotTime" @@ -18784,10 +19144,6 @@ "source": "SubnetIds", "target": "SubnetIds" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UserGroupId", "target": "UserGroupId" @@ -18813,6 +19169,38 @@ "phase": "delete", "service": "elasticache" }, + { + "cfn_type": "AWS::ElastiCache::ServerlessCacheSnapshot", + "mappings": [ + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "ServerlessCacheName", + "target": "ServerlessCacheName" + }, + { + "source": "ServerlessCacheSnapshotName", + "target": "ServerlessCacheSnapshotName" + } + ], + "operation": "CreateServerlessCacheSnapshot", + "phase": "create", + "service": "elasticache" + }, + { + "cfn_type": "AWS::ElastiCache::ServerlessCacheSnapshot", + "mappings": [ + { + "source": "ServerlessCacheSnapshotName", + "target": "ServerlessCacheSnapshotName" + } + ], + "operation": "DeleteServerlessCacheSnapshot", + "phase": "delete", + "service": "elasticache" + }, { "cfn_type": "AWS::ElastiCache::SubnetGroup", "mappings": [ @@ -18823,10 +19211,6 @@ { "source": "SubnetIds", "target": "SubnetIds" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateCacheSubnetGroup", @@ -18852,10 +19236,6 @@ "source": "AccessString", "target": "AccessString" }, - { - "source": "AuthenticationMode", - "target": "AuthenticationMode" - }, { "source": "Engine", "target": "Engine" @@ -18868,10 +19248,6 @@ "source": "Passwords", "target": "Passwords" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UserId", "target": "UserId" @@ -18904,10 +19280,6 @@ "source": "Engine", "target": "Engine" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UserGroupId", "target": "UserGroupId" @@ -18943,10 +19315,6 @@ { "source": "Description", "target": "Description" - }, - { - "source": "ResourceLifecycleConfig", - "target": "ResourceLifecycleConfig" } ], "operation": "CreateApplication", @@ -18975,10 +19343,6 @@ { "source": "Description", "target": "Description" - }, - { - "source": "SourceBundle", - "target": "SourceBundle" } ], "operation": "CreateApplicationVersion", @@ -19012,10 +19376,6 @@ "source": "EnvironmentId", "target": "EnvironmentId" }, - { - "source": "OptionSettings", - "target": "OptionSettings" - }, { "source": "PlatformArn", "target": "PlatformArn" @@ -19023,10 +19383,6 @@ { "source": "SolutionStackName", "target": "SolutionStackName" - }, - { - "source": "SourceConfiguration", - "target": "SourceConfiguration" } ], "operation": "CreateConfigurationTemplate", @@ -19068,10 +19424,6 @@ "source": "OperationsRole", "target": "OperationsRole" }, - { - "source": "OptionSettings", - "target": "OptionSettings" - }, { "source": "PlatformArn", "target": "PlatformArn" @@ -19080,18 +19432,10 @@ "source": "SolutionStackName", "target": "SolutionStackName" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TemplateName", "target": "TemplateName" }, - { - "source": "Tier", - "target": "Tier" - }, { "source": "VersionLabel", "target": "VersionLabel" @@ -19120,10 +19464,6 @@ "source": "AvailabilityZones", "target": "AvailabilityZones" }, - { - "source": "Listeners", - "target": "Listeners" - }, { "source": "LoadBalancerName", "target": "LoadBalancerName" @@ -19139,10 +19479,6 @@ { "source": "Subnets", "target": "Subnets" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateLoadBalancer", @@ -19168,22 +19504,10 @@ "source": "AlpnPolicy", "target": "AlpnPolicy" }, - { - "source": "Certificates", - "target": "Certificates" - }, - { - "source": "DefaultActions", - "target": "DefaultActions" - }, { "source": "LoadBalancerArn", "target": "LoadBalancerArn" }, - { - "source": "MutualAuthentication", - "target": "MutualAuthentication" - }, { "source": "Port", "target": "Port" @@ -19195,10 +19519,6 @@ { "source": "SslPolicy", "target": "SslPolicy" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateListener", @@ -19215,14 +19535,6 @@ { "cfn_type": "AWS::ElasticLoadBalancingV2::ListenerRule", "mappings": [ - { - "source": "Actions", - "target": "Actions" - }, - { - "source": "Conditions", - "target": "Conditions" - }, { "source": "ListenerArn", "target": "ListenerArn" @@ -19230,10 +19542,6 @@ { "source": "Priority", "target": "Priority" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateRule", @@ -19263,18 +19571,10 @@ "source": "SecurityGroups", "target": "SecurityGroups" }, - { - "source": "SubnetMappings", - "target": "SubnetMappings" - }, { "source": "Subnets", "target": "Subnets" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" @@ -19319,10 +19619,6 @@ "source": "IpAddressType", "target": "IpAddressType" }, - { - "source": "Matcher", - "target": "Matcher" - }, { "source": "Name", "target": "Name" @@ -19340,8 +19636,8 @@ "target": "ProtocolVersion" }, { - "source": "Tags", - "target": "Tags" + "source": "TargetControlPort", + "target": "TargetControlPort" }, { "source": "TargetType", @@ -19385,10 +19681,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateTrustStore", @@ -19405,10 +19697,6 @@ { "cfn_type": "AWS::ElasticLoadBalancingV2::TrustStoreRevocation", "mappings": [ - { - "source": "RevocationContents", - "target": "RevocationContents" - }, { "source": "TrustStoreArn", "target": "TrustStoreArn" @@ -19431,23 +19719,57 @@ "service": "elbv2" }, { - "cfn_type": "AWS::EntityResolution::IdMappingWorkflow", + "cfn_type": "AWS::ElementalInference::Dictionary", "mappings": [ { - "source": "description", - "target": "Description" + "source": "entries", + "target": "Entries" }, { - "source": "idMappingTechniques", - "target": "IdMappingTechniques" + "source": "language", + "target": "Language" }, { - "source": "inputSourceConfig", - "target": "InputSourceConfig" - }, + "source": "name", + "target": "Name" + } + ], + "operation": "CreateDictionary", + "phase": "create", + "service": "elementalinference" + }, + { + "cfn_type": "AWS::ElementalInference::Dictionary", + "mappings": [], + "operation": "DeleteDictionary", + "phase": "delete", + "service": "elementalinference" + }, + { + "cfn_type": "AWS::ElementalInference::Feed", + "mappings": [ { - "source": "outputSourceConfig", - "target": "OutputSourceConfig" + "source": "name", + "target": "Name" + } + ], + "operation": "CreateFeed", + "phase": "create", + "service": "elementalinference" + }, + { + "cfn_type": "AWS::ElementalInference::Feed", + "mappings": [], + "operation": "DeleteFeed", + "phase": "delete", + "service": "elementalinference" + }, + { + "cfn_type": "AWS::EntityResolution::IdMappingWorkflow", + "mappings": [ + { + "source": "description", + "target": "Description" }, { "source": "roleArn", @@ -19485,18 +19807,10 @@ "source": "description", "target": "Description" }, - { - "source": "idMappingWorkflowProperties", - "target": "IdMappingWorkflowProperties" - }, { "source": "idNamespaceName", "target": "IdNamespaceName" }, - { - "source": "inputSourceConfig", - "target": "InputSourceConfig" - }, { "source": "roleArn", "target": "RoleArn" @@ -19533,22 +19847,6 @@ "source": "description", "target": "Description" }, - { - "source": "incrementalRunConfig", - "target": "IncrementalRunConfig" - }, - { - "source": "inputSourceConfig", - "target": "InputSourceConfig" - }, - { - "source": "outputSourceConfig", - "target": "OutputSourceConfig" - }, - { - "source": "resolutionTechniques", - "target": "ResolutionTechniques" - }, { "source": "roleArn", "target": "RoleArn" @@ -19633,10 +19931,6 @@ "source": "description", "target": "Description" }, - { - "source": "mappedInputFields", - "target": "MappedInputFields" - }, { "source": "schemaName", "target": "SchemaName" @@ -19880,10 +20174,6 @@ { "cfn_type": "AWS::Events::Connection", "mappings": [ - { - "source": "AuthParameters", - "target": "AuthParameters" - }, { "source": "AuthorizationType", "target": "AuthorizationType" @@ -19892,10 +20182,6 @@ "source": "Description", "target": "Description" }, - { - "source": "InvocationConnectivityParameters", - "target": "InvocationConnectivityParameters" - }, { "source": "KmsKeyIdentifier", "target": "KmsKeyIdentifier" @@ -19928,25 +20214,13 @@ "source": "Description", "target": "Description" }, - { - "source": "EventBuses", - "target": "EventBuses" - }, { "source": "Name", "target": "Name" }, - { - "source": "ReplicationConfig", - "target": "ReplicationConfig" - }, { "source": "RoleArn", "target": "RoleArn" - }, - { - "source": "RoutingConfig", - "target": "RoutingConfig" } ], "operation": "CreateEndpoint", @@ -19968,10 +20242,6 @@ { "cfn_type": "AWS::Events::EventBus", "mappings": [ - { - "source": "DeadLetterConfig", - "target": "DeadLetterConfig" - }, { "source": "Description", "target": "Description" @@ -19984,17 +20254,9 @@ "source": "KmsKeyIdentifier", "target": "KmsKeyIdentifier" }, - { - "source": "LogConfig", - "target": "LogConfig" - }, { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateEventBus", @@ -20020,10 +20282,6 @@ "source": "Action", "target": "Action" }, - { - "source": "Condition", - "target": "Condition" - }, { "source": "EventBusName", "target": "EventBusName" @@ -20087,10 +20345,6 @@ { "source": "State", "target": "State" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "PutRule", @@ -20113,274 +20367,19 @@ "phase": "delete", "service": "events" }, - { - "cfn_type": "AWS::Evidently::Experiment", - "mappings": [ - { - "source": "description", - "target": "Description" - }, - { - "source": "metricGoals", - "target": "MetricGoals" - }, - { - "source": "name", - "target": "Name" - }, - { - "source": "onlineAbConfig", - "target": "OnlineAbConfig" - }, - { - "source": "project", - "target": "Project" - }, - { - "source": "randomizationSalt", - "target": "RandomizationSalt" - }, - { - "source": "samplingRate", - "target": "SamplingRate" - }, - { - "source": "segment", - "target": "Segment" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "treatments", - "target": "Treatments" - } - ], - "operation": "CreateExperiment", - "phase": "create", - "service": "evidently" - }, - { - "cfn_type": "AWS::Evidently::Experiment", - "mappings": [ - { - "source": "project", - "target": "Project" - } - ], - "operation": "DeleteExperiment", - "phase": "delete", - "service": "evidently" - }, - { - "cfn_type": "AWS::Evidently::Feature", - "mappings": [ - { - "source": "defaultVariation", - "target": "DefaultVariation" - }, - { - "source": "description", - "target": "Description" - }, - { - "source": "entityOverrides", - "target": "EntityOverrides" - }, - { - "source": "evaluationStrategy", - "target": "EvaluationStrategy" - }, - { - "source": "name", - "target": "Name" - }, - { - "source": "project", - "target": "Project" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "variations", - "target": "Variations" - } - ], - "operation": "CreateFeature", - "phase": "create", - "service": "evidently" - }, - { - "cfn_type": "AWS::Evidently::Feature", - "mappings": [ - { - "source": "project", - "target": "Project" - } - ], - "operation": "DeleteFeature", - "phase": "delete", - "service": "evidently" - }, - { - "cfn_type": "AWS::Evidently::Launch", - "mappings": [ - { - "source": "description", - "target": "Description" - }, - { - "source": "groups", - "target": "Groups" - }, - { - "source": "metricMonitors", - "target": "MetricMonitors" - }, - { - "source": "name", - "target": "Name" - }, - { - "source": "project", - "target": "Project" - }, - { - "source": "randomizationSalt", - "target": "RandomizationSalt" - }, - { - "source": "scheduledSplitsConfig", - "target": "ScheduledSplitsConfig" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreateLaunch", - "phase": "create", - "service": "evidently" - }, - { - "cfn_type": "AWS::Evidently::Launch", - "mappings": [ - { - "source": "project", - "target": "Project" - } - ], - "operation": "DeleteLaunch", - "phase": "delete", - "service": "evidently" - }, - { - "cfn_type": "AWS::Evidently::Project", - "mappings": [ - { - "source": "appConfigResource", - "target": "AppConfigResource" - }, - { - "source": "dataDelivery", - "target": "DataDelivery" - }, - { - "source": "description", - "target": "Description" - }, - { - "source": "name", - "target": "Name" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreateProject", - "phase": "create", - "service": "evidently" - }, - { - "cfn_type": "AWS::Evidently::Project", - "mappings": [], - "operation": "DeleteProject", - "phase": "delete", - "service": "evidently" - }, - { - "cfn_type": "AWS::Evidently::Segment", - "mappings": [ - { - "source": "description", - "target": "Description" - }, - { - "source": "name", - "target": "Name" - }, - { - "source": "pattern", - "target": "Pattern" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreateSegment", - "phase": "create", - "service": "evidently" - }, - { - "cfn_type": "AWS::Evidently::Segment", - "mappings": [], - "operation": "DeleteSegment", - "phase": "delete", - "service": "evidently" - }, { "cfn_type": "AWS::FIS::ExperimentTemplate", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "actions", - "target": "Actions" - }, { "source": "description", "target": "Description" }, - { - "source": "experimentOptions", - "target": "ExperimentOptions" - }, - { - "source": "experimentReportConfiguration", - "target": "ExperimentReportConfiguration" - }, - { - "source": "logConfiguration", - "target": "LogConfiguration" - }, { "source": "roleArn", "target": "RoleArn" - }, - { - "source": "stopConditions", - "target": "StopConditions" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "targets", - "target": "Targets" } ], "operation": "CreateExperimentTemplate", @@ -20396,6 +20395,9 @@ }, { "cfn_type": "AWS::FIS::TargetAccountConfiguration", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "accountId", @@ -20457,18 +20459,6 @@ "phase": "delete", "service": "fms" }, - { - "cfn_type": "AWS::FMS::Policy", - "mappings": [ - { - "source": "Policy", - "target": "PolicyName" - } - ], - "operation": "PutPolicy", - "phase": "create", - "service": "fms" - }, { "cfn_type": "AWS::FMS::Policy", "mappings": [ @@ -20490,6 +20480,9 @@ }, { "cfn_type": "AWS::FSx::DataRepositoryAssociation", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "BatchImportMetaDataOnCreate", @@ -20510,14 +20503,6 @@ { "source": "ImportedFileChunkSize", "target": "ImportedFileChunkSize" - }, - { - "source": "S3", - "target": "S3" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDataRepositoryAssociation", @@ -20526,6 +20511,9 @@ }, { "cfn_type": "AWS::FSx::DataRepositoryAssociation", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [], "operation": "DeleteDataRepositoryAssociation", "phase": "delete", @@ -20533,19 +20521,14 @@ }, { "cfn_type": "AWS::FSx::S3AccessPointAttachment", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "Name", "target": "Name" }, - { - "source": "OpenZFSConfiguration", - "target": "OpenZFSConfiguration" - }, - { - "source": "S3AccessPoint", - "target": "S3AccessPoint" - }, { "source": "Type", "target": "Type" @@ -20570,10 +20553,6 @@ "source": "federationMode", "target": "FederationMode" }, - { - "source": "federationParameters", - "target": "FederationParameters" - }, { "source": "kmsKeyId", "target": "KmsKeyId" @@ -20582,10 +20561,6 @@ "source": "name", "target": "Name" }, - { - "source": "superuserParameters", - "target": "SuperuserParameters" - }, { "source": "tags", "target": "Tags" @@ -20620,18 +20595,6 @@ { "source": "Domain", "target": "Domain" - }, - { - "source": "EncryptionConfig", - "target": "EncryptionConfig" - }, - { - "source": "Schema", - "target": "Schema" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDataset", @@ -20659,10 +20622,6 @@ { "source": "Domain", "target": "Domain" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDatasetGroup", @@ -20686,10 +20645,6 @@ { "source": "detectorId", "target": "DetectorId" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "PutDetector", @@ -20718,10 +20673,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "PutEntityType", @@ -20747,25 +20698,9 @@ "source": "description", "target": "Description" }, - { - "source": "entityTypes", - "target": "EntityTypes" - }, - { - "source": "eventVariables", - "target": "EventVariables" - }, - { - "source": "labels", - "target": "Labels" - }, { "source": "name", "target": "Name" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "PutEventType", @@ -20794,10 +20729,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "PutLabel", @@ -20831,10 +20762,6 @@ "source": "name", "target": "Name" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "variableType", "target": "VariableType" @@ -20866,10 +20793,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "PutOutcome", @@ -20911,10 +20834,6 @@ "source": "name", "target": "Name" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "variableType", "target": "VariableType" @@ -20946,14 +20865,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "RoutingStrategy", - "target": "RoutingStrategy" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAlias", @@ -20982,14 +20893,6 @@ "source": "ServerSdkVersion", "target": "ServerSdkVersion" }, - { - "source": "StorageLocation", - "target": "StorageLocation" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Version", "target": "Version" @@ -21029,30 +20932,10 @@ "source": "GameServerContainerGroupsPerInstance", "target": "GameServerContainerGroupsPerInstance" }, - { - "source": "GameSessionCreationLimitPolicy", - "target": "GameSessionCreationLimitPolicy" - }, - { - "source": "InstanceConnectionPortRange", - "target": "InstanceConnectionPortRange" - }, - { - "source": "InstanceInboundPermissions", - "target": "InstanceInboundPermissions" - }, { "source": "InstanceType", "target": "InstanceType" }, - { - "source": "Locations", - "target": "Locations" - }, - { - "source": "LogConfiguration", - "target": "LogConfiguration" - }, { "source": "MetricGroups", "target": "MetricGroups" @@ -21066,8 +20949,8 @@ "target": "PerInstanceContainerGroupDefinitionName" }, { - "source": "Tags", - "target": "Tags" + "source": "PlayerGatewayMode", + "target": "PlayerGatewayMode" } ], "operation": "CreateContainerFleet", @@ -21088,10 +20971,6 @@ "source": "ContainerGroupType", "target": "ContainerGroupType" }, - { - "source": "GameServerContainerDefinition", - "target": "GameServerContainerDefinition" - }, { "source": "Name", "target": "Name" @@ -21100,14 +20979,6 @@ "source": "OperatingSystem", "target": "OperatingSystem" }, - { - "source": "SupportContainerDefinitions", - "target": "SupportContainerDefinitions" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TotalMemoryLimitMebibytes", "target": "TotalMemoryLimitMebibytes" @@ -21140,18 +21011,10 @@ { "cfn_type": "AWS::GameLift::Fleet", "mappings": [ - { - "source": "AnywhereConfiguration", - "target": "AnywhereConfiguration" - }, { "source": "BuildId", "target": "BuildId" }, - { - "source": "CertificateConfiguration", - "target": "CertificateConfiguration" - }, { "source": "ComputeType", "target": "ComputeType" @@ -21160,10 +21023,6 @@ "source": "Description", "target": "Description" }, - { - "source": "EC2InboundPermissions", - "target": "EC2InboundPermissions" - }, { "source": "EC2InstanceType", "target": "EC2InstanceType" @@ -21180,10 +21039,6 @@ "source": "InstanceRoleCredentialsProvider", "target": "InstanceRoleCredentialsProvider" }, - { - "source": "Locations", - "target": "Locations" - }, { "source": "LogPaths", "target": "LogPaths" @@ -21209,12 +21064,8 @@ "target": "PeerVpcId" }, { - "source": "ResourceCreationLimitPolicy", - "target": "ResourceCreationLimitPolicy" - }, - { - "source": "RuntimeConfiguration", - "target": "RuntimeConfiguration" + "source": "PlayerGatewayMode", + "target": "PlayerGatewayMode" }, { "source": "ScriptId", @@ -21227,10 +21078,6 @@ { "source": "ServerLaunchPath", "target": "ServerLaunchPath" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateFleet", @@ -21247,10 +21094,6 @@ { "cfn_type": "AWS::GameLift::GameServerGroup", "mappings": [ - { - "source": "AutoScalingPolicy", - "target": "AutoScalingPolicy" - }, { "source": "BalancingStrategy", "target": "BalancingStrategy" @@ -21263,14 +21106,6 @@ "source": "GameServerProtectionPolicy", "target": "GameServerProtectionPolicy" }, - { - "source": "InstanceDefinitions", - "target": "InstanceDefinitions" - }, - { - "source": "LaunchTemplate", - "target": "LaunchTemplate" - }, { "source": "MaxSize", "target": "MaxSize" @@ -21283,10 +21118,6 @@ "source": "RoleArn", "target": "RoleArn" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VpcSubnets", "target": "VpcSubnets" @@ -21319,14 +21150,6 @@ "source": "CustomEventData", "target": "CustomEventData" }, - { - "source": "Destinations", - "target": "Destinations" - }, - { - "source": "FilterConfiguration", - "target": "FilterConfiguration" - }, { "source": "Name", "target": "Name" @@ -21335,18 +21158,6 @@ "source": "NotificationTarget", "target": "NotificationTarget" }, - { - "source": "PlayerLatencyPolicies", - "target": "PlayerLatencyPolicies" - }, - { - "source": "PriorityConfiguration", - "target": "PriorityConfiguration" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TimeoutInSeconds", "target": "TimeoutInSeconds" @@ -21374,10 +21185,6 @@ { "source": "LocationName", "target": "LocationName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateLocation", @@ -21427,10 +21234,6 @@ "source": "FlexMatchMode", "target": "FlexMatchMode" }, - { - "source": "GameProperties", - "target": "GameProperties" - }, { "source": "GameSessionData", "target": "GameSessionData" @@ -21454,10 +21257,6 @@ { "source": "RuleSetName", "target": "RuleSetName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateMatchmakingConfiguration", @@ -21486,10 +21285,6 @@ { "source": "RuleSetBody", "target": "RuleSetBody" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateMatchmakingRuleSet", @@ -21516,12 +21311,8 @@ "target": "Name" }, { - "source": "StorageLocation", - "target": "StorageLocation" - }, - { - "source": "Tags", - "target": "Tags" + "source": "NodeJsVersion", + "target": "NodeJsVersion" }, { "source": "Version", @@ -21541,6 +21332,9 @@ }, { "cfn_type": "AWS::GameLiftStreams::Application", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ApplicationLogOutputUri", @@ -21561,14 +21355,6 @@ { "source": "ExecutablePath", "target": "ExecutablePath" - }, - { - "source": "RuntimeEnvironment", - "target": "RuntimeEnvironment" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateApplication", @@ -21584,22 +21370,17 @@ }, { "cfn_type": "AWS::GameLiftStreams::StreamGroup", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", "target": "Description" }, - { - "source": "LocationConfigurations", - "target": "LocationConfigurations" - }, { "source": "StreamClass", "target": "StreamClass" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateStreamGroup", @@ -21615,6 +21396,9 @@ }, { "cfn_type": "AWS::GlobalAccelerator::Accelerator", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "Enabled", @@ -21631,10 +21415,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAccelerator", @@ -21650,6 +21430,9 @@ }, { "cfn_type": "AWS::GlobalAccelerator::CrossAccountAttachment", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "Name", @@ -21658,14 +21441,6 @@ { "source": "Principals", "target": "Principals" - }, - { - "source": "Resources", - "target": "Resources" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateCrossAccountAttachment", @@ -21681,11 +21456,10 @@ }, { "cfn_type": "AWS::GlobalAccelerator::EndpointGroup", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ - { - "source": "EndpointConfigurations", - "target": "EndpointConfigurations" - }, { "source": "EndpointGroupRegion", "target": "EndpointGroupRegion" @@ -21710,10 +21484,6 @@ "source": "ListenerArn", "target": "ListenerArn" }, - { - "source": "PortOverrides", - "target": "PortOverrides" - }, { "source": "ThresholdCount", "target": "ThresholdCount" @@ -21736,6 +21506,9 @@ }, { "cfn_type": "AWS::GlobalAccelerator::Listener", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "AcceleratorArn", @@ -21745,10 +21518,6 @@ "source": "ClientAffinity", "target": "ClientAffinity" }, - { - "source": "PortRanges", - "target": "PortRanges" - }, { "source": "Protocol", "target": "Protocol" @@ -21824,30 +21593,6 @@ "phase": "delete", "service": "glue" }, - { - "cfn_type": "AWS::Glue::Classifier", - "mappings": [ - { - "source": "CsvClassifier", - "target": "CsvClassifier" - }, - { - "source": "GrokClassifier", - "target": "GrokClassifier" - }, - { - "source": "JsonClassifier", - "target": "JsonClassifier" - }, - { - "source": "XMLClassifier", - "target": "XMLClassifier" - } - ], - "operation": "CreateClassifier", - "phase": "create", - "service": "glue" - }, { "cfn_type": "AWS::Glue::Classifier", "mappings": [], @@ -21878,41 +21623,17 @@ "source": "Description", "target": "Description" }, - { - "source": "LakeFormationConfiguration", - "target": "LakeFormationConfiguration" - }, { "source": "Name", "target": "Name" }, - { - "source": "RecrawlPolicy", - "target": "RecrawlPolicy" - }, { "source": "Role", "target": "Role" }, - { - "source": "Schedule", - "target": "Schedule" - }, - { - "source": "SchemaChangePolicy", - "target": "SchemaChangePolicy" - }, { "source": "TablePrefix", "target": "TablePrefix" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "Targets", - "target": "Targets" } ], "operation": "CreateCrawler", @@ -21945,10 +21666,6 @@ { "source": "RegexString", "target": "RegexString" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateCustomEntityType", @@ -21973,10 +21690,6 @@ { "source": "CatalogId", "target": "CatalogId" - }, - { - "source": "DataCatalogEncryptionSettings", - "target": "DataCatalogEncryptionSettings" } ], "operation": "PutDataCatalogEncryptionSettings", @@ -21985,6 +21698,9 @@ }, { "cfn_type": "AWS::Glue::DataQualityRuleset", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ClientToken", @@ -22001,14 +21717,6 @@ { "source": "Ruleset", "target": "Ruleset" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "TargetTable", - "target": "TargetTable" } ], "operation": "CreateDataQualityRuleset", @@ -22033,10 +21741,6 @@ { "source": "CatalogId", "target": "CatalogId" - }, - { - "source": "DatabaseInput", - "target": "DatabaseInput" } ], "operation": "CreateDatabase", @@ -22060,12 +21764,35 @@ "service": "glue" }, { - "cfn_type": "AWS::Glue::Integration", + "cfn_type": "AWS::Glue::IdentityCenterConfiguration", "mappings": [ { - "source": "AdditionalEncryptionContext", - "target": "AdditionalEncryptionContext" + "source": "InstanceArn", + "target": "InstanceArn" + }, + { + "source": "Scopes", + "target": "Scopes" }, + { + "source": "UserBackgroundSessionsEnabled", + "target": "UserBackgroundSessionsEnabled" + } + ], + "operation": "CreateGlueIdentityCenterConfiguration", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::IdentityCenterConfiguration", + "mappings": [], + "operation": "DeleteGlueIdentityCenterConfiguration", + "phase": "delete", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Integration", + "mappings": [ { "source": "DataFilter", "target": "DataFilter" @@ -22074,10 +21801,6 @@ "source": "Description", "target": "Description" }, - { - "source": "IntegrationConfig", - "target": "IntegrationConfig" - }, { "source": "IntegrationName", "target": "IntegrationName" @@ -22090,10 +21813,6 @@ "source": "SourceArn", "target": "SourceArn" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TargetArn", "target": "TargetArn" @@ -22116,20 +21835,24 @@ { "source": "ResourceArn", "target": "ResourceArn" - }, - { - "source": "SourceProcessingProperties", - "target": "SourceProcessingProperties" - }, - { - "source": "TargetProcessingProperties", - "target": "TargetProcessingProperties" } ], "operation": "CreateIntegrationResourceProperty", "phase": "create", "service": "glue" }, + { + "cfn_type": "AWS::Glue::IntegrationResourceProperty", + "mappings": [ + { + "source": "ResourceArn", + "target": "ResourceArn" + } + ], + "operation": "DeleteIntegrationResourceProperty", + "phase": "delete", + "service": "glue" + }, { "cfn_type": "AWS::Glue::Job", "mappings": [ @@ -22137,18 +21860,6 @@ "source": "AllocatedCapacity", "target": "AllocatedCapacity" }, - { - "source": "Command", - "target": "Command" - }, - { - "source": "Connections", - "target": "Connections" - }, - { - "source": "DefaultArguments", - "target": "DefaultArguments" - }, { "source": "Description", "target": "Description" @@ -22157,10 +21868,6 @@ "source": "ExecutionClass", "target": "ExecutionClass" }, - { - "source": "ExecutionProperty", - "target": "ExecutionProperty" - }, { "source": "GlueVersion", "target": "GlueVersion" @@ -22193,14 +21900,6 @@ "source": "Name", "target": "Name" }, - { - "source": "NonOverridableArguments", - "target": "NonOverridableArguments" - }, - { - "source": "NotificationProperty", - "target": "NotificationProperty" - }, { "source": "NumberOfWorkers", "target": "NumberOfWorkers" @@ -22213,10 +21912,6 @@ "source": "SecurityConfiguration", "target": "SecurityConfiguration" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Timeout", "target": "Timeout" @@ -22248,10 +21943,6 @@ "source": "GlueVersion", "target": "GlueVersion" }, - { - "source": "InputRecordTables", - "target": "InputRecordTables" - }, { "source": "MaxCapacity", "target": "MaxCapacity" @@ -22272,18 +21963,10 @@ "source": "Role", "target": "Role" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Timeout", "target": "Timeout" }, - { - "source": "TransformEncryption", - "target": "TransformEncryption" - }, { "source": "WorkerType", "target": "WorkerType" @@ -22404,10 +22087,6 @@ { "cfn_type": "AWS::Glue::SecurityConfiguration", "mappings": [ - { - "source": "EncryptionConfiguration", - "target": "EncryptionConfiguration" - }, { "source": "Name", "target": "Name" @@ -22444,10 +22123,6 @@ "source": "TableName", "target": "TableName" }, - { - "source": "TableOptimizerConfiguration", - "target": "TableOptimizerConfiguration" - }, { "source": "Type", "target": "Type" @@ -22484,26 +22159,14 @@ { "cfn_type": "AWS::Glue::Trigger", "mappings": [ - { - "source": "Actions", - "target": "Actions" - }, { "source": "Description", "target": "Description" }, - { - "source": "EventBatchingCondition", - "target": "EventBatchingCondition" - }, { "source": "Name", "target": "Name" }, - { - "source": "Predicate", - "target": "Predicate" - }, { "source": "Schedule", "target": "Schedule" @@ -22512,10 +22175,6 @@ "source": "StartOnCreation", "target": "StartOnCreation" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" @@ -22544,10 +22203,6 @@ { "cfn_type": "AWS::Glue::UsageProfile", "mappings": [ - { - "source": "Configuration", - "target": "Configuration" - }, { "source": "Description", "target": "Description" @@ -22608,10 +22263,6 @@ { "cfn_type": "AWS::Glue::Workflow", "mappings": [ - { - "source": "DefaultRunProperties", - "target": "DefaultRunProperties" - }, { "source": "Description", "target": "Description" @@ -22623,10 +22274,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateWorkflow", @@ -22647,6 +22294,9 @@ }, { "cfn_type": "AWS::Grafana::Workspace", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "accountAccessType", @@ -22664,10 +22314,6 @@ "source": "grafanaVersion", "target": "GrafanaVersion" }, - { - "source": "networkAccessControl", - "target": "NetworkAccessControl" - }, { "source": "organizationRoleName", "target": "OrganizationRoleName" @@ -22683,10 +22329,6 @@ { "source": "tags", "target": "Tags" - }, - { - "source": "vpcConfiguration", - "target": "VpcConfiguration" } ], "operation": "CreateWorkspace", @@ -22700,53 +22342,20 @@ "phase": "delete", "service": "grafana" }, - { - "cfn_type": "AWS::GreengrassV2::ComponentVersion", - "mappings": [ - { - "source": "inlineRecipe", - "target": "InlineRecipe" - }, - { - "source": "lambdaFunction", - "target": "LambdaFunction" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreateComponentVersion", - "phase": "create", - "service": "greengrassv2" - }, { "cfn_type": "AWS::GreengrassV2::Deployment", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "components", - "target": "Components" - }, { "source": "deploymentName", "target": "DeploymentName" }, - { - "source": "deploymentPolicies", - "target": "DeploymentPolicies" - }, - { - "source": "iotJobConfiguration", - "target": "IotJobConfiguration" - }, { "source": "parentTargetArn", "target": "ParentTargetArn" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "targetArn", "target": "TargetArn" @@ -22756,13 +22365,16 @@ "phase": "create", "service": "greengrassv2" }, + { + "cfn_type": "AWS::GreengrassV2::Deployment", + "mappings": [], + "operation": "DeleteDeployment", + "phase": "delete", + "service": "greengrassv2" + }, { "cfn_type": "AWS::GroundStation::Config", "mappings": [ - { - "source": "configData", - "target": "ConfigData" - }, { "source": "name", "target": "Name" @@ -22794,10 +22406,6 @@ "source": "contactPrePassDurationSeconds", "target": "ContactPrePassDurationSeconds" }, - { - "source": "endpointDetails", - "target": "EndpointDetails" - }, { "source": "tags", "target": "Tags" @@ -22815,7 +22423,7 @@ "service": "groundstation" }, { - "cfn_type": "AWS::GroundStation::MissionProfile", + "cfn_type": "AWS::GroundStation::DataflowEndpointGroupV2", "mappings": [ { "source": "contactPostPassDurationSeconds", @@ -22826,8 +22434,24 @@ "target": "ContactPrePassDurationSeconds" }, { - "source": "dataflowEdges", - "target": "DataflowEdges" + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDataflowEndpointGroupV2", + "phase": "create", + "service": "groundstation" + }, + { + "cfn_type": "AWS::GroundStation::MissionProfile", + "mappings": [ + { + "source": "contactPostPassDurationSeconds", + "target": "ContactPostPassDurationSeconds" + }, + { + "source": "contactPrePassDurationSeconds", + "target": "ContactPrePassDurationSeconds" }, { "source": "minimumViableContactDurationSeconds", @@ -22837,10 +22461,6 @@ "source": "name", "target": "Name" }, - { - "source": "streamsKmsKey", - "target": "StreamsKmsKey" - }, { "source": "streamsKmsRole", "target": "StreamsKmsRole" @@ -22849,6 +22469,10 @@ "source": "tags", "target": "Tags" }, + { + "source": "telemetrySinkConfigArn", + "target": "TelemetrySinkConfigArn" + }, { "source": "trackingConfigArn", "target": "TrackingConfigArn" @@ -22867,19 +22491,14 @@ }, { "cfn_type": "AWS::GuardDuty::Detector", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "DataSources", - "target": "DataSources" - }, { "source": "Enable", "target": "Enable" }, - { - "source": "Features", - "target": "Features" - }, { "source": "FindingPublishingFrequency", "target": "FindingPublishingFrequency" @@ -22902,6 +22521,9 @@ }, { "cfn_type": "AWS::GuardDuty::Filter", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Action", @@ -22915,10 +22537,6 @@ "source": "DetectorId", "target": "DetectorId" }, - { - "source": "FindingCriteria", - "target": "FindingCriteria" - }, { "source": "Name", "target": "Name" @@ -22950,6 +22568,9 @@ }, { "cfn_type": "AWS::GuardDuty::IPSet", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Activate", @@ -22998,15 +22619,10 @@ }, { "cfn_type": "AWS::GuardDuty::MalwareProtectionPlan", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "Actions", - "target": "Actions" - }, - { - "source": "ProtectedResource", - "target": "ProtectedResource" - }, { "source": "Role", "target": "Role" @@ -23040,12 +22656,23 @@ "service": "guardduty" }, { - "cfn_type": "AWS::GuardDuty::PublishingDestination", + "cfn_type": "AWS::GuardDuty::Member", "mappings": [ { - "source": "DestinationProperties", - "target": "DestinationProperties" - }, + "source": "DetectorId", + "target": "DetectorId" + } + ], + "operation": "DeleteMembers", + "phase": "delete", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::PublishingDestination", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ { "source": "DestinationType", "target": "DestinationType" @@ -23053,6 +22680,10 @@ { "source": "DetectorId", "target": "DetectorId" + }, + { + "source": "Tags", + "target": "Tags" } ], "operation": "CreatePublishingDestination", @@ -23071,8 +22702,62 @@ "phase": "delete", "service": "guardduty" }, + { + "cfn_type": "AWS::GuardDuty::ThreatEntitySet", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "Activate", + "target": "Activate" + }, + { + "source": "DetectorId", + "target": "DetectorId" + }, + { + "source": "ExpectedBucketOwner", + "target": "ExpectedBucketOwner" + }, + { + "source": "Format", + "target": "Format" + }, + { + "source": "Location", + "target": "Location" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateThreatEntitySet", + "phase": "create", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::ThreatEntitySet", + "mappings": [ + { + "source": "DetectorId", + "target": "DetectorId" + } + ], + "operation": "DeleteThreatEntitySet", + "phase": "delete", + "service": "guardduty" + }, { "cfn_type": "AWS::GuardDuty::ThreatIntelSet", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Activate", @@ -23120,33 +22805,109 @@ "service": "guardduty" }, { - "cfn_type": "AWS::HealthLake::FHIRDatastore", + "cfn_type": "AWS::GuardDuty::TrustedEntitySet", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { - "source": "DatastoreName", - "target": "DatastoreName" + "source": "Activate", + "target": "Activate" }, { - "source": "DatastoreTypeVersion", - "target": "DatastoreTypeVersion" + "source": "DetectorId", + "target": "DetectorId" }, { - "source": "IdentityProviderConfiguration", - "target": "IdentityProviderConfiguration" + "source": "ExpectedBucketOwner", + "target": "ExpectedBucketOwner" }, { - "source": "PreloadDataConfig", - "target": "PreloadDataConfig" + "source": "Format", + "target": "Format" + }, + { + "source": "Location", + "target": "Location" }, { - "source": "SseConfiguration", - "target": "SseConfiguration" + "source": "Name", + "target": "Name" }, { "source": "Tags", "target": "Tags" } ], + "operation": "CreateTrustedEntitySet", + "phase": "create", + "service": "guardduty" + }, + { + "cfn_type": "AWS::GuardDuty::TrustedEntitySet", + "mappings": [ + { + "source": "DetectorId", + "target": "DetectorId" + } + ], + "operation": "DeleteTrustedEntitySet", + "phase": "delete", + "service": "guardduty" + }, + { + "cfn_type": "AWS::HealthLake::DataTransformationProfile", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "ProfileDescription", + "target": "ProfileDescription" + }, + { + "source": "ProfileName", + "target": "ProfileName" + }, + { + "source": "SourceFormat", + "target": "SourceFormat" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDataTransformationProfile", + "phase": "create", + "service": "healthlake" + }, + { + "cfn_type": "AWS::HealthLake::DataTransformationProfile", + "mappings": [], + "operation": "DeleteDataTransformationProfile", + "phase": "delete", + "service": "healthlake" + }, + { + "cfn_type": "AWS::HealthLake::FHIRDatastore", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "DatastoreName", + "target": "DatastoreName" + }, + { + "source": "DatastoreTypeVersion", + "target": "DatastoreTypeVersion" + } + ], "operation": "CreateFHIRDatastore", "phase": "create", "service": "healthlake" @@ -23260,10 +23021,6 @@ { "source": "Path", "target": "Path" - }, - { - "source": "PolicyDocument", - "target": "PolicyDocument" } ], "operation": "CreatePolicy", @@ -23277,10 +23034,6 @@ "source": "ClientIDList", "target": "ClientIdList" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "ThumbprintList", "target": "ThumbprintList" @@ -23320,10 +23073,6 @@ { "source": "RoleName", "target": "RoleName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateRole", @@ -23396,10 +23145,6 @@ { "source": "SAMLMetadataDocument", "target": "SamlMetadataDocument" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateSAMLProvider", @@ -23463,10 +23208,6 @@ "source": "PermissionsBoundary", "target": "PermissionsBoundary" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UserName", "target": "UserName" @@ -23531,10 +23272,6 @@ "source": "Path", "target": "Path" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VirtualMFADeviceName", "target": "VirtualMfaDeviceName" @@ -23570,10 +23307,6 @@ "source": "latencyMode", "target": "LatencyMode" }, - { - "source": "multitrackInputConfiguration", - "target": "MultitrackInputConfiguration" - }, { "source": "name", "target": "Name" @@ -23616,10 +23349,6 @@ { "source": "tags", "target": "Tags" - }, - { - "source": "video", - "target": "Video" } ], "operation": "CreateEncoderConfiguration", @@ -23764,10 +23493,6 @@ { "cfn_type": "AWS::IVS::RecordingConfiguration", "mappings": [ - { - "source": "destinationConfiguration", - "target": "DestinationConfiguration" - }, { "source": "name", "target": "Name" @@ -23776,17 +23501,9 @@ "source": "recordingReconnectWindowSeconds", "target": "RecordingReconnectWindowSeconds" }, - { - "source": "renditionConfiguration", - "target": "RenditionConfiguration" - }, { "source": "tags", "target": "Tags" - }, - { - "source": "thumbnailConfiguration", - "target": "ThumbnailConfiguration" } ], "operation": "CreateRecordingConfiguration", @@ -23803,10 +23520,6 @@ { "cfn_type": "AWS::IVS::Stage", "mappings": [ - { - "source": "autoParticipantRecordingConfiguration", - "target": "AutoParticipantRecordingConfiguration" - }, { "source": "name", "target": "Name" @@ -23834,10 +23547,6 @@ "source": "name", "target": "Name" }, - { - "source": "s3", - "target": "S3" - }, { "source": "tags", "target": "Tags" @@ -23880,10 +23589,6 @@ { "cfn_type": "AWS::IVSChat::LoggingConfiguration", "mappings": [ - { - "source": "destinationConfiguration", - "target": "DestinationConfiguration" - }, { "source": "name", "target": "Name" @@ -23919,10 +23624,6 @@ "source": "maximumMessageRatePerSecond", "target": "MaximumMessageRatePerSecond" }, - { - "source": "messageReviewHandler", - "target": "MessageReviewHandler" - }, { "source": "name", "target": "Name" @@ -23985,10 +23686,6 @@ { "source": "IdentityStoreId", "target": "IdentityStoreId" - }, - { - "source": "MemberId", - "target": "MemberId" } ], "operation": "CreateGroupMembership", @@ -24009,6 +23706,9 @@ }, { "cfn_type": "AWS::ImageBuilder::Component", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "changeDescription", @@ -24038,10 +23738,6 @@ "source": "supportedOsVersions", "target": "SupportedOsVersions" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "uri", "target": "Uri" @@ -24060,11 +23756,10 @@ }, { "cfn_type": "AWS::ImageBuilder::ContainerRecipe", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "components", - "target": "Components" - }, { "source": "containerType", "target": "ContainerType" @@ -24085,10 +23780,6 @@ "source": "imageOsVersionOverride", "target": "ImageOsVersionOverride" }, - { - "source": "instanceConfiguration", - "target": "InstanceConfiguration" - }, { "source": "kmsKeyId", "target": "KmsKeyId" @@ -24105,14 +23796,6 @@ "source": "platformOverride", "target": "PlatformOverride" }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "targetRepository", - "target": "TargetRepository" - }, { "source": "workingDirectory", "target": "WorkingDirectory" @@ -24131,22 +23814,17 @@ }, { "cfn_type": "AWS::ImageBuilder::DistributionConfiguration", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", "target": "Description" }, - { - "source": "distributions", - "target": "Distributions" - }, { "source": "name", "target": "Name" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateDistributionConfiguration", @@ -24162,6 +23840,9 @@ }, { "cfn_type": "AWS::ImageBuilder::Image", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "containerRecipeArn", @@ -24183,25 +23864,9 @@ "source": "imageRecipeArn", "target": "ImageRecipeArn" }, - { - "source": "imageScanningConfiguration", - "target": "ImageScanningConfiguration" - }, - { - "source": "imageTestsConfiguration", - "target": "ImageTestsConfiguration" - }, { "source": "infrastructureConfigurationArn", "target": "InfrastructureConfigurationArn" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "workflows", - "target": "Workflows" } ], "operation": "CreateImage", @@ -24217,6 +23882,9 @@ }, { "cfn_type": "AWS::ImageBuilder::ImagePipeline", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "containerRecipeArn", @@ -24242,14 +23910,6 @@ "source": "imageRecipeArn", "target": "ImageRecipeArn" }, - { - "source": "imageScanningConfiguration", - "target": "ImageScanningConfiguration" - }, - { - "source": "imageTestsConfiguration", - "target": "ImageTestsConfiguration" - }, { "source": "infrastructureConfigurationArn", "target": "InfrastructureConfigurationArn" @@ -24258,21 +23918,9 @@ "source": "name", "target": "Name" }, - { - "source": "schedule", - "target": "Schedule" - }, { "source": "status", "target": "Status" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "workflows", - "target": "Workflows" } ], "operation": "CreateImagePipeline", @@ -24288,18 +23936,13 @@ }, { "cfn_type": "AWS::ImageBuilder::ImageRecipe", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "additionalInstanceConfiguration", - "target": "AdditionalInstanceConfiguration" - }, - { - "source": "blockDeviceMappings", - "target": "BlockDeviceMappings" - }, - { - "source": "components", - "target": "Components" + "source": "amiWatermarks", + "target": "AmiWatermarks" }, { "source": "description", @@ -24313,10 +23956,6 @@ "source": "parentImage", "target": "ParentImage" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "workingDirectory", "target": "WorkingDirectory" @@ -24335,15 +23974,14 @@ }, { "cfn_type": "AWS::ImageBuilder::InfrastructureConfiguration", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", "target": "Description" }, - { - "source": "instanceMetadataOptions", - "target": "InstanceMetadataOptions" - }, { "source": "instanceProfileName", "target": "InstanceProfileName" @@ -24356,22 +23994,10 @@ "source": "keyPair", "target": "KeyPair" }, - { - "source": "logging", - "target": "Logging" - }, { "source": "name", "target": "Name" }, - { - "source": "placement", - "target": "Placement" - }, - { - "source": "resourceTags", - "target": "ResourceTags" - }, { "source": "securityGroupIds", "target": "SecurityGroupIds" @@ -24384,10 +24010,6 @@ "source": "subnetId", "target": "SubnetId" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "terminateInstanceOnFailure", "target": "TerminateInstanceOnFailure" @@ -24406,6 +24028,9 @@ }, { "cfn_type": "AWS::ImageBuilder::LifecyclePolicy", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -24419,14 +24044,6 @@ "source": "name", "target": "Name" }, - { - "source": "policyDetails", - "target": "PolicyDetails" - }, - { - "source": "resourceSelection", - "target": "ResourceSelection" - }, { "source": "resourceType", "target": "ResourceType" @@ -24434,10 +24051,6 @@ { "source": "status", "target": "Status" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateLifecyclePolicy", @@ -24453,6 +24066,9 @@ }, { "cfn_type": "AWS::ImageBuilder::Workflow", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "changeDescription", @@ -24474,10 +24090,6 @@ "source": "name", "target": "Name" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "type", "target": "Type" @@ -24539,10 +24151,6 @@ { "source": "rulesPackageArns", "target": "RulesPackageArns" - }, - { - "source": "userAttributesForFindings", - "target": "UserAttributesForFindings" } ], "operation": "CreateAssessmentTemplate", @@ -24557,28 +24165,48 @@ "service": "inspector" }, { - "cfn_type": "AWS::Inspector::ResourceGroup", + "cfn_type": "AWS::Interconnect::Connection", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "resourceGroupTags", - "target": "ResourceGroupTags" + "source": "bandwidth", + "target": "Bandwidth" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "environmentId", + "target": "EnvironmentId" + }, + { + "source": "tags", + "target": "Tags" } ], - "operation": "CreateResourceGroup", + "operation": "CreateConnection", "phase": "create", - "service": "inspector" + "service": "interconnect" + }, + { + "cfn_type": "AWS::Interconnect::Connection", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [], + "operation": "DeleteConnection", + "phase": "delete", + "service": "interconnect" }, { "cfn_type": "AWS::InternetMonitor::Monitor", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "HealthEventsConfig", - "target": "HealthEventsConfig" - }, - { - "source": "InternetMeasurementsLogDelivery", - "target": "InternetMeasurementsLogDelivery" - }, { "source": "MaxCityNetworksToMonitor", "target": "MaxCityNetworksToMonitor" @@ -24618,6 +24246,9 @@ }, { "cfn_type": "AWS::Invoicing::InvoiceUnit", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -24631,14 +24262,6 @@ "source": "Name", "target": "Name" }, - { - "source": "ResourceTags", - "target": "ResourceTags" - }, - { - "source": "Rule", - "target": "Rule" - }, { "source": "TaxInheritanceDisabled", "target": "TaxInheritanceDisabled" @@ -24650,6 +24273,9 @@ }, { "cfn_type": "AWS::Invoicing::InvoiceUnit", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [], "operation": "DeleteInvoiceUnit", "phase": "delete", @@ -24685,17 +24311,9 @@ "source": "status", "target": "Status" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "tokenKeyName", "target": "TokenKeyName" - }, - { - "source": "tokenSigningPublicKeys", - "target": "TokenSigningPublicKeys" } ], "operation": "CreateAuthorizer", @@ -24720,14 +24338,6 @@ { "source": "billingGroupName", "target": "BillingGroupName" - }, - { - "source": "billingGroupProperties", - "target": "BillingGroupProperties" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateBillingGroup", @@ -24752,14 +24362,6 @@ { "source": "certificateMode", "target": "CertificateMode" - }, - { - "source": "registrationConfig", - "target": "RegistrationConfig" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "RegisterCACertificate", @@ -24802,6 +24404,9 @@ }, { "cfn_type": "AWS::IoT::CertificateProvider", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "accountDefaultForOperations", @@ -24814,10 +24419,6 @@ { "source": "lambdaFunctionArn", "target": "LambdaFunctionArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateCertificateProvider", @@ -24851,25 +24452,17 @@ "source": "displayName", "target": "DisplayName" }, - { - "source": "mandatoryParameters", - "target": "MandatoryParameters" - }, { "source": "namespace", "target": "Namespace" }, { - "source": "payload", - "target": "Payload" + "source": "payloadTemplate", + "target": "PayloadTemplate" }, { "source": "roleArn", "target": "RoleArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateCommand", @@ -24890,6 +24483,9 @@ }, { "cfn_type": "AWS::IoT::CustomMetric", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "displayName", @@ -24902,10 +24498,6 @@ { "source": "metricType", "target": "MetricType" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateCustomMetric", @@ -24926,6 +24518,9 @@ }, { "cfn_type": "AWS::IoT::Dimension", + "ignored_inputs": [ + "clientRequestToken" + ], "mappings": [ { "source": "name", @@ -24935,10 +24530,6 @@ "source": "stringValues", "target": "StringValues" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "type", "target": "Type" @@ -24971,14 +24562,6 @@ "source": "authenticationType", "target": "AuthenticationType" }, - { - "source": "authorizerConfig", - "target": "AuthorizerConfig" - }, - { - "source": "clientCertificateConfig", - "target": "ClientCertificateConfig" - }, { "source": "domainConfigurationName", "target": "DomainConfigurationName" @@ -24991,22 +24574,10 @@ "source": "serverCertificateArns", "target": "ServerCertificateArns" }, - { - "source": "serverCertificateConfig", - "target": "ServerCertificateConfig" - }, { "source": "serviceType", "target": "ServiceType" }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "tlsConfig", - "target": "TlsConfig" - }, { "source": "validationCertificateArn", "target": "ValidationCertificateArn" @@ -25035,10 +24606,6 @@ "source": "aggregationField", "target": "AggregationField" }, - { - "source": "aggregationType", - "target": "AggregationType" - }, { "source": "description", "target": "Description" @@ -25063,10 +24630,6 @@ "source": "queryVersion", "target": "QueryVersion" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "unit", "target": "Unit" @@ -25089,12 +24652,8 @@ "service": "iot" }, { - "cfn_type": "AWS::IoT::JobTemplate", + "cfn_type": "AWS::IoT::Job", "mappings": [ - { - "source": "abortConfig", - "target": "AbortConfig" - }, { "source": "description", "target": "Description" @@ -25112,36 +24671,64 @@ "target": "DocumentSource" }, { - "source": "jobArn", - "target": "JobArn" + "source": "jobId", + "target": "JobId" }, { - "source": "jobExecutionsRetryConfig", - "target": "JobExecutionsRetryConfig" + "source": "jobTemplateArn", + "target": "JobTemplateArn" }, { - "source": "jobExecutionsRolloutConfig", - "target": "JobExecutionsRolloutConfig" + "source": "targetSelection", + "target": "TargetSelection" }, { - "source": "jobTemplateId", - "target": "JobTemplateId" + "source": "targets", + "target": "Targets" + } + ], + "operation": "CreateJob", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Job", + "mappings": [ + { + "source": "jobId", + "target": "JobId" + } + ], + "operation": "DeleteJob", + "phase": "delete", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::JobTemplate", + "mappings": [ + { + "source": "description", + "target": "Description" }, { - "source": "maintenanceWindows", - "target": "MaintenanceWindows" + "source": "destinationPackageVersions", + "target": "DestinationPackageVersions" }, { - "source": "presignedUrlConfig", - "target": "PresignedUrlConfig" + "source": "document", + "target": "Document" }, { - "source": "tags", - "target": "Tags" + "source": "documentSource", + "target": "DocumentSource" }, { - "source": "timeoutConfig", - "target": "TimeoutConfig" + "source": "jobArn", + "target": "JobArn" + }, + { + "source": "jobTemplateId", + "target": "JobTemplateId" } ], "operation": "CreateJobTemplate", @@ -25183,17 +24770,9 @@ "source": "actionName", "target": "ActionName" }, - { - "source": "actionParams", - "target": "ActionParams" - }, { "source": "roleArn", "target": "RoleArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateMitigationAction", @@ -25222,10 +24801,6 @@ { "source": "policyName", "target": "PolicyName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreatePolicy", @@ -25255,18 +24830,10 @@ "source": "enabled", "target": "Enabled" }, - { - "source": "preProvisioningHook", - "target": "PreProvisioningHook" - }, { "source": "provisioningRoleArn", "target": "ProvisioningRoleArn" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "templateBody", "target": "TemplateBody" @@ -25322,10 +24889,6 @@ { "source": "roleArn", "target": "RoleArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateRoleAlias", @@ -25363,10 +24926,6 @@ "source": "scheduledAuditName", "target": "ScheduledAuditName" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "targetCheckNames", "target": "TargetCheckNames" @@ -25391,22 +24950,6 @@ { "cfn_type": "AWS::IoT::SecurityProfile", "mappings": [ - { - "source": "additionalMetricsToRetainV2", - "target": "AdditionalMetricsToRetainV2" - }, - { - "source": "alertTargets", - "target": "AlertTargets" - }, - { - "source": "behaviors", - "target": "Behaviors" - }, - { - "source": "metricsExportConfig", - "target": "MetricsExportConfig" - }, { "source": "securityProfileDescription", "target": "SecurityProfileDescription" @@ -25414,10 +24957,6 @@ { "source": "securityProfileName", "target": "SecurityProfileName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateSecurityProfile", @@ -25438,6 +24977,9 @@ }, { "cfn_type": "AWS::IoT::SoftwarePackage", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -25458,15 +25000,10 @@ }, { "cfn_type": "AWS::IoT::SoftwarePackageVersion", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "artifact", - "target": "Artifact" - }, - { - "source": "attributes", - "target": "Attributes" - }, { "source": "description", "target": "Description" @@ -25494,6 +25031,9 @@ }, { "cfn_type": "AWS::IoT::SoftwarePackageVersion", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "packageName", @@ -25511,10 +25051,6 @@ { "cfn_type": "AWS::IoT::Thing", "mappings": [ - { - "source": "attributePayload", - "target": "AttributePayload" - }, { "source": "thingName", "target": "ThingName" @@ -25543,17 +25079,9 @@ "source": "parentGroupName", "target": "ParentGroupName" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "thingGroupName", "target": "ThingGroupName" - }, - { - "source": "thingGroupProperties", - "target": "ThingGroupProperties" } ], "operation": "CreateThingGroup", @@ -25563,17 +25091,9 @@ { "cfn_type": "AWS::IoT::ThingType", "mappings": [ - { - "source": "tags", - "target": "Tags" - }, { "source": "thingTypeName", "target": "ThingTypeName" - }, - { - "source": "thingTypeProperties", - "target": "ThingTypeProperties" } ], "operation": "CreateThingType", @@ -25598,14 +25118,6 @@ { "source": "ruleName", "target": "RuleName" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "topicRulePayload", - "target": "TopicRulePayload" } ], "operation": "CreateTopicRule", @@ -25631,333 +25143,17 @@ "phase": "delete", "service": "iot" }, - { - "cfn_type": "AWS::IoTAnalytics::Channel", - "mappings": [ - { - "source": "channelName", - "target": "ChannelName" - }, - { - "source": "channelStorage", - "target": "ChannelStorage" - }, - { - "source": "retentionPeriod", - "target": "RetentionPeriod" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreateChannel", - "phase": "create", - "service": "iotanalytics" - }, - { - "cfn_type": "AWS::IoTAnalytics::Channel", - "mappings": [ - { - "source": "channelName", - "target": "ChannelName" - } - ], - "operation": "DeleteChannel", - "phase": "delete", - "service": "iotanalytics" - }, - { - "cfn_type": "AWS::IoTAnalytics::Dataset", - "mappings": [ - { - "source": "actions", - "target": "Actions" - }, - { - "source": "contentDeliveryRules", - "target": "ContentDeliveryRules" - }, - { - "source": "datasetName", - "target": "DatasetName" - }, - { - "source": "lateDataRules", - "target": "LateDataRules" - }, - { - "source": "retentionPeriod", - "target": "RetentionPeriod" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "triggers", - "target": "Triggers" - }, - { - "source": "versioningConfiguration", - "target": "VersioningConfiguration" - } - ], - "operation": "CreateDataset", - "phase": "create", - "service": "iotanalytics" - }, - { - "cfn_type": "AWS::IoTAnalytics::Dataset", - "mappings": [ - { - "source": "datasetName", - "target": "DatasetName" - } - ], - "operation": "DeleteDataset", - "phase": "delete", - "service": "iotanalytics" - }, - { - "cfn_type": "AWS::IoTAnalytics::Datastore", - "mappings": [ - { - "source": "datastoreName", - "target": "DatastoreName" - }, - { - "source": "datastorePartitions", - "target": "DatastorePartitions" - }, - { - "source": "datastoreStorage", - "target": "DatastoreStorage" - }, - { - "source": "fileFormatConfiguration", - "target": "FileFormatConfiguration" - }, - { - "source": "retentionPeriod", - "target": "RetentionPeriod" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreateDatastore", - "phase": "create", - "service": "iotanalytics" - }, - { - "cfn_type": "AWS::IoTAnalytics::Datastore", - "mappings": [ - { - "source": "datastoreName", - "target": "DatastoreName" - } - ], - "operation": "DeleteDatastore", - "phase": "delete", - "service": "iotanalytics" - }, - { - "cfn_type": "AWS::IoTAnalytics::Pipeline", - "mappings": [ - { - "source": "pipelineActivities", - "target": "PipelineActivities" - }, - { - "source": "pipelineName", - "target": "PipelineName" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreatePipeline", - "phase": "create", - "service": "iotanalytics" - }, - { - "cfn_type": "AWS::IoTAnalytics::Pipeline", - "mappings": [ - { - "source": "pipelineName", - "target": "PipelineName" - } - ], - "operation": "DeletePipeline", - "phase": "delete", - "service": "iotanalytics" - }, - { - "cfn_type": "AWS::IoTEvents::AlarmModel", - "mappings": [ - { - "source": "alarmCapabilities", - "target": "AlarmCapabilities" - }, - { - "source": "alarmEventActions", - "target": "AlarmEventActions" - }, - { - "source": "alarmModelDescription", - "target": "AlarmModelDescription" - }, - { - "source": "alarmModelName", - "target": "AlarmModelName" - }, - { - "source": "alarmRule", - "target": "AlarmRule" - }, - { - "source": "key", - "target": "Key" - }, - { - "source": "roleArn", - "target": "RoleArn" - }, - { - "source": "severity", - "target": "Severity" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreateAlarmModel", - "phase": "create", - "service": "iotevents" - }, - { - "cfn_type": "AWS::IoTEvents::AlarmModel", - "mappings": [ - { - "source": "alarmModelName", - "target": "AlarmModelName" - } - ], - "operation": "DeleteAlarmModel", - "phase": "delete", - "service": "iotevents" - }, - { - "cfn_type": "AWS::IoTEvents::DetectorModel", - "mappings": [ - { - "source": "detectorModelDefinition", - "target": "DetectorModelDefinition" - }, - { - "source": "detectorModelDescription", - "target": "DetectorModelDescription" - }, - { - "source": "detectorModelName", - "target": "DetectorModelName" - }, - { - "source": "evaluationMethod", - "target": "EvaluationMethod" - }, - { - "source": "key", - "target": "Key" - }, - { - "source": "roleArn", - "target": "RoleArn" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreateDetectorModel", - "phase": "create", - "service": "iotevents" - }, - { - "cfn_type": "AWS::IoTEvents::DetectorModel", - "mappings": [ - { - "source": "detectorModelName", - "target": "DetectorModelName" - } - ], - "operation": "DeleteDetectorModel", - "phase": "delete", - "service": "iotevents" - }, - { - "cfn_type": "AWS::IoTEvents::Input", - "mappings": [ - { - "source": "inputDefinition", - "target": "InputDefinition" - }, - { - "source": "inputDescription", - "target": "InputDescription" - }, - { - "source": "inputName", - "target": "InputName" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreateInput", - "phase": "create", - "service": "iotevents" - }, - { - "cfn_type": "AWS::IoTEvents::Input", - "mappings": [ - { - "source": "inputName", - "target": "InputName" - } - ], - "operation": "DeleteInput", - "phase": "delete", - "service": "iotevents" - }, { "cfn_type": "AWS::IoTFleetWise::Campaign", "mappings": [ - { - "source": "collectionScheme", - "target": "CollectionScheme" - }, { "source": "compression", "target": "Compression" }, - { - "source": "dataDestinationConfigs", - "target": "DataDestinationConfigs" - }, { "source": "dataExtraDimensions", "target": "DataExtraDimensions" }, - { - "source": "dataPartitions", - "target": "DataPartitions" - }, { "source": "description", "target": "Description" @@ -25966,10 +25162,6 @@ "source": "diagnosticsMode", "target": "DiagnosticsMode" }, - { - "source": "expiryTime", - "target": "ExpiryTime" - }, { "source": "name", "target": "Name" @@ -25986,26 +25178,10 @@ "source": "signalCatalogArn", "target": "SignalCatalogArn" }, - { - "source": "signalsToCollect", - "target": "SignalsToCollect" - }, - { - "source": "signalsToFetch", - "target": "SignalsToFetch" - }, { "source": "spoolingMode", "target": "SpoolingMode" }, - { - "source": "startTime", - "target": "StartTime" - }, - { - "source": "tags", - "target": "Tags" - }, { "source": "targetArn", "target": "TargetArn" @@ -26045,18 +25221,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "networkInterfaces", - "target": "NetworkInterfaces" - }, - { - "source": "signalDecoders", - "target": "SignalDecoders" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateDecoderManifest", @@ -26085,10 +25249,6 @@ { "source": "signalCatalogArn", "target": "SignalCatalogArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateFleet", @@ -26120,10 +25280,6 @@ { "source": "signalCatalogArn", "target": "SignalCatalogArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateModelManifest", @@ -26152,14 +25308,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "nodes", - "target": "Nodes" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateSignalCatalog", @@ -26204,10 +25352,6 @@ { "source": "stateTemplateProperties", "target": "StateTemplateProperties" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateStateTemplate", @@ -26228,10 +25372,6 @@ "source": "associationBehavior", "target": "AssociationBehavior" }, - { - "source": "attributes", - "target": "Attributes" - }, { "source": "decoderManifestArn", "target": "DecoderManifestArn" @@ -26239,14 +25379,6 @@ { "source": "modelManifestArn", "target": "ModelManifestArn" - }, - { - "source": "stateTemplates", - "target": "StateTemplates" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateVehicle", @@ -26262,14 +25394,13 @@ }, { "cfn_type": "AWS::IoTManagedIntegrations::CredentialLocker", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateCredentialLocker", @@ -26285,6 +25416,9 @@ }, { "cfn_type": "AWS::IoTManagedIntegrations::ManagedThing", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "AuthenticationMaterial", @@ -26298,10 +25432,6 @@ "source": "Brand", "target": "Brand" }, - { - "source": "CapabilityReport", - "target": "CapabilityReport" - }, { "source": "Classification", "target": "Classification" @@ -26310,10 +25440,6 @@ "source": "CredentialLockerId", "target": "CredentialLockerId" }, - { - "source": "MetaData", - "target": "MetaData" - }, { "source": "Model", "target": "Model" @@ -26333,10 +25459,6 @@ { "source": "SerialNumber", "target": "SerialNumber" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateManagedThing", @@ -26352,11 +25474,18 @@ }, { "cfn_type": "AWS::IoTManagedIntegrations::ProvisioningProfile", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "CaCertificate", "target": "CaCertificate" }, + { + "source": "ClaimCertificate", + "target": "ClaimCertificate" + }, { "source": "Name", "target": "Name" @@ -26364,10 +25493,6 @@ { "source": "ProvisioningType", "target": "ProvisioningType" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateProvisioningProfile", @@ -26383,18 +25508,13 @@ }, { "cfn_type": "AWS::IoTSiteWise::AccessPolicy", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "accessPolicyIdentity", - "target": "AccessPolicyIdentity" - }, { "source": "accessPolicyPermission", "target": "AccessPolicyPermission" - }, - { - "source": "accessPolicyResource", - "target": "AccessPolicyResource" } ], "operation": "CreateAccessPolicy", @@ -26403,6 +25523,9 @@ }, { "cfn_type": "AWS::IoTSiteWise::AccessPolicy", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteAccessPolicy", "phase": "delete", @@ -26410,6 +25533,9 @@ }, { "cfn_type": "AWS::IoTSiteWise::Asset", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "assetDescription", @@ -26436,13 +25562,22 @@ "phase": "create", "service": "iotsitewise" }, + { + "cfn_type": "AWS::IoTSiteWise::Asset", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [], + "operation": "DeleteAsset", + "phase": "delete", + "service": "iotsitewise" + }, { "cfn_type": "AWS::IoTSiteWise::AssetModel", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "assetModelCompositeModels", - "target": "AssetModelCompositeModels" - }, { "source": "assetModelDescription", "target": "AssetModelDescription" @@ -26451,18 +25586,10 @@ "source": "assetModelExternalId", "target": "AssetModelExternalId" }, - { - "source": "assetModelHierarchies", - "target": "AssetModelHierarchies" - }, { "source": "assetModelName", "target": "AssetModelName" }, - { - "source": "assetModelProperties", - "target": "AssetModelProperties" - }, { "source": "assetModelType", "target": "AssetModelType" @@ -26478,6 +25605,9 @@ }, { "cfn_type": "AWS::IoTSiteWise::AssetModel", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteAssetModel", "phase": "delete", @@ -26485,15 +25615,10 @@ }, { "cfn_type": "AWS::IoTSiteWise::ComputationModel", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "computationModelConfiguration", - "target": "ComputationModelConfiguration" - }, - { - "source": "computationModelDataBinding", - "target": "ComputationModelDataBinding" - }, { "source": "computationModelDescription", "target": "ComputationModelDescription" @@ -26513,6 +25638,9 @@ }, { "cfn_type": "AWS::IoTSiteWise::ComputationModel", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteComputationModel", "phase": "delete", @@ -26520,6 +25648,9 @@ }, { "cfn_type": "AWS::IoTSiteWise::Dashboard", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "dashboardDefinition", @@ -26548,6 +25679,9 @@ }, { "cfn_type": "AWS::IoTSiteWise::Dashboard", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteDashboard", "phase": "delete", @@ -26555,6 +25689,9 @@ }, { "cfn_type": "AWS::IoTSiteWise::Dataset", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "datasetDescription", @@ -26564,10 +25701,6 @@ "source": "datasetName", "target": "DatasetName" }, - { - "source": "datasetSource", - "target": "DatasetSource" - }, { "source": "tags", "target": "Tags" @@ -26579,6 +25712,9 @@ }, { "cfn_type": "AWS::IoTSiteWise::Dataset", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteDataset", "phase": "delete", @@ -26591,10 +25727,6 @@ "source": "gatewayName", "target": "GatewayName" }, - { - "source": "gatewayPlatform", - "target": "GatewayPlatform" - }, { "source": "gatewayVersion", "target": "GatewayVersion" @@ -26617,11 +25749,10 @@ }, { "cfn_type": "AWS::IoTSiteWise::Portal", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "alarms", - "target": "Alarms" - }, { "source": "notificationSenderEmail", "target": "NotificationSenderEmail" @@ -26646,10 +25777,6 @@ "source": "portalType", "target": "PortalType" }, - { - "source": "portalTypeConfiguration", - "target": "PortalTypeConfiguration" - }, { "source": "roleArn", "target": "RoleArn" @@ -26665,6 +25792,9 @@ }, { "cfn_type": "AWS::IoTSiteWise::Portal", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeletePortal", "phase": "delete", @@ -26672,6 +25802,9 @@ }, { "cfn_type": "AWS::IoTSiteWise::Project", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "portalId", @@ -26696,6 +25829,9 @@ }, { "cfn_type": "AWS::IoTSiteWise::Project", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteProject", "phase": "delete", @@ -26708,10 +25844,6 @@ "source": "componentTypeId", "target": "ComponentTypeId" }, - { - "source": "compositeComponentTypes", - "target": "CompositeComponentTypes" - }, { "source": "description", "target": "Description" @@ -26720,26 +25852,10 @@ "source": "extendsFrom", "target": "ExtendsFrom" }, - { - "source": "functions", - "target": "Functions" - }, { "source": "isSingleton", "target": "IsSingleton" }, - { - "source": "propertyDefinitions", - "target": "PropertyDefinitions" - }, - { - "source": "propertyGroups", - "target": "PropertyGroups" - }, - { - "source": "tags", - "target": "Tags" - }, { "source": "workspaceId", "target": "WorkspaceId" @@ -26768,14 +25884,6 @@ { "cfn_type": "AWS::IoTTwinMaker::Entity", "mappings": [ - { - "source": "components", - "target": "Components" - }, - { - "source": "compositeComponents", - "target": "CompositeComponents" - }, { "source": "description", "target": "Description" @@ -26792,10 +25900,6 @@ "source": "parentEntityId", "target": "ParentEntityId" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "workspaceId", "target": "WorkspaceId" @@ -26840,14 +25944,6 @@ "source": "sceneId", "target": "SceneId" }, - { - "source": "sceneMetadata", - "target": "SceneMetadata" - }, - { - "source": "tags", - "target": "Tags" - }, { "source": "workspaceId", "target": "WorkspaceId" @@ -26884,10 +25980,6 @@ "source": "syncSource", "target": "SyncSource" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "workspaceId", "target": "WorkspaceId" @@ -26928,10 +26020,6 @@ "source": "s3Location", "target": "S3Location" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "workspaceId", "target": "WorkspaceId" @@ -26955,6 +26043,9 @@ }, { "cfn_type": "AWS::IoTWireless::Destination", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "Description", @@ -26975,10 +26066,6 @@ { "source": "RoleArn", "target": "RoleArn" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDestination", @@ -26999,18 +26086,13 @@ }, { "cfn_type": "AWS::IoTWireless::DeviceProfile", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ - { - "source": "LoRaWAN", - "target": "LoRaWAN" - }, { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDeviceProfile", @@ -27026,6 +26108,9 @@ }, { "cfn_type": "AWS::IoTWireless::FuotaTask", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "Description", @@ -27039,17 +26124,9 @@ "source": "FirmwareUpdateRole", "target": "FirmwareUpdateRole" }, - { - "source": "LoRaWAN", - "target": "LoRaWAN" - }, { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateFuotaTask", @@ -27065,22 +26142,17 @@ }, { "cfn_type": "AWS::IoTWireless::MulticastGroup", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "Description", "target": "Description" }, - { - "source": "LoRaWAN", - "target": "LoRaWAN" - }, { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateMulticastGroup", @@ -27096,6 +26168,9 @@ }, { "cfn_type": "AWS::IoTWireless::NetworkAnalyzerConfiguration", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "Description", @@ -27105,14 +26180,6 @@ "source": "Name", "target": "Name" }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "TraceContent", - "target": "TraceContent" - }, { "source": "WirelessDevices", "target": "WirelessDevices" @@ -27133,22 +26200,6 @@ "phase": "delete", "service": "iotwireless" }, - { - "cfn_type": "AWS::IoTWireless::PartnerAccount", - "mappings": [ - { - "source": "Sidewalk", - "target": "Sidewalk" - }, - { - "source": "Tags", - "target": "Tags" - } - ], - "operation": "AssociateAwsAccountWithPartnerAccount", - "phase": "create", - "service": "iotwireless" - }, { "cfn_type": "AWS::IoTWireless::PartnerAccount", "mappings": [ @@ -27167,18 +26218,13 @@ }, { "cfn_type": "AWS::IoTWireless::ServiceProfile", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ - { - "source": "LoRaWAN", - "target": "LoRaWAN" - }, { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateServiceProfile", @@ -27194,6 +26240,9 @@ }, { "cfn_type": "AWS::IoTWireless::TaskDefinition", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "AutoCreateTasks", @@ -27202,14 +26251,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "Update", - "target": "Update" } ], "operation": "CreateWirelessGatewayTaskDefinition", @@ -27225,6 +26266,9 @@ }, { "cfn_type": "AWS::IoTWireless::WirelessDevice", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "Description", @@ -27234,10 +26278,6 @@ "source": "DestinationName", "target": "DestinationName" }, - { - "source": "LoRaWAN", - "target": "LoRaWAN" - }, { "source": "Name", "target": "Name" @@ -27246,10 +26286,6 @@ "source": "Positioning", "target": "Positioning" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" @@ -27268,18 +26304,13 @@ }, { "cfn_type": "AWS::IoTWireless::WirelessDeviceImportTask", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "DestinationName", "target": "DestinationName" - }, - { - "source": "Sidewalk", - "target": "Sidewalk" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "StartWirelessDeviceImportTask", @@ -27295,22 +26326,17 @@ }, { "cfn_type": "AWS::IoTWireless::WirelessGateway", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "Description", "target": "Description" }, - { - "source": "LoRaWAN", - "target": "LoRaWAN" - }, { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateWirelessGateway", @@ -27378,10 +26404,6 @@ { "source": "Origin", "target": "Origin" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateKey", @@ -27391,14 +26413,6 @@ { "cfn_type": "AWS::KafkaConnect::Connector", "mappings": [ - { - "source": "capacity", - "target": "Capacity" - }, - { - "source": "connectorConfiguration", - "target": "ConnectorConfiguration" - }, { "source": "connectorDescription", "target": "ConnectorDescription" @@ -27407,29 +26421,13 @@ "source": "connectorName", "target": "ConnectorName" }, - { - "source": "kafkaCluster", - "target": "KafkaCluster" - }, - { - "source": "kafkaClusterClientAuthentication", - "target": "KafkaClusterClientAuthentication" - }, - { - "source": "kafkaClusterEncryptionInTransit", - "target": "KafkaClusterEncryptionInTransit" - }, { "source": "kafkaConnectVersion", "target": "KafkaConnectVersion" }, { - "source": "logDelivery", - "target": "LogDelivery" - }, - { - "source": "plugins", - "target": "Plugins" + "source": "networkType", + "target": "NetworkType" }, { "source": "serviceExecutionRoleArn", @@ -27438,10 +26436,6 @@ { "source": "tags", "target": "Tags" - }, - { - "source": "workerConfiguration", - "target": "WorkerConfiguration" } ], "operation": "CreateConnector", @@ -27466,10 +26460,6 @@ "source": "description", "target": "Description" }, - { - "source": "location", - "target": "Location" - }, { "source": "name", "target": "Name" @@ -27523,11 +26513,10 @@ }, { "cfn_type": "AWS::Kendra::DataSource", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "CustomDocumentEnrichmentConfiguration", - "target": "CustomDocumentEnrichmentConfiguration" - }, { "source": "Description", "target": "Description" @@ -27552,10 +26541,6 @@ "source": "Schedule", "target": "Schedule" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" @@ -27579,6 +26564,9 @@ }, { "cfn_type": "AWS::Kendra::Faq", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -27603,14 +26591,6 @@ { "source": "RoleArn", "target": "RoleArn" - }, - { - "source": "S3Path", - "target": "S3Path" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateFaq", @@ -27631,6 +26611,9 @@ }, { "cfn_type": "AWS::Kendra::Index", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -27648,21 +26631,9 @@ "source": "RoleArn", "target": "RoleArn" }, - { - "source": "ServerSideEncryptionConfiguration", - "target": "ServerSideEncryptionConfiguration" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UserContextPolicy", "target": "UserContextPolicy" - }, - { - "source": "UserTokenConfigurations", - "target": "UserTokenConfigurations" } ], "operation": "CreateIndex", @@ -27678,11 +26649,10 @@ }, { "cfn_type": "AWS::KendraRanking::ExecutionPlan", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "CapacityUnits", - "target": "CapacityUnits" - }, { "source": "Description", "target": "Description" @@ -27690,10 +26660,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateRescoreExecutionPlan", @@ -27735,22 +26701,33 @@ "cfn_type": "AWS::Kinesis::Stream", "mappings": [ { - "source": "ShardCount", - "target": "ShardCount" + "source": "MaxRecordSizeInKiB", + "target": "MaxRecordSizeInKiB" }, { - "source": "StreamModeDetails", - "target": "StreamModeDetails" + "source": "ShardCount", + "target": "ShardCount" }, { "source": "Tags", "target": "Tags" + }, + { + "source": "WarmThroughputMiBps", + "target": "WarmThroughputMiBps" } ], "operation": "CreateStream", "phase": "create", "service": "kinesis" }, + { + "cfn_type": "AWS::Kinesis::Stream", + "mappings": [], + "operation": "DeleteStream", + "phase": "delete", + "service": "kinesis" + }, { "cfn_type": "AWS::Kinesis::StreamConsumer", "mappings": [ @@ -27790,10 +26767,6 @@ { "cfn_type": "AWS::KinesisAnalyticsV2::Application", "mappings": [ - { - "source": "ApplicationConfiguration", - "target": "ApplicationConfiguration" - }, { "source": "ApplicationDescription", "target": "ApplicationDescription" @@ -27813,10 +26786,6 @@ { "source": "ServiceExecutionRole", "target": "ServiceExecutionRole" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateApplication", @@ -27838,22 +26807,6 @@ { "cfn_type": "AWS::KinesisFirehose::DeliveryStream", "mappings": [ - { - "source": "AmazonOpenSearchServerlessDestinationConfiguration", - "target": "AmazonOpenSearchServerlessDestinationConfiguration" - }, - { - "source": "AmazonopensearchserviceDestinationConfiguration", - "target": "AmazonopensearchserviceDestinationConfiguration" - }, - { - "source": "DatabaseSourceConfiguration", - "target": "DatabaseSourceConfiguration" - }, - { - "source": "DeliveryStreamEncryptionConfigurationInput", - "target": "DeliveryStreamEncryptionConfigurationInput" - }, { "source": "DeliveryStreamName", "target": "DeliveryStreamName" @@ -27861,54 +26814,6 @@ { "source": "DeliveryStreamType", "target": "DeliveryStreamType" - }, - { - "source": "DirectPutSourceConfiguration", - "target": "DirectPutSourceConfiguration" - }, - { - "source": "ElasticsearchDestinationConfiguration", - "target": "ElasticsearchDestinationConfiguration" - }, - { - "source": "ExtendedS3DestinationConfiguration", - "target": "ExtendedS3DestinationConfiguration" - }, - { - "source": "HttpEndpointDestinationConfiguration", - "target": "HttpEndpointDestinationConfiguration" - }, - { - "source": "IcebergDestinationConfiguration", - "target": "IcebergDestinationConfiguration" - }, - { - "source": "KinesisStreamSourceConfiguration", - "target": "KinesisStreamSourceConfiguration" - }, - { - "source": "MSKSourceConfiguration", - "target": "MSKSourceConfiguration" - }, - { - "source": "RedshiftDestinationConfiguration", - "target": "RedshiftDestinationConfiguration" - }, - { - "source": "S3DestinationConfiguration", - "target": "S3DestinationConfiguration" - }, - { - "source": "SnowflakeDestinationConfiguration", - "target": "SnowflakeDestinationConfiguration" - }, - { - "source": "SplunkDestinationConfiguration", - "target": "SplunkDestinationConfiguration" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDeliveryStream", @@ -27927,18 +26832,6 @@ "phase": "delete", "service": "firehose" }, - { - "cfn_type": "AWS::KinesisVideo::SignalingChannel", - "mappings": [ - { - "source": "Tags", - "target": "Tags" - } - ], - "operation": "CreateSignalingChannel", - "phase": "create", - "service": "kinesisvideo" - }, { "cfn_type": "AWS::KinesisVideo::SignalingChannel", "mappings": [], @@ -28015,14 +26908,6 @@ { "source": "PermissionsWithGrantOption", "target": "PermissionsWithGrantOption" - }, - { - "source": "Principal", - "target": "Principal" - }, - { - "source": "Resource", - "target": "Resource" } ], "operation": "GrantPermissions", @@ -28039,14 +26924,6 @@ { "source": "PermissionsWithGrantOption", "target": "PermissionsWithGrantOption" - }, - { - "source": "Principal", - "target": "Principal" - }, - { - "source": "Resource", - "target": "Resource" } ], "operation": "RevokePermissions", @@ -28089,38 +26966,6 @@ "phase": "delete", "service": "lakeformation" }, - { - "cfn_type": "AWS::LakeFormation::TagAssociation", - "mappings": [ - { - "source": "LFTags", - "target": "LFTags" - }, - { - "source": "Resource", - "target": "Resource" - } - ], - "operation": "AddLFTagsToResource", - "phase": "create", - "service": "lakeformation" - }, - { - "cfn_type": "AWS::LakeFormation::TagAssociation", - "mappings": [ - { - "source": "LFTags", - "target": "LFTags" - }, - { - "source": "Resource", - "target": "Resource" - } - ], - "operation": "RemoveLFTagsFromResource", - "phase": "delete", - "service": "lakeformation" - }, { "cfn_type": "AWS::Lambda::Alias", "mappings": [ @@ -28139,10 +26984,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "RoutingConfig", - "target": "RoutingConfig" } ], "operation": "CreateAlias", @@ -28166,16 +27007,40 @@ "service": "lambda" }, { - "cfn_type": "AWS::Lambda::CodeSigningConfig", + "cfn_type": "AWS::Lambda::CapacityProvider", "mappings": [ { - "source": "AllowedPublishers", - "target": "AllowedPublishers" + "source": "CapacityProviderName", + "target": "CapacityProviderName" }, { - "source": "CodeSigningPolicies", - "target": "CodeSigningPolicies" + "source": "KmsKeyArn", + "target": "KmsKeyArn" }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCapacityProvider", + "phase": "create", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::CapacityProvider", + "mappings": [ + { + "source": "CapacityProviderName", + "target": "CapacityProviderName" + } + ], + "operation": "DeleteCapacityProvider", + "phase": "delete", + "service": "lambda" + }, + { + "cfn_type": "AWS::Lambda::CodeSigningConfig", + "mappings": [ { "source": "Description", "target": "Description" @@ -28199,10 +27064,6 @@ { "cfn_type": "AWS::Lambda::EventInvokeConfig", "mappings": [ - { - "source": "DestinationConfig", - "target": "DestinationConfig" - }, { "source": "FunctionName", "target": "FunctionName" @@ -28243,10 +27104,6 @@ { "cfn_type": "AWS::Lambda::EventSourceMapping", "mappings": [ - { - "source": "AmazonManagedKafkaEventSourceConfig", - "target": "AmazonManagedKafkaEventSourceConfig" - }, { "source": "BatchSize", "target": "BatchSize" @@ -28255,14 +27112,6 @@ "source": "BisectBatchOnFunctionError", "target": "BisectBatchOnFunctionError" }, - { - "source": "DestinationConfig", - "target": "DestinationConfig" - }, - { - "source": "DocumentDBEventSourceConfig", - "target": "DocumentDBEventSourceConfig" - }, { "source": "Enabled", "target": "Enabled" @@ -28271,10 +27120,6 @@ "source": "EventSourceArn", "target": "EventSourceArn" }, - { - "source": "FilterCriteria", - "target": "FilterCriteria" - }, { "source": "FunctionName", "target": "FunctionName" @@ -28299,46 +27144,18 @@ "source": "MaximumRetryAttempts", "target": "MaximumRetryAttempts" }, - { - "source": "MetricsConfig", - "target": "MetricsConfig" - }, { "source": "ParallelizationFactor", "target": "ParallelizationFactor" }, - { - "source": "ProvisionedPollerConfig", - "target": "ProvisionedPollerConfig" - }, { "source": "Queues", "target": "Queues" }, - { - "source": "ScalingConfig", - "target": "ScalingConfig" - }, - { - "source": "SelfManagedEventSource", - "target": "SelfManagedEventSource" - }, - { - "source": "SelfManagedKafkaEventSourceConfig", - "target": "SelfManagedKafkaEventSourceConfig" - }, - { - "source": "SourceAccessConfigurations", - "target": "SourceAccessConfigurations" - }, { "source": "StartingPosition", "target": "StartingPosition" }, - { - "source": "StartingPositionTimestamp", - "target": "StartingPositionTimestamp" - }, { "source": "Tags", "target": "Tags" @@ -28370,34 +27187,14 @@ "source": "Architectures", "target": "Architectures" }, - { - "source": "Code", - "target": "Code" - }, { "source": "CodeSigningConfigArn", "target": "CodeSigningConfigArn" }, - { - "source": "DeadLetterConfig", - "target": "DeadLetterConfig" - }, { "source": "Description", "target": "Description" }, - { - "source": "Environment", - "target": "Environment" - }, - { - "source": "EphemeralStorage", - "target": "EphemeralStorage" - }, - { - "source": "FileSystemConfigs", - "target": "FileSystemConfigs" - }, { "source": "FunctionName", "target": "FunctionName" @@ -28406,10 +27203,6 @@ "source": "Handler", "target": "Handler" }, - { - "source": "ImageConfig", - "target": "ImageConfig" - }, { "source": "KMSKeyArn", "target": "KmsKeyArn" @@ -28418,10 +27211,6 @@ "source": "Layers", "target": "Layers" }, - { - "source": "LoggingConfig", - "target": "LoggingConfig" - }, { "source": "MemorySize", "target": "MemorySize" @@ -28438,10 +27227,6 @@ "source": "Runtime", "target": "Runtime" }, - { - "source": "SnapStart", - "target": "SnapStart" - }, { "source": "Tags", "target": "Tags" @@ -28449,14 +27234,6 @@ { "source": "Timeout", "target": "Timeout" - }, - { - "source": "TracingConfig", - "target": "TracingConfig" - }, - { - "source": "VpcConfig", - "target": "VpcConfig" } ], "operation": "CreateFunction", @@ -28477,6 +27254,9 @@ }, { "cfn_type": "AWS::Lambda::Function", + "ignored_inputs": [ + "FunctionName" + ], "mappings": [ { "source": "Runtime", @@ -28518,10 +27298,6 @@ "source": "CompatibleRuntimes", "target": "CompatibleRuntimes" }, - { - "source": "Content", - "target": "Content" - }, { "source": "Description", "target": "Description" @@ -28578,6 +27354,86 @@ "phase": "delete", "service": "lambda" }, + { + "cfn_type": "AWS::Lambda::MicrovmImage", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "additionalOsCapabilities", + "target": "AdditionalOsCapabilities" + }, + { + "source": "baseImageArn", + "target": "BaseImageArn" + }, + { + "source": "baseImageVersion", + "target": "BaseImageVersion" + }, + { + "source": "buildRoleArn", + "target": "BuildRoleArn" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "egressNetworkConnectors", + "target": "EgressNetworkConnectors" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateMicrovmImage", + "phase": "create", + "service": "lambda-microvms" + }, + { + "cfn_type": "AWS::Lambda::MicrovmImage", + "mappings": [], + "operation": "DeleteMicrovmImage", + "phase": "delete", + "service": "lambda-microvms" + }, + { + "cfn_type": "AWS::Lambda::NetworkConnector", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "OperatorRole", + "target": "OperatorRole" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateNetworkConnector", + "phase": "create", + "service": "lambda-core" + }, + { + "cfn_type": "AWS::Lambda::NetworkConnector", + "mappings": [], + "operation": "DeleteNetworkConnector", + "phase": "delete", + "service": "lambda-core" + }, { "cfn_type": "AWS::Lambda::Permission", "mappings": [ @@ -28597,6 +27453,10 @@ "source": "FunctionUrlAuthType", "target": "FunctionUrlAuthType" }, + { + "source": "InvokedViaFunctionUrl", + "target": "InvokedViaFunctionUrl" + }, { "source": "Principal", "target": "Principal" @@ -28637,10 +27497,6 @@ "source": "AuthType", "target": "AuthType" }, - { - "source": "Cors", - "target": "Cors" - }, { "source": "InvokeMode", "target": "InvokeMode" @@ -28685,10 +27541,6 @@ "source": "name", "target": "Name" }, - { - "source": "specifications", - "target": "Specifications" - }, { "source": "tags", "target": "Tags" @@ -28712,30 +27564,14 @@ { "cfn_type": "AWS::Lex::Bot", "mappings": [ - { - "source": "botMembers", - "target": "BotMembers" - }, - { - "source": "botTags", - "target": "BotTags" - }, { "source": "botType", "target": "BotType" }, - { - "source": "dataPrivacy", - "target": "DataPrivacy" - }, { "source": "description", "target": "Description" }, - { - "source": "errorLogSettings", - "target": "ErrorLogSettings" - }, { "source": "idleSessionTTLInSeconds", "target": "IdleSessionTTLInSeconds" @@ -28743,10 +27579,6 @@ { "source": "roleArn", "target": "RoleArn" - }, - { - "source": "testBotAliasTags", - "target": "TestBotAliasTags" } ], "operation": "CreateBot", @@ -28768,10 +27600,6 @@ { "cfn_type": "AWS::Lex::BotAlias", "mappings": [ - { - "source": "botAliasLocaleSettings", - "target": "BotAliasLocaleSettings" - }, { "source": "botAliasName", "target": "BotAliasName" @@ -28784,17 +27612,9 @@ "source": "botVersion", "target": "BotVersion" }, - { - "source": "conversationLogSettings", - "target": "ConversationLogSettings" - }, { "source": "description", "target": "Description" - }, - { - "source": "sentimentAnalysisSettings", - "target": "SentimentAnalysisSettings" } ], "operation": "CreateBotAlias", @@ -28820,10 +27640,6 @@ "source": "botId", "target": "BotId" }, - { - "source": "botVersionLocaleSpecification", - "target": "BotVersionLocaleSpecification" - }, { "source": "description", "target": "Description" @@ -28875,6 +27691,9 @@ }, { "cfn_type": "AWS::LicenseManager::Grant", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "AllowedOperations", @@ -28895,10 +27714,6 @@ { "source": "Principals", "target": "Principals" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateGrant", @@ -28914,31 +27729,18 @@ }, { "cfn_type": "AWS::LicenseManager::License", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Beneficiary", "target": "Beneficiary" }, - { - "source": "ConsumptionConfiguration", - "target": "ConsumptionConfiguration" - }, - { - "source": "Entitlements", - "target": "Entitlements" - }, { "source": "HomeRegion", "target": "HomeRegion" }, - { - "source": "Issuer", - "target": "Issuer" - }, - { - "source": "LicenseMetadata", - "target": "LicenseMetadata" - }, { "source": "LicenseName", "target": "LicenseName" @@ -28950,14 +27752,6 @@ { "source": "ProductSKU", "target": "ProductSKU" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "Validity", - "target": "Validity" } ], "operation": "CreateLicense", @@ -28971,6 +27765,32 @@ "phase": "delete", "service": "license-manager" }, + { + "cfn_type": "AWS::LicenseManager::LicenseAssetRuleSet", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateLicenseAssetRuleset", + "phase": "create", + "service": "license-manager" + }, + { + "cfn_type": "AWS::LicenseManager::LicenseAssetRuleSet", + "mappings": [], + "operation": "DeleteLicenseAssetRuleset", + "phase": "delete", + "service": "license-manager" + }, { "cfn_type": "AWS::Lightsail::Alarm", "mappings": [ @@ -29045,10 +27865,6 @@ { "source": "bundleId", "target": "BundleId" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateBucket", @@ -29081,10 +27897,6 @@ { "source": "subjectAlternativeNames", "target": "SubjectAlternativeNames" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateCertificate", @@ -29110,14 +27922,6 @@ "source": "power", "target": "Power" }, - { - "source": "privateRegistryAccess", - "target": "PrivateRegistryAccess" - }, - { - "source": "publicDomainNames", - "target": "PublicDomainNames" - }, { "source": "scale", "target": "Scale" @@ -29125,10 +27929,6 @@ { "source": "serviceName", "target": "ServiceName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateContainerService", @@ -29177,10 +27977,6 @@ { "source": "relationalDatabaseName", "target": "RelationalDatabaseName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateRelationalDatabase", @@ -29209,10 +28005,6 @@ { "source": "relationalDatabaseSnapshotName", "target": "RelationalDatabaseSnapshotName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateRelationalDatabaseSnapshot", @@ -29234,10 +28026,6 @@ { "cfn_type": "AWS::Lightsail::Disk", "mappings": [ - { - "source": "addOns", - "target": "AddOns" - }, { "source": "availabilityZone", "target": "AvailabilityZone" @@ -29249,10 +28037,6 @@ { "source": "sizeInGb", "target": "SizeInGb" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateDisk", @@ -29281,10 +28065,6 @@ { "source": "diskSnapshotName", "target": "DiskSnapshotName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateDiskSnapshot", @@ -29310,22 +28090,10 @@ "source": "bundleId", "target": "BundleId" }, - { - "source": "cacheBehaviorSettings", - "target": "CacheBehaviorSettings" - }, - { - "source": "cacheBehaviors", - "target": "CacheBehaviors" - }, { "source": "certificateName", "target": "CertificateName" }, - { - "source": "defaultCacheBehavior", - "target": "DefaultCacheBehavior" - }, { "source": "distributionName", "target": "DistributionName" @@ -29333,14 +28101,6 @@ { "source": "ipAddressType", "target": "IpAddressType" - }, - { - "source": "origin", - "target": "Origin" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateDistribution", @@ -29365,10 +28125,6 @@ { "source": "domainName", "target": "DomainName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateDomain", @@ -29390,10 +28146,6 @@ { "cfn_type": "AWS::Lightsail::Instance", "mappings": [ - { - "source": "addOns", - "target": "AddOns" - }, { "source": "availabilityZone", "target": "AvailabilityZone" @@ -29410,10 +28162,6 @@ "source": "keyPairName", "target": "KeyPairName" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "userData", "target": "UserData" @@ -29445,10 +28193,6 @@ { "source": "instanceSnapshotName", "target": "InstanceSnapshotName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateInstanceSnapshot", @@ -29486,10 +28230,6 @@ "source": "loadBalancerName", "target": "LoadBalancerName" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "tlsPolicyName", "target": "TlsPolicyName" @@ -29582,10 +28322,6 @@ "source": "Description", "target": "Description" }, - { - "source": "ExpireTime", - "target": "ExpireTime" - }, { "source": "KeyName", "target": "KeyName" @@ -29594,10 +28330,6 @@ "source": "NoExpiry", "target": "NoExpiry" }, - { - "source": "Restrictions", - "target": "Restrictions" - }, { "source": "Tags", "target": "Tags" @@ -29670,10 +28402,6 @@ { "cfn_type": "AWS::Location::Map", "mappings": [ - { - "source": "Configuration", - "target": "Configuration" - }, { "source": "Description", "target": "Description" @@ -29714,10 +28442,6 @@ "source": "DataSource", "target": "DataSource" }, - { - "source": "DataSourceConfiguration", - "target": "DataSourceConfiguration" - }, { "source": "Description", "target": "Description" @@ -30076,10 +28800,6 @@ { "source": "integrationType", "target": "IntegrationType" - }, - { - "source": "resourceConfig", - "target": "ResourceConfig" } ], "operation": "PutIntegration", @@ -30140,6 +28860,10 @@ { "cfn_type": "AWS::Logs::LogGroup", "mappings": [ + { + "source": "deletionProtectionEnabled", + "target": "DeletionProtectionEnabled" + }, { "source": "kmsKeyId", "target": "KmsKeyId" @@ -30212,6 +28936,14 @@ "source": "applyOnTransformedLogs", "target": "ApplyOnTransformedLogs" }, + { + "source": "emitSystemFieldDimensions", + "target": "EmitSystemFieldDimensions" + }, + { + "source": "fieldSelectionCriteria", + "target": "FieldSelectionCriteria" + }, { "source": "filterName", "target": "FilterName" @@ -30223,10 +28955,6 @@ { "source": "logGroupName", "target": "LogGroupName" - }, - { - "source": "metricTransformations", - "target": "MetricTransformations" } ], "operation": "PutMetricFilter", @@ -30251,6 +28979,9 @@ }, { "cfn_type": "AWS::Logs::QueryDefinition", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "logGroupNames", @@ -30311,10 +29042,22 @@ { "cfn_type": "AWS::Logs::ScheduledQuery", "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "executionRoleArn", + "target": "ExecutionRoleArn" + }, { "source": "logGroupIdentifiers", "target": "LogGroupIdentifiers" }, + { + "source": "name", + "target": "Name" + }, { "source": "queryLanguage", "target": "QueryLanguage" @@ -30322,9 +29065,56 @@ { "source": "queryString", "target": "QueryString" + }, + { + "source": "scheduleEndTime", + "target": "ScheduleEndTime" + }, + { + "source": "scheduleExpression", + "target": "ScheduleExpression" + }, + { + "source": "scheduleStartTime", + "target": "ScheduleStartTime" + }, + { + "source": "startTimeOffset", + "target": "StartTimeOffset" + }, + { + "source": "state", + "target": "State" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "timezone", + "target": "Timezone" } ], - "operation": "StartQuery", + "operation": "CreateScheduledQuery", + "phase": "create", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::ScheduledQuery", + "mappings": [], + "operation": "DeleteScheduledQuery", + "phase": "delete", + "service": "logs" + }, + { + "cfn_type": "AWS::Logs::StorageTierPolicy", + "mappings": [ + { + "source": "storageTier", + "target": "StorageTier" + } + ], + "operation": "PutStorageTierPolicy", "phase": "create", "service": "logs" }, @@ -30343,6 +29133,14 @@ "source": "distribution", "target": "Distribution" }, + { + "source": "emitSystemFields", + "target": "EmitSystemFields" + }, + { + "source": "fieldSelectionCriteria", + "target": "FieldSelectionCriteria" + }, { "source": "filterName", "target": "FilterName" @@ -30386,10 +29184,6 @@ { "source": "logGroupIdentifier", "target": "LogGroupIdentifier" - }, - { - "source": "transformerConfig", - "target": "TransformerConfig" } ], "operation": "PutTransformer", @@ -30410,19 +29204,14 @@ }, { "cfn_type": "AWS::LookoutEquipment::InferenceScheduler", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "DataDelayOffsetInMinutes", "target": "DataDelayOffsetInMinutes" }, - { - "source": "DataInputConfiguration", - "target": "DataInputConfiguration" - }, - { - "source": "DataOutputConfiguration", - "target": "DataOutputConfiguration" - }, { "source": "DataUploadFrequency", "target": "DataUploadFrequency" @@ -30442,10 +29231,6 @@ { "source": "ServerSideKmsKeyId", "target": "ServerSideKmsKeyId" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateInferenceScheduler", @@ -30464,37 +29249,12 @@ "phase": "delete", "service": "lookoutequipment" }, - { - "cfn_type": "AWS::LookoutVision::Project", - "mappings": [ - { - "source": "ProjectName", - "target": "ProjectName" - } - ], - "operation": "CreateProject", - "phase": "create", - "service": "lookoutvision" - }, - { - "cfn_type": "AWS::LookoutVision::Project", - "mappings": [ - { - "source": "ProjectName", - "target": "ProjectName" - } - ], - "operation": "DeleteProject", - "phase": "delete", - "service": "lookoutvision" - }, { "cfn_type": "AWS::M2::Application", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "definition", - "target": "Definition" - }, { "source": "description", "target": "Description" @@ -30514,10 +29274,6 @@ { "source": "roleArn", "target": "RoleArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateApplication", @@ -30533,6 +29289,9 @@ }, { "cfn_type": "AWS::M2::Deployment", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "applicationId", @@ -30569,6 +29328,9 @@ }, { "cfn_type": "AWS::M2::Environment", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -30582,10 +29344,6 @@ "source": "engineVersion", "target": "EngineVersion" }, - { - "source": "highAvailabilityConfig", - "target": "HighAvailabilityConfig" - }, { "source": "instanceType", "target": "InstanceType" @@ -30614,17 +29372,9 @@ "source": "securityGroupIds", "target": "SecurityGroupIds" }, - { - "source": "storageConfigurations", - "target": "StorageConfigurations" - }, { "source": "subnetIds", "target": "SubnetIds" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateEnvironment", @@ -30640,15 +29390,10 @@ }, { "cfn_type": "AWS::MPA::ApprovalTeam", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "ApprovalStrategy", - "target": "ApprovalStrategy" - }, - { - "source": "Approvers", - "target": "Approvers" - }, { "source": "Description", "target": "Description" @@ -30657,10 +29402,6 @@ "source": "Name", "target": "Name" }, - { - "source": "Policies", - "target": "Policies" - }, { "source": "Tags", "target": "Tags" @@ -30672,11 +29413,10 @@ }, { "cfn_type": "AWS::MPA::IdentitySource", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "IdentitySourceParameters", - "target": "IdentitySourceParameters" - }, { "source": "Tags", "target": "Tags" @@ -30694,28 +29434,40 @@ "service": "mpa" }, { - "cfn_type": "AWS::MSK::Cluster", + "cfn_type": "AWS::MSK::Channel", "mappings": [ { - "source": "BrokerNodeGroupInfo", - "target": "BrokerNodeGroupInfo" + "source": "ChannelName", + "target": "ChannelName" }, { - "source": "ClientAuthentication", - "target": "ClientAuthentication" - }, + "source": "ClusterArn", + "target": "ClusterArn" + } + ], + "operation": "CreateChannel", + "phase": "create", + "service": "kafka" + }, + { + "cfn_type": "AWS::MSK::Channel", + "mappings": [ + { + "source": "ClusterArn", + "target": "ClusterArn" + } + ], + "operation": "DeleteChannel", + "phase": "delete", + "service": "kafka" + }, + { + "cfn_type": "AWS::MSK::Cluster", + "mappings": [ { "source": "ClusterName", "target": "ClusterName" }, - { - "source": "ConfigurationInfo", - "target": "ConfigurationInfo" - }, - { - "source": "EncryptionInfo", - "target": "EncryptionInfo" - }, { "source": "EnhancedMonitoring", "target": "EnhancedMonitoring" @@ -30724,25 +29476,13 @@ "source": "KafkaVersion", "target": "KafkaVersion" }, - { - "source": "LoggingInfo", - "target": "LoggingInfo" - }, { "source": "NumberOfBrokerNodes", "target": "NumberOfBrokerNodes" }, - { - "source": "OpenMonitoring", - "target": "OpenMonitoring" - }, { "source": "StorageMode", "target": "StorageMode" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateCluster", @@ -30794,10 +29534,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "ServerProperties", - "target": "ServerProperties" } ], "operation": "CreateConfiguration", @@ -30818,14 +29554,6 @@ "source": "Description", "target": "Description" }, - { - "source": "KafkaClusters", - "target": "KafkaClusters" - }, - { - "source": "ReplicationInfoList", - "target": "ReplicationInfoList" - }, { "source": "ReplicatorName", "target": "ReplicatorName" @@ -30851,21 +29579,49 @@ "service": "kafka" }, { - "cfn_type": "AWS::MSK::ServerlessCluster", + "cfn_type": "AWS::MSK::Topic", "mappings": [ { - "source": "ClusterName", - "target": "ClusterName" + "source": "ClusterArn", + "target": "ClusterArn" }, { - "source": "Tags", - "target": "Tags" + "source": "Configs", + "target": "Configs" + }, + { + "source": "PartitionCount", + "target": "PartitionCount" + }, + { + "source": "ReplicationFactor", + "target": "ReplicationFactor" + }, + { + "source": "TopicName", + "target": "TopicName" } ], - "operation": "CreateClusterV2", + "operation": "CreateTopic", "phase": "create", "service": "kafka" }, + { + "cfn_type": "AWS::MSK::Topic", + "mappings": [ + { + "source": "ClusterArn", + "target": "ClusterArn" + }, + { + "source": "TopicName", + "target": "TopicName" + } + ], + "operation": "DeleteTopic", + "phase": "delete", + "service": "kafka" + }, { "cfn_type": "AWS::MSK::VpcConnection", "mappings": [ @@ -30881,10 +29637,6 @@ "source": "SecurityGroups", "target": "SecurityGroups" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TargetClusterArn", "target": "TargetClusterArn" @@ -30908,10 +29660,6 @@ { "cfn_type": "AWS::MWAA::Environment", "mappings": [ - { - "source": "AirflowConfigurationOptions", - "target": "AirflowConfigurationOptions" - }, { "source": "AirflowVersion", "target": "AirflowVersion" @@ -30936,10 +29684,6 @@ "source": "KmsKey", "target": "KmsKey" }, - { - "source": "LoggingConfiguration", - "target": "LoggingConfiguration" - }, { "source": "MaxWebservers", "target": "MaxWebservers" @@ -30960,10 +29704,6 @@ "source": "Name", "target": "Name" }, - { - "source": "NetworkConfiguration", - "target": "NetworkConfiguration" - }, { "source": "PluginsS3ObjectVersion", "target": "PluginsS3ObjectVersion" @@ -30996,10 +29736,6 @@ "source": "StartupScriptS3Path", "target": "StartupScriptS3Path" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "WebserverAccessMode", "target": "WebserverAccessMode" @@ -31026,12 +29762,45 @@ "service": "mwaa" }, { - "cfn_type": "AWS::Macie::AllowList", + "cfn_type": "AWS::MWAAServerless::Workflow", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { - "source": "criteria", - "target": "Criteria" + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" }, + { + "source": "TriggerMode", + "target": "TriggerMode" + } + ], + "operation": "CreateWorkflow", + "phase": "create", + "service": "mwaa-serverless" + }, + { + "cfn_type": "AWS::MWAAServerless::Workflow", + "mappings": [], + "operation": "DeleteWorkflow", + "phase": "delete", + "service": "mwaa-serverless" + }, + { + "cfn_type": "AWS::Macie::AllowList", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ { "source": "description", "target": "Description" @@ -31058,6 +29827,9 @@ }, { "cfn_type": "AWS::Macie::CustomDataIdentifier", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -31101,6 +29873,9 @@ }, { "cfn_type": "AWS::Macie::FindingsFilter", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "action", @@ -31110,10 +29885,6 @@ "source": "description", "target": "Description" }, - { - "source": "findingCriteria", - "target": "FindingCriteria" - }, { "source": "name", "target": "Name" @@ -31140,6 +29911,9 @@ }, { "cfn_type": "AWS::Macie::Session", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "findingPublishingFrequency", @@ -31156,6 +29930,9 @@ }, { "cfn_type": "AWS::ManagedBlockchain::Accessor", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "AccessorType", @@ -31184,33 +29961,13 @@ { "cfn_type": "AWS::MediaConnect::Bridge", "mappings": [ - { - "source": "EgressGatewayBridge", - "target": "EgressGatewayBridge" - }, - { - "source": "IngressGatewayBridge", - "target": "IngressGatewayBridge" - }, { "source": "Name", "target": "Name" }, - { - "source": "Outputs", - "target": "Outputs" - }, { "source": "PlacementArn", "target": "PlacementArn" - }, - { - "source": "SourceFailoverConfig", - "target": "SourceFailoverConfig" - }, - { - "source": "Sources", - "target": "Sources" } ], "operation": "CreateBridge", @@ -31283,37 +30040,9 @@ "source": "FlowSize", "target": "FlowSize" }, - { - "source": "Maintenance", - "target": "Maintenance" - }, - { - "source": "MediaStreams", - "target": "MediaStreams" - }, { "source": "Name", "target": "Name" - }, - { - "source": "NdiConfig", - "target": "NdiConfig" - }, - { - "source": "Source", - "target": "Source" - }, - { - "source": "SourceFailoverConfig", - "target": "SourceFailoverConfig" - }, - { - "source": "SourceMonitoringConfig", - "target": "SourceMonitoringConfig" - }, - { - "source": "VpcInterfaces", - "target": "VpcInterfaces" } ], "operation": "CreateFlow", @@ -31433,10 +30162,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Networks", - "target": "Networks" } ], "operation": "CreateGateway", @@ -31450,6 +30175,128 @@ "phase": "delete", "service": "mediaconnect" }, + { + "cfn_type": "AWS::MediaConnect::RouterInput", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "AvailabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "MaximumBitrate", + "target": "MaximumBitrate" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RegionName", + "target": "RegionName" + }, + { + "source": "RoutingScope", + "target": "RoutingScope" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Tier", + "target": "Tier" + } + ], + "operation": "CreateRouterInput", + "phase": "create", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::RouterInput", + "mappings": [], + "operation": "DeleteRouterInput", + "phase": "delete", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::RouterNetworkInterface", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "RegionName", + "target": "RegionName" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateRouterNetworkInterface", + "phase": "create", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::RouterNetworkInterface", + "mappings": [], + "operation": "DeleteRouterNetworkInterface", + "phase": "delete", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::RouterOutput", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "AvailabilityZone", + "target": "AvailabilityZone" + }, + { + "source": "MaximumBitrate", + "target": "MaximumBitrate" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RegionName", + "target": "RegionName" + }, + { + "source": "RoutingScope", + "target": "RoutingScope" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Tier", + "target": "Tier" + } + ], + "operation": "CreateRouterOutput", + "phase": "create", + "service": "mediaconnect" + }, + { + "cfn_type": "AWS::MediaConnect::RouterOutput", + "mappings": [], + "operation": "DeleteRouterOutput", + "phase": "delete", + "service": "mediaconnect" + }, { "cfn_type": "AWS::MediaConvert::Preset", "mappings": [ @@ -31464,10 +30311,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreatePreset", @@ -31488,6 +30331,9 @@ }, { "cfn_type": "AWS::MediaLive::ChannelPlacementGroup", + "ignored_inputs": [ + "RequestId" + ], "mappings": [ { "source": "ClusterId", @@ -31524,6 +30370,9 @@ }, { "cfn_type": "AWS::MediaLive::CloudWatchAlarmTemplate", + "ignored_inputs": [ + "RequestId" + ], "mappings": [ { "source": "ComparisonOperator", @@ -31561,10 +30410,6 @@ "source": "Statistic", "target": "Statistic" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TargetResourceType", "target": "TargetResourceType" @@ -31591,6 +30436,9 @@ }, { "cfn_type": "AWS::MediaLive::CloudWatchAlarmTemplateGroup", + "ignored_inputs": [ + "RequestId" + ], "mappings": [ { "source": "Description", @@ -31599,10 +30447,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateCloudWatchAlarmTemplateGroup", @@ -31618,6 +30462,9 @@ }, { "cfn_type": "AWS::MediaLive::Cluster", + "ignored_inputs": [ + "RequestId" + ], "mappings": [ { "source": "ClusterType", @@ -31631,10 +30478,6 @@ "source": "Name", "target": "Name" }, - { - "source": "NetworkSettings", - "target": "NetworkSettings" - }, { "source": "Tags", "target": "Tags" @@ -31653,15 +30496,14 @@ }, { "cfn_type": "AWS::MediaLive::EventBridgeRuleTemplate", + "ignored_inputs": [ + "RequestId" + ], "mappings": [ { "source": "Description", "target": "Description" }, - { - "source": "EventTargets", - "target": "EventTargets" - }, { "source": "EventType", "target": "EventType" @@ -31673,10 +30515,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateEventBridgeRuleTemplate", @@ -31692,6 +30530,9 @@ }, { "cfn_type": "AWS::MediaLive::EventBridgeRuleTemplateGroup", + "ignored_inputs": [ + "RequestId" + ], "mappings": [ { "source": "Description", @@ -31700,10 +30541,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateEventBridgeRuleTemplateGroup", @@ -31719,15 +30556,14 @@ }, { "cfn_type": "AWS::MediaLive::Multiplex", + "ignored_inputs": [ + "RequestId" + ], "mappings": [ { "source": "AvailabilityZones", "target": "AvailabilityZones" }, - { - "source": "MultiplexSettings", - "target": "MultiplexSettings" - }, { "source": "Name", "target": "Name" @@ -31750,15 +30586,14 @@ }, { "cfn_type": "AWS::MediaLive::Multiplexprogram", + "ignored_inputs": [ + "RequestId" + ], "mappings": [ { "source": "MultiplexId", "target": "MultiplexId" }, - { - "source": "MultiplexProgramSettings", - "target": "MultiplexProgramSettings" - }, { "source": "ProgramName", "target": "ProgramName" @@ -31786,19 +30621,14 @@ }, { "cfn_type": "AWS::MediaLive::Network", + "ignored_inputs": [ + "RequestId" + ], "mappings": [ - { - "source": "IpPools", - "target": "IpPools" - }, { "source": "Name", "target": "Name" }, - { - "source": "Routes", - "target": "Routes" - }, { "source": "Tags", "target": "Tags" @@ -31817,6 +30647,9 @@ }, { "cfn_type": "AWS::MediaLive::Node", + "ignored_inputs": [ + "RequestId" + ], "mappings": [ { "source": "ClusterId", @@ -31826,10 +30659,6 @@ "source": "Name", "target": "Name" }, - { - "source": "NodeInterfaceMappings", - "target": "NodeInterfaceMappings" - }, { "source": "Role", "target": "Role" @@ -31857,6 +30686,9 @@ }, { "cfn_type": "AWS::MediaLive::SdiSource", + "ignored_inputs": [ + "RequestId" + ], "mappings": [ { "source": "Mode", @@ -31888,6 +30720,9 @@ }, { "cfn_type": "AWS::MediaLive::SignalMap", + "ignored_inputs": [ + "RequestId" + ], "mappings": [ { "source": "CloudWatchAlarmTemplateGroupIdentifiers", @@ -31908,10 +30743,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateSignalMap", @@ -32004,30 +30835,14 @@ { "cfn_type": "AWS::MediaPackage::OriginEndpoint", "mappings": [ - { - "source": "Authorization", - "target": "Authorization" - }, { "source": "ChannelId", "target": "ChannelId" }, - { - "source": "CmafPackage", - "target": "CmafPackage" - }, - { - "source": "DashPackage", - "target": "DashPackage" - }, { "source": "Description", "target": "Description" }, - { - "source": "HlsPackage", - "target": "HlsPackage" - }, { "source": "Id", "target": "Id" @@ -32036,10 +30851,6 @@ "source": "ManifestName", "target": "ManifestName" }, - { - "source": "MssPackage", - "target": "MssPackage" - }, { "source": "Origination", "target": "Origination" @@ -32080,26 +30891,10 @@ { "cfn_type": "AWS::MediaPackage::PackagingConfiguration", "mappings": [ - { - "source": "CmafPackage", - "target": "CmafPackage" - }, - { - "source": "DashPackage", - "target": "DashPackage" - }, - { - "source": "HlsPackage", - "target": "HlsPackage" - }, { "source": "Id", "target": "Id" }, - { - "source": "MssPackage", - "target": "MssPackage" - }, { "source": "PackagingGroupId", "target": "PackagingGroupId" @@ -32128,14 +30923,6 @@ { "cfn_type": "AWS::MediaPackage::PackagingGroup", "mappings": [ - { - "source": "Authorization", - "target": "Authorization" - }, - { - "source": "EgressAccessLogs", - "target": "EgressAccessLogs" - }, { "source": "Id", "target": "Id" @@ -32163,6 +30950,9 @@ }, { "cfn_type": "AWS::MediaPackageV2::Channel", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ChannelGroupName", @@ -32176,17 +30966,13 @@ "source": "Description", "target": "Description" }, - { - "source": "InputSwitchConfiguration", - "target": "InputSwitchConfiguration" - }, { "source": "InputType", "target": "InputType" }, { - "source": "OutputHeaderConfiguration", - "target": "OutputHeaderConfiguration" + "source": "OutputLockingMode", + "target": "OutputLockingMode" }, { "source": "Tags", @@ -32215,6 +31001,9 @@ }, { "cfn_type": "AWS::MediaPackageV2::ChannelGroup", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ChannelGroupName", @@ -32283,6 +31072,9 @@ }, { "cfn_type": "AWS::MediaPackageV2::OriginEndpoint", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ChannelGroupName", @@ -32296,38 +31088,14 @@ "source": "ContainerType", "target": "ContainerType" }, - { - "source": "DashManifests", - "target": "DashManifests" - }, { "source": "Description", "target": "Description" }, - { - "source": "ForceEndpointErrorConfiguration", - "target": "ForceEndpointErrorConfiguration" - }, - { - "source": "HlsManifests", - "target": "HlsManifests" - }, - { - "source": "LowLatencyHlsManifests", - "target": "LowLatencyHlsManifests" - }, - { - "source": "MssManifests", - "target": "MssManifests" - }, { "source": "OriginEndpointName", "target": "OriginEndpointName" }, - { - "source": "Segment", - "target": "Segment" - }, { "source": "StartoverWindowSeconds", "target": "StartoverWindowSeconds" @@ -32335,6 +31103,10 @@ { "source": "Tags", "target": "Tags" + }, + { + "source": "UriSeparator", + "target": "UriSeparator" } ], "operation": "CreateOriginEndpoint", @@ -32364,10 +31136,6 @@ { "cfn_type": "AWS::MediaPackageV2::OriginEndpointPolicy", "mappings": [ - { - "source": "CdnAuthConfiguration", - "target": "CdnAuthConfiguration" - }, { "source": "ChannelGroupName", "target": "ChannelGroupName" @@ -32420,14 +31188,6 @@ "source": "ChannelName", "target": "ChannelName" }, - { - "source": "FillerSlate", - "target": "FillerSlate" - }, - { - "source": "Outputs", - "target": "Outputs" - }, { "source": "PlaybackMode", "target": "PlaybackMode" @@ -32439,10 +31199,6 @@ { "source": "Tier", "target": "Tier" - }, - { - "source": "TimeShiftConfiguration", - "target": "TimeShiftConfiguration" } ], "operation": "CreateChannel", @@ -32490,12 +31246,44 @@ "service": "mediatailor" }, { - "cfn_type": "AWS::MediaTailor::LiveSource", + "cfn_type": "AWS::MediaTailor::Function", "mappings": [ { - "source": "HttpPackageConfigurations", - "target": "HttpPackageConfigurations" + "source": "Description", + "target": "Description" + }, + { + "source": "FunctionId", + "target": "FunctionId" + }, + { + "source": "FunctionType", + "target": "FunctionType" }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "PutFunction", + "phase": "create", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::Function", + "mappings": [ + { + "source": "FunctionId", + "target": "FunctionId" + } + ], + "operation": "DeleteFunction", + "phase": "delete", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::LiveSource", + "mappings": [ { "source": "LiveSourceName", "target": "LiveSourceName" @@ -32532,46 +31320,14 @@ { "cfn_type": "AWS::MediaTailor::PlaybackConfiguration", "mappings": [ - { - "source": "AdConditioningConfiguration", - "target": "AdConditioningConfiguration" - }, { "source": "AdDecisionServerUrl", "target": "AdDecisionServerUrl" }, - { - "source": "AvailSuppression", - "target": "AvailSuppression" - }, - { - "source": "Bumper", - "target": "Bumper" - }, - { - "source": "CdnConfiguration", - "target": "CdnConfiguration" - }, - { - "source": "ConfigurationAliases", - "target": "ConfigurationAliases" - }, - { - "source": "DashConfiguration", - "target": "DashConfiguration" - }, { "source": "InsertionMode", "target": "InsertionMode" }, - { - "source": "LivePreRollConfiguration", - "target": "LivePreRollConfiguration" - }, - { - "source": "ManifestProcessingRules", - "target": "ManifestProcessingRules" - }, { "source": "Name", "target": "Name" @@ -32616,22 +31372,6 @@ { "cfn_type": "AWS::MediaTailor::SourceLocation", "mappings": [ - { - "source": "AccessConfiguration", - "target": "AccessConfiguration" - }, - { - "source": "DefaultSegmentDeliveryConfiguration", - "target": "DefaultSegmentDeliveryConfiguration" - }, - { - "source": "HttpConfiguration", - "target": "HttpConfiguration" - }, - { - "source": "SegmentDeliveryConfigurations", - "target": "SegmentDeliveryConfigurations" - }, { "source": "SourceLocationName", "target": "SourceLocationName" @@ -32660,10 +31400,6 @@ { "cfn_type": "AWS::MediaTailor::VodSource", "mappings": [ - { - "source": "HttpPackageConfigurations", - "target": "HttpPackageConfigurations" - }, { "source": "SourceLocationName", "target": "SourceLocationName" @@ -32704,10 +31440,6 @@ "source": "ACLName", "target": "ACLName" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UserNames", "target": "UserNames" @@ -32744,10 +31476,6 @@ "source": "ClusterName", "target": "ClusterName" }, - { - "source": "DataTiering", - "target": "DataTiering" - }, { "source": "Description", "target": "Description" @@ -32831,10 +31559,6 @@ { "source": "TLSEnabled", "target": "TLSEnabled" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateCluster", @@ -32895,10 +31619,6 @@ { "source": "TLSEnabled", "target": "TLSEnabled" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateMultiRegionCluster", @@ -32926,10 +31646,6 @@ { "source": "ParameterGroupName", "target": "ParameterGroupName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateParameterGroup", @@ -32962,10 +31678,6 @@ { "source": "SubnetIds", "target": "SubnetIds" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateSubnetGroup", @@ -32991,14 +31703,6 @@ "source": "AccessString", "target": "AccessString" }, - { - "source": "AuthenticationMode", - "target": "AuthenticationMode" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UserName", "target": "UserName" @@ -33067,6 +31771,10 @@ "source": "KmsKeyId", "target": "KmsKeyId" }, + { + "source": "NetworkType", + "target": "NetworkType" + }, { "source": "PreferredBackupWindow", "target": "PreferredBackupWindow" @@ -33079,10 +31787,6 @@ "source": "StorageEncrypted", "target": "StorageEncrypted" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VpcSecurityGroupIds", "target": "VpcSecurityGroupIds" @@ -33110,10 +31814,6 @@ { "source": "Description", "target": "Description" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDBClusterParameterGroup", @@ -33165,10 +31865,6 @@ { "source": "PubliclyAccessible", "target": "PubliclyAccessible" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDBInstance", @@ -33193,10 +31889,6 @@ { "source": "Description", "target": "Description" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDBParameterGroup", @@ -33224,10 +31916,6 @@ { "source": "SubnetIds", "target": "SubnetIds" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDBSubnetGroup", @@ -33272,10 +31960,6 @@ { "source": "SubscriptionName", "target": "SubscriptionName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateEventSubscription", @@ -33368,10 +32052,6 @@ { "source": "tags", "target": "Tags" - }, - { - "source": "vectorSearchConfiguration", - "target": "VectorSearchConfiguration" } ], "operation": "CreateGraph", @@ -33455,10 +32135,6 @@ "source": "AvailabilityZoneChangeProtection", "target": "AvailabilityZoneChangeProtection" }, - { - "source": "AvailabilityZoneMappings", - "target": "AvailabilityZoneMappings" - }, { "source": "DeleteProtection", "target": "DeleteProtection" @@ -33487,14 +32163,6 @@ "source": "SubnetChangeProtection", "target": "SubnetChangeProtection" }, - { - "source": "SubnetMappings", - "target": "SubnetMappings" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TransitGatewayId", "target": "TransitGatewayId" @@ -33522,22 +32190,17 @@ }, { "cfn_type": "AWS::NetworkFirewall::FirewallPolicy", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "Description", "target": "Description" }, - { - "source": "FirewallPolicy", - "target": "FirewallPolicy" - }, { "source": "FirewallPolicyName", "target": "FirewallPolicyName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateFirewallPolicy", @@ -33558,6 +32221,9 @@ }, { "cfn_type": "AWS::NetworkFirewall::RuleGroup", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "Capacity", @@ -33567,22 +32233,10 @@ "source": "Description", "target": "Description" }, - { - "source": "RuleGroup", - "target": "RuleGroup" - }, { "source": "RuleGroupName", "target": "RuleGroupName" }, - { - "source": "SummaryConfiguration", - "target": "SummaryConfiguration" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" @@ -33615,17 +32269,9 @@ "source": "Description", "target": "Description" }, - { - "source": "TLSInspectionConfiguration", - "target": "TLSInspectionConfiguration" - }, { "source": "TLSInspectionConfigurationName", "target": "TLSInspectionConfigurationName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateTLSInspectionConfiguration", @@ -33655,14 +32301,6 @@ "source": "FirewallArn", "target": "FirewallArn" }, - { - "source": "SubnetMapping", - "target": "SubnetMapping" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VpcId", "target": "VpcId" @@ -33681,19 +32319,14 @@ }, { "cfn_type": "AWS::NetworkFlowMonitor::Monitor", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "localResources", - "target": "LocalResources" - }, { "source": "monitorName", "target": "MonitorName" }, - { - "source": "remoteResources", - "target": "RemoteResources" - }, { "source": "scopeArn", "target": "ScopeArn" @@ -33721,6 +32354,9 @@ }, { "cfn_type": "AWS::NetworkManager::ConnectAttachment", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "CoreNetworkId", @@ -33731,12 +32367,8 @@ "target": "EdgeLocation" }, { - "source": "Options", - "target": "Options" - }, - { - "source": "Tags", - "target": "Tags" + "source": "RoutingPolicyLabel", + "target": "RoutingPolicyLabel" }, { "source": "TransportAttachmentId", @@ -33749,11 +32381,10 @@ }, { "cfn_type": "AWS::NetworkManager::ConnectPeer", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "BgpOptions", - "target": "BgpOptions" - }, { "source": "ConnectAttachmentId", "target": "ConnectAttachmentId" @@ -33773,10 +32404,6 @@ { "source": "SubnetArn", "target": "SubnetArn" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateConnectPeer", @@ -33792,6 +32419,9 @@ }, { "cfn_type": "AWS::NetworkManager::CoreNetwork", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -33804,10 +32434,6 @@ { "source": "PolicyDocument", "target": "PolicyDocument" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateCoreNetwork", @@ -33821,6 +32447,45 @@ "phase": "delete", "service": "networkmanager" }, + { + "cfn_type": "AWS::NetworkManager::CoreNetworkPrefixListAssociation", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "CoreNetworkId", + "target": "CoreNetworkId" + }, + { + "source": "PrefixListAlias", + "target": "PrefixListAlias" + }, + { + "source": "PrefixListArn", + "target": "PrefixListArn" + } + ], + "operation": "CreateCoreNetworkPrefixListAssociation", + "phase": "create", + "service": "networkmanager" + }, + { + "cfn_type": "AWS::NetworkManager::CoreNetworkPrefixListAssociation", + "mappings": [ + { + "source": "CoreNetworkId", + "target": "CoreNetworkId" + }, + { + "source": "PrefixListArn", + "target": "PrefixListArn" + } + ], + "operation": "DeleteCoreNetworkPrefixListAssociation", + "phase": "delete", + "service": "networkmanager" + }, { "cfn_type": "AWS::NetworkManager::CustomerGatewayAssociation", "mappings": [ @@ -33864,10 +32529,6 @@ { "cfn_type": "AWS::NetworkManager::Device", "mappings": [ - { - "source": "AWSLocation", - "target": "AWSLocation" - }, { "source": "Description", "target": "Description" @@ -33876,10 +32537,6 @@ "source": "GlobalNetworkId", "target": "GlobalNetworkId" }, - { - "source": "Location", - "target": "Location" - }, { "source": "Model", "target": "Model" @@ -33892,10 +32549,6 @@ "source": "SiteId", "target": "SiteId" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" @@ -33923,6 +32576,9 @@ }, { "cfn_type": "AWS::NetworkManager::DirectConnectGatewayAttachment", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "CoreNetworkId", @@ -33937,8 +32593,8 @@ "target": "EdgeLocations" }, { - "source": "Tags", - "target": "Tags" + "source": "RoutingPolicyLabel", + "target": "RoutingPolicyLabel" } ], "operation": "CreateDirectConnectGatewayAttachment", @@ -33951,10 +32607,6 @@ { "source": "Description", "target": "Description" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateGlobalNetwork", @@ -33971,10 +32623,6 @@ { "cfn_type": "AWS::NetworkManager::Link", "mappings": [ - { - "source": "Bandwidth", - "target": "Bandwidth" - }, { "source": "Description", "target": "Description" @@ -33991,10 +32639,6 @@ "source": "SiteId", "target": "SiteId" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" @@ -34066,14 +32710,6 @@ { "source": "GlobalNetworkId", "target": "GlobalNetworkId" - }, - { - "source": "Location", - "target": "Location" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateSite", @@ -34094,14 +32730,17 @@ }, { "cfn_type": "AWS::NetworkManager::SiteToSiteVpnAttachment", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "CoreNetworkId", "target": "CoreNetworkId" }, { - "source": "Tags", - "target": "Tags" + "source": "RoutingPolicyLabel", + "target": "RoutingPolicyLabel" }, { "source": "VpnConnectionArn", @@ -34114,15 +32753,14 @@ }, { "cfn_type": "AWS::NetworkManager::TransitGatewayPeering", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "CoreNetworkId", "target": "CoreNetworkId" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TransitGatewayArn", "target": "TransitGatewayArn" @@ -34166,14 +32804,17 @@ }, { "cfn_type": "AWS::NetworkManager::TransitGatewayRouteTableAttachment", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "PeeringId", "target": "PeeringId" }, { - "source": "Tags", - "target": "Tags" + "source": "RoutingPolicyLabel", + "target": "RoutingPolicyLabel" }, { "source": "TransitGatewayRouteTableArn", @@ -34186,23 +32827,22 @@ }, { "cfn_type": "AWS::NetworkManager::VpcAttachment", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "CoreNetworkId", "target": "CoreNetworkId" }, { - "source": "Options", - "target": "Options" + "source": "RoutingPolicyLabel", + "target": "RoutingPolicyLabel" }, { "source": "SubnetArns", "target": "SubnetArns" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VpcArn", "target": "VpcArn" @@ -34381,6 +33021,38 @@ "phase": "delete", "service": "notifications" }, + { + "cfn_type": "AWS::Notifications::OrganizationalUnitAssociation", + "mappings": [ + { + "source": "notificationConfigurationArn", + "target": "NotificationConfigurationArn" + }, + { + "source": "organizationalUnitId", + "target": "OrganizationalUnitId" + } + ], + "operation": "AssociateOrganizationalUnit", + "phase": "create", + "service": "notifications" + }, + { + "cfn_type": "AWS::Notifications::OrganizationalUnitAssociation", + "mappings": [ + { + "source": "notificationConfigurationArn", + "target": "NotificationConfigurationArn" + }, + { + "source": "organizationalUnitId", + "target": "OrganizationalUnitId" + } + ], + "operation": "DisassociateOrganizationalUnit", + "phase": "delete", + "service": "notifications" + }, { "cfn_type": "AWS::NotificationsContacts::EmailContact", "mappings": [ @@ -34408,8 +33080,37 @@ "phase": "delete", "service": "notificationscontacts" }, + { + "cfn_type": "AWS::NovaAct::WorkflowDefinition", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + } + ], + "operation": "CreateWorkflowDefinition", + "phase": "create", + "service": "nova-act" + }, + { + "cfn_type": "AWS::NovaAct::WorkflowDefinition", + "mappings": [], + "operation": "DeleteWorkflowDefinition", + "phase": "delete", + "service": "nova-act" + }, { "cfn_type": "AWS::ODB::CloudAutonomousVmCluster", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "autonomousDataStorageSizeInTBs", @@ -34443,10 +33144,6 @@ "source": "licenseModel", "target": "LicenseModel" }, - { - "source": "maintenanceWindow", - "target": "MaintenanceWindow" - }, { "source": "memoryPerOracleComputeUnitInGBs", "target": "MemoryPerOracleComputeUnitInGBs" @@ -34489,6 +33186,9 @@ }, { "cfn_type": "AWS::ODB::CloudExadataInfrastructure", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "availabilityZone", @@ -34502,10 +33202,6 @@ "source": "computeCount", "target": "ComputeCount" }, - { - "source": "customerContactsToSendToOCI", - "target": "CustomerContactsToSendToOCI" - }, { "source": "databaseServerType", "target": "DatabaseServerType" @@ -34514,10 +33210,6 @@ "source": "displayName", "target": "DisplayName" }, - { - "source": "maintenanceWindow", - "target": "MaintenanceWindow" - }, { "source": "shape", "target": "Shape" @@ -34548,6 +33240,9 @@ }, { "cfn_type": "AWS::ODB::CloudVmCluster", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "cloudExadataInfrastructureId", @@ -34561,10 +33256,6 @@ "source": "cpuCoreCount", "target": "CpuCoreCount" }, - { - "source": "dataCollectionOptions", - "target": "DataCollectionOptions" - }, { "source": "dataStorageSizeInTBs", "target": "DataStorageSizeInTBs" @@ -34643,6 +33334,9 @@ }, { "cfn_type": "AWS::ODB::OdbNetwork", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "availabilityZone", @@ -34672,6 +33366,14 @@ "source": "displayName", "target": "DisplayName" }, + { + "source": "kmsAccess", + "target": "KmsAccess" + }, + { + "source": "kmsPolicyDocument", + "target": "KmsPolicyDocument" + }, { "source": "s3Access", "target": "S3Access" @@ -34680,6 +33382,14 @@ "source": "s3PolicyDocument", "target": "S3PolicyDocument" }, + { + "source": "stsAccess", + "target": "StsAccess" + }, + { + "source": "stsPolicyDocument", + "target": "StsPolicyDocument" + }, { "source": "tags", "target": "Tags" @@ -34707,6 +33417,9 @@ }, { "cfn_type": "AWS::ODB::OdbPeeringConnection", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "displayName", @@ -34720,6 +33433,10 @@ "source": "peerNetworkId", "target": "PeerNetworkId" }, + { + "source": "peerNetworkRouteTableIds", + "target": "PeerNetworkRouteTableIds" + }, { "source": "tags", "target": "Tags" @@ -34739,18 +33456,6 @@ { "cfn_type": "AWS::OSIS::Pipeline", "mappings": [ - { - "source": "BufferOptions", - "target": "BufferOptions" - }, - { - "source": "EncryptionAtRestOptions", - "target": "EncryptionAtRestOptions" - }, - { - "source": "LogPublishingOptions", - "target": "LogPublishingOptions" - }, { "source": "MaxUnits", "target": "MaxUnits" @@ -34770,14 +33475,6 @@ { "source": "PipelineRoleArn", "target": "PipelineRoleArn" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "VpcOptions", - "target": "VpcOptions" } ], "operation": "CreatePipeline", @@ -34803,10 +33500,6 @@ "source": "LabelTemplate", "target": "LabelTemplate" }, - { - "source": "LinkConfiguration", - "target": "LinkConfiguration" - }, { "source": "ResourceTypes", "target": "ResourceTypes" @@ -34814,10 +33507,6 @@ { "source": "SinkIdentifier", "target": "SinkIdentifier" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateLink", @@ -34837,10 +33526,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateSink", @@ -34855,12 +33540,24 @@ "service": "oam" }, { - "cfn_type": "AWS::ObservabilityAdmin::OrganizationTelemetryRule", + "cfn_type": "AWS::ObservabilityAdmin::OrganizationCentralizationRule", "mappings": [ { - "source": "Rule", - "target": "Rule" + "source": "RuleName", + "target": "RuleName" }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateCentralizationRuleForOrganization", + "phase": "create", + "service": "observabilityadmin" + }, + { + "cfn_type": "AWS::ObservabilityAdmin::OrganizationTelemetryRule", + "mappings": [ { "source": "RuleName", "target": "RuleName" @@ -34875,12 +33572,47 @@ "service": "observabilityadmin" }, { - "cfn_type": "AWS::ObservabilityAdmin::TelemetryRule", + "cfn_type": "AWS::ObservabilityAdmin::S3TableIntegration", + "mappings": [ + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateS3TableIntegration", + "phase": "create", + "service": "observabilityadmin" + }, + { + "cfn_type": "AWS::ObservabilityAdmin::S3TableIntegration", + "mappings": [], + "operation": "DeleteS3TableIntegration", + "phase": "delete", + "service": "observabilityadmin" + }, + { + "cfn_type": "AWS::ObservabilityAdmin::TelemetryPipelines", "mappings": [ { - "source": "Rule", - "target": "Rule" + "source": "Name", + "target": "Name" }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateTelemetryPipeline", + "phase": "create", + "service": "observabilityadmin" + }, + { + "cfn_type": "AWS::ObservabilityAdmin::TelemetryRule", + "mappings": [ { "source": "RuleName", "target": "RuleName" @@ -34912,25 +33644,9 @@ "source": "name", "target": "Name" }, - { - "source": "reference", - "target": "Reference" - }, - { - "source": "sseConfig", - "target": "SseConfig" - }, { "source": "storeFormat", "target": "StoreFormat" - }, - { - "source": "storeOptions", - "target": "StoreOptions" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateAnnotationStore", @@ -34950,7 +33666,10 @@ "service": "omics" }, { - "cfn_type": "AWS::Omics::ReferenceStore", + "cfn_type": "AWS::Omics::Configuration", + "ignored_inputs": [ + "requestId" + ], "mappings": [ { "source": "description", @@ -34959,14 +33678,34 @@ { "source": "name", "target": "Name" - }, + } + ], + "operation": "CreateConfiguration", + "phase": "create", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::Configuration", + "mappings": [ + { + "source": "name", + "target": "Name" + } + ], + "operation": "DeleteConfiguration", + "phase": "delete", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::ReferenceStore", + "mappings": [ { - "source": "sseConfig", - "target": "SseConfig" + "source": "description", + "target": "Description" }, { - "source": "tags", - "target": "Tags" + "source": "name", + "target": "Name" } ], "operation": "CreateReferenceStore", @@ -34982,6 +33721,9 @@ }, { "cfn_type": "AWS::Omics::RunGroup", + "ignored_inputs": [ + "requestId" + ], "mappings": [ { "source": "maxCpus", @@ -35002,10 +33744,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateRunGroup", @@ -35021,6 +33759,9 @@ }, { "cfn_type": "AWS::Omics::SequenceStore", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -35041,14 +33782,6 @@ { "source": "propagatedSetLevelTags", "target": "PropagatedSetLevelTags" - }, - { - "source": "sseConfig", - "target": "SseConfig" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateSequenceStore", @@ -35072,18 +33805,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "reference", - "target": "Reference" - }, - { - "source": "sseConfig", - "target": "SseConfig" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateVariantStore", @@ -35104,14 +33825,17 @@ }, { "cfn_type": "AWS::Omics::Workflow", + "ignored_inputs": [ + "requestId" + ], "mappings": [ { "source": "accelerators", "target": "Accelerators" }, { - "source": "definitionRepository", - "target": "DefinitionRepository" + "source": "containerRegistryMapUri", + "target": "ContainerRegistryMapUri" }, { "source": "definitionUri", @@ -35133,10 +33857,6 @@ "source": "name", "target": "Name" }, - { - "source": "parameterTemplate", - "target": "ParameterTemplate" - }, { "source": "parameterTemplatePath", "target": "ParameterTemplatePath" @@ -35161,10 +33881,6 @@ "source": "storageType", "target": "StorageType" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "workflowBucketOwnerId", "target": "WorkflowBucketOwnerId" @@ -35183,14 +33899,17 @@ }, { "cfn_type": "AWS::Omics::WorkflowVersion", + "ignored_inputs": [ + "requestId" + ], "mappings": [ { "source": "accelerators", "target": "Accelerators" }, { - "source": "definitionRepository", - "target": "DefinitionRepository" + "source": "containerRegistryMapUri", + "target": "ContainerRegistryMapUri" }, { "source": "definitionUri", @@ -35208,10 +33927,6 @@ "source": "main", "target": "Main" }, - { - "source": "parameterTemplate", - "target": "ParameterTemplate" - }, { "source": "parameterTemplatePath", "target": "ParameterTemplatePath" @@ -35236,10 +33951,6 @@ "source": "storageType", "target": "StorageType" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "versionName", "target": "VersionName" @@ -35273,8 +33984,47 @@ "phase": "delete", "service": "omics" }, + { + "cfn_type": "AWS::OpenSearch::DataSource", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "AddDataSource", + "phase": "create", + "service": "opensearch" + }, + { + "cfn_type": "AWS::OpenSearch::DataSource", + "mappings": [ + { + "source": "DomainName", + "target": "DomainName" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteDataSource", + "phase": "delete", + "service": "opensearch" + }, { "cfn_type": "AWS::OpenSearchServerless::AccessPolicy", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -35299,6 +34049,9 @@ }, { "cfn_type": "AWS::OpenSearchServerless::AccessPolicy", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "name", @@ -35315,7 +34068,18 @@ }, { "cfn_type": "AWS::OpenSearchServerless::Collection", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ + { + "source": "collectionGroupName", + "target": "CollectionGroupName" + }, + { + "source": "deletionProtection", + "target": "DeletionProtection" + }, { "source": "description", "target": "Description" @@ -35328,10 +34092,6 @@ "source": "standbyReplicas", "target": "StandbyReplicas" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "type", "target": "Type" @@ -35343,11 +34103,51 @@ }, { "cfn_type": "AWS::OpenSearchServerless::Collection", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteCollection", "phase": "delete", "service": "opensearchserverless" }, + { + "cfn_type": "AWS::OpenSearchServerless::CollectionGroup", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "generation", + "target": "Generation" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "standbyReplicas", + "target": "StandbyReplicas" + } + ], + "operation": "CreateCollectionGroup", + "phase": "create", + "service": "opensearchserverless" + }, + { + "cfn_type": "AWS::OpenSearchServerless::CollectionGroup", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [], + "operation": "DeleteCollectionGroup", + "phase": "delete", + "service": "opensearchserverless" + }, { "cfn_type": "AWS::OpenSearchServerless::CollectionIndex", "mappings": [ @@ -35358,10 +34158,6 @@ { "source": "indexName", "target": "IndexName" - }, - { - "source": "indexSchema", - "target": "IndexSchema" } ], "operation": "CreateIndex", @@ -35386,6 +34182,9 @@ }, { "cfn_type": "AWS::OpenSearchServerless::LifecyclePolicy", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -35410,6 +34209,9 @@ }, { "cfn_type": "AWS::OpenSearchServerless::LifecyclePolicy", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "name", @@ -35426,27 +34228,18 @@ }, { "cfn_type": "AWS::OpenSearchServerless::SecurityConfig", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", "target": "Description" }, - { - "source": "iamFederationOptions", - "target": "IamFederationOptions" - }, - { - "source": "iamIdentityCenterOptions", - "target": "IamIdentityCenterOptions" - }, { "source": "name", "target": "Name" }, - { - "source": "samlOptions", - "target": "SamlOptions" - }, { "source": "type", "target": "Type" @@ -35458,6 +34251,9 @@ }, { "cfn_type": "AWS::OpenSearchServerless::SecurityConfig", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteSecurityConfig", "phase": "delete", @@ -35465,6 +34261,9 @@ }, { "cfn_type": "AWS::OpenSearchServerless::SecurityPolicy", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -35489,6 +34288,9 @@ }, { "cfn_type": "AWS::OpenSearchServerless::SecurityPolicy", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "name", @@ -35505,6 +34307,9 @@ }, { "cfn_type": "AWS::OpenSearchServerless::VpcEndpoint", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "name", @@ -35529,18 +34334,14 @@ }, { "cfn_type": "AWS::OpenSearchServerless::VpcEndpoint", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteVpcEndpoint", "phase": "delete", "service": "opensearchserverless" }, - { - "cfn_type": "AWS::OpenSearchService::Application", - "mappings": [], - "operation": "DeleteApplication", - "phase": "delete", - "service": "opensearch" - }, { "cfn_type": "AWS::OpenSearchService::Domain", "mappings": [ @@ -35567,10 +34368,6 @@ { "source": "RoleName", "target": "RoleName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAccount", @@ -35606,10 +34403,6 @@ { "source": "ParentId", "target": "ParentId" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateOrganizationalUnit", @@ -35638,10 +34431,6 @@ "source": "Name", "target": "Name" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" @@ -35651,16 +34440,19 @@ "phase": "create", "service": "organizations" }, + { + "cfn_type": "AWS::Organizations::Policy", + "mappings": [], + "operation": "DeletePolicy", + "phase": "delete", + "service": "organizations" + }, { "cfn_type": "AWS::Organizations::ResourcePolicy", "mappings": [ { "source": "Content", "target": "Content" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "PutResourcePolicy", @@ -35689,18 +34481,6 @@ "source": "Notes", "target": "Notes" }, - { - "source": "OperatingAddress", - "target": "OperatingAddress" - }, - { - "source": "RackPhysicalProperties", - "target": "RackPhysicalProperties" - }, - { - "source": "ShippingAddress", - "target": "ShippingAddress" - }, { "source": "Tags", "target": "Tags" @@ -35719,6 +34499,9 @@ }, { "cfn_type": "AWS::PCAConnectorAD::Connector", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "CertificateAuthorityArn", @@ -35727,14 +34510,6 @@ { "source": "DirectoryId", "target": "DirectoryId" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "VpcInformation", - "target": "VpcInformation" } ], "operation": "CreateConnector", @@ -35750,14 +34525,13 @@ }, { "cfn_type": "AWS::PCAConnectorAD::DirectoryRegistration", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "DirectoryId", "target": "DirectoryId" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDirectoryRegistration", @@ -35773,6 +34547,9 @@ }, { "cfn_type": "AWS::PCAConnectorAD::ServicePrincipalName", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ConnectorArn", @@ -35805,22 +34582,17 @@ }, { "cfn_type": "AWS::PCAConnectorAD::Template", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ConnectorArn", "target": "ConnectorArn" }, - { - "source": "Definition", - "target": "Definition" - }, { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateTemplate", @@ -35836,11 +34608,10 @@ }, { "cfn_type": "AWS::PCAConnectorAD::TemplateGroupAccessControlEntry", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "AccessRights", - "target": "AccessRights" - }, { "source": "GroupDisplayName", "target": "GroupDisplayName" @@ -35876,14 +34647,13 @@ }, { "cfn_type": "AWS::PCAConnectorSCEP::Challenge", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ConnectorArn", "target": "ConnectorArn" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateChallenge", @@ -35899,18 +34669,17 @@ }, { "cfn_type": "AWS::PCAConnectorSCEP::Connector", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "CertificateAuthorityArn", "target": "CertificateAuthorityArn" }, { - "source": "MobileDeviceManagement", - "target": "MobileDeviceManagement" - }, - { - "source": "Tags", - "target": "Tags" + "source": "VpcEndpointId", + "target": "VpcEndpointId" } ], "operation": "CreateConnector", @@ -35926,26 +34695,13 @@ }, { "cfn_type": "AWS::PCS::Cluster", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "networking", - "target": "Networking" - }, - { - "source": "scheduler", - "target": "Scheduler" - }, { "source": "size", "target": "Size" - }, - { - "source": "slurmConfiguration", - "target": "SlurmConfiguration" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateCluster", @@ -35954,6 +34710,9 @@ }, { "cfn_type": "AWS::PCS::Cluster", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteCluster", "phase": "delete", @@ -35961,46 +34720,25 @@ }, { "cfn_type": "AWS::PCS::ComputeNodeGroup", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "amiId", "target": "AmiId" }, - { - "source": "customLaunchTemplate", - "target": "CustomLaunchTemplate" - }, { "source": "iamInstanceProfileArn", "target": "IamInstanceProfileArn" }, - { - "source": "instanceConfigs", - "target": "InstanceConfigs" - }, { "source": "purchaseOption", "target": "PurchaseOption" }, - { - "source": "scalingConfiguration", - "target": "ScalingConfiguration" - }, - { - "source": "slurmConfiguration", - "target": "SlurmConfiguration" - }, - { - "source": "spotOptions", - "target": "SpotOptions" - }, { "source": "subnetIds", "target": "SubnetIds" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateComputeNodeGroup", @@ -36009,6 +34747,9 @@ }, { "cfn_type": "AWS::PCS::ComputeNodeGroup", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteComputeNodeGroup", "phase": "delete", @@ -36016,153 +34757,14 @@ }, { "cfn_type": "AWS::PCS::Queue", - "mappings": [ - { - "source": "computeNodeGroupConfigurations", - "target": "ComputeNodeGroupConfigurations" - }, - { - "source": "tags", - "target": "Tags" - } + "ignored_inputs": [ + "clientToken" ], - "operation": "CreateQueue", - "phase": "create", - "service": "pcs" - }, - { - "cfn_type": "AWS::PCS::Queue", "mappings": [], "operation": "DeleteQueue", "phase": "delete", "service": "pcs" }, - { - "cfn_type": "AWS::Panorama::ApplicationInstance", - "mappings": [ - { - "source": "ApplicationInstanceIdToReplace", - "target": "ApplicationInstanceIdToReplace" - }, - { - "source": "DefaultRuntimeContextDevice", - "target": "DefaultRuntimeContextDevice" - }, - { - "source": "Description", - "target": "Description" - }, - { - "source": "ManifestOverridesPayload", - "target": "ManifestOverridesPayload" - }, - { - "source": "ManifestPayload", - "target": "ManifestPayload" - }, - { - "source": "Name", - "target": "Name" - }, - { - "source": "RuntimeRoleArn", - "target": "RuntimeRoleArn" - }, - { - "source": "Tags", - "target": "Tags" - } - ], - "operation": "CreateApplicationInstance", - "phase": "create", - "service": "panorama" - }, - { - "cfn_type": "AWS::Panorama::ApplicationInstance", - "mappings": [], - "operation": "RemoveApplicationInstance", - "phase": "delete", - "service": "panorama" - }, - { - "cfn_type": "AWS::Panorama::Package", - "mappings": [ - { - "source": "PackageName", - "target": "PackageName" - }, - { - "source": "Tags", - "target": "Tags" - } - ], - "operation": "CreatePackage", - "phase": "create", - "service": "panorama" - }, - { - "cfn_type": "AWS::Panorama::Package", - "mappings": [], - "operation": "DeletePackage", - "phase": "delete", - "service": "panorama" - }, - { - "cfn_type": "AWS::Panorama::PackageVersion", - "mappings": [ - { - "source": "MarkLatest", - "target": "MarkLatest" - }, - { - "source": "OwnerAccount", - "target": "OwnerAccount" - }, - { - "source": "PackageId", - "target": "PackageId" - }, - { - "source": "PackageVersion", - "target": "PackageVersion" - }, - { - "source": "PatchVersion", - "target": "PatchVersion" - } - ], - "operation": "RegisterPackageVersion", - "phase": "create", - "service": "panorama" - }, - { - "cfn_type": "AWS::Panorama::PackageVersion", - "mappings": [ - { - "source": "OwnerAccount", - "target": "OwnerAccount" - }, - { - "source": "PackageId", - "target": "PackageId" - }, - { - "source": "PackageVersion", - "target": "PackageVersion" - }, - { - "source": "PatchVersion", - "target": "PatchVersion" - }, - { - "source": "UpdatedLatestPatchVersion", - "target": "UpdatedLatestPatchVersion" - } - ], - "operation": "DeregisterPackageVersion", - "phase": "delete", - "service": "panorama" - }, { "cfn_type": "AWS::PaymentCryptography::Alias", "mappings": [ @@ -36206,17 +34808,13 @@ "source": "Exportable", "target": "Exportable" }, - { - "source": "KeyAttributes", - "target": "KeyAttributes" - }, { "source": "KeyCheckValueAlgorithm", "target": "KeyCheckValueAlgorithm" }, { - "source": "Tags", - "target": "Tags" + "source": "ReplicationRegions", + "target": "ReplicationRegions" } ], "operation": "CreateKey", @@ -36292,6 +34890,29 @@ "phase": "delete", "service": "personalize" }, + { + "cfn_type": "AWS::Personalize::EventTracker", + "mappings": [ + { + "source": "datasetGroupArn", + "target": "DatasetGroupArn" + }, + { + "source": "name", + "target": "Name" + } + ], + "operation": "CreateEventTracker", + "phase": "create", + "service": "personalize" + }, + { + "cfn_type": "AWS::Personalize::EventTracker", + "mappings": [], + "operation": "DeleteEventTracker", + "phase": "delete", + "service": "personalize" + }, { "cfn_type": "AWS::Personalize::Schema", "mappings": [ @@ -36345,10 +34966,6 @@ { "source": "recipeArn", "target": "RecipeArn" - }, - { - "source": "solutionConfig", - "target": "SolutionConfig" } ], "operation": "CreateSolution", @@ -36401,18 +35018,10 @@ "source": "Enrichment", "target": "Enrichment" }, - { - "source": "EnrichmentParameters", - "target": "EnrichmentParameters" - }, { "source": "KmsKeyIdentifier", "target": "KmsKeyIdentifier" }, - { - "source": "LogConfiguration", - "target": "LogConfiguration" - }, { "source": "Name", "target": "Name" @@ -36425,21 +35034,9 @@ "source": "Source", "target": "Source" }, - { - "source": "SourceParameters", - "target": "SourceParameters" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Target", "target": "Target" - }, - { - "source": "TargetParameters", - "target": "TargetParameters" } ], "operation": "CreatePipe", @@ -36460,6 +35057,9 @@ }, { "cfn_type": "AWS::Proton::EnvironmentAccountConnection", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "codebuildRoleArn", @@ -36480,10 +35080,6 @@ { "source": "roleArn", "target": "RoleArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateEnvironmentAccountConnection", @@ -36519,10 +35115,6 @@ { "source": "provisioning", "target": "Provisioning" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateEnvironmentTemplate", @@ -36563,10 +35155,6 @@ { "source": "pipelineProvisioning", "target": "PipelineProvisioning" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateServiceTemplate", @@ -36587,11 +35175,10 @@ }, { "cfn_type": "AWS::QBusiness::Application", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "attachmentsConfiguration", - "target": "AttachmentsConfiguration" - }, { "source": "clientIdsForOIDC", "target": "ClientIdsForOIDC" @@ -36604,10 +35191,6 @@ "source": "displayName", "target": "DisplayName" }, - { - "source": "encryptionConfiguration", - "target": "EncryptionConfiguration" - }, { "source": "iamIdentityProviderArn", "target": "IamIdentityProviderArn" @@ -36620,25 +35203,9 @@ "source": "identityType", "target": "IdentityType" }, - { - "source": "personalizationConfiguration", - "target": "PersonalizationConfiguration" - }, - { - "source": "qAppsConfiguration", - "target": "QAppsConfiguration" - }, - { - "source": "quickSightConfiguration", - "target": "QuickSightConfiguration" - }, { "source": "roleArn", "target": "RoleArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateApplication", @@ -36654,19 +35221,14 @@ }, { "cfn_type": "AWS::QBusiness::DataAccessor", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "actionConfigurations", - "target": "ActionConfigurations" - }, { "source": "applicationId", "target": "ApplicationId" }, - { - "source": "authenticationDetail", - "target": "AuthenticationDetail" - }, { "source": "displayName", "target": "DisplayName" @@ -36674,10 +35236,6 @@ { "source": "principal", "target": "Principal" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateDataAccessor", @@ -36698,15 +35256,14 @@ }, { "cfn_type": "AWS::QBusiness::DataSource", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "applicationId", "target": "ApplicationId" }, - { - "source": "configuration", - "target": "Configuration" - }, { "source": "description", "target": "Description" @@ -36715,18 +35272,10 @@ "source": "displayName", "target": "DisplayName" }, - { - "source": "documentEnrichmentConfiguration", - "target": "DocumentEnrichmentConfiguration" - }, { "source": "indexId", "target": "IndexId" }, - { - "source": "mediaExtractionConfiguration", - "target": "MediaExtractionConfiguration" - }, { "source": "roleArn", "target": "RoleArn" @@ -36734,14 +35283,6 @@ { "source": "syncSchedule", "target": "SyncSchedule" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "vpcConfiguration", - "target": "VpcConfiguration" } ], "operation": "CreateDataSource", @@ -36766,15 +35307,14 @@ }, { "cfn_type": "AWS::QBusiness::Index", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "applicationId", "target": "ApplicationId" }, - { - "source": "capacityConfiguration", - "target": "CapacityConfiguration" - }, { "source": "description", "target": "Description" @@ -36783,10 +35323,6 @@ "source": "displayName", "target": "DisplayName" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "type", "target": "Type" @@ -36819,10 +35355,6 @@ "source": "applicationId", "target": "ApplicationId" }, - { - "source": "conditions", - "target": "Conditions" - }, { "source": "principal", "target": "Principal" @@ -36854,19 +35386,14 @@ }, { "cfn_type": "AWS::QBusiness::Plugin", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "applicationId", "target": "ApplicationId" }, - { - "source": "authConfiguration", - "target": "AuthConfiguration" - }, - { - "source": "customPluginConfiguration", - "target": "CustomPluginConfiguration" - }, { "source": "displayName", "target": "DisplayName" @@ -36875,10 +35402,6 @@ "source": "serverUrl", "target": "ServerUrl" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "type", "target": "Type" @@ -36902,15 +35425,14 @@ }, { "cfn_type": "AWS::QBusiness::Retriever", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "applicationId", "target": "ApplicationId" }, - { - "source": "configuration", - "target": "Configuration" - }, { "source": "displayName", "target": "DisplayName" @@ -36919,10 +35441,6 @@ "source": "roleArn", "target": "RoleArn" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "type", "target": "Type" @@ -36946,23 +35464,14 @@ }, { "cfn_type": "AWS::QBusiness::WebExperience", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "applicationId", "target": "ApplicationId" }, - { - "source": "browserExtensionConfiguration", - "target": "BrowserExtensionConfiguration" - }, - { - "source": "customizationConfiguration", - "target": "CustomizationConfiguration" - }, - { - "source": "identityProviderConfiguration", - "target": "IdentityProviderConfiguration" - }, { "source": "origins", "target": "Origins" @@ -36979,10 +35488,6 @@ "source": "subtitle", "target": "Subtitle" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "title", "target": "Title" @@ -37009,63 +35514,139 @@ "service": "qbusiness" }, { - "cfn_type": "AWS::QLDB::Stream", + "cfn_type": "AWS::QuickSight::ActionConnector", + "mappings": [ + { + "source": "ActionConnectorId", + "target": "ActionConnectorId" + }, + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "Type", + "target": "Type" + }, + { + "source": "VpcConnectionArn", + "target": "VpcConnectionArn" + } + ], + "operation": "CreateActionConnector", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::ActionConnector", "mappings": [ { - "source": "LedgerName", - "target": "LedgerName" + "source": "ActionConnectorId", + "target": "ActionConnectorId" + }, + { + "source": "AwsAccountId", + "target": "AwsAccountId" } ], - "operation": "CancelJournalKinesisStream", + "operation": "DeleteActionConnector", "phase": "delete", - "service": "qldb" + "service": "quicksight" }, { - "cfn_type": "AWS::QuickSight::Analysis", + "cfn_type": "AWS::QuickSight::Agent", "mappings": [ { - "source": "AnalysisId", - "target": "AnalysisId" + "source": "ActionConnectors", + "target": "ActionConnectors" + }, + { + "source": "AgentId", + "target": "AgentId" + }, + { + "source": "AgentLifecycle", + "target": "AgentLifecycle" }, { "source": "AwsAccountId", "target": "AwsAccountId" }, { - "source": "Definition", - "target": "Definition" + "source": "Description", + "target": "Description" }, { - "source": "FolderArns", - "target": "FolderArns" + "source": "IconId", + "target": "IconId" }, { "source": "Name", "target": "Name" }, { - "source": "Parameters", - "target": "Parameters" + "source": "Spaces", + "target": "Spaces" }, { - "source": "Permissions", - "target": "Permissions" + "source": "StarterPrompts", + "target": "StarterPrompts" }, { - "source": "SourceEntity", - "target": "SourceEntity" + "source": "WelcomeMessage", + "target": "WelcomeMessage" + } + ], + "operation": "CreateAgent", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Agent", + "mappings": [ + { + "source": "AgentId", + "target": "AgentId" }, { - "source": "Tags", - "target": "Tags" + "source": "AwsAccountId", + "target": "AwsAccountId" + } + ], + "operation": "DeleteAgent", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Analysis", + "mappings": [ + { + "source": "AnalysisId", + "target": "AnalysisId" }, { - "source": "ThemeArn", - "target": "ThemeArn" + "source": "AwsAccountId", + "target": "AwsAccountId" }, { - "source": "ValidationStrategy", - "target": "ValidationStrategy" + "source": "FolderArns", + "target": "FolderArns" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ThemeArn", + "target": "ThemeArn" } ], "operation": "CreateAnalysis", @@ -37095,17 +35676,9 @@ "source": "AwsAccountId", "target": "AwsAccountId" }, - { - "source": "Capabilities", - "target": "Capabilities" - }, { "source": "CustomPermissionsName", "target": "CustomPermissionsName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateCustomPermissions", @@ -37139,14 +35712,6 @@ "source": "DashboardId", "target": "DashboardId" }, - { - "source": "DashboardPublishOptions", - "target": "DashboardPublishOptions" - }, - { - "source": "Definition", - "target": "Definition" - }, { "source": "FolderArns", "target": "FolderArns" @@ -37155,38 +35720,14 @@ "source": "LinkEntities", "target": "LinkEntities" }, - { - "source": "LinkSharingConfiguration", - "target": "LinkSharingConfiguration" - }, { "source": "Name", "target": "Name" }, - { - "source": "Parameters", - "target": "Parameters" - }, - { - "source": "Permissions", - "target": "Permissions" - }, - { - "source": "SourceEntity", - "target": "SourceEntity" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "ThemeArn", "target": "ThemeArn" }, - { - "source": "ValidationStrategy", - "target": "ValidationStrategy" - }, { "source": "VersionDescription", "target": "VersionDescription" @@ -37219,30 +35760,10 @@ "source": "AwsAccountId", "target": "AwsAccountId" }, - { - "source": "ColumnGroups", - "target": "ColumnGroups" - }, - { - "source": "ColumnLevelPermissionRules", - "target": "ColumnLevelPermissionRules" - }, { "source": "DataSetId", "target": "DataSetId" }, - { - "source": "DataSetUsageConfiguration", - "target": "DataSetUsageConfiguration" - }, - { - "source": "DatasetParameters", - "target": "DatasetParameters" - }, - { - "source": "FieldFolders", - "target": "FieldFolders" - }, { "source": "FolderArns", "target": "FolderArns" @@ -37251,38 +35772,10 @@ "source": "ImportMode", "target": "ImportMode" }, - { - "source": "LogicalTableMap", - "target": "LogicalTableMap" - }, { "source": "Name", "target": "Name" }, - { - "source": "PerformanceConfiguration", - "target": "PerformanceConfiguration" - }, - { - "source": "Permissions", - "target": "Permissions" - }, - { - "source": "PhysicalTableMap", - "target": "PhysicalTableMap" - }, - { - "source": "RowLevelPermissionDataSet", - "target": "RowLevelPermissionDataSet" - }, - { - "source": "RowLevelPermissionTagConfiguration", - "target": "RowLevelPermissionTagConfiguration" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UseAs", "target": "UseAs" @@ -37315,18 +35808,10 @@ "source": "AwsAccountId", "target": "AwsAccountId" }, - { - "source": "Credentials", - "target": "Credentials" - }, { "source": "DataSourceId", "target": "DataSourceId" }, - { - "source": "DataSourceParameters", - "target": "DataSourceParameters" - }, { "source": "FolderArns", "target": "FolderArns" @@ -37335,25 +35820,9 @@ "source": "Name", "target": "Name" }, - { - "source": "Permissions", - "target": "Permissions" - }, - { - "source": "SslProperties", - "target": "SslProperties" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" - }, - { - "source": "VpcConnectionProperties", - "target": "VpcConnectionProperties" } ], "operation": "CreateDataSource", @@ -37376,6 +35845,41 @@ "phase": "delete", "service": "quicksight" }, + { + "cfn_type": "AWS::QuickSight::Flow", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateFlow", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Flow", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" + } + ], + "operation": "DeleteFlow", + "phase": "delete", + "service": "quicksight" + }, { "cfn_type": "AWS::QuickSight::Folder", "mappings": [ @@ -37399,17 +35903,9 @@ "source": "ParentFolderArn", "target": "ParentFolderArn" }, - { - "source": "Permissions", - "target": "Permissions" - }, { "source": "SharingModel", "target": "SharingModel" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateFolder", @@ -37433,198 +35929,262 @@ "service": "quicksight" }, { - "cfn_type": "AWS::QuickSight::RefreshSchedule", + "cfn_type": "AWS::QuickSight::KnowledgeBase", "mappings": [ { "source": "AwsAccountId", "target": "AwsAccountId" }, { - "source": "DataSetId", - "target": "DataSetId" + "source": "DataSourceArn", + "target": "DataSourceArn" }, { - "source": "Schedule", - "target": "Schedule" + "source": "Description", + "target": "Description" + }, + { + "source": "KnowledgeBaseId", + "target": "KnowledgeBaseId" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "PrimaryOwnerArn", + "target": "PrimaryOwnerArn" } ], - "operation": "CreateRefreshSchedule", + "operation": "CreateKnowledgeBase", "phase": "create", "service": "quicksight" }, { - "cfn_type": "AWS::QuickSight::RefreshSchedule", + "cfn_type": "AWS::QuickSight::KnowledgeBase", "mappings": [ { "source": "AwsAccountId", "target": "AwsAccountId" }, { - "source": "DataSetId", - "target": "DataSetId" + "source": "KnowledgeBaseId", + "target": "KnowledgeBaseId" } ], - "operation": "DeleteRefreshSchedule", + "operation": "DeleteKnowledgeBase", "phase": "delete", "service": "quicksight" }, { - "cfn_type": "AWS::QuickSight::Template", + "cfn_type": "AWS::QuickSight::OAuthClientApplication", "mappings": [ { - "source": "AwsAccountId", - "target": "AwsAccountId" + "source": "ClientId", + "target": "ClientId" }, { - "source": "Definition", - "target": "Definition" + "source": "ClientSecret", + "target": "ClientSecret" + }, + { + "source": "DataSourceType", + "target": "DataSourceType" }, { "source": "Name", "target": "Name" }, { - "source": "Permissions", - "target": "Permissions" + "source": "OAuthAuthorizationEndpointUrl", + "target": "OAuthAuthorizationEndpointUrl" }, { - "source": "SourceEntity", - "target": "SourceEntity" + "source": "OAuthClientApplicationId", + "target": "OAuthClientApplicationId" }, { - "source": "Tags", - "target": "Tags" + "source": "OAuthClientAuthenticationType", + "target": "OAuthClientAuthenticationType" }, { - "source": "TemplateId", - "target": "TemplateId" + "source": "OAuthScopes", + "target": "OAuthScopes" }, { - "source": "ValidationStrategy", - "target": "ValidationStrategy" + "source": "OAuthTokenEndpointUrl", + "target": "OAuthTokenEndpointUrl" + } + ], + "operation": "CreateOAuthClientApplication", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::OAuthClientApplication", + "mappings": [ + { + "source": "OAuthClientApplicationId", + "target": "OAuthClientApplicationId" + } + ], + "operation": "DeleteOAuthClientApplication", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::RefreshSchedule", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" }, { - "source": "VersionDescription", - "target": "VersionDescription" + "source": "DataSetId", + "target": "DataSetId" } ], - "operation": "CreateTemplate", + "operation": "CreateRefreshSchedule", "phase": "create", "service": "quicksight" }, { - "cfn_type": "AWS::QuickSight::Template", + "cfn_type": "AWS::QuickSight::RefreshSchedule", "mappings": [ { "source": "AwsAccountId", "target": "AwsAccountId" }, { - "source": "TemplateId", - "target": "TemplateId" + "source": "DataSetId", + "target": "DataSetId" } ], - "operation": "DeleteTemplate", + "operation": "DeleteRefreshSchedule", "phase": "delete", "service": "quicksight" }, { - "cfn_type": "AWS::QuickSight::Theme", + "cfn_type": "AWS::QuickSight::Space", "mappings": [ { "source": "AwsAccountId", "target": "AwsAccountId" }, { - "source": "BaseThemeId", - "target": "BaseThemeId" - }, - { - "source": "Configuration", - "target": "Configuration" + "source": "Description", + "target": "Description" }, { "source": "Name", "target": "Name" }, { - "source": "Permissions", - "target": "Permissions" + "source": "SpaceId", + "target": "SpaceId" + } + ], + "operation": "CreateSpace", + "phase": "create", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Space", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" }, { - "source": "Tags", - "target": "Tags" + "source": "SpaceId", + "target": "SpaceId" + } + ], + "operation": "DeleteSpace", + "phase": "delete", + "service": "quicksight" + }, + { + "cfn_type": "AWS::QuickSight::Template", + "mappings": [ + { + "source": "AwsAccountId", + "target": "AwsAccountId" }, { - "source": "ThemeId", - "target": "ThemeId" + "source": "Name", + "target": "Name" + }, + { + "source": "TemplateId", + "target": "TemplateId" }, { "source": "VersionDescription", "target": "VersionDescription" } ], - "operation": "CreateTheme", + "operation": "CreateTemplate", "phase": "create", "service": "quicksight" }, { - "cfn_type": "AWS::QuickSight::Theme", + "cfn_type": "AWS::QuickSight::Template", "mappings": [ { "source": "AwsAccountId", "target": "AwsAccountId" }, { - "source": "ThemeId", - "target": "ThemeId" + "source": "TemplateId", + "target": "TemplateId" } ], - "operation": "DeleteTheme", + "operation": "DeleteTemplate", "phase": "delete", "service": "quicksight" }, { - "cfn_type": "AWS::QuickSight::Topic", + "cfn_type": "AWS::QuickSight::Theme", "mappings": [ { "source": "AwsAccountId", "target": "AwsAccountId" }, { - "source": "CustomInstructions", - "target": "CustomInstructions" + "source": "BaseThemeId", + "target": "BaseThemeId" }, { - "source": "FolderArns", - "target": "FolderArns" + "source": "Name", + "target": "Name" }, { - "source": "Tags", - "target": "Tags" + "source": "ThemeId", + "target": "ThemeId" }, { - "source": "TopicId", - "target": "TopicId" + "source": "VersionDescription", + "target": "VersionDescription" } ], - "operation": "CreateTopic", + "operation": "CreateTheme", "phase": "create", "service": "quicksight" }, { - "cfn_type": "AWS::QuickSight::Topic", + "cfn_type": "AWS::QuickSight::Theme", "mappings": [ { "source": "AwsAccountId", "target": "AwsAccountId" }, { - "source": "TopicId", - "target": "TopicId" + "source": "ThemeId", + "target": "ThemeId" } ], - "operation": "DeleteTopic", + "operation": "DeleteTheme", "phase": "delete", "service": "quicksight" }, @@ -37655,10 +36215,6 @@ "source": "SubnetIds", "target": "SubnetIds" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VPCConnectionId", "target": "VPCConnectionId" @@ -37698,10 +36254,6 @@ { "source": "resourceType", "target": "ResourceType" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreatePermission", @@ -37741,10 +36293,6 @@ { "source": "sources", "target": "Sources" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateResourceShare", @@ -37761,6 +36309,10 @@ { "cfn_type": "AWS::RDS::CustomDBEngineVersion", "mappings": [ + { + "source": "DatabaseInstallationFiles", + "target": "DatabaseInstallationFiles" + }, { "source": "DatabaseInstallationFilesS3BucketName", "target": "DatabaseInstallationFilesS3BucketName" @@ -37797,10 +36349,6 @@ "source": "SourceCustomDbEngineVersionIdentifier", "target": "SourceCustomDbEngineVersionIdentifier" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UseAwsProvidedLatestImage", "target": "UseAwsProvidedLatestImage" @@ -37949,6 +36497,10 @@ "source": "ManageMasterUserPassword", "target": "ManageMasterUserPassword" }, + { + "source": "MasterUserAuthenticationType", + "target": "MasterUserAuthenticationType" + }, { "source": "MasterUserPassword", "target": "MasterUserPassword" @@ -37997,14 +36549,6 @@ "source": "ReplicationSourceIdentifier", "target": "ReplicationSourceIdentifier" }, - { - "source": "ScalingConfiguration", - "target": "ScalingConfiguration" - }, - { - "source": "ServerlessV2ScalingConfiguration", - "target": "ServerlessV2ScalingConfiguration" - }, { "source": "SourceRegion", "target": "SourceRegion" @@ -38017,10 +36561,6 @@ "source": "StorageType", "target": "StorageType" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VpcSecurityGroupIds", "target": "VpcSecurityGroupIds" @@ -38056,10 +36596,6 @@ { "source": "Description", "target": "Description" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDBClusterParameterGroup", @@ -38081,10 +36617,6 @@ { "cfn_type": "AWS::RDS::DBInstance", "mappings": [ - { - "source": "AllocatedStorage", - "target": "AllocatedStorage" - }, { "source": "AutoMinorVersionUpgrade", "target": "AutoMinorVersionUpgrade" @@ -38225,6 +36757,10 @@ "source": "ManageMasterUserPassword", "target": "ManageMasterUserPassword" }, + { + "source": "MasterUserAuthenticationType", + "target": "MasterUserAuthenticationType" + }, { "source": "MasterUserPassword", "target": "MasterUserPassword" @@ -38269,10 +36805,6 @@ "source": "PerformanceInsightsRetentionPeriod", "target": "PerformanceInsightsRetentionPeriod" }, - { - "source": "Port", - "target": "Port" - }, { "source": "PreferredBackupWindow", "target": "PreferredBackupWindow" @@ -38281,10 +36813,6 @@ "source": "PreferredMaintenanceWindow", "target": "PreferredMaintenanceWindow" }, - { - "source": "ProcessorFeatures", - "target": "ProcessorFeatures" - }, { "source": "PromotionTier", "target": "PromotionTier" @@ -38305,10 +36833,6 @@ "source": "StorageType", "target": "StorageType" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TdeCredentialArn", "target": "TdeCredentialArn" @@ -38352,10 +36876,6 @@ { "source": "Description", "target": "Description" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDBParameterGroup", @@ -38377,10 +36897,6 @@ { "cfn_type": "AWS::RDS::DBProxy", "mappings": [ - { - "source": "Auth", - "target": "Auth" - }, { "source": "DBProxyName", "target": "DBProxyName" @@ -38389,6 +36905,14 @@ "source": "DebugLogging", "target": "DebugLogging" }, + { + "source": "DefaultAuthScheme", + "target": "DefaultAuthScheme" + }, + { + "source": "EndpointNetworkType", + "target": "EndpointNetworkType" + }, { "source": "EngineFamily", "target": "EngineFamily" @@ -38406,8 +36930,8 @@ "target": "RoleArn" }, { - "source": "Tags", - "target": "Tags" + "source": "TargetConnectionNetworkType", + "target": "TargetConnectionNetworkType" }, { "source": "VpcSecurityGroupIds", @@ -38446,8 +36970,8 @@ "target": "DBProxyName" }, { - "source": "Tags", - "target": "Tags" + "source": "EndpointNetworkType", + "target": "EndpointNetworkType" }, { "source": "TargetRole", @@ -38552,10 +37076,6 @@ { "source": "PubliclyAccessible", "target": "PubliclyAccessible" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDBShardGroup", @@ -38588,10 +37108,6 @@ { "source": "SubnetIds", "target": "SubnetIds" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDBSubnetGroup", @@ -38636,10 +37152,6 @@ { "source": "SubscriptionName", "target": "SubscriptionName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateEventSubscription", @@ -38688,10 +37200,6 @@ { "source": "StorageEncrypted", "target": "StorageEncrypted" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateGlobalCluster", @@ -38713,10 +37221,6 @@ { "cfn_type": "AWS::RDS::Integration", "mappings": [ - { - "source": "AdditionalEncryptionContext", - "target": "AdditionalEncryptionContext" - }, { "source": "DataFilter", "target": "DataFilter" @@ -38737,10 +37241,6 @@ "source": "SourceArn", "target": "SourceArn" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TargetArn", "target": "TargetArn" @@ -38775,10 +37275,6 @@ { "source": "OptionGroupName", "target": "OptionGroupName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateOptionGroup", @@ -38798,23 +37294,248 @@ "service": "rds" }, { - "cfn_type": "AWS::RUM::AppMonitor", + "cfn_type": "AWS::RTBFabric::InboundExternalLink", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "gatewayId", + "target": "GatewayId" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateInboundExternalLink", + "phase": "create", + "service": "rtbfabric" + }, + { + "cfn_type": "AWS::RTBFabric::InboundExternalLink", + "mappings": [ + { + "source": "gatewayId", + "target": "GatewayId" + } + ], + "operation": "DeleteInboundExternalLink", + "phase": "delete", + "service": "rtbfabric" + }, + { + "cfn_type": "AWS::RTBFabric::Link", "mappings": [ { - "source": "AppMonitorConfiguration", - "target": "AppMonitorConfiguration" + "source": "gatewayId", + "target": "GatewayId" }, { - "source": "CustomEvents", - "target": "CustomEvents" + "source": "httpResponderAllowed", + "target": "HttpResponderAllowed" }, { - "source": "CwLogEnabled", - "target": "CwLogEnabled" + "source": "peerGatewayId", + "target": "PeerGatewayId" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateLink", + "phase": "create", + "service": "rtbfabric" + }, + { + "cfn_type": "AWS::RTBFabric::Link", + "mappings": [ + { + "source": "gatewayId", + "target": "GatewayId" + } + ], + "operation": "DeleteLink", + "phase": "delete", + "service": "rtbfabric" + }, + { + "cfn_type": "AWS::RTBFabric::LinkRoutingRule", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "gatewayId", + "target": "GatewayId" + }, + { + "source": "linkId", + "target": "LinkId" + }, + { + "source": "priority", + "target": "Priority" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateLinkRoutingRule", + "phase": "create", + "service": "rtbfabric" + }, + { + "cfn_type": "AWS::RTBFabric::LinkRoutingRule", + "mappings": [ + { + "source": "gatewayId", + "target": "GatewayId" + }, + { + "source": "linkId", + "target": "LinkId" + } + ], + "operation": "DeleteLinkRoutingRule", + "phase": "delete", + "service": "rtbfabric" + }, + { + "cfn_type": "AWS::RTBFabric::OutboundExternalLink", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "gatewayId", + "target": "GatewayId" + }, + { + "source": "publicEndpoint", + "target": "PublicEndpoint" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateOutboundExternalLink", + "phase": "create", + "service": "rtbfabric" + }, + { + "cfn_type": "AWS::RTBFabric::OutboundExternalLink", + "mappings": [ + { + "source": "gatewayId", + "target": "GatewayId" + } + ], + "operation": "DeleteOutboundExternalLink", + "phase": "delete", + "service": "rtbfabric" + }, + { + "cfn_type": "AWS::RTBFabric::RequesterGateway", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "securityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "subnetIds", + "target": "SubnetIds" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "vpcId", + "target": "VpcId" + } + ], + "operation": "CreateRequesterGateway", + "phase": "create", + "service": "rtbfabric" + }, + { + "cfn_type": "AWS::RTBFabric::RequesterGateway", + "mappings": [], + "operation": "DeleteRequesterGateway", + "phase": "delete", + "service": "rtbfabric" + }, + { + "cfn_type": "AWS::RTBFabric::ResponderGateway", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "domainName", + "target": "DomainName" + }, + { + "source": "gatewayType", + "target": "GatewayType" + }, + { + "source": "port", + "target": "Port" + }, + { + "source": "protocol", + "target": "Protocol" + }, + { + "source": "securityGroupIds", + "target": "SecurityGroupIds" }, { - "source": "DeobfuscationConfiguration", - "target": "DeobfuscationConfiguration" + "source": "subnetIds", + "target": "SubnetIds" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "vpcId", + "target": "VpcId" + } + ], + "operation": "CreateResponderGateway", + "phase": "create", + "service": "rtbfabric" + }, + { + "cfn_type": "AWS::RTBFabric::ResponderGateway", + "mappings": [], + "operation": "DeleteResponderGateway", + "phase": "delete", + "service": "rtbfabric" + }, + { + "cfn_type": "AWS::RUM::AppMonitor", + "mappings": [ + { + "source": "CwLogEnabled", + "target": "CwLogEnabled" }, { "source": "Domain", @@ -38828,6 +37549,10 @@ "source": "Name", "target": "Name" }, + { + "source": "Platform", + "target": "Platform" + }, { "source": "Tags", "target": "Tags" @@ -38856,29 +37581,9 @@ "source": "Description", "target": "Description" }, - { - "source": "ExcludeResourceTags", - "target": "ExcludeResourceTags" - }, - { - "source": "LockConfiguration", - "target": "LockConfiguration" - }, - { - "source": "ResourceTags", - "target": "ResourceTags" - }, { "source": "ResourceType", "target": "ResourceType" - }, - { - "source": "RetentionPeriod", - "target": "RetentionPeriod" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateRule", @@ -39019,10 +37724,6 @@ "source": "PubliclyAccessible", "target": "PubliclyAccessible" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VpcSecurityGroupIds", "target": "VpcSecurityGroupIds" @@ -39058,10 +37759,6 @@ { "source": "ParameterGroupName", "target": "ParameterGroupName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateClusterParameterGroup", @@ -39090,10 +37787,6 @@ { "source": "SubnetIds", "target": "SubnetIds" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateClusterSubnetGroup", @@ -39147,30 +37840,6 @@ "phase": "delete", "service": "redshift" }, - { - "cfn_type": "AWS::Redshift::EndpointAuthorization", - "mappings": [ - { - "source": "Account", - "target": "Account" - }, - { - "source": "ClusterIdentifier", - "target": "ClusterIdentifier" - }, - { - "source": "Force", - "target": "Force" - }, - { - "source": "VpcIds", - "target": "VpcIds" - } - ], - "operation": "RevokeEndpointAccess", - "phase": "delete", - "service": "redshift" - }, { "cfn_type": "AWS::Redshift::EventSubscription", "mappings": [ @@ -39201,10 +37870,6 @@ { "source": "SubscriptionName", "target": "SubscriptionName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateEventSubscription", @@ -39226,10 +37891,6 @@ { "cfn_type": "AWS::Redshift::Integration", "mappings": [ - { - "source": "AdditionalEncryptionContext", - "target": "AdditionalEncryptionContext" - }, { "source": "IntegrationName", "target": "IntegrationName" @@ -39265,10 +37926,6 @@ "source": "Enable", "target": "Enable" }, - { - "source": "EndTime", - "target": "EndTime" - }, { "source": "IamRole", "target": "IamRole" @@ -39284,14 +37941,6 @@ { "source": "ScheduledActionName", "target": "ScheduledActionName" - }, - { - "source": "StartTime", - "target": "StartTime" - }, - { - "source": "TargetAction", - "target": "TargetAction" } ], "operation": "CreateScheduledAction", @@ -39312,6 +37961,9 @@ }, { "cfn_type": "AWS::Redshift::SnapshotSchedule", + "ignored_inputs": [ + "DryRun" + ], "mappings": [ { "source": "ScheduleDefinitions", @@ -39324,10 +37976,6 @@ { "source": "ScheduleIdentifier", "target": "ScheduleIdentifier" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateSnapshotSchedule", @@ -39392,10 +38040,6 @@ { "source": "redshiftIdcApplicationArn", "target": "RedshiftIdcApplicationArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateNamespace", @@ -39436,10 +38080,6 @@ { "source": "snapshotName", "target": "SnapshotName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateSnapshot", @@ -39465,10 +38105,6 @@ "source": "baseCapacity", "target": "BaseCapacity" }, - { - "source": "configParameters", - "target": "ConfigParameters" - }, { "source": "enhancedVpcRouting", "target": "EnhancedVpcRouting" @@ -39485,10 +38121,6 @@ "source": "port", "target": "Port" }, - { - "source": "pricePerformanceTarget", - "target": "PricePerformanceTarget" - }, { "source": "publiclyAccessible", "target": "PubliclyAccessible" @@ -39501,10 +38133,6 @@ "source": "subnetIds", "target": "SubnetIds" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "trackName", "target": "TrackName" @@ -39532,11 +38160,10 @@ }, { "cfn_type": "AWS::RefactorSpaces::Application", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "ApiGatewayProxy", - "target": "ApiGatewayProxy" - }, { "source": "EnvironmentIdentifier", "target": "EnvironmentIdentifier" @@ -39576,6 +38203,9 @@ }, { "cfn_type": "AWS::RefactorSpaces::Environment", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -39607,15 +38237,14 @@ }, { "cfn_type": "AWS::RefactorSpaces::Route", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ApplicationIdentifier", "target": "ApplicationIdentifier" }, - { - "source": "DefaultRoute", - "target": "DefaultRoute" - }, { "source": "EnvironmentIdentifier", "target": "EnvironmentIdentifier" @@ -39631,10 +38260,6 @@ { "source": "Tags", "target": "Tags" - }, - { - "source": "UriPathRoute", - "target": "UriPathRoute" } ], "operation": "CreateRoute", @@ -39659,6 +38284,9 @@ }, { "cfn_type": "AWS::RefactorSpaces::Service", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ApplicationIdentifier", @@ -39676,10 +38304,6 @@ "source": "EnvironmentIdentifier", "target": "EnvironmentIdentifier" }, - { - "source": "LambdaEndpoint", - "target": "LambdaEndpoint" - }, { "source": "Name", "target": "Name" @@ -39688,10 +38312,6 @@ "source": "Tags", "target": "Tags" }, - { - "source": "UrlEndpoint", - "target": "UrlEndpoint" - }, { "source": "VpcId", "target": "VpcId" @@ -39745,6 +38365,33 @@ "phase": "delete", "service": "rekognition" }, + { + "cfn_type": "AWS::Rekognition::Dataset", + "mappings": [ + { + "source": "DatasetType", + "target": "DatasetType" + }, + { + "source": "ProjectArn", + "target": "ProjectArn" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateDataset", + "phase": "create", + "service": "rekognition" + }, + { + "cfn_type": "AWS::Rekognition::Dataset", + "mappings": [], + "operation": "DeleteDataset", + "phase": "delete", + "service": "rekognition" + }, { "cfn_type": "AWS::Rekognition::Project", "mappings": [ @@ -39771,10 +38418,6 @@ { "cfn_type": "AWS::Rekognition::StreamProcessor", "mappings": [ - { - "source": "DataSharingPreference", - "target": "DataSharingPreference" - }, { "source": "KmsKeyId", "target": "KmsKeyId" @@ -39783,10 +38426,6 @@ "source": "Name", "target": "Name" }, - { - "source": "NotificationChannel", - "target": "NotificationChannel" - }, { "source": "RoleArn", "target": "RoleArn" @@ -39814,26 +38453,17 @@ }, { "cfn_type": "AWS::ResilienceHub::App", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", "target": "Description" }, - { - "source": "eventSubscriptions", - "target": "EventSubscriptions" - }, { "source": "name", "target": "Name" - }, - { - "source": "permissionModel", - "target": "PermissionModel" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateApp", @@ -39842,6 +38472,9 @@ }, { "cfn_type": "AWS::ResilienceHub::App", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteApp", "phase": "delete", @@ -39849,15 +38482,14 @@ }, { "cfn_type": "AWS::ResilienceHub::ResiliencyPolicy", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "dataLocationConstraint", "target": "DataLocationConstraint" }, - { - "source": "policy", - "target": "Policy" - }, { "source": "policyDescription", "target": "PolicyDescription" @@ -39866,10 +38498,6 @@ "source": "policyName", "target": "PolicyName" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "tier", "target": "Tier" @@ -39881,307 +38509,295 @@ }, { "cfn_type": "AWS::ResilienceHub::ResiliencyPolicy", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteResiliencyPolicy", "phase": "delete", "service": "resiliencehub" }, { - "cfn_type": "AWS::ResourceExplorer2::Index", - "mappings": [ - { - "source": "Tags", - "target": "Tags" - } + "cfn_type": "AWS::ResilienceHubV2::Policy", + "ignored_inputs": [ + "clientToken" ], - "operation": "CreateIndex", - "phase": "create", - "service": "resource-explorer-2" - }, - { - "cfn_type": "AWS::ResourceExplorer2::Index", - "mappings": [], - "operation": "DeleteIndex", - "phase": "delete", - "service": "resource-explorer-2" - }, - { - "cfn_type": "AWS::ResourceExplorer2::View", "mappings": [ { - "source": "Filters", - "target": "Filters" + "source": "description", + "target": "Description" }, { - "source": "IncludedProperties", - "target": "IncludedProperties" + "source": "kmsKeyId", + "target": "KmsKeyId" }, { - "source": "Scope", - "target": "Scope" + "source": "name", + "target": "Name" }, { - "source": "Tags", + "source": "tags", "target": "Tags" - }, - { - "source": "ViewName", - "target": "ViewName" } ], - "operation": "CreateView", + "operation": "CreatePolicy", "phase": "create", - "service": "resource-explorer-2" + "service": "resiliencehubv2" }, { - "cfn_type": "AWS::ResourceExplorer2::View", + "cfn_type": "AWS::ResilienceHubV2::Policy", "mappings": [], - "operation": "DeleteView", + "operation": "DeletePolicy", "phase": "delete", - "service": "resource-explorer-2" + "service": "resiliencehubv2" }, { - "cfn_type": "AWS::ResourceGroups::Group", + "cfn_type": "AWS::ResilienceHubV2::Service", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "Configuration", - "target": "Configuration" + "source": "dependencyDiscovery", + "target": "DependencyDiscovery" }, { - "source": "Description", + "source": "description", "target": "Description" }, { - "source": "Name", + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "name", "target": "Name" }, { - "source": "ResourceQuery", - "target": "ResourceQuery" + "source": "policyArn", + "target": "PolicyArn" }, { - "source": "Tags", + "source": "regions", + "target": "Regions" + }, + { + "source": "tags", "target": "Tags" } ], - "operation": "CreateGroup", + "operation": "CreateService", "phase": "create", - "service": "resource-groups" + "service": "resiliencehubv2" }, { - "cfn_type": "AWS::ResourceGroups::Group", + "cfn_type": "AWS::ResilienceHubV2::Service", "mappings": [], - "operation": "DeleteGroup", + "operation": "DeleteService", "phase": "delete", - "service": "resource-groups" + "service": "resiliencehubv2" }, { - "cfn_type": "AWS::ResourceGroups::TagSyncTask", + "cfn_type": "AWS::ResilienceHubV2::ServiceFunction", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "Group", - "target": "Group" + "source": "criticality", + "target": "Criticality" }, { - "source": "RoleArn", - "target": "RoleArn" + "source": "description", + "target": "Description" }, { - "source": "TagKey", - "target": "TagKey" + "source": "name", + "target": "Name" }, { - "source": "TagValue", - "target": "TagValue" + "source": "serviceArn", + "target": "ServiceArn" } ], - "operation": "StartTagSyncTask", + "operation": "CreateServiceFunction", "phase": "create", - "service": "resource-groups" + "service": "resiliencehubv2" }, { - "cfn_type": "AWS::ResourceGroups::TagSyncTask", - "mappings": [], - "operation": "CancelTagSyncTask", - "phase": "delete", - "service": "resource-groups" - }, - { - "cfn_type": "AWS::RoboMaker::Fleet", + "cfn_type": "AWS::ResilienceHubV2::ServiceFunction", "mappings": [ { - "source": "name", - "target": "Name" - }, - { - "source": "tags", - "target": "Tags" + "source": "serviceArn", + "target": "ServiceArn" } ], - "operation": "CreateFleet", - "phase": "create", - "service": "robomaker" - }, - { - "cfn_type": "AWS::RoboMaker::Fleet", - "mappings": [], - "operation": "DeleteFleet", + "operation": "DeleteServiceFunction", "phase": "delete", - "service": "robomaker" + "service": "resiliencehubv2" }, { - "cfn_type": "AWS::RoboMaker::Robot", + "cfn_type": "AWS::ResilienceHubV2::System", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "architecture", - "target": "Architecture" + "source": "description", + "target": "Description" }, { - "source": "greengrassGroupId", - "target": "GreengrassGroupId" + "source": "kmsKeyId", + "target": "KmsKeyId" }, { "source": "name", "target": "Name" }, + { + "source": "sharingEnabled", + "target": "SharingEnabled" + }, { "source": "tags", "target": "Tags" } ], - "operation": "CreateRobot", + "operation": "CreateSystem", "phase": "create", - "service": "robomaker" + "service": "resiliencehubv2" }, { - "cfn_type": "AWS::RoboMaker::Robot", - "mappings": [ - { - "source": "fleet", - "target": "Fleet" - } - ], - "operation": "DeregisterRobot", + "cfn_type": "AWS::ResilienceHubV2::System", + "mappings": [], + "operation": "DeleteSystem", "phase": "delete", - "service": "robomaker" + "service": "resiliencehubv2" }, { - "cfn_type": "AWS::RoboMaker::RobotApplication", + "cfn_type": "AWS::ResilienceHubV2::UserJourney", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "environment", - "target": "Environment" + "source": "description", + "target": "Description" }, { "source": "name", "target": "Name" }, { - "source": "robotSoftwareSuite", - "target": "RobotSoftwareSuite" - }, - { - "source": "sources", - "target": "Sources" - }, - { - "source": "tags", - "target": "Tags" + "source": "policyArn", + "target": "PolicyArn" } ], - "operation": "CreateRobotApplication", + "operation": "CreateUserJourney", "phase": "create", - "service": "robomaker" + "service": "resiliencehubv2" }, { - "cfn_type": "AWS::RoboMaker::RobotApplication", + "cfn_type": "AWS::ResilienceHubV2::UserJourney", "mappings": [], - "operation": "DeleteRobotApplication", + "operation": "DeleteUserJourney", + "phase": "delete", + "service": "resiliencehubv2" + }, + { + "cfn_type": "AWS::ResourceExplorer2::Index", + "mappings": [], + "operation": "DeleteIndex", "phase": "delete", - "service": "robomaker" + "service": "resource-explorer-2" }, { - "cfn_type": "AWS::RoboMaker::RobotApplicationVersion", + "cfn_type": "AWS::ResourceExplorer2::View", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { - "source": "application", - "target": "Application" + "source": "Scope", + "target": "Scope" }, { - "source": "currentRevisionId", - "target": "CurrentRevisionId" + "source": "ViewName", + "target": "ViewName" } ], - "operation": "CreateRobotApplicationVersion", + "operation": "CreateView", "phase": "create", - "service": "robomaker" + "service": "resource-explorer-2" + }, + { + "cfn_type": "AWS::ResourceExplorer2::View", + "mappings": [], + "operation": "DeleteView", + "phase": "delete", + "service": "resource-explorer-2" }, { - "cfn_type": "AWS::RoboMaker::SimulationApplication", + "cfn_type": "AWS::ResourceGroups::Group", "mappings": [ { - "source": "environment", - "target": "Environment" + "source": "Description", + "target": "Description" }, { - "source": "name", + "source": "Name", "target": "Name" }, { - "source": "renderingEngine", - "target": "RenderingEngine" - }, - { - "source": "robotSoftwareSuite", - "target": "RobotSoftwareSuite" - }, - { - "source": "simulationSoftwareSuite", - "target": "SimulationSoftwareSuite" - }, - { - "source": "sources", - "target": "Sources" - }, - { - "source": "tags", + "source": "Tags", "target": "Tags" } ], - "operation": "CreateSimulationApplication", + "operation": "CreateGroup", "phase": "create", - "service": "robomaker" + "service": "resource-groups" }, { - "cfn_type": "AWS::RoboMaker::SimulationApplication", + "cfn_type": "AWS::ResourceGroups::Group", "mappings": [], - "operation": "DeleteSimulationApplication", + "operation": "DeleteGroup", "phase": "delete", - "service": "robomaker" + "service": "resource-groups" }, { - "cfn_type": "AWS::RoboMaker::SimulationApplicationVersion", + "cfn_type": "AWS::ResourceGroups::TagSyncTask", "mappings": [ { - "source": "application", - "target": "Application" + "source": "Group", + "target": "Group" }, { - "source": "currentRevisionId", - "target": "CurrentRevisionId" + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "TagKey", + "target": "TagKey" + }, + { + "source": "TagValue", + "target": "TagValue" } ], - "operation": "CreateSimulationApplicationVersion", + "operation": "StartTagSyncTask", "phase": "create", - "service": "robomaker" + "service": "resource-groups" + }, + { + "cfn_type": "AWS::ResourceGroups::TagSyncTask", + "mappings": [], + "operation": "CancelTagSyncTask", + "phase": "delete", + "service": "resource-groups" }, { "cfn_type": "AWS::RolesAnywhere::CRL", "mappings": [ - { - "source": "crlData", - "target": "CrlData" - }, { "source": "enabled", "target": "Enabled" @@ -40190,10 +38806,6 @@ "source": "name", "target": "Name" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "trustAnchorArn", "target": "TrustAnchorArn" @@ -40244,10 +38856,6 @@ { "source": "sessionPolicy", "target": "SessionPolicy" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateProfile", @@ -40271,18 +38879,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "notificationSettings", - "target": "NotificationSettings" - }, - { - "source": "source", - "target": "Source" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateTrustAnchor", @@ -40327,18 +38923,6 @@ "phase": "create", "service": "route53" }, - { - "cfn_type": "AWS::Route53::HealthCheck", - "mappings": [ - { - "source": "HealthCheckConfig", - "target": "HealthCheckConfig" - } - ], - "operation": "CreateHealthCheck", - "phase": "create", - "service": "route53" - }, { "cfn_type": "AWS::Route53::HealthCheck", "mappings": [], @@ -40349,10 +38933,6 @@ { "cfn_type": "AWS::Route53::HostedZone", "mappings": [ - { - "source": "HostedZoneConfig", - "target": "HostedZoneConfig" - }, { "source": "Name", "target": "Name" @@ -40410,17 +38990,340 @@ "service": "route53" }, { - "cfn_type": "AWS::Route53Profiles::Profile", + "cfn_type": "AWS::Route53GlobalResolver::AccessSource", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "Name", + "source": "cidr", + "target": "Cidr" + }, + { + "source": "clientToken", + "target": "ClientToken" + }, + { + "source": "dnsViewId", + "target": "DnsViewId" + }, + { + "source": "ipAddressType", + "target": "IpAddressType" + }, + { + "source": "name", "target": "Name" }, { - "source": "Tags", + "source": "protocol", + "target": "Protocol" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAccessSource", + "phase": "create", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::AccessSource", + "mappings": [], + "operation": "DeleteAccessSource", + "phase": "delete", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::AccessToken", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "clientToken", + "target": "ClientToken" + }, + { + "source": "dnsViewId", + "target": "DnsViewId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateAccessToken", + "phase": "create", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::AccessToken", + "mappings": [], + "operation": "DeleteAccessToken", + "phase": "delete", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::DnsView", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "clientToken", + "target": "ClientToken" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "dnssecValidation", + "target": "DnssecValidation" + }, + { + "source": "ednsClientSubnet", + "target": "EdnsClientSubnet" + }, + { + "source": "firewallRulesFailOpen", + "target": "FirewallRulesFailOpen" + }, + { + "source": "globalResolverId", + "target": "GlobalResolverId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateDNSView", + "phase": "create", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::DnsView", + "mappings": [], + "operation": "DeleteDNSView", + "phase": "delete", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::FirewallDomainList", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "clientToken", + "target": "ClientToken" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "globalResolverId", + "target": "GlobalResolverId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", "target": "Tags" } ], + "operation": "CreateFirewallDomainList", + "phase": "create", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::FirewallDomainList", + "mappings": [], + "operation": "DeleteFirewallDomainList", + "phase": "delete", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::FirewallRule", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "action", + "target": "Action" + }, + { + "source": "blockOverrideDnsType", + "target": "BlockOverrideDnsType" + }, + { + "source": "blockOverrideDomain", + "target": "BlockOverrideDomain" + }, + { + "source": "blockOverrideTtl", + "target": "BlockOverrideTtl" + }, + { + "source": "blockResponse", + "target": "BlockResponse" + }, + { + "source": "clientToken", + "target": "ClientToken" + }, + { + "source": "confidenceThreshold", + "target": "ConfidenceThreshold" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "dnsAdvancedProtection", + "target": "DnsAdvancedProtection" + }, + { + "source": "dnsViewId", + "target": "DnsViewId" + }, + { + "source": "firewallDomainListId", + "target": "FirewallDomainListId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "priority", + "target": "Priority" + }, + { + "source": "qType", + "target": "QType" + } + ], + "operation": "CreateFirewallRule", + "phase": "create", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::FirewallRule", + "mappings": [], + "operation": "DeleteFirewallRule", + "phase": "delete", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::GlobalResolver", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "clientToken", + "target": "ClientToken" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "ipAddressType", + "target": "IpAddressType" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "observabilityRegion", + "target": "ObservabilityRegion" + }, + { + "source": "regions", + "target": "Regions" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateGlobalResolver", + "phase": "create", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::GlobalResolver", + "mappings": [], + "operation": "DeleteGlobalResolver", + "phase": "delete", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::HostedZoneAssociation", + "mappings": [ + { + "source": "hostedZoneId", + "target": "HostedZoneId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "resourceArn", + "target": "ResourceArn" + } + ], + "operation": "AssociateHostedZone", + "phase": "create", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53GlobalResolver::HostedZoneAssociation", + "mappings": [ + { + "source": "hostedZoneId", + "target": "HostedZoneId" + }, + { + "source": "resourceArn", + "target": "ResourceArn" + } + ], + "operation": "DisassociateHostedZone", + "phase": "delete", + "service": "route53globalresolver" + }, + { + "cfn_type": "AWS::Route53Profiles::Profile", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], "operation": "CreateProfile", "phase": "create", "service": "route53profiles" @@ -40446,10 +39349,6 @@ { "source": "ResourceId", "target": "ResourceId" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "AssociateProfile", @@ -40514,6 +39413,9 @@ }, { "cfn_type": "AWS::Route53RecoveryControl::Cluster", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "NetworkType", @@ -40537,6 +39439,9 @@ }, { "cfn_type": "AWS::Route53RecoveryControl::ControlPanel", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ClusterArn", @@ -40560,6 +39465,9 @@ }, { "cfn_type": "AWS::Route53RecoveryControl::RoutingControl", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ClusterArn", @@ -40583,15 +39491,10 @@ }, { "cfn_type": "AWS::Route53RecoveryControl::SafetyRule", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "AssertionRule", - "target": "AssertionRule" - }, - { - "source": "GatingRule", - "target": "GatingRule" - }, { "source": "Tags", "target": "Tags" @@ -40715,10 +39618,6 @@ "source": "ResourceSetType", "target": "ResourceSetType" }, - { - "source": "Resources", - "target": "Resources" - }, { "source": "Tags", "target": "Tags" @@ -40742,14 +39641,13 @@ }, { "cfn_type": "AWS::Route53Resolver::FirewallDomainList", + "ignored_inputs": [ + "CreatorRequestId" + ], "mappings": [ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateFirewallDomainList", @@ -40765,14 +39663,13 @@ }, { "cfn_type": "AWS::Route53Resolver::FirewallRuleGroup", + "ignored_inputs": [ + "CreatorRequestId" + ], "mappings": [ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateFirewallRuleGroup", @@ -40788,6 +39685,9 @@ }, { "cfn_type": "AWS::Route53Resolver::FirewallRuleGroupAssociation", + "ignored_inputs": [ + "CreatorRequestId" + ], "mappings": [ { "source": "FirewallRuleGroupId", @@ -40805,10 +39705,6 @@ "source": "Priority", "target": "Priority" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VpcId", "target": "VpcId" @@ -40836,10 +39732,6 @@ { "source": "PreferredInstanceType", "target": "PreferredInstanceType" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateOutpostResolver", @@ -40861,8 +39753,12 @@ "target": "Direction" }, { - "source": "IpAddresses", - "target": "IpAddresses" + "source": "Dns64Enabled", + "target": "Dns64Enabled" + }, + { + "source": "Ipv6InternetAccessEnabled", + "target": "Ipv6InternetAccessEnabled" }, { "source": "Name", @@ -40884,13 +39780,17 @@ "source": "ResolverEndpointType", "target": "ResolverEndpointType" }, + { + "source": "RniEnhancedMetricsEnabled", + "target": "RniEnhancedMetricsEnabled" + }, { "source": "SecurityGroupIds", "target": "SecurityGroupIds" }, { - "source": "Tags", - "target": "Tags" + "source": "TargetNameServerMetricsEnabled", + "target": "TargetNameServerMetricsEnabled" } ], "operation": "CreateResolverEndpoint", @@ -40906,6 +39806,9 @@ }, { "cfn_type": "AWS::Route53Resolver::ResolverQueryLoggingConfig", + "ignored_inputs": [ + "CreatorRequestId" + ], "mappings": [ { "source": "DestinationArn", @@ -40914,10 +39817,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateResolverQueryLogConfig", @@ -40978,14 +39877,6 @@ { "source": "RuleType", "target": "RuleType" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "TargetIps", - "target": "TargetIps" } ], "operation": "CreateResolverRule", @@ -41038,10 +39929,6 @@ { "cfn_type": "AWS::S3::AccessGrant", "mappings": [ - { - "source": "AccessGrantsLocationConfiguration", - "target": "AccessGrantsLocationConfiguration" - }, { "source": "AccessGrantsLocationId", "target": "AccessGrantsLocationId" @@ -41050,10 +39937,6 @@ "source": "ApplicationArn", "target": "ApplicationArn" }, - { - "source": "Grantee", - "target": "Grantee" - }, { "source": "Permission", "target": "Permission" @@ -41061,10 +39944,6 @@ { "source": "S3PrefixType", "target": "S3PrefixType" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAccessGrant", @@ -41084,10 +39963,6 @@ { "source": "IdentityCenterArn", "target": "IdentityCenterArn" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAccessGrantsInstance", @@ -41111,10 +39986,6 @@ { "source": "LocationScope", "target": "LocationScope" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAccessGrantsLocation", @@ -41134,6 +40005,10 @@ { "source": "Bucket", "target": "BucketName" + }, + { + "source": "BucketNamespace", + "target": "BucketNamespace" } ], "operation": "CreateBucket", @@ -41178,50 +40053,156 @@ }, { "cfn_type": "AWS::S3::MultiRegionAccessPoint", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [], "operation": "DeleteMultiRegionAccessPoint", "phase": "delete", "service": "s3control" }, { - "cfn_type": "AWS::S3::StorageLens", + "cfn_type": "AWS::S3::StorageLensGroup", "mappings": [ { - "source": "StorageLensConfiguration", - "target": "StorageLensConfiguration" + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteStorageLensGroup", + "phase": "delete", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3Files::AccessPoint", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "clientToken", + "target": "ClientToken" }, { - "source": "Tags", - "target": "Tags" + "source": "fileSystemId", + "target": "FileSystemId" } ], - "operation": "PutStorageLensConfiguration", + "operation": "CreateAccessPoint", "phase": "create", - "service": "s3control" + "service": "s3files" }, { - "cfn_type": "AWS::S3::StorageLensGroup", + "cfn_type": "AWS::S3Files::AccessPoint", + "mappings": [], + "operation": "DeleteAccessPoint", + "phase": "delete", + "service": "s3files" + }, + { + "cfn_type": "AWS::S3Files::FileSystem", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "Tags", - "target": "Tags" + "source": "acceptBucketWarning", + "target": "AcceptBucketWarning" + }, + { + "source": "bucket", + "target": "Bucket" + }, + { + "source": "clientToken", + "target": "ClientToken" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "prefix", + "target": "Prefix" + }, + { + "source": "roleArn", + "target": "RoleArn" } ], - "operation": "CreateStorageLensGroup", + "operation": "CreateFileSystem", "phase": "create", - "service": "s3control" + "service": "s3files" }, { - "cfn_type": "AWS::S3::StorageLensGroup", + "cfn_type": "AWS::S3Files::FileSystem", + "mappings": [], + "operation": "DeleteFileSystem", + "phase": "delete", + "service": "s3files" + }, + { + "cfn_type": "AWS::S3Files::FileSystemPolicy", "mappings": [ { - "source": "Name", - "target": "Name" + "source": "fileSystemId", + "target": "FileSystemId" } ], - "operation": "DeleteStorageLensGroup", + "operation": "PutFileSystemPolicy", + "phase": "create", + "service": "s3files" + }, + { + "cfn_type": "AWS::S3Files::FileSystemPolicy", + "mappings": [ + { + "source": "fileSystemId", + "target": "FileSystemId" + } + ], + "operation": "DeleteFileSystemPolicy", "phase": "delete", - "service": "s3control" + "service": "s3files" + }, + { + "cfn_type": "AWS::S3Files::MountTarget", + "mappings": [ + { + "source": "fileSystemId", + "target": "FileSystemId" + }, + { + "source": "ipAddressType", + "target": "IpAddressType" + }, + { + "source": "ipv4Address", + "target": "Ipv4Address" + }, + { + "source": "ipv6Address", + "target": "Ipv6Address" + }, + { + "source": "securityGroups", + "target": "SecurityGroups" + }, + { + "source": "subnetId", + "target": "SubnetId" + } + ], + "operation": "CreateMountTarget", + "phase": "create", + "service": "s3files" + }, + { + "cfn_type": "AWS::S3Files::MountTarget", + "mappings": [], + "operation": "DeleteMountTarget", + "phase": "delete", + "service": "s3files" }, { "cfn_type": "AWS::S3Outposts::Bucket", @@ -41282,10 +40263,6 @@ { "cfn_type": "AWS::S3Tables::Namespace", "mappings": [ - { - "source": "namespace", - "target": "Namespace" - }, { "source": "tableBucketARN", "target": "TableBucketARN" @@ -41325,6 +40302,10 @@ { "source": "tableBucketARN", "target": "TableBucketARN" + }, + { + "source": "tags", + "target": "Tags" } ], "operation": "CreateTable", @@ -41354,13 +40335,13 @@ { "cfn_type": "AWS::S3Tables::TableBucket", "mappings": [ - { - "source": "encryptionConfiguration", - "target": "EncryptionConfiguration" - }, { "source": "name", "target": "TableBucketName" + }, + { + "source": "tags", + "target": "Tags" } ], "operation": "CreateTableBucket", @@ -41441,8 +40422,8 @@ "target": "IndexName" }, { - "source": "metadataConfiguration", - "target": "MetadataConfiguration" + "source": "tags", + "target": "Tags" }, { "source": "vectorBucketArn", @@ -41477,8 +40458,8 @@ "cfn_type": "AWS::S3Vectors::VectorBucket", "mappings": [ { - "source": "encryptionConfiguration", - "target": "EncryptionConfiguration" + "source": "tags", + "target": "Tags" }, { "source": "vectorBucketName", @@ -41556,14 +40537,6 @@ "source": "namespace", "target": "Namespace" }, - { - "source": "partitionSpec", - "target": "PartitionSpec" - }, - { - "source": "schema", - "target": "Schema" - }, { "source": "tags", "target": "Tags" @@ -41633,41 +40606,18 @@ "phase": "delete", "service": "supplychain" }, - { - "cfn_type": "AWS::SES::ConfigurationSet", - "mappings": [], - "operation": "DeleteConfigurationSet", - "phase": "delete", - "service": "ses" - }, { "cfn_type": "AWS::SES::ConfigurationSetEventDestination", "mappings": [ { "source": "ConfigurationSetName", "target": "ConfigurationSetName" - }, - { - "source": "EventDestination", - "target": "EventDestination" } ], "operation": "CreateConfigurationSetEventDestination", "phase": "create", "service": "ses" }, - { - "cfn_type": "AWS::SES::ConfigurationSetEventDestination", - "mappings": [ - { - "source": "ConfigurationSetName", - "target": "ConfigurationSetName" - } - ], - "operation": "DeleteConfigurationSetEventDestination", - "phase": "delete", - "service": "ses" - }, { "cfn_type": "AWS::SES::ContactList", "mappings": [ @@ -41678,14 +40628,6 @@ { "source": "Description", "target": "Description" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "Topics", - "target": "Topics" } ], "operation": "CreateContactList", @@ -41736,18 +40678,6 @@ "phase": "create", "service": "ses" }, - { - "cfn_type": "AWS::SES::CustomVerificationEmailTemplate", - "mappings": [ - { - "source": "TemplateName", - "target": "TemplateName" - } - ], - "operation": "DeleteCustomVerificationEmailTemplate", - "phase": "delete", - "service": "ses" - }, { "cfn_type": "AWS::SES::DedicatedIpPool", "mappings": [ @@ -41758,10 +40688,6 @@ { "source": "ScalingMode", "target": "ScalingMode" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDedicatedIpPool", @@ -41769,75 +40695,46 @@ "service": "sesv2" }, { - "cfn_type": "AWS::SES::EmailIdentity", + "cfn_type": "AWS::SES::DedicatedIpPool", "mappings": [ { - "source": "DkimSigningAttributes", - "target": "DkimSigningAttributes" - }, - { - "source": "EmailIdentity", - "target": "EmailIdentity" - }, - { - "source": "Tags", - "target": "Tags" + "source": "PoolName", + "target": "PoolName" } ], - "operation": "CreateEmailIdentity", - "phase": "create", + "operation": "DeleteDedicatedIpPool", + "phase": "delete", "service": "sesv2" }, { - "cfn_type": "AWS::SES::MailManagerAddonInstance", - "mappings": [ - { - "source": "AddonSubscriptionId", - "target": "AddonSubscriptionId" - }, - { - "source": "Tags", - "target": "Tags" - } - ], - "operation": "CreateAddonInstance", - "phase": "create", - "service": "mailmanager" - }, - { - "cfn_type": "AWS::SES::MailManagerAddonSubscription", + "cfn_type": "AWS::SES::EmailIdentity", "mappings": [ { - "source": "AddonName", - "target": "AddonName" - }, - { - "source": "Tags", - "target": "Tags" + "source": "EmailIdentity", + "target": "EmailIdentity" } ], - "operation": "CreateAddonSubscription", + "operation": "CreateEmailIdentity", "phase": "create", - "service": "mailmanager" + "service": "sesv2" }, { - "cfn_type": "AWS::SES::MailManagerAddressList", + "cfn_type": "AWS::SES::EmailIdentity", "mappings": [ { - "source": "AddressListName", - "target": "AddressListName" - }, - { - "source": "Tags", - "target": "Tags" + "source": "EmailIdentity", + "target": "EmailIdentity" } ], - "operation": "CreateAddressList", - "phase": "create", - "service": "mailmanager" + "operation": "DeleteEmailIdentity", + "phase": "delete", + "service": "sesv2" }, { "cfn_type": "AWS::SES::MailManagerArchive", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ArchiveName", @@ -41846,14 +40743,6 @@ { "source": "KmsKeyArn", "target": "KmsKeyArn" - }, - { - "source": "Retention", - "target": "Retention" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateArchive", @@ -41862,26 +40751,21 @@ }, { "cfn_type": "AWS::SES::MailManagerIngressPoint", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "IngressPointConfiguration", - "target": "IngressPointConfiguration" - }, { "source": "IngressPointName", "target": "IngressPointName" }, - { - "source": "NetworkConfiguration", - "target": "NetworkConfiguration" - }, { "source": "RuleSetId", "target": "RuleSetId" }, { - "source": "Tags", - "target": "Tags" + "source": "TlsPolicy", + "target": "TlsPolicy" }, { "source": "TrafficPolicyId", @@ -41898,11 +40782,10 @@ }, { "cfn_type": "AWS::SES::MailManagerRelay", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "Authentication", - "target": "Authentication" - }, { "source": "RelayName", "target": "RelayName" @@ -41914,38 +40797,17 @@ { "source": "ServerPort", "target": "ServerPort" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateRelay", "phase": "create", "service": "mailmanager" }, - { - "cfn_type": "AWS::SES::MailManagerRuleSet", - "mappings": [ - { - "source": "RuleSetName", - "target": "RuleSetName" - }, - { - "source": "Rules", - "target": "Rules" - }, - { - "source": "Tags", - "target": "Tags" - } - ], - "operation": "CreateRuleSet", - "phase": "create", - "service": "mailmanager" - }, { "cfn_type": "AWS::SES::MailManagerTrafficPolicy", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "DefaultAction", @@ -41955,14 +40817,6 @@ "source": "MaxMessageSizeBytes", "target": "MaxMessageSizeBytes" }, - { - "source": "PolicyStatements", - "target": "PolicyStatements" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TrafficPolicyName", "target": "TrafficPolicyName" @@ -41975,17 +40829,9 @@ { "cfn_type": "AWS::SES::MultiRegionEndpoint", "mappings": [ - { - "source": "Details", - "target": "Details" - }, { "source": "EndpointName", "target": "EndpointName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateMultiRegionEndpoint", @@ -42004,6 +40850,13 @@ "phase": "delete", "service": "sesv2" }, + { + "cfn_type": "AWS::SES::ReceiptFilter", + "mappings": [], + "operation": "DeleteReceiptFilter", + "phase": "delete", + "service": "ses" + }, { "cfn_type": "AWS::SES::ReceiptRule", "mappings": [ @@ -42011,10 +40864,6 @@ "source": "After", "target": "After" }, - { - "source": "Rule", - "target": "Rule" - }, { "source": "RuleSetName", "target": "RuleSetName" @@ -42060,32 +40909,9 @@ "phase": "delete", "service": "ses" }, - { - "cfn_type": "AWS::SES::Template", - "mappings": [ - { - "source": "Template", - "target": "Template" - } - ], - "operation": "CreateTemplate", - "phase": "create", - "service": "ses" - }, - { - "cfn_type": "AWS::SES::Template", - "mappings": [], - "operation": "DeleteTemplate", - "phase": "delete", - "service": "ses" - }, { "cfn_type": "AWS::SES::Tenant", "mappings": [ - { - "source": "Tags", - "target": "Tags" - }, { "source": "TenantName", "target": "TenantName" @@ -42117,30 +40943,17 @@ ], "operation": "CreateConfigurationSet", "phase": "create", - "service": "sms-voice" - }, - { - "cfn_type": "AWS::SMSVOICE::ConfigurationSet", - "mappings": [ - { - "source": "ConfigurationSetName", - "target": "ConfigurationSetName" - } - ], - "operation": "DeleteConfigurationSet", - "phase": "delete", - "service": "sms-voice" + "service": "pinpoint-sms-voice" }, { "cfn_type": "AWS::SMSVOICE::OptOutList", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "OptOutListName", "target": "OptOutListName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateOptOutList", @@ -42161,6 +40974,9 @@ }, { "cfn_type": "AWS::SMSVOICE::PhoneNumber", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "DeletionProtectionEnabled", @@ -42181,10 +40997,6 @@ { "source": "OptOutListName", "target": "OptOutListName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "RequestPhoneNumber", @@ -42200,14 +41012,13 @@ }, { "cfn_type": "AWS::SMSVOICE::Pool", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "DeletionProtectionEnabled", "target": "DeletionProtectionEnabled" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreatePool", @@ -42223,14 +41034,13 @@ }, { "cfn_type": "AWS::SMSVOICE::ProtectConfiguration", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "DeletionProtectionEnabled", "target": "DeletionProtectionEnabled" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateProtectConfiguration", @@ -42246,14 +41056,13 @@ }, { "cfn_type": "AWS::SMSVOICE::Registration", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "RegistrationType", "target": "RegistrationType" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateRegistration", @@ -42293,6 +41102,9 @@ }, { "cfn_type": "AWS::SMSVOICE::SenderId", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "DeletionProtectionEnabled", @@ -42305,10 +41117,6 @@ { "source": "SenderId", "target": "SenderId" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "RequestSenderId", @@ -42361,10 +41169,6 @@ { "source": "Name", "target": "TopicName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateTopic", @@ -42408,6 +41212,10 @@ "source": "ApplyOnlyAtCronInterval", "target": "ApplyOnlyAtCronInterval" }, + { + "source": "AssociationDispatchAssumeRole", + "target": "AssociationDispatchAssumeRole" + }, { "source": "AssociationName", "target": "AssociationName" @@ -42444,14 +41252,6 @@ "source": "Name", "target": "Name" }, - { - "source": "OutputLocation", - "target": "OutputLocation" - }, - { - "source": "Parameters", - "target": "Parameters" - }, { "source": "ScheduleExpression", "target": "ScheduleExpression" @@ -42463,14 +41263,6 @@ { "source": "SyncCompliance", "target": "SyncCompliance" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "Targets", - "target": "Targets" } ], "operation": "CreateAssociation", @@ -42494,12 +41286,39 @@ "service": "ssm" }, { - "cfn_type": "AWS::SSM::Document", + "cfn_type": "AWS::SSM::CloudConnector", "mappings": [ { - "source": "Attachments", - "target": "Attachments" + "source": "ConfigConnectorArn", + "target": "ConfigConnectorArn" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "DisplayName", + "target": "DisplayName" }, + { + "source": "RoleArn", + "target": "RoleArn" + } + ], + "operation": "CreateCloudConnector", + "phase": "create", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::CloudConnector", + "mappings": [], + "operation": "DeleteCloudConnector", + "phase": "delete", + "service": "ssm" + }, + { + "cfn_type": "AWS::SSM::Document", + "mappings": [ { "source": "Content", "target": "Content" @@ -42516,14 +41335,6 @@ "source": "Name", "target": "Name" }, - { - "source": "Requires", - "target": "Requires" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TargetType", "target": "TargetType" @@ -42555,6 +41366,9 @@ }, { "cfn_type": "AWS::SSM::MaintenanceWindow", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "AllowUnassociatedTargets", @@ -42595,10 +41409,6 @@ { "source": "StartDate", "target": "StartDate" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateMaintenanceWindow", @@ -42614,6 +41424,9 @@ }, { "cfn_type": "AWS::SSM::MaintenanceWindowTarget", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -42631,10 +41444,6 @@ "source": "ResourceType", "target": "ResourceType" }, - { - "source": "Targets", - "target": "Targets" - }, { "source": "WindowId", "target": "WindowId" @@ -42646,6 +41455,9 @@ }, { "cfn_type": "AWS::SSM::MaintenanceWindowTask", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "CutoffBehavior", @@ -42655,10 +41467,6 @@ "source": "Description", "target": "Description" }, - { - "source": "LoggingInfo", - "target": "LoggingInfo" - }, { "source": "MaxConcurrency", "target": "MaxConcurrency" @@ -42679,22 +41487,10 @@ "source": "ServiceRoleArn", "target": "ServiceRoleArn" }, - { - "source": "Targets", - "target": "Targets" - }, { "source": "TaskArn", "target": "TaskArn" }, - { - "source": "TaskInvocationParameters", - "target": "TaskInvocationParameters" - }, - { - "source": "TaskParameters", - "target": "TaskParameters" - }, { "source": "TaskType", "target": "TaskType" @@ -42731,10 +41527,6 @@ "source": "Source", "target": "Source" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Title", "target": "Title" @@ -42774,10 +41566,6 @@ "source": "Policies", "target": "Policies" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Tier", "target": "Tier" @@ -42809,11 +41597,10 @@ }, { "cfn_type": "AWS::SSM::PatchBaseline", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "ApprovalRules", - "target": "ApprovalRules" - }, { "source": "ApprovedPatches", "target": "ApprovedPatches" @@ -42834,10 +41621,6 @@ "source": "Description", "target": "Description" }, - { - "source": "GlobalFilters", - "target": "GlobalFilters" - }, { "source": "Name", "target": "Name" @@ -42853,14 +41636,6 @@ { "source": "RejectedPatchesAction", "target": "RejectedPatchesAction" - }, - { - "source": "Sources", - "target": "Sources" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreatePatchBaseline", @@ -42877,18 +41652,10 @@ { "cfn_type": "AWS::SSM::ResourceDataSync", "mappings": [ - { - "source": "S3Destination", - "target": "S3Destination" - }, { "source": "SyncName", "target": "SyncName" }, - { - "source": "SyncSource", - "target": "SyncSource" - }, { "source": "SyncType", "target": "SyncType" @@ -42944,6 +41711,9 @@ }, { "cfn_type": "AWS::SSMContacts::Contact", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "Alias", @@ -42953,14 +41723,6 @@ "source": "DisplayName", "target": "DisplayName" }, - { - "source": "Plan", - "target": "Plan" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" @@ -42979,6 +41741,9 @@ }, { "cfn_type": "AWS::SSMContacts::ContactChannel", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "ContactId", @@ -43002,6 +41767,9 @@ }, { "cfn_type": "AWS::SSMContacts::Rotation", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "ContactIds", @@ -43011,18 +41779,6 @@ "source": "Name", "target": "Name" }, - { - "source": "Recurrence", - "target": "Recurrence" - }, - { - "source": "StartTime", - "target": "StartTime" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TimeZoneId", "target": "TimeZoneId" @@ -43041,6 +41797,9 @@ }, { "cfn_type": "AWS::SSMGuiConnect::Preferences", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [], "operation": "DeleteConnectionRecordingPreferences", "phase": "delete", @@ -43048,11 +41807,10 @@ }, { "cfn_type": "AWS::SSMIncidents::ReplicationSet", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "regions", - "target": "Regions" - }, { "source": "tags", "target": "Tags" @@ -43071,15 +41829,10 @@ }, { "cfn_type": "AWS::SSMIncidents::ResponsePlan", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "actions", - "target": "Actions" - }, - { - "source": "chatChannel", - "target": "ChatChannel" - }, { "source": "displayName", "target": "DisplayName" @@ -43088,14 +41841,6 @@ "source": "engagements", "target": "Engagements" }, - { - "source": "incidentTemplate", - "target": "IncidentTemplate" - }, - { - "source": "integrations", - "target": "Integrations" - }, { "source": "name", "target": "Name" @@ -43119,10 +41864,6 @@ { "cfn_type": "AWS::SSMQuickSetup::ConfigurationManager", "mappings": [ - { - "source": "ConfigurationDefinitions", - "target": "ConfigurationDefinitions" - }, { "source": "Description", "target": "Description" @@ -43130,10 +41871,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateConfigurationManager", @@ -43149,6 +41886,9 @@ }, { "cfn_type": "AWS::SSO::Application", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ApplicationProviderArn", @@ -43166,17 +41906,9 @@ "source": "Name", "target": "Name" }, - { - "source": "PortalOptions", - "target": "PortalOptions" - }, { "source": "Status", "target": "Status" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateApplication", @@ -43296,14 +42028,13 @@ }, { "cfn_type": "AWS::SSO::Instance", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateInstance", @@ -43320,10 +42051,6 @@ { "cfn_type": "AWS::SSO::InstanceAccessControlAttributeConfiguration", "mappings": [ - { - "source": "InstanceAccessControlAttributeConfiguration", - "target": "InstanceAccessControlAttributeConfiguration" - }, { "source": "InstanceArn", "target": "InstanceArn" @@ -43363,10 +42090,6 @@ { "source": "SessionDuration", "target": "SessionDuration" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreatePermissionSet", @@ -43400,25 +42123,9 @@ "source": "Description", "target": "Description" }, - { - "source": "MetadataProperties", - "target": "MetadataProperties" - }, - { - "source": "Properties", - "target": "Properties" - }, - { - "source": "Source", - "target": "Source" - }, { "source": "Status", "target": "Status" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAction", @@ -43451,18 +42158,6 @@ { "source": "CertifyForMarketplace", "target": "CertifyForMarketplace" - }, - { - "source": "InferenceSpecification", - "target": "InferenceSpecification" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "TrainingSpecification", - "target": "TrainingSpecification" } ], "operation": "CreateAlgorithm", @@ -43500,14 +42195,6 @@ "source": "RecoveryMode", "target": "RecoveryMode" }, - { - "source": "ResourceSpec", - "target": "ResourceSpec" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UserProfileName", "target": "UserProfileName" @@ -43547,22 +42234,6 @@ { "source": "AppImageConfigName", "target": "AppImageConfigName" - }, - { - "source": "CodeEditorAppImageConfig", - "target": "CodeEditorAppImageConfig" - }, - { - "source": "JupyterLabAppImageConfig", - "target": "JupyterLabAppImageConfig" - }, - { - "source": "KernelGatewayImageConfig", - "target": "KernelGatewayImageConfig" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAppImageConfig", @@ -43591,22 +42262,6 @@ { "source": "ArtifactType", "target": "ArtifactType" - }, - { - "source": "MetadataProperties", - "target": "MetadataProperties" - }, - { - "source": "Properties", - "target": "Properties" - }, - { - "source": "Source", - "target": "Source" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateArtifact", @@ -43615,12 +42270,7 @@ }, { "cfn_type": "AWS::SageMaker::Artifact", - "mappings": [ - { - "source": "Source", - "target": "Source" - } - ], + "mappings": [], "operation": "DeleteArtifact", "phase": "delete", "service": "sagemaker" @@ -43633,8 +42283,8 @@ "target": "ClusterName" }, { - "source": "InstanceGroups", - "target": "InstanceGroups" + "source": "ClusterRole", + "target": "ClusterRole" }, { "source": "NodeProvisioningMode", @@ -43643,22 +42293,6 @@ { "source": "NodeRecovery", "target": "NodeRecovery" - }, - { - "source": "Orchestrator", - "target": "Orchestrator" - }, - { - "source": "RestrictedInstanceGroups", - "target": "RestrictedInstanceGroups" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "VpcConfig", - "target": "VpcConfig" } ], "operation": "CreateCluster", @@ -43691,18 +42325,6 @@ { "source": "Description", "target": "Description" - }, - { - "source": "Properties", - "target": "Properties" - }, - { - "source": "Source", - "target": "Source" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateContext", @@ -43724,45 +42346,13 @@ { "cfn_type": "AWS::SageMaker::DataQualityJobDefinition", "mappings": [ - { - "source": "DataQualityAppSpecification", - "target": "DataQualityAppSpecification" - }, - { - "source": "DataQualityBaselineConfig", - "target": "DataQualityBaselineConfig" - }, - { - "source": "DataQualityJobInput", - "target": "DataQualityJobInput" - }, - { - "source": "DataQualityJobOutputConfig", - "target": "DataQualityJobOutputConfig" - }, { "source": "JobDefinitionName", "target": "JobDefinitionName" }, - { - "source": "JobResources", - "target": "JobResources" - }, - { - "source": "NetworkConfig", - "target": "NetworkConfig" - }, { "source": "RoleArn", "target": "RoleArn" - }, - { - "source": "StoppingCondition", - "target": "StoppingCondition" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDataQualityJobDefinition", @@ -43787,10 +42377,6 @@ { "source": "DeviceFleetName", "target": "DeviceFleetName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "RegisterDevices", @@ -43820,17 +42406,9 @@ "source": "DeviceFleetName", "target": "DeviceFleetName" }, - { - "source": "OutputConfig", - "target": "OutputConfig" - }, { "source": "RoleArn", "target": "RoleArn" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDeviceFleet", @@ -43864,21 +42442,13 @@ "source": "AuthMode", "target": "AuthMode" }, - { - "source": "DefaultSpaceSettings", - "target": "DefaultSpaceSettings" - }, - { - "source": "DefaultUserSettings", - "target": "DefaultUserSettings" - }, { "source": "DomainName", "target": "DomainName" }, { - "source": "DomainSettings", - "target": "DomainSettings" + "source": "HomeEfsFileSystemCreation", + "target": "HomeEfsFileSystemCreation" }, { "source": "KmsKeyId", @@ -43892,10 +42462,6 @@ "source": "TagPropagation", "target": "TagPropagation" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VpcId", "target": "VpcId" @@ -43915,17 +42481,9 @@ { "cfn_type": "AWS::SageMaker::Endpoint", "mappings": [ - { - "source": "DeploymentConfig", - "target": "DeploymentConfig" - }, { "source": "EndpointConfigName", "target": "EndpointConfigName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateEndpoint", @@ -43953,10 +42511,6 @@ { "source": "ExperimentName", "target": "ExperimentName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateExperiment", @@ -43986,22 +42540,10 @@ "source": "EventTimeFeatureName", "target": "EventTimeFeatureName" }, - { - "source": "FeatureDefinitions", - "target": "FeatureDefinitions" - }, { "source": "FeatureGroupName", "target": "FeatureGroupName" }, - { - "source": "OfflineStoreConfig", - "target": "OfflineStoreConfig" - }, - { - "source": "OnlineStoreConfig", - "target": "OnlineStoreConfig" - }, { "source": "RecordIdentifierFeatureName", "target": "RecordIdentifierFeatureName" @@ -44009,14 +42551,6 @@ { "source": "RoleArn", "target": "RoleArn" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "ThroughputConfig", - "target": "ThroughputConfig" } ], "operation": "CreateFeatureGroup", @@ -44053,14 +42587,6 @@ { "source": "HubSearchKeywords", "target": "HubSearchKeywords" - }, - { - "source": "S3StorageConfig", - "target": "S3StorageConfig" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateHub", @@ -44079,16 +42605,36 @@ "phase": "delete", "service": "sagemaker" }, + { + "cfn_type": "AWS::SageMaker::HumanTaskUi", + "mappings": [ + { + "source": "HumanTaskUiName", + "target": "HumanTaskUiName" + } + ], + "operation": "CreateHumanTaskUi", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::HumanTaskUi", + "mappings": [ + { + "source": "HumanTaskUiName", + "target": "HumanTaskUiName" + } + ], + "operation": "DeleteHumanTaskUi", + "phase": "delete", + "service": "sagemaker" + }, { "cfn_type": "AWS::SageMaker::Image", "mappings": [ { "source": "ImageName", "target": "ImageName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateImage", @@ -44109,6 +42655,9 @@ }, { "cfn_type": "AWS::SageMaker::ImageVersion", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Aliases", @@ -44182,18 +42731,6 @@ "source": "InferenceComponentName", "target": "InferenceComponentName" }, - { - "source": "RuntimeConfig", - "target": "RuntimeConfig" - }, - { - "source": "Specification", - "target": "Specification" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VariantName", "target": "VariantName" @@ -44218,10 +42755,6 @@ { "cfn_type": "AWS::SageMaker::InferenceExperiment", "mappings": [ - { - "source": "DataStorageConfig", - "target": "DataStorageConfig" - }, { "source": "Description", "target": "Description" @@ -44234,10 +42767,6 @@ "source": "KmsKey", "target": "KmsKey" }, - { - "source": "ModelVariants", - "target": "ModelVariants" - }, { "source": "Name", "target": "Name" @@ -44246,18 +42775,6 @@ "source": "RoleArn", "target": "RoleArn" }, - { - "source": "Schedule", - "target": "Schedule" - }, - { - "source": "ShadowModeConfig", - "target": "ShadowModeConfig" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" @@ -44279,6 +42796,41 @@ "phase": "delete", "service": "sagemaker" }, + { + "cfn_type": "AWS::SageMaker::MlflowApp", + "mappings": [ + { + "source": "ArtifactStoreUri", + "target": "ArtifactStoreUri" + }, + { + "source": "ModelRegistrationMode", + "target": "ModelRegistrationMode" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "WeeklyMaintenanceWindowStart", + "target": "WeeklyMaintenanceWindowStart" + } + ], + "operation": "CreateMlflowApp", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::MlflowApp", + "mappings": [], + "operation": "DeleteMlflowApp", + "phase": "delete", + "service": "sagemaker" + }, { "cfn_type": "AWS::SageMaker::MlflowTrackingServer", "mappings": [ @@ -44298,10 +42850,6 @@ "source": "RoleArn", "target": "RoleArn" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TrackingServerName", "target": "TrackingServerName" @@ -44334,10 +42882,6 @@ { "cfn_type": "AWS::SageMaker::Model", "mappings": [ - { - "source": "Containers", - "target": "Containers" - }, { "source": "EnableNetworkIsolation", "target": "EnableNetworkIsolation" @@ -44346,25 +42890,9 @@ "source": "ExecutionRoleArn", "target": "ExecutionRoleArn" }, - { - "source": "InferenceExecutionConfig", - "target": "InferenceExecutionConfig" - }, { "source": "ModelName", "target": "ModelName" - }, - { - "source": "PrimaryContainer", - "target": "PrimaryContainer" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "VpcConfig", - "target": "VpcConfig" } ], "operation": "CreateModel", @@ -44390,41 +42918,9 @@ "source": "JobDefinitionName", "target": "JobDefinitionName" }, - { - "source": "JobResources", - "target": "JobResources" - }, - { - "source": "ModelBiasAppSpecification", - "target": "ModelBiasAppSpecification" - }, - { - "source": "ModelBiasBaselineConfig", - "target": "ModelBiasBaselineConfig" - }, - { - "source": "ModelBiasJobInput", - "target": "ModelBiasJobInput" - }, - { - "source": "ModelBiasJobOutputConfig", - "target": "ModelBiasJobOutputConfig" - }, - { - "source": "NetworkConfig", - "target": "NetworkConfig" - }, { "source": "RoleArn", "target": "RoleArn" - }, - { - "source": "StoppingCondition", - "target": "StoppingCondition" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateModelBiasJobDefinition", @@ -44446,10 +42942,6 @@ { "cfn_type": "AWS::SageMaker::ModelCard", "mappings": [ - { - "source": "Content", - "target": "Content" - }, { "source": "ModelCardName", "target": "ModelCardName" @@ -44457,14 +42949,6 @@ { "source": "ModelCardStatus", "target": "ModelCardStatus" - }, - { - "source": "SecurityConfig", - "target": "SecurityConfig" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateModelCard", @@ -44490,41 +42974,9 @@ "source": "JobDefinitionName", "target": "JobDefinitionName" }, - { - "source": "JobResources", - "target": "JobResources" - }, - { - "source": "ModelExplainabilityAppSpecification", - "target": "ModelExplainabilityAppSpecification" - }, - { - "source": "ModelExplainabilityBaselineConfig", - "target": "ModelExplainabilityBaselineConfig" - }, - { - "source": "ModelExplainabilityJobInput", - "target": "ModelExplainabilityJobInput" - }, - { - "source": "ModelExplainabilityJobOutputConfig", - "target": "ModelExplainabilityJobOutputConfig" - }, - { - "source": "NetworkConfig", - "target": "NetworkConfig" - }, { "source": "RoleArn", "target": "RoleArn" - }, - { - "source": "StoppingCondition", - "target": "StoppingCondition" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateModelExplainabilityJobDefinition", @@ -44545,11 +42997,10 @@ }, { "cfn_type": "AWS::SageMaker::ModelPackage", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "AdditionalInferenceSpecifications", - "target": "AdditionalInferenceSpecifications" - }, { "source": "CertifyForMarketplace", "target": "CertifyForMarketplace" @@ -44558,38 +43009,14 @@ "source": "ClientToken", "target": "ClientToken" }, - { - "source": "CustomerMetadataProperties", - "target": "CustomerMetadataProperties" - }, { "source": "Domain", "target": "Domain" }, - { - "source": "DriftCheckBaselines", - "target": "DriftCheckBaselines" - }, - { - "source": "InferenceSpecification", - "target": "InferenceSpecification" - }, - { - "source": "MetadataProperties", - "target": "MetadataProperties" - }, { "source": "ModelApprovalStatus", "target": "ModelApprovalStatus" }, - { - "source": "ModelCard", - "target": "ModelCard" - }, - { - "source": "ModelMetrics", - "target": "ModelMetrics" - }, { "source": "ModelPackageDescription", "target": "ModelPackageDescription" @@ -44606,33 +43033,17 @@ "source": "SamplePayloadUrl", "target": "SamplePayloadUrl" }, - { - "source": "SecurityConfig", - "target": "SecurityConfig" - }, { "source": "SkipModelValidation", "target": "SkipModelValidation" }, - { - "source": "SourceAlgorithmSpecification", - "target": "SourceAlgorithmSpecification" - }, { "source": "SourceUri", "target": "SourceUri" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Task", "target": "Task" - }, - { - "source": "ValidationSpecification", - "target": "ValidationSpecification" } ], "operation": "CreateModelPackage", @@ -44661,10 +43072,6 @@ { "source": "ModelPackageGroupName", "target": "ModelPackageGroupName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateModelPackageGroup", @@ -44690,41 +43097,9 @@ "source": "JobDefinitionName", "target": "JobDefinitionName" }, - { - "source": "JobResources", - "target": "JobResources" - }, - { - "source": "ModelQualityAppSpecification", - "target": "ModelQualityAppSpecification" - }, - { - "source": "ModelQualityBaselineConfig", - "target": "ModelQualityBaselineConfig" - }, - { - "source": "ModelQualityJobInput", - "target": "ModelQualityJobInput" - }, - { - "source": "ModelQualityJobOutputConfig", - "target": "ModelQualityJobOutputConfig" - }, - { - "source": "NetworkConfig", - "target": "NetworkConfig" - }, { "source": "RoleArn", "target": "RoleArn" - }, - { - "source": "StoppingCondition", - "target": "StoppingCondition" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateModelQualityJobDefinition", @@ -44746,17 +43121,9 @@ { "cfn_type": "AWS::SageMaker::MonitoringSchedule", "mappings": [ - { - "source": "MonitoringScheduleConfig", - "target": "MonitoringScheduleConfig" - }, { "source": "MonitoringScheduleName", "target": "MonitoringScheduleName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateMonitoringSchedule", @@ -44777,11 +43144,10 @@ }, { "cfn_type": "AWS::SageMaker::PartnerApp", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "ApplicationConfig", - "target": "ApplicationConfig" - }, { "source": "AuthType", "target": "AuthType" @@ -44790,6 +43156,10 @@ "source": "ClientToken", "target": "ClientToken" }, + { + "source": "EnableAutoMinorVersionUpgrade", + "target": "EnableAutoMinorVersionUpgrade" + }, { "source": "EnableIamSessionBasedIdentity", "target": "EnableIamSessionBasedIdentity" @@ -44802,18 +43172,10 @@ "source": "KmsKeyId", "target": "KmsKeyId" }, - { - "source": "MaintenanceConfig", - "target": "MaintenanceConfig" - }, { "source": "Name", "target": "Name" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Tier", "target": "Tier" @@ -44829,6 +43191,9 @@ }, { "cfn_type": "AWS::SageMaker::PartnerApp", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ClientToken", @@ -44841,15 +43206,10 @@ }, { "cfn_type": "AWS::SageMaker::Pipeline", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ - { - "source": "ParallelismConfiguration", - "target": "ParallelismConfiguration" - }, - { - "source": "PipelineDefinition", - "target": "PipelineDefinition" - }, { "source": "PipelineDescription", "target": "PipelineDescription" @@ -44865,10 +43225,6 @@ { "source": "RoleArn", "target": "RoleArn" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreatePipeline", @@ -44877,6 +43233,9 @@ }, { "cfn_type": "AWS::SageMaker::Pipeline", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "PipelineName", @@ -44890,55 +43249,31 @@ { "cfn_type": "AWS::SageMaker::ProcessingJob", "mappings": [ - { - "source": "AppSpecification", - "target": "AppSpecification" - }, - { - "source": "Environment", - "target": "Environment" - }, - { - "source": "ExperimentConfig", - "target": "ExperimentConfig" - }, - { - "source": "NetworkConfig", - "target": "NetworkConfig" - }, - { - "source": "ProcessingInputs", - "target": "ProcessingInputs" - }, { "source": "ProcessingJobName", "target": "ProcessingJobName" }, - { - "source": "ProcessingOutputConfig", - "target": "ProcessingOutputConfig" - }, - { - "source": "ProcessingResources", - "target": "ProcessingResources" - }, { "source": "RoleArn", "target": "RoleArn" - }, - { - "source": "StoppingCondition", - "target": "StoppingCondition" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateProcessingJob", "phase": "create", "service": "sagemaker" }, + { + "cfn_type": "AWS::SageMaker::ProcessingJob", + "mappings": [ + { + "source": "ProcessingJobName", + "target": "ProcessingJobName" + } + ], + "operation": "DeleteProcessingJob", + "phase": "delete", + "service": "sagemaker" + }, { "cfn_type": "AWS::SageMaker::Project", "mappings": [ @@ -44949,14 +43284,6 @@ { "source": "ProjectName", "target": "ProjectName" - }, - { - "source": "ServiceCatalogProvisioningDetails", - "target": "ServiceCatalogProvisioningDetails" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateProject", @@ -44982,10 +43309,6 @@ "source": "DomainId", "target": "DomainId" }, - { - "source": "OwnershipSettings", - "target": "OwnershipSettings" - }, { "source": "SpaceDisplayName", "target": "SpaceDisplayName" @@ -44993,18 +43316,6 @@ { "source": "SpaceName", "target": "SpaceName" - }, - { - "source": "SpaceSettings", - "target": "SpaceSettings" - }, - { - "source": "SpaceSharingSettings", - "target": "SpaceSharingSettings" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateSpace", @@ -45041,10 +43352,6 @@ { "source": "StudioLifecycleConfigName", "target": "StudioLifecycleConfigName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateStudioLifecycleConfig", @@ -45070,30 +43377,6 @@ "source": "DisplayName", "target": "DisplayName" }, - { - "source": "InputArtifacts", - "target": "InputArtifacts" - }, - { - "source": "MetadataProperties", - "target": "MetadataProperties" - }, - { - "source": "OutputArtifacts", - "target": "OutputArtifacts" - }, - { - "source": "Parameters", - "target": "Parameters" - }, - { - "source": "Status", - "target": "Status" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TrialComponentName", "target": "TrialComponentName" @@ -45130,17 +43413,9 @@ "source": "SingleSignOnUserValue", "target": "SingleSignOnUserValue" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UserProfileName", "target": "UserProfileName" - }, - { - "source": "UserSettings", - "target": "UserSettings" } ], "operation": "CreateUserProfile", @@ -45164,19 +43439,42 @@ "service": "sagemaker" }, { - "cfn_type": "AWS::Scheduler::Schedule", + "cfn_type": "AWS::SageMaker::Workforce", "mappings": [ { - "source": "Description", - "target": "Description" + "source": "IpAddressType", + "target": "IpAddressType" }, { - "source": "EndDate", - "target": "EndDate" - }, + "source": "WorkforceName", + "target": "WorkforceName" + } + ], + "operation": "CreateWorkforce", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::Workforce", + "mappings": [ { - "source": "FlexibleTimeWindow", - "target": "FlexibleTimeWindow" + "source": "WorkforceName", + "target": "WorkforceName" + } + ], + "operation": "DeleteWorkforce", + "phase": "delete", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::Scheduler::Schedule", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "Description", + "target": "Description" }, { "source": "GroupName", @@ -45198,17 +43496,9 @@ "source": "ScheduleExpressionTimezone", "target": "ScheduleExpressionTimezone" }, - { - "source": "StartDate", - "target": "StartDate" - }, { "source": "State", "target": "State" - }, - { - "source": "Target", - "target": "Target" } ], "operation": "CreateSchedule", @@ -45217,6 +43507,9 @@ }, { "cfn_type": "AWS::Scheduler::Schedule", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "GroupName", @@ -45233,14 +43526,13 @@ }, { "cfn_type": "AWS::Scheduler::ScheduleGroup", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateScheduleGroup", @@ -45249,6 +43541,9 @@ }, { "cfn_type": "AWS::Scheduler::ScheduleGroup", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Name", @@ -45293,6 +43588,9 @@ }, { "cfn_type": "AWS::SecretsManager::Secret", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "Description", @@ -45311,8 +43609,8 @@ "target": "SecretString" }, { - "source": "Tags", - "target": "Tags" + "source": "Type", + "target": "Type" } ], "operation": "CreateSecret", @@ -45326,8 +43624,199 @@ "phase": "delete", "service": "secretsmanager" }, + { + "cfn_type": "AWS::SecurityAgent::AgentSpace", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetDomainIds", + "target": "TargetDomainIds" + } + ], + "operation": "CreateAgentSpace", + "phase": "create", + "service": "securityagent" + }, + { + "cfn_type": "AWS::SecurityAgent::AgentSpace", + "mappings": [], + "operation": "DeleteAgentSpace", + "phase": "delete", + "service": "securityagent" + }, + { + "cfn_type": "AWS::SecurityAgent::Application", + "mappings": [ + { + "source": "defaultKmsKeyId", + "target": "DefaultKmsKeyId" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "securityagent" + }, + { + "cfn_type": "AWS::SecurityAgent::Application", + "mappings": [], + "operation": "DeleteApplication", + "phase": "delete", + "service": "securityagent" + }, + { + "cfn_type": "AWS::SecurityAgent::Artifact", + "mappings": [ + { + "source": "agentSpaceId", + "target": "AgentSpaceId" + }, + { + "source": "artifactType", + "target": "ArtifactType" + }, + { + "source": "fileName", + "target": "FileName" + } + ], + "operation": "AddArtifact", + "phase": "create", + "service": "securityagent" + }, + { + "cfn_type": "AWS::SecurityAgent::Artifact", + "mappings": [ + { + "source": "agentSpaceId", + "target": "AgentSpaceId" + } + ], + "operation": "DeleteArtifact", + "phase": "delete", + "service": "securityagent" + }, + { + "cfn_type": "AWS::SecurityAgent::Pentest", + "mappings": [ + { + "source": "agentSpaceId", + "target": "AgentSpaceId" + }, + { + "source": "codeRemediationStrategy", + "target": "CodeRemediationStrategy" + }, + { + "source": "disableManagedSkills", + "target": "DisableManagedSkills" + }, + { + "source": "excludeRiskTypes", + "target": "ExcludeRiskTypes" + }, + { + "source": "serviceRole", + "target": "ServiceRole" + }, + { + "source": "title", + "target": "Title" + } + ], + "operation": "CreatePentest", + "phase": "create", + "service": "securityagent" + }, + { + "cfn_type": "AWS::SecurityAgent::SecurityRequirementPack", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "status", + "target": "Status" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSecurityRequirementPack", + "phase": "create", + "service": "securityagent" + }, + { + "cfn_type": "AWS::SecurityAgent::SecurityRequirementPack", + "mappings": [], + "operation": "DeleteSecurityRequirementPack", + "phase": "delete", + "service": "securityagent" + }, + { + "cfn_type": "AWS::SecurityAgent::TargetDomain", + "mappings": [ + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetDomainName", + "target": "TargetDomainName" + }, + { + "source": "verificationMethod", + "target": "VerificationMethod" + } + ], + "operation": "CreateTargetDomain", + "phase": "create", + "service": "securityagent" + }, + { + "cfn_type": "AWS::SecurityAgent::TargetDomain", + "mappings": [], + "operation": "DeleteTargetDomain", + "phase": "delete", + "service": "securityagent" + }, { "cfn_type": "AWS::SecurityHub::AggregatorV2", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "LinkedRegions", @@ -45336,10 +43825,6 @@ { "source": "RegionLinkingMode", "target": "RegionLinkingMode" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAggregatorV2", @@ -45356,14 +43841,6 @@ { "cfn_type": "AWS::SecurityHub::AutomationRule", "mappings": [ - { - "source": "Actions", - "target": "Actions" - }, - { - "source": "Criteria", - "target": "Criteria" - }, { "source": "Description", "target": "Description" @@ -45383,10 +43860,6 @@ { "source": "RuleStatus", "target": "RuleStatus" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAutomationRule", @@ -45395,15 +43868,10 @@ }, { "cfn_type": "AWS::SecurityHub::AutomationRuleV2", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ - { - "source": "Actions", - "target": "Actions" - }, - { - "source": "Criteria", - "target": "Criteria" - }, { "source": "Description", "target": "Description" @@ -45419,10 +43887,6 @@ { "source": "RuleStatus", "target": "RuleStatus" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAutomationRuleV2", @@ -45439,10 +43903,6 @@ { "cfn_type": "AWS::SecurityHub::ConfigurationPolicy", "mappings": [ - { - "source": "ConfigurationPolicy", - "target": "ConfigurationPolicy" - }, { "source": "Description", "target": "Description" @@ -45450,10 +43910,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateConfigurationPolicy", @@ -45467,8 +43923,37 @@ "phase": "delete", "service": "securityhub" }, + { + "cfn_type": "AWS::SecurityHub::Connector", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateConnector", + "phase": "create", + "service": "securityhub" + }, + { + "cfn_type": "AWS::SecurityHub::Connector", + "mappings": [], + "operation": "DeleteConnector", + "phase": "delete", + "service": "securityhub" + }, { "cfn_type": "AWS::SecurityHub::ConnectorV2", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -45481,14 +43966,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "Provider", - "target": "Provider" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateConnectorV2", @@ -45535,35 +44012,15 @@ { "source": "EnableDefaultStandards", "target": "EnableDefaultStandards" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "EnableSecurityHub", "phase": "create", "service": "securityhub" }, - { - "cfn_type": "AWS::SecurityHub::HubV2", - "mappings": [ - { - "source": "Tags", - "target": "Tags" - } - ], - "operation": "EnableSecurityHubV2", - "phase": "create", - "service": "securityhub" - }, { "cfn_type": "AWS::SecurityHub::Insight", "mappings": [ - { - "source": "Filters", - "target": "Filters" - }, { "source": "GroupByAttribute", "target": "GroupByAttribute" @@ -45597,10 +44054,6 @@ { "source": "metaStoreManagerRoleArn", "target": "MetaStoreManagerRoleArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateDataLake", @@ -45621,25 +44074,13 @@ "source": "accessTypes", "target": "AccessTypes" }, - { - "source": "sources", - "target": "Sources" - }, { "source": "subscriberDescription", "target": "SubscriberDescription" }, - { - "source": "subscriberIdentity", - "target": "SubscriberIdentity" - }, { "source": "subscriberName", "target": "SubscriberName" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateSubscriber", @@ -45660,8 +44101,70 @@ "phase": "delete", "service": "securitylake" }, + { + "cfn_type": "AWS::ServerlessRepo::Application", + "mappings": [ + { + "source": "Author", + "target": "Author" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "HomePageUrl", + "target": "HomePageUrl" + }, + { + "source": "Labels", + "target": "Labels" + }, + { + "source": "LicenseBody", + "target": "LicenseBody" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "ReadmeBody", + "target": "ReadmeBody" + }, + { + "source": "SemanticVersion", + "target": "SemanticVersion" + }, + { + "source": "SourceCodeUrl", + "target": "SourceCodeUrl" + }, + { + "source": "SpdxLicenseId", + "target": "SpdxLicenseId" + }, + { + "source": "TemplateBody", + "target": "TemplateBody" + } + ], + "operation": "CreateApplication", + "phase": "create", + "service": "serverlessrepo" + }, + { + "cfn_type": "AWS::ServerlessRepo::Application", + "mappings": [], + "operation": "DeleteApplication", + "phase": "delete", + "service": "serverlessrepo" + }, { "cfn_type": "AWS::ServiceCatalog::CloudFormationProduct", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "AcceptLanguage", @@ -45687,14 +44190,6 @@ "source": "ProductType", "target": "ProductType" }, - { - "source": "ProvisioningArtifactParameters", - "target": "ProvisioningArtifactParameters" - }, - { - "source": "SourceConnection", - "target": "SourceConnection" - }, { "source": "SupportDescription", "target": "SupportDescription" @@ -45706,10 +44201,6 @@ { "source": "SupportUrl", "target": "SupportUrl" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateProduct", @@ -45718,6 +44209,9 @@ }, { "cfn_type": "AWS::ServiceCatalog::CloudFormationProvisionedProduct", + "ignored_inputs": [ + "ProvisionToken" + ], "mappings": [ { "source": "AcceptLanguage", @@ -45754,18 +44248,6 @@ { "source": "ProvisioningArtifactName", "target": "ProvisioningArtifactName" - }, - { - "source": "ProvisioningParameters", - "target": "ProvisioningParameters" - }, - { - "source": "ProvisioningPreferences", - "target": "ProvisioningPreferences" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "ProvisionProduct", @@ -45774,6 +44256,9 @@ }, { "cfn_type": "AWS::ServiceCatalog::CloudFormationProvisionedProduct", + "ignored_inputs": [ + "TerminateToken" + ], "mappings": [ { "source": "AcceptLanguage", @@ -45790,6 +44275,9 @@ }, { "cfn_type": "AWS::ServiceCatalog::Portfolio", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "AcceptLanguage", @@ -45806,10 +44294,6 @@ { "source": "ProviderName", "target": "ProviderName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreatePortfolio", @@ -45966,15 +44450,14 @@ }, { "cfn_type": "AWS::ServiceCatalog::ServiceAction", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "AcceptLanguage", "target": "AcceptLanguage" }, - { - "source": "Definition", - "target": "Definition" - }, { "source": "DefinitionType", "target": "DefinitionType" @@ -45994,6 +44477,9 @@ }, { "cfn_type": "AWS::ServiceCatalog::ServiceAction", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "AcceptLanguage", @@ -46006,6 +44492,9 @@ }, { "cfn_type": "AWS::ServiceCatalog::ServiceActionAssociation", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "ProductId", @@ -46026,6 +44515,9 @@ }, { "cfn_type": "AWS::ServiceCatalog::ServiceActionAssociation", + "ignored_inputs": [ + "IdempotencyToken" + ], "mappings": [ { "source": "ProductId", @@ -46101,6 +44593,9 @@ }, { "cfn_type": "AWS::ServiceCatalogAppRegistry::Application", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -46109,10 +44604,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateApplication", @@ -46128,6 +44619,9 @@ }, { "cfn_type": "AWS::ServiceCatalogAppRegistry::AttributeGroup", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "attributes", @@ -46140,10 +44634,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateAttributeGroup", @@ -46230,7 +44720,10 @@ "service": "servicecatalog-appregistry" }, { - "cfn_type": "AWS::ServiceDiscovery::PublicDnsNamespace", + "cfn_type": "AWS::ServiceDiscovery::PrivateDnsNamespace", + "ignored_inputs": [ + "CreatorRequestId" + ], "mappings": [ { "source": "Description", @@ -46241,12 +44734,27 @@ "target": "Name" }, { - "source": "Properties", - "target": "Properties" + "source": "Vpc", + "target": "Vpc" + } + ], + "operation": "CreatePrivateDnsNamespace", + "phase": "create", + "service": "servicediscovery" + }, + { + "cfn_type": "AWS::ServiceDiscovery::PublicDnsNamespace", + "ignored_inputs": [ + "CreatorRequestId" + ], + "mappings": [ + { + "source": "Description", + "target": "Description" }, { - "source": "Tags", - "target": "Tags" + "source": "Name", + "target": "Name" } ], "operation": "CreatePublicDnsNamespace", @@ -46255,23 +44763,14 @@ }, { "cfn_type": "AWS::ServiceDiscovery::Service", + "ignored_inputs": [ + "CreatorRequestId" + ], "mappings": [ { "source": "Description", "target": "Description" }, - { - "source": "DnsConfig", - "target": "DnsConfig" - }, - { - "source": "HealthCheckConfig", - "target": "HealthCheckConfig" - }, - { - "source": "HealthCheckCustomConfig", - "target": "HealthCheckCustomConfig" - }, { "source": "Name", "target": "Name" @@ -46280,10 +44779,6 @@ "source": "NamespaceId", "target": "NamespaceId" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Type", "target": "Type" @@ -46310,10 +44805,6 @@ { "source": "ResourceArn", "target": "ResourceArn" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateProtection", @@ -46349,10 +44840,6 @@ { "source": "ResourceType", "target": "ResourceType" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateProtectionGroup", @@ -46422,10 +44909,6 @@ "source": "platformId", "target": "PlatformId" }, - { - "source": "signatureValidityPeriod", - "target": "SignatureValidityPeriod" - }, { "source": "tags", "target": "Tags" @@ -46442,55 +44925,12 @@ "phase": "delete", "service": "signer" }, - { - "cfn_type": "AWS::SimSpaceWeaver::Simulation", - "mappings": [ - { - "source": "MaximumDuration", - "target": "MaximumDuration" - }, - { - "source": "Name", - "target": "Name" - }, - { - "source": "RoleArn", - "target": "RoleArn" - }, - { - "source": "SchemaS3Location", - "target": "SchemaS3Location" - }, - { - "source": "SnapshotS3Location", - "target": "SnapshotS3Location" - } - ], - "operation": "StartSimulation", - "phase": "create", - "service": "simspaceweaver" - }, - { - "cfn_type": "AWS::SimSpaceWeaver::Simulation", - "mappings": [], - "operation": "DeleteSimulation", - "phase": "delete", - "service": "simspaceweaver" - }, { "cfn_type": "AWS::StepFunctions::Activity", "mappings": [ - { - "source": "encryptionConfiguration", - "target": "EncryptionConfiguration" - }, { "source": "name", "target": "Name" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateActivity", @@ -46507,18 +44947,6 @@ { "cfn_type": "AWS::StepFunctions::StateMachine", "mappings": [ - { - "source": "definition", - "target": "Definition" - }, - { - "source": "encryptionConfiguration", - "target": "EncryptionConfiguration" - }, - { - "source": "loggingConfiguration", - "target": "LoggingConfiguration" - }, { "source": "name", "target": "StateMachineName" @@ -46526,14 +44954,6 @@ { "source": "roleArn", "target": "RoleArn" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "tracingConfiguration", - "target": "TracingConfiguration" } ], "operation": "CreateStateMachine", @@ -46557,10 +44977,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "routingConfiguration", - "target": "RoutingConfiguration" } ], "operation": "CreateStateMachineAlias", @@ -46615,10 +45031,6 @@ { "source": "StorageClass", "target": "StorageClass" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateTapePool", @@ -46722,22 +45134,18 @@ { "cfn_type": "AWS::Synthetics::Canary", "mappings": [ - { - "source": "ArtifactConfig", - "target": "ArtifactConfig" - }, { "source": "ArtifactS3Location", "target": "ArtifactS3Location" }, - { - "source": "Code", - "target": "Code" - }, { "source": "ExecutionRoleArn", "target": "ExecutionRoleArn" }, + { + "source": "KmsKeyArn", + "target": "KmsKeyArn" + }, { "source": "Name", "target": "Name" @@ -46750,25 +45158,13 @@ "source": "ResourcesToReplicateTags", "target": "ResourcesToReplicateTags" }, - { - "source": "RunConfig", - "target": "RunConfig" - }, { "source": "RuntimeVersion", "target": "RuntimeVersion" }, - { - "source": "Schedule", - "target": "Schedule" - }, { "source": "Tags", "target": "Tags" - }, - { - "source": "VpcConfig", - "target": "VPCConfig" } ], "operation": "CreateCanary", @@ -46820,10 +45216,6 @@ { "source": "KmsKeyId", "target": "KmsKeyId" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDatabase", @@ -46873,10 +45265,6 @@ "source": "failoverMode", "target": "FailoverMode" }, - { - "source": "logDeliveryConfiguration", - "target": "LogDeliveryConfiguration" - }, { "source": "name", "target": "Name" @@ -46949,10 +45337,6 @@ "source": "deploymentType", "target": "DeploymentType" }, - { - "source": "logDeliveryConfiguration", - "target": "LogDeliveryConfiguration" - }, { "source": "name", "target": "Name" @@ -47000,15 +45384,14 @@ }, { "cfn_type": "AWS::Timestream::ScheduledQuery", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "ClientToken", "target": "ClientToken" }, - { - "source": "ErrorReportConfiguration", - "target": "ErrorReportConfiguration" - }, { "source": "KmsKeyId", "target": "KmsKeyId" @@ -47017,29 +45400,13 @@ "source": "Name", "target": "ScheduledQueryName" }, - { - "source": "NotificationConfiguration", - "target": "NotificationConfiguration" - }, { "source": "QueryString", "target": "QueryString" }, - { - "source": "ScheduleConfiguration", - "target": "ScheduleConfiguration" - }, { "source": "ScheduledQueryExecutionRoleArn", "target": "ScheduledQueryExecutionRoleArn" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "TargetConfiguration", - "target": "TargetConfiguration" } ], "operation": "CreateScheduledQuery", @@ -47060,25 +45427,9 @@ "source": "DatabaseName", "target": "DatabaseName" }, - { - "source": "MagneticStoreWriteProperties", - "target": "MagneticStoreWriteProperties" - }, - { - "source": "RetentionProperties", - "target": "RetentionProperties" - }, - { - "source": "Schema", - "target": "Schema" - }, { "source": "TableName", "target": "TableName" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateTable", @@ -47112,10 +45463,6 @@ "source": "LanguageCode", "target": "LanguageCode" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "VocabularyFilterFileUri", "target": "VocabularyFilterFileUri" @@ -47156,10 +45503,6 @@ "source": "BaseDirectory", "target": "BaseDirectory" }, - { - "source": "CustomDirectories", - "target": "CustomDirectories" - }, { "source": "Description", "target": "Description" @@ -47187,10 +45530,6 @@ { "source": "Status", "target": "Status" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateAgreement", @@ -47212,10 +45551,6 @@ { "cfn_type": "AWS::Transfer::Certificate", "mappings": [ - { - "source": "ActiveDate", - "target": "ActiveDate" - }, { "source": "Certificate", "target": "Certificate" @@ -47228,18 +45563,10 @@ "source": "Description", "target": "Description" }, - { - "source": "InactiveDate", - "target": "InactiveDate" - }, { "source": "PrivateKey", "target": "PrivateKey" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Usage", "target": "Usage" @@ -47264,8 +45591,8 @@ "target": "AccessRole" }, { - "source": "As2Config", - "target": "As2Config" + "source": "IpAddressType", + "target": "IpAddressType" }, { "source": "LoggingRole", @@ -47275,14 +45602,6 @@ "source": "SecurityPolicyName", "target": "SecurityPolicyName" }, - { - "source": "SftpConfig", - "target": "SftpConfig" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "Url", "target": "Url" @@ -47299,6 +45618,38 @@ "phase": "delete", "service": "transfer" }, + { + "cfn_type": "AWS::Transfer::HostKey", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "HostKeyBody", + "target": "HostKeyBody" + }, + { + "source": "ServerId", + "target": "ServerId" + } + ], + "operation": "ImportHostKey", + "phase": "create", + "service": "transfer" + }, + { + "cfn_type": "AWS::Transfer::HostKey", + "mappings": [ + { + "source": "ServerId", + "target": "ServerId" + } + ], + "operation": "DeleteHostKey", + "phase": "delete", + "service": "transfer" + }, { "cfn_type": "AWS::Transfer::Profile", "mappings": [ @@ -47313,10 +45664,6 @@ { "source": "ProfileType", "target": "ProfileType" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateProfile", @@ -47341,18 +45688,10 @@ "source": "Domain", "target": "Domain" }, - { - "source": "EndpointDetails", - "target": "EndpointDetails" - }, { "source": "EndpointType", "target": "EndpointType" }, - { - "source": "IdentityProviderDetails", - "target": "IdentityProviderDetails" - }, { "source": "IdentityProviderType", "target": "IdentityProviderType" @@ -47373,18 +45712,10 @@ "source": "PreAuthenticationLoginBanner", "target": "PreAuthenticationLoginBanner" }, - { - "source": "ProtocolDetails", - "target": "ProtocolDetails" - }, { "source": "Protocols", "target": "Protocols" }, - { - "source": "S3StorageOptions", - "target": "S3StorageOptions" - }, { "source": "SecurityPolicyName", "target": "SecurityPolicyName" @@ -47392,14 +45723,6 @@ { "source": "StructuredLogDestinations", "target": "StructuredLogDestinations" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "WorkflowDetails", - "target": "WorkflowDetails" } ], "operation": "CreateServer", @@ -47420,10 +45743,6 @@ "source": "HomeDirectory", "target": "HomeDirectory" }, - { - "source": "HomeDirectoryMappings", - "target": "HomeDirectoryMappings" - }, { "source": "HomeDirectoryType", "target": "HomeDirectoryType" @@ -47432,10 +45751,6 @@ "source": "Policy", "target": "Policy" }, - { - "source": "PosixProfile", - "target": "PosixProfile" - }, { "source": "Role", "target": "Role" @@ -47444,10 +45759,6 @@ "source": "ServerId", "target": "ServerId" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "UserName", "target": "UserName" @@ -47480,21 +45791,9 @@ "source": "AccessEndpoint", "target": "AccessEndpoint" }, - { - "source": "IdentityProviderDetails", - "target": "IdentityProviderDetails" - }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "WebAppEndpointPolicy", "target": "WebAppEndpointPolicy" - }, - { - "source": "WebAppUnits", - "target": "WebAppUnits" } ], "operation": "CreateWebApp", @@ -47514,18 +45813,6 @@ { "source": "Description", "target": "Description" - }, - { - "source": "OnExceptionSteps", - "target": "OnExceptionSteps" - }, - { - "source": "Steps", - "target": "Steps" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateWorkflow", @@ -47541,11 +45828,10 @@ }, { "cfn_type": "AWS::VerifiedPermissions::IdentitySource", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "configuration", - "target": "Configuration" - }, { "source": "policyStoreId", "target": "PolicyStoreId" @@ -47573,10 +45859,13 @@ }, { "cfn_type": "AWS::VerifiedPermissions::Policy", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "definition", - "target": "Definition" + "source": "name", + "target": "Name" }, { "source": "policyStoreId", @@ -47601,11 +45890,10 @@ }, { "cfn_type": "AWS::VerifiedPermissions::PolicyStore", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "deletionProtection", - "target": "DeletionProtection" - }, { "source": "description", "target": "Description" @@ -47613,10 +45901,6 @@ { "source": "tags", "target": "Tags" - }, - { - "source": "validationSettings", - "target": "ValidationSettings" } ], "operation": "CreatePolicyStore", @@ -47630,13 +45914,48 @@ "phase": "delete", "service": "verifiedpermissions" }, + { + "cfn_type": "AWS::VerifiedPermissions::PolicyStoreAlias", + "mappings": [ + { + "source": "aliasName", + "target": "AliasName" + }, + { + "source": "policyStoreId", + "target": "PolicyStoreId" + } + ], + "operation": "CreatePolicyStoreAlias", + "phase": "create", + "service": "verifiedpermissions" + }, + { + "cfn_type": "AWS::VerifiedPermissions::PolicyStoreAlias", + "mappings": [ + { + "source": "aliasName", + "target": "AliasName" + } + ], + "operation": "DeletePolicyStoreAlias", + "phase": "delete", + "service": "verifiedpermissions" + }, { "cfn_type": "AWS::VerifiedPermissions::PolicyTemplate", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", "target": "Description" }, + { + "source": "name", + "target": "Name" + }, { "source": "policyStoreId", "target": "PolicyStoreId" @@ -47664,6 +45983,9 @@ }, { "cfn_type": "AWS::VoiceID::Domain", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "Description", @@ -47672,14 +45994,6 @@ { "source": "Name", "target": "Name" - }, - { - "source": "ServerSideEncryptionConfiguration", - "target": "ServerSideEncryptionConfiguration" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateDomain", @@ -47695,6 +46009,9 @@ }, { "cfn_type": "AWS::VpcLattice::AccessLogSubscription", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "destinationArn", @@ -47753,12 +46070,37 @@ "service": "vpc-lattice" }, { - "cfn_type": "AWS::VpcLattice::Listener", + "cfn_type": "AWS::VpcLattice::DomainVerification", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { - "source": "defaultAction", - "target": "DefaultAction" + "source": "domainName", + "target": "DomainName" }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "StartDomainVerification", + "phase": "create", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::DomainVerification", + "mappings": [], + "operation": "DeleteDomainVerification", + "phase": "delete", + "service": "vpc-lattice" + }, + { + "cfn_type": "AWS::VpcLattice::Listener", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ { "source": "name", "target": "Name" @@ -47798,7 +46140,18 @@ }, { "cfn_type": "AWS::VpcLattice::ResourceConfiguration", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ + { + "source": "customDomainName", + "target": "CustomDomainName" + }, + { + "source": "groupDomain", + "target": "GroupDomain" + }, { "source": "name", "target": "Name" @@ -47807,10 +46160,6 @@ "source": "portRanges", "target": "PortRanges" }, - { - "source": "resourceConfigurationDefinition", - "target": "ResourceConfigurationDefinition" - }, { "source": "tags", "target": "Tags" @@ -47829,15 +46178,26 @@ }, { "cfn_type": "AWS::VpcLattice::ResourceGateway", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "ipAddressType", "target": "IpAddressType" }, + { + "source": "ipv4AddressesPerEni", + "target": "Ipv4AddressesPerEni" + }, { "source": "name", "target": "Name" }, + { + "source": "resourceConfigDnsResolution", + "target": "ResourceConfigDnsResolution" + }, { "source": "securityGroupIds", "target": "SecurityGroupIds" @@ -47896,19 +46256,14 @@ }, { "cfn_type": "AWS::VpcLattice::Rule", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "action", - "target": "Action" - }, { "source": "listenerIdentifier", "target": "ListenerIdentifier" }, - { - "source": "match", - "target": "Match" - }, { "source": "name", "target": "Name" @@ -47948,6 +46303,9 @@ }, { "cfn_type": "AWS::VpcLattice::Service", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "authType", @@ -47961,6 +46319,10 @@ "source": "customDomainName", "target": "CustomDomainName" }, + { + "source": "idleTimeoutSeconds", + "target": "IdleTimeoutSeconds" + }, { "source": "name", "target": "Name" @@ -47983,6 +46345,9 @@ }, { "cfn_type": "AWS::VpcLattice::ServiceNetwork", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "authType", @@ -47992,10 +46357,6 @@ "source": "name", "target": "Name" }, - { - "source": "sharingConfig", - "target": "SharingConfig" - }, { "source": "tags", "target": "Tags" @@ -48014,7 +46375,14 @@ }, { "cfn_type": "AWS::VpcLattice::ServiceNetworkResourceAssociation", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ + { + "source": "privateDnsEnabled", + "target": "PrivateDnsEnabled" + }, { "source": "tags", "target": "Tags" @@ -48033,6 +46401,9 @@ }, { "cfn_type": "AWS::VpcLattice::ServiceNetworkServiceAssociation", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "serviceIdentifier", @@ -48060,7 +46431,14 @@ }, { "cfn_type": "AWS::VpcLattice::ServiceNetworkVpcAssociation", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ + { + "source": "privateDnsEnabled", + "target": "PrivateDnsEnabled" + }, { "source": "securityGroupIds", "target": "SecurityGroupIds" @@ -48091,11 +46469,10 @@ }, { "cfn_type": "AWS::VpcLattice::TargetGroup", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "config", - "target": "Config" - }, { "source": "name", "target": "Name" @@ -48142,10 +46519,6 @@ { "source": "Scope", "target": "Scope" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateIPSet", @@ -48191,17 +46564,9 @@ "source": "Name", "target": "Name" }, - { - "source": "RegularExpressionList", - "target": "RegularExpressionList" - }, { "source": "Scope", "target": "Scope" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateRegexPatternSet", @@ -48231,10 +46596,6 @@ "source": "Capacity", "target": "Capacity" }, - { - "source": "CustomResponseBodies", - "target": "CustomResponseBodies" - }, { "source": "Description", "target": "Description" @@ -48243,21 +46604,9 @@ "source": "Name", "target": "Name" }, - { - "source": "Rules", - "target": "Rules" - }, { "source": "Scope", "target": "Scope" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "VisibilityConfig", - "target": "VisibilityConfig" } ], "operation": "CreateRuleGroup", @@ -48283,34 +46632,6 @@ { "cfn_type": "AWS::WAFv2::WebACL", "mappings": [ - { - "source": "ApplicationConfig", - "target": "ApplicationConfig" - }, - { - "source": "AssociationConfig", - "target": "AssociationConfig" - }, - { - "source": "CaptchaConfig", - "target": "CaptchaConfig" - }, - { - "source": "ChallengeConfig", - "target": "ChallengeConfig" - }, - { - "source": "CustomResponseBodies", - "target": "CustomResponseBodies" - }, - { - "source": "DataProtectionConfig", - "target": "DataProtectionConfig" - }, - { - "source": "DefaultAction", - "target": "DefaultAction" - }, { "source": "Description", "target": "Description" @@ -48319,29 +46640,13 @@ "source": "Name", "target": "Name" }, - { - "source": "OnSourceDDoSProtectionConfig", - "target": "OnSourceDDoSProtectionConfig" - }, - { - "source": "Rules", - "target": "Rules" - }, { "source": "Scope", "target": "Scope" }, - { - "source": "Tags", - "target": "Tags" - }, { "source": "TokenDomains", "target": "TokenDomains" - }, - { - "source": "VisibilityConfig", - "target": "VisibilityConfig" } ], "operation": "CreateWebACL", @@ -48364,24 +46669,11 @@ "phase": "delete", "service": "wafv2" }, - { - "cfn_type": "AWS::WAFv2::WebACLAssociation", - "mappings": [ - { - "source": "ResourceArn", - "target": "ResourceArn" - }, - { - "source": "WebACLArn", - "target": "WebACLArn" - } - ], - "operation": "AssociateWebACL", - "phase": "create", - "service": "wafv2" - }, { "cfn_type": "AWS::WellArchitected::Lens", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "JSONString", @@ -48398,6 +46690,9 @@ }, { "cfn_type": "AWS::WellArchitected::Lens", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [], "operation": "DeleteLens", "phase": "delete", @@ -48405,6 +46700,9 @@ }, { "cfn_type": "AWS::WellArchitected::Profile", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "ProfileDescription", @@ -48414,10 +46712,6 @@ "source": "ProfileName", "target": "ProfileName" }, - { - "source": "ProfileQuestions", - "target": "ProfileQuestions" - }, { "source": "Tags", "target": "Tags" @@ -48429,6 +46723,9 @@ }, { "cfn_type": "AWS::WellArchitected::Profile", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [], "operation": "DeleteProfile", "phase": "delete", @@ -48436,6 +46733,9 @@ }, { "cfn_type": "AWS::WellArchitected::ReviewTemplate", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [ { "source": "Description", @@ -48464,22 +46764,123 @@ }, { "cfn_type": "AWS::WellArchitected::ReviewTemplate", + "ignored_inputs": [ + "ClientRequestToken" + ], "mappings": [], "operation": "DeleteReviewTemplate", "phase": "delete", "service": "wellarchitected" }, + { + "cfn_type": "AWS::WellArchitected::Workload", + "ignored_inputs": [ + "ClientRequestToken" + ], + "mappings": [ + { + "source": "AccountIds", + "target": "AccountIds" + }, + { + "source": "ArchitecturalDesign", + "target": "ArchitecturalDesign" + }, + { + "source": "AwsRegions", + "target": "AwsRegions" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "Environment", + "target": "Environment" + }, + { + "source": "Industry", + "target": "Industry" + }, + { + "source": "IndustryType", + "target": "IndustryType" + }, + { + "source": "Lenses", + "target": "Lenses" + }, + { + "source": "NonAwsRegions", + "target": "NonAwsRegions" + }, + { + "source": "Notes", + "target": "Notes" + }, + { + "source": "ReviewOwner", + "target": "ReviewOwner" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "WorkloadName", + "target": "WorkloadName" + } + ], + "operation": "CreateWorkload", + "phase": "create", + "service": "wellarchitected" + }, + { + "cfn_type": "AWS::WellArchitected::Workload", + "ignored_inputs": [ + "ClientRequestToken" + ], + "mappings": [], + "operation": "DeleteWorkload", + "phase": "delete", + "service": "wellarchitected" + }, + { + "cfn_type": "AWS::Wickr::Network", + "mappings": [ + { + "source": "accessLevel", + "target": "AccessLevel" + }, + { + "source": "networkName", + "target": "NetworkName" + } + ], + "operation": "CreateNetwork", + "phase": "create", + "service": "wickr" + }, + { + "cfn_type": "AWS::Wickr::Network", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [], + "operation": "DeleteNetwork", + "phase": "delete", + "service": "wickr" + }, { "cfn_type": "AWS::Wisdom::AIAgent", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "assistantId", "target": "AssistantId" }, - { - "source": "configuration", - "target": "Configuration" - }, { "source": "description", "target": "Description" @@ -48488,10 +46889,6 @@ "source": "name", "target": "Name" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "type", "target": "Type" @@ -48515,6 +46912,9 @@ }, { "cfn_type": "AWS::Wisdom::AIAgentVersion", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "aiAgentId", @@ -48547,6 +46947,9 @@ }, { "cfn_type": "AWS::Wisdom::AIGuardrail", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "assistantId", @@ -48560,14 +46963,6 @@ "source": "blockedOutputsMessaging", "target": "BlockedOutputsMessaging" }, - { - "source": "contentPolicyConfig", - "target": "ContentPolicyConfig" - }, - { - "source": "contextualGroundingPolicyConfig", - "target": "ContextualGroundingPolicyConfig" - }, { "source": "description", "target": "Description" @@ -48575,22 +46970,6 @@ { "source": "name", "target": "Name" - }, - { - "source": "sensitiveInformationPolicyConfig", - "target": "SensitiveInformationPolicyConfig" - }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "topicPolicyConfig", - "target": "TopicPolicyConfig" - }, - { - "source": "wordPolicyConfig", - "target": "WordPolicyConfig" } ], "operation": "CreateAIGuardrail", @@ -48611,6 +46990,9 @@ }, { "cfn_type": "AWS::Wisdom::AIGuardrailVersion", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "aiGuardrailId", @@ -48643,6 +47025,9 @@ }, { "cfn_type": "AWS::Wisdom::AIPrompt", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "apiFormat", @@ -48664,14 +47049,6 @@ "source": "name", "target": "Name" }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "templateConfiguration", - "target": "TemplateConfiguration" - }, { "source": "templateType", "target": "TemplateType" @@ -48699,6 +47076,9 @@ }, { "cfn_type": "AWS::Wisdom::AIPromptVersion", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "aiPromptId", @@ -48731,6 +47111,9 @@ }, { "cfn_type": "AWS::Wisdom::Assistant", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -48740,10 +47123,6 @@ "source": "name", "target": "Name" }, - { - "source": "serverSideEncryptionConfiguration", - "target": "ServerSideEncryptionConfiguration" - }, { "source": "tags", "target": "Tags" @@ -48766,15 +47145,14 @@ }, { "cfn_type": "AWS::Wisdom::AssistantAssociation", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "assistantId", "target": "AssistantId" }, - { - "source": "association", - "target": "Association" - }, { "source": "associationType", "target": "AssociationType" @@ -48802,6 +47180,9 @@ }, { "cfn_type": "AWS::Wisdom::KnowledgeBase", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "description", @@ -48815,18 +47196,6 @@ "source": "name", "target": "Name" }, - { - "source": "renderingConfiguration", - "target": "RenderingConfiguration" - }, - { - "source": "serverSideEncryptionConfiguration", - "target": "ServerSideEncryptionConfiguration" - }, - { - "source": "sourceConfiguration", - "target": "SourceConfiguration" - }, { "source": "tags", "target": "Tags" @@ -48845,27 +47214,18 @@ }, { "cfn_type": "AWS::Wisdom::MessageTemplate", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "channelSubtype", "target": "ChannelSubtype" }, - { - "source": "content", - "target": "Content" - }, - { - "source": "defaultAttributes", - "target": "DefaultAttributes" - }, { "source": "description", "target": "Description" }, - { - "source": "groupingConfiguration", - "target": "GroupingConfiguration" - }, { "source": "language", "target": "Language" @@ -48904,15 +47264,14 @@ }, { "cfn_type": "AWS::Wisdom::QuickResponse", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "channels", "target": "Channels" }, - { - "source": "content", - "target": "Content" - }, { "source": "contentType", "target": "ContentType" @@ -48921,10 +47280,6 @@ "source": "description", "target": "Description" }, - { - "source": "groupingConfiguration", - "target": "GroupingConfiguration" - }, { "source": "isActive", "target": "IsActive" @@ -48963,10 +47318,6 @@ { "source": "ConnectionString", "target": "ConnectionString" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateConnectionAlias", @@ -48997,14 +47348,6 @@ { "source": "GroupName", "target": "GroupName" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "UserRules", - "target": "UserRules" } ], "operation": "CreateIpGroup", @@ -49014,18 +47357,10 @@ { "cfn_type": "AWS::WorkSpaces::WorkspacesPool", "mappings": [ - { - "source": "ApplicationSettings", - "target": "ApplicationSettings" - }, { "source": "BundleId", "target": "BundleId" }, - { - "source": "Capacity", - "target": "Capacity" - }, { "source": "Description", "target": "Description" @@ -49041,14 +47376,6 @@ { "source": "RunningMode", "target": "RunningMode" - }, - { - "source": "Tags", - "target": "Tags" - }, - { - "source": "TimeoutSettings", - "target": "TimeoutSettings" } ], "operation": "CreateWorkspacesPool", @@ -49064,6 +47391,9 @@ }, { "cfn_type": "AWS::WorkSpacesThinClient::Environment", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "desiredSoftwareSetId", @@ -49077,18 +47407,10 @@ "source": "desktopEndpoint", "target": "DesktopEndpoint" }, - { - "source": "deviceCreationTags", - "target": "DeviceCreationTags" - }, { "source": "kmsKeyArn", "target": "KmsKeyArn" }, - { - "source": "maintenanceWindow", - "target": "MaintenanceWindow" - }, { "source": "name", "target": "Name" @@ -49112,6 +47434,9 @@ }, { "cfn_type": "AWS::WorkSpacesThinClient::Environment", + "ignored_inputs": [ + "clientToken" + ], "mappings": [], "operation": "DeleteEnvironment", "phase": "delete", @@ -49119,11 +47444,10 @@ }, { "cfn_type": "AWS::WorkSpacesWeb::BrowserSettings", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "additionalEncryptionContext", - "target": "AdditionalEncryptionContext" - }, { "source": "browserPolicy", "target": "BrowserPolicy" @@ -49131,10 +47455,6 @@ { "source": "customerManagedKey", "target": "CustomerManagedKey" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateBrowserSettings", @@ -49150,11 +47470,10 @@ }, { "cfn_type": "AWS::WorkSpacesWeb::DataProtectionSettings", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "additionalEncryptionContext", - "target": "AdditionalEncryptionContext" - }, { "source": "customerManagedKey", "target": "CustomerManagedKey" @@ -49166,14 +47485,6 @@ { "source": "displayName", "target": "DisplayName" - }, - { - "source": "inlineRedactionConfiguration", - "target": "InlineRedactionConfiguration" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateDataProtectionSettings", @@ -49189,11 +47500,10 @@ }, { "cfn_type": "AWS::WorkSpacesWeb::IdentityProvider", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "identityProviderDetails", - "target": "IdentityProviderDetails" - }, { "source": "identityProviderName", "target": "IdentityProviderName" @@ -49205,10 +47515,6 @@ { "source": "portalArn", "target": "PortalArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateIdentityProvider", @@ -49224,11 +47530,10 @@ }, { "cfn_type": "AWS::WorkSpacesWeb::IpAccessSettings", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "additionalEncryptionContext", - "target": "AdditionalEncryptionContext" - }, { "source": "customerManagedKey", "target": "CustomerManagedKey" @@ -49240,14 +47545,6 @@ { "source": "displayName", "target": "DisplayName" - }, - { - "source": "ipRules", - "target": "IpRules" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateIpAccessSettings", @@ -49263,6 +47560,9 @@ }, { "cfn_type": "AWS::WorkSpacesWeb::NetworkSettings", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "securityGroupIds", @@ -49272,10 +47572,6 @@ "source": "subnetIds", "target": "SubnetIds" }, - { - "source": "tags", - "target": "Tags" - }, { "source": "vpcId", "target": "VpcId" @@ -49294,11 +47590,10 @@ }, { "cfn_type": "AWS::WorkSpacesWeb::Portal", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "additionalEncryptionContext", - "target": "AdditionalEncryptionContext" - }, { "source": "authenticationType", "target": "AuthenticationType" @@ -49320,8 +47615,8 @@ "target": "MaxConcurrentSessions" }, { - "source": "tags", - "target": "Tags" + "source": "portalCustomDomain", + "target": "PortalCustomDomain" } ], "operation": "CreatePortal", @@ -49337,11 +47632,10 @@ }, { "cfn_type": "AWS::WorkSpacesWeb::SessionLogger", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "additionalEncryptionContext", - "target": "AdditionalEncryptionContext" - }, { "source": "customerManagedKey", "target": "CustomerManagedKey" @@ -49349,18 +47643,6 @@ { "source": "displayName", "target": "DisplayName" - }, - { - "source": "eventFilter", - "target": "EventFilter" - }, - { - "source": "logConfiguration", - "target": "LogConfiguration" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateSessionLogger", @@ -49374,22 +47656,6 @@ "phase": "delete", "service": "workspaces-web" }, - { - "cfn_type": "AWS::WorkSpacesWeb::TrustStore", - "mappings": [ - { - "source": "certificateList", - "target": "CertificateList" - }, - { - "source": "tags", - "target": "Tags" - } - ], - "operation": "CreateTrustStore", - "phase": "create", - "service": "workspaces-web" - }, { "cfn_type": "AWS::WorkSpacesWeb::TrustStore", "mappings": [], @@ -49399,14 +47665,13 @@ }, { "cfn_type": "AWS::WorkSpacesWeb::UserAccessLoggingSettings", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ { "source": "kinesisStreamArn", "target": "KinesisStreamArn" - }, - { - "source": "tags", - "target": "Tags" } ], "operation": "CreateUserAccessLoggingSettings", @@ -49422,15 +47687,10 @@ }, { "cfn_type": "AWS::WorkSpacesWeb::UserSettings", + "ignored_inputs": [ + "clientToken" + ], "mappings": [ - { - "source": "additionalEncryptionContext", - "target": "AdditionalEncryptionContext" - }, - { - "source": "cookieSynchronizationConfiguration", - "target": "CookieSynchronizationConfiguration" - }, { "source": "copyAllowed", "target": "CopyAllowed" @@ -49463,17 +47723,13 @@ "source": "printAllowed", "target": "PrintAllowed" }, - { - "source": "tags", - "target": "Tags" - }, - { - "source": "toolbarConfiguration", - "target": "ToolbarConfiguration" - }, { "source": "uploadAllowed", "target": "UploadAllowed" + }, + { + "source": "webAuthnAllowed", + "target": "WebAuthnAllowed" } ], "operation": "CreateUserSettings", @@ -49489,6 +47745,9 @@ }, { "cfn_type": "AWS::WorkspacesInstances::Volume", + "ignored_inputs": [ + "ClientToken" + ], "mappings": [ { "source": "AvailabilityZone", @@ -49514,10 +47773,6 @@ "source": "SnapshotId", "target": "SnapshotId" }, - { - "source": "TagSpecifications", - "target": "TagSpecifications" - }, { "source": "Throughput", "target": "Throughput" @@ -49582,22 +47837,6 @@ "phase": "delete", "service": "workspaces-instances" }, - { - "cfn_type": "AWS::WorkspacesInstances::WorkspaceInstance", - "mappings": [ - { - "source": "ManagedInstance", - "target": "ManagedInstance" - }, - { - "source": "Tags", - "target": "Tags" - } - ], - "operation": "CreateWorkspaceInstance", - "phase": "create", - "service": "workspaces-instances" - }, { "cfn_type": "AWS::WorkspacesInstances::WorkspaceInstance", "mappings": [], @@ -49615,14 +47854,6 @@ { "source": "GroupName", "target": "GroupName" - }, - { - "source": "InsightsConfiguration", - "target": "InsightsConfiguration" - }, - { - "source": "Tags", - "target": "Tags" } ], "operation": "CreateGroup", @@ -49673,22 +47904,6 @@ "phase": "delete", "service": "xray" }, - { - "cfn_type": "AWS::XRay::SamplingRule", - "mappings": [ - { - "source": "SamplingRule", - "target": "SamplingRule" - }, - { - "source": "Tags", - "target": "Tags" - } - ], - "operation": "CreateSamplingRule", - "phase": "create", - "service": "xray" - }, { "cfn_type": "AWS::XRay::SamplingRule", "mappings": [ @@ -49702,5 +47917,13 @@ "service": "xray" } ], - "format_version": 1 + "format_version": 1, + "source": { + "botocore_service_count": 431, + "botocore_version": "2.0.0dev155", + "compiled_schemas_sha256": "76bcee337b3f10bb9d72cbbe836d0c89afbb96a05c2d37c18eebf65685fbabb5", + "compiled_type_count": 1731, + "provider_schemas_sha256": "929767f603b6744fda6741ba5ca5ec42b54847a4f9a07402369ebecd8ce82248", + "provider_type_count": 1729 + } } diff --git a/src/data-source/scripts/generate_aws_api_catalog.py b/src/data-source/scripts/generate_aws_api_catalog.py index a986b754..d98445e6 100644 --- a/src/data-source/scripts/generate_aws_api_catalog.py +++ b/src/data-source/scripts/generate_aws_api_catalog.py @@ -29,7 +29,8 @@ validated as SKIPPED at runtime, never guessed. Usage: - PYTHONPATH= python3 generate_aws_api_catalog.py \ + python3 generate_aws_api_catalog.py \ + --botocore-root /path/to/botocore \ --provider-schemas schemas-standard.zip \ --compiled-schemas ../generated/schema-validator/compiled_schemas.json \ --output ../generated/data/aws_api_operation_catalog.json @@ -37,6 +38,7 @@ import argparse import hashlib +import importlib import json import subprocess import sys @@ -44,9 +46,6 @@ from collections import defaultdict from pathlib import Path -import botocore -import botocore.session - FORMAT_VERSION = 1 # Multiple provider types can list the same underlying operation. Keep a @@ -182,6 +181,7 @@ def _ignored_inputs_for_operation(members, phase, service, operation): def _parse_args(): parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--botocore-root', required=True, type=Path) parser.add_argument('--provider-schemas', required=True, type=Path) parser.add_argument('--compiled-schemas', required=True, type=Path) parser.add_argument('--output', required=True, type=Path) @@ -196,7 +196,8 @@ class BotocoreIndex: """Resolves IAM action prefixes to concrete botocore operations.""" def __init__(self): - self._session = botocore.session.Session() + botocore_session = importlib.import_module('botocore.session') + self._session = botocore_session.Session() self._identities = {} self._operations = {} self._by_identity = defaultdict(set) @@ -726,22 +727,152 @@ def _compute_coverage(unique_adapters, index, compiled_schemas): } +def _render_derivation(role, counters): + """Explain how provider resource types were matched to API operations.""" + rejection_reasons = ( + ('type_not_compiled', 'Missing from compiled CloudFormation schemas'), + ('no_handler', f'No {role} handler declared in the provider schema'), + ('excluded_service', 'Service excluded from catalog generation'), + ( + 'no_candidates', + 'Handler permissions contained no usable botocore API operation', + ), + ( + 'rejected', + 'Best candidate failed resource-name/property matching safety checks', + ), + ('tied_rejected', 'Multiple API operations tied for best candidate'), + ) + known_outcomes = { + 'verified', + 'stale_model_rejected', + *(outcome for outcome, _ in rejection_reasons), + } + unknown_outcomes = set(counters) - known_outcomes + if unknown_outcomes: + names = ', '.join(sorted(unknown_outcomes)) + raise ValueError(f'no reader-facing description for derivation outcomes: {names}') + + selected = counters.get('verified', 0) + not_selected = sum( + counters.get(outcome, 0) for outcome, _ in rejection_reasons + ) + stale_model_rejected = counters.get('stale_model_rejected', 0) + rejected = counters.get('rejected', 0) + if stale_model_rejected > rejected: + raise ValueError( + 'stale-model rejection count exceeds total candidate rejections' + ) + + title = role.capitalize() + lines = [ + f'{title} API operation matching:', + f' Resource types evaluated from provider schemas: {selected + not_selected:,}', + f' Resource types with one API operation selected: {selected:,}', + f' Resource types without an operation selection: {not_selected:,}', + ] + for outcome, description in rejection_reasons: + count = counters.get(outcome, 0) + if count == 0: + continue + lines.append(f' {description}: {count:,}') + if outcome == 'rejected' and stale_model_rejected: + lines.append( + f' Of those, the exact {role} operation from handler ' + 'permissions was absent from the loaded botocore models: ' + f'{stale_model_rejected:,}' + ) + return lines + + +def _render_fraction(description, entry): + covered = entry['covered'] + total = entry['total'] + percent = (covered / total * 100) if total > 0 else 0.0 + return f' {description}: {covered:,} of {total:,} ({percent:.1f}%)' + + def _render_coverage(coverage): - """Render coverage metrics as human-readable lines.""" - lines = [] - for label in ( - 'catalog_services', 'catalog_resources', 'catalog_commands', - 'state_services', 'state_resources', 'state_commands', - 'writable_properties', - ): - entry = coverage[label] - covered = entry['covered'] - total = entry['total'] - percent = (covered / total * 100) if total > 0 else 0.0 - lines.append(f'{label}: {covered}/{total} ({percent:.1f}%)') + """Render coverage metrics with explicit populations and denominators.""" + lines = [ + 'Catalog coverage (all final create, update, and delete adapters):', + _render_fraction( + 'botocore services represented', coverage['catalog_services'] + ), + _render_fraction( + 'Compiled CloudFormation resource types represented', + coverage['catalog_resources'], + ), + _render_fraction( + 'botocore API operations represented', coverage['catalog_commands'] + ), + '', + ( + 'State validation coverage (create/update adapters with at least ' + 'one writable-property mapping):' + ), + _render_fraction( + 'botocore services with state validation', coverage['state_services'] + ), + _render_fraction( + 'Compiled CloudFormation resource types with state validation', + coverage['state_resources'], + ), + _render_fraction( + 'botocore API operations used for state validation', + coverage['state_commands'], + ), + _render_fraction( + 'Writable CloudFormation properties mapped for state validation', + coverage['writable_properties'], + ), + '', + 'Final adapters by lifecycle phase:', + ] lifecycle = coverage.get('lifecycle_adapters', {}) - parts = ', '.join(f'{k} {v}' for k, v in sorted(lifecycle.items())) - lines.append(f'lifecycle_adapters: {parts}') + for phase in ('create', 'update', 'delete'): + lines.append(f' {phase.capitalize()} adapters: {lifecycle.get(phase, 0):,}') + for phase in sorted(set(lifecycle) - {'create', 'update', 'delete'}): + lines.append(f' {phase.capitalize()} adapters: {lifecycle[phase]:,}') + return lines + + +def _render_generation_report( + create_counters, + delete_counters, + dropped_count, + coverage, + adapter_count, + output_path, +): + """Render the complete catalog generation report.""" + lines = [ + 'AWS API catalog generation summary', + ( + 'An adapter links one CloudFormation resource type and lifecycle ' + 'action to one botocore API operation.' + ), + '', + ] + lines.extend(_render_derivation('create', create_counters)) + lines.append('') + lines.extend(_render_derivation('delete', delete_counters)) + lines.extend([ + '', + 'API operation uniqueness check:', + ( + ' Adapters removed so each botocore API operation appears only ' + f'once: {dropped_count:,}' + ), + '', + ]) + lines.extend(_render_coverage(coverage)) + lines.extend([ + '', + 'Catalog output:', + f' Adapters written: {adapter_count:,}', + f' File: {output_path}', + ]) return lines @@ -762,6 +893,18 @@ def _run_unit_tests(): def main(): args = _parse_args() _run_unit_tests() + if not args.botocore_root.is_dir(): + raise SystemExit( + f'botocore root directory not found: {args.botocore_root}' + ) + sys.path.insert(0, str(args.botocore_root.resolve())) + try: + botocore_module = importlib.import_module('botocore') + except ModuleNotFoundError as error: + raise SystemExit( + f'cannot import botocore from {args.botocore_root}: {error}' + ) from error + compiled_schemas = json.loads(args.compiled_schemas.read_text()) provider_schemas = _load_provider_schemas(args.provider_schemas) index = BotocoreIndex() @@ -813,7 +956,7 @@ def main(): 'compiled_schemas_sha256': _source_sha256( args.compiled_schemas ), - 'botocore_version': botocore.__version__, + 'botocore_version': botocore_module.__version__, 'botocore_service_count': index.service_count, 'provider_type_count': len(provider_schemas), 'compiled_type_count': len(compiled_schemas), @@ -824,12 +967,15 @@ def main(): args.output.write_text(json.dumps(document, indent=1, sort_keys=True) + '\n') coverage = _compute_coverage(unique_adapters, index, compiled_schemas) - print(f'create derivation: {dict(create_counters)}') - print(f'delete derivation: {dict(delete_counters)}') - print(f'uniqueness dropped: {len(dropped)}') - for line in _render_coverage(coverage): + for line in _render_generation_report( + create_counters, + delete_counters, + len(dropped), + coverage, + len(unique_adapters), + args.output, + ): print(line) - print(f"catalog: {len(unique_adapters)} adapters -> {args.output}") return 0 diff --git a/src/data-source/scripts/test_generate_aws_api_catalog.py b/src/data-source/scripts/test_generate_aws_api_catalog.py index aa4bf722..e56aaa56 100644 --- a/src/data-source/scripts/test_generate_aws_api_catalog.py +++ b/src/data-source/scripts/test_generate_aws_api_catalog.py @@ -257,6 +257,49 @@ def test_writable_properties_are_deduplicated_across_adapters(self): coverage = self._synthetic_coverage(adapters) self.assertEqual(coverage['writable_properties']['covered'], 1) + def test_render_derivation_explains_outcomes_and_subset(self): + counters = { + 'verified': 1305, + 'rejected': 113, + 'no_candidates': 118, + 'no_handler': 175, + 'tied_rejected': 3, + 'excluded_service': 15, + 'stale_model_rejected': 1, + } + + lines = catalog._render_derivation('create', counters) + + self.assertEqual([ + 'Create API operation matching:', + ' Resource types evaluated from provider schemas: 1,729', + ' Resource types with one API operation selected: 1,305', + ' Resource types without an operation selection: 424', + ' No create handler declared in the provider schema: 175', + ' Service excluded from catalog generation: 15', + ( + ' Handler permissions contained no usable botocore API ' + 'operation: 118' + ), + ( + ' Best candidate failed resource-name/property matching ' + 'safety checks: 113' + ), + ( + ' Of those, the exact create operation from handler ' + 'permissions was absent from the loaded botocore models: 1' + ), + ' Multiple API operations tied for best candidate: 3', + ], lines) + + def test_render_derivation_rejects_unexplained_outcome(self): + with self.assertRaisesRegex( + ValueError, 'no reader-facing description.*new_outcome' + ): + catalog._render_derivation( + 'create', {'verified': 1, 'new_outcome': 1} + ) + def test_render_coverage_formats_percentages(self): coverage = { 'catalog_services': {'covered': 3, 'total': 10}, @@ -268,15 +311,80 @@ def test_render_coverage_formats_percentages(self): 'writable_properties': {'covered': 15, 'total': 100}, 'lifecycle_adapters': {'create': 4, 'delete': 3}, } + lines = catalog._render_coverage(coverage) - self.assertIn('catalog_services: 3/10 (30.0%)', lines) - self.assertIn('catalog_resources: 5/20 (25.0%)', lines) - self.assertIn('catalog_commands: 7/50 (14.0%)', lines) - self.assertIn('state_services: 2/10 (20.0%)', lines) - self.assertIn('state_resources: 4/20 (20.0%)', lines) - self.assertIn('state_commands: 4/50 (8.0%)', lines) - self.assertIn('writable_properties: 15/100 (15.0%)', lines) - self.assertIn('lifecycle_adapters: create 4, delete 3', lines) + + self.assertEqual([ + 'Catalog coverage (all final create, update, and delete adapters):', + ' botocore services represented: 3 of 10 (30.0%)', + ( + ' Compiled CloudFormation resource types represented: ' + '5 of 20 (25.0%)' + ), + ' botocore API operations represented: 7 of 50 (14.0%)', + '', + ( + 'State validation coverage (create/update adapters with at ' + 'least one writable-property mapping):' + ), + ' botocore services with state validation: 2 of 10 (20.0%)', + ( + ' Compiled CloudFormation resource types with state ' + 'validation: 4 of 20 (20.0%)' + ), + ( + ' botocore API operations used for state validation: ' + '4 of 50 (8.0%)' + ), + ( + ' Writable CloudFormation properties mapped for state ' + 'validation: 15 of 100 (15.0%)' + ), + '', + 'Final adapters by lifecycle phase:', + ' Create adapters: 4', + ' Update adapters: 0', + ' Delete adapters: 3', + ], lines) + + def test_generation_report_explains_uniqueness_and_output(self): + coverage = { + 'catalog_services': {'covered': 1, 'total': 1}, + 'catalog_resources': {'covered': 1, 'total': 1}, + 'catalog_commands': {'covered': 2, 'total': 2}, + 'state_services': {'covered': 1, 'total': 1}, + 'state_resources': {'covered': 1, 'total': 1}, + 'state_commands': {'covered': 1, 'total': 2}, + 'writable_properties': {'covered': 1, 'total': 2}, + 'lifecycle_adapters': {'create': 1, 'delete': 1}, + } + + lines = catalog._render_generation_report( + {'verified': 1}, + {'verified': 1}, + 2, + coverage, + 2, + Path('/tmp/catalog.json'), + ) + + self.assertEqual('AWS API catalog generation summary', lines[0]) + self.assertIn( + 'An adapter links one CloudFormation resource type and lifecycle ' + 'action to one botocore API operation.', + lines, + ) + self.assertIn('API operation uniqueness check:', lines) + self.assertIn( + ' Adapters removed so each botocore API operation appears only ' + 'once: 2', + lines, + ) + self.assertEqual([ + 'Catalog output:', + ' Adapters written: 2', + ' File: /tmp/catalog.json', + ], lines[-3:]) def test_zero_total_does_not_divide_by_zero(self): coverage = { @@ -289,8 +397,12 @@ def test_zero_total_does_not_divide_by_zero(self): 'writable_properties': {'covered': 0, 'total': 0}, 'lifecycle_adapters': {}, } + lines = catalog._render_coverage(coverage) - self.assertTrue(all('0.0%' in line for line in lines if '/' in line)) + + percentage_lines = [line for line in lines if line.endswith('%)')] + self.assertEqual(7, len(percentage_lines)) + self.assertTrue(all('(0.0%)' in line for line in percentage_lines)) def test_exact_percentage_calculation(self): adapters = [ diff --git a/src/data-source/src/generate.rs b/src/data-source/src/generate.rs index 0ad0ebb5..76e2d18f 100644 --- a/src/data-source/src/generate.rs +++ b/src/data-source/src/generate.rs @@ -9,7 +9,7 @@ fn main() -> anyhow::Result<()> { eprintln!( "Usage: cargo run -p data-source --features maintenance --example generate\n\n\ Generates all outputs from existing upstream data.\n\ - To refresh upstream data first, run `cargo run -p data-source --features maintenance --example sync -- --cfn-lint-root `." + To refresh upstream data first, run `cargo run -p data-source --features maintenance --example sync -- --cfn-lint-root --aws-cli-root `." ); return Ok(()); } diff --git a/src/data-source/src/lib.rs b/src/data-source/src/lib.rs index f035a9ca..6381ca1f 100644 --- a/src/data-source/src/lib.rs +++ b/src/data-source/src/lib.rs @@ -135,6 +135,108 @@ pub fn sync_upstream(upstream_dir: &Path, rule_source_root: &str) -> anyhow::Res Ok(()) } +#[cfg(feature = "maintenance")] +const AWS_API_OPERATION_CATALOG_FORMAT_VERSION: u64 = 1; + +#[cfg(feature = "maintenance")] +#[derive(serde::Deserialize)] +struct AwsApiOperationCatalog { + format_version: u64, + adapters: Vec, +} + +/// Generate the AWS API operation catalog from synced provider schemas and compiled schemas. +#[cfg(feature = "maintenance")] +pub fn generate_aws_api_catalog(upstream_dir: &Path, generated_dir: &Path, aws_cli_root: &Path) -> anyhow::Result<()> { + let botocore_root = aws_cli_root.join("awscli"); + let botocore_package = botocore_root.join("botocore").join("__init__.py"); + let provider_schemas = upstream_dir.join("schemas"); + let compiled_schemas = generated_dir.join("schema-validator").join("compiled_schemas.json"); + let catalog_path = generated_dir.join("data").join("aws_api_operation_catalog.json"); + let script_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("scripts").join("generate_aws_api_catalog.py"); + + anyhow::ensure!(script_path.is_file(), "AWS API catalog generator not found at {}", script_path.display()); + anyhow::ensure!(aws_cli_root.is_dir(), "AWS CLI checkout not found at {}", aws_cli_root.display()); + anyhow::ensure!( + botocore_package.is_file(), + "AWS CLI checkout does not contain botocore at {}", + botocore_package.display() + ); + anyhow::ensure!( + provider_schemas.is_dir(), + "provider schemas directory not found at {}", + provider_schemas.display() + ); + anyhow::ensure!(compiled_schemas.is_file(), "compiled schemas not found at {}", compiled_schemas.display()); + + info!("Generating AWS API operation catalog via {}", script_path.display()); + let status = std::process::Command::new("python3") + .arg(&script_path) + .arg("--botocore-root") + .arg(&botocore_root) + .arg("--provider-schemas") + .arg(&provider_schemas) + .arg("--compiled-schemas") + .arg(&compiled_schemas) + .arg("--output") + .arg(&catalog_path) + .status() + .map_err(|error| anyhow::anyhow!("failed to start AWS API catalog generator: {error}"))?; + anyhow::ensure!(status.success(), "AWS API catalog generator failed with {status}"); + + let catalog_bytes = fs::read(&catalog_path) + .map_err(|error| anyhow::anyhow!("failed to read generated catalog {}: {error}", catalog_path.display()))?; + let adapter_count = validate_aws_api_catalog(&catalog_bytes)?; + info!("Generated AWS API operation catalog with {adapter_count} adapters at {}", catalog_path.display()); + Ok(()) +} + +#[cfg(feature = "maintenance")] +fn validate_aws_api_catalog(catalog_bytes: &[u8]) -> anyhow::Result { + let catalog: AwsApiOperationCatalog = serde_json::from_slice(catalog_bytes) + .map_err(|error| anyhow::anyhow!("generated AWS API operation catalog is invalid JSON: {error}"))?; + anyhow::ensure!( + catalog.format_version == AWS_API_OPERATION_CATALOG_FORMAT_VERSION, + "generated AWS API operation catalog has format version {}, expected {}", + catalog.format_version, + AWS_API_OPERATION_CATALOG_FORMAT_VERSION + ); + anyhow::ensure!(!catalog.adapters.is_empty(), "generated AWS API operation catalog contains no adapters"); + Ok(catalog.adapters.len()) +} + +#[cfg(all(test, feature = "maintenance"))] +mod aws_api_catalog_tests { + use super::*; + + #[test] + fn current_catalog_format_with_adapters_is_valid() { + let catalog = br#"{"format_version":1,"adapters":[{}]}"#; + + let adapter_count = validate_aws_api_catalog(catalog).expect("catalog should be valid"); + + assert_eq!(1, adapter_count); + } + + #[test] + fn unsupported_catalog_format_is_rejected() { + let catalog = br#"{"format_version":2,"adapters":[{}]}"#; + + let error = validate_aws_api_catalog(catalog).expect_err("unsupported format must fail"); + + assert!(error.to_string().contains("format version 2, expected 1")); + } + + #[test] + fn catalog_without_adapters_is_rejected() { + let catalog = br#"{"format_version":1,"adapters":[]}"#; + + let error = validate_aws_api_catalog(catalog).expect_err("empty adapters must fail"); + + assert!(error.to_string().contains("contains no adapters")); + } +} + #[cfg(feature = "maintenance")] pub fn generate_all(upstream_dir: &Path, generated_dir: &Path, handwritten_dir: &Path) -> anyhow::Result<()> { info!("=== Generate phase ==="); diff --git a/src/data-source/src/sync.rs b/src/data-source/src/sync.rs index bc197357..e71ad081 100644 --- a/src/data-source/src/sync.rs +++ b/src/data-source/src/sync.rs @@ -1,5 +1,5 @@ use anyhow::Context; -use data_source::{generate_all, sync_upstream}; +use data_source::{generate_all, generate_aws_api_catalog, sync_upstream}; use log::{error, info}; use std::env; use std::fs; @@ -12,6 +12,7 @@ fn main() -> anyhow::Result<()> { let args: Vec = env::args().collect(); let mut rule_source_root: Option = None; + let mut aws_cli_root: Option = None; let mut i = 1; while i < args.len() { match args[i].as_str() { @@ -23,6 +24,14 @@ fn main() -> anyhow::Result<()> { } rule_source_root = Some(args[i].clone()); } + "--aws-cli-root" => { + i += 1; + if i >= args.len() { + error!("--aws-cli-root requires a path argument"); + process::exit(1); + } + aws_cli_root = Some(args[i].clone()); + } "--help" | "-h" => { print_usage(); return Ok(()); @@ -37,6 +46,7 @@ fn main() -> anyhow::Result<()> { } let rule_source_root = rule_source_root.ok_or_else(|| anyhow::anyhow!("--cfn-lint-root is required"))?; + let aws_cli_root = aws_cli_root.ok_or_else(|| anyhow::anyhow!("--aws-cli-root is required"))?; let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let upstream_dir = manifest.join("upstream"); let generated_dir = manifest.join("generated"); @@ -48,6 +58,7 @@ fn main() -> anyhow::Result<()> { sync_upstream(&upstream_dir, &rule_source_root)?; generate_all(&upstream_dir, &generated_dir, &handwritten_dir)?; + generate_aws_api_catalog(&upstream_dir, &generated_dir, Path::new(&aws_cli_root))?; info!("Sync and generation complete"); Ok(()) @@ -61,12 +72,14 @@ fn clear_cache_directory(cache_directory: &Path) -> anyhow::Result<()> { fn print_usage() { eprintln!( - "Usage: cargo run -p data-source --features maintenance --example sync -- --cfn-lint-root + "Usage: cargo run -p data-source --features maintenance --example sync -- --cfn-lint-root --aws-cli-root -Refreshes all upstream sources, records their versions, and generates every output. +Refreshes all upstream sources, records their versions, generates every output, +and rebuilds the AWS API operation catalog. Options: --cfn-lint-root Path to cfn-lint repo (required) + --aws-cli-root Path to AWS CLI checkout (required) -h, --help Show this help" ); } From f3475307b0a1664a4845ceb6f097a9c473010b37 Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Thu, 20 Aug 2026 13:24:46 -0600 Subject: [PATCH 08/19] more fixes --- src/bindings-go/README.md | 15 +- src/bindings-go/go/cfnvalidate.go | 56 +++ src/bindings-go/go/cfnvalidate_test.go | 149 +++++++ src/bindings-go/go/types.go | 13 + src/bindings-go/tests/smoke_test.go | 175 +++++++++ src/bindings-jvm/README.md | 95 +---- .../amazon/cloudformation/validate/Api.kt | 7 + .../tests/kotlin/src/test/kotlin/SmokeTest.kt | 105 +++++ src/bindings-python/README.md | 8 +- .../cloudformation_validate/__init__.py | 7 + src/bindings-python/tests/smoke_test.py | 72 ++++ src/bindings-wasm/README.md | 49 ++- src/bindings-wasm/src/lib.rs | 25 +- src/bindings-wasm/tests/smoke.test.ts | 236 +++++++++++- src/bindings-wasm/ts/index.ts | 264 +++++++++++++ .../scripts/generate_aws_api_catalog.py | 320 ++++++++++++--- .../scripts/test_generate_aws_api_catalog.py | 364 ++++++++++++++++++ src/validation-engine/API.md | 8 +- src/validation-engine/src/aws_api.rs | 24 +- 19 files changed, 1822 insertions(+), 170 deletions(-) diff --git a/src/bindings-go/README.md b/src/bindings-go/README.md index 2f6e7828..b4b0bbf2 100644 --- a/src/bindings-go/README.md +++ b/src/bindings-go/README.md @@ -227,7 +227,7 @@ if result.Report != nil { ```go type AWSAPIRequest struct { - ServiceName string // AWS service (e.g. "s3", "DynamoDb") - case-insensitive + ServiceName string // canonical botocore service name (e.g. "s3") - ASCII case-insensitive OperationName string // operation name (e.g. "CreateBucket") - case-sensitive Parameters map[string]any // request parameters: strings, numbers, booleans, []byte, maps, slices, nil ServicePrefix string // optional signing prefix (e.g. "cloudcontrolapi") @@ -245,6 +245,13 @@ are represented as unsupported rather than rounded through `float64`. SDK-define Non-finite floats, maps with non-string keys, and unsupported types are represented as `UNSUPPORTED` rather than coerced. +The canonical `ServiceName` is authoritative; the optional `ServicePrefix` is context only and cannot override it. +`ServiceName` must be the exact canonical botocore service name, normalized only for ASCII case. The core does not +guess signing, endpoint, or punctuation aliases and never matches on substrings. Any caller, including a future AWS +SDK adapter in any language, must translate its native service identity to the canonical botocore `ServiceName` before +invoking this API. `TemplateBody` validation is restricted to CloudFormation operations that accept it, and +`TypeName`+`DesiredState` wrapping applies only to exact Cloud Control `CreateResource`. + ### AWSAPIRequestValidation ```go @@ -255,9 +262,15 @@ type AWSAPIRequestValidation struct { ResourceTypes []string // inferred CloudFormation resource types Reason string // human-readable explanation Report *StandardReport // present only when Status is VALIDATED + Template []byte // exact validated/synthesized template bytes; nil when SKIPPED } ``` +`Template` carries the exact bytes that were validated - the caller's original `TemplateBody` without reserializing, or +the synthesized JSON template for adapter-mapped requests - so consumers can display the modeled template that produced +the diagnostics. It is nil when the request was skipped. The core serializes these bytes as a JSON integer array, which +the Go decoder converts back into a `[]byte`. + ## TemplateModel Parses a template into the resolved `SemanticModel` for direct inspection - the same model the engines evaluate rules diff --git a/src/bindings-go/go/cfnvalidate.go b/src/bindings-go/go/cfnvalidate.go index 887fdf37..78a33461 100644 --- a/src/bindings-go/go/cfnvalidate.go +++ b/src/bindings-go/go/cfnvalidate.go @@ -19,6 +19,7 @@ package cfnvalidate import ( + "bytes" "encoding/json" "fmt" "math" @@ -391,6 +392,61 @@ func encodeAwsApiValue(v any, depth int) awsApiValue { } } +// UnmarshalJSON decodes an AWSAPIRequestValidation, translating the core's +// integer-array encoding of the validated template into a byte slice. +// +// The core serializes the template as a JSON array of byte-valued integers +// (serde's representation of a byte vector), which encoding/json cannot decode +// into a []byte directly - it expects a base64 string. Every other field +// decodes with the standard rules. On any failure the receiver is left +// unchanged, so a decode error never leaves a partially populated result. +func (v *AWSAPIRequestValidation) UnmarshalJSON(data []byte) error { + type withoutTemplate AWSAPIRequestValidation + var wire struct { + withoutTemplate + Template json.RawMessage `json:"template"` + } + if err := json.Unmarshal(data, &wire); err != nil { + return err + } + template, err := decodeTemplateBytes(wire.Template) + if err != nil { + return err + } + decoded := AWSAPIRequestValidation(wire.withoutTemplate) + decoded.Template = template + *v = decoded + return nil +} + +// decodeTemplateBytes converts the core's JSON integer-array template encoding +// into a byte slice. A missing or null field yields nil; an empty array yields +// a non-nil empty slice; every element must be an integer in the range 0-255. +func decodeTemplateBytes(raw json.RawMessage) ([]byte, error) { + trimmed := bytes.TrimSpace(raw) + if len(trimmed) == 0 || string(trimmed) == "null" { + return nil, nil + } + decoder := json.NewDecoder(bytes.NewReader(trimmed)) + decoder.UseNumber() + var elements []json.Number + if err := decoder.Decode(&elements); err != nil { + return nil, fmt.Errorf("cfnvalidate: template must be a JSON array of byte integers: %w", err) + } + template := make([]byte, len(elements)) + for i, element := range elements { + value, err := element.Int64() + if err != nil { + return nil, fmt.Errorf("cfnvalidate: template byte at index %d is not an integer: %s", i, element) + } + if value < 0 || value > 255 { + return nil, fmt.Errorf("cfnvalidate: template byte at index %d is out of range 0-255: %d", i, value) + } + template[i] = byte(value) + } + return template, nil +} + // SchemaValidator validates resources against the compiled CloudFormation // provider schemas. type SchemaValidator struct { diff --git a/src/bindings-go/go/cfnvalidate_test.go b/src/bindings-go/go/cfnvalidate_test.go index 5feb33f7..ef2d6db8 100644 --- a/src/bindings-go/go/cfnvalidate_test.go +++ b/src/bindings-go/go/cfnvalidate_test.go @@ -2,6 +2,7 @@ package cfnvalidate import ( "encoding/json" + "strconv" "strings" "testing" "time" @@ -74,3 +75,151 @@ func encodedAWSAPIParameter(t *testing.T, value any) map[string]any { } return encoded } + +// templateIntegerArray renders a string's bytes as the JSON integer array the +// core emits for the validated template field. +func templateIntegerArray(text string) string { + elements := make([]string, len(text)) + for i := 0; i < len(text); i++ { + elements[i] = strconv.Itoa(int(text[i])) + } + return "[" + strings.Join(elements, ",") + "]" +} + +func unmarshalValidation(t *testing.T, blob string) AWSAPIRequestValidation { + t.Helper() + var validation AWSAPIRequestValidation + if err := json.Unmarshal([]byte(blob), &validation); err != nil { + t.Fatalf("unmarshalling AWSAPIRequestValidation failed: %v", err) + } + return validation +} + +func TestUnmarshalValidationDecodesTemplateIntegerArrayToBytes(t *testing.T) { + const template = `{"Resources":{"Resource":{"Type":"AWS::S3::Bucket"}}}` + blob := `{"operationKind":"CLOUD_FORMATION_CREATE","status":"VALIDATED","resourceTypes":[],"reason":"synthesized","template":` + + templateIntegerArray(template) + `}` + + validation := unmarshalValidation(t, blob) + + if got := string(validation.Template); got != template { + t.Errorf("Template = %q, want %q", got, template) + } +} + +func TestUnmarshalValidationTreatsMissingTemplateAsNil(t *testing.T) { + validation := unmarshalValidation(t, `{"operationKind":"READ_ONLY","status":"SKIPPED","resourceTypes":[],"reason":"read-only calls do not need validation"}`) + + if validation.Template != nil { + t.Errorf("Template = %v, want nil for an absent field", validation.Template) + } +} + +func TestUnmarshalValidationTreatsNullTemplateAsNil(t *testing.T) { + validation := unmarshalValidation(t, `{"operationKind":"READ_ONLY","status":"SKIPPED","resourceTypes":[],"reason":"skipped","template":null}`) + + if validation.Template != nil { + t.Errorf("Template = %v, want nil for a JSON null", validation.Template) + } +} + +func TestUnmarshalValidationKeepsEmptyTemplateNonNil(t *testing.T) { + validation := unmarshalValidation(t, `{"operationKind":"CLOUD_FORMATION_CREATE","status":"VALIDATED","resourceTypes":[],"reason":"synthesized","template":[]}`) + + if validation.Template == nil { + t.Fatal("Template = nil, want a non-nil empty slice for an empty array") + } + if len(validation.Template) != 0 { + t.Errorf("len(Template) = %d, want 0", len(validation.Template)) + } +} + +func TestUnmarshalValidationRejectsMalformedTemplateBytes(t *testing.T) { + cases := map[string]string{ + "non-integer fraction": `[123,2.5]`, + "non-numeric element": `[123,"x"]`, + "negative byte": `[-1]`, + "byte above 255": `[256]`, + "not an array": `"SGVsbG8="`, + } + for name, template := range cases { + t.Run(name, func(t *testing.T) { + blob := `{"operationKind":"CLOUD_FORMATION_CREATE","status":"VALIDATED","resourceTypes":[],"reason":"synthesized","template":` + template + `}` + var validation AWSAPIRequestValidation + if err := json.Unmarshal([]byte(blob), &validation); err == nil { + t.Fatalf("expected an error for template %s, got Template = %v", template, validation.Template) + } + }) + } +} + +func TestUnmarshalValidationReportsOffendingByteInError(t *testing.T) { + blob := `{"operationKind":"CLOUD_FORMATION_CREATE","status":"VALIDATED","resourceTypes":[],"reason":"synthesized","template":[10,256]}` + + err := json.Unmarshal([]byte(blob), &AWSAPIRequestValidation{}) + if err == nil { + t.Fatal("expected an out-of-range error") + } + if !strings.Contains(err.Error(), "index 1") || !strings.Contains(err.Error(), "256") { + t.Errorf("error must name the offending index and value, got: %v", err) + } +} + +func TestUnmarshalValidationLeavesReceiverUnchangedOnError(t *testing.T) { + existing := AWSAPIRequestValidation{ + OperationKind: AWSAPIOperationKindReadOnly, + Status: AWSAPIRequestValidationStatusSkipped, + Reason: "unchanged", + Template: []byte("original"), + } + validation := existing + + blob := `{"operationKind":"CLOUD_FORMATION_CREATE","status":"VALIDATED","resourceTypes":[],"reason":"synthesized","template":[999]}` + if err := json.Unmarshal([]byte(blob), &validation); err == nil { + t.Fatal("expected the out-of-range template to fail unmarshalling") + } + if validation.Status != existing.Status || validation.Reason != existing.Reason || + string(validation.Template) != string(existing.Template) { + t.Errorf("receiver was mutated on failure: got %+v, want %+v", validation, existing) + } +} + +func TestUnmarshalValidationDecodesEveryFieldAlongsideTemplate(t *testing.T) { + const template = `{"Resources":{"Resource":{"Type":"AWS::S3::Bucket","Properties":{"BucketName":"synthetic"}}}}` + blob := `{ + "operationKind":"CLOUD_FORMATION_CREATE", + "status":"VALIDATED", + "templateSource":"SYNTHESIZED_CREATE", + "resourceTypes":["AWS::S3::Bucket"], + "reason":"synthesized one unambiguous CloudFormation resource", + "report":{"filePath":"aws-api://s3/CreateBucket","status":"OK","version":"0.0.0","diagnostics":[]}, + "template":` + templateIntegerArray(template) + ` + }` + + validation := unmarshalValidation(t, blob) + + if validation.OperationKind != AWSAPIOperationKindCloudFormationCreate { + t.Errorf("OperationKind = %q, want CLOUD_FORMATION_CREATE", validation.OperationKind) + } + if validation.Status != AWSAPIRequestValidationStatusValidated { + t.Errorf("Status = %q, want VALIDATED", validation.Status) + } + if validation.TemplateSource == nil || *validation.TemplateSource != AWSAPITemplateSourceSynthesizedCreate { + t.Errorf("TemplateSource = %v, want SYNTHESIZED_CREATE", validation.TemplateSource) + } + if len(validation.ResourceTypes) != 1 || validation.ResourceTypes[0] != "AWS::S3::Bucket" { + t.Errorf("ResourceTypes = %v, want [AWS::S3::Bucket]", validation.ResourceTypes) + } + if validation.Reason != "synthesized one unambiguous CloudFormation resource" { + t.Errorf("Reason = %q", validation.Reason) + } + if validation.Report == nil { + t.Fatal("Report = nil, want the nested report to decode") + } + if validation.Report.FilePath != "aws-api://s3/CreateBucket" { + t.Errorf("Report.FilePath = %q, want aws-api://s3/CreateBucket", validation.Report.FilePath) + } + if got := string(validation.Template); got != template { + t.Errorf("Template = %q, want %q", got, template) + } +} diff --git a/src/bindings-go/go/types.go b/src/bindings-go/go/types.go index 9037fc7d..add48751 100644 --- a/src/bindings-go/go/types.go +++ b/src/bindings-go/go/types.go @@ -303,6 +303,13 @@ type ValidateConfig struct { // AWSAPIRequest holds an AWS API service call for offline CloudFormation // validation. ServiceName and OperationName identify the API; Parameters carry // the request values (maps, strings, numbers, booleans, byte slices, etc.). +// +// ServiceName is the canonical botocore service name (for example "s3" or +// "cloudformation") and is the authoritative mapping identity, normalized only +// for ASCII case - never a signing name, ARN prefix, or endpoint alias. A +// future AWS SDK adapter, in any language, must translate its native service +// identity to the canonical botocore ServiceName before calling; the core does +// not guess aliases. type AWSAPIRequest struct { ServiceName string `json:"serviceName"` OperationName string `json:"operationName"` @@ -352,4 +359,10 @@ type AWSAPIRequestValidation struct { ResourceTypes []string `json:"resourceTypes"` Reason string `json:"reason"` Report *StandardReport `json:"report,omitempty"` + // Template is the exact template bytes that were validated: the caller's + // original TemplateBody without reserializing, or the synthesized JSON + // template for adapter-mapped requests. It is nil when the request was + // skipped. The core serializes these bytes as a JSON integer array, which + // UnmarshalJSON decodes back into a byte slice. + Template []byte `json:"template,omitempty"` } diff --git a/src/bindings-go/tests/smoke_test.go b/src/bindings-go/tests/smoke_test.go index bba97c13..7136a1c3 100644 --- a/src/bindings-go/tests/smoke_test.go +++ b/src/bindings-go/tests/smoke_test.go @@ -7,6 +7,7 @@ package cfnvalidate_test import ( + "bytes" "encoding/json" "fmt" "os" @@ -480,3 +481,177 @@ func TestErrorsSurfaceAsGoErrors(t *testing.T) { t.Error("invalid custom rule must fail engine construction") } } + +func synthesizedBucketName(t *testing.T, template []byte) string { + t.Helper() + if template == nil { + t.Fatal("a validated request must carry the synthesized template bytes") + } + var document struct { + Resources struct { + Resource struct { + Type string `json:"Type"` + Properties struct { + BucketName string `json:"BucketName"` + } `json:"Properties"` + } `json:"Resource"` + } `json:"Resources"` + } + if err := json.Unmarshal(template, &document); err != nil { + t.Fatalf("decoding synthesized template JSON: %v", err) + } + if document.Resources.Resource.Type != "AWS::S3::Bucket" { + t.Errorf("synthesized resource type = %q, want AWS::S3::Bucket", document.Resources.Resource.Type) + } + return document.Resources.Resource.Properties.BucketName +} + +func TestValidateAWSAPIRequestSynthesizesS3CreateBucketOnBothEngines(t *testing.T) { + const bucketName = "synthetic-bucket" + request := cfnvalidate.AWSAPIRequest{ + ServiceName: "s3", + OperationName: "CreateBucket", + Parameters: map[string]any{"Bucket": bucketName}, + } + + perEngine := map[string]*cfnvalidate.AWSAPIRequestValidation{} + for name, engine := range bothEngines(t) { + validation, err := engine.ValidateAWSAPIRequest(request, nil) + if err != nil { + t.Fatalf("%s: ValidateAWSAPIRequest failed: %v", name, err) + } + if validation.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { + t.Errorf("%s: status = %s, want VALIDATED", name, validation.Status) + } + if validation.OperationKind != cfnvalidate.AWSAPIOperationKindCloudFormationCreate { + t.Errorf("%s: operationKind = %s, want CLOUD_FORMATION_CREATE", name, validation.OperationKind) + } + if len(validation.ResourceTypes) != 1 || validation.ResourceTypes[0] != "AWS::S3::Bucket" { + t.Errorf("%s: resourceTypes = %v, want [AWS::S3::Bucket]", name, validation.ResourceTypes) + } + if validation.TemplateSource == nil || *validation.TemplateSource != cfnvalidate.AWSAPITemplateSourceSynthesizedCreate { + t.Errorf("%s: templateSource = %v, want SYNTHESIZED_CREATE", name, validation.TemplateSource) + } + if validation.Report == nil { + t.Fatalf("%s: report must be present for a validated request", name) + } + if bucket := synthesizedBucketName(t, validation.Template); bucket != bucketName { + t.Errorf("%s: synthesized BucketName = %q, want %q", name, bucket, bucketName) + } + perEngine[name] = validation + } + + // Rego and CEL must model the request identically; performance timings are + // intentionally excluded from the comparison. + rego, cel := perEngine["rego"], perEngine["cel"] + if !bytes.Equal(rego.Template, cel.Template) { + t.Errorf("engines synthesized different templates:\nrego: %s\ncel: %s", rego.Template, cel.Template) + } + if !equalStrings(diagnosticKeys(rego.Report), diagnosticKeys(cel.Report)) { + t.Errorf("engines disagree on diagnostics:\nrego: %v\ncel: %v", diagnosticKeys(rego.Report), diagnosticKeys(cel.Report)) + } +} + +func TestValidateAWSAPIRequestPreservesExactTemplateBodyBytes(t *testing.T) { + // Distinctive whitespace and key order that a reserialization would not + // reproduce, so an exact match proves the original bytes are returned. + templateBody := []byte("{\n \"Resources\": {\n \"Bucket\": { \"Type\": \"AWS::S3::Bucket\" }\n }\n}") + request := cfnvalidate.AWSAPIRequest{ + ServiceName: "cloudformation", + OperationName: "ValidateTemplate", + Parameters: map[string]any{"TemplateBody": templateBody}, + } + + for name, engine := range bothEngines(t) { + validation, err := engine.ValidateAWSAPIRequest(request, nil) + if err != nil { + t.Fatalf("%s: ValidateAWSAPIRequest failed: %v", name, err) + } + if validation.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { + t.Errorf("%s: status = %s, want VALIDATED", name, validation.Status) + } + if validation.TemplateSource == nil || *validation.TemplateSource != cfnvalidate.AWSAPITemplateSourceTemplateBody { + t.Errorf("%s: templateSource = %v, want TEMPLATE_BODY", name, validation.TemplateSource) + } + if !bytes.Equal(validation.Template, templateBody) { + t.Errorf("%s: returned template = %q, want exact request bytes %q", name, validation.Template, templateBody) + } + } +} + +func TestValidateAWSAPIRequestConservativelySkipsNestedDynamoDbFields(t *testing.T) { + request := cfnvalidate.AWSAPIRequest{ + ServiceName: "dynamodb", + OperationName: "CreateTable", + Parameters: map[string]any{ + "TableName": "Synthetic", + "KeySchema": []any{map[string]any{"AttributeName": "id", "KeyType": "HASH"}}, + "AttributeDefinitions": []any{map[string]any{"AttributeName": "id", "AttributeType": "S"}}, + "BillingMode": "PAY_PER_REQUEST", + }, + } + + for name, engine := range bothEngines(t) { + validation, err := engine.ValidateAWSAPIRequest(request, nil) + if err != nil { + t.Fatalf("%s: ValidateAWSAPIRequest failed: %v", name, err) + } + if validation.Status != cfnvalidate.AWSAPIRequestValidationStatusSkipped { + t.Errorf("%s: status = %s, want SKIPPED for unrepresentable nested fields", name, validation.Status) + } + if validation.Report != nil { + t.Errorf("%s: a skipped request must have no report", name) + } + if validation.Template != nil { + t.Errorf("%s: a skipped request must have a nil template, got %q", name, validation.Template) + } + if len(validation.ResourceTypes) != 1 || validation.ResourceTypes[0] != "AWS::DynamoDB::Table" { + t.Errorf("%s: resourceTypes = %v, want the type still identified as [AWS::DynamoDB::Table]", name, validation.ResourceTypes) + } + if !strings.Contains(validation.Reason, "has no mapping") { + t.Errorf("%s: reason must explain the unmapped nested parameter, got %q", name, validation.Reason) + } + } +} + +func TestValidateAWSAPIRequestDoesNotGuessNoncanonicalServiceAlias(t *testing.T) { + // CloudWatch's canonical botocore service name is "cloudwatch"; "monitoring" + // is its signing name. The core must resolve the operation under the + // canonical name but never guess the signing alias. + canonical := cfnvalidate.AWSAPIRequest{ + ServiceName: "cloudwatch", + OperationName: "PutMetricAlarm", + Parameters: map[string]any{"AlarmName": "synthetic"}, + } + alias := cfnvalidate.AWSAPIRequest{ + ServiceName: "monitoring", + OperationName: "PutMetricAlarm", + Parameters: map[string]any{"AlarmName": "synthetic"}, + } + + for name, engine := range bothEngines(t) { + canonicalValidation, err := engine.ValidateAWSAPIRequest(canonical, nil) + if err != nil { + t.Fatalf("%s: canonical ValidateAWSAPIRequest failed: %v", name, err) + } + if !containsString(canonicalValidation.ResourceTypes, "AWS::CloudWatch::Alarm") { + t.Errorf("%s: canonical cloudwatch:PutMetricAlarm must identify AWS::CloudWatch::Alarm, got %v", + name, canonicalValidation.ResourceTypes) + } + + aliasValidation, err := engine.ValidateAWSAPIRequest(alias, nil) + if err != nil { + t.Fatalf("%s: alias ValidateAWSAPIRequest failed: %v", name, err) + } + if aliasValidation.Status != cfnvalidate.AWSAPIRequestValidationStatusSkipped { + t.Errorf("%s: signing alias must not classify as a CloudFormation operation, status = %s", name, aliasValidation.Status) + } + if containsString(aliasValidation.ResourceTypes, "AWS::CloudWatch::Alarm") { + t.Errorf("%s: signing alias 'monitoring' must not resolve to AWS::CloudWatch::Alarm, got %v", + name, aliasValidation.ResourceTypes) + } + if aliasValidation.Template != nil { + t.Errorf("%s: an unresolved alias must not synthesize a template", name) + } + } +} diff --git a/src/bindings-jvm/README.md b/src/bindings-jvm/README.md index 0623d19f..c243ce4e 100644 --- a/src/bindings-jvm/README.md +++ b/src/bindings-jvm/README.md @@ -111,98 +111,13 @@ provider handler metadata and verified against botocore models and the compiled verified service+operation pairs produce inferred resource types and synthesized templates. Unregistered operations are classified as `UNMAPPED_MUTATION` or `DATA_PLANE_MUTATION` with `SKIPPED` status and no inferred resource types. Cloud Control `UpdateResource` and `DeleteResource` may echo a known `TypeName` supplied by the request, but never synthesize state. -The canonical `serviceName` is authoritative; `servicePrefix` cannot override it. Case normalization accepts CLI names -(for example, `s3`) and Java SDK `SERVICE_NAME` values (for example, `S3`) without fuzzy or punctuation aliases. -`TemplateBody` validation is restricted to CloudFormation operations that accept it, and +The canonical `serviceName` is authoritative; the optional `servicePrefix` is context only and cannot override it. +`serviceName` must be the exact canonical botocore service name, normalized only for ASCII case. The core does not +guess signing, endpoint, or punctuation aliases and never matches on substrings. Any caller, including a future AWS +SDK adapter in any language, must translate its native service identity to the canonical botocore `serviceName` before +invoking this API. `TemplateBody` validation is restricted to CloudFormation operations that accept it, and `TypeName`+`DesiredState` wrapping applies only to exact Cloud Control `CreateResource`. -#### AWS SDK for Java 2.x integration - -An `ExecutionInterceptor` can validate the real `SdkRequest` in `beforeExecution`, before the SDK marshals or sends it. -Convert `SdkPojo` fields recursively so nested request models and `SdkBytes` retain their values: - -```java -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.core.SdkField; -import software.amazon.awssdk.core.SdkPojo; -import software.amazon.awssdk.core.interceptor.Context; -import software.amazon.awssdk.core.interceptor.ExecutionAttributes; -import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; -import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; -import software.amazon.cloudformation.validate.AwsApiRequest; -import software.amazon.cloudformation.validate.RegoEngine; -import software.amazon.cloudformation.validate.ValidateConfig; -import software.amazon.cloudformation.validate.engine.AwsApiRequestValidation; - -public final class CloudFormationValidationInterceptor implements ExecutionInterceptor { - private final RegoEngine engine = new RegoEngine(); - - @Override - public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes attributes) { - AwsApiRequestValidation result = engine.validateAwsApiRequest( - new AwsApiRequest( - attributes.getAttribute(SdkExecutionAttribute.SERVICE_NAME), - attributes.getAttribute(SdkExecutionAttribute.OPERATION_NAME), - sdkFields(context.request()) - ), - new ValidateConfig() - ); - - if (result.getReport() != null) { - result.getReport().getDiagnostics().forEach(diagnostic -> - System.out.println(diagnostic.getRuleId() + ": " + diagnostic.getMessage()) - ); - } else { - System.out.println(result.getStatus() + ": " + result.getReason()); - } - } - - private static Map sdkFields(SdkPojo pojo) { - Map values = new LinkedHashMap<>(); - for (SdkField field : pojo.sdkFields()) { - Object value = field.getValueOrDefault(pojo); - if (value != null) { - values.put(field.memberName(), sdkValue(value)); - } - } - return values; - } - - private static Object sdkValue(Object value) { - if (value instanceof SdkBytes) { - return ((SdkBytes) value).asByteArray(); - } - if (value instanceof SdkPojo) { - return sdkFields((SdkPojo) value); - } - if (value instanceof Map) { - Map converted = new LinkedHashMap<>(); - for (Map.Entry entry : ((Map) value).entrySet()) { - converted.put(String.valueOf(entry.getKey()), sdkValue(entry.getValue())); - } - return converted; - } - if (value instanceof Iterable) { - List converted = new ArrayList<>(); - for (Object item : (Iterable) value) { - converted.add(sdkValue(item)); - } - return converted; - } - return value; - } -} -``` - -Register the interceptor through the SDK client's `overrideConfiguration`. The engine is safe to reuse; constructing it -for every request needlessly recompiles the bundled rules. The example reports findings, but an interceptor can instead -throw after applying application-specific policy to prevent the API call. The adapter adds no AWS SDK dependency to -`cloudformation-validate` itself. - ### `EngineConfig` Passed to the constructor. All fields default to empty lists. diff --git a/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt b/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt index 0ba69f66..71fdad23 100644 --- a/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt +++ b/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt @@ -27,6 +27,13 @@ interface Engine { /** * Service, operation, and request values for CloudFormation validation. * + * [serviceName] is the canonical botocore service name (for example "s3" or + * "cloudformation") and is the authoritative mapping identity, normalized only + * for ASCII case - never a signing name, ARN prefix, or endpoint alias. A future + * AWS SDK adapter, in any language, must translate its native service identity to + * the canonical botocore [serviceName] before calling; the core does not guess + * aliases. + * * [parameters] accepts nested maps/lists, strings, numbers, booleans, nulls, * byte arrays, and Java time values. Unsupported values are marked explicitly * and conservatively omitted during request-to-template synthesis. diff --git a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt index e664eb92..93adc8f8 100644 --- a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt +++ b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt @@ -468,6 +468,111 @@ class SmokeTest { } } + // ── AWS API request validation ──────────────────────────────────────────── + + private val awsApiEngines: List> = listOf("rego" to REGO, "cel" to CEL) + + private fun diagnosticKeys(report: StandardReport): List = + report.diagnostics.map { "${it.ruleId}|${it.severity}|${it.startLine}|${it.startColumn}" }.sorted() + + @Suppress("UNCHECKED_CAST") + private fun synthesizedBucketName(template: ByteArray?): String { + assertNotNull(template, "a validated request must carry the synthesized template bytes") + val document = JsonParser(String(template!!)).parseValue() as Map + val resource = (document["Resources"] as Map)["Resource"] as Map + assertEquals("AWS::S3::Bucket", resource["Type"], "synthesized resource type") + return (resource["Properties"] as Map)["BucketName"] as String + } + + @Test + fun awsApiS3CreateBucketSynthesizesOnBothEngines() { + val request = AwsApiRequest("s3", "CreateBucket", mapOf("Bucket" to "synthetic-bucket")) + val perEngine = LinkedHashMap() + for ((name, engine) in awsApiEngines) { + val validation = engine.validateAwsApiRequest(request) + assertEquals(AwsApiRequestValidationStatus.VALIDATED, validation.status, "$name: status") + assertEquals(AwsApiOperationKind.CLOUD_FORMATION_CREATE, validation.operationKind, "$name: operation kind") + assertEquals(listOf("AWS::S3::Bucket"), validation.resourceTypes, "$name: resource types") + assertEquals(AwsApiTemplateSource.SYNTHESIZED_CREATE, validation.templateSource, "$name: template source") + assertNotNull(validation.report, "$name: report must be present for a validated request") + assertEquals("synthetic-bucket", synthesizedBucketName(validation.template), "$name: synthesized bucket name") + perEngine[name] = validation + } + // Rego/CEL parity on the modeled template and diagnostics, not timings. + assertTrue( + perEngine.getValue("rego").template.contentEquals(perEngine.getValue("cel").template), + "engines must synthesize identical templates", + ) + assertEquals( + diagnosticKeys(perEngine.getValue("rego").report!!), + diagnosticKeys(perEngine.getValue("cel").report!!), + "engines must agree on diagnostics", + ) + } + + @Test + fun awsApiValidateTemplatePreservesExactBytes() { + // Distinctive whitespace and key order a reserialization would not reproduce. + val templateBody = "{\n \"Resources\": {\n \"Bucket\": { \"Type\": \"AWS::S3::Bucket\" }\n }\n}".toByteArray() + val request = AwsApiRequest("cloudformation", "ValidateTemplate", mapOf("TemplateBody" to templateBody)) + for ((name, engine) in awsApiEngines) { + val validation = engine.validateAwsApiRequest(request) + assertEquals(AwsApiRequestValidationStatus.VALIDATED, validation.status, "$name: status") + assertEquals(AwsApiTemplateSource.TEMPLATE_BODY, validation.templateSource, "$name: template source") + assertTrue( + templateBody.contentEquals(validation.template), + "$name: TemplateBody must be preserved byte-for-byte", + ) + } + } + + @Test + fun awsApiConservativelySkipsNestedDynamoDbFields() { + val request = AwsApiRequest( + "dynamodb", + "CreateTable", + mapOf( + "TableName" to "Synthetic", + "KeySchema" to listOf(mapOf("AttributeName" to "id", "KeyType" to "HASH")), + "AttributeDefinitions" to listOf(mapOf("AttributeName" to "id", "AttributeType" to "S")), + "BillingMode" to "PAY_PER_REQUEST", + ), + ) + for ((name, engine) in awsApiEngines) { + val validation = engine.validateAwsApiRequest(request) + assertEquals(AwsApiRequestValidationStatus.SKIPPED, validation.status, "$name: status") + assertNull(validation.report, "$name: a skipped request must have no report") + assertNull(validation.template, "$name: a skipped request must have no template") + assertEquals(listOf("AWS::DynamoDB::Table"), validation.resourceTypes, "$name: resource type still identified") + assertTrue( + validation.reason.contains("has no mapping"), + "$name: reason must explain the unmapped nested field: ${validation.reason}", + ) + } + } + + @Test + fun awsApiDoesNotGuessNoncanonicalServiceAlias() { + // CloudWatch's canonical botocore name is "cloudwatch"; "monitoring" is its + // signing name. The core resolves the canonical name but never the alias. + val canonical = AwsApiRequest("cloudwatch", "PutMetricAlarm", mapOf("AlarmName" to "synthetic")) + val alias = AwsApiRequest("monitoring", "PutMetricAlarm", mapOf("AlarmName" to "synthetic")) + for ((name, engine) in awsApiEngines) { + val canonicalValidation = engine.validateAwsApiRequest(canonical) + assertTrue( + canonicalValidation.resourceTypes.contains("AWS::CloudWatch::Alarm"), + "$name: canonical cloudwatch:PutMetricAlarm must identify AWS::CloudWatch::Alarm", + ) + val aliasValidation = engine.validateAwsApiRequest(alias) + assertEquals(AwsApiRequestValidationStatus.SKIPPED, aliasValidation.status, "$name: alias status") + assertFalse( + aliasValidation.resourceTypes.contains("AWS::CloudWatch::Alarm"), + "$name: signing alias 'monitoring' must not resolve to AWS::CloudWatch::Alarm", + ) + assertNull(aliasValidation.template, "$name: an unresolved alias must not synthesize a template") + } + } + companion object { private val resourcesRoot: File = listOf( File("${System.getProperty("user.dir")}/../../resources"), diff --git a/src/bindings-python/README.md b/src/bindings-python/README.md index a565c5a9..819fa6a1 100644 --- a/src/bindings-python/README.md +++ b/src/bindings-python/README.md @@ -97,9 +97,11 @@ provider handler metadata and verified against botocore models and the compiled verified service+operation pairs produce inferred resource types and synthesized templates. Unregistered operations are classified as `UNMAPPED_MUTATION` or `DATA_PLANE_MUTATION` with `SKIPPED` status and no inferred resource types. Cloud Control `UpdateResource` and `DeleteResource` may echo a known `TypeName` supplied by the request, but never synthesize state. -The canonical `service_name` is authoritative; `service_prefix` cannot override it. Case normalization accepts CLI -names (for example, `s3`) and Java SDK casing (for example, `S3`) without fuzzy or punctuation aliases. -`TemplateBody` validation is restricted to CloudFormation operations that accept it, and +The canonical `service_name` is authoritative; the optional `service_prefix` is context only and cannot override it. +`service_name` must be the exact canonical botocore service name, normalized only for ASCII case. The core does not +guess signing, endpoint, or punctuation aliases and never matches on substrings. Any caller, including a future AWS +SDK adapter in any language, must translate its native service identity to the canonical botocore `service_name` +before invoking this API. `TemplateBody` validation is restricted to CloudFormation operations that accept it, and `TypeName`+`DesiredState` wrapping applies only to exact Cloud Control `CreateResource`. #### AWS CLI integration diff --git a/src/bindings-python/python/cloudformation_validate/__init__.py b/src/bindings-python/python/cloudformation_validate/__init__.py index e7cc29a4..45cfaf6d 100644 --- a/src/bindings-python/python/cloudformation_validate/__init__.py +++ b/src/bindings-python/python/cloudformation_validate/__init__.py @@ -224,6 +224,13 @@ def file_to_external_rule_source(path: typing.Union[str, os.PathLike]) -> Extern class AwsApiRequest: """Service, operation, and request values for CloudFormation validation. + ``service_name`` is the canonical botocore service name (for example ``"s3"`` + or ``"cloudformation"``) and is the authoritative mapping identity, normalized + only for ASCII case - never a signing name, ARN prefix, or endpoint alias. A + future AWS SDK adapter, in any language, must translate its native service + identity to the canonical botocore ``service_name`` before calling; the core + does not guess aliases. + ``parameters`` accepts the same Python values used by botocore request dictionaries, including nested mappings/sequences, ``bytes``, and ``datetime.datetime``. Values that cannot be represented are carried as an diff --git a/src/bindings-python/tests/smoke_test.py b/src/bindings-python/tests/smoke_test.py index 058a60e7..2799665c 100644 --- a/src/bindings-python/tests/smoke_test.py +++ b/src/bindings-python/tests/smoke_test.py @@ -6,6 +6,7 @@ Standard library only - no test dependencies. """ +import json import os import re import tempfile @@ -15,6 +16,10 @@ import cloudformation_validate._native as native_loader from cloudformation_validate import ( AdditionalSchemaSource, + AwsApiOperationKind, + AwsApiRequest, + AwsApiRequestValidationStatus, + AwsApiTemplateSource, CelEngine, EngineConfig, EntityType, @@ -415,5 +420,72 @@ def test_multi_combined_custom_and_guard(self): self.assertEqual(description, actual_description, rule_id) +class AwsApiRequestTest(unittest.TestCase): + def test_s3_create_bucket_synthesizes_on_both_engines(self): + request = AwsApiRequest("s3", "CreateBucket", {"Bucket": "synthetic-bucket"}) + results = {} + for engine in (REGO, CEL): + name = engine.engine_name() + validation = engine.validate_aws_api_request(request) + self.assertEqual(AwsApiRequestValidationStatus.VALIDATED, validation.status, name) + self.assertEqual(AwsApiOperationKind.CLOUD_FORMATION_CREATE, validation.operation_kind, name) + self.assertEqual(["AWS::S3::Bucket"], validation.resource_types, name) + self.assertEqual(AwsApiTemplateSource.SYNTHESIZED_CREATE, validation.template_source, name) + self.assertIsNotNone(validation.report, name) + self.assertIsNotNone(validation.template, f"{name}: a validated request must carry template bytes") + document = json.loads(validation.template) + self.assertEqual("AWS::S3::Bucket", document["Resources"]["Resource"]["Type"], name) + self.assertEqual("synthetic-bucket", document["Resources"]["Resource"]["Properties"]["BucketName"], name) + results[name] = validation + # Rego/CEL parity on the modeled template and diagnostics, not timings. + self.assertEqual(results["rego"].template, results["cel"].template) + self.assertEqual(diagnostic_keys(results["rego"].report), diagnostic_keys(results["cel"].report)) + + def test_validate_template_preserves_exact_template_body_bytes(self): + # Distinctive whitespace and key order a reserialization would not reproduce. + template_body = b'{\n "Resources": {\n "Bucket": { "Type": "AWS::S3::Bucket" }\n }\n}' + request = AwsApiRequest("cloudformation", "ValidateTemplate", {"TemplateBody": template_body}) + for engine in (REGO, CEL): + name = engine.engine_name() + validation = engine.validate_aws_api_request(request) + self.assertEqual(AwsApiRequestValidationStatus.VALIDATED, validation.status, name) + self.assertEqual(AwsApiTemplateSource.TEMPLATE_BODY, validation.template_source, name) + self.assertEqual(template_body, validation.template, f"{name}: TemplateBody must be preserved byte-for-byte") + + def test_conservatively_skips_nested_dynamodb_fields(self): + request = AwsApiRequest( + "dynamodb", + "CreateTable", + { + "TableName": "Synthetic", + "KeySchema": [{"AttributeName": "id", "KeyType": "HASH"}], + "AttributeDefinitions": [{"AttributeName": "id", "AttributeType": "S"}], + "BillingMode": "PAY_PER_REQUEST", + }, + ) + for engine in (REGO, CEL): + name = engine.engine_name() + validation = engine.validate_aws_api_request(request) + self.assertEqual(AwsApiRequestValidationStatus.SKIPPED, validation.status, name) + self.assertIsNone(validation.report, name) + self.assertIsNone(validation.template, name) + self.assertEqual(["AWS::DynamoDB::Table"], validation.resource_types, name) + self.assertIn("has no mapping", validation.reason, name) + + def test_does_not_guess_noncanonical_service_alias(self): + # CloudWatch's canonical botocore name is "cloudwatch"; "monitoring" is its + # signing name. The core resolves the canonical name but never the alias. + canonical = AwsApiRequest("cloudwatch", "PutMetricAlarm", {"AlarmName": "synthetic"}) + alias = AwsApiRequest("monitoring", "PutMetricAlarm", {"AlarmName": "synthetic"}) + for engine in (REGO, CEL): + name = engine.engine_name() + canonical_validation = engine.validate_aws_api_request(canonical) + self.assertIn("AWS::CloudWatch::Alarm", canonical_validation.resource_types, name) + alias_validation = engine.validate_aws_api_request(alias) + self.assertEqual(AwsApiRequestValidationStatus.SKIPPED, alias_validation.status, name) + self.assertNotIn("AWS::CloudWatch::Alarm", alias_validation.resource_types, name) + self.assertIsNone(alias_validation.template, name) + + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/src/bindings-wasm/README.md b/src/bindings-wasm/README.md index b579c6aa..d7a76e12 100644 --- a/src/bindings-wasm/README.md +++ b/src/bindings-wasm/README.md @@ -45,11 +45,12 @@ diagnostics for the same template and config. | Method | Returns | Description | |---------------------------------------|------------------|------------------------------------------------------------------------------------------------------------------| -| `validateStandard(template, config?)` | `StandardReport` | Validates and returns diagnostics without extended context | -| `validateDetailed(template, config?)` | `DetailedReport` | Validates and returns diagnostics with documentation URLs, rule descriptions, phase tags, and `ViolationContext` | -| `listRules()` | `RuleInfo[]` | Returns metadata for every built-in and loaded custom rule | -| `engineName()` | `string` | `"rego"` or `"cel"` | -| `free()` | `void` | Releases the engine's off-heap memory | +| `validateStandard(template, config?)` | `StandardReport` | Validates and returns diagnostics without extended context | +| `validateDetailed(template, config?)` | `DetailedReport` | Validates and returns diagnostics with documentation URLs, rule descriptions, phase tags, and `ViolationContext` | +| `validateAwsApiRequest(request, config?)` | `AwsApiRequestValidation` | Classifies, models, and validates one AWS API request entirely offline | +| `listRules()` | `RuleInfo[]` | Returns metadata for every built-in and loaded custom rule | +| `engineName()` | `string` | `"rego"` or `"cel"` | +| `free()` | `void` | Releases the engine's off-heap memory | ### `EngineConfig` @@ -107,6 +108,44 @@ const engine = new CelEngine({ }); ``` +## AWS API request validation + +`validateAwsApiRequest` classifies and validates an AWS API request against the same bundled CloudFormation schemas and +rules, without credentials or network access: + +```typescript +import { AwsApiRequest, RegoEngine } from "@aws/cloudformation-validate"; + +const engine = new RegoEngine(); +try { + const request = new AwsApiRequest("s3", "CreateBucket", { + Bucket: "my-bucket", + }); + const validation = engine.validateAwsApiRequest(request); + console.log(validation.status, validation.resourceTypes); + if (validation.template !== null) { + console.log(Buffer.from(validation.template).toString("utf8")); + } +} finally { + engine.free(); +} +``` + +`serviceName` is the exact canonical botocore service name (for example, `"s3"`, `"cloudwatch"`, or +`"cloudformation"`) and is normalized for ASCII case only. `servicePrefix` is optional context and never overrides +`serviceName`. The core does not guess endpoint prefixes, signing names, punctuation variants, or other aliases. A +future SDK adapter in any language must translate its native SDK identity to this canonical name before calling the +API. + +Request parameters accept JavaScript strings, booleans, finite numbers, 64-bit `bigint` values, `null`, `Date`, nested +arrays and plain objects, and `Uint8Array` (including Node.js `Buffer`) for byte strings. Unsupported values are marked +explicitly rather than coerced. Synthesis is all-or-nothing: if any supplied resource-state field cannot be represented +or has no proven mapping, the result is `SKIPPED` with no report or template. + +`AwsApiRequestValidation.template` is a `Uint8Array | null`. For CloudFormation `TemplateBody`, it contains the exact +caller-provided bytes; synthesized requests contain the generated JSON template bytes. A `VALIDATED` result includes a +standard report, while a `SKIPPED` result explains why validation was not attempted. + ## ValidateConfig Controls filtering, severity, parameter overrides, and behavior. All fields optional - omitting the config or passing diff --git a/src/bindings-wasm/src/lib.rs b/src/bindings-wasm/src/lib.rs index da15bce6..407bdb57 100644 --- a/src/bindings-wasm/src/lib.rs +++ b/src/bindings-wasm/src/lib.rs @@ -5,7 +5,10 @@ use rules::{FilterConfig, RuleFilterConfig, Severity}; use schema_validator::{SchemaValidator, SchemaValidatorConfig}; use serde::Deserialize; use template_model::{PseudoParameterOverrides, SemanticModel}; -use validation_engine::{EngineConfig, ValidationEngine, catch_panics, validate_bytes_with_path}; +use validation_engine::{ + AwsApiRequestContext, EngineConfig, ValidationEngine, catch_panics, validate_aws_api_request, + validate_bytes_with_path, +}; use wasm_bindgen::prelude::*; const SERIALIZER: serde_wasm_bindgen::Serializer = serde_wasm_bindgen::Serializer::json_compatible(); @@ -178,6 +181,26 @@ macro_rules! wasm_engine { ) } + #[wasm_bindgen(js_name = "validateAwsApiRequest")] + pub fn validate_aws_api_request( + &self, + request: JsValue, + options: ValidateConfig, + ) -> Result { + catch_panics( + || { + let request: AwsApiRequestContext = serde_wasm_bindgen::from_value(request) + .map_err(|error| JsValue::from_str(&format!("invalid AWS API request: {error}")))?; + let config = build_core_config(options, DetailLevel::Standard); + let validation = + validate_aws_api_request(&self.engine, &self.schema_validator, &request, config) + .map_err(to_js_err)?; + to_js(&validation) + }, + wasm_panic_err, + ) + } + #[wasm_bindgen(js_name = "listRules")] pub fn list_rules(&self) -> Result { catch_panics(|| to_js(&self.engine.list_rules()), wasm_panic_err) diff --git a/src/bindings-wasm/tests/smoke.test.ts b/src/bindings-wasm/tests/smoke.test.ts index 56d38396..016808fb 100644 --- a/src/bindings-wasm/tests/smoke.test.ts +++ b/src/bindings-wasm/tests/smoke.test.ts @@ -6,6 +6,7 @@ import * as path from 'path'; const { RegoEngine, CelEngine, + AwsApiRequest, SchemaValidator, SchemaFile, TemplateModel, @@ -233,6 +234,237 @@ describe('invalid input', () => { }); }); +// ── AWS API request validation ────────────────────────────────────────────── + +describe('AWS API request validation', () => { + const engines = [ + ['rego', REGO], + ['cel', CEL], + ] as const; + + function diagnosticKeys(report: any): string[] { + return (report?.diagnostics ?? []).map( + (diagnostic: any) => + `${diagnostic.ruleId}|${diagnostic.entity?.logicalId ?? ''}|${diagnostic.propertyPath ?? ''}`, + ); + } + + it('synthesizes canonical S3 CreateBucket state on both engines', () => { + const request = new AwsApiRequest('s3', 'CreateBucket', { Bucket: 'synthetic-bucket' }); + const validations: Record = {}; + + for (const [name, engine] of engines) { + const validation = engine.validateAwsApiRequest(request); + expect(validation.status, name).toBe('VALIDATED'); + expect(validation.operationKind, name).toBe('CLOUD_FORMATION_CREATE'); + expect(validation.resourceTypes, name).toEqual(['AWS::S3::Bucket']); + expect(validation.templateSource, name).toBe('SYNTHESIZED_CREATE'); + expect(validation.report, name).not.toBeNull(); + expect(validation.template, name).toBeInstanceOf(Uint8Array); + const document = JSON.parse(Buffer.from(validation.template).toString('utf8')); + expect(document.Resources.Resource.Type, name).toBe('AWS::S3::Bucket'); + expect(document.Resources.Resource.Properties.BucketName, name).toBe('synthetic-bucket'); + validations[name] = validation; + } + + expect(Array.from(validations.rego.template)).toEqual(Array.from(validations.cel.template)); + expect(diagnosticKeys(validations.rego.report)).toEqual(diagnosticKeys(validations.cel.report)); + }); + + it('preserves CloudFormation TemplateBody bytes exactly on both engines', () => { + const templateBody = Buffer.from( + '{\n "Resources": {\n "Bucket": { "Type": "AWS::S3::Bucket" }\n }\n}', + ); + const request = new AwsApiRequest('cloudformation', 'ValidateTemplate', { + TemplateBody: templateBody, + }); + + for (const [name, engine] of engines) { + const validation = engine.validateAwsApiRequest(request); + expect(validation.status, name).toBe('VALIDATED'); + expect(validation.templateSource, name).toBe('TEMPLATE_BODY'); + expect(validation.template, name).toBeInstanceOf(Uint8Array); + expect(Buffer.from(validation.template), name).toEqual(templateBody); + } + }); + + it('conservatively skips unmapped nested DynamoDB state on both engines', () => { + const request = new AwsApiRequest('dynamodb', 'CreateTable', { + TableName: 'Synthetic', + KeySchema: [{ AttributeName: 'id', KeyType: 'HASH' }], + AttributeDefinitions: [{ AttributeName: 'id', AttributeType: 'S' }], + BillingMode: 'PAY_PER_REQUEST', + }); + + for (const [name, engine] of engines) { + const validation = engine.validateAwsApiRequest(request); + expect(validation.status, name).toBe('SKIPPED'); + expect(validation.report, name).toBeNull(); + expect(validation.template, name).toBeNull(); + expect(validation.resourceTypes, name).toEqual(['AWS::DynamoDB::Table']); + expect(validation.reason, name).toContain('has no mapping'); + } + }); + + it('never guesses the noncanonical CloudWatch signing alias', () => { + const canonical = new AwsApiRequest('cloudwatch', 'PutMetricAlarm', { + AlarmName: 'synthetic', + }); + const alias = new AwsApiRequest('monitoring', 'PutMetricAlarm', { + AlarmName: 'synthetic', + }); + + for (const [name, engine] of engines) { + const canonicalValidation = engine.validateAwsApiRequest(canonical); + expect(canonicalValidation.resourceTypes, name).toContain('AWS::CloudWatch::Alarm'); + const aliasValidation = engine.validateAwsApiRequest(alias); + expect(aliasValidation.status, name).toBe('SKIPPED'); + expect(aliasValidation.resourceTypes, name).not.toContain('AWS::CloudWatch::Alarm'); + expect(aliasValidation.template, name).toBeNull(); + } + }); + + it('preserves signed and unsigned 64-bit bigint values across the WASM boundary', () => { + const request = new AwsApiRequest('lambda', 'CreateFunction', { + MemorySize: 18446744073709551615n, + Timeout: -9223372036854775808n, + }); + + for (const [name, engine] of engines) { + const validation = engine.validateAwsApiRequest(request); + expect(validation.status, name).toBe('VALIDATED'); + expect(validation.template, name).toBeInstanceOf(Uint8Array); + const template = Buffer.from(validation.template).toString('utf8'); + expect(template, name).toContain('"MemorySize":18446744073709551615'); + expect(template, name).toContain('"Timeout":-9223372036854775808'); + } + }); + + it('marks unsupported request values conservatively instead of coercing them', () => { + const request = new AwsApiRequest('s3', 'CreateBucket', { + Bucket: Symbol('not-a-bucket-name'), + }); + + for (const [name, engine] of engines) { + const validation = engine.validateAwsApiRequest(request); + expect(validation.status, name).toBe('SKIPPED'); + expect(validation.report, name).toBeNull(); + expect(validation.template, name).toBeNull(); + } + }); + + it('does not invoke object accessors', () => { + let accessorInvoked = false; + const state: Record = Object.create(null); + Object.defineProperty(state, 'accessor', { + enumerable: true, + get() { + accessorInvoked = true; + throw new Error('request accessors must not run'); + }, + }); + const request = new AwsApiRequest('s3', 'CreateBucket', { Bucket: state }); + + for (const [name, engine] of engines) { + const validation = engine.validateAwsApiRequest(request); + expect(validation.status, name).toBe('SKIPPED'); + expect(validation.template, name).toBeNull(); + } + expect(accessorInvoked).toBe(false); + }); + + it('does not invoke indexed array accessors', () => { + let accessorInvoked = false; + const state: unknown[] = []; + Object.defineProperty(state, '0', { + enumerable: true, + get() { + accessorInvoked = true; + throw new Error('request accessors must not run'); + }, + }); + const request = new AwsApiRequest('s3', 'CreateBucket', { Bucket: state }); + + for (const [name, engine] of engines) { + const validation = engine.validateAwsApiRequest(request); + expect(validation.status, name).toBe('SKIPPED'); + expect(validation.template, name).toBeNull(); + } + expect(accessorInvoked).toBe(false); + }); + + it('skips cyclic request state conservatively', () => { + const state: Record = Object.create(null); + state.cycle = state; + const request = new AwsApiRequest('s3', 'CreateBucket', { Bucket: state }); + + for (const [name, engine] of engines) { + const validation = engine.validateAwsApiRequest(request); + expect(validation.status, name).toBe('SKIPPED'); + expect(validation.template, name).toBeNull(); + } + }); + + it('bypasses caller-overridden Date and Uint8Array methods', () => { + let overrideInvoked = false; + const date = new Date('2024-01-02T03:04:05.000Z'); + Object.defineProperty(date, 'getTime', { + get() { + overrideInvoked = true; + throw new Error('request overrides must not run'); + }, + }); + Object.defineProperty(date, 'toISOString', { + get() { + overrideInvoked = true; + throw new Error('request overrides must not run'); + }, + }); + + const expectedTemplateBody = Buffer.from('{"Resources":{"Bucket":{"Type":"AWS::S3::Bucket"}}}'); + const templateBody = new Uint8Array(expectedTemplateBody); + Object.defineProperty(templateBody, Symbol.iterator, { + get() { + overrideInvoked = true; + throw new Error('request overrides must not run'); + }, + }); + Object.defineProperty(templateBody, 'forEach', { + get() { + overrideInvoked = true; + throw new Error('request overrides must not run'); + }, + }); + + const dateRequest = new AwsApiRequest('s3', 'CreateBucket', { Bucket: date }); + const bytesRequest = new AwsApiRequest('cloudformation', 'ValidateTemplate', { + TemplateBody: templateBody, + }); + for (const [name, engine] of engines) { + const dateValidation = engine.validateAwsApiRequest(dateRequest); + expect(dateValidation.status, name).toBe('VALIDATED'); + expect(Buffer.from(dateValidation.template).toString('utf8'), name).toContain('2024-01-02T03:04:05.000Z'); + + const bytesValidation = engine.validateAwsApiRequest(bytesRequest); + expect(bytesValidation.status, name).toBe('VALIDATED'); + expect(Buffer.from(bytesValidation.template), name).toEqual(expectedTemplateBody); + } + expect(overrideInvoked).toBe(false); + }); + + it('rejects invalid top-level parameter dictionaries without dropping keys', () => { + expect(() => new AwsApiRequest('s3', 'CreateBucket', [] as unknown as Record)).toThrow( + 'parameters must be a plain object', + ); + + const symbolParameters: Record = Object.create(null); + symbolParameters[Symbol('Bucket')] = 'synthetic-bucket'; + expect(() => new AwsApiRequest('s3', 'CreateBucket', symbolParameters as Record)).toThrow( + 'request parameter names must be strings', + ); + }); +}); + // ── Additional schema overlays ────────────────────────────────────────────── describe('additional schemas', () => { @@ -256,7 +488,9 @@ describe('additional schemas', () => { `${name} baseline must report the unpublished property`, ).toBe(true); - const engine = new EngineType({ schemaValidatorConfig: { additionalSchemas: [new SchemaFile(schemaPath)] } }); + const engine = new EngineType({ + schemaValidatorConfig: { additionalSchemas: [new SchemaFile(schemaPath)] }, + }); const report = engine.validateStandard(template); expect( report.diagnostics.some((diagnostic: any) => diagnostic.ruleId === 'F3002'), diff --git a/src/bindings-wasm/ts/index.ts b/src/bindings-wasm/ts/index.ts index a82b3c0e..b61c3e17 100644 --- a/src/bindings-wasm/ts/index.ts +++ b/src/bindings-wasm/ts/index.ts @@ -76,9 +76,263 @@ export type { } from '../dist/bindings_wasm'; export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }; + +export type AwsApiOperationKind = + | 'READ_ONLY' + | 'CLOUD_FORMATION_CREATE' + | 'CLOUD_FORMATION_UPDATE' + | 'CLOUD_FORMATION_DELETE' + | 'DATA_PLANE_MUTATION' + | 'UNMAPPED_MUTATION'; +export type AwsApiRequestValidationStatus = 'VALIDATED' | 'SKIPPED'; +export type AwsApiTemplateSource = + 'TEMPLATE_BODY' | 'CLOUD_CONTROL_DESIRED_STATE' | 'SYNTHESIZED_CREATE' | 'SYNTHESIZED_UPDATE'; + +export interface AwsApiRequestOptions { + servicePrefix?: string; + httpMethod?: string; + isReadOnly?: boolean; +} + +/** + * Service, operation, and input values for one AWS API request. + * + * `serviceName` is the canonical botocore service name and is normalized only + * for ASCII case. Callers adapting an SDK request must translate its native + * service identity before constructing this request; endpoint and signing-name + * aliases are never guessed by the validation core. + */ +export class AwsApiRequest { + public readonly parameters: Record; + public readonly servicePrefix?: string; + public readonly httpMethod?: string; + public readonly isReadOnly?: boolean; + + constructor( + public readonly serviceName: string, + public readonly operationName: string, + parameters: Record, + options: AwsApiRequestOptions = {}, + ) { + if (!isPlainRecord(parameters)) { + throw new TypeError('parameters must be a plain object with string keys'); + } + const copiedParameters = Object.create(null) as Record; + for (const key of Reflect.ownKeys(parameters)) { + if (typeof key !== 'string') { + throw new TypeError('request parameter names must be strings'); + } + const descriptor = Object.getOwnPropertyDescriptor(parameters, key); + if (descriptor === undefined || !('value' in descriptor)) { + throw new TypeError(`request parameter ${JSON.stringify(key)} must be a value property`); + } + copiedParameters[key] = descriptor.value; + } + this.parameters = copiedParameters; + this.servicePrefix = options.servicePrefix; + this.httpMethod = options.httpMethod; + this.isReadOnly = options.isReadOnly; + } +} + +export interface AwsApiRequestValidation { + operationKind: AwsApiOperationKind; + status: AwsApiRequestValidationStatus; + templateSource: AwsApiTemplateSource | null; + resourceTypes: string[]; + reason: string; + report: StandardReport | null; + template: Uint8Array | null; +} + +type WireAwsApiValue = + | { type: 'NULL' } + | { type: 'BOOLEAN'; value: boolean } + | { type: 'INTEGER'; value: number | bigint } + | { type: 'UNSIGNED_INTEGER'; value: bigint } + | { type: 'NUMBER'; value: number } + | { type: 'STRING'; value: string } + | { type: 'BYTES'; value: number[] } + | { type: 'ARRAY'; items: WireAwsApiValue[] } + | { type: 'OBJECT'; entries: Record } + | { type: 'UNSUPPORTED'; type_name: string }; + +interface WireAwsApiRequest { + serviceName: string; + operationName: string; + parameters: Record; + servicePrefix?: string; + httpMethod?: string; + isReadOnly?: boolean; +} + +interface WireAwsApiRequestValidation extends Omit { + template?: number[] | Uint8Array | null; +} + +const MIN_SIGNED_64 = -(1n << 63n); +const MAX_SIGNED_64 = (1n << 63n) - 1n; +const MAX_UNSIGNED_64 = (1n << 64n) - 1n; +const MAX_REQUEST_VALUE_DEPTH = 64; +const DATE_GET_TIME = Date.prototype.getTime; +const DATE_TO_ISO_STRING = Date.prototype.toISOString; +const UINT8_ARRAY_FOR_EACH = Uint8Array.prototype.forEach; + +function isPlainRecord(value: unknown): value is Record { + if (value === null || typeof value !== 'object' || Array.isArray(value)) { + return false; + } + const prototype = Object.getPrototypeOf(value); + return prototype === Object.prototype || prototype === null; +} + +function unsupportedValue(typeName: string): WireAwsApiValue { + return { type: 'UNSUPPORTED', type_name: typeName }; +} + +function encodeAwsApiValue(value: unknown, depth = 0, ancestors = new Set()): WireAwsApiValue { + if (depth > MAX_REQUEST_VALUE_DEPTH) { + return unsupportedValue('recursion depth exceeded'); + } + if (value === null) { + return { type: 'NULL' }; + } + if (typeof value === 'boolean') { + return { type: 'BOOLEAN', value }; + } + if (typeof value === 'number') { + if (!Number.isFinite(value)) { + return unsupportedValue('non-finite floating-point number'); + } + if (Number.isInteger(value)) { + return Number.isSafeInteger(value) + ? { type: 'INTEGER', value } + : unsupportedValue('integer outside the JavaScript safe range'); + } + return { type: 'NUMBER', value }; + } + if (typeof value === 'bigint') { + if (value >= MIN_SIGNED_64 && value <= MAX_SIGNED_64) { + return { type: 'INTEGER', value }; + } + if (value >= 0n && value <= MAX_UNSIGNED_64) { + return { type: 'UNSIGNED_INTEGER', value }; + } + return unsupportedValue('integer outside the 64-bit request range'); + } + if (typeof value === 'string') { + return { type: 'STRING', value }; + } + if (value instanceof Uint8Array) { + const bytes: number[] = []; + try { + UINT8_ARRAY_FOR_EACH.call(value, (byte: number) => { + bytes.push(byte); + }); + } catch { + return unsupportedValue('invalid Uint8Array'); + } + return { type: 'BYTES', value: bytes }; + } + if (value instanceof Date) { + try { + const timestamp = DATE_GET_TIME.call(value); + return Number.isFinite(timestamp) + ? { type: 'STRING', value: DATE_TO_ISO_STRING.call(value) } + : unsupportedValue('invalid Date'); + } catch { + return unsupportedValue('invalid Date'); + } + } + if (Array.isArray(value)) { + if (ancestors.has(value)) { + return unsupportedValue('cyclic array'); + } + ancestors.add(value); + try { + const lengthDescriptor = Object.getOwnPropertyDescriptor(value, 'length'); + if ( + lengthDescriptor === undefined || + !('value' in lengthDescriptor) || + !Number.isSafeInteger(lengthDescriptor.value) || + lengthDescriptor.value < 0 + ) { + return unsupportedValue('array with invalid length'); + } + const items: WireAwsApiValue[] = []; + for (let index = 0; index < lengthDescriptor.value; index += 1) { + const descriptor = Object.getOwnPropertyDescriptor(value, String(index)); + if (descriptor === undefined) { + return unsupportedValue('sparse array'); + } + if (!('value' in descriptor)) { + return unsupportedValue('array with accessor elements'); + } + items.push(encodeAwsApiValue(descriptor.value, depth + 1, ancestors)); + } + return { type: 'ARRAY', items }; + } finally { + ancestors.delete(value); + } + } + if (isPlainRecord(value)) { + if (ancestors.has(value)) { + return unsupportedValue('cyclic object'); + } + ancestors.add(value); + try { + const entries = Object.create(null) as Record; + for (const key of Reflect.ownKeys(value)) { + if (typeof key !== 'string') { + return unsupportedValue('mapping with non-string keys'); + } + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (descriptor === undefined || !('value' in descriptor)) { + return unsupportedValue('mapping with accessor properties'); + } + entries[key] = encodeAwsApiValue(descriptor.value, depth + 1, ancestors); + } + return { type: 'OBJECT', entries }; + } finally { + ancestors.delete(value); + } + } + return unsupportedValue(typeof value); +} + +function toWireAwsApiRequest(request: AwsApiRequest): WireAwsApiRequest { + const parameters = Object.create(null) as Record; + for (const [name, value] of Object.entries(request.parameters)) { + try { + parameters[name] = encodeAwsApiValue(value); + } catch { + parameters[name] = unsupportedValue('request value inspection failed'); + } + } + return { + serviceName: request.serviceName, + operationName: request.operationName, + parameters, + ...(request.servicePrefix === undefined ? {} : { servicePrefix: request.servicePrefix }), + ...(request.httpMethod === undefined ? {} : { httpMethod: request.httpMethod }), + ...(request.isReadOnly === undefined ? {} : { isReadOnly: request.isReadOnly }), + }; +} + +function fromWireAwsApiRequestValidation(validation: WireAwsApiRequestValidation): AwsApiRequestValidation { + const template = validation.template; + return { + ...validation, + templateSource: validation.templateSource ?? null, + report: validation.report ?? null, + template: template == null ? null : Uint8Array.from(template), + }; +} + export interface Engine { validateStandard(template: TemplateFile, config?: ValidateConfig): StandardReport; validateDetailed(template: TemplateFile, config?: ValidateConfig): DetailedReport; + validateAwsApiRequest(request: AwsApiRequest, config?: ValidateConfig): AwsApiRequestValidation; listRules(): RuleInfo[]; engineName(): string; free(): void; @@ -246,6 +500,7 @@ export class SchemaValidator { interface WasmEngineInstance { validateStandard(template: Uint8Array, options: ValidateConfig, filePath: string): StandardReport; validateDetailed(template: Uint8Array, options: ValidateConfig, filePath: string): DetailedReport; + validateAwsApiRequest(request: WireAwsApiRequest, options: ValidateConfig): WireAwsApiRequestValidation; listRules(): RuleInfo[]; engineName(): string; free(): void; @@ -269,6 +524,15 @@ function createEngineClass( return this.inner.validateDetailed(template.readBytes(), config ?? {}, template.path); } + validateAwsApiRequest(request: AwsApiRequest, config?: ValidateConfig): AwsApiRequestValidation { + if (!(request instanceof AwsApiRequest)) { + throw new TypeError('request must be an AwsApiRequest'); + } + return fromWireAwsApiRequestValidation( + this.inner.validateAwsApiRequest(toWireAwsApiRequest(request), config ?? {}), + ); + } + listRules(): RuleInfo[] { return this.inner.listRules(); } diff --git a/src/data-source/scripts/generate_aws_api_catalog.py b/src/data-source/scripts/generate_aws_api_catalog.py index d98445e6..7d7f798d 100644 --- a/src/data-source/scripts/generate_aws_api_catalog.py +++ b/src/data-source/scripts/generate_aws_api_catalog.py @@ -14,13 +14,24 @@ permissions at a time. The global inverse (operation -> type by name) is unsafe and is never used. Every candidate must pass all of: -- service identity tiering: the action must belong to the type's own service - (botocore service name match beats IAM-prefix match beats signing-identity - match beats substring; lower tiers are dropped when a higher tier exists) +- service identity resolution: an IAM action prefix denotes a botocore service + only by exact identity match (botocore service name, endpoint prefix, signing + name, or service id, compared literally and case-insensitively with + punctuation preserved) or a reviewed action-prefix override + (``ACTION_PREFIX_SERVICE_OVERRIDES``). There is no substring, fuzzy, or + punctuation-folding fallback, so a prefix never denotes a service merely + because one string contains the other or differs only in punctuation. +- service relatedness: the resolved service must relate to the type's own CFN + segment; a botocore-service-name match beats an IAM-prefix match beats a + signing-identity match, and lower-priority relationships are dropped when a + stronger one exists - lifecycle verb family match for the handler role - structural verification: operation input members must map onto writable - properties of the type in the validator's OWN compiled schemas (verbatim, - or via the reviewed identifier-rename rules below) + properties of the type in the validator's OWN compiled schemas by + case-insensitive identifier match, or via the reviewed identifier-rename + allowlist (``PROPERTY_RENAME_ALLOWLIST``); a differently-named property is + accepted only for a fully reviewed cfn_type/service/operation/source/target + context - noun agreement or property-overlap thresholds; ties are dropped entirely - global reverse uniqueness: one (service, operation) key maps to exactly one catalog entry; unresolvable collisions are dropped entirely @@ -71,18 +82,86 @@ 'unsubscribe', 'purge', ) -# CFN service segments whose IAM/service identity differs beyond casing. +# Reviewed property-identifier renames. A create/delete input member maps onto a +# differently-named CloudFormation property only when the full context +# (cfn_type, service, operation, source member, target property) appears here. +# Same-identifier mappings (case-insensitive) never need an entry; every rename +# below was individually audited against the resource's own schema. An +# unreviewed `` -> `Name` or `Name` -> `Name` transform +# is rejected, so a resource-name rename can never be synthesized implicitly. +PROPERTY_RENAME_ALLOWLIST = frozenset({ + ('AWS::Batch::ComputeEnvironment', 'batch', 'DeleteComputeEnvironment', 'computeEnvironment', 'ComputeEnvironmentName'), + ('AWS::Batch::ConsumableResource', 'batch', 'DeleteConsumableResource', 'consumableResource', 'ConsumableResourceName'), + ('AWS::Batch::JobDefinition', 'batch', 'DeregisterJobDefinition', 'jobDefinition', 'JobDefinitionName'), + ('AWS::Batch::JobQueue', 'batch', 'DeleteJobQueue', 'jobQueue', 'JobQueueName'), + ('AWS::Batch::ServiceEnvironment', 'batch', 'DeleteServiceEnvironment', 'serviceEnvironment', 'ServiceEnvironmentName'), + ('AWS::CloudTrail::Trail', 'cloudtrail', 'CreateTrail', 'Name', 'TrailName'), + ('AWS::CloudTrail::Trail', 'cloudtrail', 'DeleteTrail', 'Name', 'TrailName'), + ('AWS::CodeArtifact::Domain', 'codeartifact', 'CreateDomain', 'domain', 'DomainName'), + ('AWS::CodeArtifact::Domain', 'codeartifact', 'DeleteDomain', 'domain', 'DomainName'), + ('AWS::CodeArtifact::PackageGroup', 'codeartifact', 'CreatePackageGroup', 'domain', 'DomainName'), + ('AWS::CodeArtifact::PackageGroup', 'codeartifact', 'DeletePackageGroup', 'domain', 'DomainName'), + ('AWS::CodeArtifact::Repository', 'codeartifact', 'CreateRepository', 'domain', 'DomainName'), + ('AWS::CodeArtifact::Repository', 'codeartifact', 'CreateRepository', 'repository', 'RepositoryName'), + ('AWS::CodeArtifact::Repository', 'codeartifact', 'DeleteRepository', 'domain', 'DomainName'), + ('AWS::CodeArtifact::Repository', 'codeartifact', 'DeleteRepository', 'repository', 'RepositoryName'), + ('AWS::ECS::CapacityProvider', 'ecs', 'CreateCapacityProvider', 'cluster', 'ClusterName'), + ('AWS::ECS::CapacityProvider', 'ecs', 'DeleteCapacityProvider', 'cluster', 'ClusterName'), + ('AWS::ECS::Cluster', 'ecs', 'DeleteCluster', 'cluster', 'ClusterName'), + ('AWS::ECS::Service', 'ecs', 'DeleteService', 'service', 'ServiceName'), + ('AWS::Glue::Database', 'glue', 'DeleteDatabase', 'Name', 'DatabaseName'), + ('AWS::Lex::BotAlias', 'lex-models', 'DeleteBotAlias', 'name', 'BotAliasName'), + ('AWS::S3::Bucket', 's3', 'CreateBucket', 'Bucket', 'BucketName'), + ('AWS::S3::Bucket', 's3', 'DeleteBucket', 'Bucket', 'BucketName'), + ('AWS::S3Outposts::Bucket', 's3control', 'CreateBucket', 'Bucket', 'BucketName'), + ('AWS::S3Tables::Table', 's3tables', 'CreateTable', 'name', 'TableName'), + ('AWS::S3Tables::Table', 's3tables', 'DeleteTable', 'name', 'TableName'), + ('AWS::S3Tables::TableBucket', 's3tables', 'CreateTableBucket', 'name', 'TableBucketName'), + ('AWS::SNS::Topic', 'sns', 'CreateTopic', 'Name', 'TopicName'), + ('AWS::StepFunctions::StateMachine', 'stepfunctions', 'CreateStateMachine', 'name', 'StateMachineName'), + ('AWS::Timestream::ScheduledQuery', 'timestream-query', 'CreateScheduledQuery', 'Name', 'ScheduledQueryName'), +}) + +# CFN service segments whose botocore/IAM service identity differs beyond casing. +# Maps a normalized CFN service segment to the exact normalized botocore service +# identities it may relate to (lowercased, punctuation stripped). This reviewed +# table is how a segment relates to a differently-named service: relatedness comes +# only from an exact identity/prefix match or an explicit entry here, never from +# one string being a substring of another. SEGMENT_ALIASES = { - 'msk': 'kafka', - 'opensearchservice': 'es', - 'certificatemanager': 'acm', - 'elasticloadbalancingv2': 'elasticloadbalancing', - 'ses': 'sesv2', + 'msk': ('kafka',), + 'opensearchservice': ('es',), + 'certificatemanager': ('acm',), + 'elasticloadbalancingv2': ('elasticloadbalancing',), + 'ses': ('sesv2',), + 'amazonmq': ('mq',), + 'bcm': ('bcmdashboards',), + 'cognito': ('cognitoidentity', 'cognitoidp'), + 'eventschemas': ('schemas',), + 'kinesisfirehose': ('firehose',), + 'macie': ('macie2',), + 'mediapackage': ('mediapackagevod',), + 'route53recoverycontrol': ('route53recoverycontrolconfig',), + 'timestream': ('timestreaminfluxdb',), } # Services handled by dedicated validation paths; adapters must not shadow them. EXCLUDED_SERVICES = frozenset({'cloudformation', 'cloudcontrol'}) +# Reviewed IAM action-prefix -> canonical botocore service overrides. Keys are the +# literal-lowercase action prefix (the form `resolve` receives), matched exactly +# with punctuation preserved. These cover IAM prefixes that are not themselves a +# botocore service identity, so exact identity resolution alone would miss them. +# Each entry was audited against the resource's own handler permissions and the +# canonical service's operations: +# `kafka-cluster` (MSK topic data-plane actions) -> `kafka` +# `s3-outposts` (S3 on Outposts bucket actions) -> `s3control` +# They are exact reviewed entries, not substring or punctuation-folded aliases. +ACTION_PREFIX_SERVICE_OVERRIDES = { + 'kafka-cluster': 'kafka', + 's3-outposts': 's3control', +} + # Hand-reviewed update adapters. Update APIs carry partial state, so update # entries are curated rather than derived; each is verified like derived ones. CURATED_UPDATE_ADAPTERS = [ @@ -189,9 +268,26 @@ def _parse_args(): def _normalize(value): + """Fold a CFN resource noun or segment for relatedness comparison. + + Case- and punctuation-insensitive, so segment/noun agreement ignores both. + This folding is deliberately lossy and is only ever used to judge whether a + resolved service relates to a type's own segment; it must never key service + identity resolution, where punctuation is significant. + """ return ''.join(c for c in value.lower() if c.isalnum()) +def _identity_key(value): + """Fold a service identity or IAM action prefix for exact lookup. + + Case-insensitive only, preserving punctuation, so ``s3-control`` and + ``s3control`` stay distinct keys. Service resolution keys on this form, so + no punctuation difference can be erased into a false identity match. + """ + return value.lower() + + class BotocoreIndex: """Resolves IAM action prefixes to concrete botocore operations.""" @@ -203,8 +299,8 @@ def __init__(self): self._by_identity = defaultdict(set) for service in self._session.get_available_services(): model = self._session.get_service_model(service) - identities = { - _normalize(value) + identity_sources = [ + value for value in ( service, model.endpoint_prefix or '', @@ -212,10 +308,12 @@ def __init__(self): str(getattr(model, 'service_id', '') or ''), ) if value + ] + self._identities[service] = { + _normalize(value) for value in identity_sources } - self._identities[service] = identities - for identity in identities: - self._by_identity[identity].add(service) + for value in identity_sources: + self._by_identity[_identity_key(value)].add(service) self._operations[service] = { op.lower(): op for op in model.operation_names } @@ -235,22 +333,29 @@ def input_members(self, service, operation): return dict(shape.members) if shape else {} def resolve(self, action_prefix, action_name): - """Every (service, operation) the action can denote.""" + """Every (service, operation) the exact action identity can denote. + + Resolution is exact and case-insensitive with punctuation preserved: + ``action_prefix`` is a literal-lowercase IAM prefix that must equal one + of a botocore service's literal-lowercase identity components (service + name, endpoint prefix, signing name, or service id). The only + non-identity resolutions are the reviewed + ``ACTION_PREFIX_SERVICE_OVERRIDES``. There is no substring, fuzzy, or + punctuation-folding fallback, so an IAM prefix never denotes a service + because one string contains the other or differs only in punctuation. + """ resolved = set() for service in self._by_identity.get(action_prefix, ()): operation = self._operations[service].get(action_name.lower()) if operation: resolved.add((service, operation)) - if resolved: - return resolved - for identity, services in self._by_identity.items(): - if action_prefix in identity or identity in action_prefix: - for service in services: - operation = self._operations[service].get( - action_name.lower() - ) - if operation: - resolved.add((service, operation)) + override_service = ACTION_PREFIX_SERVICE_OVERRIDES.get(action_prefix) + if override_service is not None: + operation = self._operations.get(override_service, {}).get( + action_name.lower() + ) + if operation: + resolved.add((override_service, operation)) return resolved def identity_tier(self, action_prefix, service, segment_aliases): @@ -261,11 +366,6 @@ def identity_tier(self, action_prefix, service, segment_aliases): return 1 if self._identities[service] & segment_aliases: return 2 - if any( - action_prefix in alias or alias in action_prefix - for alias in segment_aliases - ): - return 3 return None @@ -386,24 +486,121 @@ def _schema_node_for_type(node, definitions, expected_type): return None +def _resolve_tag_schema_node(node, definitions): + seen = set() + while isinstance(node, dict): + reference = node.get('ref_name') + if not reference: + return node + if set(node) != {'ref_name'} or reference in seen: + return None + seen.add(reference) + node = definitions.get(reference) + return None + + +def _key_value_tag_branch_match(schema_node, definitions): + schema_node = _resolve_tag_schema_node(schema_node, definitions) + if schema_node is None: + return None + supported_keys = { + 'type', 'properties', 'required', 'additional_properties', + 'description', + } + if set(schema_node) - supported_keys: + return None + + schema_type = schema_node.get('type') + if isinstance(schema_type, str): + schema_types = {schema_type} + elif isinstance(schema_type, list): + if not all(isinstance(value, str) for value in schema_type): + return None + schema_types = set(schema_type) + elif schema_type is None: + schema_types = set() + else: + return None + if schema_types and 'object' not in schema_types: + return False, set() + + properties = schema_node.get('properties') or {} + required = schema_node.get('required') or [] + additional_properties = schema_node.get('additional_properties') + if (not isinstance(properties, dict) + or not isinstance(required, list) + or not all(isinstance(field, str) for field in required) + or (additional_properties is not None + and not isinstance(additional_properties, bool))): + return None + + generated_fields = {'Key', 'Value'} + property_names = set(properties) + if not set(required) <= generated_fields: + return False, set() + if (additional_properties is False + and not generated_fields <= property_names): + return False, set() + for field in generated_fields & property_names: + if 'string' not in _schema_types(properties[field], definitions): + return False, set() + return True, property_names & generated_fields + + def _is_key_value_tag_array(target_schema, definitions): array_schema = _schema_node_for_type( target_schema, definitions, 'array' ) if not array_schema: return False - item_schema = _resolve_schema_node( + item_schema = _resolve_tag_schema_node( array_schema.get('items') or {}, definitions ) - alternatives = [item_schema] - for key in ('any_of', 'one_of'): - alternatives.extend( - _resolve_schema_node(option, definitions) - for option in item_schema.get(key) or [] - ) + if item_schema is None: + return False + + base_schema = { + key: value for key, value in item_schema.items() + if key not in ('any_of', 'one_of') + } + base_match = _key_value_tag_branch_match(base_schema, definitions) + if base_match is None or not base_match[0]: + return False + base_fields = base_match[1] + + any_of = item_schema.get('any_of') or [] + any_of_fields = [set()] + if any_of: + any_of_fields = [] + for alternative in any_of: + alternative_match = _key_value_tag_branch_match( + alternative, definitions + ) + if alternative_match is not None and alternative_match[0]: + any_of_fields.append(alternative_match[1]) + if not any_of_fields: + return False + + one_of = item_schema.get('one_of') or [] + one_of_fields = [set()] + if one_of: + one_of_fields = [] + for alternative in one_of: + alternative_match = _key_value_tag_branch_match( + alternative, definitions + ) + if alternative_match is None: + return False + if alternative_match[0]: + one_of_fields.append(alternative_match[1]) + if len(one_of_fields) != 1: + return False + + generated_fields = {'Key', 'Value'} return any( - {'Key', 'Value'} <= set(option.get('properties') or {}) - for option in alternatives + generated_fields <= base_fields | any_fields | one_fields + for any_fields in any_of_fields + for one_fields in one_of_fields ) @@ -440,19 +637,32 @@ def _is_runtime_safe_mapping(source_shape, target_schema, definitions, target): def _property_mappings( - members, property_schemas, writable_by_lower, resource_segment, definitions + members, property_schemas, writable_by_lower, resource_segment, definitions, + cfn_type, service, operation ): - """Return mappings the runtime can serialize without nested rewriting.""" + """Return mappings the runtime can serialize without nested rewriting. + + A member maps onto a property with the same identifier (case-insensitive) + unconditionally. A member that maps onto a differently-named property is a + reviewed rename, accepted only when (cfn_type, service, operation, member, + target) is present in ``PROPERTY_RENAME_ALLOWLIST``. + """ mappings = [] for member in sorted(members): lowered = member.lower() target = None if lowered in writable_by_lower: target = writable_by_lower[lowered] - elif lowered + 'name' in writable_by_lower: - target = writable_by_lower[lowered + 'name'] - elif lowered == 'name' and resource_segment + 'name' in writable_by_lower: - target = writable_by_lower[resource_segment + 'name'] + else: + renamed_target = None + if lowered + 'name' in writable_by_lower: + renamed_target = writable_by_lower[lowered + 'name'] + elif lowered == 'name' and resource_segment + 'name' in writable_by_lower: + renamed_target = writable_by_lower[resource_segment + 'name'] + if renamed_target is not None and ( + cfn_type, service, operation, member, renamed_target + ) in PROPERTY_RENAME_ALLOWLIST: + target = renamed_target if target and _is_runtime_safe_mapping( members[member], property_schemas[target], definitions, target ): @@ -481,8 +691,7 @@ def _derive_role(role, verbs, provider_schemas, compiled_schemas, index, require counters['excluded_service'] += 1 continue segment_aliases = {service_segment} - if service_segment in SEGMENT_ALIASES: - segment_aliases.add(SEGMENT_ALIASES[service_segment]) + segment_aliases.update(SEGMENT_ALIASES.get(service_segment, ())) candidates = set() has_unavailable_exact_lifecycle_operation = False for action in handler.get('permissions') or []: @@ -492,16 +701,17 @@ def _derive_role(role, verbs, provider_schemas, compiled_schemas, index, require rank = _verb_rank(action_name, verbs) if rank is None: continue - prefix = _normalize(prefix) - resolved_actions = index.resolve(prefix, action_name) + identity_prefix = _identity_key(prefix) + segment_prefix = _normalize(prefix) + resolved_actions = index.resolve(identity_prefix, action_name) related_actions = { (service, operation) for service, operation in resolved_actions - if index.identity_tier(prefix, service, segment_aliases) + if index.identity_tier(segment_prefix, service, segment_aliases) is not None } if ( - prefix in segment_aliases + segment_prefix in segment_aliases and _noun_matches(action_name, resource_segment) and not related_actions ): @@ -512,7 +722,7 @@ def _derive_role(role, verbs, provider_schemas, compiled_schemas, index, require or (service, operation) in FORBIDDEN_OPERATIONS ): continue - tier = index.identity_tier(prefix, service, segment_aliases) + tier = index.identity_tier(segment_prefix, service, segment_aliases) candidates.add((tier, rank, service, operation)) if not candidates: counters['no_candidates'] += 1 @@ -527,7 +737,7 @@ def _derive_role(role, verbs, provider_schemas, compiled_schemas, index, require members = index.input_members(service, operation) mappings = _property_mappings( members, property_schemas, writable_by_lower, - resource_segment, definitions + resource_segment, definitions, type_name, service, operation ) precision = len(mappings) / len(members) if members else 0.0 noun = _noun_matches(operation, resource_segment) diff --git a/src/data-source/scripts/test_generate_aws_api_catalog.py b/src/data-source/scripts/test_generate_aws_api_catalog.py index e56aaa56..ff659984 100644 --- a/src/data-source/scripts/test_generate_aws_api_catalog.py +++ b/src/data-source/scripts/test_generate_aws_api_catalog.py @@ -17,6 +17,178 @@ def __init__(self, type_name, *, member=None, value=None, metadata=None, self.serialization = serialization or {} +class ExactServiceResolutionTest(unittest.TestCase): + """Resolution is literal: a case-insensitive identity or a reviewed override. + + Punctuation is significant and substrings never match, so an IAM prefix + denotes a service only when it equals a service identity exactly (ignoring + case) or is a reviewed action-prefix override. + """ + + @staticmethod + def _index(by_identity, operations): + index = catalog.BotocoreIndex.__new__(catalog.BotocoreIndex) + index._by_identity = {key: set(services) for key, services in by_identity.items()} + index._operations = operations + index._identities = {service: set() for service in operations} + return index + + def test_identity_key_lowercases_and_preserves_punctuation(self): + # The identity key folds case only; _normalize is the lossy sibling used + # for relatedness. Keeping them distinct is what stops punctuation from + # being erased into a false identity match. + self.assertEqual('s3-control', catalog._identity_key('S3-Control')) + self.assertEqual('kafka-cluster', catalog._identity_key('Kafka-Cluster')) + self.assertEqual('s3control', catalog._normalize('S3-Control')) + + def test_substring_service_identity_no_longer_resolves(self): + # A prefix that is only a *substring* of a real identity resolves to + # nothing: 'mq' is contained in 'amazonmq', but 'amazonmq' is neither an + # identity nor an override. + index = self._index({'mq': {'mq'}}, {'mq': {'createbroker': 'CreateBroker'}}) + self.assertEqual(set(), index.resolve('amazonmq', 'CreateBroker')) + # 'es' is contained in many identities; without a substring fallback an + # unrelated 'es'-containing service is never reached. + index = self._index( + {'esoteric': {'esoteric'}}, {'esoteric': {'deleteapplication': 'DeleteApplication'}} + ) + self.assertEqual(set(), index.resolve('es', 'DeleteApplication')) + + def test_punctuation_variant_does_not_resolve(self): + # The identity is literally 's3-control'; the hyphen is preserved, so the + # punctuation-free prefix 's3control' is a different key and reaches no + # service. Only the exact-punctuation prefix resolves. + index = self._index( + {'s3-control': {'s3control'}}, + {'s3control': {'deletebucketpolicy': 'DeleteBucketPolicy'}}, + ) + self.assertEqual(set(), index.resolve('s3control', 'DeleteBucketPolicy')) + self.assertEqual( + {('s3control', 'DeleteBucketPolicy')}, + index.resolve('s3-control', 'DeleteBucketPolicy'), + ) + + def test_exact_identity_resolves(self): + index = self._index({'s3': {'s3'}}, {'s3': {'createbucket': 'CreateBucket'}}) + self.assertEqual({('s3', 'CreateBucket')}, index.resolve('s3', 'CreateBucket')) + + def test_reviewed_action_prefix_overrides_resolve(self): + index = self._index( + { + 'kafka': {'kafka'}, + 's3control': {'s3control'}, + 's3-outposts': {'s3outposts'}, + }, + { + 'kafka': {'createtopic': 'CreateTopic'}, + 's3control': {'deletebucket': 'DeleteBucket'}, + 's3outposts': {'createendpoint': 'CreateEndpoint'}, + }, + ) + # 'kafka-cluster' is not a botocore identity, so it resolves only through + # the reviewed override to 'kafka'. + self.assertEqual({('kafka', 'CreateTopic')}, index.resolve('kafka-cluster', 'CreateTopic')) + # The literal 's3-outposts' bucket action resolves only to s3control (the + # review); no 's3'-containing service is reached by containment. + self.assertEqual({('s3control', 'DeleteBucket')}, index.resolve('s3-outposts', 'DeleteBucket')) + # Where the s3outposts service itself owns the operation, its exact + # 's3-outposts' endpoint-prefix identity resolves alongside the review. + self.assertEqual({('s3outposts', 'CreateEndpoint')}, index.resolve('s3-outposts', 'CreateEndpoint')) + + def test_punctuation_free_override_spelling_does_not_resolve(self): + # Override keys are literal ('s3-outposts', 'kafka-cluster'), so the + # punctuation-free spellings that punctuation-folding once produced are + # not keys and resolve to nothing. + index = self._index( + {'s3control': {'s3control'}, 'kafka': {'kafka'}}, + {'s3control': {'deletebucket': 'DeleteBucket'}, 'kafka': {'createtopic': 'CreateTopic'}}, + ) + self.assertEqual(set(), index.resolve('s3outposts', 'DeleteBucket')) + self.assertEqual(set(), index.resolve('kafkacluster', 'CreateTopic')) + + def test_override_is_silent_when_service_lacks_operation(self): + index = self._index({'kafka': {'kafka'}}, {'kafka': {'createcluster': 'CreateCluster'}}) + self.assertEqual(set(), index.resolve('kafka-cluster', 'CreateTopic')) + + +class PropertyRenameAllowlistTest(unittest.TestCase): + """Renames are accepted only in a fully reviewed context.""" + + @staticmethod + def _mappings(member, target, cfn_type, service, operation, resource_segment='thing'): + members = {member: Shape('string')} + property_schemas = {target: {'type': 'string'}} + writable_by_lower = {target.lower(): target} + return catalog._property_mappings( + members, property_schemas, writable_by_lower, resource_segment, {}, + cfn_type, service, operation, + ) + + def test_case_only_same_identifier_mapping_is_always_allowed(self): + # Member and property are the same identifier differing only in case. + result = self._mappings('bucketname', 'BucketName', 'AWS::Any::Type', 'any', 'AnyOp') + self.assertEqual([('bucketname', 'BucketName')], result) + + def test_reviewed_rename_in_exact_context_is_accepted(self): + result = self._mappings('Bucket', 'BucketName', 'AWS::S3::Bucket', 's3', 'CreateBucket') + self.assertEqual([('Bucket', 'BucketName')], result) + + def test_reviewed_name_to_segment_rename_is_accepted(self): + result = self._mappings( + 'Name', 'TopicName', 'AWS::SNS::Topic', 'sns', 'CreateTopic', resource_segment='topic' + ) + self.assertEqual([('Name', 'TopicName')], result) + + def test_unreviewed_rename_is_rejected(self): + # Same shape of rename, but this (cfn_type, service, operation) tuple was + # never reviewed, so the resource-name transform must not be synthesized. + self.assertEqual([], self._mappings('Bucket', 'BucketName', 'AWS::Other::Thing', 'other', 'CreateThing')) + + def test_reviewed_rename_rejected_when_any_context_field_changes(self): + # Each field individually differs from the reviewed S3 bucket entry. + self.assertEqual([], self._mappings('Bucket', 'BucketName', 'AWS::S3::AccessPoint', 's3', 'CreateBucket')) + self.assertEqual([], self._mappings('Bucket', 'BucketName', 'AWS::S3::Bucket', 's3control', 'CreateBucket')) + self.assertEqual([], self._mappings('Bucket', 'BucketName', 'AWS::S3::Bucket', 's3', 'PutBucket')) + + +class SegmentRelatednessTest(unittest.TestCase): + """Service relatedness is exact: an alias or exact identity, never substring containment.""" + + @staticmethod + def _index(identities): + index = catalog.BotocoreIndex.__new__(catalog.BotocoreIndex) + index._identities = {service: set(ids) for service, ids in identities.items()} + index._by_identity = {} + index._operations = {} + return index + + def test_containment_without_alias_is_unrelated(self): + # 'mq' is a substring of segment 'amazonmq', but without an explicit alias + # the service is unrelated: the old containment tier no longer applies. + index = self._index({'mq': {'mq'}}) + self.assertIsNone(index.identity_tier('mq', 'mq', {'amazonmq'})) + + def test_exact_segment_name_is_strongest_tier(self): + index = self._index({'kafka': {'kafka'}}) + self.assertEqual(0, index.identity_tier('kafka', 'kafka', {'msk', 'kafka'})) + + def test_reviewed_alias_relates_exactly(self): + # The reviewed 'amazonmq' -> 'mq' alias makes the exact 'mq' identity relate. + index = self._index({'mq': {'mq'}}) + aliases = {'amazonmq'} + aliases.update(catalog.SEGMENT_ALIASES.get('amazonmq', ())) + self.assertEqual(0, index.identity_tier('mq', 'mq', aliases)) + + def test_alias_table_values_are_identity_tuples(self): + # Every entry is a tuple of normalized identities so one segment can relate + # to more than one service (for example cognito). + for segment, identities in catalog.SEGMENT_ALIASES.items(): + self.assertIsInstance(identities, tuple, segment) + self.assertTrue(all(isinstance(identity, str) for identity in identities), segment) + self.assertIn('cognitoidp', catalog.SEGMENT_ALIASES['cognito']) + self.assertIn('cognitoidentity', catalog.SEGMENT_ALIASES['cognito']) + + class CatalogGeneratorTest(unittest.TestCase): def test_unreviewed_collision_is_dropped(self): adapters = [ @@ -79,6 +251,16 @@ def test_runtime_safe_mapping_accepts_only_serializable_shapes(self): Shape("map", value=Shape("string")), tags, definitions, "Tags" ) ) + self.assertFalse( + catalog._is_runtime_safe_mapping( + Shape("map", value=Shape("integer")), tags, definitions, "Tags" + ) + ) + self.assertFalse( + catalog._is_runtime_safe_mapping( + Shape("map", value=Shape("string")), tags, definitions, "Labels" + ) + ) self.assertFalse( catalog._is_runtime_safe_mapping(Shape("structure"), {"type": "object"}, {}, "Config") ) @@ -91,6 +273,188 @@ def test_runtime_safe_mapping_accepts_only_serializable_shapes(self): ) ) + def test_tag_map_rejects_additional_required_target_field(self): + definitions = { + "Tag": { + "type": "object", + "properties": { + "Key": {"type": "string"}, + "Value": {"type": "string"}, + "PropagateAtLaunch": {"type": "boolean"}, + }, + "required": ["Key", "Value", "PropagateAtLaunch"], + } + } + tags = {"type": "array", "items": {"ref_name": "Tag"}} + + self.assertFalse( + catalog._is_runtime_safe_mapping( + Shape("map", value=Shape("string")), tags, definitions, "Tags" + ) + ) + + def test_tag_map_rejects_additional_required_alternative_field(self): + definitions = { + "Tag": { + "type": "object", + "properties": {"Key": {"type": "string"}}, + "required": ["Key"], + "any_of": [ + { + "properties": { + "Value": {"type": "string"}, + "PropagateAtLaunch": {"type": "boolean"}, + }, + "required": ["Value", "PropagateAtLaunch"], + } + ], + } + } + tags = {"type": "array", "items": {"ref_name": "Tag"}} + + self.assertFalse( + catalog._is_runtime_safe_mapping( + Shape("map", value=Shape("string")), tags, definitions, "Tags" + ) + ) + + def test_tag_map_accepts_unambiguous_key_value_alternative(self): + definitions = { + "Tag": { + "one_of": [ + { + "type": "object", + "properties": { + "Key": {"type": "string"}, + "Value": {"type": "string"}, + }, + "required": ["Key", "Value"], + "additional_properties": False, + }, + { + "type": "object", + "properties": { + "TagKey": {"type": "string"}, + "TagValue": {"type": "string"}, + }, + "required": ["TagKey", "TagValue"], + "additional_properties": False, + }, + ] + } + } + tags = {"type": "array", "items": {"ref_name": "Tag"}} + + self.assertTrue( + catalog._is_runtime_safe_mapping( + Shape("map", value=Shape("string")), tags, definitions, "Tags" + ) + ) + + def test_tag_map_rejects_ambiguous_key_value_alternatives(self): + key_value_branch = { + "type": "object", + "properties": { + "Key": {"type": "string"}, + "Value": {"type": "string"}, + }, + "required": ["Key", "Value"], + } + definitions = { + "Tag": {"one_of": [key_value_branch, dict(key_value_branch)]} + } + tags = {"type": "array", "items": {"ref_name": "Tag"}} + + self.assertFalse( + catalog._is_runtime_safe_mapping( + Shape("map", value=Shape("string")), tags, definitions, "Tags" + ) + ) + + def test_tag_map_rejects_permissive_one_of_sibling(self): + definitions = { + "Tag": { + "one_of": [ + { + "type": "object", + "properties": { + "Key": {"type": "string"}, + "Value": {"type": "string"}, + }, + "required": ["Key", "Value"], + }, + {"type": "object"}, + ] + } + } + tags = {"type": "array", "items": {"ref_name": "Tag"}} + + self.assertFalse( + catalog._is_runtime_safe_mapping( + Shape("map", value=Shape("string")), tags, definitions, "Tags" + ) + ) + + def test_tag_map_rejects_unmodeled_object_constraint(self): + definitions = { + "Tag": { + "type": "object", + "properties": { + "Key": {"type": "string"}, + "Value": {"type": "string"}, + "Scope": {"type": "string"}, + }, + "required": ["Key", "Value"], + "dependent_required": {"Key": ["Scope"]}, + } + } + tags = {"type": "array", "items": {"ref_name": "Tag"}} + + self.assertFalse( + catalog._is_runtime_safe_mapping( + Shape("map", value=Shape("string")), tags, definitions, "Tags" + ) + ) + + def test_tag_map_rejects_non_string_value_field(self): + definitions = { + "Tag": { + "type": "object", + "properties": { + "Key": {"type": "string"}, + "Value": {"type": "integer"}, + }, + "required": ["Key", "Value"], + } + } + tags = {"type": "array", "items": {"ref_name": "Tag"}} + + self.assertFalse( + catalog._is_runtime_safe_mapping( + Shape("map", value=Shape("string")), tags, definitions, "Tags" + ) + ) + + def test_tag_map_accepts_optional_additional_target_field(self): + definitions = { + "Tag": { + "type": "object", + "properties": { + "Key": {"type": "string"}, + "Value": {"type": "string"}, + "Description": {"type": "string"}, + }, + "required": ["Key", "Value"], + } + } + tags = {"type": "array", "items": {"ref_name": "Tag"}} + + self.assertTrue( + catalog._is_runtime_safe_mapping( + Shape("map", value=Shape("string")), tags, definitions, "Tags" + ) + ) + def test_provider_schema_directory_is_loaded_deterministically(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) diff --git a/src/validation-engine/API.md b/src/validation-engine/API.md index a9911694..b7dc70c1 100644 --- a/src/validation-engine/API.md +++ b/src/validation-engine/API.md @@ -104,9 +104,11 @@ Cloud Control `UpdateResource` and `DeleteResource` may report a known `TypeName but they never synthesize state. There is no fuzzy inference, substring matching, or generic property-name guessing. `TemplateBody` validation is restricted to the closed set of CloudFormation operations that accept it; `TypeName`+`DesiredState` wrapping is restricted to exact Cloud Control `CreateResource`. `service_name` is the -authoritative mapping identity; the optional signing `service_prefix` cannot override it. Case normalization supports -both CLI names (for example, `s3`) and Java SDK `SERVICE_NAME` values (for example, `S3`) without punctuation or -substring aliases. +authoritative mapping identity and must be the exact canonical botocore service name, normalized only for ASCII case; +the optional signing `service_prefix` is context only and cannot override it. There are no signing, endpoint, +punctuation, or substring aliases. Any caller, including a future AWS SDK adapter in any language, must translate its +native service identity to the canonical botocore `service_name` before invoking this API; the core intentionally does +not guess aliases. ## Constructing an Engine diff --git a/src/validation-engine/src/aws_api.rs b/src/validation-engine/src/aws_api.rs index 56108e1e..db49f707 100644 --- a/src/validation-engine/src/aws_api.rs +++ b/src/validation-engine/src/aws_api.rs @@ -358,7 +358,7 @@ const TEMPLATE_BODY_OPERATIONS: &[(&str, &str)] = &[ ("cloudformation", "ValidateTemplate"), ]; -/// CLI and Java SDK service names differ only in casing for the supported adapters. +/// Callers pass canonical botocore service names, so case-normalization is the only lookup adjustment. fn normalize_service(name: &str) -> String { name.to_ascii_lowercase() } @@ -1431,7 +1431,7 @@ mod tests { } #[test] - fn dynamodb_create_table_skips_when_nested_values_are_unrepresentable() { + fn dynamodb_create_table_skips_when_unmapped_nested_parameters_are_present() { let schema_validator = SchemaValidator::default(); let request = request( "dynamodb", @@ -1447,10 +1447,10 @@ mod tests { assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); assert_eq!(classification.candidates, ["AWS::DynamoDB::Table"]); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); - assert!(synthesis.template.is_none(), "nested struct arrays must skip synthesis"); + assert!(synthesis.template.is_none(), "unmapped nested parameters must skip synthesis"); assert!( - synthesis.reason.contains("cannot be represented"), - "reason must explain the type mismatch: {}", + synthesis.reason.contains("has no mapping"), + "reason must explain the unmapped parameter: {}", synthesis.reason ); } @@ -1488,8 +1488,7 @@ mod tests { "CreateRole", serde_json::json!({ "RoleName": "Synthetic", - "AssumeRolePolicyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[]}", - "Tags": {"Team": "CLI"} + "AssumeRolePolicyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[]}" }), ); let (classification, synthesis, document) = synthesized_json(&request); @@ -1551,7 +1550,7 @@ mod tests { #[test] fn sns_create_topic_synthesizes_with_explicit_mappings() { - let request = request("sns", "CreateTopic", serde_json::json!({"Name": "Synthetic", "Tags": {"Team": "CLI"}})); + let request = request("sns", "CreateTopic", serde_json::json!({"Name": "Synthetic"})); let (classification, synthesis, document) = synthesized_json(&request); assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); assert_eq!(classification.candidates, ["AWS::SNS::Topic"]); @@ -1593,8 +1592,10 @@ mod tests { assert!(synthesis.template.is_none()); } #[test] - fn java_sdk_service_name_casing_resolves_adapters() { + fn canonical_service_name_matching_is_case_insensitive() { let schema_validator = SchemaValidator::default(); + // Non-lowercase spellings of the canonical botocore service name must + // normalize to the same adapter; service_name is ASCII case-normalized only. for (service, operation, expected_type) in [ ("S3", "CreateBucket", "AWS::S3::Bucket"), ("DynamoDb", "CreateTable", "AWS::DynamoDB::Table"), @@ -1608,7 +1609,7 @@ mod tests { assert_eq!( classification.candidates, [expected_type], - "Java SDK casing {service}:{operation} should resolve to {expected_type}" + "canonical service name {service}:{operation} must resolve case-insensitively to {expected_type}" ); } } @@ -1840,13 +1841,14 @@ mod tests { #[test] fn incompatible_property_value_skips_synthesis() { let schema_validator = SchemaValidator::default(); + // MaxSessionDuration is mapped and expects integer; pass a boolean. let request = request( "iam", "CreateRole", serde_json::json!({ "RoleName": "Synthetic", "AssumeRolePolicyDocument": "{}", - "Tags": {"Key": 42} + "MaxSessionDuration": true }), ); let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); From e20b1c4454c5c5df4c04c1f5488658e815bc0383 Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Thu, 10 Sep 2026 17:18:09 -0400 Subject: [PATCH 09/19] updates --- src/bindings-go/README.md | 22 +- src/bindings-go/go/cfnvalidate.go | 106 +-- src/bindings-go/go/cfnvalidate_test.go | 46 +- src/bindings-go/go/types.go | 54 +- src/bindings-go/src/lib.rs | 74 +- src/bindings-go/tests/smoke_test.go | 54 +- src/bindings-jvm/README.md | 12 +- src/bindings-jvm/src/lib.rs | 12 +- .../amazon/cloudformation/validate/Api.kt | 80 +- .../tests/kotlin/src/test/kotlin/SmokeTest.kt | 56 +- src/bindings-python/README.md | 18 +- .../cloudformation_validate/__init__.py | 78 +- src/bindings-python/src/lib.rs | 12 +- src/bindings-python/tests/smoke_test.py | 44 +- src/bindings-wasm/README.md | 14 +- src/bindings-wasm/src/lib.rs | 13 +- src/bindings-wasm/tests/smoke.test.ts | 58 +- src/bindings-wasm/ts/index.ts | 68 +- src/data-source/Cargo.toml | 5 + src/data-source/build.rs | 2 +- ...og.json => aws_cli_operation_catalog.json} | 65 +- ...catalog.py => generate_aws_cli_catalog.py} | 20 +- .../scripts/test_generate_aws_api_catalog.py | 804 ------------------ src/data-source/src/aws_cli_catalog.rs | 151 ++++ src/schema-validator/src/resource_schema.rs | 2 +- src/validation-engine/API.md | 20 +- .../src/{aws_api.rs => aws_cli.rs} | 423 +++++---- src/validation-engine/src/lib.rs | 8 +- 28 files changed, 842 insertions(+), 1479 deletions(-) rename src/data-source/generated/data/{aws_api_operation_catalog.json => aws_cli_operation_catalog.json} (99%) rename src/data-source/scripts/{generate_aws_api_catalog.py => generate_aws_cli_catalog.py} (98%) delete mode 100644 src/data-source/scripts/test_generate_aws_api_catalog.py create mode 100644 src/data-source/src/aws_cli_catalog.rs rename src/validation-engine/src/{aws_api.rs => aws_cli.rs} (86%) diff --git a/src/bindings-go/README.md b/src/bindings-go/README.md index 54524758..dc1c8fdc 100644 --- a/src/bindings-go/README.md +++ b/src/bindings-go/README.md @@ -59,7 +59,7 @@ diagnostics for the same template and config. A `nil` config uses only the built |------------------------------------------------------------------------------|-------------------------------------|-------------------------------------------------------------------------------------------------| | `ValidateTemplate(template []byte, config *ValidateConfig, filePath string)` | `(*ValidationReport, error)` | Validates bytes; `config.DetailLevel` selects the detail level; `filePath` labels the report. | | `ValidateTemplateFile(path string, config *ValidateConfig)` | `(*ValidationReport, error)` | Reads a template from disk, then validates it | -| `ValidateAWSAPIRequest(request AWSAPIRequest, config *ValidateConfig)` | `(*AWSAPIRequestValidation, error)` | Classifies and validates an AWS API request offline | +| `ValidateAWSCLICommand(request AWSCLICommand, config *ValidateConfig)` | `(*AWSCLICommandValidation, error)` | Classifies and validates an AWS CLI command offline | | `ListRules()` | `([]RuleInfo, error)` | Returns metadata for every built-in and loaded custom rule | | `EngineName()` | `string` | `"rego"` or `"cel"` | | `Destroy()` | - | Releases the native engine; the engine must not be used afterwards | @@ -199,9 +199,9 @@ type PseudoParameterOverrides struct { } ``` -## AWS API Request Validation +## AWS CLI Command Validation -Validates an AWS API request by classifying the operation, inferring the CloudFormation resource type, and running +Validates an AWS CLI command by classifying the operation, inferring the CloudFormation resource type, and running schema and rule validation against a synthesized template - entirely offline. The method returns classification metadata and an optional `ValidationReport` when the request was validated (not skipped for read-only operations). The report is projected at the `STANDARD` detail level: enrichment fields are nil because a synthesized template has no @@ -211,7 +211,7 @@ user-authored source to annotate. engine, _ := cfnvalidate.NewRegoEngine(nil) defer engine.Destroy() -result, err := engine.ValidateAWSAPIRequest(cfnvalidate.AWSAPIRequest{ +result, err := engine.ValidateAWSCLICommand(cfnvalidate.AWSCLICommand{ ServiceName: "s3", OperationName: "CreateBucket", Parameters: map[string]any{"Bucket": "my-bucket"}, @@ -229,10 +229,10 @@ if result.Report != nil { } ``` -### AWSAPIRequest +### AWSCLICommand ```go -type AWSAPIRequest struct { +type AWSCLICommand struct { ServiceName string // canonical botocore service name (e.g. "s3") - ASCII case-insensitive OperationName string // operation name (e.g. "CreateBucket") - case-sensitive Parameters map[string]any // request parameters: strings, numbers, booleans, []byte, maps, slices, nil @@ -258,13 +258,13 @@ SDK adapter in any language, must translate its native service identity to the c invoking this API. `TemplateBody` validation is restricted to CloudFormation operations that accept it, and `TypeName`+`DesiredState` wrapping applies only to exact Cloud Control `CreateResource`. -### AWSAPIRequestValidation +### AWSCLICommandValidation ```go -type AWSAPIRequestValidation struct { - OperationKind AWSAPIOperationKind // READ_ONLY, CLOUD_FORMATION_CREATE, etc. - Status AWSAPIRequestValidationStatus // VALIDATED or SKIPPED - TemplateSource *AWSAPITemplateSource // TEMPLATE_BODY, SYNTHESIZED_CREATE, etc. +type AWSCLICommandValidation struct { + OperationKind AWSCLIOperationKind // READ_ONLY, CLOUD_FORMATION_CREATE, etc. + Status AWSCLICommandValidationStatus // VALIDATED or SKIPPED + TemplateSource *AWSCLITemplateSource // TEMPLATE_BODY, SYNTHESIZED_CREATE, etc. ResourceTypes []string // inferred CloudFormation resource types Reason string // human-readable explanation Report *ValidationReport // present only when Status is VALIDATED diff --git a/src/bindings-go/go/cfnvalidate.go b/src/bindings-go/go/cfnvalidate.go index d5f9cc39..6353308f 100644 --- a/src/bindings-go/go/cfnvalidate.go +++ b/src/bindings-go/go/cfnvalidate.go @@ -82,7 +82,7 @@ func decodeInto[T any](data string, what string) (*T, error) { // nativeEngine is the method set shared by the generated engine objects. type nativeEngine interface { ValidateTemplateJson(template []byte, optionsJson string, filePath string) (string, error) - ValidateAwsApiRequestJson(requestJson string, optionsJson string) (string, error) + ValidateAwsCliCommandJson(requestJson string, optionsJson string) (string, error) ListRulesJson() (string, error) EngineName() string Destroy() @@ -176,45 +176,45 @@ func (e *Engine) Destroy() { e.inner.Destroy() } -// ValidateAWSAPIRequest classifies and validates an AWS API request against +// ValidateAWSCLICommand classifies and validates an AWS CLI command against // CloudFormation schemas and rules entirely offline. The result contains // operation classification, resource type inference, and an optional // ValidationReport when the request was validated (not skipped). The report // carries only the shared diagnostic fields; enrichment fields are left nil. -func (e *Engine) ValidateAWSAPIRequest(request AWSAPIRequest, config *ValidateConfig) (*AWSAPIRequestValidation, error) { +func (e *Engine) ValidateAWSCLICommand(request AWSCLICommand, config *ValidateConfig) (*AWSCLICommandValidation, error) { optionsJSON, err := validateConfigJSON(config) if err != nil { return nil, err } - requestJSON, err := marshalAWSAPIRequest(request) + requestJSON, err := marshalAWSCLICommand(request) if err != nil { return nil, err } - data, err := e.inner.ValidateAwsApiRequestJson(requestJSON, optionsJSON) + data, err := e.inner.ValidateAwsCliCommandJson(requestJSON, optionsJSON) if err != nil { return nil, err } - return decodeInto[AWSAPIRequestValidation](data, "AWS API request validation") + return decodeInto[AWSCLICommandValidation](data, "AWS CLI command validation") } -// marshalAWSAPIRequest encodes an AWSAPIRequest into the wire JSON that the -// Rust side expects, converting Go parameter values into tagged AwsApiValue +// marshalAWSCLICommand encodes an AWSCLICommand into the wire JSON that the +// Rust side expects, converting Go parameter values into tagged AwsCliValue // objects. -func marshalAWSAPIRequest(request AWSAPIRequest) (string, error) { - wire := awsApiRequestWire{ +func marshalAWSCLICommand(request AWSCLICommand) (string, error) { + wire := awsCliCommandWire{ ServiceName: request.ServiceName, OperationName: request.OperationName, - Parameters: make(map[string]awsApiValue, len(request.Parameters)), + Parameters: make(map[string]awsCliValue, len(request.Parameters)), ServicePrefix: nilIfEmpty(request.ServicePrefix), HTTPMethod: nilIfEmpty(request.HTTPMethod), IsReadOnly: request.IsReadOnly, } for key, value := range request.Parameters { - wire.Parameters[key] = encodeAwsApiValue(value, 0) + wire.Parameters[key] = encodeAwsCliValue(value, 0) } data, err := json.Marshal(wire) if err != nil { - return "", fmt.Errorf("cfnvalidate: encoding AWS API request: %w", err) + return "", fmt.Errorf("cfnvalidate: encoding AWS CLI command: %w", err) } return string(data), nil } @@ -226,44 +226,44 @@ func nilIfEmpty(s string) *string { return &s } -// awsApiRequestWire is the JSON structure consumed by the Rust wire parser. -type awsApiRequestWire struct { +// awsCliCommandWire is the JSON structure consumed by the Rust wire parser. +type awsCliCommandWire struct { ServiceName string `json:"serviceName"` OperationName string `json:"operationName"` - Parameters map[string]awsApiValue `json:"parameters"` + Parameters map[string]awsCliValue `json:"parameters"` ServicePrefix *string `json:"servicePrefix,omitempty"` HTTPMethod *string `json:"httpMethod,omitempty"` IsReadOnly *bool `json:"isReadOnly,omitempty"` } -// awsApiValue is the tagged union wire format matching the core AwsApiValue +// awsCliValue is the tagged union wire format matching the core AwsCliValue // serde representation (tag = "type", rename_all = "SCREAMING_SNAKE_CASE"). // Items and Entries use pointer fields so that empty slices/maps serialize as // their JSON zero ([] / {}) while remaining absent for unrelated variants. -type awsApiValue struct { +type awsCliValue struct { Type string `json:"type"` Value any `json:"value,omitempty"` - Items *[]awsApiValue `json:"items,omitempty"` - Entries *map[string]awsApiValue `json:"entries,omitempty"` + Items *[]awsCliValue `json:"items,omitempty"` + Entries *map[string]awsCliValue `json:"entries,omitempty"` TypeName string `json:"type_name,omitempty"` } // maxEncodeDepth prevents stack overflow on cyclic or deeply nested structures. const maxEncodeDepth = 64 -// encodeAwsApiValue recursively converts a Go value into the tagged wire +// encodeAwsCliValue recursively converts a Go value into the tagged wire // format. It is non-mutating: no pointer is followed through a write path. // Unsupported types are represented as UNSUPPORTED rather than coerced. // // SDK-defined type aliases (e.g. types.InstanceType is a named string) are // handled via reflect.Kind after concrete type checks, so any alias of a // scalar kind is encoded correctly without enumerating every SDK type. -func encodeAwsApiValue(v any, depth int) awsApiValue { +func encodeAwsCliValue(v any, depth int) awsCliValue { if depth > maxEncodeDepth { - return awsApiValue{Type: "UNSUPPORTED", TypeName: "recursion depth exceeded"} + return awsCliValue{Type: "UNSUPPORTED", TypeName: "recursion depth exceeded"} } if v == nil { - return awsApiValue{Type: "NULL"} + return awsCliValue{Type: "NULL"} } // Unwrap interface and pointer layers. Count indirections separately because @@ -273,10 +273,10 @@ func encodeAwsApiValue(v any, depth int) awsApiValue { indirections := 0 for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface { if depth+indirections > maxEncodeDepth { - return awsApiValue{Type: "UNSUPPORTED", TypeName: "recursion depth exceeded"} + return awsCliValue{Type: "UNSUPPORTED", TypeName: "recursion depth exceeded"} } if rv.IsNil() { - return awsApiValue{Type: "NULL"} + return awsCliValue{Type: "NULL"} } rv = rv.Elem() indirections++ @@ -287,49 +287,49 @@ func encodeAwsApiValue(v any, depth int) awsApiValue { // underlying kind (time.Time and json.Number). switch val := v.(type) { case time.Time: - return awsApiValue{Type: "STRING", Value: val.UTC().Format(time.RFC3339Nano)} + return awsCliValue{Type: "STRING", Value: val.UTC().Format(time.RFC3339Nano)} case json.Number: text := string(val) if i, err := val.Int64(); err == nil { - return awsApiValue{Type: "INTEGER", Value: i} + return awsCliValue{Type: "INTEGER", Value: i} } if u, err := strconv.ParseUint(text, 10, 64); err == nil { - return awsApiValue{Type: "UNSIGNED_INTEGER", Value: u} + return awsCliValue{Type: "UNSIGNED_INTEGER", Value: u} } if !strings.ContainsAny(text, ".eE") && json.Valid([]byte(text)) { - return awsApiValue{Type: "UNSUPPORTED", TypeName: "integer outside 64-bit range"} + return awsCliValue{Type: "UNSUPPORTED", TypeName: "integer outside 64-bit range"} } if f, err := val.Float64(); err == nil { if math.IsInf(f, 0) || math.IsNaN(f) { - return awsApiValue{Type: "UNSUPPORTED", TypeName: "non-finite floating-point number"} + return awsCliValue{Type: "UNSUPPORTED", TypeName: "non-finite floating-point number"} } - return awsApiValue{Type: "NUMBER", Value: f} + return awsCliValue{Type: "NUMBER", Value: f} } - return awsApiValue{Type: "UNSUPPORTED", TypeName: "unparseable json.Number"} + return awsCliValue{Type: "UNSUPPORTED", TypeName: "unparseable json.Number"} } // Kind-based handling covers both built-in types and SDK-defined aliases // (e.g. types.InstanceType is `type InstanceType string`). switch rv.Kind() { case reflect.Bool: - return awsApiValue{Type: "BOOLEAN", Value: rv.Bool()} + return awsCliValue{Type: "BOOLEAN", Value: rv.Bool()} case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return awsApiValue{Type: "INTEGER", Value: rv.Int()} + return awsCliValue{Type: "INTEGER", Value: rv.Int()} case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return awsApiValue{Type: "UNSIGNED_INTEGER", Value: rv.Uint()} + return awsCliValue{Type: "UNSIGNED_INTEGER", Value: rv.Uint()} case reflect.Float32, reflect.Float64: f := rv.Float() if math.IsInf(f, 0) || math.IsNaN(f) { - return awsApiValue{Type: "UNSUPPORTED", TypeName: "non-finite floating-point number"} + return awsCliValue{Type: "UNSUPPORTED", TypeName: "non-finite floating-point number"} } - return awsApiValue{Type: "NUMBER", Value: f} + return awsCliValue{Type: "NUMBER", Value: f} case reflect.String: - return awsApiValue{Type: "STRING", Value: rv.String()} + return awsCliValue{Type: "STRING", Value: rv.String()} case reflect.Slice, reflect.Array: // []byte / [N]byte → BYTES, encoded as a JSON integer array so the @@ -340,34 +340,34 @@ func encodeAwsApiValue(v any, depth int) awsApiValue { for i := range ints { ints[i] = int(rv.Index(i).Uint()) } - return awsApiValue{Type: "BYTES", Value: ints} + return awsCliValue{Type: "BYTES", Value: ints} } - items := make([]awsApiValue, rv.Len()) + items := make([]awsCliValue, rv.Len()) for i := range items { - items[i] = encodeAwsApiValue(rv.Index(i).Interface(), depth+1) + items[i] = encodeAwsCliValue(rv.Index(i).Interface(), depth+1) } - return awsApiValue{Type: "ARRAY", Items: &items} + return awsCliValue{Type: "ARRAY", Items: &items} case reflect.Map: if rv.Type().Key().Kind() != reflect.String { - return awsApiValue{Type: "UNSUPPORTED", TypeName: "mapping with non-string keys"} + return awsCliValue{Type: "UNSUPPORTED", TypeName: "mapping with non-string keys"} } - entries := make(map[string]awsApiValue, rv.Len()) + entries := make(map[string]awsCliValue, rv.Len()) iter := rv.MapRange() for iter.Next() { - entries[iter.Key().String()] = encodeAwsApiValue(iter.Value().Interface(), depth+1) + entries[iter.Key().String()] = encodeAwsCliValue(iter.Value().Interface(), depth+1) } - return awsApiValue{Type: "OBJECT", Entries: &entries} + return awsCliValue{Type: "OBJECT", Entries: &entries} case reflect.Struct: - return awsApiValue{Type: "UNSUPPORTED", TypeName: rv.Type().String()} + return awsCliValue{Type: "UNSUPPORTED", TypeName: rv.Type().String()} default: - return awsApiValue{Type: "UNSUPPORTED", TypeName: rv.Type().String()} + return awsCliValue{Type: "UNSUPPORTED", TypeName: rv.Type().String()} } } -// UnmarshalJSON decodes an AWSAPIRequestValidation, translating the core's +// UnmarshalJSON decodes an AWSCLICommandValidation, translating the core's // integer-array encoding of the validated template into a byte slice. // // The core serializes the template as a JSON array of byte-valued integers @@ -375,8 +375,8 @@ func encodeAwsApiValue(v any, depth int) awsApiValue { // into a []byte directly - it expects a base64 string. Every other field // decodes with the standard rules. On any failure the receiver is left // unchanged, so a decode error never leaves a partially populated result. -func (v *AWSAPIRequestValidation) UnmarshalJSON(data []byte) error { - type withoutTemplate AWSAPIRequestValidation +func (v *AWSCLICommandValidation) UnmarshalJSON(data []byte) error { + type withoutTemplate AWSCLICommandValidation var wire struct { withoutTemplate Template json.RawMessage `json:"template"` @@ -388,7 +388,7 @@ func (v *AWSAPIRequestValidation) UnmarshalJSON(data []byte) error { if err != nil { return err } - decoded := AWSAPIRequestValidation(wire.withoutTemplate) + decoded := AWSCLICommandValidation(wire.withoutTemplate) decoded.Template = template *v = decoded return nil diff --git a/src/bindings-go/go/cfnvalidate_test.go b/src/bindings-go/go/cfnvalidate_test.go index ef2d6db8..6c1f53e7 100644 --- a/src/bindings-go/go/cfnvalidate_test.go +++ b/src/bindings-go/go/cfnvalidate_test.go @@ -8,10 +8,10 @@ import ( "time" ) -func TestMarshalAWSAPIRequestFormatsTimeAsRFC3339UTC(t *testing.T) { +func TestMarshalAWSCLICommandFormatsTimeAsRFC3339UTC(t *testing.T) { timestamp := time.Date(2025, time.January, 2, 3, 4, 5, 123456789, time.FixedZone("UTC+2", 2*60*60)) - encoded := encodedAWSAPIParameter(t, timestamp) + encoded := encodedAWSCLIParameter(t, timestamp) if got := encoded["type"]; got != "STRING" { t.Fatalf("type = %v, want STRING", got) @@ -21,8 +21,8 @@ func TestMarshalAWSAPIRequestFormatsTimeAsRFC3339UTC(t *testing.T) { } } -func TestMarshalAWSAPIRequestPreservesUnsignedJSONNumber(t *testing.T) { - encoded := encodedAWSAPIParameter(t, json.Number("18446744073709551615")) +func TestMarshalAWSCLICommandPreservesUnsignedJSONNumber(t *testing.T) { + encoded := encodedAWSCLIParameter(t, json.Number("18446744073709551615")) if got := encoded["type"]; got != "UNSIGNED_INTEGER" { t.Fatalf("type = %v, want UNSIGNED_INTEGER", got) @@ -36,8 +36,8 @@ func TestMarshalAWSAPIRequestPreservesUnsignedJSONNumber(t *testing.T) { } } -func TestMarshalAWSAPIRequestMarksOutOfRangeIntegerUnsupported(t *testing.T) { - encoded := encodedAWSAPIParameter(t, json.Number("18446744073709551616")) +func TestMarshalAWSCLICommandMarksOutOfRangeIntegerUnsupported(t *testing.T) { + encoded := encodedAWSCLIParameter(t, json.Number("18446744073709551616")) if got := encoded["type"]; got != "UNSUPPORTED" { t.Fatalf("type = %v, want UNSUPPORTED", got) @@ -50,15 +50,15 @@ func TestMarshalAWSAPIRequestMarksOutOfRangeIntegerUnsupported(t *testing.T) { } } -func encodedAWSAPIParameter(t *testing.T, value any) map[string]any { +func encodedAWSCLIParameter(t *testing.T, value any) map[string]any { t.Helper() - requestJSON, err := marshalAWSAPIRequest(AWSAPIRequest{ + requestJSON, err := marshalAWSCLICommand(AWSCLICommand{ ServiceName: "test", OperationName: "TestOperation", Parameters: map[string]any{"Value": value}, }) if err != nil { - t.Fatalf("marshalAWSAPIRequest failed: %v", err) + t.Fatalf("marshalAWSCLICommand failed: %v", err) } decoder := json.NewDecoder(strings.NewReader(requestJSON)) @@ -86,11 +86,11 @@ func templateIntegerArray(text string) string { return "[" + strings.Join(elements, ",") + "]" } -func unmarshalValidation(t *testing.T, blob string) AWSAPIRequestValidation { +func unmarshalValidation(t *testing.T, blob string) AWSCLICommandValidation { t.Helper() - var validation AWSAPIRequestValidation + var validation AWSCLICommandValidation if err := json.Unmarshal([]byte(blob), &validation); err != nil { - t.Fatalf("unmarshalling AWSAPIRequestValidation failed: %v", err) + t.Fatalf("unmarshalling AWSCLICommandValidation failed: %v", err) } return validation } @@ -145,7 +145,7 @@ func TestUnmarshalValidationRejectsMalformedTemplateBytes(t *testing.T) { for name, template := range cases { t.Run(name, func(t *testing.T) { blob := `{"operationKind":"CLOUD_FORMATION_CREATE","status":"VALIDATED","resourceTypes":[],"reason":"synthesized","template":` + template + `}` - var validation AWSAPIRequestValidation + var validation AWSCLICommandValidation if err := json.Unmarshal([]byte(blob), &validation); err == nil { t.Fatalf("expected an error for template %s, got Template = %v", template, validation.Template) } @@ -156,7 +156,7 @@ func TestUnmarshalValidationRejectsMalformedTemplateBytes(t *testing.T) { func TestUnmarshalValidationReportsOffendingByteInError(t *testing.T) { blob := `{"operationKind":"CLOUD_FORMATION_CREATE","status":"VALIDATED","resourceTypes":[],"reason":"synthesized","template":[10,256]}` - err := json.Unmarshal([]byte(blob), &AWSAPIRequestValidation{}) + err := json.Unmarshal([]byte(blob), &AWSCLICommandValidation{}) if err == nil { t.Fatal("expected an out-of-range error") } @@ -166,9 +166,9 @@ func TestUnmarshalValidationReportsOffendingByteInError(t *testing.T) { } func TestUnmarshalValidationLeavesReceiverUnchangedOnError(t *testing.T) { - existing := AWSAPIRequestValidation{ - OperationKind: AWSAPIOperationKindReadOnly, - Status: AWSAPIRequestValidationStatusSkipped, + existing := AWSCLICommandValidation{ + OperationKind: AWSCLIOperationKindReadOnly, + Status: AWSCLICommandValidationStatusSkipped, Reason: "unchanged", Template: []byte("original"), } @@ -192,19 +192,19 @@ func TestUnmarshalValidationDecodesEveryFieldAlongsideTemplate(t *testing.T) { "templateSource":"SYNTHESIZED_CREATE", "resourceTypes":["AWS::S3::Bucket"], "reason":"synthesized one unambiguous CloudFormation resource", - "report":{"filePath":"aws-api://s3/CreateBucket","status":"OK","version":"0.0.0","diagnostics":[]}, + "report":{"filePath":"aws-cli://s3/CreateBucket","status":"OK","version":"0.0.0","diagnostics":[]}, "template":` + templateIntegerArray(template) + ` }` validation := unmarshalValidation(t, blob) - if validation.OperationKind != AWSAPIOperationKindCloudFormationCreate { + if validation.OperationKind != AWSCLIOperationKindCloudFormationCreate { t.Errorf("OperationKind = %q, want CLOUD_FORMATION_CREATE", validation.OperationKind) } - if validation.Status != AWSAPIRequestValidationStatusValidated { + if validation.Status != AWSCLICommandValidationStatusValidated { t.Errorf("Status = %q, want VALIDATED", validation.Status) } - if validation.TemplateSource == nil || *validation.TemplateSource != AWSAPITemplateSourceSynthesizedCreate { + if validation.TemplateSource == nil || *validation.TemplateSource != AWSCLITemplateSourceSynthesizedCreate { t.Errorf("TemplateSource = %v, want SYNTHESIZED_CREATE", validation.TemplateSource) } if len(validation.ResourceTypes) != 1 || validation.ResourceTypes[0] != "AWS::S3::Bucket" { @@ -216,8 +216,8 @@ func TestUnmarshalValidationDecodesEveryFieldAlongsideTemplate(t *testing.T) { if validation.Report == nil { t.Fatal("Report = nil, want the nested report to decode") } - if validation.Report.FilePath != "aws-api://s3/CreateBucket" { - t.Errorf("Report.FilePath = %q, want aws-api://s3/CreateBucket", validation.Report.FilePath) + if validation.Report.FilePath != "aws-cli://s3/CreateBucket" { + t.Errorf("Report.FilePath = %q, want aws-cli://s3/CreateBucket", validation.Report.FilePath) } if got := string(validation.Template); got != template { t.Errorf("Template = %q, want %q", got, template) diff --git a/src/bindings-go/go/types.go b/src/bindings-go/go/types.go index 875b9bed..740e4a85 100644 --- a/src/bindings-go/go/types.go +++ b/src/bindings-go/go/types.go @@ -302,7 +302,7 @@ type ValidateConfig struct { DisableBuiltinRules *bool `json:"disableBuiltinRules,omitempty"` } -// AWSAPIRequest holds an AWS API service call for offline CloudFormation +// AWSCLICommand holds an AWS CLI command for offline CloudFormation // validation. ServiceName and OperationName identify the API; Parameters carry // the request values (maps, strings, numbers, booleans, byte slices, etc.). // @@ -312,7 +312,7 @@ type ValidateConfig struct { // future AWS SDK adapter, in any language, must translate its native service // identity to the canonical botocore ServiceName before calling; the core does // not guess aliases. -type AWSAPIRequest struct { +type AWSCLICommand struct { ServiceName string `json:"serviceName"` OperationName string `json:"operationName"` Parameters map[string]any `json:"parameters"` @@ -321,43 +321,43 @@ type AWSAPIRequest struct { IsReadOnly *bool `json:"isReadOnly,omitempty"` } -// AWSAPIOperationKind classifies an AWS API operation. -type AWSAPIOperationKind string +// AWSCLIOperationKind classifies an AWS CLI operation. +type AWSCLIOperationKind string const ( - AWSAPIOperationKindReadOnly AWSAPIOperationKind = "READ_ONLY" - AWSAPIOperationKindCloudFormationCreate AWSAPIOperationKind = "CLOUD_FORMATION_CREATE" - AWSAPIOperationKindCloudFormationUpdate AWSAPIOperationKind = "CLOUD_FORMATION_UPDATE" - AWSAPIOperationKindCloudFormationDelete AWSAPIOperationKind = "CLOUD_FORMATION_DELETE" - AWSAPIOperationKindDataPlaneMutation AWSAPIOperationKind = "DATA_PLANE_MUTATION" - AWSAPIOperationKindUnmappedMutation AWSAPIOperationKind = "UNMAPPED_MUTATION" + AWSCLIOperationKindReadOnly AWSCLIOperationKind = "READ_ONLY" + AWSCLIOperationKindCloudFormationCreate AWSCLIOperationKind = "CLOUD_FORMATION_CREATE" + AWSCLIOperationKindCloudFormationUpdate AWSCLIOperationKind = "CLOUD_FORMATION_UPDATE" + AWSCLIOperationKindCloudFormationDelete AWSCLIOperationKind = "CLOUD_FORMATION_DELETE" + AWSCLIOperationKindDataPlaneMutation AWSCLIOperationKind = "DATA_PLANE_MUTATION" + AWSCLIOperationKindUnmappedMutation AWSCLIOperationKind = "UNMAPPED_MUTATION" ) -// AWSAPIRequestValidationStatus indicates whether validation ran or was skipped. -type AWSAPIRequestValidationStatus string +// AWSCLICommandValidationStatus indicates whether validation ran or was skipped. +type AWSCLICommandValidationStatus string const ( - AWSAPIRequestValidationStatusValidated AWSAPIRequestValidationStatus = "VALIDATED" - AWSAPIRequestValidationStatusSkipped AWSAPIRequestValidationStatus = "SKIPPED" + AWSCLICommandValidationStatusValidated AWSCLICommandValidationStatus = "VALIDATED" + AWSCLICommandValidationStatusSkipped AWSCLICommandValidationStatus = "SKIPPED" ) -// AWSAPITemplateSource identifies the provenance of the template validated for -// an API request. -type AWSAPITemplateSource string +// AWSCLITemplateSource identifies the provenance of the template validated for +// an AWS CLI command. +type AWSCLITemplateSource string const ( - AWSAPITemplateSourceTemplateBody AWSAPITemplateSource = "TEMPLATE_BODY" - AWSAPITemplateSourceCloudControlDesiredState AWSAPITemplateSource = "CLOUD_CONTROL_DESIRED_STATE" - AWSAPITemplateSourceSynthesizedCreate AWSAPITemplateSource = "SYNTHESIZED_CREATE" - AWSAPITemplateSourceSynthesizedUpdate AWSAPITemplateSource = "SYNTHESIZED_UPDATE" + AWSCLITemplateSourceTemplateBody AWSCLITemplateSource = "TEMPLATE_BODY" + AWSCLITemplateSourceCloudControlDesiredState AWSCLITemplateSource = "CLOUD_CONTROL_DESIRED_STATE" + AWSCLITemplateSourceSynthesizedCreate AWSCLITemplateSource = "SYNTHESIZED_CREATE" + AWSCLITemplateSourceSynthesizedUpdate AWSCLITemplateSource = "SYNTHESIZED_UPDATE" ) -// AWSAPIRequestValidation is the canonical result of validating an AWS API -// request. Report is present only when Status is VALIDATED. -type AWSAPIRequestValidation struct { - OperationKind AWSAPIOperationKind `json:"operationKind"` - Status AWSAPIRequestValidationStatus `json:"status"` - TemplateSource *AWSAPITemplateSource `json:"templateSource,omitempty"` +// AWSCLICommandValidation is the canonical result of validating an AWS CLI +// command. Report is present only when Status is VALIDATED. +type AWSCLICommandValidation struct { + OperationKind AWSCLIOperationKind `json:"operationKind"` + Status AWSCLICommandValidationStatus `json:"status"` + TemplateSource *AWSCLITemplateSource `json:"templateSource,omitempty"` ResourceTypes []string `json:"resourceTypes"` Reason string `json:"reason"` Report *ValidationReport `json:"report,omitempty"` diff --git a/src/bindings-go/src/lib.rs b/src/bindings-go/src/lib.rs index 36397268..e2283270 100644 --- a/src/bindings-go/src/lib.rs +++ b/src/bindings-go/src/lib.rs @@ -178,17 +178,17 @@ fn to_json(value: &T) -> Result { serde_json::to_string(value).map_err(|e| ValidationError::new(format!("failed to serialize result: {e}"))) } -/// Wire struct for an AWS API request received from Go as JSON. +/// Wire struct for an AWS CLI command received from Go as JSON. /// -/// Field names match the Go `AWSAPIRequest` struct's `json` tags exactly. +/// Field names match the Go `AWSCLICommand` struct's `json` tags exactly. /// Unknown fields are rejected so a drifted field name surfaces as an error /// instead of silently ignoring the caller's intent. #[derive(Debug, serde::Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -struct AwsApiRequestWire { +struct AwsCliCommandWire { service_name: String, operation_name: String, - parameters: HashMap, + parameters: HashMap, #[serde(default)] service_prefix: Option, #[serde(default)] @@ -197,13 +197,13 @@ struct AwsApiRequestWire { is_read_only: Option, } -impl AwsApiRequestWire { +impl AwsCliCommandWire { fn parse(json: &str) -> Result { - serde_json::from_str(json).map_err(|e| ValidationError::new(format!("invalid AWS API request JSON: {e}"))) + serde_json::from_str(json).map_err(|e| ValidationError::new(format!("invalid AWS CLI command JSON: {e}"))) } - fn into_context(self) -> validation_engine::AwsApiRequestContext { - validation_engine::AwsApiRequestContext { + fn into_context(self) -> validation_engine::AwsCliCommand { + validation_engine::AwsCliCommand { service_name: self.service_name, operation_name: self.operation_name, parameters: self.parameters, @@ -332,17 +332,17 @@ macro_rules! impl_go_engine { ) } - /// Validates an AWS API request and returns the canonical result as JSON. - pub fn validate_aws_api_request_json( + /// Validates an AWS CLI command and returns the canonical result as JSON. + pub fn validate_aws_cli_command_json( &self, request_json: String, options_json: String, ) -> Result { catch_panics( || { - let request = AwsApiRequestWire::parse(&request_json)?.into_context(); + let request = AwsCliCommandWire::parse(&request_json)?.into_context(); let config = ValidateOptions::parse(&options_json)?.to_core(); - let result = validation_engine::validate_aws_api_request( + let result = validation_engine::validate_aws_cli_command( &self.engine, &self.schema_validator, &request, @@ -663,8 +663,8 @@ mod tests { } #[test] - fn aws_api_request_parses_valid_minimal_request() { - let wire = AwsApiRequestWire::parse( + fn aws_cli_command_parses_valid_minimal_request() { + let wire = AwsCliCommandWire::parse( r#"{"serviceName":"s3","operationName":"CreateBucket","parameters":{"Bucket":{"type":"STRING","value":"test"}}}"#, ) .expect("valid minimal request must parse"); @@ -678,8 +678,8 @@ mod tests { } #[test] - fn aws_api_request_parses_nested_values_and_bytes() { - let wire = AwsApiRequestWire::parse( + fn aws_cli_command_parses_nested_values_and_bytes() { + let wire = AwsCliCommandWire::parse( r#"{ "serviceName": "cloudformation", "operationName": "CreateStack", @@ -704,27 +704,27 @@ mod tests { let context = wire.into_context(); match context.parameters.get("TemplateBody") { - Some(validation_engine::AwsApiValue::Bytes { value }) => assert_eq!(value, &[123, 125]), + Some(validation_engine::AwsCliValue::Bytes { value }) => assert_eq!(value, &[123, 125]), other => panic!("expected Bytes, got {other:?}"), } match context.parameters.get("Count") { - Some(validation_engine::AwsApiValue::Integer { value }) => assert_eq!(*value, 42), + Some(validation_engine::AwsCliValue::Integer { value }) => assert_eq!(*value, 42), other => panic!("expected Integer, got {other:?}"), } } #[test] - fn aws_api_request_rejects_malformed_json() { - let error = AwsApiRequestWire::parse("not json").expect_err("malformed JSON must fail"); + fn aws_cli_command_rejects_malformed_json() { + let error = AwsCliCommandWire::parse("not json").expect_err("malformed JSON must fail"); assert!( - error.to_string().contains("invalid AWS API request JSON"), + error.to_string().contains("invalid AWS CLI command JSON"), "error must identify the failing input: {error}" ); } #[test] - fn aws_api_request_rejects_unknown_fields() { - let error = AwsApiRequestWire::parse( + fn aws_cli_command_rejects_unknown_fields() { + let error = AwsCliCommandWire::parse( r#"{"serviceName":"s3","operationName":"CreateBucket","parameters":{},"unknownField":"x"}"#, ) .expect_err("unknown field must fail"); @@ -732,19 +732,19 @@ mod tests { } #[test] - fn aws_api_request_rejects_missing_required_fields() { + fn aws_cli_command_rejects_missing_required_fields() { let error = - AwsApiRequestWire::parse(r#"{"serviceName":"s3"}"#).expect_err("missing required operationName must fail"); + AwsCliCommandWire::parse(r#"{"serviceName":"s3"}"#).expect_err("missing required operationName must fail"); assert!(error.to_string().contains("operationName"), "error must name the missing field: {error}"); } #[test] - fn aws_api_value_unsupported_uses_type_name_field() { + fn aws_cli_value_unsupported_uses_type_name_field() { let json = r#"{"type":"UNSUPPORTED","type_name":"non-finite floating-point number"}"#; - let value: validation_engine::AwsApiValue = + let value: validation_engine::AwsCliValue = serde_json::from_str(json).expect("UNSUPPORTED with type_name must parse"); match value { - validation_engine::AwsApiValue::Unsupported { type_name } => { + validation_engine::AwsCliValue::Unsupported { type_name } => { assert_eq!(type_name, "non-finite floating-point number"); } other => panic!("expected Unsupported, got {other:?}"), @@ -752,12 +752,12 @@ mod tests { } #[test] - fn aws_api_value_bytes_parses_integer_array() { + fn aws_cli_value_bytes_parses_integer_array() { let json = r#"{"type":"BYTES","value":[72,101,108,108,111]}"#; - let value: validation_engine::AwsApiValue = + let value: validation_engine::AwsCliValue = serde_json::from_str(json).expect("BYTES with integer array must parse"); match value { - validation_engine::AwsApiValue::Bytes { value } => { + validation_engine::AwsCliValue::Bytes { value } => { assert_eq!(value, vec![72, 101, 108, 108, 111]); } other => panic!("expected Bytes, got {other:?}"), @@ -765,12 +765,12 @@ mod tests { } #[test] - fn aws_api_value_empty_array_has_items_field() { + fn aws_cli_value_empty_array_has_items_field() { let json = r#"{"type":"ARRAY","items":[]}"#; - let value: validation_engine::AwsApiValue = + let value: validation_engine::AwsCliValue = serde_json::from_str(json).expect("ARRAY with empty items must parse"); match value { - validation_engine::AwsApiValue::Array { items } => { + validation_engine::AwsCliValue::Array { items } => { assert!(items.is_empty()); } other => panic!("expected Array, got {other:?}"), @@ -778,12 +778,12 @@ mod tests { } #[test] - fn aws_api_value_empty_object_has_entries_field() { + fn aws_cli_value_empty_object_has_entries_field() { let json = r#"{"type":"OBJECT","entries":{}}"#; - let value: validation_engine::AwsApiValue = + let value: validation_engine::AwsCliValue = serde_json::from_str(json).expect("OBJECT with empty entries must parse"); match value { - validation_engine::AwsApiValue::Object { entries } => { + validation_engine::AwsCliValue::Object { entries } => { assert!(entries.is_empty()); } other => panic!("expected Object, got {other:?}"), diff --git a/src/bindings-go/tests/smoke_test.go b/src/bindings-go/tests/smoke_test.go index 74844d97..d149d0e6 100644 --- a/src/bindings-go/tests/smoke_test.go +++ b/src/bindings-go/tests/smoke_test.go @@ -542,30 +542,30 @@ func synthesizedBucketName(t *testing.T, template []byte) string { return document.Resources.Resource.Properties.BucketName } -func TestValidateAWSAPIRequestSynthesizesS3CreateBucketOnBothEngines(t *testing.T) { +func TestValidateAWSCLICommandSynthesizesS3CreateBucketOnBothEngines(t *testing.T) { const bucketName = "synthetic-bucket" - request := cfnvalidate.AWSAPIRequest{ + request := cfnvalidate.AWSCLICommand{ ServiceName: "s3", OperationName: "CreateBucket", Parameters: map[string]any{"Bucket": bucketName}, } - perEngine := map[string]*cfnvalidate.AWSAPIRequestValidation{} + perEngine := map[string]*cfnvalidate.AWSCLICommandValidation{} for name, engine := range bothEngines(t) { - validation, err := engine.ValidateAWSAPIRequest(request, nil) + validation, err := engine.ValidateAWSCLICommand(request, nil) if err != nil { - t.Fatalf("%s: ValidateAWSAPIRequest failed: %v", name, err) + t.Fatalf("%s: ValidateAWSCLICommand failed: %v", name, err) } - if validation.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { + if validation.Status != cfnvalidate.AWSCLICommandValidationStatusValidated { t.Errorf("%s: status = %s, want VALIDATED", name, validation.Status) } - if validation.OperationKind != cfnvalidate.AWSAPIOperationKindCloudFormationCreate { + if validation.OperationKind != cfnvalidate.AWSCLIOperationKindCloudFormationCreate { t.Errorf("%s: operationKind = %s, want CLOUD_FORMATION_CREATE", name, validation.OperationKind) } if len(validation.ResourceTypes) != 1 || validation.ResourceTypes[0] != "AWS::S3::Bucket" { t.Errorf("%s: resourceTypes = %v, want [AWS::S3::Bucket]", name, validation.ResourceTypes) } - if validation.TemplateSource == nil || *validation.TemplateSource != cfnvalidate.AWSAPITemplateSourceSynthesizedCreate { + if validation.TemplateSource == nil || *validation.TemplateSource != cfnvalidate.AWSCLITemplateSourceSynthesizedCreate { t.Errorf("%s: templateSource = %v, want SYNTHESIZED_CREATE", name, validation.TemplateSource) } if validation.Report == nil { @@ -588,25 +588,25 @@ func TestValidateAWSAPIRequestSynthesizesS3CreateBucketOnBothEngines(t *testing. } } -func TestValidateAWSAPIRequestPreservesExactTemplateBodyBytes(t *testing.T) { +func TestValidateAWSCLICommandPreservesExactTemplateBodyBytes(t *testing.T) { // Distinctive whitespace and key order that a reserialization would not // reproduce, so an exact match proves the original bytes are returned. templateBody := []byte("{\n \"Resources\": {\n \"Bucket\": { \"Type\": \"AWS::S3::Bucket\" }\n }\n}") - request := cfnvalidate.AWSAPIRequest{ + request := cfnvalidate.AWSCLICommand{ ServiceName: "cloudformation", OperationName: "ValidateTemplate", Parameters: map[string]any{"TemplateBody": templateBody}, } for name, engine := range bothEngines(t) { - validation, err := engine.ValidateAWSAPIRequest(request, nil) + validation, err := engine.ValidateAWSCLICommand(request, nil) if err != nil { - t.Fatalf("%s: ValidateAWSAPIRequest failed: %v", name, err) + t.Fatalf("%s: ValidateAWSCLICommand failed: %v", name, err) } - if validation.Status != cfnvalidate.AWSAPIRequestValidationStatusValidated { + if validation.Status != cfnvalidate.AWSCLICommandValidationStatusValidated { t.Errorf("%s: status = %s, want VALIDATED", name, validation.Status) } - if validation.TemplateSource == nil || *validation.TemplateSource != cfnvalidate.AWSAPITemplateSourceTemplateBody { + if validation.TemplateSource == nil || *validation.TemplateSource != cfnvalidate.AWSCLITemplateSourceTemplateBody { t.Errorf("%s: templateSource = %v, want TEMPLATE_BODY", name, validation.TemplateSource) } if !bytes.Equal(validation.Template, templateBody) { @@ -615,8 +615,8 @@ func TestValidateAWSAPIRequestPreservesExactTemplateBodyBytes(t *testing.T) { } } -func TestValidateAWSAPIRequestConservativelySkipsNestedDynamoDbFields(t *testing.T) { - request := cfnvalidate.AWSAPIRequest{ +func TestValidateAWSCLICommandConservativelySkipsNestedDynamoDbFields(t *testing.T) { + request := cfnvalidate.AWSCLICommand{ ServiceName: "dynamodb", OperationName: "CreateTable", Parameters: map[string]any{ @@ -628,11 +628,11 @@ func TestValidateAWSAPIRequestConservativelySkipsNestedDynamoDbFields(t *testing } for name, engine := range bothEngines(t) { - validation, err := engine.ValidateAWSAPIRequest(request, nil) + validation, err := engine.ValidateAWSCLICommand(request, nil) if err != nil { - t.Fatalf("%s: ValidateAWSAPIRequest failed: %v", name, err) + t.Fatalf("%s: ValidateAWSCLICommand failed: %v", name, err) } - if validation.Status != cfnvalidate.AWSAPIRequestValidationStatusSkipped { + if validation.Status != cfnvalidate.AWSCLICommandValidationStatusSkipped { t.Errorf("%s: status = %s, want SKIPPED for unrepresentable nested fields", name, validation.Status) } if validation.Report != nil { @@ -650,36 +650,36 @@ func TestValidateAWSAPIRequestConservativelySkipsNestedDynamoDbFields(t *testing } } -func TestValidateAWSAPIRequestDoesNotGuessNoncanonicalServiceAlias(t *testing.T) { +func TestValidateAWSCLICommandDoesNotGuessNoncanonicalServiceAlias(t *testing.T) { // CloudWatch's canonical botocore service name is "cloudwatch"; "monitoring" // is its signing name. The core must resolve the operation under the // canonical name but never guess the signing alias. - canonical := cfnvalidate.AWSAPIRequest{ + canonical := cfnvalidate.AWSCLICommand{ ServiceName: "cloudwatch", OperationName: "PutMetricAlarm", Parameters: map[string]any{"AlarmName": "synthetic"}, } - alias := cfnvalidate.AWSAPIRequest{ + alias := cfnvalidate.AWSCLICommand{ ServiceName: "monitoring", OperationName: "PutMetricAlarm", Parameters: map[string]any{"AlarmName": "synthetic"}, } for name, engine := range bothEngines(t) { - canonicalValidation, err := engine.ValidateAWSAPIRequest(canonical, nil) + canonicalValidation, err := engine.ValidateAWSCLICommand(canonical, nil) if err != nil { - t.Fatalf("%s: canonical ValidateAWSAPIRequest failed: %v", name, err) + t.Fatalf("%s: canonical ValidateAWSCLICommand failed: %v", name, err) } if !containsString(canonicalValidation.ResourceTypes, "AWS::CloudWatch::Alarm") { t.Errorf("%s: canonical cloudwatch:PutMetricAlarm must identify AWS::CloudWatch::Alarm, got %v", name, canonicalValidation.ResourceTypes) } - aliasValidation, err := engine.ValidateAWSAPIRequest(alias, nil) + aliasValidation, err := engine.ValidateAWSCLICommand(alias, nil) if err != nil { - t.Fatalf("%s: alias ValidateAWSAPIRequest failed: %v", name, err) + t.Fatalf("%s: alias ValidateAWSCLICommand failed: %v", name, err) } - if aliasValidation.Status != cfnvalidate.AWSAPIRequestValidationStatusSkipped { + if aliasValidation.Status != cfnvalidate.AWSCLICommandValidationStatusSkipped { t.Errorf("%s: signing alias must not classify as a CloudFormation operation, status = %s", name, aliasValidation.Status) } if containsString(aliasValidation.ResourceTypes, "AWS::CloudWatch::Alarm") { diff --git a/src/bindings-jvm/README.md b/src/bindings-jvm/README.md index 4798cfe2..5ed950b7 100644 --- a/src/bindings-jvm/README.md +++ b/src/bindings-jvm/README.md @@ -74,15 +74,15 @@ interface Engine { `template` is a `java.io.File` - the engine reads the bytes and uses the file path for diagnostic source locations. -### AWS API request validation +### AWS CLI command validation -Use `validateAwsApiRequest` for AWS SDK-style request values rather than a complete template. The validator classifies +Use `validateAwsCliCommand` for an AWS CLI command rather than a complete template. The validator classifies the operation, selects a CloudFormation resource type, models representable create/update state, and validates the resulting template entirely offline: ```kotlin -val result = RegoEngine().validateAwsApiRequest( - AwsApiRequest( +val result = RegoEngine().validateAwsCliCommand( + AwsCliCommand( serviceName = "s3", servicePrefix = "s3", operationName = "CreateBucket", @@ -98,10 +98,10 @@ result.report?.diagnostics?.forEach { diagnostic -> } ?: println("${result.status}: ${result.reason}") ``` -`AwsApiRequest.parameters` accepts nested maps, iterables and arrays, scalars, byte arrays, and Java temporal values +`AwsCliCommand.parameters` accepts nested maps, iterables and arrays, scalars, byte arrays, and Java temporal values without mutating the supplied map. `TemplateBody` bytes are validated exactly; `TemplateURL` is skipped because the validator does not perform network requests. Every result reports `status`, `operationKind`, `templateSource`, -`resourceTypes`, and `reason`; skipped requests have a null `report`. +`resourceTypes`, and `reason`; skipped commands have a null `report`. The same classes and methods are callable from Java with conventional generated getters. Operation-to-resource mapping uses a deterministic closed adapter catalog generated from each resource type's own diff --git a/src/bindings-jvm/src/lib.rs b/src/bindings-jvm/src/lib.rs index 47583df5..d15a1dd9 100644 --- a/src/bindings-jvm/src/lib.rs +++ b/src/bindings-jvm/src/lib.rs @@ -24,8 +24,8 @@ pub use template_model::model::{ pub use template_model::resolver::{MapEntry, ParameterInfo, RefKind, ResolvedValue}; pub use template_model::{JsonValue, PseudoParameterOverrides, SourceSpan}; pub use validation_engine::{ - AwsApiOperationKind, AwsApiRequestContext, AwsApiRequestValidation, AwsApiRequestValidationStatus, - AwsApiTemplateSource, AwsApiValue, EngineConfig, EngineType, ExternalRuleSource, + AwsCliCommand, AwsCliCommandValidation, AwsCliCommandValidationStatus, AwsCliOperationKind, AwsCliTemplateSource, + AwsCliValue, EngineConfig, EngineType, ExternalRuleSource, }; pub use schema_validator::SchemaValidatorConfig; @@ -181,15 +181,15 @@ macro_rules! impl_jvm_engine { ) } - pub fn validate_aws_api_request( + pub fn validate_aws_cli_command( &self, - request: AwsApiRequestContext, + request: AwsCliCommand, config: ValidateConfig, - ) -> Result { + ) -> Result { validation_engine::catch_panics( || { let core_config = config.to_core(); - let validation = validation_engine::validate_aws_api_request( + let validation = validation_engine::validate_aws_cli_command( &self.engine, &self.schema_validator, &request, diff --git a/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt b/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt index 2abbb5b5..cce1b99f 100644 --- a/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt +++ b/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt @@ -3,9 +3,9 @@ package software.amazon.cloudformation.validate import software.amazon.cloudformation.validate.datasource.AdditionalSchemaSource import software.amazon.cloudformation.validate.diagnostics.Diagnostic import software.amazon.cloudformation.validate.diagnostics.ValidationReport -import software.amazon.cloudformation.validate.engine.AwsApiRequestContext as NativeAwsApiRequest -import software.amazon.cloudformation.validate.engine.AwsApiRequestValidation -import software.amazon.cloudformation.validate.engine.AwsApiValue as NativeAwsApiValue +import software.amazon.cloudformation.validate.engine.AwsCliCommandContext as NativeAwsCliCommand +import software.amazon.cloudformation.validate.engine.AwsCliCommandValidation +import software.amazon.cloudformation.validate.engine.AwsCliValue as NativeAwsCliValue import software.amazon.cloudformation.validate.engine.EngineConfig import software.amazon.cloudformation.validate.engine.ExternalRuleSource import software.amazon.cloudformation.validate.rules.RuleInfo @@ -14,10 +14,10 @@ import java.io.File interface Engine { fun validateTemplate(template: File, config: ValidateConfig = ValidateConfig()): ValidationReport - fun validateAwsApiRequest( - request: AwsApiRequest, + fun validateAwsCliCommand( + request: AwsCliCommand, config: ValidateConfig = ValidateConfig(), - ): AwsApiRequestValidation + ): AwsCliCommandValidation fun listRules(): List fun engineName(): String } @@ -36,7 +36,7 @@ interface Engine { * byte arrays, and Java time values. Unsupported values are marked explicitly * and conservatively omitted during request-to-template synthesis. */ -class AwsApiRequest @JvmOverloads constructor( +class AwsCliCommand @JvmOverloads constructor( val serviceName: String, val operationName: String, parameters: Map, @@ -46,56 +46,56 @@ class AwsApiRequest @JvmOverloads constructor( ) { val parameters: Map = LinkedHashMap(parameters) - internal fun toNative(): NativeAwsApiRequest = - NativeAwsApiRequest( + internal fun toNative(): NativeAwsCliCommand = + NativeAwsCliCommand( serviceName = serviceName, operationName = operationName, - parameters = parameters.mapValues { (_, value) -> value.toNativeAwsApiValue() }, + parameters = parameters.mapValues { (_, value) -> value.toNativeAwsCliValue() }, servicePrefix = servicePrefix, httpMethod = httpMethod, isReadOnly = isReadOnly, ) } -private fun Any?.toNativeAwsApiValue(): NativeAwsApiValue = +private fun Any?.toNativeAwsCliValue(): NativeAwsCliValue = when (this) { - null -> NativeAwsApiValue.Null - is Boolean -> NativeAwsApiValue.Boolean(value = this) - is Byte -> NativeAwsApiValue.Integer(value = toLong()) - is Short -> NativeAwsApiValue.Integer(value = toLong()) - is Int -> NativeAwsApiValue.Integer(value = toLong()) - is Long -> NativeAwsApiValue.Integer(value = this) - is UByte -> NativeAwsApiValue.UnsignedInteger(value = toULong()) - is UShort -> NativeAwsApiValue.UnsignedInteger(value = toULong()) - is UInt -> NativeAwsApiValue.UnsignedInteger(value = toULong()) - is ULong -> NativeAwsApiValue.UnsignedInteger(value = this) + null -> NativeAwsCliValue.Null + is Boolean -> NativeAwsCliValue.Boolean(value = this) + is Byte -> NativeAwsCliValue.Integer(value = toLong()) + is Short -> NativeAwsCliValue.Integer(value = toLong()) + is Int -> NativeAwsCliValue.Integer(value = toLong()) + is Long -> NativeAwsCliValue.Integer(value = this) + is UByte -> NativeAwsCliValue.UnsignedInteger(value = toULong()) + is UShort -> NativeAwsCliValue.UnsignedInteger(value = toULong()) + is UInt -> NativeAwsCliValue.UnsignedInteger(value = toULong()) + is ULong -> NativeAwsCliValue.UnsignedInteger(value = this) is Float -> if (isFinite()) { - NativeAwsApiValue.Number(value = toDouble()) + NativeAwsCliValue.Number(value = toDouble()) } else { - NativeAwsApiValue.Unsupported(typeName = "non-finite floating-point number") + NativeAwsCliValue.Unsupported(typeName = "non-finite floating-point number") } is Double -> if (isFinite()) { - NativeAwsApiValue.Number(value = this) + NativeAwsCliValue.Number(value = this) } else { - NativeAwsApiValue.Unsupported(typeName = "non-finite floating-point number") + NativeAwsCliValue.Unsupported(typeName = "non-finite floating-point number") } - is String -> NativeAwsApiValue.String(value = this) - is ByteArray -> NativeAwsApiValue.Bytes(value = this) - is java.time.temporal.TemporalAccessor -> NativeAwsApiValue.String(value = toString()) + is String -> NativeAwsCliValue.String(value = this) + is ByteArray -> NativeAwsCliValue.Bytes(value = this) + is java.time.temporal.TemporalAccessor -> NativeAwsCliValue.String(value = toString()) is Map<*, *> -> { if (keys.any { it !is String }) { - NativeAwsApiValue.Unsupported(typeName = "mapping with non-string keys") + NativeAwsCliValue.Unsupported(typeName = "mapping with non-string keys") } else { - NativeAwsApiValue.Object( - entries = entries.associate { (key, value) -> key as String to value.toNativeAwsApiValue() }, + NativeAwsCliValue.Object( + entries = entries.associate { (key, value) -> key as String to value.toNativeAwsCliValue() }, ) } } - is Iterable<*> -> NativeAwsApiValue.Array(items = map { it.toNativeAwsApiValue() }) - is Array<*> -> NativeAwsApiValue.Array(items = map { it.toNativeAwsApiValue() }) - else -> NativeAwsApiValue.Unsupported(typeName = javaClass.name) + is Iterable<*> -> NativeAwsCliValue.Array(items = map { it.toNativeAwsCliValue() }) + is Array<*> -> NativeAwsCliValue.Array(items = map { it.toNativeAwsCliValue() }) + else -> NativeAwsCliValue.Unsupported(typeName = javaClass.name) } /** @@ -148,10 +148,10 @@ class RegoEngine( override fun validateTemplate(template: File, config: ValidateConfig): ValidationReport = inner.validateTemplate(template.readBytes(), config, template.path) - override fun validateAwsApiRequest( - request: AwsApiRequest, + override fun validateAwsCliCommand( + request: AwsCliCommand, config: ValidateConfig, - ): AwsApiRequestValidation = inner.validateAwsApiRequest(request.toNative(), config) + ): AwsCliCommandValidation = inner.validateAwsCliCommand(request.toNative(), config) override fun listRules(): List = inner.listRules() override fun engineName(): String = inner.engineName() @@ -165,10 +165,10 @@ class CelEngine( override fun validateTemplate(template: File, config: ValidateConfig): ValidationReport = inner.validateTemplate(template.readBytes(), config, template.path) - override fun validateAwsApiRequest( - request: AwsApiRequest, + override fun validateAwsCliCommand( + request: AwsCliCommand, config: ValidateConfig, - ): AwsApiRequestValidation = inner.validateAwsApiRequest(request.toNative(), config) + ): AwsCliCommandValidation = inner.validateAwsCliCommand(request.toNative(), config) override fun listRules(): List = inner.listRules() override fun engineName(): String = inner.engineName() diff --git a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt index af6820eb..f84d9958 100644 --- a/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt +++ b/src/bindings-jvm/tests/kotlin/src/test/kotlin/SmokeTest.kt @@ -469,9 +469,9 @@ class SmokeTest { } } - // ── AWS API request validation ──────────────────────────────────────────── + // ── AWS CLI command validation ──────────────────────────────────────────── - private val awsApiEngines: List> = listOf("rego" to REGO, "cel" to CEL) + private val awsCliEngines: List> = listOf("rego" to REGO, "cel" to CEL) private fun diagnosticKeys(report: ValidationReport): List = report.diagnostics.map { "${it.ruleId}|${it.severity}|${it.startLine}|${it.startColumn}" }.sorted() @@ -486,15 +486,15 @@ class SmokeTest { } @Test - fun awsApiS3CreateBucketSynthesizesOnBothEngines() { - val request = AwsApiRequest("s3", "CreateBucket", mapOf("Bucket" to "synthetic-bucket")) - val perEngine = LinkedHashMap() - for ((name, engine) in awsApiEngines) { - val validation = engine.validateAwsApiRequest(request) - assertEquals(AwsApiRequestValidationStatus.VALIDATED, validation.status, "$name: status") - assertEquals(AwsApiOperationKind.CLOUD_FORMATION_CREATE, validation.operationKind, "$name: operation kind") + fun awsCliS3CreateBucketSynthesizesOnBothEngines() { + val request = AwsCliCommand("s3", "CreateBucket", mapOf("Bucket" to "synthetic-bucket")) + val perEngine = LinkedHashMap() + for ((name, engine) in awsCliEngines) { + val validation = engine.validateAwsCliCommand(request) + assertEquals(AwsCliCommandValidationStatus.VALIDATED, validation.status, "$name: status") + assertEquals(AwsCliOperationKind.CLOUD_FORMATION_CREATE, validation.operationKind, "$name: operation kind") assertEquals(listOf("AWS::S3::Bucket"), validation.resourceTypes, "$name: resource types") - assertEquals(AwsApiTemplateSource.SYNTHESIZED_CREATE, validation.templateSource, "$name: template source") + assertEquals(AwsCliTemplateSource.SYNTHESIZED_CREATE, validation.templateSource, "$name: template source") assertNotNull(validation.report, "$name: report must be present for a validated request") assertEquals("synthetic-bucket", synthesizedBucketName(validation.template), "$name: synthesized bucket name") perEngine[name] = validation @@ -512,14 +512,14 @@ class SmokeTest { } @Test - fun awsApiValidateTemplatePreservesExactBytes() { + fun awsCliValidateTemplatePreservesExactBytes() { // Distinctive whitespace and key order a reserialization would not reproduce. val templateBody = "{\n \"Resources\": {\n \"Bucket\": { \"Type\": \"AWS::S3::Bucket\" }\n }\n}".toByteArray() - val request = AwsApiRequest("cloudformation", "ValidateTemplate", mapOf("TemplateBody" to templateBody)) - for ((name, engine) in awsApiEngines) { - val validation = engine.validateAwsApiRequest(request) - assertEquals(AwsApiRequestValidationStatus.VALIDATED, validation.status, "$name: status") - assertEquals(AwsApiTemplateSource.TEMPLATE_BODY, validation.templateSource, "$name: template source") + val request = AwsCliCommand("cloudformation", "ValidateTemplate", mapOf("TemplateBody" to templateBody)) + for ((name, engine) in awsCliEngines) { + val validation = engine.validateAwsCliCommand(request) + assertEquals(AwsCliCommandValidationStatus.VALIDATED, validation.status, "$name: status") + assertEquals(AwsCliTemplateSource.TEMPLATE_BODY, validation.templateSource, "$name: template source") assertTrue( templateBody.contentEquals(validation.template), "$name: TemplateBody must be preserved byte-for-byte", @@ -528,8 +528,8 @@ class SmokeTest { } @Test - fun awsApiConservativelySkipsNestedDynamoDbFields() { - val request = AwsApiRequest( + fun awsCliConservativelySkipsNestedDynamoDbFields() { + val request = AwsCliCommand( "dynamodb", "CreateTable", mapOf( @@ -539,9 +539,9 @@ class SmokeTest { "BillingMode" to "PAY_PER_REQUEST", ), ) - for ((name, engine) in awsApiEngines) { - val validation = engine.validateAwsApiRequest(request) - assertEquals(AwsApiRequestValidationStatus.SKIPPED, validation.status, "$name: status") + for ((name, engine) in awsCliEngines) { + val validation = engine.validateAwsCliCommand(request) + assertEquals(AwsCliCommandValidationStatus.SKIPPED, validation.status, "$name: status") assertNull(validation.report, "$name: a skipped request must have no report") assertNull(validation.template, "$name: a skipped request must have no template") assertEquals(listOf("AWS::DynamoDB::Table"), validation.resourceTypes, "$name: resource type still identified") @@ -553,19 +553,19 @@ class SmokeTest { } @Test - fun awsApiDoesNotGuessNoncanonicalServiceAlias() { + fun awsCliDoesNotGuessNoncanonicalServiceAlias() { // CloudWatch's canonical botocore name is "cloudwatch"; "monitoring" is its // signing name. The core resolves the canonical name but never the alias. - val canonical = AwsApiRequest("cloudwatch", "PutMetricAlarm", mapOf("AlarmName" to "synthetic")) - val alias = AwsApiRequest("monitoring", "PutMetricAlarm", mapOf("AlarmName" to "synthetic")) - for ((name, engine) in awsApiEngines) { - val canonicalValidation = engine.validateAwsApiRequest(canonical) + val canonical = AwsCliCommand("cloudwatch", "PutMetricAlarm", mapOf("AlarmName" to "synthetic")) + val alias = AwsCliCommand("monitoring", "PutMetricAlarm", mapOf("AlarmName" to "synthetic")) + for ((name, engine) in awsCliEngines) { + val canonicalValidation = engine.validateAwsCliCommand(canonical) assertTrue( canonicalValidation.resourceTypes.contains("AWS::CloudWatch::Alarm"), "$name: canonical cloudwatch:PutMetricAlarm must identify AWS::CloudWatch::Alarm", ) - val aliasValidation = engine.validateAwsApiRequest(alias) - assertEquals(AwsApiRequestValidationStatus.SKIPPED, aliasValidation.status, "$name: alias status") + val aliasValidation = engine.validateAwsCliCommand(alias) + assertEquals(AwsCliCommandValidationStatus.SKIPPED, aliasValidation.status, "$name: alias status") assertFalse( aliasValidation.resourceTypes.contains("AWS::CloudWatch::Alarm"), "$name: signing alias 'monitoring' must not resolve to AWS::CloudWatch::Alarm", diff --git a/src/bindings-python/README.md b/src/bindings-python/README.md index bbff4868..c7ec38e5 100644 --- a/src/bindings-python/README.md +++ b/src/bindings-python/README.md @@ -56,18 +56,18 @@ the same template and config. `template` is a file path (`str` / `os.PathLike`) or raw `bytes`; `config` is an optional `ValidateConfig`. -### AWS API request validation +### AWS CLI command validation -Use `validate_aws_api_request` when the input is an AWS SDK-style request rather than a complete template. The +Use `validate_aws_cli_command` when the input is an AWS SDK-style request rather than a complete template. The validator classifies the operation, selects a CloudFormation resource type, models representable create/update state, and validates the resulting template entirely offline: ```python -from cloudformation_validate import AwsApiRequest, RegoEngine +from cloudformation_validate import AwsCliCommand, RegoEngine engine = RegoEngine() -result = engine.validate_aws_api_request( - AwsApiRequest( +result = engine.validate_aws_cli_command( + AwsCliCommand( service_name="s3", service_prefix="s3", operation_name="CreateBucket", @@ -83,7 +83,7 @@ else: print(result.status.name, result.reason) ``` -`AwsApiRequest.parameters` accepts nested mappings and sequences, scalars, `bytes`, and `datetime.datetime` values +`AwsCliCommand.parameters` accepts nested mappings and sequences, scalars, `bytes`, and `datetime.datetime` values without mutating the supplied mapping. `TemplateBody` bytes are validated exactly; `TemplateURL` is skipped because the validator does not perform network requests. The result always reports `status`, `operation_kind`, `template_source`, `resource_types`, and `reason`; skipped requests have `report is None`. The `template` field carries the exact bytes @@ -109,15 +109,15 @@ AWS CLI emits `provide-client-params..` before serializing o handler on the CLI's botocore session to validate the exact parameter dictionary without making another network call: ```python -from cloudformation_validate import AwsApiRequest, RegoEngine +from cloudformation_validate import AwsCliCommand, RegoEngine engine = RegoEngine() # construct once and reuse def validate_create_stack(params, model, **_kwargs): service = model.service_model - result = engine.validate_aws_api_request( - AwsApiRequest( + result = engine.validate_aws_cli_command( + AwsCliCommand( service_name=service.service_name, service_prefix=service.signing_name, operation_name=model.name, diff --git a/src/bindings-python/python/cloudformation_validate/__init__.py b/src/bindings-python/python/cloudformation_validate/__init__.py index 79041235..4230ec31 100644 --- a/src/bindings-python/python/cloudformation_validate/__init__.py +++ b/src/bindings-python/python/cloudformation_validate/__init__.py @@ -92,12 +92,12 @@ from .data_source import AdditionalSchemaSource from .schema_validator import SchemaValidatorConfig from .validation_engine import ( - AwsApiOperationKind, - AwsApiRequestContext as _NativeAwsApiRequest, - AwsApiRequestValidation, - AwsApiRequestValidationStatus, - AwsApiTemplateSource, - AwsApiValue as _NativeAwsApiValue, + AwsCliCommandContext as _NativeAwsCliCommand, + AwsCliCommandValidation, + AwsCliCommandValidationStatus, + AwsCliOperationKind, + AwsCliTemplateSource, + AwsCliValue as _NativeAwsCliValue, EngineConfig, EngineType, ExternalRuleSource, @@ -105,11 +105,11 @@ __all__ = [ "AdditionalSchemaSource", - "AwsApiOperationKind", - "AwsApiRequest", - "AwsApiRequestValidation", - "AwsApiRequestValidationStatus", - "AwsApiTemplateSource", + "AwsCliCommand", + "AwsCliCommandValidation", + "AwsCliCommandValidationStatus", + "AwsCliOperationKind", + "AwsCliTemplateSource", "CelEngine", "ConditionalNull", "ConditionalNullEntry", @@ -217,7 +217,7 @@ def file_to_external_rule_source(path: typing.Union[str, os.PathLike]) -> Extern return ExternalRuleSource(name=str(resolved), content=f.read()) -class AwsApiRequest: +class AwsCliCommand: """Service, operation, and request values for CloudFormation validation. ``service_name`` is the canonical botocore service name (for example ``"s3"`` @@ -257,48 +257,48 @@ def __init__( self.http_method = http_method self.is_read_only = is_read_only - def _to_native(self) -> _NativeAwsApiRequest: - return _NativeAwsApiRequest( + def _to_native(self) -> _NativeAwsCliCommand: + return _NativeAwsCliCommand( service_name=self.service_name, operation_name=self.operation_name, - parameters={name: _to_native_aws_api_value(value) for name, value in self.parameters.items()}, + parameters={name: _to_native_aws_cli_value(value) for name, value in self.parameters.items()}, service_prefix=self.service_prefix, http_method=self.http_method, is_read_only=self.is_read_only, ) -def _to_native_aws_api_value(value: object) -> _NativeAwsApiValue: +def _to_native_aws_cli_value(value: object) -> _NativeAwsCliValue: if value is None: - return _NativeAwsApiValue.NULL() + return _NativeAwsCliValue.NULL() if isinstance(value, bool): - return _NativeAwsApiValue.BOOLEAN(value=value) + return _NativeAwsCliValue.BOOLEAN(value=value) if isinstance(value, int): if -(2**63) <= value < 2**63: - return _NativeAwsApiValue.INTEGER(value=value) + return _NativeAwsCliValue.INTEGER(value=value) if 0 <= value < 2**64: - return _NativeAwsApiValue.UNSIGNED_INTEGER(value=value) - return _NativeAwsApiValue.UNSUPPORTED(type_name="integer outside the 64-bit request range") + return _NativeAwsCliValue.UNSIGNED_INTEGER(value=value) + return _NativeAwsCliValue.UNSUPPORTED(type_name="integer outside the 64-bit request range") if isinstance(value, float): if math.isfinite(value): - return _NativeAwsApiValue.NUMBER(value=value) - return _NativeAwsApiValue.UNSUPPORTED(type_name="non-finite floating-point number") + return _NativeAwsCliValue.NUMBER(value=value) + return _NativeAwsCliValue.UNSUPPORTED(type_name="non-finite floating-point number") if isinstance(value, str): - return _NativeAwsApiValue.STRING(value=value) + return _NativeAwsCliValue.STRING(value=value) if isinstance(value, (bytes, bytearray, memoryview)): - return _NativeAwsApiValue.BYTES(value=bytes(value)) + return _NativeAwsCliValue.BYTES(value=bytes(value)) if isinstance(value, datetime.datetime): - return _NativeAwsApiValue.STRING(value=value.isoformat()) + return _NativeAwsCliValue.STRING(value=value.isoformat()) if isinstance(value, Mapping): if not all(isinstance(name, str) for name in value): - return _NativeAwsApiValue.UNSUPPORTED(type_name="mapping with non-string keys") - return _NativeAwsApiValue.OBJECT( - entries={name: _to_native_aws_api_value(item) for name, item in value.items()} + return _NativeAwsCliValue.UNSUPPORTED(type_name="mapping with non-string keys") + return _NativeAwsCliValue.OBJECT( + entries={name: _to_native_aws_cli_value(item) for name, item in value.items()} ) if isinstance(value, (list, tuple)): - return _NativeAwsApiValue.ARRAY(items=[_to_native_aws_api_value(item) for item in value]) + return _NativeAwsCliValue.ARRAY(items=[_to_native_aws_cli_value(item) for item in value]) value_type = type(value) - return _NativeAwsApiValue.UNSUPPORTED(type_name=f"{value_type.__module__}.{value_type.__qualname__}") + return _NativeAwsCliValue.UNSUPPORTED(type_name=f"{value_type.__module__}.{value_type.__qualname__}") class Engine: @@ -332,18 +332,18 @@ def validate_template(self, template: Template, config: typing.Optional[Validate content, path = _template_bytes(template) return self._inner.validate_template(content, config if config is not None else ValidateConfig(), path) - def validate_aws_api_request( - self, request: AwsApiRequest, config: typing.Optional[ValidateConfig] = None - ) -> AwsApiRequestValidation: - """Classifies, models, and validates an AWS API request. + def validate_aws_cli_command( + self, request: AwsCliCommand, config: typing.Optional[ValidateConfig] = None + ) -> AwsCliCommandValidation: + """Classifies, models, and validates an AWS CLI command. - A skipped request has ``report is None`` and an explicit status and reason. + A skipped command has ``report is None`` and an explicit status and reason. The ``template`` field carries the exact bytes validated (the caller's original ``TemplateBody`` or the synthesized JSON), or ``None`` when skipped. """ - if not isinstance(request, AwsApiRequest): - raise TypeError("request must be an AwsApiRequest") - return self._inner.validate_aws_api_request( + if not isinstance(request, AwsCliCommand): + raise TypeError("request must be an AwsCliCommand") + return self._inner.validate_aws_cli_command( request._to_native(), config if config is not None else ValidateConfig() ) diff --git a/src/bindings-python/src/lib.rs b/src/bindings-python/src/lib.rs index 1857a0b8..e9d9a3de 100644 --- a/src/bindings-python/src/lib.rs +++ b/src/bindings-python/src/lib.rs @@ -24,8 +24,8 @@ pub use template_model::model::{ pub use template_model::resolver::{MapEntry, ParameterInfo, RefKind, ResolvedValue}; pub use template_model::{JsonValue, PseudoParameterOverrides, SourceSpan}; pub use validation_engine::{ - AwsApiOperationKind, AwsApiRequestContext, AwsApiRequestValidation, AwsApiRequestValidationStatus, - AwsApiTemplateSource, AwsApiValue, EngineConfig, EngineType, ExternalRuleSource, + AwsCliCommand, AwsCliCommandValidation, AwsCliCommandValidationStatus, AwsCliOperationKind, AwsCliTemplateSource, + AwsCliValue, EngineConfig, EngineType, ExternalRuleSource, }; pub use schema_validator::SchemaValidatorConfig; @@ -181,15 +181,15 @@ macro_rules! impl_py_engine { ) } - pub fn validate_aws_api_request( + pub fn validate_aws_cli_command( &self, - request: AwsApiRequestContext, + request: AwsCliCommand, config: ValidateConfig, - ) -> Result { + ) -> Result { validation_engine::catch_panics( || { let core_config = config.to_core(); - let validation = validation_engine::validate_aws_api_request( + let validation = validation_engine::validate_aws_cli_command( &self.engine, &self.schema_validator, &request, diff --git a/src/bindings-python/tests/smoke_test.py b/src/bindings-python/tests/smoke_test.py index 54eea8fa..2e8ffd9c 100644 --- a/src/bindings-python/tests/smoke_test.py +++ b/src/bindings-python/tests/smoke_test.py @@ -15,10 +15,10 @@ import cloudformation_validate._native as native_loader from cloudformation_validate import ( AdditionalSchemaSource, - AwsApiOperationKind, - AwsApiRequest, - AwsApiRequestValidationStatus, - AwsApiTemplateSource, + AwsCliCommand, + AwsCliCommandValidationStatus, + AwsCliOperationKind, + AwsCliTemplateSource, CelEngine, DetailLevel, EngineConfig, @@ -447,17 +447,17 @@ def test_multi_combined_custom_and_guard(self): self.assertEqual(description, actual_description, rule_id) -class AwsApiRequestTest(unittest.TestCase): +class AwsCliCommandTest(unittest.TestCase): def test_s3_create_bucket_synthesizes_on_both_engines(self): - request = AwsApiRequest("s3", "CreateBucket", {"Bucket": "synthetic-bucket"}) + request = AwsCliCommand("s3", "CreateBucket", {"Bucket": "synthetic-bucket"}) results = {} for engine in (REGO, CEL): name = engine.engine_name() - validation = engine.validate_aws_api_request(request) - self.assertEqual(AwsApiRequestValidationStatus.VALIDATED, validation.status, name) - self.assertEqual(AwsApiOperationKind.CLOUD_FORMATION_CREATE, validation.operation_kind, name) + validation = engine.validate_aws_cli_command(request) + self.assertEqual(AwsCliCommandValidationStatus.VALIDATED, validation.status, name) + self.assertEqual(AwsCliOperationKind.CLOUD_FORMATION_CREATE, validation.operation_kind, name) self.assertEqual(["AWS::S3::Bucket"], validation.resource_types, name) - self.assertEqual(AwsApiTemplateSource.SYNTHESIZED_CREATE, validation.template_source, name) + self.assertEqual(AwsCliTemplateSource.SYNTHESIZED_CREATE, validation.template_source, name) self.assertIsNotNone(validation.report, name) self.assertIsNotNone(validation.template, f"{name}: a validated request must carry template bytes") document = json.loads(validation.template) @@ -471,16 +471,16 @@ def test_s3_create_bucket_synthesizes_on_both_engines(self): def test_validate_template_preserves_exact_template_body_bytes(self): # Distinctive whitespace and key order a reserialization would not reproduce. template_body = b'{\n "Resources": {\n "Bucket": { "Type": "AWS::S3::Bucket" }\n }\n}' - request = AwsApiRequest("cloudformation", "ValidateTemplate", {"TemplateBody": template_body}) + request = AwsCliCommand("cloudformation", "ValidateTemplate", {"TemplateBody": template_body}) for engine in (REGO, CEL): name = engine.engine_name() - validation = engine.validate_aws_api_request(request) - self.assertEqual(AwsApiRequestValidationStatus.VALIDATED, validation.status, name) - self.assertEqual(AwsApiTemplateSource.TEMPLATE_BODY, validation.template_source, name) + validation = engine.validate_aws_cli_command(request) + self.assertEqual(AwsCliCommandValidationStatus.VALIDATED, validation.status, name) + self.assertEqual(AwsCliTemplateSource.TEMPLATE_BODY, validation.template_source, name) self.assertEqual(template_body, validation.template, f"{name}: TemplateBody must be preserved byte-for-byte") def test_conservatively_skips_nested_dynamodb_fields(self): - request = AwsApiRequest( + request = AwsCliCommand( "dynamodb", "CreateTable", { @@ -492,8 +492,8 @@ def test_conservatively_skips_nested_dynamodb_fields(self): ) for engine in (REGO, CEL): name = engine.engine_name() - validation = engine.validate_aws_api_request(request) - self.assertEqual(AwsApiRequestValidationStatus.SKIPPED, validation.status, name) + validation = engine.validate_aws_cli_command(request) + self.assertEqual(AwsCliCommandValidationStatus.SKIPPED, validation.status, name) self.assertIsNone(validation.report, name) self.assertIsNone(validation.template, name) self.assertEqual(["AWS::DynamoDB::Table"], validation.resource_types, name) @@ -502,14 +502,14 @@ def test_conservatively_skips_nested_dynamodb_fields(self): def test_does_not_guess_noncanonical_service_alias(self): # CloudWatch's canonical botocore name is "cloudwatch"; "monitoring" is its # signing name. The core resolves the canonical name but never the alias. - canonical = AwsApiRequest("cloudwatch", "PutMetricAlarm", {"AlarmName": "synthetic"}) - alias = AwsApiRequest("monitoring", "PutMetricAlarm", {"AlarmName": "synthetic"}) + canonical = AwsCliCommand("cloudwatch", "PutMetricAlarm", {"AlarmName": "synthetic"}) + alias = AwsCliCommand("monitoring", "PutMetricAlarm", {"AlarmName": "synthetic"}) for engine in (REGO, CEL): name = engine.engine_name() - canonical_validation = engine.validate_aws_api_request(canonical) + canonical_validation = engine.validate_aws_cli_command(canonical) self.assertIn("AWS::CloudWatch::Alarm", canonical_validation.resource_types, name) - alias_validation = engine.validate_aws_api_request(alias) - self.assertEqual(AwsApiRequestValidationStatus.SKIPPED, alias_validation.status, name) + alias_validation = engine.validate_aws_cli_command(alias) + self.assertEqual(AwsCliCommandValidationStatus.SKIPPED, alias_validation.status, name) self.assertNotIn("AWS::CloudWatch::Alarm", alias_validation.resource_types, name) self.assertIsNone(alias_validation.template, name) diff --git a/src/bindings-wasm/README.md b/src/bindings-wasm/README.md index 6a58975d..13d97b77 100644 --- a/src/bindings-wasm/README.md +++ b/src/bindings-wasm/README.md @@ -46,7 +46,7 @@ diagnostics for the same template and config. | Method | Returns | Description | |-------------------------------------------|---------------------------|------------------------------------------------------------------------------------------------------------------| | `validateTemplate(template, config?)` | `ValidationReport` | Validates and returns diagnostics. `config.detailLevel` (default `DETAILED`) sets how much per-diagnostic context is attached: `DETAILED` adds documentation URLs, rule descriptions, phase tags, and `ViolationContext`; `STANDARD` omits those enrichment fields | -| `validateAwsApiRequest(request, config?)` | `AwsApiRequestValidation` | Classifies, models, and validates one AWS API request entirely offline | +| `validateAwsCliCommand(request, config?)` | `AwsCliCommandValidation` | Classifies, models, and validates one AWS CLI command entirely offline | | `listRules()` | `RuleInfo[]` | Returns metadata for every built-in and loaded custom rule | | `engineName()` | `string` | `"rego"` or `"cel"` | | `free()` | `void` | Releases the engine's off-heap memory | @@ -107,20 +107,20 @@ const engine = new CelEngine({ }); ``` -## AWS API request validation +## AWS CLI command validation -`validateAwsApiRequest` classifies and validates an AWS API request against the same bundled CloudFormation schemas and +`validateAwsCliCommand` classifies and validates an AWS CLI command against the same bundled CloudFormation schemas and rules, without credentials or network access: ```typescript -import { AwsApiRequest, RegoEngine } from "@aws/cloudformation-validate"; +import { AwsCliCommand, RegoEngine } from "@aws/cloudformation-validate"; const engine = new RegoEngine(); try { - const request = new AwsApiRequest("s3", "CreateBucket", { + const request = new AwsCliCommand("s3", "CreateBucket", { Bucket: "my-bucket", }); - const validation = engine.validateAwsApiRequest(request); + const validation = engine.validateAwsCliCommand(request); console.log(validation.status, validation.resourceTypes); if (validation.template !== null) { console.log(Buffer.from(validation.template).toString("utf8")); @@ -141,7 +141,7 @@ arrays and plain objects, and `Uint8Array` (including Node.js `Buffer`) for byte explicitly rather than coerced. Synthesis is all-or-nothing: if any supplied resource-state field cannot be represented or has no proven mapping, the result is `SKIPPED` with no report or template. -`AwsApiRequestValidation.template` is a `Uint8Array | null`. For CloudFormation `TemplateBody`, it contains the exact +`AwsCliCommandValidation.template` is a `Uint8Array | null`. For CloudFormation `TemplateBody`, it contains the exact caller-provided bytes; synthesized requests contain the generated JSON template bytes. A `VALIDATED` result includes a standard report, while a `SKIPPED` result explains why validation was not attempted. diff --git a/src/bindings-wasm/src/lib.rs b/src/bindings-wasm/src/lib.rs index c37023b8..ee358a6a 100644 --- a/src/bindings-wasm/src/lib.rs +++ b/src/bindings-wasm/src/lib.rs @@ -6,8 +6,7 @@ use schema_validator::{SchemaValidator, SchemaValidatorConfig}; use serde::Deserialize; use template_model::{PseudoParameterOverrides, SemanticModel}; use validation_engine::{ - AwsApiRequestContext, EngineConfig, ValidationEngine, catch_panics, validate_aws_api_request, - validate_bytes_with_path, + AwsCliCommand, EngineConfig, ValidationEngine, catch_panics, validate_aws_cli_command, validate_bytes_with_path, }; use wasm_bindgen::prelude::*; @@ -167,19 +166,19 @@ macro_rules! wasm_engine { ) } - #[wasm_bindgen(js_name = "validateAwsApiRequest")] - pub fn validate_aws_api_request( + #[wasm_bindgen(js_name = "validateAwsCliCommand")] + pub fn validate_aws_cli_command( &self, request: JsValue, options: ValidateConfig, ) -> Result { catch_panics( || { - let request: AwsApiRequestContext = serde_wasm_bindgen::from_value(request) - .map_err(|error| JsValue::from_str(&format!("invalid AWS API request: {error}")))?; + let request: AwsCliCommand = serde_wasm_bindgen::from_value(request) + .map_err(|error| JsValue::from_str(&format!("invalid AWS CLI command: {error}")))?; let config = build_core_config(options); let validation = - validate_aws_api_request(&self.engine, &self.schema_validator, &request, config) + validate_aws_cli_command(&self.engine, &self.schema_validator, &request, config) .map_err(to_js_err)?; to_js(&validation) }, diff --git a/src/bindings-wasm/tests/smoke.test.ts b/src/bindings-wasm/tests/smoke.test.ts index ffeeff3b..7ea51725 100644 --- a/src/bindings-wasm/tests/smoke.test.ts +++ b/src/bindings-wasm/tests/smoke.test.ts @@ -6,7 +6,7 @@ import * as path from 'path'; const { RegoEngine, CelEngine, - AwsApiRequest, + AwsCliCommand, SchemaValidator, SchemaFile, TemplateModel, @@ -270,9 +270,9 @@ describe('invalid input', () => { }); }); -// ── AWS API request validation ────────────────────────────────────────────── +// ── AWS CLI command validation ────────────────────────────────────────────── -describe('AWS API request validation', () => { +describe('AWS CLI command validation', () => { const engines = [ ['rego', REGO], ['cel', CEL], @@ -286,11 +286,11 @@ describe('AWS API request validation', () => { } it('synthesizes canonical S3 CreateBucket state on both engines', () => { - const request = new AwsApiRequest('s3', 'CreateBucket', { Bucket: 'synthetic-bucket' }); + const request = new AwsCliCommand('s3', 'CreateBucket', { Bucket: 'synthetic-bucket' }); const validations: Record = {}; for (const [name, engine] of engines) { - const validation = engine.validateAwsApiRequest(request); + const validation = engine.validateAwsCliCommand(request); expect(validation.status, name).toBe('VALIDATED'); expect(validation.operationKind, name).toBe('CLOUD_FORMATION_CREATE'); expect(validation.resourceTypes, name).toEqual(['AWS::S3::Bucket']); @@ -311,12 +311,12 @@ describe('AWS API request validation', () => { const templateBody = Buffer.from( '{\n "Resources": {\n "Bucket": { "Type": "AWS::S3::Bucket" }\n }\n}', ); - const request = new AwsApiRequest('cloudformation', 'ValidateTemplate', { + const request = new AwsCliCommand('cloudformation', 'ValidateTemplate', { TemplateBody: templateBody, }); for (const [name, engine] of engines) { - const validation = engine.validateAwsApiRequest(request); + const validation = engine.validateAwsCliCommand(request); expect(validation.status, name).toBe('VALIDATED'); expect(validation.templateSource, name).toBe('TEMPLATE_BODY'); expect(validation.template, name).toBeInstanceOf(Uint8Array); @@ -325,7 +325,7 @@ describe('AWS API request validation', () => { }); it('conservatively skips unmapped nested DynamoDB state on both engines', () => { - const request = new AwsApiRequest('dynamodb', 'CreateTable', { + const request = new AwsCliCommand('dynamodb', 'CreateTable', { TableName: 'Synthetic', KeySchema: [{ AttributeName: 'id', KeyType: 'HASH' }], AttributeDefinitions: [{ AttributeName: 'id', AttributeType: 'S' }], @@ -333,7 +333,7 @@ describe('AWS API request validation', () => { }); for (const [name, engine] of engines) { - const validation = engine.validateAwsApiRequest(request); + const validation = engine.validateAwsCliCommand(request); expect(validation.status, name).toBe('SKIPPED'); expect(validation.report, name).toBeNull(); expect(validation.template, name).toBeNull(); @@ -343,17 +343,17 @@ describe('AWS API request validation', () => { }); it('never guesses the noncanonical CloudWatch signing alias', () => { - const canonical = new AwsApiRequest('cloudwatch', 'PutMetricAlarm', { + const canonical = new AwsCliCommand('cloudwatch', 'PutMetricAlarm', { AlarmName: 'synthetic', }); - const alias = new AwsApiRequest('monitoring', 'PutMetricAlarm', { + const alias = new AwsCliCommand('monitoring', 'PutMetricAlarm', { AlarmName: 'synthetic', }); for (const [name, engine] of engines) { - const canonicalValidation = engine.validateAwsApiRequest(canonical); + const canonicalValidation = engine.validateAwsCliCommand(canonical); expect(canonicalValidation.resourceTypes, name).toContain('AWS::CloudWatch::Alarm'); - const aliasValidation = engine.validateAwsApiRequest(alias); + const aliasValidation = engine.validateAwsCliCommand(alias); expect(aliasValidation.status, name).toBe('SKIPPED'); expect(aliasValidation.resourceTypes, name).not.toContain('AWS::CloudWatch::Alarm'); expect(aliasValidation.template, name).toBeNull(); @@ -361,13 +361,13 @@ describe('AWS API request validation', () => { }); it('preserves signed and unsigned 64-bit bigint values across the WASM boundary', () => { - const request = new AwsApiRequest('lambda', 'CreateFunction', { + const request = new AwsCliCommand('lambda', 'CreateFunction', { MemorySize: 18446744073709551615n, Timeout: -9223372036854775808n, }); for (const [name, engine] of engines) { - const validation = engine.validateAwsApiRequest(request); + const validation = engine.validateAwsCliCommand(request); expect(validation.status, name).toBe('VALIDATED'); expect(validation.template, name).toBeInstanceOf(Uint8Array); const template = Buffer.from(validation.template).toString('utf8'); @@ -377,12 +377,12 @@ describe('AWS API request validation', () => { }); it('marks unsupported request values conservatively instead of coercing them', () => { - const request = new AwsApiRequest('s3', 'CreateBucket', { + const request = new AwsCliCommand('s3', 'CreateBucket', { Bucket: Symbol('not-a-bucket-name'), }); for (const [name, engine] of engines) { - const validation = engine.validateAwsApiRequest(request); + const validation = engine.validateAwsCliCommand(request); expect(validation.status, name).toBe('SKIPPED'); expect(validation.report, name).toBeNull(); expect(validation.template, name).toBeNull(); @@ -399,10 +399,10 @@ describe('AWS API request validation', () => { throw new Error('request accessors must not run'); }, }); - const request = new AwsApiRequest('s3', 'CreateBucket', { Bucket: state }); + const request = new AwsCliCommand('s3', 'CreateBucket', { Bucket: state }); for (const [name, engine] of engines) { - const validation = engine.validateAwsApiRequest(request); + const validation = engine.validateAwsCliCommand(request); expect(validation.status, name).toBe('SKIPPED'); expect(validation.template, name).toBeNull(); } @@ -419,10 +419,10 @@ describe('AWS API request validation', () => { throw new Error('request accessors must not run'); }, }); - const request = new AwsApiRequest('s3', 'CreateBucket', { Bucket: state }); + const request = new AwsCliCommand('s3', 'CreateBucket', { Bucket: state }); for (const [name, engine] of engines) { - const validation = engine.validateAwsApiRequest(request); + const validation = engine.validateAwsCliCommand(request); expect(validation.status, name).toBe('SKIPPED'); expect(validation.template, name).toBeNull(); } @@ -432,10 +432,10 @@ describe('AWS API request validation', () => { it('skips cyclic request state conservatively', () => { const state: Record = Object.create(null); state.cycle = state; - const request = new AwsApiRequest('s3', 'CreateBucket', { Bucket: state }); + const request = new AwsCliCommand('s3', 'CreateBucket', { Bucket: state }); for (const [name, engine] of engines) { - const validation = engine.validateAwsApiRequest(request); + const validation = engine.validateAwsCliCommand(request); expect(validation.status, name).toBe('SKIPPED'); expect(validation.template, name).toBeNull(); } @@ -472,16 +472,16 @@ describe('AWS API request validation', () => { }, }); - const dateRequest = new AwsApiRequest('s3', 'CreateBucket', { Bucket: date }); - const bytesRequest = new AwsApiRequest('cloudformation', 'ValidateTemplate', { + const dateRequest = new AwsCliCommand('s3', 'CreateBucket', { Bucket: date }); + const bytesRequest = new AwsCliCommand('cloudformation', 'ValidateTemplate', { TemplateBody: templateBody, }); for (const [name, engine] of engines) { - const dateValidation = engine.validateAwsApiRequest(dateRequest); + const dateValidation = engine.validateAwsCliCommand(dateRequest); expect(dateValidation.status, name).toBe('VALIDATED'); expect(Buffer.from(dateValidation.template).toString('utf8'), name).toContain('2024-01-02T03:04:05.000Z'); - const bytesValidation = engine.validateAwsApiRequest(bytesRequest); + const bytesValidation = engine.validateAwsCliCommand(bytesRequest); expect(bytesValidation.status, name).toBe('VALIDATED'); expect(Buffer.from(bytesValidation.template), name).toEqual(expectedTemplateBody); } @@ -489,13 +489,13 @@ describe('AWS API request validation', () => { }); it('rejects invalid top-level parameter dictionaries without dropping keys', () => { - expect(() => new AwsApiRequest('s3', 'CreateBucket', [] as unknown as Record)).toThrow( + expect(() => new AwsCliCommand('s3', 'CreateBucket', [] as unknown as Record)).toThrow( 'parameters must be a plain object', ); const symbolParameters: Record = Object.create(null); symbolParameters[Symbol('Bucket')] = 'synthetic-bucket'; - expect(() => new AwsApiRequest('s3', 'CreateBucket', symbolParameters as Record)).toThrow( + expect(() => new AwsCliCommand('s3', 'CreateBucket', symbolParameters as Record)).toThrow( 'request parameter names must be strings', ); }); diff --git a/src/bindings-wasm/ts/index.ts b/src/bindings-wasm/ts/index.ts index 397c36fc..8e04f1e6 100644 --- a/src/bindings-wasm/ts/index.ts +++ b/src/bindings-wasm/ts/index.ts @@ -74,32 +74,32 @@ export type { export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }; -export type AwsApiOperationKind = +export type AwsCliOperationKind = | 'READ_ONLY' | 'CLOUD_FORMATION_CREATE' | 'CLOUD_FORMATION_UPDATE' | 'CLOUD_FORMATION_DELETE' | 'DATA_PLANE_MUTATION' | 'UNMAPPED_MUTATION'; -export type AwsApiRequestValidationStatus = 'VALIDATED' | 'SKIPPED'; -export type AwsApiTemplateSource = +export type AwsCliCommandValidationStatus = 'VALIDATED' | 'SKIPPED'; +export type AwsCliTemplateSource = 'TEMPLATE_BODY' | 'CLOUD_CONTROL_DESIRED_STATE' | 'SYNTHESIZED_CREATE' | 'SYNTHESIZED_UPDATE'; -export interface AwsApiRequestOptions { +export interface AwsCliCommandOptions { servicePrefix?: string; httpMethod?: string; isReadOnly?: boolean; } /** - * Service, operation, and input values for one AWS API request. + * Service, operation, and input values for one AWS CLI command. * * `serviceName` is the canonical botocore service name and is normalized only * for ASCII case. Callers adapting an SDK request must translate its native * service identity before constructing this request; endpoint and signing-name * aliases are never guessed by the validation core. */ -export class AwsApiRequest { +export class AwsCliCommand { public readonly parameters: Record; public readonly servicePrefix?: string; public readonly httpMethod?: string; @@ -109,7 +109,7 @@ export class AwsApiRequest { public readonly serviceName: string, public readonly operationName: string, parameters: Record, - options: AwsApiRequestOptions = {}, + options: AwsCliCommandOptions = {}, ) { if (!isPlainRecord(parameters)) { throw new TypeError('parameters must be a plain object with string keys'); @@ -132,17 +132,17 @@ export class AwsApiRequest { } } -export interface AwsApiRequestValidation { - operationKind: AwsApiOperationKind; - status: AwsApiRequestValidationStatus; - templateSource: AwsApiTemplateSource | null; +export interface AwsCliCommandValidation { + operationKind: AwsCliOperationKind; + status: AwsCliCommandValidationStatus; + templateSource: AwsCliTemplateSource | null; resourceTypes: string[]; reason: string; report: ValidationReport | null; template: Uint8Array | null; } -type WireAwsApiValue = +type WireAwsCliValue = | { type: 'NULL' } | { type: 'BOOLEAN'; value: boolean } | { type: 'INTEGER'; value: number | bigint } @@ -150,20 +150,20 @@ type WireAwsApiValue = | { type: 'NUMBER'; value: number } | { type: 'STRING'; value: string } | { type: 'BYTES'; value: number[] } - | { type: 'ARRAY'; items: WireAwsApiValue[] } - | { type: 'OBJECT'; entries: Record } + | { type: 'ARRAY'; items: WireAwsCliValue[] } + | { type: 'OBJECT'; entries: Record } | { type: 'UNSUPPORTED'; type_name: string }; -interface WireAwsApiRequest { +interface WireAwsCliCommand { serviceName: string; operationName: string; - parameters: Record; + parameters: Record; servicePrefix?: string; httpMethod?: string; isReadOnly?: boolean; } -interface WireAwsApiRequestValidation extends Omit { +interface WireAwsCliCommandValidation extends Omit { template?: number[] | Uint8Array | null; } @@ -183,11 +183,11 @@ function isPlainRecord(value: unknown): value is Record { return prototype === Object.prototype || prototype === null; } -function unsupportedValue(typeName: string): WireAwsApiValue { +function unsupportedValue(typeName: string): WireAwsCliValue { return { type: 'UNSUPPORTED', type_name: typeName }; } -function encodeAwsApiValue(value: unknown, depth = 0, ancestors = new Set()): WireAwsApiValue { +function encodeAwsCliValue(value: unknown, depth = 0, ancestors = new Set()): WireAwsCliValue { if (depth > MAX_REQUEST_VALUE_DEPTH) { return unsupportedValue('recursion depth exceeded'); } @@ -256,7 +256,7 @@ function encodeAwsApiValue(value: unknown, depth = 0, ancestors = new Set; + const entries = Object.create(null) as Record; for (const key of Reflect.ownKeys(value)) { if (typeof key !== 'string') { return unsupportedValue('mapping with non-string keys'); @@ -287,7 +287,7 @@ function encodeAwsApiValue(value: unknown, depth = 0, ancestors = new Set; +function toWireAwsCliCommand(request: AwsCliCommand): WireAwsCliCommand { + const parameters = Object.create(null) as Record; for (const [name, value] of Object.entries(request.parameters)) { try { - parameters[name] = encodeAwsApiValue(value); + parameters[name] = encodeAwsCliValue(value); } catch { parameters[name] = unsupportedValue('request value inspection failed'); } @@ -316,7 +316,7 @@ function toWireAwsApiRequest(request: AwsApiRequest): WireAwsApiRequest { }; } -function fromWireAwsApiRequestValidation(validation: WireAwsApiRequestValidation): AwsApiRequestValidation { +function fromWireAwsCliCommandValidation(validation: WireAwsCliCommandValidation): AwsCliCommandValidation { const template = validation.template; return { ...validation, @@ -328,7 +328,7 @@ function fromWireAwsApiRequestValidation(validation: WireAwsApiRequestValidation export interface Engine { validateTemplate(template: TemplateFile, config?: ValidateConfig): ValidationReport; - validateAwsApiRequest(request: AwsApiRequest, config?: ValidateConfig): AwsApiRequestValidation; + validateAwsCliCommand(request: AwsCliCommand, config?: ValidateConfig): AwsCliCommandValidation; listRules(): RuleInfo[]; engineName(): string; free(): void; @@ -495,7 +495,7 @@ export class SchemaValidator { interface WasmEngineInstance { validateTemplate(template: Uint8Array, options: ValidateConfig, filePath: string): ValidationReport; - validateAwsApiRequest(request: WireAwsApiRequest, options: ValidateConfig): WireAwsApiRequestValidation; + validateAwsCliCommand(request: WireAwsCliCommand, options: ValidateConfig): WireAwsCliCommandValidation; listRules(): RuleInfo[]; engineName(): string; free(): void; @@ -515,12 +515,12 @@ function createEngineClass( return this.inner.validateTemplate(template.readBytes(), config ?? {}, template.path); } - validateAwsApiRequest(request: AwsApiRequest, config?: ValidateConfig): AwsApiRequestValidation { - if (!(request instanceof AwsApiRequest)) { - throw new TypeError('request must be an AwsApiRequest'); + validateAwsCliCommand(request: AwsCliCommand, config?: ValidateConfig): AwsCliCommandValidation { + if (!(request instanceof AwsCliCommand)) { + throw new TypeError('request must be an AwsCliCommand'); } - return fromWireAwsApiRequestValidation( - this.inner.validateAwsApiRequest(toWireAwsApiRequest(request), config ?? {}), + return fromWireAwsCliCommandValidation( + this.inner.validateAwsCliCommand(toWireAwsCliCommand(request), config ?? {}), ); } diff --git a/src/data-source/Cargo.toml b/src/data-source/Cargo.toml index 00964928..7087d1c0 100644 --- a/src/data-source/Cargo.toml +++ b/src/data-source/Cargo.toml @@ -24,6 +24,11 @@ name = "generate" path = "src/generate.rs" required-features = ["maintenance"] +[[example]] +name = "generate_aws_cli_catalog" +path = "examples/generate_aws_cli_catalog.rs" +required-features = ["maintenance"] + [[example]] name = "embedding_format_bench" path = "src/embedding_format_bench.rs" diff --git a/src/data-source/build.rs b/src/data-source/build.rs index e6e605f5..3e1ce2be 100644 --- a/src/data-source/build.rs +++ b/src/data-source/build.rs @@ -25,7 +25,7 @@ const GENERATED_JSON: &[(&str, &str)] = &[ ("data/getatt_attributes.json", "GETATT_ATTRIBUTES"), ("data/known_resource_types.json", "KNOWN_RESOURCE_TYPES"), ("data/stateful_resource_types.json", "STATEFUL_RESOURCE_TYPES"), - ("data/aws_api_operation_catalog.json", "AWS_API_OPERATION_CATALOG"), + ("data/aws_cli_operation_catalog.json", "AWS_CLI_OPERATION_CATALOG"), ("data/retention_period_requirements.json", "RETENTION_PERIOD_REQUIREMENTS"), ("data/codepipeline_action_artifact_counts.json", "CODEPIPELINE_ACTION_ARTIFACT_COUNTS"), ("data/aws_rds_dbinstance_dbinstanceclass_enum.json", "AWS_RDS_DBINSTANCE_DBINSTANCECLASS_ENUM"), diff --git a/src/data-source/generated/data/aws_api_operation_catalog.json b/src/data-source/generated/data/aws_cli_operation_catalog.json similarity index 99% rename from src/data-source/generated/data/aws_api_operation_catalog.json rename to src/data-source/generated/data/aws_cli_operation_catalog.json index 22a69e0d..0e14e9dc 100644 --- a/src/data-source/generated/data/aws_api_operation_catalog.json +++ b/src/data-source/generated/data/aws_cli_operation_catalog.json @@ -16866,6 +16866,10 @@ "source": "NetworkLoadBalancerArns", "target": "NetworkLoadBalancerArns" }, + { + "source": "PrivateDnsName", + "target": "PrivateDnsName" + }, { "source": "SupportedIpAddressTypes", "target": "SupportedIpAddressTypes" @@ -19748,6 +19752,10 @@ { "cfn_type": "AWS::ElementalInference::Feed", "mappings": [ + { + "source": "accessRoleArn", + "target": "AccessRoleArn" + }, { "source": "name", "target": "Name" @@ -31100,6 +31108,10 @@ "source": "StartoverWindowSeconds", "target": "StartoverWindowSeconds" }, + { + "source": "StreamNameOutputMode", + "target": "StreamNameOutputMode" + }, { "source": "Tags", "target": "Tags" @@ -34342,6 +34354,13 @@ "phase": "delete", "service": "opensearchserverless" }, + { + "cfn_type": "AWS::OpenSearchService::Application", + "mappings": [], + "operation": "DeleteApplication", + "phase": "delete", + "service": "opensearch" + }, { "cfn_type": "AWS::OpenSearchService::Domain", "mappings": [ @@ -40220,6 +40239,37 @@ "phase": "create", "service": "s3control" }, + { + "cfn_type": "AWS::S3Outposts::Bucket", + "mappings": [], + "operation": "DeleteBucket", + "phase": "delete", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3Outposts::BucketPolicy", + "mappings": [ + { + "source": "Bucket", + "target": "Bucket" + } + ], + "operation": "PutBucketPolicy", + "phase": "create", + "service": "s3control" + }, + { + "cfn_type": "AWS::S3Outposts::BucketPolicy", + "mappings": [ + { + "source": "Bucket", + "target": "Bucket" + } + ], + "operation": "DeleteBucketPolicy", + "phase": "delete", + "service": "s3control" + }, { "cfn_type": "AWS::S3Outposts::Endpoint", "mappings": [ @@ -42484,6 +42534,10 @@ { "source": "EndpointConfigName", "target": "EndpointConfigName" + }, + { + "source": "EndpointName", + "target": "EndpointName" } ], "operation": "CreateEndpoint", @@ -42492,7 +42546,12 @@ }, { "cfn_type": "AWS::SageMaker::Endpoint", - "mappings": [], + "mappings": [ + { + "source": "EndpointName", + "target": "EndpointName" + } + ], "operation": "DeleteEndpoint", "phase": "delete", "service": "sagemaker" @@ -47921,8 +47980,8 @@ "source": { "botocore_service_count": 431, "botocore_version": "2.0.0dev155", - "compiled_schemas_sha256": "76bcee337b3f10bb9d72cbbe836d0c89afbb96a05c2d37c18eebf65685fbabb5", - "compiled_type_count": 1731, + "compiled_schemas_sha256": "9f33ce28894f17ac771051a55696e18e04596cfdf01014ec13edcafd75524299", + "compiled_type_count": 1766, "provider_schemas_sha256": "929767f603b6744fda6741ba5ca5ec42b54847a4f9a07402369ebecd8ce82248", "provider_type_count": 1729 } diff --git a/src/data-source/scripts/generate_aws_api_catalog.py b/src/data-source/scripts/generate_aws_cli_catalog.py similarity index 98% rename from src/data-source/scripts/generate_aws_api_catalog.py rename to src/data-source/scripts/generate_aws_cli_catalog.py index 7d7f798d..a506abdd 100644 --- a/src/data-source/scripts/generate_aws_api_catalog.py +++ b/src/data-source/scripts/generate_aws_cli_catalog.py @@ -40,18 +40,17 @@ validated as SKIPPED at runtime, never guessed. Usage: - python3 generate_aws_api_catalog.py \ + python3 generate_aws_cli_catalog.py \ --botocore-root /path/to/botocore \ --provider-schemas schemas-standard.zip \ --compiled-schemas ../generated/schema-validator/compiled_schemas.json \ - --output ../generated/data/aws_api_operation_catalog.json + --output ../generated/data/aws_cli_operation_catalog.json """ import argparse import hashlib import importlib import json -import subprocess import sys import zipfile from collections import defaultdict @@ -1086,23 +1085,8 @@ def _render_generation_report( return lines -def _run_unit_tests(): - test_file = Path(__file__).with_name('test_generate_aws_api_catalog.py') - completed = subprocess.run( - [sys.executable, '-m', 'unittest', '-v', test_file.stem], - cwd=test_file.parent, - check=False, - ) - if completed.returncode != 0: - raise SystemExit( - 'catalog generator unit tests failed with exit code ' - f'{completed.returncode}' - ) - - def main(): args = _parse_args() - _run_unit_tests() if not args.botocore_root.is_dir(): raise SystemExit( f'botocore root directory not found: {args.botocore_root}' diff --git a/src/data-source/scripts/test_generate_aws_api_catalog.py b/src/data-source/scripts/test_generate_aws_api_catalog.py deleted file mode 100644 index ff659984..00000000 --- a/src/data-source/scripts/test_generate_aws_api_catalog.py +++ /dev/null @@ -1,804 +0,0 @@ -#!/usr/bin/env python3 -import json -import tempfile -import unittest -from pathlib import Path - -import generate_aws_api_catalog as catalog - - -class Shape: - def __init__(self, type_name, *, member=None, value=None, metadata=None, - serialization=None): - self.type_name = type_name - self.member = member - self.value = value - self.metadata = metadata or {} - self.serialization = serialization or {} - - -class ExactServiceResolutionTest(unittest.TestCase): - """Resolution is literal: a case-insensitive identity or a reviewed override. - - Punctuation is significant and substrings never match, so an IAM prefix - denotes a service only when it equals a service identity exactly (ignoring - case) or is a reviewed action-prefix override. - """ - - @staticmethod - def _index(by_identity, operations): - index = catalog.BotocoreIndex.__new__(catalog.BotocoreIndex) - index._by_identity = {key: set(services) for key, services in by_identity.items()} - index._operations = operations - index._identities = {service: set() for service in operations} - return index - - def test_identity_key_lowercases_and_preserves_punctuation(self): - # The identity key folds case only; _normalize is the lossy sibling used - # for relatedness. Keeping them distinct is what stops punctuation from - # being erased into a false identity match. - self.assertEqual('s3-control', catalog._identity_key('S3-Control')) - self.assertEqual('kafka-cluster', catalog._identity_key('Kafka-Cluster')) - self.assertEqual('s3control', catalog._normalize('S3-Control')) - - def test_substring_service_identity_no_longer_resolves(self): - # A prefix that is only a *substring* of a real identity resolves to - # nothing: 'mq' is contained in 'amazonmq', but 'amazonmq' is neither an - # identity nor an override. - index = self._index({'mq': {'mq'}}, {'mq': {'createbroker': 'CreateBroker'}}) - self.assertEqual(set(), index.resolve('amazonmq', 'CreateBroker')) - # 'es' is contained in many identities; without a substring fallback an - # unrelated 'es'-containing service is never reached. - index = self._index( - {'esoteric': {'esoteric'}}, {'esoteric': {'deleteapplication': 'DeleteApplication'}} - ) - self.assertEqual(set(), index.resolve('es', 'DeleteApplication')) - - def test_punctuation_variant_does_not_resolve(self): - # The identity is literally 's3-control'; the hyphen is preserved, so the - # punctuation-free prefix 's3control' is a different key and reaches no - # service. Only the exact-punctuation prefix resolves. - index = self._index( - {'s3-control': {'s3control'}}, - {'s3control': {'deletebucketpolicy': 'DeleteBucketPolicy'}}, - ) - self.assertEqual(set(), index.resolve('s3control', 'DeleteBucketPolicy')) - self.assertEqual( - {('s3control', 'DeleteBucketPolicy')}, - index.resolve('s3-control', 'DeleteBucketPolicy'), - ) - - def test_exact_identity_resolves(self): - index = self._index({'s3': {'s3'}}, {'s3': {'createbucket': 'CreateBucket'}}) - self.assertEqual({('s3', 'CreateBucket')}, index.resolve('s3', 'CreateBucket')) - - def test_reviewed_action_prefix_overrides_resolve(self): - index = self._index( - { - 'kafka': {'kafka'}, - 's3control': {'s3control'}, - 's3-outposts': {'s3outposts'}, - }, - { - 'kafka': {'createtopic': 'CreateTopic'}, - 's3control': {'deletebucket': 'DeleteBucket'}, - 's3outposts': {'createendpoint': 'CreateEndpoint'}, - }, - ) - # 'kafka-cluster' is not a botocore identity, so it resolves only through - # the reviewed override to 'kafka'. - self.assertEqual({('kafka', 'CreateTopic')}, index.resolve('kafka-cluster', 'CreateTopic')) - # The literal 's3-outposts' bucket action resolves only to s3control (the - # review); no 's3'-containing service is reached by containment. - self.assertEqual({('s3control', 'DeleteBucket')}, index.resolve('s3-outposts', 'DeleteBucket')) - # Where the s3outposts service itself owns the operation, its exact - # 's3-outposts' endpoint-prefix identity resolves alongside the review. - self.assertEqual({('s3outposts', 'CreateEndpoint')}, index.resolve('s3-outposts', 'CreateEndpoint')) - - def test_punctuation_free_override_spelling_does_not_resolve(self): - # Override keys are literal ('s3-outposts', 'kafka-cluster'), so the - # punctuation-free spellings that punctuation-folding once produced are - # not keys and resolve to nothing. - index = self._index( - {'s3control': {'s3control'}, 'kafka': {'kafka'}}, - {'s3control': {'deletebucket': 'DeleteBucket'}, 'kafka': {'createtopic': 'CreateTopic'}}, - ) - self.assertEqual(set(), index.resolve('s3outposts', 'DeleteBucket')) - self.assertEqual(set(), index.resolve('kafkacluster', 'CreateTopic')) - - def test_override_is_silent_when_service_lacks_operation(self): - index = self._index({'kafka': {'kafka'}}, {'kafka': {'createcluster': 'CreateCluster'}}) - self.assertEqual(set(), index.resolve('kafka-cluster', 'CreateTopic')) - - -class PropertyRenameAllowlistTest(unittest.TestCase): - """Renames are accepted only in a fully reviewed context.""" - - @staticmethod - def _mappings(member, target, cfn_type, service, operation, resource_segment='thing'): - members = {member: Shape('string')} - property_schemas = {target: {'type': 'string'}} - writable_by_lower = {target.lower(): target} - return catalog._property_mappings( - members, property_schemas, writable_by_lower, resource_segment, {}, - cfn_type, service, operation, - ) - - def test_case_only_same_identifier_mapping_is_always_allowed(self): - # Member and property are the same identifier differing only in case. - result = self._mappings('bucketname', 'BucketName', 'AWS::Any::Type', 'any', 'AnyOp') - self.assertEqual([('bucketname', 'BucketName')], result) - - def test_reviewed_rename_in_exact_context_is_accepted(self): - result = self._mappings('Bucket', 'BucketName', 'AWS::S3::Bucket', 's3', 'CreateBucket') - self.assertEqual([('Bucket', 'BucketName')], result) - - def test_reviewed_name_to_segment_rename_is_accepted(self): - result = self._mappings( - 'Name', 'TopicName', 'AWS::SNS::Topic', 'sns', 'CreateTopic', resource_segment='topic' - ) - self.assertEqual([('Name', 'TopicName')], result) - - def test_unreviewed_rename_is_rejected(self): - # Same shape of rename, but this (cfn_type, service, operation) tuple was - # never reviewed, so the resource-name transform must not be synthesized. - self.assertEqual([], self._mappings('Bucket', 'BucketName', 'AWS::Other::Thing', 'other', 'CreateThing')) - - def test_reviewed_rename_rejected_when_any_context_field_changes(self): - # Each field individually differs from the reviewed S3 bucket entry. - self.assertEqual([], self._mappings('Bucket', 'BucketName', 'AWS::S3::AccessPoint', 's3', 'CreateBucket')) - self.assertEqual([], self._mappings('Bucket', 'BucketName', 'AWS::S3::Bucket', 's3control', 'CreateBucket')) - self.assertEqual([], self._mappings('Bucket', 'BucketName', 'AWS::S3::Bucket', 's3', 'PutBucket')) - - -class SegmentRelatednessTest(unittest.TestCase): - """Service relatedness is exact: an alias or exact identity, never substring containment.""" - - @staticmethod - def _index(identities): - index = catalog.BotocoreIndex.__new__(catalog.BotocoreIndex) - index._identities = {service: set(ids) for service, ids in identities.items()} - index._by_identity = {} - index._operations = {} - return index - - def test_containment_without_alias_is_unrelated(self): - # 'mq' is a substring of segment 'amazonmq', but without an explicit alias - # the service is unrelated: the old containment tier no longer applies. - index = self._index({'mq': {'mq'}}) - self.assertIsNone(index.identity_tier('mq', 'mq', {'amazonmq'})) - - def test_exact_segment_name_is_strongest_tier(self): - index = self._index({'kafka': {'kafka'}}) - self.assertEqual(0, index.identity_tier('kafka', 'kafka', {'msk', 'kafka'})) - - def test_reviewed_alias_relates_exactly(self): - # The reviewed 'amazonmq' -> 'mq' alias makes the exact 'mq' identity relate. - index = self._index({'mq': {'mq'}}) - aliases = {'amazonmq'} - aliases.update(catalog.SEGMENT_ALIASES.get('amazonmq', ())) - self.assertEqual(0, index.identity_tier('mq', 'mq', aliases)) - - def test_alias_table_values_are_identity_tuples(self): - # Every entry is a tuple of normalized identities so one segment can relate - # to more than one service (for example cognito). - for segment, identities in catalog.SEGMENT_ALIASES.items(): - self.assertIsInstance(identities, tuple, segment) - self.assertTrue(all(isinstance(identity, str) for identity in identities), segment) - self.assertIn('cognitoidp', catalog.SEGMENT_ALIASES['cognito']) - self.assertIn('cognitoidentity', catalog.SEGMENT_ALIASES['cognito']) - - -class CatalogGeneratorTest(unittest.TestCase): - def test_unreviewed_collision_is_dropped(self): - adapters = [ - { - "service": "quicksight", - "operation": "CreateTopic", - "cfn_type": "AWS::QuickSight::Topic", - }, - { - "service": "quicksight", - "operation": "CreateTopic", - "cfn_type": "AWS::QuickSight::TopicV2", - }, - ] - - kept, dropped = catalog._enforce_global_uniqueness(adapters) - - self.assertEqual([], kept) - self.assertEqual(2, len(dropped)) - - def test_reviewed_collision_keeps_only_preferred_type(self): - adapters = [ - { - "service": "dynamodb", - "operation": "CreateTable", - "cfn_type": "AWS::DynamoDB::GlobalTable", - }, - { - "service": "dynamodb", - "operation": "CreateTable", - "cfn_type": "AWS::DynamoDB::Table", - }, - ] - - kept, dropped = catalog._enforce_global_uniqueness(adapters) - - self.assertEqual(["AWS::DynamoDB::Table"], [entry["cfn_type"] for entry in kept]) - self.assertEqual(["AWS::DynamoDB::GlobalTable"], [entry["cfn_type"] for entry in dropped]) - - def test_runtime_safe_mapping_accepts_only_serializable_shapes(self): - definitions = { - "Tag": { - "type": "object", - "properties": {"Key": {"type": "string"}, "Value": {"type": "string"}}, - } - } - tags = {"type": "array", "items": {"ref_name": "Tag"}} - strings = {"type": "array", "items": {"type": "string"}} - - self.assertTrue( - catalog._is_runtime_safe_mapping(Shape("string"), {"type": "string"}, {}, "Name") - ) - self.assertTrue( - catalog._is_runtime_safe_mapping( - Shape("list", member=Shape("string")), strings, {}, "Names" - ) - ) - self.assertTrue( - catalog._is_runtime_safe_mapping( - Shape("map", value=Shape("string")), tags, definitions, "Tags" - ) - ) - self.assertFalse( - catalog._is_runtime_safe_mapping( - Shape("map", value=Shape("integer")), tags, definitions, "Tags" - ) - ) - self.assertFalse( - catalog._is_runtime_safe_mapping( - Shape("map", value=Shape("string")), tags, definitions, "Labels" - ) - ) - self.assertFalse( - catalog._is_runtime_safe_mapping(Shape("structure"), {"type": "object"}, {}, "Config") - ) - self.assertFalse( - catalog._is_runtime_safe_mapping( - Shape("list", member=Shape("structure")), - {"type": "array", "items": {"type": "object"}}, - {}, - "Configs", - ) - ) - - def test_tag_map_rejects_additional_required_target_field(self): - definitions = { - "Tag": { - "type": "object", - "properties": { - "Key": {"type": "string"}, - "Value": {"type": "string"}, - "PropagateAtLaunch": {"type": "boolean"}, - }, - "required": ["Key", "Value", "PropagateAtLaunch"], - } - } - tags = {"type": "array", "items": {"ref_name": "Tag"}} - - self.assertFalse( - catalog._is_runtime_safe_mapping( - Shape("map", value=Shape("string")), tags, definitions, "Tags" - ) - ) - - def test_tag_map_rejects_additional_required_alternative_field(self): - definitions = { - "Tag": { - "type": "object", - "properties": {"Key": {"type": "string"}}, - "required": ["Key"], - "any_of": [ - { - "properties": { - "Value": {"type": "string"}, - "PropagateAtLaunch": {"type": "boolean"}, - }, - "required": ["Value", "PropagateAtLaunch"], - } - ], - } - } - tags = {"type": "array", "items": {"ref_name": "Tag"}} - - self.assertFalse( - catalog._is_runtime_safe_mapping( - Shape("map", value=Shape("string")), tags, definitions, "Tags" - ) - ) - - def test_tag_map_accepts_unambiguous_key_value_alternative(self): - definitions = { - "Tag": { - "one_of": [ - { - "type": "object", - "properties": { - "Key": {"type": "string"}, - "Value": {"type": "string"}, - }, - "required": ["Key", "Value"], - "additional_properties": False, - }, - { - "type": "object", - "properties": { - "TagKey": {"type": "string"}, - "TagValue": {"type": "string"}, - }, - "required": ["TagKey", "TagValue"], - "additional_properties": False, - }, - ] - } - } - tags = {"type": "array", "items": {"ref_name": "Tag"}} - - self.assertTrue( - catalog._is_runtime_safe_mapping( - Shape("map", value=Shape("string")), tags, definitions, "Tags" - ) - ) - - def test_tag_map_rejects_ambiguous_key_value_alternatives(self): - key_value_branch = { - "type": "object", - "properties": { - "Key": {"type": "string"}, - "Value": {"type": "string"}, - }, - "required": ["Key", "Value"], - } - definitions = { - "Tag": {"one_of": [key_value_branch, dict(key_value_branch)]} - } - tags = {"type": "array", "items": {"ref_name": "Tag"}} - - self.assertFalse( - catalog._is_runtime_safe_mapping( - Shape("map", value=Shape("string")), tags, definitions, "Tags" - ) - ) - - def test_tag_map_rejects_permissive_one_of_sibling(self): - definitions = { - "Tag": { - "one_of": [ - { - "type": "object", - "properties": { - "Key": {"type": "string"}, - "Value": {"type": "string"}, - }, - "required": ["Key", "Value"], - }, - {"type": "object"}, - ] - } - } - tags = {"type": "array", "items": {"ref_name": "Tag"}} - - self.assertFalse( - catalog._is_runtime_safe_mapping( - Shape("map", value=Shape("string")), tags, definitions, "Tags" - ) - ) - - def test_tag_map_rejects_unmodeled_object_constraint(self): - definitions = { - "Tag": { - "type": "object", - "properties": { - "Key": {"type": "string"}, - "Value": {"type": "string"}, - "Scope": {"type": "string"}, - }, - "required": ["Key", "Value"], - "dependent_required": {"Key": ["Scope"]}, - } - } - tags = {"type": "array", "items": {"ref_name": "Tag"}} - - self.assertFalse( - catalog._is_runtime_safe_mapping( - Shape("map", value=Shape("string")), tags, definitions, "Tags" - ) - ) - - def test_tag_map_rejects_non_string_value_field(self): - definitions = { - "Tag": { - "type": "object", - "properties": { - "Key": {"type": "string"}, - "Value": {"type": "integer"}, - }, - "required": ["Key", "Value"], - } - } - tags = {"type": "array", "items": {"ref_name": "Tag"}} - - self.assertFalse( - catalog._is_runtime_safe_mapping( - Shape("map", value=Shape("string")), tags, definitions, "Tags" - ) - ) - - def test_tag_map_accepts_optional_additional_target_field(self): - definitions = { - "Tag": { - "type": "object", - "properties": { - "Key": {"type": "string"}, - "Value": {"type": "string"}, - "Description": {"type": "string"}, - }, - "required": ["Key", "Value"], - } - } - tags = {"type": "array", "items": {"ref_name": "Tag"}} - - self.assertTrue( - catalog._is_runtime_safe_mapping( - Shape("map", value=Shape("string")), tags, definitions, "Tags" - ) - ) - - def test_provider_schema_directory_is_loaded_deterministically(self): - with tempfile.TemporaryDirectory() as directory: - root = Path(directory) - (root / "b.json").write_text(json.dumps({"typeName": "AWS::Test::B"})) - (root / "a.json").write_text(json.dumps({"typeName": "AWS::Test::A"})) - (root / "ignored.json").write_text(json.dumps({"notTypeName": "AWS::Test::Ignored"})) - - schemas = catalog._load_provider_schemas(root) - first_hash = catalog._source_sha256(root) - second_hash = catalog._source_sha256(root) - - self.assertEqual(["AWS::Test::A", "AWS::Test::B"], sorted(schemas)) - self.assertEqual(first_hash, second_hash) - - -class IgnoredInputsTest(unittest.TestCase): - """Tests for _ignored_inputs_for_operation.""" - - def test_curated_name_is_ignored(self): - members = { - 'ClientToken': Shape('string'), - 'BucketName': Shape('string'), - } - result = catalog._ignored_inputs_for_operation(members, 'create', 's3', 'CreateBucket') - self.assertEqual(result, ['ClientToken']) - - def test_dry_run_is_ignored(self): - members = { - 'DryRun': Shape('boolean'), - 'InstanceId': Shape('string'), - } - result = catalog._ignored_inputs_for_operation(members, 'create', 'ec2', 'RunInstances') - self.assertEqual(result, ['DryRun']) - - def test_idempotency_token_metadata_is_detected(self): - members = { - 'Token': Shape('string', metadata={'idempotencyToken': True}), - 'Name': Shape('string'), - } - result = catalog._ignored_inputs_for_operation(members, 'create', 'test', 'CreateThing') - self.assertEqual(result, ['Token']) - - def test_idempotency_token_serialization_is_detected(self): - members = { - 'RequestId': Shape('string', serialization={'idempotencyToken': True}), - 'Data': Shape('string'), - } - result = catalog._ignored_inputs_for_operation(members, 'create', 'test', 'CreateThing') - self.assertEqual(result, ['RequestId']) - - def test_update_phase_does_not_add_curated_identifiers(self): - """_ignored_inputs_for_operation derives only request-control fields.""" - members = { - 'FunctionName': Shape('string'), - 'MemorySize': Shape('integer'), - } - result = catalog._ignored_inputs_for_operation( - members, 'update', 'lambda', 'UpdateFunctionConfiguration' - ) - self.assertNotIn('FunctionName', result) - - def test_no_heuristic_detection(self): - """Members not in the curated set or metadata are never ignored.""" - members = { - 'TokenValue': Shape('string'), - 'RequestId': Shape('string'), - 'Nonce': Shape('string'), - } - result = catalog._ignored_inputs_for_operation(members, 'create', 'test', 'CreateThing') - self.assertEqual(result, []) - - def test_returns_sorted(self): - members = { - 'DryRun': Shape('boolean'), - 'ClientToken': Shape('string'), - 'BucketName': Shape('string'), - } - result = catalog._ignored_inputs_for_operation(members, 'create', 's3', 'CreateBucket') - self.assertEqual(result, sorted(result)) - - -class CoverageMetricsTest(unittest.TestCase): - """Tests for _compute_coverage and _render_coverage with synthetic data.""" - - def _synthetic_coverage(self, adapters, botocore_operations=100, - botocore_services=10, compiled_schemas=None): - """Build a synthetic coverage computation.""" - if compiled_schemas is None: - compiled_schemas = { - 'AWS::Test::Type': { - 'properties': {'Name': {}, 'Arn': {}, 'Id': {}}, - 'read_only_properties': ['Arn'], - }, - } - - class FakeIndex: - def __init__(self, services, operations): - self.service_count = services - self.operation_count = operations - - index = FakeIndex(botocore_services, botocore_operations) - return catalog._compute_coverage(adapters, index, compiled_schemas) - - def test_zero_adapters_yields_zero_coverage(self): - coverage = self._synthetic_coverage([]) - self.assertEqual(coverage['catalog_services']['covered'], 0) - self.assertEqual(coverage['catalog_resources']['covered'], 0) - self.assertEqual(coverage['writable_properties']['covered'], 0) - self.assertEqual(coverage['catalog_commands']['covered'], 0) - self.assertEqual(coverage['state_commands']['covered'], 0) - - def test_single_create_adapter_coverage(self): - adapters = [{ - 'service': 'test', - 'operation': 'CreateThing', - 'cfn_type': 'AWS::Test::Type', - 'phase': 'create', - 'mappings': [{'source': 'Name', 'target': 'Name'}], - }] - coverage = self._synthetic_coverage(adapters) - self.assertEqual(coverage['catalog_services']['covered'], 1) - self.assertEqual(coverage['catalog_services']['total'], 10) - self.assertEqual(coverage['catalog_resources']['covered'], 1) - self.assertEqual(coverage['catalog_commands']['covered'], 1) - self.assertEqual(coverage['catalog_commands']['total'], 100) - self.assertEqual(coverage['state_commands']['covered'], 1) - self.assertEqual(coverage['state_services']['covered'], 1) - self.assertEqual(coverage['state_resources']['covered'], 1) - self.assertEqual(coverage['writable_properties']['covered'], 1) - # Total writable is 2 (Name + Id; Arn is read-only) - self.assertEqual(coverage['writable_properties']['total'], 2) - - def test_delete_adapter_does_not_count_as_state_validation(self): - adapters = [{ - 'service': 'test', - 'operation': 'DeleteThing', - 'cfn_type': 'AWS::Test::Type', - 'phase': 'delete', - 'mappings': [], - }] - coverage = self._synthetic_coverage(adapters) - self.assertEqual(coverage['state_commands']['covered'], 0) - self.assertEqual(coverage['catalog_commands']['covered'], 1) - self.assertEqual(coverage['lifecycle_adapters'], {'delete': 1}) - - def test_writable_properties_are_deduplicated_across_adapters(self): - """Two adapters mapping to the same (cfn_type, target) count once.""" - adapters = [ - { - 'service': 'test', - 'operation': 'Create', - 'cfn_type': 'AWS::Test::Type', - 'phase': 'create', - 'mappings': [{'source': 'Name', 'target': 'Name'}], - }, - { - 'service': 'test', - 'operation': 'Update', - 'cfn_type': 'AWS::Test::Type', - 'phase': 'update', - 'mappings': [{'source': 'Name', 'target': 'Name'}], - }, - ] - coverage = self._synthetic_coverage(adapters) - self.assertEqual(coverage['writable_properties']['covered'], 1) - - def test_render_derivation_explains_outcomes_and_subset(self): - counters = { - 'verified': 1305, - 'rejected': 113, - 'no_candidates': 118, - 'no_handler': 175, - 'tied_rejected': 3, - 'excluded_service': 15, - 'stale_model_rejected': 1, - } - - lines = catalog._render_derivation('create', counters) - - self.assertEqual([ - 'Create API operation matching:', - ' Resource types evaluated from provider schemas: 1,729', - ' Resource types with one API operation selected: 1,305', - ' Resource types without an operation selection: 424', - ' No create handler declared in the provider schema: 175', - ' Service excluded from catalog generation: 15', - ( - ' Handler permissions contained no usable botocore API ' - 'operation: 118' - ), - ( - ' Best candidate failed resource-name/property matching ' - 'safety checks: 113' - ), - ( - ' Of those, the exact create operation from handler ' - 'permissions was absent from the loaded botocore models: 1' - ), - ' Multiple API operations tied for best candidate: 3', - ], lines) - - def test_render_derivation_rejects_unexplained_outcome(self): - with self.assertRaisesRegex( - ValueError, 'no reader-facing description.*new_outcome' - ): - catalog._render_derivation( - 'create', {'verified': 1, 'new_outcome': 1} - ) - - def test_render_coverage_formats_percentages(self): - coverage = { - 'catalog_services': {'covered': 3, 'total': 10}, - 'catalog_resources': {'covered': 5, 'total': 20}, - 'catalog_commands': {'covered': 7, 'total': 50}, - 'state_services': {'covered': 2, 'total': 10}, - 'state_resources': {'covered': 4, 'total': 20}, - 'state_commands': {'covered': 4, 'total': 50}, - 'writable_properties': {'covered': 15, 'total': 100}, - 'lifecycle_adapters': {'create': 4, 'delete': 3}, - } - - lines = catalog._render_coverage(coverage) - - self.assertEqual([ - 'Catalog coverage (all final create, update, and delete adapters):', - ' botocore services represented: 3 of 10 (30.0%)', - ( - ' Compiled CloudFormation resource types represented: ' - '5 of 20 (25.0%)' - ), - ' botocore API operations represented: 7 of 50 (14.0%)', - '', - ( - 'State validation coverage (create/update adapters with at ' - 'least one writable-property mapping):' - ), - ' botocore services with state validation: 2 of 10 (20.0%)', - ( - ' Compiled CloudFormation resource types with state ' - 'validation: 4 of 20 (20.0%)' - ), - ( - ' botocore API operations used for state validation: ' - '4 of 50 (8.0%)' - ), - ( - ' Writable CloudFormation properties mapped for state ' - 'validation: 15 of 100 (15.0%)' - ), - '', - 'Final adapters by lifecycle phase:', - ' Create adapters: 4', - ' Update adapters: 0', - ' Delete adapters: 3', - ], lines) - - def test_generation_report_explains_uniqueness_and_output(self): - coverage = { - 'catalog_services': {'covered': 1, 'total': 1}, - 'catalog_resources': {'covered': 1, 'total': 1}, - 'catalog_commands': {'covered': 2, 'total': 2}, - 'state_services': {'covered': 1, 'total': 1}, - 'state_resources': {'covered': 1, 'total': 1}, - 'state_commands': {'covered': 1, 'total': 2}, - 'writable_properties': {'covered': 1, 'total': 2}, - 'lifecycle_adapters': {'create': 1, 'delete': 1}, - } - - lines = catalog._render_generation_report( - {'verified': 1}, - {'verified': 1}, - 2, - coverage, - 2, - Path('/tmp/catalog.json'), - ) - - self.assertEqual('AWS API catalog generation summary', lines[0]) - self.assertIn( - 'An adapter links one CloudFormation resource type and lifecycle ' - 'action to one botocore API operation.', - lines, - ) - self.assertIn('API operation uniqueness check:', lines) - self.assertIn( - ' Adapters removed so each botocore API operation appears only ' - 'once: 2', - lines, - ) - self.assertEqual([ - 'Catalog output:', - ' Adapters written: 2', - ' File: /tmp/catalog.json', - ], lines[-3:]) - - def test_zero_total_does_not_divide_by_zero(self): - coverage = { - 'catalog_services': {'covered': 0, 'total': 0}, - 'catalog_resources': {'covered': 0, 'total': 0}, - 'catalog_commands': {'covered': 0, 'total': 0}, - 'state_services': {'covered': 0, 'total': 0}, - 'state_resources': {'covered': 0, 'total': 0}, - 'state_commands': {'covered': 0, 'total': 0}, - 'writable_properties': {'covered': 0, 'total': 0}, - 'lifecycle_adapters': {}, - } - - lines = catalog._render_coverage(coverage) - - percentage_lines = [line for line in lines if line.endswith('%)')] - self.assertEqual(7, len(percentage_lines)) - self.assertTrue(all('(0.0%)' in line for line in percentage_lines)) - - def test_exact_percentage_calculation(self): - adapters = [ - { - 'service': 'svc0', - 'operation': 'Create', - 'cfn_type': 'AWS::A::B', - 'phase': 'create', - 'mappings': [{'source': 'X', 'target': 'Y'}], - }, - { - 'service': 'svc1', - 'operation': 'Update', - 'cfn_type': 'AWS::A::B', - 'phase': 'update', - 'mappings': [{'source': 'Z', 'target': 'W'}], - }, - ] - # 2 services out of 4, 2 commands out of 8 - compiled = {'AWS::A::B': {'properties': {'Y': {}, 'W': {}}, 'read_only_properties': []}} - coverage = self._synthetic_coverage( - adapters, botocore_operations=8, botocore_services=4, - compiled_schemas=compiled, - ) - self.assertEqual(coverage['catalog_services']['covered'], 2) - self.assertEqual(coverage['catalog_services']['total'], 4) - self.assertEqual(coverage['catalog_commands']['covered'], 2) - self.assertEqual(coverage['catalog_commands']['total'], 8) - self.assertEqual(coverage['state_commands']['covered'], 2) - self.assertEqual(coverage['writable_properties']['covered'], 2) - self.assertEqual(coverage['writable_properties']['total'], 2) - - -if __name__ == "__main__": - unittest.main() diff --git a/src/data-source/src/aws_cli_catalog.rs b/src/data-source/src/aws_cli_catalog.rs new file mode 100644 index 00000000..756601ce --- /dev/null +++ b/src/data-source/src/aws_cli_catalog.rs @@ -0,0 +1,151 @@ +//! Standalone AWS CLI operation catalog generation. +//! +//! This is intentionally decoupled from the [`crate::sync_upstream`] / +//! [`crate::generate_all`] maintenance pipeline: the catalog derives from AWS +//! CLI botocore service models plus CloudFormation provider handler metadata, +//! sources that the rest of the build does not touch, and it runs on its own +//! cadence. The `generate_aws_cli_catalog` example is the command-line entry +//! point; this module owns the logic it calls. + +use crate::schema; +use anyhow::Context; +use log::info; +use std::fs; +use std::path::{Path, PathBuf}; + +/// Format version the generator emits and the runtime loader accepts. +const AWS_CLI_OPERATION_CATALOG_FORMAT_VERSION: u64 = 1; + +#[derive(serde::Deserialize)] +struct AwsCliOperationCatalog { + format_version: u64, + adapters: Vec, +} + +/// Inputs for a standalone catalog generation run. +pub struct CatalogInputs { + /// Path to a local AWS CLI (`aws-cli`) checkout. Its `awscli/` directory + /// supplies the bundled botocore service models. + pub aws_cli_root: PathBuf, + /// Optional pre-downloaded provider-schema source (a directory of per-type + /// JSON files or a `schemas-cfn-lint.zip` archive). When `None`, the current + /// enhanced provider schemas are downloaded to a temporary directory. + pub provider_schemas: Option, +} + +/// Generate the AWS CLI operation catalog into `generated_dir/data`. +/// +/// Provider schemas are downloaded on demand when `inputs.provider_schemas` is +/// `None`, so this command does not depend on the maintenance `sync` having run. +/// The compiled CloudFormation schemas the generator verifies against are read +/// from the committed `generated_dir/schema-validator/compiled_schemas.json`. +pub fn generate_aws_cli_catalog(generated_dir: &Path, inputs: &CatalogInputs) -> anyhow::Result<()> { + let botocore_root = inputs.aws_cli_root.join("awscli"); + let botocore_package = botocore_root.join("botocore").join("__init__.py"); + let compiled_schemas = generated_dir.join("schema-validator").join("compiled_schemas.json"); + let catalog_path = generated_dir.join("data").join("aws_cli_operation_catalog.json"); + let script_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("scripts").join("generate_aws_cli_catalog.py"); + + anyhow::ensure!(script_path.is_file(), "AWS CLI catalog generator not found at {}", script_path.display()); + anyhow::ensure!(inputs.aws_cli_root.is_dir(), "AWS CLI checkout not found at {}", inputs.aws_cli_root.display()); + anyhow::ensure!( + botocore_package.is_file(), + "AWS CLI checkout does not contain botocore at {}", + botocore_package.display() + ); + anyhow::ensure!(compiled_schemas.is_file(), "compiled schemas not found at {}", compiled_schemas.display()); + + // Resolve provider schemas: use a caller-supplied path, or download the + // current enhanced schemas into a scratch directory owned by this run. + let downloaded_schemas; + let provider_schemas = match &inputs.provider_schemas { + Some(path) => { + anyhow::ensure!(path.exists(), "provider schemas source not found at {}", path.display()); + path.clone() + } + None => { + downloaded_schemas = download_provider_schemas()?; + downloaded_schemas + } + }; + + info!("Generating AWS CLI operation catalog via {}", script_path.display()); + let status = std::process::Command::new("python3") + .arg(&script_path) + .arg("--botocore-root") + .arg(&botocore_root) + .arg("--provider-schemas") + .arg(&provider_schemas) + .arg("--compiled-schemas") + .arg(&compiled_schemas) + .arg("--output") + .arg(&catalog_path) + .status() + .map_err(|error| anyhow::anyhow!("failed to start AWS CLI catalog generator: {error}"))?; + anyhow::ensure!(status.success(), "AWS CLI catalog generator failed with {status}"); + + let catalog_bytes = fs::read(&catalog_path) + .map_err(|error| anyhow::anyhow!("failed to read generated catalog {}: {error}", catalog_path.display()))?; + let adapter_count = validate_catalog(&catalog_bytes)?; + info!("Generated AWS CLI operation catalog with {adapter_count} adapters at {}", catalog_path.display()); + Ok(()) +} + +/// Download the current enhanced provider schemas into a scratch directory under +/// `upstream/` and return the per-type schema directory. Each schema body carries +/// the handler metadata the generator reads, matching the layout its directory +/// reader expects. +fn download_provider_schemas() -> anyhow::Result { + let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let scratch = manifest.join("upstream").join("aws_cli_catalog_schemas"); + info!("Downloading enhanced provider schemas for the AWS CLI catalog into {}", scratch.display()); + if scratch.exists() { + fs::remove_dir_all(&scratch) + .with_context(|| format!("failed to clear schema scratch directory {}", scratch.display()))?; + } + // `download_schemas` writes the per-region provider maps and the per-type + // schema bodies (which carry handler metadata) under `/schemas`. + let (stats, version) = schema::download_schemas(&scratch)?; + stats.fail_on_errors("AWS CLI catalog schema download")?; + info!("Downloaded {} provider schemas (version {})", stats.files_written, version); + Ok(schema::schema_dir(&scratch)) +} + +fn validate_catalog(catalog_bytes: &[u8]) -> anyhow::Result { + let catalog: AwsCliOperationCatalog = serde_json::from_slice(catalog_bytes) + .map_err(|error| anyhow::anyhow!("generated AWS CLI operation catalog is invalid JSON: {error}"))?; + anyhow::ensure!( + catalog.format_version == AWS_CLI_OPERATION_CATALOG_FORMAT_VERSION, + "generated AWS CLI operation catalog has format version {}, expected {}", + catalog.format_version, + AWS_CLI_OPERATION_CATALOG_FORMAT_VERSION + ); + anyhow::ensure!(!catalog.adapters.is_empty(), "generated AWS CLI operation catalog contains no adapters"); + Ok(catalog.adapters.len()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn current_catalog_format_with_adapters_is_valid() { + let catalog = br#"{"format_version":1,"adapters":[{}]}"#; + let adapter_count = validate_catalog(catalog).expect("catalog should be valid"); + assert_eq!(1, adapter_count); + } + + #[test] + fn unsupported_catalog_format_is_rejected() { + let catalog = br#"{"format_version":2,"adapters":[{}]}"#; + let error = validate_catalog(catalog).expect_err("unsupported format must fail"); + assert!(error.to_string().contains("format version 2, expected 1")); + } + + #[test] + fn catalog_without_adapters_is_rejected() { + let catalog = br#"{"format_version":1,"adapters":[]}"#; + let error = validate_catalog(catalog).expect_err("empty adapters must fail"); + assert!(error.to_string().contains("contains no adapters")); + } +} diff --git a/src/schema-validator/src/resource_schema.rs b/src/schema-validator/src/resource_schema.rs index 9cae198a..80a3bf58 100644 --- a/src/schema-validator/src/resource_schema.rs +++ b/src/schema-validator/src/resource_schema.rs @@ -13,7 +13,7 @@ pub enum PropertyValueType { String, } -/// Schema information needed to map an AWS API request into one resource. +/// Schema information needed to map an AWS CLI command into one resource. /// /// This is intentionally narrower than the validator's compiled schema model: /// callers can select and type-check resource properties without depending on diff --git a/src/validation-engine/API.md b/src/validation-engine/API.md index d493144f..7b2598c1 100644 --- a/src/validation-engine/API.md +++ b/src/validation-engine/API.md @@ -33,9 +33,9 @@ for d in &report.diagnostics { On parse failure, `validate_bytes_with_path` returns `Ok(report)` with a synthetic `F1101` diagnostic and `status=Error` rather than returning `Err`. This ensures callers always get a structured report. -## Validating an AWS API Request +## Validating an AWS CLI Command -`validate_aws_api_request` accepts raw service, operation, HTTP, trait, and request-parameter context. It owns operation +`validate_aws_cli_command` accepts raw service, operation, HTTP, trait, and request-parameter context. It owns operation classification, deterministic CloudFormation resource-type selection, request-to-template modeling, schema-backed property mapping, and diagnostic scoping to explicitly modeled properties: @@ -43,22 +43,22 @@ mapping, and diagnostic scoping to explicitly modeled properties: use rego_engine::RegoEngine; use schema_validator::SchemaValidator; use validation_engine::{ - AwsApiRequest, AwsApiValue, EngineConfig, ValidateConfig, validate_aws_api_request, + AwsCliCommand, AwsCliValue, EngineConfig, ValidateConfig, validate_aws_cli_command, }; let engine = RegoEngine::new(EngineConfig::default())?; let schema_validator = SchemaValidator::default(); -let request = AwsApiRequest::new( +let request = AwsCliCommand::new( "s3", "CreateBucket", [ - ("Bucket".into(), AwsApiValue::String { value: "example-bucket".into() }), + ("Bucket".into(), AwsCliValue::String { value: "example-bucket".into() }), ], ) .with_service_prefix("s3") .with_http_method("PUT"); -let result = validate_aws_api_request( +let result = validate_aws_cli_command( &engine, &schema_validator, &request, @@ -73,21 +73,21 @@ if let Some(report) = &result.report { } ``` -`AwsApiValue` preserves bytes and 64-bit integer widths and explicitly marks unsupported values. Exact `TemplateBody` +`AwsCliValue` preserves bytes and 64-bit integer widths and explicitly marks unsupported values. Exact `TemplateBody` bytes are validated without rewriting; `TemplateURL` is skipped because validation is offline. Every result includes an operation kind, validation status, optional template source, resource candidates, and reason. `Validated` means the modeled template reached the normal validation pipeline; `Skipped` has no report and explains why. -`AwsApiRequestValidation` carries an `Option` projected at the `STANDARD` detail +`AwsCliCommandValidation` carries an `Option` projected at the `STANDARD` detail level — detailed enrichment is not supported for synthesized API-request templates because there is no user-authored source to annotate with context. The `template` field carries the exact bytes that were validated — the caller's original `TemplateBody` without reserializing, or the synthesized JSON template for adapter-mapped requests — so consumers can display the modeled template that produced the diagnostics. It is `None` when the request was skipped. -Use `validate_aws_api_request_with_path` when the embedding application needs a custom report path. +Use `validate_aws_cli_command_with_path` when the embedding application needs a custom report path. **Deterministic closed-adapter contract.** Operation-to-resource mapping uses a generated adapter catalog keyed by case-normalized canonical `service_name` and exact operation name. The catalog is produced by -`data-source/scripts/generate_aws_api_catalog.py` from each resource type's own provider handler metadata, resolved +`data-source/scripts/generate_aws_cli_catalog.py` from each resource type's own provider handler metadata, resolved against botocore service models and structurally verified against the compiled CloudFormation schemas; it covers create and delete lifecycles for roughly seventy percent of all resource types plus curated update entries. Each adapter declares one CloudFormation resource type with explicit request-parameter-to-property pairs. Unregistered diff --git a/src/validation-engine/src/aws_api.rs b/src/validation-engine/src/aws_cli.rs similarity index 86% rename from src/validation-engine/src/aws_api.rs rename to src/validation-engine/src/aws_cli.rs index 0715ba7e..51c588c7 100644 --- a/src/validation-engine/src/aws_api.rs +++ b/src/validation-engine/src/aws_cli.rs @@ -7,7 +7,7 @@ use std::sync::LazyLock; use crate::{ValidateConfig, ValidationEngine, ValidationError, validate_bytes_with_path}; -/// A recursively typed value from an AWS API request. +/// A recursively typed value from an AWS CLI command. /// /// Unlike JSON, this model preserves byte strings such as CloudFormation's /// `TemplateBody`. `Unsupported` lets language bindings carry an explicit marker @@ -15,7 +15,7 @@ use crate::{ValidateConfig, ValidationEngine, ValidationError, validate_bytes_wi #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Enum))] #[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")] -pub enum AwsApiValue { +pub enum AwsCliValue { Null, Boolean { value: bool }, Integer { value: i64 }, @@ -23,12 +23,12 @@ pub enum AwsApiValue { Number { value: f64 }, String { value: String }, Bytes { value: Vec }, - Array { items: Vec }, - Object { entries: HashMap }, + Array { items: Vec }, + Object { entries: HashMap }, Unsupported { type_name: String }, } -impl AwsApiValue { +impl AwsCliValue { /// Converts a JSON value without losing integer width. pub fn from_json(value: serde_json::Value) -> Self { match value { @@ -84,21 +84,21 @@ impl AwsApiValue { } } -impl From for AwsApiValue { +impl From for AwsCliValue { fn from(value: serde_json::Value) -> Self { Self::from_json(value) } } -/// AWS service, operation, and input values needed to model one API request as -/// CloudFormation resource state. +/// AWS service, operation, and input values needed to model one AWS CLI command +/// as CloudFormation resource state. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))] #[serde(rename_all = "camelCase")] -pub struct AwsApiRequestContext { +pub struct AwsCliCommandContext { pub service_name: String, pub operation_name: String, - pub parameters: HashMap, + pub parameters: HashMap, #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "uniffi-bindings", uniffi(default))] pub service_prefix: Option, @@ -110,14 +110,17 @@ pub struct AwsApiRequestContext { pub is_read_only: Option, } -/// Idiomatic Rust name for the AWS API request context record. -pub type AwsApiRequest = AwsApiRequestContext; +/// Idiomatic Rust name for the AWS CLI command record. The `Context`-suffixed +/// struct is the name UniFFI exports to the language bindings, which keeps the +/// generated native record distinct from each binding's friendly `AwsCliCommand` +/// wrapper so wildcard imports never collide. +pub type AwsCliCommand = AwsCliCommandContext; -impl AwsApiRequestContext { +impl AwsCliCommandContext { pub fn new( service_name: impl Into, operation_name: impl Into, - parameters: impl IntoIterator, + parameters: impl IntoIterator, ) -> Self { Self { service_name: service_name.into(), @@ -145,7 +148,7 @@ impl AwsApiRequestContext { } fn default_file_path(&self) -> String { - format!("aws-api://{}/{}", self.service_name, self.operation_name) + format!("aws-cli://{}/{}", self.service_name, self.operation_name) } } @@ -153,7 +156,7 @@ impl AwsApiRequestContext { #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Enum))] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum AwsApiOperationKind { +pub enum AwsCliOperationKind { ReadOnly, CloudFormationCreate, CloudFormationUpdate, @@ -166,35 +169,35 @@ pub enum AwsApiOperationKind { #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Enum))] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum AwsApiRequestValidationStatus { +pub enum AwsCliCommandValidationStatus { Validated, Skipped, } -/// Provenance of the template validated for an AWS API request. +/// Provenance of the template validated for an AWS CLI command. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Enum))] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum AwsApiTemplateSource { +pub enum AwsCliTemplateSource { TemplateBody, CloudControlDesiredState, SynthesizedCreate, SynthesizedUpdate, } -/// Canonical result for AWS API request validation. +/// Canonical result for AWS CLI command validation. /// /// The report is projected at the `STANDARD` detail level — detailed enrichment -/// is not meaningful for synthesized API-request templates because there is no +/// is not meaningful for synthesized command templates because there is no /// user-authored source to annotate with context. #[derive(Debug, Clone, Serialize)] #[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))] #[serde(rename_all = "camelCase")] #[must_use] -pub struct AwsApiRequestValidation { - pub operation_kind: AwsApiOperationKind, - pub status: AwsApiRequestValidationStatus, - pub template_source: Option, +pub struct AwsCliCommandValidation { + pub operation_kind: AwsCliOperationKind, + pub status: AwsCliCommandValidationStatus, + pub template_source: Option, pub resource_types: Vec, pub reason: String, pub report: Option, @@ -208,31 +211,31 @@ pub struct AwsApiRequestValidation { pub template: Option>, } -/// Classifies, models, and validates one AWS API request entirely offline. -pub fn validate_aws_api_request( +/// Classifies, models, and validates one AWS CLI command entirely offline. +pub fn validate_aws_cli_command( engine: &dyn ValidationEngine, schema_validator: &SchemaValidator, - request: &AwsApiRequest, + request: &AwsCliCommand, config: ValidateConfig, -) -> Result { - validate_aws_api_request_with_path(engine, schema_validator, request, config, request.default_file_path()) +) -> Result { + validate_aws_cli_command_with_path(engine, schema_validator, request, config, request.default_file_path()) } -/// Same as [`validate_aws_api_request`], with an explicit report path supplied +/// Same as [`validate_aws_cli_command`], with an explicit report path supplied /// by the embedding application. -pub fn validate_aws_api_request_with_path( +pub fn validate_aws_cli_command_with_path( engine: &dyn ValidationEngine, schema_validator: &SchemaValidator, - request: &AwsApiRequest, + request: &AwsCliCommand, config: ValidateConfig, file_path: String, -) -> Result { +) -> Result { let classification = classify_operation(request, schema_validator)?; let synthesis = synthesize_request(request, &classification, schema_validator)?; let Some(template) = synthesis.template else { - return Ok(AwsApiRequestValidation { + return Ok(AwsCliCommandValidation { operation_kind: classification.kind, - status: AwsApiRequestValidationStatus::Skipped, + status: AwsCliCommandValidationStatus::Skipped, template_source: None, resource_types: synthesis.resource_types, reason: synthesis.reason, @@ -242,15 +245,15 @@ pub fn validate_aws_api_request_with_path( }; // Force standard detail level — detailed enrichment is not meaningful for - // synthesized API-request templates (no user-authored source to annotate). + // synthesized command templates (no user-authored source to annotate). let standard_config = ValidateConfig { detail_level: DetailLevel::Standard, ..config }; let mut report = validate_bytes_with_path(engine, schema_validator, &template, standard_config, file_path)?; if let Some(properties) = synthesis.diagnostic_properties.as_ref() { scope_synthesized_report(&mut report, properties); } - Ok(AwsApiRequestValidation { + Ok(AwsCliCommandValidation { operation_kind: classification.kind, - status: AwsApiRequestValidationStatus::Validated, + status: AwsCliCommandValidationStatus::Validated, template_source: synthesis.source, resource_types: synthesis.resource_types, reason: synthesis.reason, @@ -293,33 +296,19 @@ struct OperationCatalog { // These entries exposed dependent or ambiguous provider permissions in an // older generated artifact. Filtering them here keeps shipped catalogs // conservative while the maintenance pipeline catches up. -const REJECTED_CATALOG_OPERATIONS: &[(&str, &str)] = &[ - ("acm", "RemoveTagsFromCertificate"), - ("logs", "StartQuery"), - ("quicksight", "CreateTopic"), - ("quicksight", "DeleteTopic"), - ("robomaker", "DeregisterRobot"), -]; - -fn is_rejected_catalog_operation(service: &str, operation: &str) -> bool { - REJECTED_CATALOG_OPERATIONS.iter().any(|(candidate_service, candidate_operation)| { - *candidate_service == service && *candidate_operation == operation - }) -} - -/// Generated by `data-source/scripts/generate_aws_api_catalog.py`: each entry is +/// Generated by `data-source/scripts/generate_aws_cli_catalog.py`: each entry is /// derived from the resource type's own provider handler metadata, resolved /// against botocore service models, and structurally verified against the /// compiled CloudFormation schemas. Only exact service+operation keys resolve; /// unregistered operations stay unmapped. static ADAPTER_REGISTRY: LazyLock, String>> = - LazyLock::new(|| parse_adapter_registry(&data_source::embedded::AWS_API_OPERATION_CATALOG_BYTES)); + LazyLock::new(|| parse_adapter_registry(&data_source::embedded::AWS_CLI_OPERATION_CATALOG_BYTES)); fn parse_adapter_registry(bytes: &[u8]) -> Result, String> { let catalog: OperationCatalog = serde_json::from_slice(bytes) - .map_err(|error| format!("embedded AWS API operation catalog is invalid: {error}"))?; + .map_err(|error| format!("embedded AWS CLI operation catalog is invalid: {error}"))?; if catalog.format_version != 1 { - return Err(format!("unsupported AWS API operation catalog format {}", catalog.format_version)); + return Err(format!("unsupported AWS CLI operation catalog format {}", catalog.format_version)); } let mut registry = HashMap::new(); for adapter in catalog.adapters { @@ -327,14 +316,11 @@ fn parse_adapter_registry(bytes: &[u8]) -> Result bool { TEMPLATE_BODY_OPERATIONS.iter().any(|(s, o)| normalize_service(s) == normalized && *o == operation) } -fn template_body_operation_kind(service: &str, operation: &str) -> Option { +fn template_body_operation_kind(service: &str, operation: &str) -> Option { if normalize_service(service) != "cloudformation" { return None; } match operation { - "CreateChangeSet" | "CreateStack" | "CreateStackSet" => Some(AwsApiOperationKind::CloudFormationCreate), - "UpdateStack" | "UpdateStackSet" => Some(AwsApiOperationKind::CloudFormationUpdate), - "EstimateTemplateCost" | "GetTemplateSummary" | "ValidateTemplate" => Some(AwsApiOperationKind::ReadOnly), + "CreateChangeSet" | "CreateStack" | "CreateStackSet" => Some(AwsCliOperationKind::CloudFormationCreate), + "UpdateStack" | "UpdateStackSet" => Some(AwsCliOperationKind::CloudFormationUpdate), + "EstimateTemplateCost" | "GetTemplateSummary" | "ValidateTemplate" => Some(AwsCliOperationKind::ReadOnly), _ => None, } } @@ -400,7 +386,7 @@ enum OperationPhase { #[derive(Debug, Clone)] struct Classification { - kind: AwsApiOperationKind, + kind: AwsCliOperationKind, candidates: Vec, } @@ -472,7 +458,7 @@ const DATA_PLANE_VERBS: &[&str] = &[ const MODIFIER_PREFIXES: &[&str] = &["Admin", "Batch", "Bulk", "Transact"]; fn classify_operation( - request: &AwsApiRequest, + request: &AwsCliCommand, schema_validator: &SchemaValidator, ) -> Result { if let Some(kind) = template_body_operation_kind(&request.service_name, &request.operation_name) { @@ -481,14 +467,14 @@ fn classify_operation( // The modeled read-only trait is authoritative even over a registered adapter. if request.is_read_only == Some(true) { - return Ok(Classification { kind: AwsApiOperationKind::ReadOnly, candidates: Vec::new() }); + return Ok(Classification { kind: AwsCliOperationKind::ReadOnly, candidates: Vec::new() }); } if let Some(adapter) = lookup_adapter(&request.service_name, &request.operation_name)? { let kind = match adapter.phase { - AdapterPhase::Create => AwsApiOperationKind::CloudFormationCreate, - AdapterPhase::Update => AwsApiOperationKind::CloudFormationUpdate, - AdapterPhase::Delete => AwsApiOperationKind::CloudFormationDelete, + AdapterPhase::Create => AwsCliOperationKind::CloudFormationCreate, + AdapterPhase::Update => AwsCliOperationKind::CloudFormationUpdate, + AdapterPhase::Delete => AwsCliOperationKind::CloudFormationDelete, }; return Ok(Classification { kind, candidates: vec![adapter.cfn_type.clone()] }); } @@ -498,10 +484,10 @@ fn classify_operation( let phase = operation_phase(request, verb); if phase == OperationPhase::Read { - return Ok(Classification { kind: AwsApiOperationKind::ReadOnly, candidates: Vec::new() }); + return Ok(Classification { kind: AwsCliOperationKind::ReadOnly, candidates: Vec::new() }); } if phase == OperationPhase::Data { - return Ok(Classification { kind: AwsApiOperationKind::DataPlaneMutation, candidates: Vec::new() }); + return Ok(Classification { kind: AwsCliOperationKind::DataPlaneMutation, candidates: Vec::new() }); } if is_cloud_control_service(&request.service_name) { @@ -509,9 +495,9 @@ fn classify_operation( matches!(request.operation_name.as_str(), "CreateResource" | "UpdateResource" | "DeleteResource"); if is_resource_op && let Some(type_name) = explicit_valid_type_name(request, schema_validator) { let kind = match request.operation_name.as_str() { - "CreateResource" => AwsApiOperationKind::CloudFormationCreate, - "DeleteResource" => AwsApiOperationKind::CloudFormationDelete, - _ => AwsApiOperationKind::UnmappedMutation, + "CreateResource" => AwsCliOperationKind::CloudFormationCreate, + "DeleteResource" => AwsCliOperationKind::CloudFormationDelete, + _ => AwsCliOperationKind::UnmappedMutation, }; return Ok(Classification { kind, candidates: vec![type_name] }); } @@ -519,9 +505,9 @@ fn classify_operation( // Unknown mutation: classify by verb family but never assign resource types. let kind = if DATA_PLANE_IF_UNMAPPED_VERBS.contains(&verb) { - AwsApiOperationKind::DataPlaneMutation + AwsCliOperationKind::DataPlaneMutation } else { - AwsApiOperationKind::UnmappedMutation + AwsCliOperationKind::UnmappedMutation }; Ok(Classification { kind, candidates: Vec::new() }) } @@ -529,7 +515,7 @@ fn classify_operation( const DATA_PLANE_IF_UNMAPPED_VERBS: &[&str] = &["Execute", "Invoke", "Post", "Publish", "Put", "Send", "Upload", "Write"]; -fn operation_phase(request: &AwsApiRequest, verb: &str) -> OperationPhase { +fn operation_phase(request: &AwsCliCommand, verb: &str) -> OperationPhase { if request.is_read_only == Some(true) || READ_VERBS.contains(&verb) { return OperationPhase::Read; } @@ -694,15 +680,15 @@ fn effective_verb(words: &[String]) -> &str { } } -fn explicit_valid_type_name(request: &AwsApiRequest, schema_validator: &SchemaValidator) -> Option { +fn explicit_valid_type_name(request: &AwsCliCommand, schema_validator: &SchemaValidator) -> Option { match request.parameters.get("TypeName") { - Some(AwsApiValue::String { value }) if schema_validator.has_resource_type(value) => Some(value.clone()), + Some(AwsCliValue::String { value }) if schema_validator.has_resource_type(value) => Some(value.clone()), _ => None, } } struct Synthesis { template: Option>, - source: Option, + source: Option, reason: String, resource_types: Vec, diagnostic_properties: Option>, @@ -715,7 +701,7 @@ impl Synthesis { } fn synthesize_request( - request: &AwsApiRequest, + request: &AwsCliCommand, classification: &Classification, schema_validator: &SchemaValidator, ) -> Result { @@ -725,7 +711,7 @@ fn synthesize_request( if let Some(template) = template_body_bytes(request.parameters.get("TemplateBody")) { return Ok(Synthesis { template: Some(template), - source: Some(AwsApiTemplateSource::TemplateBody), + source: Some(AwsCliTemplateSource::TemplateBody), reason: "using exact request TemplateBody".into(), resource_types: Vec::new(), diagnostic_properties: None, @@ -736,7 +722,7 @@ fn synthesize_request( } } - if classification.kind == AwsApiOperationKind::ReadOnly { + if classification.kind == AwsCliOperationKind::ReadOnly { return Ok(Synthesis::skipped("read-only calls do not need validation", Vec::new())); } @@ -760,19 +746,19 @@ fn synthesize_request( adapter_template(request, classification, schema_validator) } -fn template_body_bytes(value: Option<&AwsApiValue>) -> Option> { +fn template_body_bytes(value: Option<&AwsCliValue>) -> Option> { match value { - Some(AwsApiValue::Bytes { value }) if !value.is_empty() => Some(value.clone()), - Some(AwsApiValue::String { value }) if !value.is_empty() => Some(value.as_bytes().to_vec()), + Some(AwsCliValue::Bytes { value }) if !value.is_empty() => Some(value.clone()), + Some(AwsCliValue::String { value }) if !value.is_empty() => Some(value.as_bytes().to_vec()), _ => None, } } fn desired_state_template( - request: &AwsApiRequest, + request: &AwsCliCommand, schema_validator: &SchemaValidator, ) -> Result { - let Some(AwsApiValue::String { value: type_name }) = request.parameters.get("TypeName") else { + let Some(AwsCliValue::String { value: type_name }) = request.parameters.get("TypeName") else { return Ok(Synthesis::skipped("DesiredState has no known CloudFormation TypeName", Vec::new())); }; if !schema_validator.has_resource_type(type_name) { @@ -782,8 +768,8 @@ fn desired_state_template( return Ok(Synthesis::skipped("DesiredState is missing", vec![type_name.clone()])); }; let properties = match desired_state { - AwsApiValue::String { value } if !value.is_empty() => serde_json::from_str(value), - AwsApiValue::Bytes { value } if !value.is_empty() => serde_json::from_slice(value), + AwsCliValue::String { value } if !value.is_empty() => serde_json::from_str(value), + AwsCliValue::Bytes { value } if !value.is_empty() => serde_json::from_slice(value), _ => return Ok(Synthesis::skipped("DesiredState is missing", vec![type_name.clone()])), }; let properties: serde_json::Value = match properties { @@ -795,7 +781,7 @@ fn desired_state_template( }; Ok(Synthesis { template: Some(resource_template(type_name, properties)?), - source: Some(AwsApiTemplateSource::CloudControlDesiredState), + source: Some(AwsCliTemplateSource::CloudControlDesiredState), reason: "wrapped exact Cloud Control desired state".into(), resource_types: vec![type_name.clone()], diagnostic_properties: None, @@ -803,13 +789,13 @@ fn desired_state_template( } fn adapter_template( - request: &AwsApiRequest, + request: &AwsCliCommand, classification: &Classification, schema_validator: &SchemaValidator, ) -> Result { if !matches!( classification.kind, - AwsApiOperationKind::CloudFormationCreate | AwsApiOperationKind::CloudFormationUpdate + AwsCliOperationKind::CloudFormationCreate | AwsCliOperationKind::CloudFormationUpdate ) { return Ok(Synthesis::skipped( "classification has no representable resource state", @@ -849,9 +835,9 @@ fn adapter_template( let diagnostic_properties = Some(properties.keys().cloned().collect::>()); let source = if adapter.phase == AdapterPhase::Update { - AwsApiTemplateSource::SynthesizedUpdate + AwsCliTemplateSource::SynthesizedUpdate } else { - AwsApiTemplateSource::SynthesizedCreate + AwsCliTemplateSource::SynthesizedCreate }; let reason = if adapter.phase == AdapterPhase::Update { "synthesized explicitly updated CloudFormation properties" @@ -875,7 +861,7 @@ enum AdapterMappingResult { } fn map_adapter_properties( - parameters: &HashMap, + parameters: &HashMap, schema: &ResourceSchemaMetadata, adapter: &OperationAdapter, ) -> Result { @@ -971,7 +957,7 @@ fn resource_template( } fn mapped_value( - value: &AwsApiValue, + value: &AwsCliValue, accepted_types: &BTreeSet, property_name: &str, ) -> Option { @@ -979,15 +965,15 @@ fn mapped_value( // Key/Value object arrays for the same resource state. if property_name == "Tags" && accepts_type(accepted_types, PropertyValueType::Array) - && let AwsApiValue::Object { entries } = value - && entries.values().all(|value| matches!(value, AwsApiValue::String { .. })) + && let AwsCliValue::Object { entries } = value + && entries.values().all(|value| matches!(value, AwsCliValue::String { .. })) { - let mut tags: Vec<(&String, &AwsApiValue)> = entries.iter().collect(); + let mut tags: Vec<(&String, &AwsCliValue)> = entries.iter().collect(); tags.sort_by_key(|(key, _)| key.as_str()); return Some(serde_json::Value::Array( tags.into_iter() .filter_map(|(key, value)| match value { - AwsApiValue::String { value } => Some(serde_json::json!({"Key": key, "Value": value})), + AwsCliValue::String { value } => Some(serde_json::json!({"Key": key, "Value": value})), _ => None, }) .collect(), @@ -1003,31 +989,31 @@ fn accepts_type(types: &BTreeSet, expected: PropertyValueType types.contains(&PropertyValueType::Any) || types.contains(&expected) } -fn value_matches_types(value: &AwsApiValue, types: &BTreeSet) -> bool { +fn value_matches_types(value: &AwsCliValue, types: &BTreeSet) -> bool { let accepts_any = types.contains(&PropertyValueType::Any); match value { - AwsApiValue::Array { items } => { + AwsCliValue::Array { items } => { (accepts_any || types.contains(&PropertyValueType::Array)) && items.iter().all(is_scalar_api_value) } - AwsApiValue::Object { .. } => false, - AwsApiValue::Boolean { .. } => accepts_any || types.contains(&PropertyValueType::Boolean), - AwsApiValue::Integer { .. } | AwsApiValue::UnsignedInteger { .. } => { + AwsCliValue::Object { .. } => false, + AwsCliValue::Boolean { .. } => accepts_any || types.contains(&PropertyValueType::Boolean), + AwsCliValue::Integer { .. } | AwsCliValue::UnsignedInteger { .. } => { accepts_any || types.contains(&PropertyValueType::Integer) || types.contains(&PropertyValueType::Number) } - AwsApiValue::Number { .. } => accepts_any || types.contains(&PropertyValueType::Number), - AwsApiValue::String { .. } => accepts_any || types.contains(&PropertyValueType::String), - AwsApiValue::Null | AwsApiValue::Bytes { .. } | AwsApiValue::Unsupported { .. } => false, + AwsCliValue::Number { .. } => accepts_any || types.contains(&PropertyValueType::Number), + AwsCliValue::String { .. } => accepts_any || types.contains(&PropertyValueType::String), + AwsCliValue::Null | AwsCliValue::Bytes { .. } | AwsCliValue::Unsupported { .. } => false, } } -fn is_scalar_api_value(value: &AwsApiValue) -> bool { +fn is_scalar_api_value(value: &AwsCliValue) -> bool { matches!( value, - AwsApiValue::Boolean { .. } - | AwsApiValue::Integer { .. } - | AwsApiValue::UnsignedInteger { .. } - | AwsApiValue::Number { .. } - | AwsApiValue::String { .. } + AwsCliValue::Boolean { .. } + | AwsCliValue::Integer { .. } + | AwsCliValue::UnsignedInteger { .. } + | AwsCliValue::Number { .. } + | AwsCliValue::String { .. } ) } fn scope_synthesized_report(report: &mut ValidationReport, properties: &BTreeSet) { @@ -1109,21 +1095,21 @@ mod tests { } } - fn value(value: serde_json::Value) -> AwsApiValue { - AwsApiValue::from_json(value) + fn value(value: serde_json::Value) -> AwsCliValue { + AwsCliValue::from_json(value) } - fn request(service: &str, operation: &str, parameters: serde_json::Value) -> AwsApiRequest { - let parameters: HashMap = parameters + fn request(service: &str, operation: &str, parameters: serde_json::Value) -> AwsCliCommand { + let parameters: HashMap = parameters .as_object() .expect("test parameters must be an object") .iter() - .map(|(name, value)| (name.clone(), AwsApiValue::from_json(value.clone()))) + .map(|(name, value)| (name.clone(), AwsCliValue::from_json(value.clone()))) .collect(); - AwsApiRequest::new(service, operation, parameters).with_http_method("POST") + AwsCliCommand::new(service, operation, parameters).with_http_method("POST") } - fn synthesized_json(request: &AwsApiRequest) -> (Classification, Synthesis, serde_json::Value) { + fn synthesized_json(request: &AwsCliCommand) -> (Classification, Synthesis, serde_json::Value) { let schema_validator = SchemaValidator::default(); let classification = classify_operation(request, &schema_validator).expect("classification succeeds"); let synthesis = synthesize_request(request, &classification, &schema_validator).expect("synthesis succeeds"); @@ -1255,30 +1241,13 @@ mod tests { } } - #[test] - fn rejected_catalog_operations_never_report_resource_types() { - let schema_validator = SchemaValidator::default(); - for (service, operation) in REJECTED_CATALOG_OPERATIONS { - let request = request(service, operation, serde_json::json!({})); - let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); - assert!( - classification.candidates.is_empty(), - "{service}:{operation} must not report a CloudFormation type" - ); - let synthesis = - synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); - assert!(synthesis.template.is_none(), "{service}:{operation} must not synthesize state"); - assert!(synthesis.resource_types.is_empty(), "{service}:{operation} must not report resource types"); - } - } - #[test] fn nested_values_are_rejected_without_recursive_shape_mappings() { let object_types = BTreeSet::from([PropertyValueType::Object]); let array_types = BTreeSet::from([PropertyValueType::Array]); - let object = AwsApiValue::from_json(serde_json::json!({"lowerCamel": "value"})); - let object_array = AwsApiValue::from_json(serde_json::json!([{"lowerCamel": "value"}])); - let scalar_array = AwsApiValue::from_json(serde_json::json!(["one", "two"])); + let object = AwsCliValue::from_json(serde_json::json!({"lowerCamel": "value"})); + let object_array = AwsCliValue::from_json(serde_json::json!([{"lowerCamel": "value"}])); + let scalar_array = AwsCliValue::from_json(serde_json::json!(["one", "two"])); assert!(mapped_value(&object, &object_types, "Configuration").is_none()); assert!(mapped_value(&object_array, &array_types, "Configurations").is_none()); @@ -1413,9 +1382,9 @@ mod tests { fn s3_create_bucket_synthesizes_with_explicit_mappings() { let request = request("s3", "CreateBucket", serde_json::json!({"Bucket": "synthetic-bucket"})); let (classification, synthesis, document) = synthesized_json(&request); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationCreate); assert_eq!(classification.candidates, ["AWS::S3::Bucket"]); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(synthesis.source, Some(AwsCliTemplateSource::SynthesizedCreate)); assert_eq!(document["Resources"]["Resource"]["Properties"]["BucketName"], "synthetic-bucket"); } @@ -1424,7 +1393,7 @@ mod tests { let schema_validator = SchemaValidator::default(); let request = request("s3", "DeleteBucket", serde_json::json!({"Bucket": "synthetic-bucket"})); let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationDelete); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationDelete); assert_eq!(classification.candidates, ["AWS::S3::Bucket"]); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); @@ -1444,7 +1413,7 @@ mod tests { }), ); let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationCreate); assert_eq!(classification.candidates, ["AWS::DynamoDB::Table"]); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none(), "unmapped nested parameters must skip synthesis"); @@ -1463,9 +1432,9 @@ mod tests { serde_json::json!({"TableName": "Synthetic", "BillingMode": "PAY_PER_REQUEST"}), ); let (classification, synthesis, document) = synthesized_json(&request); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationCreate); assert_eq!(classification.candidates, ["AWS::DynamoDB::Table"]); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(synthesis.source, Some(AwsCliTemplateSource::SynthesizedCreate)); assert_eq!(document["Resources"]["Resource"]["Properties"]["TableName"], "Synthetic"); assert_eq!(document["Resources"]["Resource"]["Properties"]["BillingMode"], "PAY_PER_REQUEST"); } @@ -1475,7 +1444,7 @@ mod tests { let schema_validator = SchemaValidator::default(); let request = request("dynamodb", "DeleteTable", serde_json::json!({"TableName": "Synthetic"})); let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationDelete); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationDelete); assert_eq!(classification.candidates, ["AWS::DynamoDB::Table"]); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); @@ -1492,9 +1461,9 @@ mod tests { }), ); let (classification, synthesis, document) = synthesized_json(&request); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationCreate); assert_eq!(classification.candidates, ["AWS::IAM::Role"]); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(synthesis.source, Some(AwsCliTemplateSource::SynthesizedCreate)); assert_eq!(document["Resources"]["Resource"]["Properties"]["RoleName"], "Synthetic"); } @@ -1503,7 +1472,7 @@ mod tests { let schema_validator = SchemaValidator::default(); let request = request("iam", "DeleteRole", serde_json::json!({"RoleName": "Synthetic"})); let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationDelete); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationDelete); assert_eq!(classification.candidates, ["AWS::IAM::Role"]); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); @@ -1514,9 +1483,9 @@ mod tests { let request = request("lambda", "CreateFunction", serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 128})); let (classification, synthesis, document) = synthesized_json(&request); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationCreate); assert_eq!(classification.candidates, ["AWS::Lambda::Function"]); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(synthesis.source, Some(AwsCliTemplateSource::SynthesizedCreate)); assert_eq!(synthesis.diagnostic_properties, Some(BTreeSet::from(["FunctionName".into(), "MemorySize".into()]))); assert_eq!(document["Resources"]["Resource"]["Properties"]["FunctionName"], "Synthetic"); assert_eq!(document["Resources"]["Resource"]["Properties"]["MemorySize"], 128); @@ -1530,9 +1499,9 @@ mod tests { serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 128}), ); let (classification, synthesis, document) = synthesized_json(&request); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationUpdate); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationUpdate); assert_eq!(classification.candidates, ["AWS::Lambda::Function"]); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedUpdate)); + assert_eq!(synthesis.source, Some(AwsCliTemplateSource::SynthesizedUpdate)); assert_eq!(document["Resources"]["Resource"]["Properties"], serde_json::json!({"MemorySize": 128})); assert_eq!(synthesis.diagnostic_properties, Some(BTreeSet::from(["MemorySize".into()]))); } @@ -1542,7 +1511,7 @@ mod tests { let schema_validator = SchemaValidator::default(); let request = request("lambda", "DeleteFunction", serde_json::json!({"FunctionName": "Synthetic"})); let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationDelete); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationDelete); assert_eq!(classification.candidates, ["AWS::Lambda::Function"]); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); @@ -1552,9 +1521,9 @@ mod tests { fn sns_create_topic_synthesizes_with_explicit_mappings() { let request = request("sns", "CreateTopic", serde_json::json!({"Name": "Synthetic"})); let (classification, synthesis, document) = synthesized_json(&request); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationCreate); assert_eq!(classification.candidates, ["AWS::SNS::Topic"]); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(synthesis.source, Some(AwsCliTemplateSource::SynthesizedCreate)); assert_eq!(document["Resources"]["Resource"]["Properties"]["TopicName"], "Synthetic"); } @@ -1563,7 +1532,7 @@ mod tests { let schema_validator = SchemaValidator::default(); let request = request("sns", "DeleteTopic", serde_json::json!({"TopicArn": "arn:aws:sns:us-east-1:123:Topic"})); let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationDelete); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationDelete); assert_eq!(classification.candidates, ["AWS::SNS::Topic"]); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); @@ -1574,9 +1543,9 @@ mod tests { let request = request("sqs", "CreateQueue", serde_json::json!({"QueueName": "Synthetic", "tags": {"Team": "CLI"}})); let (classification, synthesis, document) = synthesized_json(&request); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationCreate); assert_eq!(classification.candidates, ["AWS::SQS::Queue"]); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(synthesis.source, Some(AwsCliTemplateSource::SynthesizedCreate)); assert_eq!(document["Resources"]["Resource"]["Properties"]["QueueName"], "Synthetic"); } @@ -1586,7 +1555,7 @@ mod tests { let request = request("sqs", "DeleteQueue", serde_json::json!({"QueueUrl": "https://sqs.us-east-1.amazonaws.com/123/Q"})); let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationDelete); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationDelete); assert_eq!(classification.candidates, ["AWS::SQS::Queue"]); let synthesis = synthesize_request(&request, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); @@ -1619,19 +1588,19 @@ mod tests { let template = br#"{"Resources":{}}"#.to_vec(); let mut cfn_request = request("cloudformation", "CreateChangeSet", serde_json::json!({})); - cfn_request.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: template.clone() }); + cfn_request.parameters.insert("TemplateBody".into(), AwsCliValue::Bytes { value: template.clone() }); let classification = classify_operation(&cfn_request, &schema_validator).expect("classification succeeds"); let synthesis = synthesize_request(&cfn_request, &classification, &schema_validator).expect("synthesis succeeds"); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::TemplateBody)); + assert_eq!(synthesis.source, Some(AwsCliTemplateSource::TemplateBody)); assert_eq!(synthesis.template, Some(template.clone())); let mut s3_request = request("s3", "PutObject", serde_json::json!({})); - s3_request.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: template.clone() }); + s3_request.parameters.insert("TemplateBody".into(), AwsCliValue::Bytes { value: template.clone() }); let classification = classify_operation(&s3_request, &schema_validator).expect("classification succeeds"); let synthesis = synthesize_request(&s3_request, &classification, &schema_validator).expect("synthesis succeeds"); - assert_ne!(synthesis.source, Some(AwsApiTemplateSource::TemplateBody)); + assert_ne!(synthesis.source, Some(AwsCliTemplateSource::TemplateBody)); } #[test] @@ -1655,12 +1624,12 @@ mod tests { let template = br#"{"Resources":{}}"#.to_vec(); for (service, operation) in TEMPLATE_BODY_OPERATIONS { let mut req = request(service, operation, serde_json::json!({})); - req.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: template.clone() }); + req.parameters.insert("TemplateBody".into(), AwsCliValue::Bytes { value: template.clone() }); let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); assert_eq!( synthesis.source, - Some(AwsApiTemplateSource::TemplateBody), + Some(AwsCliTemplateSource::TemplateBody), "{service}:{operation} should accept TemplateBody" ); } @@ -1673,29 +1642,29 @@ mod tests { serde_json::json!({"TypeName": "AWS::SNS::Topic", "DesiredState": "{\"TopicName\":\"Synthetic\"}"}), ); let (classification, synthesis, document) = synthesized_json(&known); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationCreate); assert_eq!(classification.candidates, ["AWS::SNS::Topic"]); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::CloudControlDesiredState)); + assert_eq!(synthesis.source, Some(AwsCliTemplateSource::CloudControlDesiredState)); assert_eq!(document["Resources"]["Resource"]["Properties"]["TopicName"], "Synthetic"); } #[test] fn cloud_control_with_signing_prefix_cloudcontrolapi() { - let parameters: HashMap = serde_json::json!({ + let parameters: HashMap = serde_json::json!({ "TypeName": "AWS::SNS::Topic", "DesiredState": "{\"TopicName\":\"Synthetic\"}" }) .as_object() .unwrap() .iter() - .map(|(k, v)| (k.clone(), AwsApiValue::from_json(v.clone()))) + .map(|(k, v)| (k.clone(), AwsCliValue::from_json(v.clone()))) .collect(); let req = - AwsApiRequest::new("cloudcontrol", "CreateResource", parameters).with_service_prefix("cloudcontrolapi"); + AwsCliCommand::new("cloudcontrol", "CreateResource", parameters).with_service_prefix("cloudcontrolapi"); let (classification, synthesis, document) = synthesized_json(&req); - assert_eq!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_eq!(classification.kind, AwsCliOperationKind::CloudFormationCreate); assert_eq!(classification.candidates, ["AWS::SNS::Topic"]); - assert_eq!(synthesis.source, Some(AwsApiTemplateSource::CloudControlDesiredState)); + assert_eq!(synthesis.source, Some(AwsCliTemplateSource::CloudControlDesiredState)); assert_eq!(document["Resources"]["Resource"]["Properties"]["TopicName"], "Synthetic"); } @@ -1722,7 +1691,7 @@ mod tests { serde_json::json!({"TypeName": "AWS::SNS::Topic", "PatchDocument": "[{\"op\":\"replace\"}]"}), ); let classification = classify_operation(&update, &schema_validator).expect("classification succeeds"); - assert_eq!(classification.kind, AwsApiOperationKind::UnmappedMutation); + assert_eq!(classification.kind, AwsCliOperationKind::UnmappedMutation); assert_eq!(classification.candidates, ["AWS::SNS::Topic"]); let synthesis = synthesize_request(&update, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); @@ -1736,13 +1705,13 @@ mod tests { explicitly_readonly.is_read_only = Some(true); assert_eq!( classify_operation(&explicitly_readonly, &schema_validator).expect("classification succeeds").kind, - AwsApiOperationKind::ReadOnly + AwsCliOperationKind::ReadOnly ); let mut get_request = request("test", "FrobnicateThing", serde_json::json!({})); get_request.http_method = Some("GET".into()); assert_eq!( classify_operation(&get_request, &schema_validator).expect("classification succeeds").kind, - AwsApiOperationKind::ReadOnly + AwsCliOperationKind::ReadOnly ); } @@ -1752,7 +1721,7 @@ mod tests { let lambda_invoke = request("lambda", "Invoke", serde_json::json!({})); assert_eq!( classify_operation(&lambda_invoke, &schema_validator).expect("classification succeeds").kind, - AwsApiOperationKind::DataPlaneMutation + AwsCliOperationKind::DataPlaneMutation ); } #[test] @@ -1761,7 +1730,7 @@ mod tests { let req = request("ecs", "RunTask", serde_json::json!({"TaskDefinition": "my-task"})); let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); assert!(classification.candidates.is_empty(), "ecs:RunTask must not map to any resource type"); - assert_ne!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_ne!(classification.kind, AwsCliOperationKind::CloudFormationCreate); let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); } @@ -1772,7 +1741,7 @@ mod tests { let req = request("ec2", "StartInstances", serde_json::json!({"InstanceIds": ["i-12345"]})); let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); assert!(classification.candidates.is_empty(), "ec2:StartInstances must not map to any resource type"); - assert_ne!(classification.kind, AwsApiOperationKind::CloudFormationCreate); + assert_ne!(classification.kind, AwsCliOperationKind::CloudFormationCreate); let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); assert!(synthesis.template.is_none()); } @@ -1787,7 +1756,7 @@ mod tests { "iot:StartThingRegistrationTask must not map to any resource type" ); let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); - assert_ne!(synthesis.source, Some(AwsApiTemplateSource::TemplateBody)); + assert_ne!(synthesis.source, Some(AwsCliTemplateSource::TemplateBody)); } #[test] @@ -1796,12 +1765,12 @@ mod tests { let template = br#"{"Resources":{}}"#.to_vec(); for service in ["s3", "lambda", "iot", "dynamodb"] { let mut req = request(service, "SomeOperation", serde_json::json!({})); - req.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: template.clone() }); + req.parameters.insert("TemplateBody".into(), AwsCliValue::Bytes { value: template.clone() }); let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); assert_ne!( synthesis.source, - Some(AwsApiTemplateSource::TemplateBody), + Some(AwsCliTemplateSource::TemplateBody), "{service}:SomeOperation should not treat TemplateBody as CFN template" ); } @@ -1817,7 +1786,7 @@ mod tests { ); let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); - assert_ne!(synthesis.source, Some(AwsApiTemplateSource::CloudControlDesiredState)); + assert_ne!(synthesis.source, Some(AwsCliTemplateSource::CloudControlDesiredState)); } #[test] @@ -1866,11 +1835,11 @@ mod tests { let engine = NoopEngine::default(); let schema_validator = SchemaValidator::default(); let mut exact = request("cloudformation", "CreateChangeSet", serde_json::json!({})); - exact.parameters.insert("TemplateBody".into(), AwsApiValue::Bytes { value: br#"{"Resources":{}}"#.to_vec() }); - let validation = validate_aws_api_request(&engine, &schema_validator, &exact, ValidateConfig::default()) + exact.parameters.insert("TemplateBody".into(), AwsCliValue::Bytes { value: br#"{"Resources":{}}"#.to_vec() }); + let validation = validate_aws_cli_command(&engine, &schema_validator, &exact, ValidateConfig::default()) .expect("validation succeeds"); - assert_eq!(validation.status, AwsApiRequestValidationStatus::Validated); - assert_eq!(validation.template_source, Some(AwsApiTemplateSource::TemplateBody)); + assert_eq!(validation.status, AwsCliCommandValidationStatus::Validated); + assert_eq!(validation.template_source, Some(AwsCliTemplateSource::TemplateBody)); assert!(validation.report.is_some()); assert_eq!( validation.template, @@ -1879,10 +1848,10 @@ mod tests { ); let read = request("iam", "GetRole", serde_json::json!({"RoleName": "Synthetic"})); - let validation = validate_aws_api_request(&engine, &schema_validator, &read, ValidateConfig::default()) + let validation = validate_aws_cli_command(&engine, &schema_validator, &read, ValidateConfig::default()) .expect("classification succeeds"); - assert_eq!(validation.status, AwsApiRequestValidationStatus::Skipped); - assert_eq!(validation.operation_kind, AwsApiOperationKind::ReadOnly); + assert_eq!(validation.status, AwsCliCommandValidationStatus::Skipped); + assert_eq!(validation.operation_kind, AwsCliOperationKind::ReadOnly); assert!(validation.report.is_none()); assert_eq!(validation.template, None, "skipped requests must have template=None"); } @@ -1896,7 +1865,7 @@ mod tests { "UpdateFunctionConfiguration", serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 0}), ); - let validation = validate_aws_api_request(&engine, &schema_validator, &update, ValidateConfig::default()) + let validation = validate_aws_cli_command(&engine, &schema_validator, &update, ValidateConfig::default()) .expect("validation succeeds"); let report = validation.report.expect("update is validated"); assert!( @@ -1920,7 +1889,7 @@ mod tests { ("AttributeDefinitions".into(), value(serde_json::json!([{"AttributeName": "id", "AttributeType": "S"}]))), ]); let original = parameters.clone(); - let request = AwsApiRequest::new("dynamodb", "CreateTable", parameters); + let request = AwsCliCommand::new("dynamodb", "CreateTable", parameters); let schema_validator = SchemaValidator::default(); let classification = classify_operation(&request, &schema_validator).expect("classification succeeds"); let _ = synthesize_request(&request, &classification, &schema_validator); @@ -1929,9 +1898,9 @@ mod tests { #[test] fn json_conversion_rejects_non_json_values_without_coercion() { - assert!(AwsApiValue::Bytes { value: vec![1, 2] }.to_json().is_err()); - assert!(AwsApiValue::Number { value: f64::NAN }.to_json().is_err()); - assert!(AwsApiValue::Unsupported { type_name: "timestamp".into() }.to_json().is_err()); + assert!(AwsCliValue::Bytes { value: vec![1, 2] }.to_json().is_err()); + assert!(AwsCliValue::Number { value: f64::NAN }.to_json().is_err()); + assert!(AwsCliValue::Unsupported { type_name: "timestamp".into() }.to_json().is_err()); } #[test] @@ -1954,13 +1923,13 @@ mod tests { #[test] fn conflicting_service_prefix_does_not_map_adapter() { let schema_validator = SchemaValidator::default(); - let parameters: HashMap = serde_json::json!({"Bucket": "test"}) + let parameters: HashMap = serde_json::json!({"Bucket": "test"}) .as_object() .unwrap() .iter() - .map(|(k, v)| (k.clone(), AwsApiValue::from_json(v.clone()))) + .map(|(k, v)| (k.clone(), AwsCliValue::from_json(v.clone()))) .collect(); - let req = AwsApiRequest::new("ecs", "CreateBucket", parameters).with_service_prefix("s3"); + let req = AwsCliCommand::new("ecs", "CreateBucket", parameters).with_service_prefix("s3"); let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); assert!( classification.candidates.is_empty(), @@ -1976,14 +1945,14 @@ mod tests { fn conflicting_service_prefix_does_not_validate_template_body() { let schema_validator = SchemaValidator::default(); let template = br#"{"Resources":{}}"#.to_vec(); - let parameters: HashMap = - [("TemplateBody".to_string(), AwsApiValue::Bytes { value: template })].into_iter().collect(); - let req = AwsApiRequest::new("iot", "CreateStack", parameters).with_service_prefix("cloudformation"); + let parameters: HashMap = + [("TemplateBody".to_string(), AwsCliValue::Bytes { value: template })].into_iter().collect(); + let req = AwsCliCommand::new("iot", "CreateStack", parameters).with_service_prefix("cloudformation"); let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); let synthesis = synthesize_request(&req, &classification, &schema_validator).expect("synthesis succeeds"); assert_ne!( synthesis.source, - Some(AwsApiTemplateSource::TemplateBody), + Some(AwsCliTemplateSource::TemplateBody), "service_name=iot with service_prefix=cloudformation must not validate TemplateBody" ); } @@ -2004,10 +1973,10 @@ mod tests { let engine = NoopEngine::default(); let schema_validator = SchemaValidator::default(); let req = request("lambda", "CreateFunction", serde_json::json!({"MemorySize": 0})); - let validation = validate_aws_api_request(&engine, &schema_validator, &req, ValidateConfig::default()) + let validation = validate_aws_cli_command(&engine, &schema_validator, &req, ValidateConfig::default()) .expect("validation succeeds"); - assert_eq!(validation.status, AwsApiRequestValidationStatus::Validated); - assert_eq!(validation.template_source, Some(AwsApiTemplateSource::SynthesizedCreate)); + assert_eq!(validation.status, AwsCliCommandValidationStatus::Validated); + assert_eq!(validation.template_source, Some(AwsCliTemplateSource::SynthesizedCreate)); let report = validation.report.expect("create is validated"); for diagnostic in &report.diagnostics { assert!( @@ -2037,7 +2006,7 @@ mod tests { let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); assert_eq!( classification.kind, - AwsApiOperationKind::CloudFormationCreate, + AwsCliOperationKind::CloudFormationCreate, "cloudformation:{operation} must be CloudFormationCreate" ); } @@ -2051,7 +2020,7 @@ mod tests { let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); assert_eq!( classification.kind, - AwsApiOperationKind::CloudFormationUpdate, + AwsCliOperationKind::CloudFormationUpdate, "cloudformation:{operation} must be CloudFormationUpdate" ); } @@ -2065,7 +2034,7 @@ mod tests { let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); assert_eq!( classification.kind, - AwsApiOperationKind::ReadOnly, + AwsCliOperationKind::ReadOnly, "cloudformation:{operation} must be ReadOnly" ); } @@ -2077,17 +2046,17 @@ mod tests { let schema_validator = SchemaValidator::default(); let template = br#"{"Resources":{}}"#.to_vec(); for operation in ["EstimateTemplateCost", "GetTemplateSummary", "ValidateTemplate"] { - let parameters: HashMap = - [("TemplateBody".to_string(), AwsApiValue::Bytes { value: template.clone() })].into_iter().collect(); - let req = AwsApiRequest::new("cloudformation", operation, parameters); - let validation = validate_aws_api_request(&engine, &schema_validator, &req, ValidateConfig::default()) + let parameters: HashMap = + [("TemplateBody".to_string(), AwsCliValue::Bytes { value: template.clone() })].into_iter().collect(); + let req = AwsCliCommand::new("cloudformation", operation, parameters); + let validation = validate_aws_cli_command(&engine, &schema_validator, &req, ValidateConfig::default()) .expect("validation succeeds"); assert_eq!( validation.status, - AwsApiRequestValidationStatus::Validated, + AwsCliCommandValidationStatus::Validated, "cloudformation:{operation} with TemplateBody must still validate" ); - assert_eq!(validation.template_source, Some(AwsApiTemplateSource::TemplateBody)); + assert_eq!(validation.template_source, Some(AwsCliTemplateSource::TemplateBody)); } } @@ -2096,7 +2065,7 @@ mod tests { let schema_validator = SchemaValidator::default(); let req = request("cloudformation", "DeleteChangeSet", serde_json::json!({})); let classification = classify_operation(&req, &schema_validator).expect("classification succeeds"); - assert_eq!(classification.kind, AwsApiOperationKind::UnmappedMutation); + assert_eq!(classification.kind, AwsCliOperationKind::UnmappedMutation); assert!(classification.candidates.is_empty()); } @@ -2126,9 +2095,9 @@ mod tests { mappings: vec![mapping("Bucket", "BucketName")], ignored_inputs: vec!["ClientToken".into()], }; - let parameters: HashMap = [ - ("Bucket".into(), AwsApiValue::String { value: "test".into() }), - ("ClientToken".into(), AwsApiValue::String { value: "idempotent-token".into() }), + let parameters: HashMap = [ + ("Bucket".into(), AwsCliValue::String { value: "test".into() }), + ("ClientToken".into(), AwsCliValue::String { value: "idempotent-token".into() }), ] .into_iter() .collect(); @@ -2159,9 +2128,9 @@ mod tests { ignored_inputs: Vec::new(), }; // FunctionName is a primary identifier for AWS::Lambda::Function - let parameters: HashMap = [ - ("MemorySize".into(), AwsApiValue::Integer { value: 256 }), - ("FunctionName".into(), AwsApiValue::String { value: "my-func".into() }), + let parameters: HashMap = [ + ("MemorySize".into(), AwsCliValue::Integer { value: 256 }), + ("FunctionName".into(), AwsCliValue::String { value: "my-func".into() }), ] .into_iter() .collect(); diff --git a/src/validation-engine/src/lib.rs b/src/validation-engine/src/lib.rs index 2b899ca6..6a11be0b 100644 --- a/src/validation-engine/src/lib.rs +++ b/src/validation-engine/src/lib.rs @@ -1,14 +1,14 @@ #[cfg(feature = "uniffi-bindings")] uniffi::setup_scaffolding!(); -pub mod aws_api; +pub mod aws_cli; pub mod engine; pub mod guard; pub(crate) mod step_functions; -pub use aws_api::{ - AwsApiOperationKind, AwsApiRequest, AwsApiRequestContext, AwsApiRequestValidation, AwsApiRequestValidationStatus, - AwsApiTemplateSource, AwsApiValue, validate_aws_api_request, validate_aws_api_request_with_path, +pub use aws_cli::{ + AwsCliCommand, AwsCliCommandContext, AwsCliCommandValidation, AwsCliCommandValidationStatus, AwsCliOperationKind, + AwsCliTemplateSource, AwsCliValue, validate_aws_cli_command, validate_aws_cli_command_with_path, }; pub use engine::{ DIAGNOSTIC_SOURCE_PATH_FIELD, EngineConfig, EngineType, ExternalRuleSource, ValidateConfig, ValidationEngine, From 93434c7363d8b1930854341ad806472cf416441d Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Thu, 10 Sep 2026 17:24:38 -0400 Subject: [PATCH 10/19] add build tool --- src/data-source/README.md | 41 ++++--- .../examples/generate_aws_cli_catalog.rs | 78 +++++++++++++ src/data-source/src/aws_cli_catalog.rs | 79 ++++--------- src/data-source/src/generate.rs | 2 +- src/data-source/src/lib.rs | 106 +----------------- src/data-source/src/sync.rs | 19 +--- 6 files changed, 132 insertions(+), 193 deletions(-) create mode 100644 src/data-source/examples/generate_aws_cli_catalog.rs diff --git a/src/data-source/README.md b/src/data-source/README.md index 7c38aae4..801a1dad 100644 --- a/src/data-source/README.md +++ b/src/data-source/README.md @@ -8,28 +8,37 @@ compile time. Everything compiles into the binary - no runtime fetching. ## Commands ```bash -# Generate from existing upstream data +# Generate schema and rule artifacts from existing upstream data cargo run -p cloudformation-validate-data-source --features maintenance --example generate -# Refresh all upstream sources, then generate every output including the AWS API operation catalog -# (cfn-lint root and AWS CLI root are required) -cargo run -p cloudformation-validate-data-source --features maintenance --example sync -- \ - --cfn-lint-root \ - --aws-cli-root +# Refresh all upstream sources, then generate every output (cfn-lint root is required) +cargo run -p cloudformation-validate-data-source --features maintenance --example sync -- --cfn-lint-root + +# Regenerate the AWS CLI operation catalog (standalone; independent of sync/generate). +# --aws-cli-root points at a local aws-cli checkout. The resource data must already be +# present (upstream/schemas from a prior sync, plus the committed compiled schemas). +cargo run -p cloudformation-validate-data-source --features maintenance \ + --example generate_aws_cli_catalog -- --aws-cli-root ``` -The `generate` and `sync` examples require the `maintenance` feature, which enables dependencies used only by the -data maintenance pipeline. `sync` is the complete workflow: it refreshes every upstream source, records source -versions, generates the schema and rule outputs, then generates and verifies the AWS API operation catalog. Pass the -AWS CLI checkout root through `--aws-cli-root`; sync derives its bundled botocore package path, and the catalog -generator runs its unit tests before generating the catalog. +The examples require the `maintenance` feature, which enables dependencies used only by the data maintenance +pipeline. `sync` is the schema/rule workflow: it refreshes every upstream source, records source versions, and +generates the schema and rule outputs. `generate` reruns that schema and rule generation from existing upstream data +without network access. + +`--cfn-lint-root` is required by `sync`, which fails before starting work when it is absent. A successful sync records +its source-qualified versions only after all source processing succeeds. + +### AWS CLI operation catalog -`generate` reruns schema and rule generation from existing upstream data without network access. It does not rebuild -the AWS API operation catalog because that step requires botocore service models and is owned by the complete `sync` -workflow. +`generate_aws_cli_catalog` is a separate command that (re)builds +`generated/data/aws_cli_operation_catalog.json`, the adapter catalog that maps AWS CLI operations to CloudFormation +resource types for `validateAwsCliCommand`. It is intentionally decoupled from `sync`/`generate` because it derives +from AWS CLI botocore service models plus CloudFormation provider handler metadata and is regenerated on its own +cadence. It only generates the catalog and never downloads or processes schemas: it reads the provider schemas +already present under `upstream/schemas` (written by `sync`) and the committed compiled schemas under +`generated/schema-validator`, and fails with a clear message if either is missing. -`--cfn-lint-root` and `--aws-cli-root` are required by `sync`, which fails before starting work when either is absent. -A successful sync records both strict, source-qualified values together only after all source processing succeeds. ## Directory Structure diff --git a/src/data-source/examples/generate_aws_cli_catalog.rs b/src/data-source/examples/generate_aws_cli_catalog.rs new file mode 100644 index 00000000..0d7d9df1 --- /dev/null +++ b/src/data-source/examples/generate_aws_cli_catalog.rs @@ -0,0 +1,78 @@ +//! Standalone command that (re)generates the AWS CLI operation catalog. +//! +//! This is deliberately separate from the `sync` and `generate` maintenance +//! examples: it only generates the catalog and never downloads or processes +//! schemas. It assumes the resource data is already present - the provider +//! schemas under `upstream/schemas` (written by `sync`) and the committed +//! compiled schemas under `generated/schema-validator`. +//! +//! Usage: +//! cargo run -p cloudformation-validate-data-source --features maintenance \ +//! --example generate_aws_cli_catalog -- --aws-cli-root +//! +//! `--aws-cli-root` points at a local `aws-cli` checkout (its `awscli/` supplies +//! botocore). + +use data_source::generate_aws_cli_catalog; +use log::error; +use std::env; +use std::path::PathBuf; +use std::process; + +fn main() -> anyhow::Result<()> { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); + + let args: Vec = env::args().collect(); + let mut aws_cli_root: Option = None; + let mut i = 1; + while i < args.len() { + match args[i].as_str() { + "--aws-cli-root" => { + i += 1; + if i >= args.len() { + error!("--aws-cli-root requires a path argument"); + process::exit(1); + } + aws_cli_root = Some(args[i].clone()); + } + "--help" | "-h" => { + print_usage(); + return Ok(()); + } + other => { + error!("unknown argument: {other}"); + print_usage(); + process::exit(1); + } + } + i += 1; + } + + let Some(aws_cli_root) = aws_cli_root else { + print_usage(); + anyhow::bail!("--aws-cli-root is required"); + }; + + let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let upstream_dir = manifest.join("upstream"); + let generated_dir = manifest.join("generated"); + + generate_aws_cli_catalog(&upstream_dir, &generated_dir, &PathBuf::from(aws_cli_root))?; + Ok(()) +} + +fn print_usage() { + eprintln!( + "Usage: cargo run -p cloudformation-validate-data-source --features maintenance \\ + --example generate_aws_cli_catalog -- --aws-cli-root + +Generates the AWS CLI operation catalog (generated/data/aws_cli_operation_catalog.json) +from AWS CLI botocore service models and CloudFormation provider handler metadata. +It only generates the catalog: the resource data must already be present +(upstream/schemas from a prior sync, plus the committed compiled schemas). + +Options: + --aws-cli-root Path to a local aws-cli checkout (required) + -h, --help Show this help" + ); +} diff --git a/src/data-source/src/aws_cli_catalog.rs b/src/data-source/src/aws_cli_catalog.rs index 756601ce..c7cdb9e5 100644 --- a/src/data-source/src/aws_cli_catalog.rs +++ b/src/data-source/src/aws_cli_catalog.rs @@ -3,15 +3,16 @@ //! This is intentionally decoupled from the [`crate::sync_upstream`] / //! [`crate::generate_all`] maintenance pipeline: the catalog derives from AWS //! CLI botocore service models plus CloudFormation provider handler metadata, -//! sources that the rest of the build does not touch, and it runs on its own -//! cadence. The `generate_aws_cli_catalog` example is the command-line entry -//! point; this module owns the logic it calls. +//! and it runs on its own cadence. It only generates the catalog - it never +//! downloads or processes schemas. The resource data it reads must already be +//! present: the provider schemas under `upstream/schemas` (written by `sync`) +//! and the committed compiled schemas under `generated/schema-validator`. +//! The `generate_aws_cli_catalog` example is the command-line entry point; this +//! module owns the logic it calls. -use crate::schema; -use anyhow::Context; use log::info; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::Path; /// Format version the generator emits and the runtime loader accepts. const AWS_CLI_OPERATION_CATALOG_FORMAT_VERSION: u64 = 1; @@ -22,53 +23,35 @@ struct AwsCliOperationCatalog { adapters: Vec, } -/// Inputs for a standalone catalog generation run. -pub struct CatalogInputs { - /// Path to a local AWS CLI (`aws-cli`) checkout. Its `awscli/` directory - /// supplies the bundled botocore service models. - pub aws_cli_root: PathBuf, - /// Optional pre-downloaded provider-schema source (a directory of per-type - /// JSON files or a `schemas-cfn-lint.zip` archive). When `None`, the current - /// enhanced provider schemas are downloaded to a temporary directory. - pub provider_schemas: Option, -} - /// Generate the AWS CLI operation catalog into `generated_dir/data`. /// -/// Provider schemas are downloaded on demand when `inputs.provider_schemas` is -/// `None`, so this command does not depend on the maintenance `sync` having run. -/// The compiled CloudFormation schemas the generator verifies against are read -/// from the committed `generated_dir/schema-validator/compiled_schemas.json`. -pub fn generate_aws_cli_catalog(generated_dir: &Path, inputs: &CatalogInputs) -> anyhow::Result<()> { - let botocore_root = inputs.aws_cli_root.join("awscli"); +/// `aws_cli_root` is a local AWS CLI (`aws-cli`) checkout; its `awscli/` +/// directory supplies the bundled botocore service models. Provider schemas are +/// read from `upstream_dir/schemas` and compiled schemas from +/// `generated_dir/schema-validator/compiled_schemas.json`; both must already +/// exist - this command does not download or process schemas. +pub fn generate_aws_cli_catalog(upstream_dir: &Path, generated_dir: &Path, aws_cli_root: &Path) -> anyhow::Result<()> { + let botocore_root = aws_cli_root.join("awscli"); let botocore_package = botocore_root.join("botocore").join("__init__.py"); + let provider_schemas = upstream_dir.join("schemas"); let compiled_schemas = generated_dir.join("schema-validator").join("compiled_schemas.json"); let catalog_path = generated_dir.join("data").join("aws_cli_operation_catalog.json"); let script_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("scripts").join("generate_aws_cli_catalog.py"); anyhow::ensure!(script_path.is_file(), "AWS CLI catalog generator not found at {}", script_path.display()); - anyhow::ensure!(inputs.aws_cli_root.is_dir(), "AWS CLI checkout not found at {}", inputs.aws_cli_root.display()); + anyhow::ensure!(aws_cli_root.is_dir(), "AWS CLI checkout not found at {}", aws_cli_root.display()); anyhow::ensure!( botocore_package.is_file(), "AWS CLI checkout does not contain botocore at {}", botocore_package.display() ); + anyhow::ensure!( + provider_schemas.is_dir(), + "provider schemas not found at {}; run the sync example first to populate the resource data", + provider_schemas.display() + ); anyhow::ensure!(compiled_schemas.is_file(), "compiled schemas not found at {}", compiled_schemas.display()); - // Resolve provider schemas: use a caller-supplied path, or download the - // current enhanced schemas into a scratch directory owned by this run. - let downloaded_schemas; - let provider_schemas = match &inputs.provider_schemas { - Some(path) => { - anyhow::ensure!(path.exists(), "provider schemas source not found at {}", path.display()); - path.clone() - } - None => { - downloaded_schemas = download_provider_schemas()?; - downloaded_schemas - } - }; - info!("Generating AWS CLI operation catalog via {}", script_path.display()); let status = std::process::Command::new("python3") .arg(&script_path) @@ -91,26 +74,6 @@ pub fn generate_aws_cli_catalog(generated_dir: &Path, inputs: &CatalogInputs) -> Ok(()) } -/// Download the current enhanced provider schemas into a scratch directory under -/// `upstream/` and return the per-type schema directory. Each schema body carries -/// the handler metadata the generator reads, matching the layout its directory -/// reader expects. -fn download_provider_schemas() -> anyhow::Result { - let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let scratch = manifest.join("upstream").join("aws_cli_catalog_schemas"); - info!("Downloading enhanced provider schemas for the AWS CLI catalog into {}", scratch.display()); - if scratch.exists() { - fs::remove_dir_all(&scratch) - .with_context(|| format!("failed to clear schema scratch directory {}", scratch.display()))?; - } - // `download_schemas` writes the per-region provider maps and the per-type - // schema bodies (which carry handler metadata) under `/schemas`. - let (stats, version) = schema::download_schemas(&scratch)?; - stats.fail_on_errors("AWS CLI catalog schema download")?; - info!("Downloaded {} provider schemas (version {})", stats.files_written, version); - Ok(schema::schema_dir(&scratch)) -} - fn validate_catalog(catalog_bytes: &[u8]) -> anyhow::Result { let catalog: AwsCliOperationCatalog = serde_json::from_slice(catalog_bytes) .map_err(|error| anyhow::anyhow!("generated AWS CLI operation catalog is invalid JSON: {error}"))?; diff --git a/src/data-source/src/generate.rs b/src/data-source/src/generate.rs index f39d89e0..78799e9e 100644 --- a/src/data-source/src/generate.rs +++ b/src/data-source/src/generate.rs @@ -9,7 +9,7 @@ fn main() -> anyhow::Result<()> { eprintln!( "Usage: cargo run -p cloudformation-validate-data-source --features maintenance --example generate\n\n\ Generates all outputs from existing upstream data.\n\ - To refresh upstream data first, run `cargo run -p cloudformation-validate-data-source --features maintenance --example sync -- --cfn-lint-root --aws-cli-root `." + To refresh upstream data first, run `cargo run -p cloudformation-validate-data-source --features maintenance --example sync -- --cfn-lint-root `." ); return Ok(()); } diff --git a/src/data-source/src/lib.rs b/src/data-source/src/lib.rs index 6381ca1f..f32fb932 100644 --- a/src/data-source/src/lib.rs +++ b/src/data-source/src/lib.rs @@ -9,6 +9,8 @@ pub mod additional_schema_source; #[cfg(feature = "maintenance")] pub mod additional_specs; #[cfg(feature = "maintenance")] +pub mod aws_cli_catalog; +#[cfg(feature = "maintenance")] pub mod cfnlint_tables; #[cfg(feature = "maintenance")] pub mod codegen_cel; @@ -29,6 +31,8 @@ mod source_versions; pub mod types; pub use additional_schema_source::{AdditionalSchemaSource, SchemaSourceError}; +#[cfg(feature = "maintenance")] +pub use aws_cli_catalog::generate_aws_cli_catalog; #[cfg(feature = "maintenance")] use log::{error, info}; @@ -135,108 +139,6 @@ pub fn sync_upstream(upstream_dir: &Path, rule_source_root: &str) -> anyhow::Res Ok(()) } -#[cfg(feature = "maintenance")] -const AWS_API_OPERATION_CATALOG_FORMAT_VERSION: u64 = 1; - -#[cfg(feature = "maintenance")] -#[derive(serde::Deserialize)] -struct AwsApiOperationCatalog { - format_version: u64, - adapters: Vec, -} - -/// Generate the AWS API operation catalog from synced provider schemas and compiled schemas. -#[cfg(feature = "maintenance")] -pub fn generate_aws_api_catalog(upstream_dir: &Path, generated_dir: &Path, aws_cli_root: &Path) -> anyhow::Result<()> { - let botocore_root = aws_cli_root.join("awscli"); - let botocore_package = botocore_root.join("botocore").join("__init__.py"); - let provider_schemas = upstream_dir.join("schemas"); - let compiled_schemas = generated_dir.join("schema-validator").join("compiled_schemas.json"); - let catalog_path = generated_dir.join("data").join("aws_api_operation_catalog.json"); - let script_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("scripts").join("generate_aws_api_catalog.py"); - - anyhow::ensure!(script_path.is_file(), "AWS API catalog generator not found at {}", script_path.display()); - anyhow::ensure!(aws_cli_root.is_dir(), "AWS CLI checkout not found at {}", aws_cli_root.display()); - anyhow::ensure!( - botocore_package.is_file(), - "AWS CLI checkout does not contain botocore at {}", - botocore_package.display() - ); - anyhow::ensure!( - provider_schemas.is_dir(), - "provider schemas directory not found at {}", - provider_schemas.display() - ); - anyhow::ensure!(compiled_schemas.is_file(), "compiled schemas not found at {}", compiled_schemas.display()); - - info!("Generating AWS API operation catalog via {}", script_path.display()); - let status = std::process::Command::new("python3") - .arg(&script_path) - .arg("--botocore-root") - .arg(&botocore_root) - .arg("--provider-schemas") - .arg(&provider_schemas) - .arg("--compiled-schemas") - .arg(&compiled_schemas) - .arg("--output") - .arg(&catalog_path) - .status() - .map_err(|error| anyhow::anyhow!("failed to start AWS API catalog generator: {error}"))?; - anyhow::ensure!(status.success(), "AWS API catalog generator failed with {status}"); - - let catalog_bytes = fs::read(&catalog_path) - .map_err(|error| anyhow::anyhow!("failed to read generated catalog {}: {error}", catalog_path.display()))?; - let adapter_count = validate_aws_api_catalog(&catalog_bytes)?; - info!("Generated AWS API operation catalog with {adapter_count} adapters at {}", catalog_path.display()); - Ok(()) -} - -#[cfg(feature = "maintenance")] -fn validate_aws_api_catalog(catalog_bytes: &[u8]) -> anyhow::Result { - let catalog: AwsApiOperationCatalog = serde_json::from_slice(catalog_bytes) - .map_err(|error| anyhow::anyhow!("generated AWS API operation catalog is invalid JSON: {error}"))?; - anyhow::ensure!( - catalog.format_version == AWS_API_OPERATION_CATALOG_FORMAT_VERSION, - "generated AWS API operation catalog has format version {}, expected {}", - catalog.format_version, - AWS_API_OPERATION_CATALOG_FORMAT_VERSION - ); - anyhow::ensure!(!catalog.adapters.is_empty(), "generated AWS API operation catalog contains no adapters"); - Ok(catalog.adapters.len()) -} - -#[cfg(all(test, feature = "maintenance"))] -mod aws_api_catalog_tests { - use super::*; - - #[test] - fn current_catalog_format_with_adapters_is_valid() { - let catalog = br#"{"format_version":1,"adapters":[{}]}"#; - - let adapter_count = validate_aws_api_catalog(catalog).expect("catalog should be valid"); - - assert_eq!(1, adapter_count); - } - - #[test] - fn unsupported_catalog_format_is_rejected() { - let catalog = br#"{"format_version":2,"adapters":[{}]}"#; - - let error = validate_aws_api_catalog(catalog).expect_err("unsupported format must fail"); - - assert!(error.to_string().contains("format version 2, expected 1")); - } - - #[test] - fn catalog_without_adapters_is_rejected() { - let catalog = br#"{"format_version":1,"adapters":[]}"#; - - let error = validate_aws_api_catalog(catalog).expect_err("empty adapters must fail"); - - assert!(error.to_string().contains("contains no adapters")); - } -} - #[cfg(feature = "maintenance")] pub fn generate_all(upstream_dir: &Path, generated_dir: &Path, handwritten_dir: &Path) -> anyhow::Result<()> { info!("=== Generate phase ==="); diff --git a/src/data-source/src/sync.rs b/src/data-source/src/sync.rs index aca87a1e..109a956e 100644 --- a/src/data-source/src/sync.rs +++ b/src/data-source/src/sync.rs @@ -1,5 +1,5 @@ use anyhow::Context; -use data_source::{generate_all, generate_aws_api_catalog, sync_upstream}; +use data_source::{generate_all, sync_upstream}; use log::{error, info}; use std::env; use std::fs; @@ -12,7 +12,6 @@ fn main() -> anyhow::Result<()> { let args: Vec = env::args().collect(); let mut rule_source_root: Option = None; - let mut aws_cli_root: Option = None; let mut i = 1; while i < args.len() { match args[i].as_str() { @@ -24,14 +23,6 @@ fn main() -> anyhow::Result<()> { } rule_source_root = Some(args[i].clone()); } - "--aws-cli-root" => { - i += 1; - if i >= args.len() { - error!("--aws-cli-root requires a path argument"); - process::exit(1); - } - aws_cli_root = Some(args[i].clone()); - } "--help" | "-h" => { print_usage(); return Ok(()); @@ -46,7 +37,6 @@ fn main() -> anyhow::Result<()> { } let rule_source_root = rule_source_root.ok_or_else(|| anyhow::anyhow!("--cfn-lint-root is required"))?; - let aws_cli_root = aws_cli_root.ok_or_else(|| anyhow::anyhow!("--aws-cli-root is required"))?; let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let upstream_dir = manifest.join("upstream"); let generated_dir = manifest.join("generated"); @@ -58,7 +48,6 @@ fn main() -> anyhow::Result<()> { sync_upstream(&upstream_dir, &rule_source_root)?; generate_all(&upstream_dir, &generated_dir, &handwritten_dir)?; - generate_aws_api_catalog(&upstream_dir, &generated_dir, Path::new(&aws_cli_root))?; info!("Sync and generation complete"); Ok(()) @@ -72,14 +61,12 @@ fn clear_cache_directory(cache_directory: &Path) -> anyhow::Result<()> { fn print_usage() { eprintln!( - "Usage: cargo run -p cloudformation-validate-data-source --features maintenance --example sync -- --cfn-lint-root --aws-cli-root + "Usage: cargo run -p cloudformation-validate-data-source --features maintenance --example sync -- --cfn-lint-root -Refreshes all upstream sources, records their versions, generates every output, -and rebuilds the AWS API operation catalog. +Refreshes all upstream sources, records their versions, and generates every output. Options: --cfn-lint-root Path to cfn-lint repo (required) - --aws-cli-root Path to AWS CLI checkout (required) -h, --help Show this help" ); } From da3ccaa38dc15b2f1538f4b093aec8e86e772967 Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Fri, 11 Sep 2026 15:41:59 +0000 Subject: [PATCH 11/19] Update build process and catalog generation --- src/data-source/README.md | 8 + .../scripts/generate_aws_cli_catalog.py | 264 ++++++++++-- src/data-source/src/aws_cli_catalog.rs | 51 ++- src/data-source/src/lib.rs | 10 +- src/validation-engine/API.md | 17 + src/validation-engine/src/aws_cli.rs | 400 +++++++++++++++++- 6 files changed, 697 insertions(+), 53 deletions(-) diff --git a/src/data-source/README.md b/src/data-source/README.md index 801a1dad..2da7f943 100644 --- a/src/data-source/README.md +++ b/src/data-source/README.md @@ -39,6 +39,14 @@ cadence. It only generates the catalog and never downloads or processes schemas: already present under `upstream/schemas` (written by `sync`) and the committed compiled schemas under `generated/schema-validator`, and fails with a clear message if either is missing. +Besides the operation-to-type and parameter-to-property pairs, each mapping records the API value domain that the +compiled CloudFormation schema cannot represent (`unrepresentable`: enum members, numeric bounds, string lengths, list +sizes, tag key/value lengths), derived by comparing the botocore input shape against the compiled property schema. The +runtime uses it to skip synthesis for a command whose values the service accepts but CloudFormation would reject. +Same-named inputs whose meaning differs from the CloudFormation property are excluded by the reviewed +`PROPERTY_SEMANTIC_DENYLIST` in the script. Re-run the command after `sync` (which refreshes the provider schemas and +compiled schemas), after changing the generator's mapping rules, or after updating the AWS CLI checkout. + ## Directory Structure diff --git a/src/data-source/scripts/generate_aws_cli_catalog.py b/src/data-source/scripts/generate_aws_cli_catalog.py index a506abdd..f7ba2593 100644 --- a/src/data-source/scripts/generate_aws_cli_catalog.py +++ b/src/data-source/scripts/generate_aws_cli_catalog.py @@ -1,14 +1,18 @@ #!/usr/bin/env python3 """Generate the AWS API operation adapter catalog for validation-engine. -Derives CloudFormation-type -> API-operation adapters from two public sources: - -1. CloudFormation resource provider schemas WITH handler metadata - (https://github.com/aws-cloudformation/resource-provider-enhanced-schemas - releases, ``schemas-standard.zip``). Each type's own create/delete handler - permissions contain the type's canonical lifecycle API actions. -2. Botocore service models (importable ``botocore``), which resolve IAM action - prefixes to real services and operations and provide exact input shapes. +Derives CloudFormation-type -> API-operation adapters from two sources: + +1. CloudFormation resource provider schemas WITH handler metadata, as synced + into ``upstream/schemas`` (from the + https://github.com/aws-cloudformation/resource-provider-enhanced-schemas + releases). Each type's own create/delete handler permissions contain the + type's canonical lifecycle API actions. Every mapping is verified against + the committed compiled schemas + (``generated/schema-validator/compiled_schemas.json``). +2. Botocore service models (importable ``botocore`` from an AWS CLI checkout), + which resolve IAM action prefixes to real services and operations and + provide exact input shapes. Derivation direction is type -> operation, scoped to one type's own handler permissions at a time. The global inverse (operation -> type by name) is @@ -31,7 +35,13 @@ case-insensitive identifier match, or via the reviewed identifier-rename allowlist (``PROPERTY_RENAME_ALLOWLIST``); a differently-named property is accepted only for a fully reviewed cfn_type/service/operation/source/target - context + context; a same-named member whose meaning differs from the property + (``PROPERTY_SEMANTIC_DENYLIST``) never maps +- value-domain verification: an API input value that the service accepts but + the CloudFormation schema rejects (enum members, numeric bounds, string + lengths, list sizes, tag key/value lengths) is recorded per mapping as an + ``unrepresentable`` domain so the runtime skips synthesis for such a value + instead of reporting a CloudFormation finding against a valid API call - noun agreement or property-overlap thresholds; ties are dropped entirely - global reverse uniqueness: one (service, operation) key maps to exactly one catalog entry; unresolvable collisions are dropped entirely @@ -41,8 +51,8 @@ Usage: python3 generate_aws_cli_catalog.py \ - --botocore-root /path/to/botocore \ - --provider-schemas schemas-standard.zip \ + --botocore-root /path/to/aws-cli/awscli \ + --provider-schemas ../upstream/schemas \ --compiled-schemas ../generated/schema-validator/compiled_schemas.json \ --output ../generated/data/aws_cli_operation_catalog.json """ @@ -51,6 +61,7 @@ import hashlib import importlib import json +import re import sys import zipfile from collections import defaultdict @@ -121,6 +132,22 @@ ('AWS::Timestream::ScheduledQuery', 'timestream-query', 'CreateScheduledQuery', 'Name', 'ScheduledQueryName'), }) +# Reviewed same-identifier mappings whose meaning differs between the API and +# the CloudFormation property, so a value that is valid for the API can never +# be the CloudFormation value. Each entry names an API member carrying a bare +# resource identifier where the same-named CloudFormation property carries the +# resource ARN (its schema pattern is anchored on ``^arn:``). Such a member is +# never mapped; when supplied, the request is skipped at runtime. +PROPERTY_SEMANTIC_DENYLIST = frozenset({ + ('AWS::Connect::ApprovedOrigin', 'connect', 'AssociateApprovedOrigin', 'InstanceId', 'InstanceId'), + ('AWS::Connect::ContactFlowModuleAlias', 'connect', 'CreateContactFlowModuleAlias', 'ContactFlowModuleId', 'ContactFlowModuleId'), + ('AWS::Connect::ContactFlowModuleVersion', 'connect', 'CreateContactFlowModuleVersion', 'ContactFlowModuleId', 'ContactFlowModuleId'), + ('AWS::Connect::ContactFlowVersion', 'connect', 'CreateContactFlowVersion', 'ContactFlowId', 'ContactFlowId'), + ('AWS::Connect::IntegrationAssociation', 'connect', 'CreateIntegrationAssociation', 'InstanceId', 'InstanceId'), + ('AWS::Connect::SecurityKey', 'connect', 'AssociateSecurityKey', 'InstanceId', 'InstanceId'), + ('AWS::S3Outposts::BucketPolicy', 's3control', 'PutBucketPolicy', 'Bucket', 'Bucket'), +}) + # CFN service segments whose botocore/IAM service identity differs beyond casing. # Maps a normalized CFN service segment to the exact normalized botocore service # identities it may relate to (lowercased, punctuation stripped). This reviewed @@ -401,6 +428,21 @@ def _source_sha256(source_path): return digest.hexdigest() +def _aws_cli_version(botocore_root): + """Release version of the AWS CLI checkout whose ``awscli/`` is ``botocore_root``. + + Read textually from ``awscli/__init__.py`` because importing ``awscli`` + installs import hooks and is not needed to learn the version. + """ + init_path = botocore_root / '__init__.py' + match = re.search( + r"^__version__\s*=\s*['\"]([^'\"]+)['\"]", init_path.read_text(), re.M + ) + if match is None: + raise SystemExit(f'cannot determine the AWS CLI version from {init_path}') + return match.group(1) + + def _load_provider_schemas(source_path): schemas = {} if source_path.is_dir(): @@ -635,6 +677,148 @@ def _is_runtime_safe_mapping(source_shape, target_schema, definitions, target): return False +def _shape_bounds(shape): + """botocore ``min``/``max`` traits: string length, list size, or numeric range.""" + metadata = getattr(shape, 'metadata', None) or {} + return metadata.get('min'), metadata.get('max') + + +def _cfn_pattern_rejects(pattern, value): + """True only when the CloudFormation pattern provably rejects ``value``. + + JSON Schema patterns are unanchored ECMA regexes; ``re.search`` mirrors + that. A pattern Python cannot compile is treated as not rejecting anything, + so a mapping is never restricted on unverifiable evidence. + """ + try: + return re.search(pattern, value) is None + except re.error: + return False + + +def _string_unrepresentable(source_shape, target_schema, definitions): + node = ( + _schema_node_for_type(target_schema, definitions, 'string') + or _resolve_schema_node(target_schema, definitions) + ) + domain = {} + api_min, api_max = _shape_bounds(source_shape) + cfn_min = node.get('min_length') + cfn_max = node.get('max_length') + api_enum = list(getattr(source_shape, 'enum', None) or []) + if api_enum: + cfn_enum = node.get('enum') + pattern = node.get('pattern') + rejected = sorted( + value for value in api_enum + if (isinstance(cfn_enum, list) and value not in cfn_enum) + or (pattern and _cfn_pattern_rejects(pattern, value)) + or (cfn_min is not None and len(value) < cfn_min) + or (cfn_max is not None and len(value) > cfn_max) + ) + if rejected: + domain['rejected_values'] = rejected + return domain + if api_max is not None and cfn_max is not None and api_max > cfn_max: + domain['max_length'] = cfn_max + if api_min is not None and cfn_min is not None and api_min < cfn_min: + domain['min_length'] = cfn_min + return domain + + +def _numeric_unrepresentable(source_shape, target_schema, definitions): + node = ( + _schema_node_for_type(target_schema, definitions, 'integer') + or _schema_node_for_type(target_schema, definitions, 'number') + or _resolve_schema_node(target_schema, definitions) + ) + domain = {} + api_min, api_max = _shape_bounds(source_shape) + cfn_min = node.get('minimum') + cfn_max = node.get('maximum') + if api_max is not None and cfn_max is not None and api_max > cfn_max: + domain['maximum'] = cfn_max + if api_min is not None and cfn_min is not None and api_min < cfn_min: + domain['minimum'] = cfn_min + return domain + + +def _tag_field_schema(item_schema, definitions, field): + """Schema node of the ``Key`` or ``Value`` field of a Key/Value tag item.""" + item_schema = _resolve_tag_schema_node(item_schema, definitions) or {} + candidates = [item_schema] + for alternatives in ('any_of', 'one_of'): + candidates.extend(item_schema.get(alternatives) or []) + for candidate in candidates: + candidate = _resolve_tag_schema_node(candidate, definitions) or {} + properties = candidate.get('properties') or {} + if field in properties: + return properties[field] + return {} + + +def _unrepresentable_domain(source_shape, target_schema, definitions, target): + """API-valid values of ``source_shape`` that ``target_schema`` rejects. + + Only provable exclusions are recorded: an API enum member absent from the + CloudFormation enum or rejected by its pattern, and API numeric, length, or + size bounds that exceed the CloudFormation bounds. The returned mapping is + empty when every API-valid value has a CloudFormation representation. + Keys name the CloudFormation constraint the API range exceeds: + ``rejected_values`` (API enum members CloudFormation rejects), + ``minimum``/``maximum`` (numeric bounds), ``min_length``/``max_length`` + (string length or list size), ``items`` (nested domain of list elements), + ``tag_key``/``tag_value`` (nested string domains for tag maps). + """ + source_type = source_shape.type_name + if source_type == 'string': + return _string_unrepresentable(source_shape, target_schema, definitions) + if source_type in ('integer', 'long', 'float', 'double'): + return _numeric_unrepresentable(source_shape, target_schema, definitions) + if source_type == 'list': + array_schema = _schema_node_for_type( + target_schema, definitions, 'array' + ) or {} + domain = {} + item_domain = _unrepresentable_domain( + source_shape.member, array_schema.get('items') or {}, definitions, + target, + ) + if item_domain: + domain['items'] = item_domain + api_min, api_max = _shape_bounds(source_shape) + cfn_min = array_schema.get('min_items') + cfn_max = array_schema.get('max_items') + if api_max is not None and cfn_max is not None and api_max > cfn_max: + domain['max_length'] = cfn_max + if api_min is not None and cfn_min is not None and api_min < cfn_min: + domain['min_length'] = cfn_min + return domain + if source_type == 'map' and target == 'Tags': + array_schema = _schema_node_for_type( + target_schema, definitions, 'array' + ) or {} + item_schema = array_schema.get('items') or {} + domain = {} + for field, key in (('Key', 'tag_key'), ('Value', 'tag_value')): + field_domain = _string_unrepresentable( + source_shape.key if field == 'Key' else source_shape.value, + _tag_field_schema(item_schema, definitions, field), + definitions, + ) + if field_domain: + domain[key] = field_domain + return domain + return {} + + +def _mapping_entry(source, target, unrepresentable): + entry = {'source': source, 'target': target} + if unrepresentable: + entry['unrepresentable'] = unrepresentable + return entry + + def _property_mappings( members, property_schemas, writable_by_lower, resource_segment, definitions, cfn_type, service, operation @@ -642,9 +826,12 @@ def _property_mappings( """Return mappings the runtime can serialize without nested rewriting. A member maps onto a property with the same identifier (case-insensitive) - unconditionally. A member that maps onto a differently-named property is a - reviewed rename, accepted only when (cfn_type, service, operation, member, - target) is present in ``PROPERTY_RENAME_ALLOWLIST``. + unconditionally unless the pair is a reviewed semantic mismatch + (``PROPERTY_SEMANTIC_DENYLIST``). A member that maps onto a + differently-named property is a reviewed rename, accepted only when + (cfn_type, service, operation, member, target) is present in + ``PROPERTY_RENAME_ALLOWLIST``. Each mapping carries the API value domain the + CloudFormation schema cannot represent, when any exists. """ mappings = [] for member in sorted(members): @@ -652,6 +839,8 @@ def _property_mappings( target = None if lowered in writable_by_lower: target = writable_by_lower[lowered] + if (cfn_type, service, operation, member, target) in PROPERTY_SEMANTIC_DENYLIST: + target = None else: renamed_target = None if lowered + 'name' in writable_by_lower: @@ -665,7 +854,13 @@ def _property_mappings( if target and _is_runtime_safe_mapping( members[member], property_schemas[target], definitions, target ): - mappings.append((member, target)) + mappings.append(_mapping_entry( + member, + target, + _unrepresentable_domain( + members[member], property_schemas[target], definitions, target + ), + )) return mappings @@ -780,10 +975,7 @@ def _derive_role(role, verbs, provider_schemas, compiled_schemas, index, require 'service': top[4], 'operation': top[5], 'phase': role, - 'mappings': [ - {'source': source, 'target': target} - for source, target in mappings - ], + 'mappings': list(mappings), 'ignored_inputs': _ignored_inputs_for_operation( index.input_members(top[4], top[5]), role, top[4], top[5] ), @@ -820,6 +1012,12 @@ def _enforce_global_uniqueness(adapters): def _verify_curated_updates(compiled_schemas, index): + """Return the curated update adapters, verified like derived ones. + + Each returned adapter is a copy whose mappings carry the API value domain the + CloudFormation schema cannot represent. + """ + verified_adapters = [] for adapter in CURATED_UPDATE_ADAPTERS: constraints = _compiled_constraints(compiled_schemas, adapter['cfn_type']) if constraints is None: @@ -829,6 +1027,7 @@ def _verify_curated_updates(compiled_schemas, index): property_schemas, read_only, primary, definitions = constraints members = index.input_members(adapter['service'], adapter['operation']) mapping_sources = set() + verified_mappings = [] for mapping in adapter['mappings']: if mapping['source'] not in members: raise SystemExit( @@ -841,6 +1040,14 @@ def _verify_curated_updates(compiled_schemas, index): raise SystemExit( f"curated mapping target {target} is invalid for {adapter['cfn_type']}" ) + if ( + adapter['cfn_type'], adapter['service'], adapter['operation'], + mapping['source'], target, + ) in PROPERTY_SEMANTIC_DENYLIST: + raise SystemExit( + f"curated mapping {mapping['source']} -> {target} is a reviewed " + "semantic mismatch" + ) if not _is_runtime_safe_mapping( members[mapping['source']], property_schemas[target], definitions, target @@ -849,6 +1056,14 @@ def _verify_curated_updates(compiled_schemas, index): f"curated mapping {mapping['source']} -> {target} is not " "runtime shape-compatible" ) + verified_mappings.append(_mapping_entry( + mapping['source'], + target, + _unrepresentable_domain( + members[mapping['source']], property_schemas[target], + definitions, target, + ), + )) for ignored_name in adapter.get('ignored_inputs', []): if ignored_name not in members: raise SystemExit( @@ -860,6 +1075,8 @@ def _verify_curated_updates(compiled_schemas, index): f"curated ignored_inputs entry '{ignored_name}' overlaps a mapping " f"source in {adapter['service']}:{adapter['operation']}" ) + verified_adapters.append({**adapter, 'mappings': verified_mappings}) + return verified_adapters def _compute_coverage(unique_adapters, index, compiled_schemas): @@ -1109,13 +1326,9 @@ def main(): deletes, delete_counters = _derive_role( 'delete', DELETE_VERBS, provider_schemas, compiled_schemas, index, False ) - _verify_curated_updates(compiled_schemas, index) + curated_updates = _verify_curated_updates(compiled_schemas, index) - all_adapters = ( - list(creates.values()) - + list(deletes.values()) - + [dict(adapter) for adapter in CURATED_UPDATE_ADAPTERS] - ) + all_adapters = list(creates.values()) + list(deletes.values()) + curated_updates unique_adapters, dropped = _enforce_global_uniqueness(all_adapters) for adapter in unique_adapters: @@ -1150,6 +1363,7 @@ def main(): 'compiled_schemas_sha256': _source_sha256( args.compiled_schemas ), + 'aws_cli_version': _aws_cli_version(args.botocore_root), 'botocore_version': botocore_module.__version__, 'botocore_service_count': index.service_count, 'provider_type_count': len(provider_schemas), diff --git a/src/data-source/src/aws_cli_catalog.rs b/src/data-source/src/aws_cli_catalog.rs index c7cdb9e5..1d83c464 100644 --- a/src/data-source/src/aws_cli_catalog.rs +++ b/src/data-source/src/aws_cli_catalog.rs @@ -10,6 +10,8 @@ //! The `generate_aws_cli_catalog` example is the command-line entry point; this //! module owns the logic it calls. +use crate::source_versions::{AWS_CLI_SOURCE, SOURCE_VERSIONS_FILE, SourceVersions}; +use crate::write_source_versions; use log::info; use std::fs; use std::path::Path; @@ -17,10 +19,17 @@ use std::path::Path; /// Format version the generator emits and the runtime loader accepts. const AWS_CLI_OPERATION_CATALOG_FORMAT_VERSION: u64 = 1; -#[derive(serde::Deserialize)] +#[derive(Debug, serde::Deserialize)] struct AwsCliOperationCatalog { format_version: u64, adapters: Vec, + source: AwsCliOperationCatalogSource, +} + +#[derive(Debug, serde::Deserialize)] +struct AwsCliOperationCatalogSource { + /// Release version of the AWS CLI checkout whose bundled botocore models the catalog derives from. + aws_cli_version: String, } /// Generate the AWS CLI operation catalog into `generated_dir/data`. @@ -69,12 +78,20 @@ pub fn generate_aws_cli_catalog(upstream_dir: &Path, generated_dir: &Path, aws_c let catalog_bytes = fs::read(&catalog_path) .map_err(|error| anyhow::anyhow!("failed to read generated catalog {}: {error}", catalog_path.display()))?; - let adapter_count = validate_catalog(&catalog_bytes)?; - info!("Generated AWS CLI operation catalog with {adapter_count} adapters at {}", catalog_path.display()); + let catalog = validate_catalog(&catalog_bytes)?; + info!("Generated AWS CLI operation catalog with {} adapters at {}", catalog.adapters.len(), catalog_path.display()); + + let source_versions_path = generated_dir.join("data").join(SOURCE_VERSIONS_FILE); + let aws_cli_version = format!("{AWS_CLI_SOURCE}@{}", catalog.source.aws_cli_version); + let source_versions = SourceVersions::read(&source_versions_path) + .and_then(|versions| versions.with_aws_cli_version(aws_cli_version.clone())) + .map_err(anyhow::Error::msg)?; + write_source_versions(&source_versions_path, source_versions)?; + info!("Recorded {aws_cli_version} in {}", source_versions_path.display()); Ok(()) } -fn validate_catalog(catalog_bytes: &[u8]) -> anyhow::Result { +fn validate_catalog(catalog_bytes: &[u8]) -> anyhow::Result { let catalog: AwsCliOperationCatalog = serde_json::from_slice(catalog_bytes) .map_err(|error| anyhow::anyhow!("generated AWS CLI operation catalog is invalid JSON: {error}"))?; anyhow::ensure!( @@ -84,7 +101,11 @@ fn validate_catalog(catalog_bytes: &[u8]) -> anyhow::Result { AWS_CLI_OPERATION_CATALOG_FORMAT_VERSION ); anyhow::ensure!(!catalog.adapters.is_empty(), "generated AWS CLI operation catalog contains no adapters"); - Ok(catalog.adapters.len()) + anyhow::ensure!( + !catalog.source.aws_cli_version.trim().is_empty(), + "generated AWS CLI operation catalog does not record the AWS CLI version" + ); + Ok(catalog) } #[cfg(test)] @@ -93,22 +114,32 @@ mod tests { #[test] fn current_catalog_format_with_adapters_is_valid() { - let catalog = br#"{"format_version":1,"adapters":[{}]}"#; - let adapter_count = validate_catalog(catalog).expect("catalog should be valid"); - assert_eq!(1, adapter_count); + let catalog = br#"{"format_version":1,"adapters":[{}],"source":{"aws_cli_version":"2.36.43"}}"#; + let catalog = validate_catalog(catalog).expect("catalog should be valid"); + assert_eq!(1, catalog.adapters.len()); + assert_eq!("2.36.43", catalog.source.aws_cli_version); } #[test] fn unsupported_catalog_format_is_rejected() { - let catalog = br#"{"format_version":2,"adapters":[{}]}"#; + let catalog = br#"{"format_version":2,"adapters":[{}],"source":{"aws_cli_version":"2.36.43"}}"#; let error = validate_catalog(catalog).expect_err("unsupported format must fail"); assert!(error.to_string().contains("format version 2, expected 1")); } #[test] fn catalog_without_adapters_is_rejected() { - let catalog = br#"{"format_version":1,"adapters":[]}"#; + let catalog = br#"{"format_version":1,"adapters":[],"source":{"aws_cli_version":"2.36.43"}}"#; let error = validate_catalog(catalog).expect_err("empty adapters must fail"); assert!(error.to_string().contains("contains no adapters")); } + + #[test] + fn catalog_without_aws_cli_version_is_rejected() { + let catalog = br#"{"format_version":1,"adapters":[{}],"source":{"aws_cli_version":" "}}"#; + let error = validate_catalog(catalog).expect_err("blank version must fail"); + assert!(error.to_string().contains("does not record the AWS CLI version")); + let catalog = br#"{"format_version":1,"adapters":[{}],"source":{}}"#; + assert!(validate_catalog(catalog).is_err(), "missing version must fail"); + } } diff --git a/src/data-source/src/lib.rs b/src/data-source/src/lib.rs index f32fb932..cf48fc71 100644 --- a/src/data-source/src/lib.rs +++ b/src/data-source/src/lib.rs @@ -90,9 +90,7 @@ impl SyncStats { } #[cfg(feature = "maintenance")] -fn write_source_versions(path: &Path, versions: source_versions::SourceVersions) -> anyhow::Result<()> { - let versions = source_versions::SourceVersions::new(versions.cfn_lint_version, versions.resource_schema_version) - .map_err(anyhow::Error::msg)?; +pub(crate) fn write_source_versions(path: &Path, versions: source_versions::SourceVersions) -> anyhow::Result<()> { let mut contents = serde_json::to_string_pretty(&versions)?; contents.push('\n'); fs::write(path, contents)?; @@ -131,8 +129,10 @@ pub fn sync_upstream(upstream_dir: &Path, rule_source_root: &str) -> anyhow::Res table_stats.fail_on_errors("CfnLintTables")?; verify_files_exist_and_populated(REQUIRED_SYNC_FILES, &generated_data, "Sync")?; - let source_versions = - source_versions::SourceVersions::new(cfn_lint_version, resource_schema_version).map_err(anyhow::Error::msg)?; + // The AWS CLI entry is owned by the catalog generator; sync keeps it. + let source_versions = source_versions::SourceVersions::read(&source_versions_path) + .and_then(|versions| versions.with_sync_versions(cfn_lint_version, resource_schema_version)) + .map_err(anyhow::Error::msg)?; write_source_versions(&source_versions_path, source_versions)?; info!("Recorded complete data source provenance in {}", source_versions_path.display()); diff --git a/src/validation-engine/API.md b/src/validation-engine/API.md index cdd82d19..4979384c 100644 --- a/src/validation-engine/API.md +++ b/src/validation-engine/API.md @@ -101,6 +101,23 @@ fails both conditions — because it has no mapping, or its value cannot be type synthesis is SKIPPED and the reason names the offending parameter. This guarantees that validated templates faithfully represent the full caller-supplied state: no parameter is ever silently omitted from the synthesized template. +**API-valid values are never reported as CloudFormation violations.** A same-named API input and CloudFormation +property can differ in value domain: the service may accept an enum member, numeric bound, string length, list size, +or tag key/value length that the CloudFormation schema rejects. The generator records that API-only domain on each +mapping as `unrepresentable`, and the runtime skips synthesis — again naming the parameter and the value — whenever a +supplied value falls inside it, so a command the service would accept is never modeled as a template that +CloudFormation would reject. Same-named inputs whose meaning differs from the property (for example an API resource ID +where the CloudFormation property carries the resource ARN) are removed from the catalog by a reviewed denylist and +therefore skip synthesis as unmapped parameters. + +**Template-authoring advice is not reported for modeled state.** Synthesized templates and wrapped Cloud Control +desired state never had a template author, so rules whose only remediation is a template construct — replace a literal +ARN, AMI ID, account ID, availability zone, password, or pseudo-parameter lookalike with a parameter, `Ref`/`GetAtt`, +mapping, or dynamic reference — are dropped from those reports and counted +as suppressed. They still apply to `TemplateBody` requests, which validate the caller's real template unchanged. Rules +that judge the values themselves (schema constraints, deprecations, security posture such as a publicly accessible +database) are always kept. + Cloud Control `UpdateResource` and `DeleteResource` may report a known `TypeName` supplied explicitly by the request, but they never synthesize state. There is no fuzzy inference, substring matching, or generic property-name guessing. `TemplateBody` validation is restricted to the closed set of CloudFormation operations that accept it; diff --git a/src/validation-engine/src/aws_cli.rs b/src/validation-engine/src/aws_cli.rs index 51c588c7..2a926730 100644 --- a/src/validation-engine/src/aws_cli.rs +++ b/src/validation-engine/src/aws_cli.rs @@ -248,6 +248,9 @@ pub fn validate_aws_cli_command_with_path( // synthesized command templates (no user-authored source to annotate). let standard_config = ValidateConfig { detail_level: DetailLevel::Standard, ..config }; let mut report = validate_bytes_with_path(engine, schema_validator, &template, standard_config, file_path)?; + if synthesis.source != Some(AwsCliTemplateSource::TemplateBody) { + suppress_template_authoring_advice(&mut report); + } if let Some(properties) = synthesis.diagnostic_properties.as_ref() { scope_synthesized_report(&mut report, properties); } @@ -269,13 +272,98 @@ enum AdapterPhase { Delete, } -#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +#[derive(Debug, Clone, PartialEq, Deserialize)] struct PropertyMapping { source: String, target: String, + /// API-valid input values the CloudFormation schema cannot represent. + #[serde(default)] + unrepresentable: Option, +} + +/// The part of an API input's value domain that the mapped CloudFormation +/// property rejects. A request value inside this domain is legal for the +/// service, so reporting the CloudFormation constraint against it would be a +/// false finding; synthesis is skipped instead. Each bound is the CloudFormation +/// constraint that the API range exceeds. +#[derive(Debug, Clone, PartialEq, Default, Deserialize)] +#[serde(default)] +struct UnrepresentableDomain { + rejected_values: Vec, + minimum: Option, + maximum: Option, + min_length: Option, + max_length: Option, + items: Option>, + tag_key: Option>, + tag_value: Option>, +} + +impl UnrepresentableDomain { + /// Describes why `value` has no CloudFormation representation, or `None` + /// when every part of it is representable. + fn rejection(&self, value: &AwsCliValue) -> Option { + if let Some(json_value) = value.json_value() + && self.rejected_values.contains(&json_value) + { + return Some(format!("value {json_value} is valid for the API but not for CloudFormation")); + } + match value { + AwsCliValue::String { value } => self.length_rejection(value.chars().count() as u64, "length"), + AwsCliValue::Integer { value } => self.numeric_rejection(*value as f64), + AwsCliValue::UnsignedInteger { value } => self.numeric_rejection(*value as f64), + AwsCliValue::Number { value } => self.numeric_rejection(*value), + AwsCliValue::Array { items } => self.length_rejection(items.len() as u64, "item count").or_else(|| { + self.items.as_ref().and_then(|domain| items.iter().find_map(|item| domain.rejection(item))) + }), + AwsCliValue::Object { entries } => entries.iter().find_map(|(key, entry)| { + let key_rejection = self + .tag_key + .as_ref() + .and_then(|domain| domain.rejection(&AwsCliValue::String { value: key.clone() })); + key_rejection.or_else(|| self.tag_value.as_ref().and_then(|domain| domain.rejection(entry))) + }), + AwsCliValue::Null + | AwsCliValue::Boolean { .. } + | AwsCliValue::Bytes { .. } + | AwsCliValue::Unsupported { .. } => None, + } + } + + fn numeric_rejection(&self, value: f64) -> Option { + if let Some(minimum) = self.minimum + && value < minimum + { + return Some(format!("value {value} is valid for the API but below the CloudFormation minimum {minimum}")); + } + if let Some(maximum) = self.maximum + && value > maximum + { + return Some(format!("value {value} is valid for the API but above the CloudFormation maximum {maximum}")); + } + None + } + + fn length_rejection(&self, length: u64, measure: &str) -> Option { + if let Some(min_length) = self.min_length + && length < min_length + { + return Some(format!( + "{measure} {length} is valid for the API but below the CloudFormation minimum {min_length}" + )); + } + if let Some(max_length) = self.max_length + && length > max_length + { + return Some(format!( + "{measure} {length} is valid for the API but above the CloudFormation maximum {max_length}" + )); + } + None + } } -#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +#[derive(Debug, Clone, PartialEq, Deserialize)] struct OperationAdapter { service: String, operation: String, @@ -299,8 +387,11 @@ struct OperationCatalog { /// Generated by `data-source/scripts/generate_aws_cli_catalog.py`: each entry is /// derived from the resource type's own provider handler metadata, resolved /// against botocore service models, and structurally verified against the -/// compiled CloudFormation schemas. Only exact service+operation keys resolve; -/// unregistered operations stay unmapped. +/// compiled CloudFormation schemas. Each mapping also records the API value +/// domain the schema cannot represent, so a value that is valid for the service +/// but not for CloudFormation skips synthesis instead of producing a finding. +/// Only exact service+operation keys resolve; unregistered operations stay +/// unmapped. static ADAPTER_REGISTRY: LazyLock, String>> = LazyLock::new(|| parse_adapter_registry(&data_source::embedded::AWS_CLI_OPERATION_CATALOG_BYTES)); @@ -903,6 +994,14 @@ fn map_adapter_properties( continue; }; mapped_sources.insert(&mapping.source); + if let Some(domain) = mapping.unrepresentable.as_ref() + && let Some(rejection) = domain.rejection(value) + { + return Ok(AdapterMappingResult::Skip(format!( + "parameter '{}' {rejection}, so it has no representation as property '{}' on {}", + mapping.source, mapping.target, adapter.cfn_type + ))); + } match mapped_value(value, accepted_types, &mapping.target) { Some(json_value) => { mapped.insert(mapping.target.clone(), json_value); @@ -1016,11 +1115,29 @@ fn is_scalar_api_value(value: &AwsCliValue) -> bool { | AwsCliValue::String { .. } ) } +/// Rules whose only remediation is a template-authoring construct: replace a +/// literal ARN, AMI ID, account ID, availability zone, password, or +/// pseudo-parameter lookalike with a parameter, `Ref`/`GetAtt`, mapping, or +/// dynamic reference. They judge how a template author expressed a value, not +/// whether the value is valid resource state. +const TEMPLATE_AUTHORING_ADVICE_RULES: &[&str] = &["W1054", "W2501", "W3010", "W9002", "W9010", "W9013"]; + +/// Resource state modeled from an API request never had a template, so advice +/// about how a template author should have expressed a literal cannot be acted +/// on and is dropped. Findings about the values themselves are kept. +fn suppress_template_authoring_advice(report: &mut ValidationReport) { + retain_diagnostics(report, |diagnostic| !TEMPLATE_AUTHORING_ADVICE_RULES.contains(&diagnostic.rule_id.as_str())); +} + fn scope_synthesized_report(report: &mut ValidationReport, properties: &BTreeSet) { - let before = report.diagnostics.len(); - report.diagnostics.retain(|diagnostic| { + retain_diagnostics(report, |diagnostic| { diagnostic.property_path.as_deref().is_some_and(|path| diagnostic_in_scope(path, properties)) }); +} + +fn retain_diagnostics(report: &mut ValidationReport, keep: impl FnMut(&diagnostics::Diagnostic) -> bool) { + let before = report.diagnostics.len(); + report.diagnostics.retain(keep); let removed = before.saturating_sub(report.diagnostics.len()) as u32; report.metadata.suppressed = report.metadata.suppressed.saturating_add(removed); report.metadata.counts = summarize_diagnostics(&report.diagnostics); @@ -1049,7 +1166,7 @@ fn summarize_diagnostics(diagnostics: &[diagnostics::Diagnostic]) -> Summary { #[cfg(test)] mod tests { use super::*; - use diagnostics::{Diagnostic, PhaseMetric}; + use diagnostics::{Diagnostic, PhaseMetric, RegisteredDiagnostic}; use rules::{RuleInfo, RuleMetadataEntry}; use std::sync::Arc; use template_model::SemanticModel; @@ -1119,20 +1236,43 @@ mod tests { } fn mapping(source: &str, target: &str) -> PropertyMapping { - PropertyMapping { source: source.into(), target: target.into() } + PropertyMapping { source: source.into(), target: target.into(), unrepresentable: None } } - fn malformed_adapter_error(mappings: Vec) -> String { - let schema_validator = SchemaValidator::default(); - let schema = schema_validator.resource_schema_metadata("AWS::S3::Bucket").expect("S3 bucket schema must exist"); - let adapter = OperationAdapter { + fn s3_create_bucket_adapter(mappings: Vec) -> OperationAdapter { + OperationAdapter { service: "s3".into(), operation: "CreateBucket".into(), phase: AdapterPhase::Create, cfn_type: "AWS::S3::Bucket".into(), mappings, ignored_inputs: Vec::new(), - }; + } + } + + fn map_s3_create_bucket(adapter: &OperationAdapter, parameters: serde_json::Value) -> AdapterMappingResult { + let schema_validator = SchemaValidator::default(); + let schema = schema_validator.resource_schema_metadata("AWS::S3::Bucket").expect("S3 bucket schema must exist"); + let parameters: HashMap = parameters + .as_object() + .expect("test parameters must be an object") + .iter() + .map(|(name, value)| (name.clone(), AwsCliValue::from_json(value.clone()))) + .collect(); + map_adapter_properties(¶meters, &schema, adapter).expect("well-formed adapter must not error") + } + + fn skip_reason(result: AdapterMappingResult) -> String { + match result { + AdapterMappingResult::Skip(reason) => reason, + AdapterMappingResult::Mapped(properties) => panic!("expected skip, got mapped properties {properties:?}"), + } + } + + fn malformed_adapter_error(mappings: Vec) -> String { + let schema_validator = SchemaValidator::default(); + let schema = schema_validator.resource_schema_metadata("AWS::S3::Bucket").expect("S3 bucket schema must exist"); + let adapter = s3_create_bucket_adapter(mappings); match map_adapter_properties(&HashMap::new(), &schema, &adapter) .expect_err("malformed adapter must return an error") { @@ -1140,6 +1280,145 @@ mod tests { error => panic!("expected engine error, got {error:?}"), } } + + #[test] + fn catalog_parser_reads_unrepresentable_domains() { + let catalog = br#"{ + "format_version": 1, + "adapters": [{ + "service": "s3", "operation": "CreateBucket", "phase": "create", "cfn_type": "AWS::S3::Bucket", + "mappings": [ + {"source": "Bucket", "target": "BucketName", + "unrepresentable": {"rejected_values": ["legacy"], "min_length": 3, "max_length": 63}}, + {"source": "Tags", "target": "Tags", + "unrepresentable": {"tag_value": {"min_length": 1}, "items": {"maximum": 10.0}}} + ] + }] + }"#; + let registry = parse_adapter_registry(catalog).expect("catalog with domains parses"); + let adapter = ®istry[&("s3".to_string(), "CreateBucket".to_string())]; + assert_eq!( + adapter.mappings[0].unrepresentable, + Some(UnrepresentableDomain { + rejected_values: vec![serde_json::json!("legacy")], + min_length: Some(3), + max_length: Some(63), + ..UnrepresentableDomain::default() + }) + ); + let tags = adapter.mappings[1].unrepresentable.as_ref().expect("tags domain parses"); + assert_eq!(tags.tag_value.as_deref().and_then(|domain| domain.min_length), Some(1)); + assert_eq!(tags.items.as_deref().and_then(|domain| domain.maximum), Some(10.0)); + } + + #[test] + fn unrepresentable_domain_rejects_only_values_outside_cloudformation_bounds() { + let enumeration = UnrepresentableDomain { + rejected_values: vec![serde_json::json!("MANAGED_INSTANCES")], + ..UnrepresentableDomain::default() + }; + assert!(enumeration.rejection(&value(serde_json::json!("MANAGED_INSTANCES"))).is_some()); + assert!(enumeration.rejection(&value(serde_json::json!("FARGATE"))).is_none()); + + let numeric = + UnrepresentableDomain { minimum: Some(1.0), maximum: Some(10.0), ..UnrepresentableDomain::default() }; + assert!(numeric.rejection(&value(serde_json::json!(0))).is_some()); + assert!(numeric.rejection(&value(serde_json::json!(50))).is_some()); + assert!(numeric.rejection(&value(serde_json::json!(10))).is_none()); + assert!(numeric.rejection(&value(serde_json::json!(1))).is_none()); + assert!(numeric.rejection(&AwsCliValue::UnsignedInteger { value: 11 }).is_some()); + assert!(numeric.rejection(&AwsCliValue::Number { value: 0.5 }).is_some()); + + let text_length = + UnrepresentableDomain { min_length: Some(5), max_length: Some(6), ..UnrepresentableDomain::default() }; + assert!(text_length.rejection(&value(serde_json::json!("abc"))).is_some()); + assert!(text_length.rejection(&value(serde_json::json!("abcdefg"))).is_some()); + assert!(text_length.rejection(&value(serde_json::json!("abcde"))).is_none()); + assert!(text_length.rejection(&value(serde_json::json!("ééééé"))).is_none(), "length counts characters"); + + let list = UnrepresentableDomain { + max_length: Some(2), + items: Some(Box::new(UnrepresentableDomain { + rejected_values: vec![serde_json::json!("SUPER_USER")], + ..UnrepresentableDomain::default() + })), + ..UnrepresentableDomain::default() + }; + assert!(list.rejection(&value(serde_json::json!(["a", "b", "c"]))).is_some(), "item count above maximum"); + assert!(list.rejection(&value(serde_json::json!(["SELECT", "SUPER_USER"]))).is_some(), "rejected item"); + assert!(list.rejection(&value(serde_json::json!(["SELECT", "ALTER"]))).is_none()); + + let tags = UnrepresentableDomain { + tag_key: Some(Box::new(UnrepresentableDomain { max_length: Some(3), ..UnrepresentableDomain::default() })), + tag_value: Some(Box::new(UnrepresentableDomain { + min_length: Some(1), + ..UnrepresentableDomain::default() + })), + ..UnrepresentableDomain::default() + }; + assert!(tags.rejection(&value(serde_json::json!({"Team": "cli"}))).is_some(), "key too long"); + assert!(tags.rejection(&value(serde_json::json!({"Env": ""}))).is_some(), "empty value"); + assert!(tags.rejection(&value(serde_json::json!({"Env": "prod"}))).is_none()); + + let empty = UnrepresentableDomain::default(); + assert!(empty.rejection(&value(serde_json::json!(true))).is_none()); + assert!(empty.rejection(&value(serde_json::json!(null))).is_none()); + } + + #[test] + fn adapter_skips_when_supplied_value_is_valid_for_the_api_but_not_for_cloudformation() { + let adapter = s3_create_bucket_adapter(vec![PropertyMapping { + source: "Bucket".into(), + target: "BucketName".into(), + unrepresentable: Some(UnrepresentableDomain { + rejected_values: vec![serde_json::json!("legacy")], + min_length: Some(3), + ..UnrepresentableDomain::default() + }), + }]); + + let reason = skip_reason(map_s3_create_bucket(&adapter, serde_json::json!({"Bucket": "legacy"}))); + assert!(reason.contains("parameter 'Bucket'"), "{reason}"); + assert!(reason.contains("valid for the API"), "{reason}"); + assert!(reason.contains("property 'BucketName' on AWS::S3::Bucket"), "{reason}"); + + let reason = skip_reason(map_s3_create_bucket(&adapter, serde_json::json!({"Bucket": "ab"}))); + assert!(reason.contains("length 2"), "{reason}"); + assert!(reason.contains("minimum 3"), "{reason}"); + + match map_s3_create_bucket(&adapter, serde_json::json!({"Bucket": "my-bucket"})) { + AdapterMappingResult::Mapped(properties) => { + assert_eq!(properties.get("BucketName"), Some(&serde_json::json!("my-bucket"))); + } + AdapterMappingResult::Skip(reason) => panic!("representable value must map: {reason}"), + } + } + + #[test] + fn adapter_skips_tag_maps_whose_entries_have_no_cloudformation_representation() { + let adapter = s3_create_bucket_adapter(vec![PropertyMapping { + source: "Tags".into(), + target: "Tags".into(), + unrepresentable: Some(UnrepresentableDomain { + tag_value: Some(Box::new(UnrepresentableDomain { + min_length: Some(1), + ..UnrepresentableDomain::default() + })), + ..UnrepresentableDomain::default() + }), + }]); + + let reason = skip_reason(map_s3_create_bucket(&adapter, serde_json::json!({"Tags": {"Env": ""}}))); + assert!(reason.contains("parameter 'Tags'"), "{reason}"); + assert!(reason.contains("length 0"), "{reason}"); + + match map_s3_create_bucket(&adapter, serde_json::json!({"Tags": {"Env": "prod"}})) { + AdapterMappingResult::Mapped(properties) => { + assert_eq!(properties.get("Tags"), Some(&serde_json::json!([{"Key": "Env", "Value": "prod"}]))); + } + AdapterMappingResult::Skip(reason) => panic!("representable tags must map: {reason}"), + } + } #[test] fn catalog_parser_rejects_invalid_formats_and_normalized_duplicates() { assert!(parse_adapter_registry(b"not json").expect_err("invalid JSON must fail").contains("invalid")); @@ -1856,6 +2135,101 @@ mod tests { assert_eq!(validation.template, None, "skipped requests must have template=None"); } + /// Emits one template-authoring advice diagnostic and one value-validity + /// diagnostic on `Properties.QueueName` of every resource. + struct AdviceEngine(NoopEngine); + + impl ValidationEngine for AdviceEngine { + fn engine_name(&self) -> &str { + "advice" + } + + fn evaluate_rules( + &self, + model: &Arc, + _config: &ValidateConfig, + ) -> Result, ValidationError> { + Ok(model + .resources + .keys() + .flat_map(|logical_id| { + ["W9002", "W3030"].into_iter().map(move |rule_id| { + RegisteredDiagnostic::new(rule_id, format!("{rule_id} on {logical_id}")) + .resource(logical_id.clone(), None) + .property_path("Properties.QueueName") + .build() + }) + }) + .collect()) + } + + fn list_rules(&self) -> Vec { + self.0.list_rules() + } + + fn rule_metadata(&self) -> &HashMap { + self.0.rule_metadata() + } + + fn external_rule_metadata(&self) -> HashMap { + self.0.external_rule_metadata() + } + + fn init_metric(&self) -> &PhaseMetric { + self.0.init_metric() + } + } + + fn reported_rule_ids(validation: &AwsCliCommandValidation) -> BTreeSet { + let report = validation.report.as_ref().expect("request must be validated"); + report.diagnostics.iter().map(|diagnostic| diagnostic.rule_id.clone()).collect() + } + + #[test] + fn template_authoring_advice_is_dropped_for_modeled_state_but_kept_for_template_bodies() { + let engine = AdviceEngine(NoopEngine::default()); + let schema_validator = SchemaValidator::default(); + + let mut template_body = request("cloudformation", "CreateStack", serde_json::json!({"StackName": "s"})); + template_body.parameters.insert( + "TemplateBody".into(), + AwsCliValue::String { + value: r#"{"Resources":{"Q":{"Type":"AWS::SQS::Queue","Properties":{"QueueName":"orders"}}}}"#.into(), + }, + ); + let validation = + validate_aws_cli_command(&engine, &schema_validator, &template_body, ValidateConfig::default()) + .expect("validation succeeds"); + assert_eq!(validation.template_source, Some(AwsCliTemplateSource::TemplateBody)); + let reported = reported_rule_ids(&validation); + assert!(reported.contains("W9002"), "a real template keeps authoring advice: {reported:?}"); + assert!(reported.contains("W3030"), "{reported:?}"); + + let synthesized = request("sqs", "CreateQueue", serde_json::json!({"QueueName": "orders"})); + let validation = validate_aws_cli_command(&engine, &schema_validator, &synthesized, ValidateConfig::default()) + .expect("validation succeeds"); + assert_eq!(validation.template_source, Some(AwsCliTemplateSource::SynthesizedCreate)); + let reported = reported_rule_ids(&validation); + assert!(!reported.contains("W9002"), "modeled API state drops authoring advice: {reported:?}"); + assert!(reported.contains("W3030"), "value findings are kept: {reported:?}"); + let report = validation.report.as_ref().expect("validated"); + assert!(report.metadata.suppressed >= 1, "dropped advice is counted as suppressed"); + assert_eq!(report.metadata.counts.warnings, 1); + + let desired_state = request( + "cloudcontrol", + "CreateResource", + serde_json::json!({"TypeName": "AWS::SQS::Queue", "DesiredState": "{\"QueueName\":\"orders\"}"}), + ); + let validation = + validate_aws_cli_command(&engine, &schema_validator, &desired_state, ValidateConfig::default()) + .expect("validation succeeds"); + assert_eq!(validation.template_source, Some(AwsCliTemplateSource::CloudControlDesiredState)); + let reported = reported_rule_ids(&validation); + assert!(!reported.contains("W9002"), "Cloud Control desired state drops authoring advice: {reported:?}"); + assert!(reported.contains("W3030"), "{reported:?}"); + } + #[test] fn partial_update_scoping_keeps_report_counts_consistent() { let engine = NoopEngine::default(); From 4c5139436224ee7dc6bcdadf909ecfc18b33e535 Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Fri, 11 Sep 2026 16:22:02 +0000 Subject: [PATCH 12/19] Update build versions --- src/data-source/build.rs | 11 ++- src/data-source/src/lib.rs | 64 +++++++++++++++++ src/data-source/src/source_versions.rs | 97 ++++++++++++++------------ 3 files changed, 127 insertions(+), 45 deletions(-) diff --git a/src/data-source/build.rs b/src/data-source/build.rs index 5f5ffdef..8e7a6252 100644 --- a/src/data-source/build.rs +++ b/src/data-source/build.rs @@ -94,10 +94,10 @@ fn main() { println!("cargo:rerun-if-changed={}", source_versions_path.display()); require_file(&source_versions_path, "data source provenance"); let source_versions = SourceVersions::read(&source_versions_path) - .and_then(|versions| SourceVersions::new(versions.cfn_lint_version, versions.resource_schema_version)) .unwrap_or_else(|error| panic!("failed to load required data source versions: {error}")); emit_version("CFN_LINT_VERSION", &source_versions.cfn_lint_version, &mut code); emit_version("RESOURCE_SCHEMA_VERSION", &source_versions.resource_schema_version, &mut code); + emit_optional_version("AWS_CLI_VERSION", source_versions.aws_cli_version.as_deref(), &mut code); for (relative_path, const_name) in GENERATED_JSON { let path = generated_dir.join(relative_path); @@ -186,6 +186,15 @@ fn emit_version(const_name: &str, version: &str, code: &mut String) { code.push_str(&format!("pub const {const_name}: &str = {version:?};\n")); } +/// A provenance entry that a later maintenance step may not have recorded yet. +fn emit_optional_version(const_name: &str, version: Option<&str>, code: &mut String) { + let literal = match version { + Some(version) => format!("Some({version:?})"), + None => "None".to_string(), + }; + code.push_str(&format!("pub const {const_name}: Option<&str> = {literal};\n")); +} + fn require_file(path: &Path, label: &str) { if !path.is_file() { panic!("missing required {label}: {}", path.display()); diff --git a/src/data-source/src/lib.rs b/src/data-source/src/lib.rs index cf48fc71..689cf9bb 100644 --- a/src/data-source/src/lib.rs +++ b/src/data-source/src/lib.rs @@ -89,6 +89,35 @@ impl SyncStats { } } +/// Manifest writers. They live here rather than in `source_versions.rs` because +/// the build script compiles that file too and only ever reads the manifest. +#[cfg(feature = "maintenance")] +impl source_versions::SourceVersions { + pub(crate) fn new( + cfn_lint_version: String, + resource_schema_version: String, + aws_cli_version: Option, + ) -> Result { + let versions = Self { cfn_lint_version, resource_schema_version, aws_cli_version }; + versions.validate()?; + Ok(versions) + } + + /// The manifest with the `sync`-owned entries replaced and the AWS CLI entry kept. + pub(crate) fn with_sync_versions( + self, + cfn_lint_version: String, + resource_schema_version: String, + ) -> Result { + Self::new(cfn_lint_version, resource_schema_version, self.aws_cli_version) + } + + /// The manifest with the AWS CLI entry recorded and the `sync`-owned entries kept. + pub(crate) fn with_aws_cli_version(self, aws_cli_version: String) -> Result { + Self::new(self.cfn_lint_version, self.resource_schema_version, Some(aws_cli_version)) + } +} + #[cfg(feature = "maintenance")] pub(crate) fn write_source_versions(path: &Path, versions: source_versions::SourceVersions) -> anyhow::Result<()> { let mut contents = serde_json::to_string_pretty(&versions)?; @@ -292,3 +321,38 @@ fn is_stub(path: &Path) -> bool { Err(_) => true, } } + +#[cfg(all(test, feature = "maintenance"))] +mod source_version_writer_tests { + use crate::source_versions::{AWS_CLI_SOURCE, CFN_LINT_SOURCE, RESOURCE_SCHEMA_SOURCE, SourceVersions}; + + const SYNC_ONLY_MANIFEST: &str = r#"{ + "cfn_lint_version":"https://github.com/aws-cloudformation/cfn-lint@1.54.0", + "resource_schema_version":"https://github.com/aws-cloudformation/resource-provider-enhanced-schemas@2026-08-07T18:20:13Z" + }"#; + + #[test] + fn each_writer_preserves_the_entries_it_does_not_own() { + let recorded = SourceVersions::from_json(SYNC_ONLY_MANIFEST) + .expect("manifest should parse") + .with_aws_cli_version(format!("{AWS_CLI_SOURCE}@2.36.43")) + .expect("catalog update should be valid"); + assert_eq!(recorded.aws_cli_version.as_deref(), Some("https://github.com/aws/aws-cli@2.36.43")); + assert_eq!(recorded.cfn_lint_version, format!("{CFN_LINT_SOURCE}@1.54.0")); + + let synced = recorded + .with_sync_versions( + format!("{CFN_LINT_SOURCE}@1.56.0"), + format!("{RESOURCE_SCHEMA_SOURCE}@2026-09-08T00:15:23Z"), + ) + .expect("sync update should be valid"); + assert_eq!(synced.cfn_lint_version, format!("{CFN_LINT_SOURCE}@1.56.0")); + assert_eq!(synced.aws_cli_version.as_deref(), Some("https://github.com/aws/aws-cli@2.36.43")); + + let error = SourceVersions::from_json(SYNC_ONLY_MANIFEST) + .expect("manifest should parse") + .with_aws_cli_version("2.36.43".to_string()) + .expect_err("unqualified version must fail"); + assert!(error.contains(AWS_CLI_SOURCE)); + } +} diff --git a/src/data-source/src/source_versions.rs b/src/data-source/src/source_versions.rs index 9eb617aa..b2b28945 100644 --- a/src/data-source/src/source_versions.rs +++ b/src/data-source/src/source_versions.rs @@ -5,21 +5,24 @@ use std::path::Path; pub const SOURCE_VERSIONS_FILE: &str = "source_versions.json"; pub const CFN_LINT_SOURCE: &str = "https://github.com/aws-cloudformation/cfn-lint"; pub const RESOURCE_SCHEMA_SOURCE: &str = "https://github.com/aws-cloudformation/resource-provider-enhanced-schemas"; - +pub const AWS_CLI_SOURCE: &str = "https://github.com/aws/aws-cli"; + +/// Provenance of every external input behind the committed generated data. +/// +/// `sync` records the cfn-lint and resource-schema versions; the AWS CLI +/// operation catalog generator records the AWS CLI release whose bundled +/// botocore models the catalog derives from, so that entry is absent until the +/// catalog has been generated. Each writer preserves the entries it does not own. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct SourceVersions { pub cfn_lint_version: String, pub resource_schema_version: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub aws_cli_version: Option, } impl SourceVersions { - pub fn new(cfn_lint_version: String, resource_schema_version: String) -> Result { - let versions = Self { cfn_lint_version, resource_schema_version }; - versions.validate()?; - Ok(versions) - } - pub fn read(path: &Path) -> Result { let contents = fs::read_to_string(path).map_err(|error| format!("failed to read {}: {error}", path.display()))?; @@ -32,9 +35,13 @@ impl SourceVersions { Ok(versions) } - fn validate(&self) -> Result<(), String> { + pub fn validate(&self) -> Result<(), String> { validate_source_version("cfn_lint_version", &self.cfn_lint_version, CFN_LINT_SOURCE)?; - validate_source_version("resource_schema_version", &self.resource_schema_version, RESOURCE_SCHEMA_SOURCE) + validate_source_version("resource_schema_version", &self.resource_schema_version, RESOURCE_SCHEMA_SOURCE)?; + match &self.aws_cli_version { + Some(aws_cli_version) => validate_source_version("aws_cli_version", aws_cli_version, AWS_CLI_SOURCE), + None => Ok(()), + } } } @@ -53,64 +60,66 @@ fn validate_source_version(field: &str, value: &str, source: &str) -> Result<(), mod tests { use super::*; - #[test] - fn parses_complete_manifest() { - let versions = SourceVersions::from_json( - r#"{"cfn_lint_version":"https://github.com/aws-cloudformation/cfn-lint@1.54.0","resource_schema_version":"https://github.com/aws-cloudformation/resource-provider-enhanced-schemas@2026-08-07T18:20:13Z"}"#, - ) - .expect("manifest should parse"); + const SYNC_ONLY_MANIFEST: &str = r#"{ + "cfn_lint_version":"https://github.com/aws-cloudformation/cfn-lint@1.54.0", + "resource_schema_version":"https://github.com/aws-cloudformation/resource-provider-enhanced-schemas@2026-08-07T18:20:13Z" + }"#; + const COMPLETE_MANIFEST: &str = r#"{ + "cfn_lint_version":"https://github.com/aws-cloudformation/cfn-lint@1.54.0", + "resource_schema_version":"https://github.com/aws-cloudformation/resource-provider-enhanced-schemas@2026-08-07T18:20:13Z", + "aws_cli_version":"https://github.com/aws/aws-cli@2.36.43" + }"#; + + #[test] + fn parses_manifest_before_the_catalog_has_been_generated() { + let versions = SourceVersions::from_json(SYNC_ONLY_MANIFEST).expect("manifest should parse"); assert_eq!(versions.cfn_lint_version, "https://github.com/aws-cloudformation/cfn-lint@1.54.0"); assert_eq!( versions.resource_schema_version, "https://github.com/aws-cloudformation/resource-provider-enhanced-schemas@2026-08-07T18:20:13Z" ); + assert_eq!(versions.aws_cli_version, None); } #[test] - fn serializes_complete_manifest() { - let versions = SourceVersions::new( - format!("{CFN_LINT_SOURCE}@1.54.0"), - format!("{RESOURCE_SCHEMA_SOURCE}@2026-08-07T18:20:13Z"), - ) - .expect("source versions should be valid"); - - let json = serde_json::to_value(versions).expect("manifest should serialize"); + fn parses_complete_manifest() { + let versions = SourceVersions::from_json(COMPLETE_MANIFEST).expect("manifest should parse"); + assert_eq!(versions.aws_cli_version.as_deref(), Some("https://github.com/aws/aws-cli@2.36.43")); + } + + #[test] + fn serializes_only_recorded_entries() { + let sync_only = SourceVersions::from_json(SYNC_ONLY_MANIFEST).expect("manifest should parse"); + let json = serde_json::to_value(sync_only).expect("manifest should serialize"); assert_eq!(json.as_object().expect("manifest should be an object").len(), 2); - assert_eq!(json["cfn_lint_version"], format!("{CFN_LINT_SOURCE}@1.54.0")); - assert_eq!(json["resource_schema_version"], format!("{RESOURCE_SCHEMA_SOURCE}@2026-08-07T18:20:13Z")); + + let complete = SourceVersions::from_json(COMPLETE_MANIFEST).expect("manifest should parse"); + let json = serde_json::to_value(complete).expect("manifest should serialize"); + assert_eq!(json.as_object().expect("manifest should be an object").len(), 3); + assert_eq!(json["aws_cli_version"], format!("{AWS_CLI_SOURCE}@2.36.43")); } #[test] - fn missing_field_is_rejected() { + fn missing_sync_field_is_rejected() { let manifest = r#"{"resource_schema_version":"https://github.com/aws-cloudformation/resource-provider-enhanced-schemas@2026-08-07T18:20:13Z"}"#; assert!(SourceVersions::from_json(manifest).is_err()); } #[test] - fn blank_version_suffix_is_rejected() { - let error = SourceVersions::new( - format!("{CFN_LINT_SOURCE}@ "), - format!("{RESOURCE_SCHEMA_SOURCE}@2026-08-07T18:20:13Z"), - ) - .expect_err("blank version must fail"); + fn malformed_versions_are_rejected() { + let blank = SYNC_ONLY_MANIFEST.replace("cfn-lint@1.54.0", "cfn-lint@ "); + let error = SourceVersions::from_json(&blank).expect_err("blank version must fail"); assert!(error.contains("cfn_lint_version must include a nonblank version")); - } - #[test] - fn unqualified_version_is_rejected() { - let error = SourceVersions::new("1.54.0".to_string(), format!("{RESOURCE_SCHEMA_SOURCE}@2026-08-07T18:20:13Z")) - .expect_err("unqualified version must fail"); - assert!(error.contains(CFN_LINT_SOURCE)); + let unqualified = COMPLETE_MANIFEST.replace("https://github.com/aws/aws-cli@2.36.43", "2.36.43"); + let error = SourceVersions::from_json(&unqualified).expect_err("unqualified version must fail"); + assert!(error.contains(AWS_CLI_SOURCE)); } #[test] fn unknown_field_is_rejected() { - let manifest = r#"{ - "cfn_lint_version":"https://github.com/aws-cloudformation/cfn-lint@1.54.0", - "resource_schema_version":"https://github.com/aws-cloudformation/resource-provider-enhanced-schemas@2026-08-07T18:20:13Z", - "unexpected":"value" - }"#; - assert!(SourceVersions::from_json(manifest).is_err()); + let manifest = COMPLETE_MANIFEST.replace("\"aws_cli_version\"", "\"unexpected\":\"value\",\"aws_cli_version\""); + assert!(SourceVersions::from_json(&manifest).is_err()); } } From 9a5c3fd9680bf2c82d59f5e8482c2db4c53d77ee Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Fri, 11 Sep 2026 14:22:01 -0400 Subject: [PATCH 13/19] Generated catalog --- src/data-source/README.md | 6 +- .../data/aws_cli_operation_catalog.json | 3495 +++++++++++++++-- .../generated/data/source_versions.json | 3 +- 3 files changed, 3165 insertions(+), 339 deletions(-) diff --git a/src/data-source/README.md b/src/data-source/README.md index 2a8abc93..c58597fd 100644 --- a/src/data-source/README.md +++ b/src/data-source/README.md @@ -15,10 +15,8 @@ cargo run -p cloudformation-validate-data-source --features maintenance --exampl cargo run -p cloudformation-validate-data-source --features maintenance --example sync -- --cfn-lint-root # Regenerate the AWS CLI operation catalog (standalone; independent of sync/generate). -# --aws-cli-root points at a local aws-cli checkout. The resource data must already be -# present (upstream/schemas from a prior sync, plus the committed compiled schemas). -cargo run -p cloudformation-validate-data-source --features maintenance \ - --example generate_aws_cli_catalog -- --aws-cli-root +# --aws-cli-root points at a local aws-cli checkout. +cargo run -p cloudformation-validate-data-source --features maintenance --example generate_aws_cli_catalog -- --aws-cli-root ``` The examples require the `maintenance` feature, which enables dependencies used only by the data maintenance diff --git a/src/data-source/generated/data/aws_cli_operation_catalog.json b/src/data-source/generated/data/aws_cli_operation_catalog.json index 0e14e9dc..3780f862 100644 --- a/src/data-source/generated/data/aws_cli_operation_catalog.json +++ b/src/data-source/generated/data/aws_cli_operation_catalog.json @@ -198,7 +198,10 @@ "mappings": [ { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 64 + } }, { "source": "tags", @@ -217,7 +220,10 @@ "mappings": [ { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 64 + } } ], "operation": "DeleteRuleGroupsNamespace", @@ -778,6 +784,33 @@ "phase": "delete", "service": "amplify" }, + { + "cfn_type": "AWS::Amplify::Webhook", + "mappings": [ + { + "source": "appId", + "target": "AppId" + }, + { + "source": "branchName", + "target": "BranchName" + }, + { + "source": "description", + "target": "Description" + } + ], + "operation": "CreateWebhook", + "phase": "create", + "service": "amplify" + }, + { + "cfn_type": "AWS::Amplify::Webhook", + "mappings": [], + "operation": "DeleteWebhook", + "phase": "delete", + "service": "amplify" + }, { "cfn_type": "AWS::AmplifyUIBuilder::Component", "ignored_inputs": [ @@ -1199,6 +1232,34 @@ "phase": "delete", "service": "appconfig" }, + { + "cfn_type": "AWS::AppConfig::ExperimentRun", + "mappings": [ + { + "source": "ApplicationIdentifier", + "target": "ApplicationIdentifier" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "ExperimentDefinitionIdentifier", + "target": "ExperimentDefinitionIdentifier" + }, + { + "source": "ExposurePercentage", + "target": "ExposurePercentage" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "StartExperimentRun", + "phase": "create", + "service": "appconfig" + }, { "cfn_type": "AWS::AppConfig::Extension", "mappings": [ @@ -1352,7 +1413,17 @@ }, { "source": "connectorType", - "target": "ConnectorType" + "target": "ConnectorType", + "unrepresentable": { + "rejected_values": [ + "CustomerProfiles", + "EventBridge", + "Honeycode", + "LookoutMetrics", + "S3", + "Upsolver" + ] + } }, { "source": "kmsArn", @@ -1426,7 +1497,10 @@ }, { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "InitializationTimeout", @@ -1472,7 +1546,10 @@ "mappings": [ { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "KmsKey", @@ -1510,7 +1587,10 @@ "mappings": [ { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "EventBridgeBus", @@ -2454,6 +2534,38 @@ "phase": "delete", "service": "appsync" }, + { + "cfn_type": "AWS::AppSync::Type", + "mappings": [ + { + "source": "apiId", + "target": "ApiId" + }, + { + "source": "definition", + "target": "Definition" + }, + { + "source": "format", + "target": "Format" + } + ], + "operation": "CreateType", + "phase": "create", + "service": "appsync" + }, + { + "cfn_type": "AWS::AppSync::Type", + "mappings": [ + { + "source": "apiId", + "target": "ApiId" + } + ], + "operation": "DeleteType", + "phase": "delete", + "service": "appsync" + }, { "cfn_type": "AWS::ApplicationAutoScaling::ScalableTarget", "mappings": [ @@ -2539,7 +2651,10 @@ "mappings": [ { "source": "PolicyName", - "target": "PolicyName" + "target": "PolicyName", + "unrepresentable": { + "max_length": 256 + } }, { "source": "ResourceId", @@ -2818,7 +2933,10 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 127 + } }, { "source": "tags", @@ -3115,7 +3233,10 @@ }, { "source": "NotificationMetadata", - "target": "NotificationMetadata" + "target": "NotificationMetadata", + "unrepresentable": { + "max_length": 1023 + } }, { "source": "NotificationTargetARN", @@ -3961,7 +4082,10 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "min_length": 1 + } }, { "source": "tags", @@ -4420,7 +4544,10 @@ "mappings": [ { "source": "apiKey", - "target": "ApiKey" + "target": "ApiKey", + "unrepresentable": { + "min_length": 1 + } }, { "source": "apiKeySecretSource", @@ -5239,7 +5366,10 @@ }, { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 1200 + } }, { "source": "roleArn", @@ -5384,7 +5514,12 @@ }, { "source": "Tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateBillingGroup", @@ -5426,7 +5561,12 @@ }, { "source": "Tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateCustomLineItem", @@ -5460,7 +5600,12 @@ }, { "source": "Tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreatePricingPlan", @@ -5510,7 +5655,12 @@ }, { "source": "Tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } }, { "source": "Type", @@ -5636,6 +5786,41 @@ "phase": "delete", "service": "cur" }, + { + "cfn_type": "AWS::Cases::Case", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "domainId", + "target": "DomainId" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "templateId", + "target": "TemplateId" + } + ], + "operation": "CreateCase", + "phase": "create", + "service": "connectcases" + }, + { + "cfn_type": "AWS::Cases::Case", + "mappings": [ + { + "source": "domainId", + "target": "DomainId" + } + ], + "operation": "DeleteCase", + "phase": "delete", + "service": "connectcases" + }, { "cfn_type": "AWS::Cases::CaseRule", "mappings": [ @@ -6084,6 +6269,59 @@ "phase": "delete", "service": "chime-sdk-messaging" }, + { + "cfn_type": "AWS::Chime::MediaPipelineKinesisVideoStreamPool", + "ignored_inputs": [ + "ClientRequestToken" + ], + "mappings": [ + { + "source": "PoolName", + "target": "PoolName" + } + ], + "operation": "CreateMediaPipelineKinesisVideoStreamPool", + "phase": "create", + "service": "chime-sdk-media-pipelines" + }, + { + "cfn_type": "AWS::Chime::MediaPipelineKinesisVideoStreamPool", + "mappings": [], + "operation": "DeleteMediaPipelineKinesisVideoStreamPool", + "phase": "delete", + "service": "chime-sdk-media-pipelines" + }, + { + "cfn_type": "AWS::Chime::VoiceConnector", + "mappings": [ + { + "source": "AwsRegion", + "target": "AwsRegion" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "NetworkType", + "target": "NetworkType" + }, + { + "source": "RequireEncryption", + "target": "RequireEncryption" + } + ], + "operation": "CreateVoiceConnector", + "phase": "create", + "service": "chime-sdk-voice" + }, + { + "cfn_type": "AWS::Chime::VoiceConnector", + "mappings": [], + "operation": "DeleteVoiceConnector", + "phase": "delete", + "service": "chime-sdk-voice" + }, { "cfn_type": "AWS::CleanRooms::AnalysisTemplate", "mappings": [ @@ -6105,7 +6343,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateAnalysisTemplate", @@ -6145,7 +6388,10 @@ }, { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "isMetricsEnabled", @@ -6165,7 +6411,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateCollaboration", @@ -6204,7 +6455,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateConfiguredTable", @@ -6243,7 +6499,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateConfiguredTableAssociation", @@ -6283,7 +6544,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateIdMappingTable", @@ -6319,7 +6585,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateIdNamespaceAssociation", @@ -6359,7 +6630,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateIntermediateTable", @@ -6399,7 +6675,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateMembership", @@ -6430,7 +6711,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreatePrivacyBudgetTemplate", @@ -6470,7 +6756,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateConfiguredModelAlgorithm", @@ -6505,7 +6796,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateConfiguredModelAlgorithmAssociation", @@ -6541,7 +6837,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateTrainingDataset", @@ -6560,7 +6861,12 @@ "mappings": [ { "source": "IpAddressType", - "target": "IpAddressType" + "target": "IpAddressType", + "unrepresentable": { + "rejected_values": [ + "ipv6" + ] + } }, { "source": "IpCount", @@ -7357,7 +7663,10 @@ "mappings": [ { "source": "FirehoseArn", - "target": "FirehoseArn" + "target": "FirehoseArn", + "unrepresentable": { + "min_length": 20 + } }, { "source": "IncludeLinkedAccountsMetrics", @@ -7373,7 +7682,10 @@ }, { "source": "RoleArn", - "target": "RoleArn" + "target": "RoleArn", + "unrepresentable": { + "min_length": 20 + } } ], "operation": "PutMetricStream", @@ -7501,11 +7813,27 @@ }, { "source": "computeType", - "target": "ComputeType" + "target": "ComputeType", + "unrepresentable": { + "rejected_values": [ + "BUILD_LAMBDA_10GB", + "BUILD_LAMBDA_1GB", + "BUILD_LAMBDA_2GB", + "BUILD_LAMBDA_4GB", + "BUILD_LAMBDA_8GB" + ] + } }, { "source": "environmentType", - "target": "EnvironmentType" + "target": "EnvironmentType", + "unrepresentable": { + "rejected_values": [ + "ARM_LAMBDA_CONTAINER", + "LINUX_LAMBDA_CONTAINER", + "WINDOWS_CONTAINER" + ] + } }, { "source": "fleetServiceRole", @@ -7535,6 +7863,73 @@ "phase": "delete", "service": "codebuild" }, + { + "cfn_type": "AWS::CodeBuild::SourceCredential", + "mappings": [ + { + "source": "authType", + "target": "AuthType" + }, + { + "source": "serverType", + "target": "ServerType" + }, + { + "source": "token", + "target": "Token" + }, + { + "source": "username", + "target": "Username" + } + ], + "operation": "ImportSourceCredentials", + "phase": "create", + "service": "codebuild" + }, + { + "cfn_type": "AWS::CodeBuild::SourceCredential", + "mappings": [], + "operation": "DeleteSourceCredentials", + "phase": "delete", + "service": "codebuild" + }, + { + "cfn_type": "AWS::CodeCommit::Repository", + "mappings": [ + { + "source": "kmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "repositoryDescription", + "target": "RepositoryDescription" + }, + { + "source": "repositoryName", + "target": "RepositoryName" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRepository", + "phase": "create", + "service": "codecommit" + }, + { + "cfn_type": "AWS::CodeCommit::Repository", + "mappings": [ + { + "source": "repositoryName", + "target": "RepositoryName" + } + ], + "operation": "DeleteRepository", + "phase": "delete", + "service": "codecommit" + }, { "cfn_type": "AWS::CodeConnections::Connection", "mappings": [ @@ -7562,6 +7957,33 @@ "phase": "delete", "service": "codeconnections" }, + { + "cfn_type": "AWS::CodeConnections::Host", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "ProviderEndpoint", + "target": "ProviderEndpoint" + }, + { + "source": "ProviderType", + "target": "ProviderType" + } + ], + "operation": "CreateHost", + "phase": "create", + "service": "codeconnections" + }, + { + "cfn_type": "AWS::CodeConnections::Host", + "mappings": [], + "operation": "DeleteHost", + "phase": "delete", + "service": "codeconnections" + }, { "cfn_type": "AWS::CodeDeploy::Application", "mappings": [ @@ -8188,7 +8610,10 @@ }, { "source": "RefreshTokenValidity", - "target": "RefreshTokenValidity" + "target": "RefreshTokenValidity", + "unrepresentable": { + "minimum": 1.0 + } }, { "source": "SupportedIdentityProviders", @@ -8459,7 +8884,17 @@ }, { "source": "LanguageCode", - "target": "LanguageCode" + "target": "LanguageCode", + "unrepresentable": { + "rejected_values": [ + "ar", + "hi", + "ja", + "ko", + "zh", + "zh-TW" + ] + } }, { "source": "Mode", @@ -8646,6 +9081,13 @@ "phase": "delete", "service": "config" }, + { + "cfn_type": "AWS::Config::DeliveryChannel", + "mappings": [], + "operation": "DeleteDeliveryChannel", + "phase": "delete", + "service": "config" + }, { "cfn_type": "AWS::Config::OrganizationConformancePack", "mappings": [ @@ -8752,10 +9194,6 @@ "ClientToken" ], "mappings": [ - { - "source": "InstanceId", - "target": "InstanceId" - }, { "source": "Origin", "target": "Origin" @@ -8860,10 +9298,6 @@ { "cfn_type": "AWS::Connect::ContactFlowModuleAlias", "mappings": [ - { - "source": "ContactFlowModuleId", - "target": "ContactFlowModuleId" - }, { "source": "ContactFlowModuleVersion", "target": "ContactFlowModuleVersion" @@ -8892,10 +9326,6 @@ { "cfn_type": "AWS::Connect::ContactFlowModuleVersion", "mappings": [ - { - "source": "ContactFlowModuleId", - "target": "ContactFlowModuleId" - }, { "source": "Description", "target": "Description" @@ -8920,10 +9350,6 @@ { "cfn_type": "AWS::Connect::ContactFlowVersion", "mappings": [ - { - "source": "ContactFlowId", - "target": "ContactFlowId" - }, { "source": "Description", "target": "Description" @@ -9063,7 +9489,11 @@ "mappings": [ { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 250, + "min_length": 1 + } }, { "source": "DisplayName", @@ -9220,17 +9650,23 @@ { "cfn_type": "AWS::Connect::IntegrationAssociation", "mappings": [ - { - "source": "InstanceId", - "target": "InstanceId" - }, { "source": "IntegrationArn", "target": "IntegrationArn" }, { "source": "IntegrationType", - "target": "IntegrationType" + "target": "IntegrationType", + "unrepresentable": { + "rejected_values": [ + "ANALYTICS_CONNECTOR", + "CALL_TRANSFER_CONNECTOR", + "COGNITO_USER_POOL", + "EVENT", + "PINPOINT_APP", + "VOICE_ID" + ] + } }, { "source": "Tags", @@ -9321,7 +9757,10 @@ }, { "source": "Purposes", - "target": "Purposes" + "target": "Purposes", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "CreatePredefinedAttribute", @@ -9496,10 +9935,6 @@ "ClientToken" ], "mappings": [ - { - "source": "InstanceId", - "target": "InstanceId" - }, { "source": "Key", "target": "Key" @@ -9687,7 +10122,10 @@ }, { "source": "Username", - "target": "Username" + "target": "Username", + "unrepresentable": { + "max_length": 64 + } } ], "operation": "CreateUser", @@ -9817,7 +10255,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateCampaign", @@ -10369,6 +10812,85 @@ "phase": "delete", "service": "customer-profiles" }, + { + "cfn_type": "AWS::DAX::ParameterGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "ParameterGroupName", + "target": "ParameterGroupName" + } + ], + "operation": "CreateParameterGroup", + "phase": "create", + "service": "dax" + }, + { + "cfn_type": "AWS::DAX::ParameterGroup", + "mappings": [ + { + "source": "ParameterGroupName", + "target": "ParameterGroupName" + } + ], + "operation": "DeleteParameterGroup", + "phase": "delete", + "service": "dax" + }, + { + "cfn_type": "AWS::DLM::LifecyclePolicy", + "mappings": [ + { + "source": "CopyTags", + "target": "CopyTags" + }, + { + "source": "CreateInterval", + "target": "CreateInterval" + }, + { + "source": "DefaultPolicy", + "target": "DefaultPolicy" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "ExecutionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "ExtendDeletion", + "target": "ExtendDeletion" + }, + { + "source": "RetainInterval", + "target": "RetainInterval" + }, + { + "source": "State", + "target": "State" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateLifecyclePolicy", + "phase": "create", + "service": "dlm" + }, + { + "cfn_type": "AWS::DLM::LifecyclePolicy", + "mappings": [], + "operation": "DeleteLifecyclePolicy", + "phase": "delete", + "service": "dlm" + }, { "cfn_type": "AWS::DMS::Certificate", "mappings": [ @@ -10738,6 +11260,65 @@ "phase": "delete", "service": "dms" }, + { + "cfn_type": "AWS::DMS::ReplicationTask", + "mappings": [ + { + "source": "CdcStartPosition", + "target": "CdcStartPosition" + }, + { + "source": "CdcStopPosition", + "target": "CdcStopPosition" + }, + { + "source": "MigrationType", + "target": "MigrationType" + }, + { + "source": "ReplicationInstanceArn", + "target": "ReplicationInstanceArn" + }, + { + "source": "ReplicationTaskIdentifier", + "target": "ReplicationTaskIdentifier" + }, + { + "source": "ReplicationTaskSettings", + "target": "ReplicationTaskSettings" + }, + { + "source": "ResourceIdentifier", + "target": "ResourceIdentifier" + }, + { + "source": "SourceEndpointArn", + "target": "SourceEndpointArn" + }, + { + "source": "TableMappings", + "target": "TableMappings" + }, + { + "source": "TargetEndpointArn", + "target": "TargetEndpointArn" + }, + { + "source": "TaskData", + "target": "TaskData" + } + ], + "operation": "CreateReplicationTask", + "phase": "create", + "service": "dms" + }, + { + "cfn_type": "AWS::DMS::ReplicationTask", + "mappings": [], + "operation": "DeleteReplicationTask", + "phase": "delete", + "service": "dms" + }, { "cfn_type": "AWS::DRS::SourceNetwork", "mappings": [ @@ -10785,7 +11366,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateCluster", @@ -11061,6 +11647,25 @@ "phase": "delete", "service": "dataexchange" }, + { + "cfn_type": "AWS::DataExchange::EventAction", + "mappings": [ + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateEventAction", + "phase": "create", + "service": "dataexchange" + }, + { + "cfn_type": "AWS::DataExchange::EventAction", + "mappings": [], + "operation": "DeleteEventAction", + "phase": "delete", + "service": "dataexchange" + }, { "cfn_type": "AWS::DataPipeline::Pipeline", "mappings": [ @@ -11148,7 +11753,10 @@ }, { "source": "FileSystemAccessRoleArn", - "target": "FileSystemAccessRoleArn" + "target": "FileSystemAccessRoleArn", + "unrepresentable": { + "max_length": 128 + } }, { "source": "InTransitEncryption", @@ -11316,7 +11924,10 @@ "mappings": [ { "source": "AccessKey", - "target": "AccessKey" + "target": "AccessKey", + "unrepresentable": { + "min_length": 1 + } }, { "source": "AgentArns", @@ -11328,7 +11939,10 @@ }, { "source": "SecretKey", - "target": "SecretKey" + "target": "SecretKey", + "unrepresentable": { + "min_length": 8 + } }, { "source": "ServerHostname", @@ -11356,15 +11970,26 @@ "mappings": [ { "source": "S3BucketArn", - "target": "S3BucketArn" + "target": "S3BucketArn", + "unrepresentable": { + "max_length": 156 + } }, { "source": "S3StorageClass", - "target": "S3StorageClass" + "target": "S3StorageClass", + "unrepresentable": { + "rejected_values": [ + "OUTPOSTS" + ] + } }, { "source": "Subdirectory", - "target": "Subdirectory" + "target": "Subdirectory", + "unrepresentable": { + "max_length": 1024 + } } ], "operation": "CreateLocationS3", @@ -11384,7 +12009,12 @@ }, { "source": "DnsIpAddresses", - "target": "DnsIpAddresses" + "target": "DnsIpAddresses", + "unrepresentable": { + "items": { + "max_length": 15 + } + } }, { "source": "Domain", @@ -11428,7 +12058,10 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "min_length": 1 + } }, { "source": "SourceLocationArn", @@ -11980,7 +12613,12 @@ }, { "source": "policyType", - "target": "PolicyType" + "target": "PolicyType", + "unrepresentable": { + "rejected_values": [ + "USE_ASSET_TYPE" + ] + } } ], "operation": "AddPolicyGrant", @@ -12007,7 +12645,12 @@ }, { "source": "policyType", - "target": "PolicyType" + "target": "PolicyType", + "unrepresentable": { + "rejected_values": [ + "USE_ASSET_TYPE" + ] + } } ], "operation": "RemovePolicyGrant", @@ -12162,7 +12805,10 @@ }, { "source": "authorizedPrincipals", - "target": "AuthorizedPrincipals" + "target": "AuthorizedPrincipals", + "unrepresentable": { + "max_length": 10 + } }, { "source": "domainIdentifier", @@ -12236,6 +12882,49 @@ "phase": "create", "service": "datazone" }, + { + "cfn_type": "AWS::Deadline::Budget", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "approximateDollarLimit", + "target": "ApproximateDollarLimit" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "displayName", + "target": "DisplayName" + }, + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateBudget", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::Budget", + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + } + ], + "operation": "DeleteBudget", + "phase": "delete", + "service": "deadline" + }, { "cfn_type": "AWS::Deadline::Farm", "ignored_inputs": [ @@ -12696,6 +13385,45 @@ "phase": "delete", "service": "deadline" }, + { + "cfn_type": "AWS::Deadline::Worker", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "fleetId", + "target": "FleetId" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateWorker", + "phase": "create", + "service": "deadline" + }, + { + "cfn_type": "AWS::Deadline::Worker", + "mappings": [ + { + "source": "farmId", + "target": "FarmId" + }, + { + "source": "fleetId", + "target": "FleetId" + } + ], + "operation": "DeleteWorker", + "phase": "delete", + "service": "deadline" + }, { "cfn_type": "AWS::Detective::Graph", "mappings": [ @@ -12930,7 +13658,10 @@ "mappings": [ { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "maxDevices", @@ -12938,7 +13669,10 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "min_length": 1 + } }, { "source": "projectArn", @@ -12961,7 +13695,10 @@ "mappings": [ { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "excludeAppPackagesFromCleanup", @@ -12969,7 +13706,10 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "min_length": 1 + } }, { "source": "packageCleanup", @@ -12996,7 +13736,10 @@ "mappings": [ { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "downlinkBandwidthBits", @@ -13016,7 +13759,10 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "min_length": 1 + } }, { "source": "projectArn", @@ -13063,7 +13809,10 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "CreateProject", @@ -13136,19 +13885,31 @@ "mappings": [ { "source": "serviceDnsName", - "target": "ServiceDnsName" + "target": "ServiceDnsName", + "unrepresentable": { + "min_length": 1 + } }, { "source": "vpceConfigurationDescription", - "target": "VpceConfigurationDescription" + "target": "VpceConfigurationDescription", + "unrepresentable": { + "min_length": 1 + } }, { "source": "vpceConfigurationName", - "target": "VpceConfigurationName" + "target": "VpceConfigurationName", + "unrepresentable": { + "min_length": 1 + } }, { "source": "vpceServiceName", - "target": "VpceServiceName" + "target": "VpceServiceName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "CreateVPCEConfiguration", @@ -13343,6 +14104,25 @@ "phase": "create", "service": "ds" }, + { + "cfn_type": "AWS::DocDB::DBClusterParameterGroup", + "mappings": [ + { + "source": "Description", + "target": "Description" + } + ], + "operation": "CreateDBClusterParameterGroup", + "phase": "create", + "service": "docdb" + }, + { + "cfn_type": "AWS::DocDB::DBClusterParameterGroup", + "mappings": [], + "operation": "DeleteDBClusterParameterGroup", + "phase": "delete", + "service": "docdb" + }, { "cfn_type": "AWS::DocDB::DBSubnetGroup", "mappings": [ @@ -13436,7 +14216,10 @@ }, { "source": "GlobalClusterIdentifier", - "target": "GlobalClusterIdentifier" + "target": "GlobalClusterIdentifier", + "unrepresentable": { + "max_length": 63 + } }, { "source": "SourceDBClusterIdentifier", @@ -13456,7 +14239,10 @@ "mappings": [ { "source": "GlobalClusterIdentifier", - "target": "GlobalClusterIdentifier" + "target": "GlobalClusterIdentifier", + "unrepresentable": { + "max_length": 63 + } } ], "operation": "DeleteGlobalCluster", @@ -13546,7 +14332,11 @@ }, { "source": "TableName", - "target": "TableName" + "target": "TableName", + "unrepresentable": { + "max_length": 255, + "min_length": 3 + } } ], "operation": "CreateBackup", @@ -13596,6 +14386,81 @@ "phase": "delete", "service": "dynamodb" }, + { + "cfn_type": "AWS::EC2::ApplicationStatusCheck", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], + "mappings": [ + { + "source": "Aggregation", + "target": "Aggregation" + }, + { + "source": "DeviceIndex", + "target": "DeviceIndex" + }, + { + "source": "FailureThreshold", + "target": "FailureThreshold" + }, + { + "source": "InitializationGracePeriodSeconds", + "target": "InitializationGracePeriodSeconds" + }, + { + "source": "Interval", + "target": "Interval" + }, + { + "source": "IpScope", + "target": "IpScope" + }, + { + "source": "IpVersion", + "target": "IpVersion" + }, + { + "source": "Path", + "target": "Path" + }, + { + "source": "Port", + "target": "Port" + }, + { + "source": "Protocol", + "target": "Protocol" + }, + { + "source": "StatusCodeMatcher", + "target": "StatusCodeMatcher" + }, + { + "source": "SuccessThreshold", + "target": "SuccessThreshold" + }, + { + "source": "Timeout", + "target": "Timeout" + } + ], + "operation": "CreateApplicationStatusCheck", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::ApplicationStatusCheck", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], + "mappings": [], + "operation": "DeleteApplicationStatusCheck", + "phase": "delete", + "service": "ec2" + }, { "cfn_type": "AWS::EC2::CapacityManagerDataExport", "ignored_inputs": [ @@ -14004,6 +14869,36 @@ "phase": "delete", "service": "ec2" }, + { + "cfn_type": "AWS::EC2::FpgaImage", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateFpgaImage", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::FpgaImage", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [], + "operation": "DeleteFpgaImage", + "phase": "delete", + "service": "ec2" + }, { "cfn_type": "AWS::EC2::Host", "ignored_inputs": [ @@ -14510,6 +15405,32 @@ "phase": "delete", "service": "ec2" }, + { + "cfn_type": "AWS::EC2::IpamExternalResourceVerificationToken", + "ignored_inputs": [ + "ClientToken", + "DryRun" + ], + "mappings": [ + { + "source": "IpamId", + "target": "IpamId" + } + ], + "operation": "CreateIpamExternalResourceVerificationToken", + "phase": "create", + "service": "ec2" + }, + { + "cfn_type": "AWS::EC2::IpamExternalResourceVerificationToken", + "ignored_inputs": [ + "DryRun" + ], + "mappings": [], + "operation": "DeleteIpamExternalResourceVerificationToken", + "phase": "delete", + "service": "ec2" + }, { "cfn_type": "AWS::EC2::KeyPair", "ignored_inputs": [ @@ -15354,7 +16275,12 @@ }, { "source": "PersistRoutes", - "target": "PersistRoutes" + "target": "PersistRoutes", + "unrepresentable": { + "rejected_values": [ + "reset" + ] + } }, { "source": "PersistRoutesDuration", @@ -15675,7 +16601,12 @@ "mappings": [ { "source": "State", - "target": "State" + "target": "State", + "unrepresentable": { + "rejected_values": [ + "unblocked" + ] + } } ], "operation": "EnableSnapshotBlockPublicAccess", @@ -16070,7 +17001,13 @@ }, { "source": "DestinationTransitGatewayAttachmentType", - "target": "DestinationTransitGatewayAttachmentType" + "target": "DestinationTransitGatewayAttachmentType", + "unrepresentable": { + "rejected_values": [ + "connect", + "tgw-peering" + ] + } }, { "source": "MeteredAccount", @@ -16098,7 +17035,13 @@ }, { "source": "SourceTransitGatewayAttachmentType", - "target": "SourceTransitGatewayAttachmentType" + "target": "SourceTransitGatewayAttachmentType", + "unrepresentable": { + "rejected_values": [ + "connect", + "tgw-peering" + ] + } }, { "source": "TransitGatewayMeteringPolicyId", @@ -17495,7 +18438,10 @@ }, { "source": "lifecyclePolicy", - "target": "LifecyclePolicy" + "target": "LifecyclePolicy", + "unrepresentable": { + "min_length": 100 + } }, { "source": "prefix", @@ -17767,7 +18713,12 @@ }, { "source": "launchType", - "target": "LaunchType" + "target": "LaunchType", + "unrepresentable": { + "rejected_values": [ + "MANAGED_INSTANCES" + ] + } }, { "source": "platformVersion", @@ -17775,7 +18726,12 @@ }, { "source": "propagateTags", - "target": "PropagateTags" + "target": "PropagateTags", + "unrepresentable": { + "rejected_values": [ + "NONE" + ] + } }, { "source": "role", @@ -17882,7 +18838,13 @@ }, { "source": "launchType", - "target": "LaunchType" + "target": "LaunchType", + "unrepresentable": { + "rejected_values": [ + "EXTERNAL", + "MANAGED_INSTANCES" + ] + } }, { "source": "platformVersion", @@ -18107,7 +19069,15 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_key": { + "max_length": 127 + }, + "tag_value": { + "max_length": 255 + } + } } ], "operation": "CreateAddon", @@ -18252,7 +19222,15 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_key": { + "max_length": 127 + }, + "tag_value": { + "max_length": 255 + } + } } ], "operation": "CreateFargateProfile", @@ -18480,7 +19458,10 @@ }, { "source": "DefaultS3Location", - "target": "DefaultS3Location" + "target": "DefaultS3Location", + "unrepresentable": { + "min_length": 6 + } }, { "source": "Description", @@ -18492,7 +19473,10 @@ }, { "source": "EngineSecurityGroupId", - "target": "EngineSecurityGroupId" + "target": "EngineSecurityGroupId", + "unrepresentable": { + "min_length": 4 + } }, { "source": "IdcInstanceArn", @@ -18504,7 +19488,10 @@ }, { "source": "IdpAuthUrl", - "target": "IdpAuthUrl" + "target": "IdpAuthUrl", + "unrepresentable": { + "max_length": 4096 + } }, { "source": "IdpRelayStateParameterName", @@ -18512,7 +19499,10 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "min_length": 1 + } }, { "source": "ServiceRole", @@ -18567,7 +19557,10 @@ }, { "source": "StudioId", - "target": "StudioId" + "target": "StudioId", + "unrepresentable": { + "min_length": 4 + } } ], "operation": "CreateStudioSessionMapping", @@ -18587,7 +19580,10 @@ }, { "source": "StudioId", - "target": "StudioId" + "target": "StudioId", + "unrepresentable": { + "min_length": 4 + } } ], "operation": "DeleteStudioSessionMapping", @@ -20387,7 +21383,10 @@ }, { "source": "roleArn", - "target": "RoleArn" + "target": "RoleArn", + "unrepresentable": { + "max_length": 1224 + } } ], "operation": "CreateExperimentTemplate", @@ -20421,7 +21420,10 @@ }, { "source": "roleArn", - "target": "RoleArn" + "target": "RoleArn", + "unrepresentable": { + "max_length": 1224 + } } ], "operation": "CreateTargetAccountConfiguration", @@ -20764,7 +21766,12 @@ }, { "source": "elements", - "target": "Elements" + "target": "Elements", + "unrepresentable": { + "items": { + "max_length": 64 + } + } }, { "source": "name", @@ -20824,11 +21831,21 @@ "mappings": [ { "source": "dataSource", - "target": "DataSource" + "target": "DataSource", + "unrepresentable": { + "rejected_values": [ + "MODEL_SCORE" + ] + } }, { "source": "dataType", - "target": "DataType" + "target": "DataType", + "unrepresentable": { + "rejected_values": [ + "DATETIME" + ] + } }, { "source": "defaultValue", @@ -21009,7 +22026,10 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 128 + } } ], "operation": "DeleteContainerGroupDefinition", @@ -21144,7 +22164,10 @@ }, { "source": "GameServerGroupName", - "target": "GameServerGroupName" + "target": "GameServerGroupName", + "unrepresentable": { + "max_length": 128 + } } ], "operation": "DeleteGameServerGroup", @@ -21156,7 +22179,10 @@ "mappings": [ { "source": "CustomEventData", - "target": "CustomEventData" + "target": "CustomEventData", + "unrepresentable": { + "min_length": 1 + } }, { "source": "Name", @@ -21164,7 +22190,10 @@ }, { "source": "NotificationTarget", - "target": "NotificationTarget" + "target": "NotificationTarget", + "unrepresentable": { + "min_length": 1 + } }, { "source": "TimeoutInSeconds", @@ -21180,7 +22209,10 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 128 + } } ], "operation": "DeleteGameSessionQueue", @@ -21204,7 +22236,11 @@ "mappings": [ { "source": "LocationName", - "target": "LocationName" + "target": "LocationName", + "unrepresentable": { + "max_length": 64, + "min_length": 8 + } } ], "operation": "DeleteLocation", @@ -21248,7 +22284,12 @@ }, { "source": "GameSessionQueueArns", - "target": "GameSessionQueueArns" + "target": "GameSessionQueueArns", + "unrepresentable": { + "items": { + "max_length": 256 + } + } }, { "source": "Name", @@ -21264,7 +22305,10 @@ }, { "source": "RuleSetName", - "target": "RuleSetName" + "target": "RuleSetName", + "unrepresentable": { + "max_length": 128 + } } ], "operation": "CreateMatchmakingConfiguration", @@ -21276,7 +22320,10 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 128 + } } ], "operation": "DeleteMatchmakingConfiguration", @@ -21304,7 +22351,10 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 128 + } } ], "operation": "DeleteMatchmakingRuleSet", @@ -21422,7 +22472,10 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 64 + } } ], "operation": "CreateAccelerator", @@ -21571,7 +22624,10 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 128 + } } ], "operation": "DeleteBlueprint", @@ -21608,6 +22664,30 @@ "phase": "delete", "service": "glue" }, + { + "cfn_type": "AWS::Glue::Connection", + "mappings": [ + { + "source": "CatalogId", + "target": "CatalogId" + } + ], + "operation": "CreateConnection", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Connection", + "mappings": [ + { + "source": "CatalogId", + "target": "CatalogId" + } + ], + "operation": "DeleteConnection", + "phase": "delete", + "service": "glue" + }, { "cfn_type": "AWS::Glue::Crawler", "mappings": [ @@ -21996,7 +23076,10 @@ "mappings": [ { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 1000 + } }, { "source": "Tags", @@ -22027,7 +23110,10 @@ }, { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 1000 + } }, { "source": "SchemaDefinition", @@ -22116,6 +23202,78 @@ "phase": "delete", "service": "glue" }, + { + "cfn_type": "AWS::Glue::Session", + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "GlueVersion", + "target": "GlueVersion" + }, + { + "source": "Id", + "target": "Id" + }, + { + "source": "IdleTimeout", + "target": "IdleTimeout" + }, + { + "source": "MaxCapacity", + "target": "MaxCapacity" + }, + { + "source": "NumberOfWorkers", + "target": "NumberOfWorkers" + }, + { + "source": "RequestOrigin", + "target": "RequestOrigin" + }, + { + "source": "Role", + "target": "Role" + }, + { + "source": "SecurityConfiguration", + "target": "SecurityConfiguration" + }, + { + "source": "Tags", + "target": "Tags" + }, + { + "source": "Timeout", + "target": "Timeout" + }, + { + "source": "WorkerType", + "target": "WorkerType" + } + ], + "operation": "CreateSession", + "phase": "create", + "service": "glue" + }, + { + "cfn_type": "AWS::Glue::Session", + "mappings": [ + { + "source": "Id", + "target": "Id" + }, + { + "source": "RequestOrigin", + "target": "RequestOrigin" + } + ], + "operation": "DeleteSession", + "phase": "delete", + "service": "glue" + }, { "cfn_type": "AWS::Glue::TableOptimizer", "mappings": [ @@ -22213,11 +23371,19 @@ "mappings": [ { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 512, + "min_length": 1 + } }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 128, + "min_length": 5 + } }, { "source": "Tags", @@ -22233,7 +23399,11 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 128, + "min_length": 5 + } } ], "operation": "DeleteUsageProfile", @@ -22722,7 +23892,10 @@ }, { "source": "DetectorId", - "target": "DetectorId" + "target": "DetectorId", + "unrepresentable": { + "max_length": 32 + } }, { "source": "ExpectedBucketOwner", @@ -22754,7 +23927,10 @@ "mappings": [ { "source": "DetectorId", - "target": "DetectorId" + "target": "DetectorId", + "unrepresentable": { + "max_length": 32 + } } ], "operation": "DeleteThreatEntitySet", @@ -22773,7 +23949,10 @@ }, { "source": "DetectorId", - "target": "DetectorId" + "target": "DetectorId", + "unrepresentable": { + "max_length": 32 + } }, { "source": "ExpectedBucketOwner", @@ -22805,7 +23984,10 @@ "mappings": [ { "source": "DetectorId", - "target": "DetectorId" + "target": "DetectorId", + "unrepresentable": { + "max_length": 32 + } } ], "operation": "DeleteThreatIntelSet", @@ -22824,7 +24006,10 @@ }, { "source": "DetectorId", - "target": "DetectorId" + "target": "DetectorId", + "unrepresentable": { + "max_length": 32 + } }, { "source": "ExpectedBucketOwner", @@ -22856,7 +24041,10 @@ "mappings": [ { "source": "DetectorId", - "target": "DetectorId" + "target": "DetectorId", + "unrepresentable": { + "max_length": 32 + } } ], "operation": "DeleteTrustedEntitySet", @@ -23230,7 +24418,10 @@ "mappings": [ { "source": "UserName", - "target": "UserName" + "target": "UserName", + "unrepresentable": { + "max_length": 64 + } } ], "operation": "DeleteUser", @@ -23630,7 +24821,10 @@ }, { "source": "maximumMessageRatePerSecond", - "target": "MaximumMessageRatePerSecond" + "target": "MaximumMessageRatePerSecond", + "unrepresentable": { + "maximum": 10.0 + } }, { "source": "name", @@ -23638,7 +24832,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateRoom", @@ -23712,6 +24911,74 @@ "phase": "delete", "service": "identitystore" }, + { + "cfn_type": "AWS::IdentityStore::User", + "mappings": [ + { + "source": "Birthdate", + "target": "Birthdate" + }, + { + "source": "DisplayName", + "target": "DisplayName" + }, + { + "source": "IdentityStoreId", + "target": "IdentityStoreId" + }, + { + "source": "Locale", + "target": "Locale" + }, + { + "source": "NickName", + "target": "NickName" + }, + { + "source": "PreferredLanguage", + "target": "PreferredLanguage" + }, + { + "source": "ProfileUrl", + "target": "ProfileUrl" + }, + { + "source": "Timezone", + "target": "Timezone" + }, + { + "source": "Title", + "target": "Title" + }, + { + "source": "UserName", + "target": "UserName" + }, + { + "source": "UserType", + "target": "UserType" + }, + { + "source": "Website", + "target": "Website" + } + ], + "operation": "CreateUser", + "phase": "create", + "service": "identitystore" + }, + { + "cfn_type": "AWS::IdentityStore::User", + "mappings": [ + { + "source": "IdentityStoreId", + "target": "IdentityStoreId" + } + ], + "operation": "DeleteUser", + "phase": "delete", + "service": "identitystore" + }, { "cfn_type": "AWS::ImageBuilder::Component", "ignored_inputs": [ @@ -23802,7 +25069,12 @@ }, { "source": "platformOverride", - "target": "PlatformOverride" + "target": "PlatformOverride", + "unrepresentable": { + "rejected_values": [ + "macOS" + ] + } }, { "source": "workingDirectory", @@ -24188,7 +25460,10 @@ }, { "source": "environmentId", - "target": "EnvironmentId" + "target": "EnvironmentId", + "unrepresentable": { + "max_length": 32 + } }, { "source": "tags", @@ -24289,6 +25564,63 @@ "phase": "delete", "service": "invoicing" }, + { + "cfn_type": "AWS::Invoicing::ProcurementPortalPreference", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "BuyerDomain", + "target": "BuyerDomain" + }, + { + "source": "BuyerIdentifier", + "target": "BuyerIdentifier" + }, + { + "source": "EinvoiceDeliveryEnabled", + "target": "EinvoiceDeliveryEnabled" + }, + { + "source": "ProcurementPortalInstanceEndpoint", + "target": "ProcurementPortalInstanceEndpoint" + }, + { + "source": "ProcurementPortalName", + "target": "ProcurementPortalName" + }, + { + "source": "ProcurementPortalSharedSecret", + "target": "ProcurementPortalSharedSecret" + }, + { + "source": "PurchaseOrderRetrievalEnabled", + "target": "PurchaseOrderRetrievalEnabled" + }, + { + "source": "SupplierDomain", + "target": "SupplierDomain" + }, + { + "source": "SupplierIdentifier", + "target": "SupplierIdentifier" + } + ], + "operation": "CreateProcurementPortalPreference", + "phase": "create", + "service": "invoicing" + }, + { + "cfn_type": "AWS::Invoicing::ProcurementPortalPreference", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [], + "operation": "DeleteProcurementPortalPreference", + "phase": "delete", + "service": "invoicing" + }, { "cfn_type": "AWS::IoT::AccountAuditConfiguration", "mappings": [], @@ -24396,7 +25728,12 @@ }, { "source": "status", - "target": "Status" + "target": "Status", + "unrepresentable": { + "rejected_values": [ + "REGISTER_INACTIVE" + ] + } } ], "operation": "RegisterCertificate", @@ -24426,7 +25763,10 @@ }, { "source": "lambdaFunctionArn", - "target": "LambdaFunctionArn" + "target": "LambdaFunctionArn", + "unrepresentable": { + "max_length": 170 + } } ], "operation": "CreateCertificateProvider", @@ -24470,7 +25810,10 @@ }, { "source": "roleArn", - "target": "RoleArn" + "target": "RoleArn", + "unrepresentable": { + "max_length": 2028 + } } ], "operation": "CreateCommand", @@ -24536,7 +25879,10 @@ }, { "source": "stringValues", - "target": "StringValues" + "target": "StringValues", + "unrepresentable": { + "max_length": 5 + } }, { "source": "type", @@ -24804,7 +26150,10 @@ "mappings": [ { "source": "policyDocument", - "target": "PolicyDocument" + "target": "PolicyDocument", + "unrepresentable": { + "min_length": 1 + } }, { "source": "policyName", @@ -24876,7 +26225,12 @@ }, { "source": "targetType", - "target": "TargetType" + "target": "TargetType", + "unrepresentable": { + "rejected_values": [ + "DEFAULT" + ] + } } ], "operation": "DeleteV2LoggingLevel", @@ -24999,7 +26353,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreatePackage", @@ -25026,7 +26385,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } }, { "source": "versionName", @@ -25056,6 +26420,38 @@ "phase": "delete", "service": "iot" }, + { + "cfn_type": "AWS::IoT::Stream", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "roleArn", + "target": "RoleArn" + }, + { + "source": "streamId", + "target": "StreamId" + } + ], + "operation": "CreateStream", + "phase": "create", + "service": "iot" + }, + { + "cfn_type": "AWS::IoT::Stream", + "mappings": [ + { + "source": "streamId", + "target": "StreamId" + } + ], + "operation": "DeleteStream", + "phase": "delete", + "service": "iot" + }, { "cfn_type": "AWS::IoT::Thing", "mappings": [ @@ -25283,7 +26679,10 @@ }, { "source": "nodes", - "target": "Nodes" + "target": "Nodes", + "unrepresentable": { + "min_length": 1 + } }, { "source": "signalCatalogArn", @@ -25434,7 +26833,14 @@ }, { "source": "AuthenticationMaterialType", - "target": "AuthenticationMaterialType" + "target": "AuthenticationMaterialType", + "unrepresentable": { + "rejected_values": [ + "CUSTOM_PROTOCOL_QR_BAR_CODE", + "DISCOVERED_DEVICE", + "PRE_ONBOARDED_CLOUD" + ] + } }, { "source": "Brand", @@ -25755,6 +27161,49 @@ "phase": "delete", "service": "iotsitewise" }, + { + "cfn_type": "AWS::IoTSiteWise::Pipeline", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "pipelineName", + "target": "PipelineName" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workspaceName", + "target": "WorkspaceName" + } + ], + "operation": "CreatePipeline", + "phase": "create", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Pipeline", + "mappings": [ + { + "source": "pipelineName", + "target": "PipelineName" + }, + { + "source": "workspaceName", + "target": "WorkspaceName" + } + ], + "operation": "DeletePipeline", + "phase": "delete", + "service": "iotsitewise" + }, { "cfn_type": "AWS::IoTSiteWise::Portal", "ignored_inputs": [ @@ -25845,6 +27294,87 @@ "phase": "delete", "service": "iotsitewise" }, + { + "cfn_type": "AWS::IoTSiteWise::Task", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "taskName", + "target": "TaskName" + }, + { + "source": "workspaceName", + "target": "WorkspaceName" + } + ], + "operation": "CreateTask", + "phase": "create", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Task", + "mappings": [ + { + "source": "taskName", + "target": "TaskName" + }, + { + "source": "workspaceName", + "target": "WorkspaceName" + } + ], + "operation": "DeleteTask", + "phase": "delete", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Workspace", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "tags", + "target": "Tags" + }, + { + "source": "workspaceDescription", + "target": "WorkspaceDescription" + }, + { + "source": "workspaceName", + "target": "WorkspaceName" + } + ], + "operation": "CreateWorkspace", + "phase": "create", + "service": "iotsitewise" + }, + { + "cfn_type": "AWS::IoTSiteWise::Workspace", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "workspaceName", + "target": "WorkspaceName" + } + ], + "operation": "DeleteWorkspace", + "phase": "delete", + "service": "iotsitewise" + }, { "cfn_type": "AWS::IoTTwinMaker::ComponentType", "mappings": [ @@ -25854,7 +27384,10 @@ }, { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 512 + } }, { "source": "extendsFrom", @@ -25894,7 +27427,10 @@ "mappings": [ { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 512 + } }, { "source": "entityId", @@ -25946,7 +27482,10 @@ }, { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 512 + } }, { "source": "sceneId", @@ -26018,7 +27557,10 @@ "mappings": [ { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 512 + } }, { "source": "role", @@ -26126,11 +27668,17 @@ }, { "source": "FirmwareUpdateImage", - "target": "FirmwareUpdateImage" + "target": "FirmwareUpdateImage", + "unrepresentable": { + "max_length": 2048 + } }, { "source": "FirmwareUpdateRole", - "target": "FirmwareUpdateRole" + "target": "FirmwareUpdateRole", + "unrepresentable": { + "max_length": 256 + } }, { "source": "Name", @@ -26258,7 +27806,10 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 256 + } } ], "operation": "CreateWirelessGatewayTaskDefinition", @@ -26367,7 +27918,10 @@ }, { "source": "TargetKeyId", - "target": "TargetKeyId" + "target": "TargetKeyId", + "unrepresentable": { + "max_length": 256 + } } ], "operation": "CreateAlias", @@ -26411,7 +27965,13 @@ }, { "source": "Origin", - "target": "Origin" + "target": "Origin", + "unrepresentable": { + "rejected_values": [ + "AWS_CLOUDHSM", + "EXTERNAL_KEY_STORE" + ] + } } ], "operation": "CreateKey", @@ -26527,7 +28087,10 @@ "mappings": [ { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "IndexId", @@ -26543,7 +28106,10 @@ }, { "source": "RoleArn", - "target": "RoleArn" + "target": "RoleArn", + "unrepresentable": { + "min_length": 1 + } }, { "source": "Schedule", @@ -26551,7 +28117,18 @@ }, { "source": "Type", - "target": "Type" + "target": "Type", + "unrepresentable": { + "rejected_values": [ + "ALFRESCO", + "BOX", + "FSX", + "GITHUB", + "JIRA", + "QUIP", + "SLACK" + ] + } } ], "operation": "CreateDataSource", @@ -26578,7 +28155,10 @@ "mappings": [ { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "FileFormat", @@ -26598,7 +28178,10 @@ }, { "source": "RoleArn", - "target": "RoleArn" + "target": "RoleArn", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "CreateFaq", @@ -26637,7 +28220,10 @@ }, { "source": "RoleArn", - "target": "RoleArn" + "target": "RoleArn", + "unrepresentable": { + "min_length": 1 + } }, { "source": "UserContextPolicy", @@ -26655,6 +28241,84 @@ "phase": "delete", "service": "kendra" }, + { + "cfn_type": "AWS::Kendra::QuerySuggestionsBlockList", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "IndexId", + "target": "IndexId" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" + } + ], + "operation": "CreateQuerySuggestionsBlockList", + "phase": "create", + "service": "kendra" + }, + { + "cfn_type": "AWS::Kendra::QuerySuggestionsBlockList", + "mappings": [ + { + "source": "IndexId", + "target": "IndexId" + } + ], + "operation": "DeleteQuerySuggestionsBlockList", + "phase": "delete", + "service": "kendra" + }, + { + "cfn_type": "AWS::Kendra::Thesaurus", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "IndexId", + "target": "IndexId" + }, + { + "source": "Name", + "target": "Name" + }, + { + "source": "RoleArn", + "target": "RoleArn" + } + ], + "operation": "CreateThesaurus", + "phase": "create", + "service": "kendra" + }, + { + "cfn_type": "AWS::Kendra::Thesaurus", + "mappings": [ + { + "source": "IndexId", + "target": "IndexId" + } + ], + "operation": "DeleteThesaurus", + "phase": "delete", + "service": "kendra" + }, { "cfn_type": "AWS::KendraRanking::ExecutionPlan", "ignored_inputs": [ @@ -26718,7 +28382,12 @@ }, { "source": "Tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "max_length": 255 + } + } }, { "source": "WarmThroughputMiBps", @@ -26749,7 +28418,12 @@ }, { "source": "Tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "max_length": 255 + } + } } ], "operation": "RegisterStreamConsumer", @@ -26895,7 +28569,11 @@ }, { "source": "TableCatalogId", - "target": "TableCatalogId" + "target": "TableCatalogId", + "unrepresentable": { + "max_length": 12, + "min_length": 12 + } }, { "source": "TableName", @@ -26911,11 +28589,29 @@ "mappings": [ { "source": "Permissions", - "target": "Permissions" + "target": "Permissions", + "unrepresentable": { + "items": { + "rejected_values": [ + "CREATE_CATALOG", + "CREATE_LF_TAG_EXPRESSION", + "SUPER_USER" + ] + } + } }, { "source": "PermissionsWithGrantOption", - "target": "PermissionsWithGrantOption" + "target": "PermissionsWithGrantOption", + "unrepresentable": { + "items": { + "rejected_values": [ + "CREATE_CATALOG", + "CREATE_LF_TAG_EXPRESSION", + "SUPER_USER" + ] + } + } } ], "operation": "GrantPermissions", @@ -26927,11 +28623,29 @@ "mappings": [ { "source": "Permissions", - "target": "Permissions" + "target": "Permissions", + "unrepresentable": { + "items": { + "rejected_values": [ + "CREATE_CATALOG", + "CREATE_LF_TAG_EXPRESSION", + "SUPER_USER" + ] + } + } }, { "source": "PermissionsWithGrantOption", - "target": "PermissionsWithGrantOption" + "target": "PermissionsWithGrantOption", + "unrepresentable": { + "items": { + "rejected_values": [ + "CREATE_CATALOG", + "CREATE_LF_TAG_EXPRESSION", + "SUPER_USER" + ] + } + } } ], "operation": "RevokePermissions", @@ -26943,7 +28657,11 @@ "mappings": [ { "source": "CatalogId", - "target": "CatalogId" + "target": "CatalogId", + "unrepresentable": { + "max_length": 12, + "min_length": 12 + } }, { "source": "TagKey", @@ -26963,7 +28681,11 @@ "mappings": [ { "source": "CatalogId", - "target": "CatalogId" + "target": "CatalogId", + "unrepresentable": { + "max_length": 12, + "min_length": 12 + } }, { "source": "TagKey", @@ -27126,11 +28848,18 @@ }, { "source": "EventSourceArn", - "target": "EventSourceArn" + "target": "EventSourceArn", + "unrepresentable": { + "max_length": 1024, + "min_length": 12 + } }, { "source": "FunctionName", - "target": "FunctionName" + "target": "FunctionName", + "unrepresentable": { + "max_length": 140 + } }, { "source": "FunctionResponseTypes", @@ -27138,7 +28867,11 @@ }, { "source": "KMSKeyArn", - "target": "KmsKeyArn" + "target": "KmsKeyArn", + "unrepresentable": { + "max_length": 2048, + "min_length": 12 + } }, { "source": "MaximumBatchingWindowInSeconds", @@ -27447,15 +29180,25 @@ "mappings": [ { "source": "Action", - "target": "Action" + "target": "Action", + "unrepresentable": { + "max_length": 256, + "min_length": 1 + } }, { "source": "EventSourceToken", - "target": "EventSourceToken" + "target": "EventSourceToken", + "unrepresentable": { + "min_length": 1 + } }, { "source": "FunctionName", - "target": "FunctionName" + "target": "FunctionName", + "unrepresentable": { + "max_length": 140 + } }, { "source": "FunctionUrlAuthType", @@ -27467,7 +29210,11 @@ }, { "source": "Principal", - "target": "Principal" + "target": "Principal", + "unrepresentable": { + "max_length": 256, + "min_length": 1 + } }, { "source": "PrincipalOrgID", @@ -27475,11 +29222,18 @@ }, { "source": "SourceAccount", - "target": "SourceAccount" + "target": "SourceAccount", + "unrepresentable": { + "min_length": 12 + } }, { "source": "SourceArn", - "target": "SourceArn" + "target": "SourceArn", + "unrepresentable": { + "max_length": 1024, + "min_length": 12 + } } ], "operation": "AddPermission", @@ -27491,7 +29245,10 @@ "mappings": [ { "source": "FunctionName", - "target": "FunctionName" + "target": "FunctionName", + "unrepresentable": { + "max_length": 140 + } } ], "operation": "RemovePermission", @@ -27622,7 +29379,10 @@ }, { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 200 + } } ], "operation": "CreateBotAlias", @@ -27650,7 +29410,10 @@ }, { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 200 + } } ], "operation": "CreateBotVersion", @@ -27923,6 +29686,34 @@ "phase": "delete", "service": "lightsail" }, + { + "cfn_type": "AWS::Lightsail::ContactMethod", + "mappings": [ + { + "source": "contactEndpoint", + "target": "ContactEndpoint" + }, + { + "source": "protocol", + "target": "Protocol" + } + ], + "operation": "CreateContactMethod", + "phase": "create", + "service": "lightsail" + }, + { + "cfn_type": "AWS::Lightsail::ContactMethod", + "mappings": [ + { + "source": "protocol", + "target": "Protocol" + } + ], + "operation": "DeleteContactMethod", + "phase": "delete", + "service": "lightsail" + }, { "cfn_type": "AWS::Lightsail::Container", "mappings": [ @@ -28380,7 +30171,13 @@ }, { "source": "PricingPlan", - "target": "PricingPlan" + "target": "PricingPlan", + "unrepresentable": { + "rejected_values": [ + "MobileAssetManagement", + "MobileAssetTracking" + ] + } }, { "source": "PricingPlanDataSource", @@ -28420,7 +30217,13 @@ }, { "source": "PricingPlan", - "target": "PricingPlan" + "target": "PricingPlan", + "unrepresentable": { + "rejected_values": [ + "MobileAssetManagement", + "MobileAssetTracking" + ] + } }, { "source": "Tags", @@ -28460,7 +30263,13 @@ }, { "source": "PricingPlan", - "target": "PricingPlan" + "target": "PricingPlan", + "unrepresentable": { + "rejected_values": [ + "MobileAssetManagement", + "MobileAssetTracking" + ] + } }, { "source": "Tags", @@ -28500,7 +30309,13 @@ }, { "source": "PricingPlan", - "target": "PricingPlan" + "target": "PricingPlan", + "unrepresentable": { + "rejected_values": [ + "MobileAssetManagement", + "MobileAssetTracking" + ] + } }, { "source": "Tags", @@ -28548,7 +30363,13 @@ }, { "source": "PricingPlan", - "target": "PricingPlan" + "target": "PricingPlan", + "unrepresentable": { + "rejected_values": [ + "MobileAssetManagement", + "MobileAssetTracking" + ] + } }, { "source": "PricingPlanDataSource", @@ -28668,11 +30489,19 @@ }, { "source": "fieldDelimiter", - "target": "FieldDelimiter" + "target": "FieldDelimiter", + "unrepresentable": { + "min_length": 1 + } }, { "source": "recordFields", - "target": "RecordFields" + "target": "RecordFields", + "unrepresentable": { + "items": { + "max_length": 50 + } + } }, { "source": "tags", @@ -28839,7 +30668,12 @@ }, { "source": "evaluationFrequency", - "target": "EvaluationFrequency" + "target": "EvaluationFrequency", + "unrepresentable": { + "rejected_values": [ + "ONE_MIN" + ] + } }, { "source": "filterPattern", @@ -28851,7 +30685,12 @@ }, { "source": "logGroupArnList", - "target": "LogGroupArnList" + "target": "LogGroupArnList", + "unrepresentable": { + "items": { + "min_length": 20 + } + } } ], "operation": "CreateLogAnomalyDetector", @@ -29064,7 +30903,10 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 255 + } }, { "source": "queryLanguage", @@ -29119,7 +30961,12 @@ "mappings": [ { "source": "storageTier", - "target": "StorageTier" + "target": "StorageTier", + "unrepresentable": { + "rejected_values": [ + "STANDARD" + ] + } } ], "operation": "PutStorageTierPolicy", @@ -29396,6 +31243,37 @@ "phase": "delete", "service": "m2" }, + { + "cfn_type": "AWS::MGN::NetworkMigrationDefinition", + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + }, + { + "source": "targetDeployment", + "target": "TargetDeployment" + } + ], + "operation": "CreateNetworkMigrationDefinition", + "phase": "create", + "service": "mgn" + }, + { + "cfn_type": "AWS::MGN::NetworkMigrationDefinition", + "mappings": [], + "operation": "DeleteNetworkMigrationDefinition", + "phase": "delete", + "service": "mgn" + }, { "cfn_type": "AWS::MPA::ApprovalTeam", "ignored_inputs": [ @@ -29952,7 +31830,16 @@ }, { "source": "Tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_key": { + "max_length": 127 + }, + "tag_value": { + "max_length": 255, + "min_length": 1 + } + } } ], "operation": "CreateAccessor", @@ -31270,7 +33157,12 @@ }, { "source": "FunctionType", - "target": "FunctionType" + "target": "FunctionType", + "unrepresentable": { + "rejected_values": [ + "CONCURRENT_EXECUTOR" + ] + } }, { "source": "Tags", @@ -31381,6 +33273,50 @@ "phase": "delete", "service": "mediatailor" }, + { + "cfn_type": "AWS::MediaTailor::PrefetchSchedule", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "PlaybackConfigurationName", + "target": "PlaybackConfigurationName" + }, + { + "source": "ScheduleType", + "target": "ScheduleType" + }, + { + "source": "StreamId", + "target": "StreamId" + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreatePrefetchSchedule", + "phase": "create", + "service": "mediatailor" + }, + { + "cfn_type": "AWS::MediaTailor::PrefetchSchedule", + "mappings": [ + { + "source": "Name", + "target": "Name" + }, + { + "source": "PlaybackConfigurationName", + "target": "PlaybackConfigurationName" + } + ], + "operation": "DeletePrefetchSchedule", + "phase": "delete", + "service": "mediatailor" + }, { "cfn_type": "AWS::MediaTailor::SourceLocation", "mappings": [ @@ -32251,7 +34187,12 @@ }, { "source": "Type", - "target": "Type" + "target": "Type", + "unrepresentable": { + "rejected_values": [ + "STATEFUL_DOMAIN" + ] + } } ], "operation": "CreateRuleGroup", @@ -32267,7 +34208,12 @@ }, { "source": "Type", - "target": "Type" + "target": "Type", + "unrepresentable": { + "rejected_values": [ + "STATEFUL_DOMAIN" + ] + } } ], "operation": "DeleteRuleGroup", @@ -33266,7 +35212,10 @@ }, { "source": "cpuCoreCount", - "target": "CpuCoreCount" + "target": "CpuCoreCount", + "unrepresentable": { + "maximum": 368.0 + } }, { "source": "dataStorageSizeInTBs", @@ -33731,6 +35680,48 @@ "phase": "delete", "service": "omics" }, + { + "cfn_type": "AWS::Omics::RunCache", + "ignored_inputs": [ + "requestId" + ], + "mappings": [ + { + "source": "cacheBehavior", + "target": "CacheBehavior" + }, + { + "source": "cacheBucketOwnerId", + "target": "CacheBucketOwnerId" + }, + { + "source": "cacheS3Location", + "target": "CacheS3Location" + }, + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateRunCache", + "phase": "create", + "service": "omics" + }, + { + "cfn_type": "AWS::Omics::RunCache", + "mappings": [], + "operation": "DeleteRunCache", + "phase": "delete", + "service": "omics" + }, { "cfn_type": "AWS::Omics::RunGroup", "ignored_inputs": [ @@ -33859,7 +35850,12 @@ }, { "source": "engine", - "target": "Engine" + "target": "Engine", + "unrepresentable": { + "rejected_values": [ + "WDL_LENIENT" + ] + } }, { "source": "main", @@ -33933,7 +35929,12 @@ }, { "source": "engine", - "target": "Engine" + "target": "Engine", + "unrepresentable": { + "rejected_values": [ + "WDL_LENIENT" + ] + } }, { "source": "main", @@ -34040,7 +36041,10 @@ "mappings": [ { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "name", @@ -34279,7 +36283,10 @@ "mappings": [ { "source": "description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "name", @@ -34932,6 +36939,29 @@ "phase": "delete", "service": "personalize" }, + { + "cfn_type": "AWS::Personalize::MetricAttribution", + "mappings": [ + { + "source": "datasetGroupArn", + "target": "DatasetGroupArn" + }, + { + "source": "name", + "target": "Name" + } + ], + "operation": "CreateMetricAttribution", + "phase": "create", + "service": "personalize" + }, + { + "cfn_type": "AWS::Personalize::MetricAttribution", + "mappings": [], + "operation": "DeleteMetricAttribution", + "phase": "delete", + "service": "personalize" + }, { "cfn_type": "AWS::Personalize::Schema", "mappings": [ @@ -34945,7 +36975,10 @@ }, { "source": "schema", - "target": "Schema" + "target": "Schema", + "unrepresentable": { + "max_length": 10000 + } } ], "operation": "CreateSchema", @@ -35074,6 +37107,43 @@ "phase": "delete", "service": "pipes" }, + { + "cfn_type": "AWS::PricingPlanManager::Subscription", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "planFamily", + "target": "PlanFamily" + }, + { + "source": "planTier", + "target": "PlanTier" + }, + { + "source": "resourceArns", + "target": "ResourceArns" + }, + { + "source": "usageLevel", + "target": "UsageLevel" + } + ], + "operation": "CreateSubscription", + "phase": "create", + "service": "pricing-plan-manager" + }, + { + "cfn_type": "AWS::PricingPlanManager::Subscription", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [], + "operation": "CancelSubscription", + "phase": "delete", + "service": "pricing-plan-manager" + }, { "cfn_type": "AWS::Proton::EnvironmentAccountConnection", "ignored_inputs": [ @@ -35082,11 +37152,17 @@ "mappings": [ { "source": "codebuildRoleArn", - "target": "CodebuildRoleArn" + "target": "CodebuildRoleArn", + "unrepresentable": { + "max_length": 200 + } }, { "source": "componentRoleArn", - "target": "ComponentRoleArn" + "target": "ComponentRoleArn", + "unrepresentable": { + "max_length": 200 + } }, { "source": "environmentName", @@ -35098,7 +37174,10 @@ }, { "source": "roleArn", - "target": "RoleArn" + "target": "RoleArn", + "unrepresentable": { + "max_length": 200 + } } ], "operation": "CreateEnvironmentAccountConnection", @@ -35396,7 +37475,10 @@ }, { "source": "statementId", - "target": "StatementId" + "target": "StatementId", + "unrepresentable": { + "max_length": 100 + } } ], "operation": "DisassociatePermission", @@ -35493,7 +37575,12 @@ }, { "source": "origins", - "target": "Origins" + "target": "Origins", + "unrepresentable": { + "items": { + "max_length": 64 + } + } }, { "source": "roleArn", @@ -36092,11 +38179,17 @@ }, { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 512 + } }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 128 + } }, { "source": "SpaceId", @@ -36325,6 +38418,34 @@ "phase": "delete", "service": "ram" }, + { + "cfn_type": "AWS::RDS::ClusterSnapshot", + "mappings": [ + { + "source": "DBClusterIdentifier", + "target": "DBClusterIdentifier" + }, + { + "source": "DBClusterSnapshotIdentifier", + "target": "DBClusterSnapshotIdentifier" + } + ], + "operation": "CreateDBClusterSnapshot", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::ClusterSnapshot", + "mappings": [ + { + "source": "DBClusterSnapshotIdentifier", + "target": "DBClusterSnapshotIdentifier" + } + ], + "operation": "DeleteDBClusterSnapshot", + "phase": "delete", + "service": "rds" + }, { "cfn_type": "AWS::RDS::CustomDBEngineVersion", "mappings": [ @@ -36502,7 +38623,10 @@ }, { "source": "GlobalClusterIdentifier", - "target": "GlobalClusterIdentifier" + "target": "GlobalClusterIdentifier", + "unrepresentable": { + "max_length": 63 + } }, { "source": "Iops", @@ -37113,6 +39237,34 @@ "phase": "delete", "service": "rds" }, + { + "cfn_type": "AWS::RDS::DBSnapshot", + "mappings": [ + { + "source": "DBInstanceIdentifier", + "target": "DBInstanceIdentifier" + }, + { + "source": "DBSnapshotIdentifier", + "target": "DBSnapshotIdentifier" + } + ], + "operation": "CreateDBSnapshot", + "phase": "create", + "service": "rds" + }, + { + "cfn_type": "AWS::RDS::DBSnapshot", + "mappings": [ + { + "source": "DBSnapshotIdentifier", + "target": "DBSnapshotIdentifier" + } + ], + "operation": "DeleteDBSnapshot", + "phase": "delete", + "service": "rds" + }, { "cfn_type": "AWS::RDS::DBSubnetGroup", "mappings": [ @@ -37210,7 +39362,10 @@ }, { "source": "GlobalClusterIdentifier", - "target": "GlobalClusterIdentifier" + "target": "GlobalClusterIdentifier", + "unrepresentable": { + "max_length": 63 + } }, { "source": "SourceDBClusterIdentifier", @@ -37230,7 +39385,10 @@ "mappings": [ { "source": "GlobalClusterIdentifier", - "target": "GlobalClusterIdentifier" + "target": "GlobalClusterIdentifier", + "unrepresentable": { + "max_length": 63 + } } ], "operation": "DeleteGlobalCluster", @@ -37246,7 +39404,10 @@ }, { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "IntegrationName", @@ -37324,7 +39485,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "max_length": 256 + } + } } ], "operation": "CreateInboundExternalLink", @@ -37360,7 +39526,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "max_length": 256 + } + } } ], "operation": "CreateLink", @@ -37399,7 +39570,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "max_length": 256 + } + } } ], "operation": "CreateLinkRoutingRule", @@ -37438,7 +39614,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "max_length": 256 + } + } } ], "operation": "CreateOutboundExternalLink", @@ -37531,7 +39712,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "max_length": 256 + } + } }, { "source": "vpcId", @@ -37641,11 +39827,17 @@ }, { "source": "ClusterIdentifier", - "target": "ClusterIdentifier" + "target": "ClusterIdentifier", + "unrepresentable": { + "max_length": 63 + } }, { "source": "ClusterParameterGroupName", - "target": "ClusterParameterGroupName" + "target": "ClusterParameterGroupName", + "unrepresentable": { + "max_length": 255 + } }, { "source": "ClusterSecurityGroups", @@ -37717,7 +39909,10 @@ }, { "source": "MasterUsername", - "target": "MasterUsername" + "target": "MasterUsername", + "unrepresentable": { + "max_length": 128 + } }, { "source": "MultiAZ", @@ -37757,7 +39952,10 @@ "mappings": [ { "source": "ClusterIdentifier", - "target": "ClusterIdentifier" + "target": "ClusterIdentifier", + "unrepresentable": { + "max_length": 63 + } } ], "operation": "DeleteCluster", @@ -37777,7 +39975,10 @@ }, { "source": "ParameterGroupName", - "target": "ParameterGroupName" + "target": "ParameterGroupName", + "unrepresentable": { + "max_length": 255 + } } ], "operation": "CreateClusterParameterGroup", @@ -37789,7 +39990,10 @@ "mappings": [ { "source": "ParameterGroupName", - "target": "ParameterGroupName" + "target": "ParameterGroupName", + "unrepresentable": { + "max_length": 255 + } } ], "operation": "DeleteClusterParameterGroup", @@ -38519,7 +40723,12 @@ }, { "source": "tier", - "target": "Tier" + "target": "Tier", + "unrepresentable": { + "rejected_values": [ + "NotApplicable" + ] + } } ], "operation": "CreateResiliencyPolicy", @@ -38761,11 +40970,17 @@ "mappings": [ { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "max_length": 512 + } }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 128 + } }, { "source": "Tags", @@ -38788,7 +41003,10 @@ "mappings": [ { "source": "Group", - "target": "Group" + "target": "Group", + "unrepresentable": { + "min_length": 12 + } }, { "source": "RoleArn", @@ -39016,7 +41234,10 @@ "mappings": [ { "source": "cidr", - "target": "Cidr" + "target": "Cidr", + "unrepresentable": { + "max_length": 42 + } }, { "source": "clientToken", @@ -39274,11 +41495,19 @@ }, { "source": "observabilityRegion", - "target": "ObservabilityRegion" + "target": "ObservabilityRegion", + "unrepresentable": { + "min_length": 1 + } }, { "source": "regions", - "target": "Regions" + "target": "Regions", + "unrepresentable": { + "items": { + "min_length": 1 + } + } }, { "source": "tags", @@ -39340,7 +41569,10 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "CreateProfile", @@ -40228,7 +42460,10 @@ "mappings": [ { "source": "Bucket", - "target": "BucketName" + "target": "BucketName", + "unrepresentable": { + "max_length": 63 + } }, { "source": "OutpostId", @@ -40251,19 +42486,10 @@ "mappings": [ { "source": "Bucket", - "target": "Bucket" - } - ], - "operation": "PutBucketPolicy", - "phase": "create", - "service": "s3control" - }, - { - "cfn_type": "AWS::S3Outposts::BucketPolicy", - "mappings": [ - { - "source": "Bucket", - "target": "Bucket" + "target": "Bucket", + "unrepresentable": { + "min_length": 20 + } } ], "operation": "DeleteBucketPolicy", @@ -40656,6 +42882,13 @@ "phase": "delete", "service": "supplychain" }, + { + "cfn_type": "AWS::SDB::Domain", + "mappings": [], + "operation": "DeleteDomain", + "phase": "delete", + "service": "sdb" + }, { "cfn_type": "AWS::SES::ConfigurationSetEventDestination", "mappings": [ @@ -41015,7 +43248,10 @@ "mappings": [ { "source": "OptOutListName", - "target": "OptOutListName" + "target": "OptOutListName", + "unrepresentable": { + "max_length": 64 + } } ], "operation": "DeleteOptOutList", @@ -41038,7 +43274,14 @@ }, { "source": "NumberCapabilities", - "target": "NumberCapabilities" + "target": "NumberCapabilities", + "unrepresentable": { + "items": { + "rejected_values": [ + "RCS" + ] + } + } }, { "source": "NumberType", @@ -41344,7 +43587,10 @@ }, { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "DisplayName", @@ -41379,7 +43625,13 @@ }, { "source": "DocumentType", - "target": "DocumentType" + "target": "DocumentType", + "unrepresentable": { + "rejected_values": [ + "ConformancePackTemplate", + "QuickSetup" + ] + } }, { "source": "Name", @@ -41622,7 +43874,12 @@ }, { "source": "Type", - "target": "Type" + "target": "Type", + "unrepresentable": { + "rejected_values": [ + "SecureString" + ] + } }, { "source": "Value", @@ -41771,7 +44028,10 @@ }, { "source": "DisplayName", - "target": "DisplayName" + "target": "DisplayName", + "unrepresentable": { + "min_length": 1 + } }, { "source": "Type", @@ -41863,7 +44123,12 @@ "mappings": [ { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateReplicationSet", @@ -41885,11 +44150,19 @@ "mappings": [ { "source": "displayName", - "target": "DisplayName" + "target": "DisplayName", + "unrepresentable": { + "min_length": 1 + } }, { "source": "engagements", - "target": "Engagements" + "target": "Engagements", + "unrepresentable": { + "items": { + "max_length": 1000 + } + } }, { "source": "name", @@ -41897,7 +44170,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateResponsePlan", @@ -42084,7 +44362,11 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "max_length": 32, + "min_length": 1 + } } ], "operation": "CreateInstance", @@ -42231,15 +44513,29 @@ "mappings": [ { "source": "AppName", - "target": "AppName" + "target": "AppName", + "unrepresentable": { + "min_length": 1 + } }, { "source": "AppType", - "target": "AppType" + "target": "AppType", + "unrepresentable": { + "rejected_values": [ + "CodeEditor", + "DetailedProfiler", + "JupyterLab", + "TensorBoard" + ] + } }, { "source": "DomainId", - "target": "DomainId" + "target": "DomainId", + "unrepresentable": { + "min_length": 1 + } }, { "source": "RecoveryMode", @@ -42247,7 +44543,10 @@ }, { "source": "UserProfileName", - "target": "UserProfileName" + "target": "UserProfileName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "CreateApp", @@ -42259,19 +44558,36 @@ "mappings": [ { "source": "AppName", - "target": "AppName" + "target": "AppName", + "unrepresentable": { + "min_length": 1 + } }, { "source": "AppType", - "target": "AppType" + "target": "AppType", + "unrepresentable": { + "rejected_values": [ + "CodeEditor", + "DetailedProfiler", + "JupyterLab", + "TensorBoard" + ] + } }, { "source": "DomainId", - "target": "DomainId" + "target": "DomainId", + "unrepresentable": { + "min_length": 1 + } }, { "source": "UserProfileName", - "target": "UserProfileName" + "target": "UserProfileName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "DeleteApp", @@ -42283,7 +44599,10 @@ "mappings": [ { "source": "AppImageConfigName", - "target": "AppImageConfigName" + "target": "AppImageConfigName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "CreateAppImageConfig", @@ -42295,7 +44614,10 @@ "mappings": [ { "source": "AppImageConfigName", - "target": "AppImageConfigName" + "target": "AppImageConfigName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "DeleteAppImageConfig", @@ -42354,7 +44676,11 @@ "mappings": [ { "source": "ClusterName", - "target": "ClusterName" + "target": "ClusterName", + "unrepresentable": { + "max_length": 63, + "min_length": 1 + } } ], "operation": "DeleteCluster", @@ -42533,11 +44859,17 @@ "mappings": [ { "source": "EndpointConfigName", - "target": "EndpointConfigName" + "target": "EndpointConfigName", + "unrepresentable": { + "min_length": 1 + } }, { "source": "EndpointName", - "target": "EndpointName" + "target": "EndpointName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "CreateEndpoint", @@ -42549,13 +44881,58 @@ "mappings": [ { "source": "EndpointName", - "target": "EndpointName" + "target": "EndpointName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "DeleteEndpoint", "phase": "delete", "service": "sagemaker" }, + { + "cfn_type": "AWS::SageMaker::EndpointConfig", + "mappings": [ + { + "source": "EnableNetworkIsolation", + "target": "EnableNetworkIsolation" + }, + { + "source": "EndpointConfigName", + "target": "EndpointConfigName", + "unrepresentable": { + "min_length": 1 + } + }, + { + "source": "ExecutionRoleArn", + "target": "ExecutionRoleArn" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + } + ], + "operation": "CreateEndpointConfig", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::EndpointConfig", + "mappings": [ + { + "source": "EndpointConfigName", + "target": "EndpointConfigName", + "unrepresentable": { + "min_length": 1 + } + } + ], + "operation": "DeleteEndpointConfig", + "phase": "delete", + "service": "sagemaker" + }, { "cfn_type": "AWS::SageMaker::Experiment", "mappings": [ @@ -42816,7 +45193,10 @@ "mappings": [ { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "min_length": 1 + } }, { "source": "EndpointName", @@ -42895,7 +45275,10 @@ "mappings": [ { "source": "ArtifactStoreUri", - "target": "ArtifactStoreUri" + "target": "ArtifactStoreUri", + "unrepresentable": { + "min_length": 1 + } }, { "source": "AutomaticModelRegistration", @@ -42903,7 +45286,10 @@ }, { "source": "MlflowVersion", - "target": "MlflowVersion" + "target": "MlflowVersion", + "unrepresentable": { + "min_length": 1 + } }, { "source": "RoleArn", @@ -43114,7 +45500,10 @@ "mappings": [ { "source": "ModelPackageName", - "target": "ModelPackageName" + "target": "ModelPackageName", + "unrepresentable": { + "max_length": 63 + } } ], "operation": "DeleteModelPackage", @@ -43142,7 +45531,10 @@ "mappings": [ { "source": "ModelPackageGroupName", - "target": "ModelPackageGroupName" + "target": "ModelPackageGroupName", + "unrepresentable": { + "max_length": 63 + } } ], "operation": "DeleteModelPackageGroup", @@ -43201,6 +45593,88 @@ "phase": "delete", "service": "sagemaker" }, + { + "cfn_type": "AWS::SageMaker::NotebookInstance", + "mappings": [ + { + "source": "AcceleratorTypes", + "target": "AcceleratorTypes" + }, + { + "source": "AdditionalCodeRepositories", + "target": "AdditionalCodeRepositories" + }, + { + "source": "DefaultCodeRepository", + "target": "DefaultCodeRepository" + }, + { + "source": "DirectInternetAccess", + "target": "DirectInternetAccess" + }, + { + "source": "InstanceType", + "target": "InstanceType" + }, + { + "source": "KmsKeyId", + "target": "KmsKeyId" + }, + { + "source": "LifecycleConfigName", + "target": "LifecycleConfigName" + }, + { + "source": "NotebookInstanceName", + "target": "NotebookInstanceName", + "unrepresentable": { + "min_length": 1 + } + }, + { + "source": "PlatformIdentifier", + "target": "PlatformIdentifier" + }, + { + "source": "RoleArn", + "target": "RoleArn" + }, + { + "source": "RootAccess", + "target": "RootAccess" + }, + { + "source": "SecurityGroupIds", + "target": "SecurityGroupIds" + }, + { + "source": "SubnetId", + "target": "SubnetId" + }, + { + "source": "VolumeSizeInGB", + "target": "VolumeSizeInGB" + } + ], + "operation": "CreateNotebookInstance", + "phase": "create", + "service": "sagemaker" + }, + { + "cfn_type": "AWS::SageMaker::NotebookInstance", + "mappings": [ + { + "source": "NotebookInstanceName", + "target": "NotebookInstanceName", + "unrepresentable": { + "min_length": 1 + } + } + ], + "operation": "DeleteNotebookInstance", + "phase": "delete", + "service": "sagemaker" + }, { "cfn_type": "AWS::SageMaker::PartnerApp", "ignored_inputs": [ @@ -43237,7 +45711,10 @@ }, { "source": "Tier", - "target": "Tier" + "target": "Tier", + "unrepresentable": { + "min_length": 1 + } }, { "source": "Type", @@ -43366,7 +45843,10 @@ "mappings": [ { "source": "DomainId", - "target": "DomainId" + "target": "DomainId", + "unrepresentable": { + "min_length": 1 + } }, { "source": "SpaceDisplayName", @@ -43374,7 +45854,10 @@ }, { "source": "SpaceName", - "target": "SpaceName" + "target": "SpaceName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "CreateSpace", @@ -43386,11 +45869,17 @@ "mappings": [ { "source": "DomainId", - "target": "DomainId" + "target": "DomainId", + "unrepresentable": { + "min_length": 1 + } }, { "source": "SpaceName", - "target": "SpaceName" + "target": "SpaceName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "DeleteSpace", @@ -43410,7 +45899,10 @@ }, { "source": "StudioLifecycleConfigName", - "target": "StudioLifecycleConfigName" + "target": "StudioLifecycleConfigName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "CreateStudioLifecycleConfig", @@ -43422,7 +45914,10 @@ "mappings": [ { "source": "StudioLifecycleConfigName", - "target": "StudioLifecycleConfigName" + "target": "StudioLifecycleConfigName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "DeleteStudioLifecycleConfig", @@ -43462,7 +45957,10 @@ "mappings": [ { "source": "DomainId", - "target": "DomainId" + "target": "DomainId", + "unrepresentable": { + "min_length": 1 + } }, { "source": "SingleSignOnUserIdentifier", @@ -43470,11 +45968,17 @@ }, { "source": "SingleSignOnUserValue", - "target": "SingleSignOnUserValue" + "target": "SingleSignOnUserValue", + "unrepresentable": { + "min_length": 1 + } }, { "source": "UserProfileName", - "target": "UserProfileName" + "target": "UserProfileName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "CreateUserProfile", @@ -43486,11 +45990,17 @@ "mappings": [ { "source": "DomainId", - "target": "DomainId" + "target": "DomainId", + "unrepresentable": { + "min_length": 1 + } }, { "source": "UserProfileName", - "target": "UserProfileName" + "target": "UserProfileName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "DeleteUserProfile", @@ -44139,7 +46649,10 @@ }, { "source": "subscriberName", - "target": "SubscriberName" + "target": "SubscriberName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "CreateSubscriber", @@ -44247,7 +46760,12 @@ }, { "source": "ProductType", - "target": "ProductType" + "target": "ProductType", + "unrepresentable": { + "rejected_values": [ + "MARKETPLACE" + ] + } }, { "source": "SupportDescription", @@ -44294,7 +46812,10 @@ }, { "source": "ProductName", - "target": "ProductName" + "target": "ProductName", + "unrepresentable": { + "max_length": 128 + } }, { "source": "ProvisionedProductName", @@ -44325,7 +46846,10 @@ }, { "source": "ProvisionedProductName", - "target": "ProvisionedProductName" + "target": "ProvisionedProductName", + "unrepresentable": { + "max_length": 128 + } } ], "operation": "TerminateProvisionedProduct", @@ -44715,7 +47239,10 @@ }, { "source": "attributeGroup", - "target": "AttributeGroup" + "target": "AttributeGroup", + "unrepresentable": { + "max_length": 256 + } } ], "operation": "AssociateAttributeGroup", @@ -44731,7 +47258,10 @@ }, { "source": "attributeGroup", - "target": "AttributeGroup" + "target": "AttributeGroup", + "unrepresentable": { + "max_length": 256 + } } ], "operation": "DisassociateAttributeGroup", @@ -44751,7 +47281,12 @@ }, { "source": "resourceType", - "target": "ResourceType" + "target": "ResourceType", + "unrepresentable": { + "rejected_values": [ + "RESOURCE_TAG_VALUE" + ] + } } ], "operation": "AssociateResource", @@ -44771,13 +47306,72 @@ }, { "source": "resourceType", - "target": "ResourceType" + "target": "ResourceType", + "unrepresentable": { + "rejected_values": [ + "RESOURCE_TAG_VALUE" + ] + } } ], "operation": "DisassociateResource", "phase": "delete", "service": "servicecatalog-appregistry" }, + { + "cfn_type": "AWS::ServiceDiscovery::HttpNamespace", + "ignored_inputs": [ + "CreatorRequestId" + ], + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateHttpNamespace", + "phase": "create", + "service": "servicediscovery" + }, + { + "cfn_type": "AWS::ServiceDiscovery::Instance", + "ignored_inputs": [ + "CreatorRequestId" + ], + "mappings": [ + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "ServiceId", + "target": "ServiceId" + } + ], + "operation": "RegisterInstance", + "phase": "create", + "service": "servicediscovery" + }, + { + "cfn_type": "AWS::ServiceDiscovery::Instance", + "mappings": [ + { + "source": "InstanceId", + "target": "InstanceId" + }, + { + "source": "ServiceId", + "target": "ServiceId" + } + ], + "operation": "DeregisterInstance", + "phase": "delete", + "service": "servicediscovery" + }, { "cfn_type": "AWS::ServiceDiscovery::PrivateDnsNamespace", "ignored_inputs": [ @@ -44970,7 +47564,15 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_key": { + "max_length": 127 + }, + "tag_value": { + "max_length": 255 + } + } } ], "operation": "PutSigningProfile", @@ -45190,6 +47792,40 @@ "phase": "delete", "service": "support-app" }, + { + "cfn_type": "AWS::SupportAuthZ::SupportPermit", + "ignored_inputs": [ + "clientToken" + ], + "mappings": [ + { + "source": "description", + "target": "Description" + }, + { + "source": "name", + "target": "Name" + }, + { + "source": "supportCaseDisplayId", + "target": "SupportCaseDisplayId" + }, + { + "source": "tags", + "target": "Tags" + } + ], + "operation": "CreateSupportPermit", + "phase": "create", + "service": "supportauthz" + }, + { + "cfn_type": "AWS::SupportAuthZ::SupportPermit", + "mappings": [], + "operation": "DeleteSupportPermit", + "phase": "delete", + "service": "supportauthz" + }, { "cfn_type": "AWS::Synthetics::Canary", "mappings": [ @@ -45265,6 +47901,54 @@ "phase": "delete", "service": "synthetics" }, + { + "cfn_type": "AWS::Textract::Adapter", + "ignored_inputs": [ + "ClientRequestToken" + ], + "mappings": [ + { + "source": "AdapterName", + "target": "AdapterName" + }, + { + "source": "AutoUpdate", + "target": "AutoUpdate" + }, + { + "source": "Description", + "target": "Description" + }, + { + "source": "FeatureTypes", + "target": "FeatureTypes", + "unrepresentable": { + "items": { + "rejected_values": [ + "FORMS", + "LAYOUT", + "SIGNATURES", + "TABLES" + ] + } + } + }, + { + "source": "Tags", + "target": "Tags" + } + ], + "operation": "CreateAdapter", + "phase": "create", + "service": "textract" + }, + { + "cfn_type": "AWS::Textract::Adapter", + "mappings": [], + "operation": "DeleteAdapter", + "phase": "delete", + "service": "textract" + }, { "cfn_type": "AWS::Timestream::Database", "mappings": [ @@ -45362,7 +48046,10 @@ }, { "source": "vpcSubnetIds", - "target": "VpcSubnetIds" + "target": "VpcSubnetIds", + "unrepresentable": { + "max_length": 3 + } } ], "operation": "CreateDbCluster", @@ -45434,7 +48121,10 @@ }, { "source": "vpcSubnetIds", - "target": "VpcSubnetIds" + "target": "VpcSubnetIds", + "unrepresentable": { + "max_length": 3 + } } ], "operation": "CreateDbInstance", @@ -45659,7 +48349,10 @@ }, { "source": "SecurityPolicyName", - "target": "SecurityPolicyName" + "target": "SecurityPolicyName", + "unrepresentable": { + "max_length": 50 + } }, { "source": "Url", @@ -45885,6 +48578,37 @@ "phase": "delete", "service": "transfer" }, + { + "cfn_type": "AWS::Translate::ParallelData", + "ignored_inputs": [ + "ClientToken" + ], + "mappings": [ + { + "source": "Description", + "target": "Description" + }, + { + "source": "Name", + "target": "Name" + } + ], + "operation": "CreateParallelData", + "phase": "create", + "service": "translate" + }, + { + "cfn_type": "AWS::Translate::ParallelData", + "mappings": [ + { + "source": "Name", + "target": "Name" + } + ], + "operation": "DeleteParallelData", + "phase": "delete", + "service": "translate" + }, { "cfn_type": "AWS::VerifiedPermissions::IdentitySource", "ignored_inputs": [ @@ -45978,7 +48702,10 @@ "mappings": [ { "source": "aliasName", - "target": "AliasName" + "target": "AliasName", + "unrepresentable": { + "min_length": 1 + } }, { "source": "policyStoreId", @@ -45994,7 +48721,10 @@ "mappings": [ { "source": "aliasName", - "target": "AliasName" + "target": "AliasName", + "unrepresentable": { + "min_length": 1 + } } ], "operation": "DeletePolicyStoreAlias", @@ -46086,7 +48816,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateAccessLogSubscription", @@ -46140,7 +48875,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "StartDomainVerification", @@ -46174,11 +48914,19 @@ }, { "source": "serviceIdentifier", - "target": "ServiceIdentifier" + "target": "ServiceIdentifier", + "unrepresentable": { + "min_length": 21 + } }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateListener", @@ -46190,7 +48938,10 @@ "mappings": [ { "source": "serviceIdentifier", - "target": "ServiceIdentifier" + "target": "ServiceIdentifier", + "unrepresentable": { + "min_length": 21 + } } ], "operation": "DeleteListener", @@ -46221,7 +48972,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateResourceConfiguration", @@ -46267,7 +49023,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } }, { "source": "vpcIdentifier", @@ -46329,15 +49090,26 @@ }, { "source": "priority", - "target": "Priority" + "target": "Priority", + "unrepresentable": { + "maximum": 100.0 + } }, { "source": "serviceIdentifier", - "target": "ServiceIdentifier" + "target": "ServiceIdentifier", + "unrepresentable": { + "min_length": 20 + } }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateRule", @@ -46353,7 +49125,10 @@ }, { "source": "serviceIdentifier", - "target": "ServiceIdentifier" + "target": "ServiceIdentifier", + "unrepresentable": { + "min_length": 20 + } } ], "operation": "DeleteRule", @@ -46388,7 +49163,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateService", @@ -46418,7 +49198,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateServiceNetwork", @@ -46444,7 +49229,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateServiceNetworkResourceAssociation", @@ -46466,15 +49256,26 @@ "mappings": [ { "source": "serviceIdentifier", - "target": "ServiceIdentifier" + "target": "ServiceIdentifier", + "unrepresentable": { + "min_length": 20 + } }, { "source": "serviceNetworkIdentifier", - "target": "ServiceNetworkIdentifier" + "target": "ServiceNetworkIdentifier", + "unrepresentable": { + "min_length": 20 + } }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } } ], "operation": "CreateServiceNetworkServiceAssociation", @@ -46504,11 +49305,19 @@ }, { "source": "serviceNetworkIdentifier", - "target": "ServiceNetworkIdentifier" + "target": "ServiceNetworkIdentifier", + "unrepresentable": { + "min_length": 20 + } }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } }, { "source": "vpcIdentifier", @@ -46538,7 +49347,12 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_value": { + "min_length": 1 + } + } }, { "source": "type", @@ -47279,7 +50093,13 @@ "mappings": [ { "source": "channelSubtype", - "target": "ChannelSubtype" + "target": "ChannelSubtype", + "unrepresentable": { + "rejected_values": [ + "PUSH", + "WHATSAPP" + ] + } }, { "source": "description", @@ -47472,7 +50292,10 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "min_length": 1 + } }, { "source": "softwareSetUpdateMode", @@ -47629,7 +50452,10 @@ }, { "source": "subnetIds", - "target": "SubnetIds" + "target": "SubnetIds", + "unrepresentable": { + "max_length": 3 + } }, { "source": "vpcId", @@ -47978,11 +50804,12 @@ ], "format_version": 1, "source": { + "aws_cli_version": "2.36.19", "botocore_service_count": 431, "botocore_version": "2.0.0dev155", - "compiled_schemas_sha256": "9f33ce28894f17ac771051a55696e18e04596cfdf01014ec13edcafd75524299", - "compiled_type_count": 1766, - "provider_schemas_sha256": "929767f603b6744fda6741ba5ca5ec42b54847a4f9a07402369ebecd8ce82248", - "provider_type_count": 1729 + "compiled_schemas_sha256": "9eeedf63ea8537503ad5b34b119ab7bb35ec75c8f731e64e5a4376158ee639d7", + "compiled_type_count": 1767, + "provider_schemas_sha256": "ca88d997e27ec3a357a817c0466dd2ac1ba604bbf8985667432af312c2274d9a", + "provider_type_count": 1765 } } diff --git a/src/data-source/generated/data/source_versions.json b/src/data-source/generated/data/source_versions.json index 0161dfa0..6e1c3891 100644 --- a/src/data-source/generated/data/source_versions.json +++ b/src/data-source/generated/data/source_versions.json @@ -1,4 +1,5 @@ { "cfn_lint_version": "https://github.com/aws-cloudformation/cfn-lint@1.56.0", - "resource_schema_version": "https://github.com/aws-cloudformation/resource-provider-enhanced-schemas@2026-09-11T14:10:42Z" + "resource_schema_version": "https://github.com/aws-cloudformation/resource-provider-enhanced-schemas@2026-09-11T16:10:08Z", + "aws_cli_version": "https://github.com/aws/aws-cli@2.36.19" } From 0ef2a3842a16ac9e5e80a1ae80249ea3880a60ac Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Fri, 11 Sep 2026 18:48:29 +0000 Subject: [PATCH 14/19] update generation --- src/data-source/Cargo.toml | 5 - src/data-source/README.md | 34 +- .../examples/generate_aws_cli_catalog.rs | 78 ----- .../scripts/generate_aws_cli_catalog.py | 247 +++++++++----- src/data-source/src/aws_cli_catalog.rs | 254 ++++++++++++-- src/data-source/src/lib.rs | 9 +- src/data-source/src/sync.rs | 49 ++- src/validation-engine/API.md | 14 +- src/validation-engine/src/aws_cli.rs | 318 +++++++++++++++--- 9 files changed, 737 insertions(+), 271 deletions(-) delete mode 100644 src/data-source/examples/generate_aws_cli_catalog.rs diff --git a/src/data-source/Cargo.toml b/src/data-source/Cargo.toml index 7087d1c0..00964928 100644 --- a/src/data-source/Cargo.toml +++ b/src/data-source/Cargo.toml @@ -24,11 +24,6 @@ name = "generate" path = "src/generate.rs" required-features = ["maintenance"] -[[example]] -name = "generate_aws_cli_catalog" -path = "examples/generate_aws_cli_catalog.rs" -required-features = ["maintenance"] - [[example]] name = "embedding_format_bench" path = "src/embedding_format_bench.rs" diff --git a/src/data-source/README.md b/src/data-source/README.md index c58597fd..07f91e7a 100644 --- a/src/data-source/README.md +++ b/src/data-source/README.md @@ -11,12 +11,9 @@ compile time. Everything compiles into the binary - no runtime fetching. # Generate schema and rule artifacts from existing upstream data cargo run -p cloudformation-validate-data-source --features maintenance --example generate -# Refresh all upstream sources, then generate every output (cfn-lint root is required) -cargo run -p cloudformation-validate-data-source --features maintenance --example sync -- --cfn-lint-root - -# Regenerate the AWS CLI operation catalog (standalone; independent of sync/generate). -# --aws-cli-root points at a local aws-cli checkout. -cargo run -p cloudformation-validate-data-source --features maintenance --example generate_aws_cli_catalog -- --aws-cli-root +# Refresh all upstream sources, then generate every output (cfn-lint root is required; +# --aws-cli-root, a local aws-cli checkout, also regenerates the AWS CLI operation catalog) +cargo run -p cloudformation-validate-data-source --features maintenance --example sync -- --cfn-lint-root [--aws-cli-root ] ``` The examples require the `maintenance` feature, which enables dependencies used only by the data maintenance @@ -29,23 +26,24 @@ its source-qualified versions only after all source processing succeeds. ### AWS CLI operation catalog -`generate_aws_cli_catalog` is a separate command that (re)builds -`generated/data/aws_cli_operation_catalog.json`, the adapter catalog that maps AWS CLI operations to CloudFormation -resource types for `validateAwsCliCommand`. It is intentionally decoupled from `sync`/`generate` because it derives -from AWS CLI botocore service models plus CloudFormation provider handler metadata and is regenerated on its own -cadence. It only generates the catalog and never downloads or processes schemas: it reads the provider schemas -already present under `upstream/schemas` (written by `sync`) and the committed compiled schemas under -`generated/schema-validator`, and fails with a clear message if either is missing. Because `sync` clears -`generated/data/`, the catalog must be regenerated after every sync; the command also records the AWS CLI release it -used as `aws_cli_version` in `generated/data/source_versions.json`. +`generated/data/aws_cli_operation_catalog.json` is the adapter catalog that maps AWS CLI operations to CloudFormation +resource types for `validateAwsCliCommand`. It derives from AWS CLI botocore service models plus CloudFormation +provider handler metadata, so `sync` produces it as its final step, after the provider schemas and compiled schemas +exist. With `--aws-cli-root ` (a local aws-cli checkout whose `awscli/` supplies botocore), `sync` runs +`scripts/generate_aws_cli_catalog.py` and records the AWS CLI release it used as `aws_cli_version` in +`generated/data/source_versions.json`. Without it, `sync` keeps the committed catalog and re-verifies its mappings +against the refreshed compiled schemas, failing with a request for `--aws-cli-root` if any mapped property no longer +exists or became read-only. A repository that has never generated a catalog must pass `--aws-cli-root`, because the +build script embeds the catalog. Besides the operation-to-type and parameter-to-property pairs, each mapping records the API value domain that the compiled CloudFormation schema cannot represent (`unrepresentable`: enum members, numeric bounds, string lengths, list -sizes, tag key/value lengths), derived by comparing the botocore input shape against the compiled property schema. The +sizes, tag key/value lengths, and the API/CloudFormation regex `pattern` pair when the two differ), derived by comparing +the botocore input shape against the compiled property schema. The runtime uses it to skip synthesis for a command whose values the service accepts but CloudFormation would reject. Same-named inputs whose meaning differs from the CloudFormation property are excluded by the reviewed -`PROPERTY_SEMANTIC_DENYLIST` in the script. Re-run the command after `sync`, after changing the generator's mapping -rules, or after updating the AWS CLI checkout. +`PROPERTY_SEMANTIC_DENYLIST` in the script. Pass `--aws-cli-root` after changing the generator's mapping rules or after +updating the AWS CLI checkout. ## Directory Structure diff --git a/src/data-source/examples/generate_aws_cli_catalog.rs b/src/data-source/examples/generate_aws_cli_catalog.rs deleted file mode 100644 index 0d7d9df1..00000000 --- a/src/data-source/examples/generate_aws_cli_catalog.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! Standalone command that (re)generates the AWS CLI operation catalog. -//! -//! This is deliberately separate from the `sync` and `generate` maintenance -//! examples: it only generates the catalog and never downloads or processes -//! schemas. It assumes the resource data is already present - the provider -//! schemas under `upstream/schemas` (written by `sync`) and the committed -//! compiled schemas under `generated/schema-validator`. -//! -//! Usage: -//! cargo run -p cloudformation-validate-data-source --features maintenance \ -//! --example generate_aws_cli_catalog -- --aws-cli-root -//! -//! `--aws-cli-root` points at a local `aws-cli` checkout (its `awscli/` supplies -//! botocore). - -use data_source::generate_aws_cli_catalog; -use log::error; -use std::env; -use std::path::PathBuf; -use std::process; - -fn main() -> anyhow::Result<()> { - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); - - let args: Vec = env::args().collect(); - let mut aws_cli_root: Option = None; - let mut i = 1; - while i < args.len() { - match args[i].as_str() { - "--aws-cli-root" => { - i += 1; - if i >= args.len() { - error!("--aws-cli-root requires a path argument"); - process::exit(1); - } - aws_cli_root = Some(args[i].clone()); - } - "--help" | "-h" => { - print_usage(); - return Ok(()); - } - other => { - error!("unknown argument: {other}"); - print_usage(); - process::exit(1); - } - } - i += 1; - } - - let Some(aws_cli_root) = aws_cli_root else { - print_usage(); - anyhow::bail!("--aws-cli-root is required"); - }; - - let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let upstream_dir = manifest.join("upstream"); - let generated_dir = manifest.join("generated"); - - generate_aws_cli_catalog(&upstream_dir, &generated_dir, &PathBuf::from(aws_cli_root))?; - Ok(()) -} - -fn print_usage() { - eprintln!( - "Usage: cargo run -p cloudformation-validate-data-source --features maintenance \\ - --example generate_aws_cli_catalog -- --aws-cli-root - -Generates the AWS CLI operation catalog (generated/data/aws_cli_operation_catalog.json) -from AWS CLI botocore service models and CloudFormation provider handler metadata. -It only generates the catalog: the resource data must already be present -(upstream/schemas from a prior sync, plus the committed compiled schemas). - -Options: - --aws-cli-root Path to a local aws-cli checkout (required) - -h, --help Show this help" - ); -} diff --git a/src/data-source/scripts/generate_aws_cli_catalog.py b/src/data-source/scripts/generate_aws_cli_catalog.py index f7ba2593..d0bfdc3c 100644 --- a/src/data-source/scripts/generate_aws_cli_catalog.py +++ b/src/data-source/scripts/generate_aws_cli_catalog.py @@ -39,9 +39,11 @@ (``PROPERTY_SEMANTIC_DENYLIST``) never maps - value-domain verification: an API input value that the service accepts but the CloudFormation schema rejects (enum members, numeric bounds, string - lengths, list sizes, tag key/value lengths) is recorded per mapping as an - ``unrepresentable`` domain so the runtime skips synthesis for such a value - instead of reporting a CloudFormation finding against a valid API call + lengths, list sizes, tag key/value lengths, and values inside a botocore + ``pattern`` that the CloudFormation ``pattern`` rejects) is recorded per + mapping as an ``unrepresentable`` domain so the runtime skips synthesis for + such a value instead of reporting a CloudFormation finding against a valid + API call - noun agreement or property-overlap thresholds; ties are dropped entirely - global reverse uniqueness: one (service, operation) key maps to exactly one catalog entry; unresolvable collisions are dropped entirely @@ -696,6 +698,59 @@ def _cfn_pattern_rejects(pattern, value): return False +def _anchor_pattern(pattern): + """``pattern`` wrapped so it must match a whole value. + + Services validate a botocore ``pattern`` against the entire input, so this + is the form in which an API pattern describes the values the service + accepts. Wrapping in a non-capturing group keeps a top-level alternation + (``^$|arn:.+``) anchored as a whole. + """ + return f'^(?:{pattern})$' + + +def _strip_anchors(pattern): + """``pattern`` without a leading ``^`` or trailing unescaped ``$``. + + Two patterns that differ only in these anchors describe the same whole + values, because both the service and the schema validator apply them to + the complete string. + """ + if pattern.startswith('^'): + pattern = pattern[1:] + if pattern.endswith('$') and not pattern.endswith('\\$'): + pattern = pattern[:-1] + return pattern + + +def _pattern_divergence(source_shape, node): + """The API/CloudFormation ``pattern`` pair when the two regexes differ. + + Regex inclusion is undecidable in general, so a textual difference (beyond + the ``^``/``$`` anchors, which the schema validator and the service both + apply to whole values) is recorded as a candidate divergence and settled + per value at runtime: a value the API pattern accepts and the CloudFormation + pattern rejects skips synthesis. The API pattern is recorded anchored to the + whole value, as the service applies it; the CloudFormation pattern is + recorded as written, as the schema validator applies it. Returns ``None`` + when either side has no pattern, when the two are the same pattern, or when + the API pattern is not a regex Python can compile, so a mapping is never + restricted on evidence the generator cannot read. + """ + api_pattern = (getattr(source_shape, 'metadata', None) or {}).get('pattern') + cfn_pattern = node.get('pattern') + if not api_pattern or not cfn_pattern: + return None + if _strip_anchors(api_pattern) == _strip_anchors(cfn_pattern): + return None + anchored_api_pattern = _anchor_pattern(api_pattern) + try: + re.compile(anchored_api_pattern) + except re.error: + return None + return {'api': anchored_api_pattern, 'cloudformation': cfn_pattern} + + def _string_unrepresentable(source_shape, target_schema, definitions): node = ( _schema_node_for_type(target_schema, definitions, 'string') @@ -723,6 +778,9 @@ def _string_unrepresentable(source_shape, target_schema, definitions): domain['max_length'] = cfn_max if api_min is not None and cfn_min is not None and api_min < cfn_min: domain['min_length'] = cfn_min + pattern_divergence = _pattern_divergence(source_shape, node) + if pattern_divergence: + domain['pattern'] = pattern_divergence return domain @@ -767,8 +825,10 @@ def _unrepresentable_domain(source_shape, target_schema, definitions, target): Keys name the CloudFormation constraint the API range exceeds: ``rejected_values`` (API enum members CloudFormation rejects), ``minimum``/``maximum`` (numeric bounds), ``min_length``/``max_length`` - (string length or list size), ``items`` (nested domain of list elements), - ``tag_key``/``tag_value`` (nested string domains for tag maps). + (string length or list size), ``pattern`` (the anchored API regex and the + CloudFormation regex when they differ; settled per value at runtime), + ``items`` (nested domain of list elements), ``tag_key``/``tag_value`` + (nested string domains for tag maps). """ source_type = source_shape.type_name if source_type == 'string': @@ -1079,6 +1139,15 @@ def _verify_curated_updates(compiled_schemas, index): return verified_adapters +def _count_pattern_pairs(domain): + """Number of API/CloudFormation ``pattern`` pairs in ``domain`` and its nested domains.""" + count = 1 if domain.get('pattern') else 0 + for nested in ('items', 'tag_key', 'tag_value'): + if domain.get(nested): + count += _count_pattern_pairs(domain[nested]) + return count + + def _compute_coverage(unique_adapters, index, compiled_schemas): """Compute catalog and state-validation coverage metrics. @@ -1120,7 +1189,18 @@ def _compute_coverage(unique_adapters, index, compiled_schemas): for adapter in unique_adapters: phases[adapter['phase']] += 1 + guarded_mappings = 0 + pattern_pairs = 0 + for adapter in unique_adapters: + for mapping in adapter.get('mappings', []): + domain = mapping.get('unrepresentable') + if domain: + guarded_mappings += 1 + pattern_pairs += _count_pattern_pairs(domain) + return { + 'guarded_mappings': guarded_mappings, + 'pattern_pairs': pattern_pairs, 'catalog_services': { 'covered': len({a['service'] for a in unique_adapters}), 'total': botocore_services, @@ -1153,21 +1233,24 @@ def _compute_coverage(unique_adapters, index, compiled_schemas): } -def _render_derivation(role, counters): - """Explain how provider resource types were matched to API operations.""" +def _render_matching(role, counters): + """Explain, in plain terms, how resource types were matched to one API operation.""" rejection_reasons = ( - ('type_not_compiled', 'Missing from compiled CloudFormation schemas'), - ('no_handler', f'No {role} handler declared in the provider schema'), - ('excluded_service', 'Service excluded from catalog generation'), + ('type_not_compiled', 'not present in the compiled CloudFormation schemas'), + ('no_handler', f'the provider schema declares no {role} handler'), + ( + 'excluded_service', + 'CloudFormation and Cloud Control types are validated directly, not through adapters', + ), ( 'no_candidates', - 'Handler permissions contained no usable botocore API operation', + f'the {role} handler permissions name no operation in this AWS CLI release', ), ( 'rejected', - 'Best candidate failed resource-name/property matching safety checks', + 'the best candidate operation failed the resource-name / property-overlap checks', ), - ('tied_rejected', 'Multiple API operations tied for best candidate'), + ('tied_rejected', 'several operations tied for best candidate'), ) known_outcomes = { 'verified', @@ -1177,36 +1260,29 @@ def _render_derivation(role, counters): unknown_outcomes = set(counters) - known_outcomes if unknown_outcomes: names = ', '.join(sorted(unknown_outcomes)) - raise ValueError(f'no reader-facing description for derivation outcomes: {names}') + raise ValueError(f'no reader-facing description for matching outcomes: {names}') - selected = counters.get('verified', 0) - not_selected = sum( - counters.get(outcome, 0) for outcome, _ in rejection_reasons - ) + matched = counters.get('verified', 0) + unmatched = sum(counters.get(outcome, 0) for outcome, _ in rejection_reasons) stale_model_rejected = counters.get('stale_model_rejected', 0) - rejected = counters.get('rejected', 0) - if stale_model_rejected > rejected: - raise ValueError( - 'stale-model rejection count exceeds total candidate rejections' - ) + if stale_model_rejected > counters.get('rejected', 0): + raise ValueError('stale-model rejection count exceeds total candidate rejections') - title = role.capitalize() lines = [ - f'{title} API operation matching:', - f' Resource types evaluated from provider schemas: {selected + not_selected:,}', - f' Resource types with one API operation selected: {selected:,}', - f' Resource types without an operation selection: {not_selected:,}', + f'{role.capitalize()} operations: {matched:,} of {matched + unmatched:,} resource types ' + f'matched to exactly one {role} API operation', ] + if unmatched: + lines.append(f' The other {unmatched:,} resource types were not matched because:') for outcome, description in rejection_reasons: count = counters.get(outcome, 0) if count == 0: continue - lines.append(f' {description}: {count:,}') + lines.append(f' {count:>5,} {description}') if outcome == 'rejected' and stale_model_rejected: lines.append( - f' Of those, the exact {role} operation from handler ' - 'permissions was absent from the loaded botocore models: ' - f'{stale_model_rejected:,}' + f' {"":>5} ({stale_model_rejected:,} of these because the exact {role} ' + 'operation named by the handler is missing from this AWS CLI release)' ) return lines @@ -1215,51 +1291,62 @@ def _render_fraction(description, entry): covered = entry['covered'] total = entry['total'] percent = (covered / total * 100) if total > 0 else 0.0 - return f' {description}: {covered:,} of {total:,} ({percent:.1f}%)' + return f' {covered:>6,} of {total:>6,} ({percent:5.1f}%) {description}' -def _render_coverage(coverage): - """Render coverage metrics with explicit populations and denominators.""" +def _render_coverage(coverage, dropped_count): + """Describe what the final catalog covers, with every denominator named.""" + lifecycle = coverage.get('lifecycle_adapters', {}) + adapter_count = sum(lifecycle.values()) + by_phase = ', '.join( + f'{lifecycle.get(phase, 0):,} {phase}' + for phase in ('create', 'update', 'delete') + if lifecycle.get(phase, 0) + ) + for phase in sorted(set(lifecycle) - {'create', 'update', 'delete'}): + by_phase += f', {lifecycle[phase]:,} {phase}' lines = [ - 'Catalog coverage (all final create, update, and delete adapters):', - _render_fraction( - 'botocore services represented', coverage['catalog_services'] - ), - _render_fraction( - 'Compiled CloudFormation resource types represented', - coverage['catalog_resources'], + f'Catalog contents: {adapter_count:,} adapters ({by_phase})', + ( + f' {dropped_count:,} candidate adapters were dropped so that each API operation ' + 'maps to exactly one resource type' ), - _render_fraction( - 'botocore API operations represented', coverage['catalog_commands'] + ( + f" {coverage['guarded_mappings']:,} property mappings record values the API accepts but " + f"CloudFormation rejects ({coverage['pattern_pairs']:,} as regex pattern pairs settled per value); " + 'such a value skips validation instead of producing a finding' ), '', + 'What an AWS CLI call can be checked against', ( - 'State validation coverage (create/update adapters with at least ' - 'one writable-property mapping):' + ' A create or update call whose adapter maps at least one writable property is modeled as ' + 'CloudFormation resource state and validated; every other call is classified and skipped.' ), _render_fraction( - 'botocore services with state validation', coverage['state_services'] + 'compiled CloudFormation resource types whose create/update call is validated', + coverage['state_resources'], ), _render_fraction( - 'Compiled CloudFormation resource types with state validation', - coverage['state_resources'], + 'writable properties of those types that a call can populate', + coverage['writable_properties'], ), _render_fraction( - 'botocore API operations used for state validation', - coverage['state_commands'], + 'AWS CLI services with at least one validated create/update operation', + coverage['state_services'], ), _render_fraction( - 'Writable CloudFormation properties mapped for state validation', - coverage['writable_properties'], + 'AWS CLI operations that are validated (most operations are reads or data-plane calls)', + coverage['state_commands'], ), '', - 'Final adapters by lifecycle phase:', + 'What the catalog classifies (create, update, or delete of a known resource type)', + _render_fraction( + 'compiled CloudFormation resource types with at least one adapter', + coverage['catalog_resources'], + ), + _render_fraction('AWS CLI services with at least one adapter', coverage['catalog_services']), + _render_fraction('AWS CLI operations with an adapter', coverage['catalog_commands']), ] - lifecycle = coverage.get('lifecycle_adapters', {}) - for phase in ('create', 'update', 'delete'): - lines.append(f' {phase.capitalize()} adapters: {lifecycle.get(phase, 0):,}') - for phase in sorted(set(lifecycle) - {'create', 'update', 'delete'}): - lines.append(f' {phase.capitalize()} adapters: {lifecycle[phase]:,}') return lines @@ -1268,37 +1355,33 @@ def _render_generation_report( delete_counters, dropped_count, coverage, - adapter_count, + source, output_path, ): """Render the complete catalog generation report.""" lines = [ - 'AWS API catalog generation summary', + 'AWS CLI operation catalog', + f' Written to: {output_path}', ( - 'An adapter links one CloudFormation resource type and lifecycle ' - 'action to one botocore API operation.' + f" Derived from: AWS CLI {source['aws_cli_version']} " + f"(botocore {source['botocore_version']}, {source['botocore_service_count']:,} services), " + f"{source['provider_type_count']:,} provider schemas with handler metadata, " + f"{source['compiled_type_count']:,} compiled CloudFormation resource types" ), - '', - ] - lines.extend(_render_derivation('create', create_counters)) - lines.append('') - lines.extend(_render_derivation('delete', delete_counters)) - lines.extend([ - '', - 'API operation uniqueness check:', ( - ' Adapters removed so each botocore API operation appears only ' - f'once: {dropped_count:,}' + ' An adapter links one CloudFormation resource type and lifecycle phase ' + '(create, update, delete) to the one AWS CLI operation that performs it.' ), '', - ]) - lines.extend(_render_coverage(coverage)) - lines.extend([ - '', - 'Catalog output:', - f' Adapters written: {adapter_count:,}', - f' File: {output_path}', - ]) + 'How resource types were matched to API operations', + ] + lines.extend(_render_matching('create', create_counters)) + lines.extend(_render_matching('delete', delete_counters)) + lines.append( + ' Update operations: hand-reviewed adapters only, because update APIs carry partial state' + ) + lines.append('') + lines.extend(_render_coverage(coverage, dropped_count)) return lines @@ -1380,7 +1463,7 @@ def main(): delete_counters, len(dropped), coverage, - len(unique_adapters), + document['source'], args.output, ): print(line) diff --git a/src/data-source/src/aws_cli_catalog.rs b/src/data-source/src/aws_cli_catalog.rs index 1d83c464..60e5b47f 100644 --- a/src/data-source/src/aws_cli_catalog.rs +++ b/src/data-source/src/aws_cli_catalog.rs @@ -1,28 +1,32 @@ -//! Standalone AWS CLI operation catalog generation. +//! AWS CLI operation catalog generation for the `sync` maintenance workflow. //! -//! This is intentionally decoupled from the [`crate::sync_upstream`] / -//! [`crate::generate_all`] maintenance pipeline: the catalog derives from AWS -//! CLI botocore service models plus CloudFormation provider handler metadata, -//! and it runs on its own cadence. It only generates the catalog - it never -//! downloads or processes schemas. The resource data it reads must already be -//! present: the provider schemas under `upstream/schemas` (written by `sync`) -//! and the committed compiled schemas under `generated/schema-validator`. -//! The `generate_aws_cli_catalog` example is the command-line entry point; this -//! module owns the logic it calls. +//! The catalog derives from AWS CLI botocore service models plus CloudFormation +//! provider handler metadata, and it is structurally verified against the +//! compiled schemas. `sync` clears `generated/data/` before refreshing every +//! upstream source, so the catalog is either regenerated as the final `sync` +//! step (when an AWS CLI checkout is supplied) or carried across the sync +//! unchanged and re-verified against the freshly compiled schemas (when it is +//! not). Either way `generated/data/` ends the sync with a catalog the build +//! script can embed and a manifest that records the AWS CLI release it derives +//! from. use crate::source_versions::{AWS_CLI_SOURCE, SOURCE_VERSIONS_FILE, SourceVersions}; use crate::write_source_versions; -use log::info; +use log::{info, warn}; +use std::collections::{BTreeSet, HashMap}; use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; /// Format version the generator emits and the runtime loader accepts. const AWS_CLI_OPERATION_CATALOG_FORMAT_VERSION: u64 = 1; +/// Catalog file name under `generated/data/`. +pub const AWS_CLI_OPERATION_CATALOG_FILE: &str = "aws_cli_operation_catalog.json"; + #[derive(Debug, serde::Deserialize)] struct AwsCliOperationCatalog { format_version: u64, - adapters: Vec, + adapters: Vec, source: AwsCliOperationCatalogSource, } @@ -32,19 +36,106 @@ struct AwsCliOperationCatalogSource { aws_cli_version: String, } +/// The part of a catalog adapter that must agree with the compiled schemas. +#[derive(Debug, serde::Deserialize)] +struct CatalogAdapter { + service: String, + operation: String, + cfn_type: String, + #[serde(default)] + mappings: Vec, +} + +#[derive(Debug, serde::Deserialize)] +struct CatalogMapping { + target: String, +} + +/// The part of a compiled resource schema the catalog's mappings depend on. +#[derive(Debug, serde::Deserialize)] +struct CompiledPropertySurface { + #[serde(default)] + properties: HashMap, + #[serde(default)] + read_only_properties: BTreeSet, +} + +/// A committed catalog carried across a sync that does not regenerate it. +#[derive(Debug)] +pub struct PreservedAwsCliCatalog { + catalog: Vec, + aws_cli_version: String, +} + +fn catalog_path(generated_dir: &Path) -> PathBuf { + generated_dir.join("data").join(AWS_CLI_OPERATION_CATALOG_FILE) +} + +fn compiled_schemas_path(generated_dir: &Path) -> PathBuf { + generated_dir.join("schema-validator").join("compiled_schemas.json") +} + +/// Read the committed catalog and its recorded AWS CLI release before `sync` +/// clears `generated/data/`, so a sync run without an AWS CLI checkout can +/// restore it afterwards. Returns `None` when no catalog has been generated yet. +pub fn preserve_aws_cli_catalog(generated_dir: &Path) -> anyhow::Result> { + let path = catalog_path(generated_dir); + if !path.is_file() { + return Ok(None); + } + let catalog_bytes = fs::read(&path) + .map_err(|error| anyhow::anyhow!("failed to read committed catalog {}: {error}", path.display()))?; + let catalog = validate_catalog(&catalog_bytes) + .map_err(|error| anyhow::anyhow!("committed catalog {} cannot be preserved: {error}", path.display()))?; + Ok(Some(PreservedAwsCliCatalog { + catalog: catalog_bytes, + aws_cli_version: format!("{AWS_CLI_SOURCE}@{}", catalog.source.aws_cli_version), + })) +} + +/// Write a preserved catalog back into `generated_dir/data` after a sync, +/// verify it against the freshly compiled schemas, and record its AWS CLI +/// release in the sync-written manifest. +/// +/// A preserved catalog was derived from the compiled schemas of an earlier +/// sync. Every property it maps must still exist and still be writable in the +/// new compiled schemas; otherwise the runtime would report an engine error for +/// the affected command, so the restore fails and asks for regeneration instead. +pub fn restore_aws_cli_catalog(generated_dir: &Path, preserved: PreservedAwsCliCatalog) -> anyhow::Result<()> { + let path = catalog_path(generated_dir); + let stale = stale_mappings(&preserved.catalog, &compiled_schemas_path(generated_dir))?; + anyhow::ensure!( + stale.is_empty(), + "the preserved AWS CLI operation catalog no longer agrees with the refreshed compiled schemas \ + ({} mapping(s), e.g. {}); rerun sync with --aws-cli-root to regenerate it", + stale.len(), + stale.iter().take(3).cloned().collect::>().join(", ") + ); + fs::create_dir_all(generated_dir.join("data"))?; + fs::write(&path, &preserved.catalog) + .map_err(|error| anyhow::anyhow!("failed to restore catalog {}: {error}", path.display()))?; + record_aws_cli_version(generated_dir, preserved.aws_cli_version.clone())?; + warn!( + "Restored the previous AWS CLI operation catalog ({}) without regenerating it; pass --aws-cli-root \ + to derive it from the refreshed schemas", + preserved.aws_cli_version + ); + Ok(()) +} + /// Generate the AWS CLI operation catalog into `generated_dir/data`. /// /// `aws_cli_root` is a local AWS CLI (`aws-cli`) checkout; its `awscli/` /// directory supplies the bundled botocore service models. Provider schemas are /// read from `upstream_dir/schemas` and compiled schemas from /// `generated_dir/schema-validator/compiled_schemas.json`; both must already -/// exist - this command does not download or process schemas. +/// exist, so this runs as the final `sync` step after `generate_all`. pub fn generate_aws_cli_catalog(upstream_dir: &Path, generated_dir: &Path, aws_cli_root: &Path) -> anyhow::Result<()> { let botocore_root = aws_cli_root.join("awscli"); let botocore_package = botocore_root.join("botocore").join("__init__.py"); let provider_schemas = upstream_dir.join("schemas"); - let compiled_schemas = generated_dir.join("schema-validator").join("compiled_schemas.json"); - let catalog_path = generated_dir.join("data").join("aws_cli_operation_catalog.json"); + let compiled_schemas = compiled_schemas_path(generated_dir); + let catalog_path = catalog_path(generated_dir); let script_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("scripts").join("generate_aws_cli_catalog.py"); anyhow::ensure!(script_path.is_file(), "AWS CLI catalog generator not found at {}", script_path.display()); @@ -56,7 +147,7 @@ pub fn generate_aws_cli_catalog(upstream_dir: &Path, generated_dir: &Path, aws_c ); anyhow::ensure!( provider_schemas.is_dir(), - "provider schemas not found at {}; run the sync example first to populate the resource data", + "provider schemas not found at {}; sync must download them before generating the catalog", provider_schemas.display() ); anyhow::ensure!(compiled_schemas.is_file(), "compiled schemas not found at {}", compiled_schemas.display()); @@ -79,10 +170,19 @@ pub fn generate_aws_cli_catalog(upstream_dir: &Path, generated_dir: &Path, aws_c let catalog_bytes = fs::read(&catalog_path) .map_err(|error| anyhow::anyhow!("failed to read generated catalog {}: {error}", catalog_path.display()))?; let catalog = validate_catalog(&catalog_bytes)?; + let stale = stale_mappings(&catalog_bytes, &compiled_schemas)?; + anyhow::ensure!( + stale.is_empty(), + "generated AWS CLI operation catalog maps properties absent from the compiled schemas: {}", + stale.join(", ") + ); info!("Generated AWS CLI operation catalog with {} adapters at {}", catalog.adapters.len(), catalog_path.display()); + record_aws_cli_version(generated_dir, format!("{AWS_CLI_SOURCE}@{}", catalog.source.aws_cli_version)) +} + +fn record_aws_cli_version(generated_dir: &Path, aws_cli_version: String) -> anyhow::Result<()> { let source_versions_path = generated_dir.join("data").join(SOURCE_VERSIONS_FILE); - let aws_cli_version = format!("{AWS_CLI_SOURCE}@{}", catalog.source.aws_cli_version); let source_versions = SourceVersions::read(&source_versions_path) .and_then(|versions| versions.with_aws_cli_version(aws_cli_version.clone())) .map_err(anyhow::Error::msg)?; @@ -93,36 +193,92 @@ pub fn generate_aws_cli_catalog(upstream_dir: &Path, generated_dir: &Path, aws_c fn validate_catalog(catalog_bytes: &[u8]) -> anyhow::Result { let catalog: AwsCliOperationCatalog = serde_json::from_slice(catalog_bytes) - .map_err(|error| anyhow::anyhow!("generated AWS CLI operation catalog is invalid JSON: {error}"))?; + .map_err(|error| anyhow::anyhow!("AWS CLI operation catalog is invalid JSON: {error}"))?; anyhow::ensure!( catalog.format_version == AWS_CLI_OPERATION_CATALOG_FORMAT_VERSION, - "generated AWS CLI operation catalog has format version {}, expected {}", + "AWS CLI operation catalog has format version {}, expected {}", catalog.format_version, AWS_CLI_OPERATION_CATALOG_FORMAT_VERSION ); - anyhow::ensure!(!catalog.adapters.is_empty(), "generated AWS CLI operation catalog contains no adapters"); + anyhow::ensure!(!catalog.adapters.is_empty(), "AWS CLI operation catalog contains no adapters"); anyhow::ensure!( !catalog.source.aws_cli_version.trim().is_empty(), - "generated AWS CLI operation catalog does not record the AWS CLI version" + "AWS CLI operation catalog does not record the AWS CLI version" ); Ok(catalog) } +/// Mappings whose CloudFormation type or target property is missing from, or +/// read-only in, the compiled schemas at `compiled_schemas_path`, rendered as +/// `service:Operation -> Type.Property`. +fn stale_mappings(catalog_bytes: &[u8], compiled_schemas_path: &Path) -> anyhow::Result> { + let catalog = validate_catalog(catalog_bytes)?; + let compiled: HashMap = + serde_json::from_slice(&fs::read(compiled_schemas_path).map_err(|error| { + anyhow::anyhow!("failed to read compiled schemas {}: {error}", compiled_schemas_path.display()) + })?) + .map_err(|error| { + anyhow::anyhow!("failed to parse compiled schemas {}: {error}", compiled_schemas_path.display()) + })?; + Ok(stale_mappings_against(&catalog, &compiled)) +} + +fn stale_mappings_against( + catalog: &AwsCliOperationCatalog, + compiled: &HashMap, +) -> Vec { + let mut stale = Vec::new(); + for adapter in &catalog.adapters { + let Some(schema) = compiled.get(&adapter.cfn_type) else { + stale.push(format!("{}:{} -> {}", adapter.service, adapter.operation, adapter.cfn_type)); + continue; + }; + for mapping in &adapter.mappings { + if !schema.properties.contains_key(&mapping.target) || schema.read_only_properties.contains(&mapping.target) + { + stale.push(format!( + "{}:{} -> {}.{}", + adapter.service, adapter.operation, adapter.cfn_type, mapping.target + )); + } + } + } + stale +} + #[cfg(test)] mod tests { use super::*; + const VALID_CATALOG: &[u8] = br#"{ + "format_version": 1, + "adapters": [{ + "service": "s3", "operation": "CreateBucket", "cfn_type": "AWS::S3::Bucket", "phase": "create", + "mappings": [{"source": "Bucket", "target": "BucketName"}] + }], + "source": {"aws_cli_version": "2.36.43"} + }"#; + + fn compiled(properties: &[&str], read_only: &[&str]) -> HashMap { + HashMap::from([( + "AWS::S3::Bucket".to_string(), + CompiledPropertySurface { + properties: properties.iter().map(|name| (name.to_string(), serde_json::json!({}))).collect(), + read_only_properties: read_only.iter().map(|name| name.to_string()).collect(), + }, + )]) + } + #[test] fn current_catalog_format_with_adapters_is_valid() { - let catalog = br#"{"format_version":1,"adapters":[{}],"source":{"aws_cli_version":"2.36.43"}}"#; - let catalog = validate_catalog(catalog).expect("catalog should be valid"); + let catalog = validate_catalog(VALID_CATALOG).expect("catalog should be valid"); assert_eq!(1, catalog.adapters.len()); assert_eq!("2.36.43", catalog.source.aws_cli_version); } #[test] fn unsupported_catalog_format_is_rejected() { - let catalog = br#"{"format_version":2,"adapters":[{}],"source":{"aws_cli_version":"2.36.43"}}"#; + let catalog = br#"{"format_version":2,"adapters":[{"service":"s3","operation":"CreateBucket","cfn_type":"AWS::S3::Bucket"}],"source":{"aws_cli_version":"2.36.43"}}"#; let error = validate_catalog(catalog).expect_err("unsupported format must fail"); assert!(error.to_string().contains("format version 2, expected 1")); } @@ -136,10 +292,54 @@ mod tests { #[test] fn catalog_without_aws_cli_version_is_rejected() { - let catalog = br#"{"format_version":1,"adapters":[{}],"source":{"aws_cli_version":" "}}"#; + let catalog = br#"{"format_version":1,"adapters":[{"service":"s3","operation":"CreateBucket","cfn_type":"AWS::S3::Bucket"}],"source":{"aws_cli_version":" "}}"#; let error = validate_catalog(catalog).expect_err("blank version must fail"); assert!(error.to_string().contains("does not record the AWS CLI version")); - let catalog = br#"{"format_version":1,"adapters":[{}],"source":{}}"#; + let catalog = br#"{"format_version":1,"adapters":[{"service":"s3","operation":"CreateBucket","cfn_type":"AWS::S3::Bucket"}],"source":{}}"#; assert!(validate_catalog(catalog).is_err(), "missing version must fail"); } + + #[test] + fn catalog_agreeing_with_compiled_schemas_has_no_stale_mappings() { + let catalog = validate_catalog(VALID_CATALOG).expect("catalog should be valid"); + assert!(stale_mappings_against(&catalog, &compiled(&["BucketName"], &[])).is_empty()); + } + + #[test] + fn mapping_onto_removed_or_read_only_property_is_stale() { + let catalog = validate_catalog(VALID_CATALOG).expect("catalog should be valid"); + assert_eq!( + stale_mappings_against(&catalog, &compiled(&["Arn"], &[])), + vec!["s3:CreateBucket -> AWS::S3::Bucket.BucketName"] + ); + assert_eq!( + stale_mappings_against(&catalog, &compiled(&["BucketName"], &["BucketName"])), + vec!["s3:CreateBucket -> AWS::S3::Bucket.BucketName"] + ); + } + + #[test] + fn mapping_onto_removed_type_is_stale() { + let catalog = validate_catalog(VALID_CATALOG).expect("catalog should be valid"); + assert_eq!(stale_mappings_against(&catalog, &HashMap::new()), vec!["s3:CreateBucket -> AWS::S3::Bucket"]); + } + + #[test] + fn preserving_a_missing_catalog_yields_none() { + let dir = std::env::temp_dir().join(format!("aws-cli-catalog-preserve-{}", std::process::id())); + fs::create_dir_all(dir.join("data")).expect("temp dir is writable"); + assert!(preserve_aws_cli_catalog(&dir).expect("missing catalog is not an error").is_none()); + fs::remove_dir_all(&dir).expect("temp dir is removable"); + } + + #[test] + fn preserved_catalog_carries_bytes_and_source_qualified_version() { + let dir = std::env::temp_dir().join(format!("aws-cli-catalog-preserve-some-{}", std::process::id())); + fs::create_dir_all(dir.join("data")).expect("temp dir is writable"); + fs::write(dir.join("data").join(AWS_CLI_OPERATION_CATALOG_FILE), VALID_CATALOG).expect("catalog is writable"); + let preserved = preserve_aws_cli_catalog(&dir).expect("valid catalog preserves").expect("catalog exists"); + assert_eq!(preserved.catalog, VALID_CATALOG); + assert_eq!(preserved.aws_cli_version, format!("{AWS_CLI_SOURCE}@2.36.43")); + fs::remove_dir_all(&dir).expect("temp dir is removable"); + } } diff --git a/src/data-source/src/lib.rs b/src/data-source/src/lib.rs index 1f3ac09d..837b4ea6 100644 --- a/src/data-source/src/lib.rs +++ b/src/data-source/src/lib.rs @@ -32,7 +32,10 @@ pub mod types; pub use additional_schema_source::{AdditionalSchemaSource, SchemaSourceError}; #[cfg(feature = "maintenance")] -pub use aws_cli_catalog::generate_aws_cli_catalog; +pub use aws_cli_catalog::{ + AWS_CLI_OPERATION_CATALOG_FILE, PreservedAwsCliCatalog, generate_aws_cli_catalog, preserve_aws_cli_catalog, + restore_aws_cli_catalog, +}; #[cfg(feature = "maintenance")] use log::{error, info}; @@ -152,8 +155,8 @@ pub fn sync_upstream(upstream_dir: &Path, rule_source_root: &str) -> anyhow::Res verify_files_exist_and_populated(REQUIRED_SYNC_FILES, &generated_data, "Sync")?; verify_files_exist_and_populated(REQUIRED_UPSTREAM_FILES, upstream_dir, "Sync")?; // `generated/data` was cleared ahead of this sync, so the manifest is written - // fresh; the AWS CLI entry is recorded by the catalog generator, which must - // run after every sync because the catalog is cleared with it. + // fresh; the AWS CLI entry is recorded by the final `sync` step, which either + // regenerates the catalog or restores the preserved one. let source_versions = source_versions::SourceVersions::new(cfn_lint_version, resource_schema_version, None) .map_err(anyhow::Error::msg)?; write_source_versions(&source_versions_path, source_versions)?; diff --git a/src/data-source/src/sync.rs b/src/data-source/src/sync.rs index 445ba117..92a9614f 100644 --- a/src/data-source/src/sync.rs +++ b/src/data-source/src/sync.rs @@ -1,5 +1,8 @@ use anyhow::Context; -use data_source::{generate_all, sync_upstream}; +use data_source::{ + AWS_CLI_OPERATION_CATALOG_FILE, PreservedAwsCliCatalog, generate_all, generate_aws_cli_catalog, + preserve_aws_cli_catalog, restore_aws_cli_catalog, sync_upstream, +}; use log::{error, info}; use std::env; use std::fs; @@ -12,6 +15,7 @@ fn main() -> anyhow::Result<()> { let args: Vec = env::args().collect(); let mut rule_source_root: Option = None; + let mut aws_cli_root: Option = None; let mut i = 1; while i < args.len() { match args[i].as_str() { @@ -23,6 +27,14 @@ fn main() -> anyhow::Result<()> { } rule_source_root = Some(args[i].clone()); } + "--aws-cli-root" => { + i += 1; + if i >= args.len() { + error!("--aws-cli-root requires a path argument"); + process::exit(1); + } + aws_cli_root = Some(PathBuf::from(&args[i])); + } "--help" | "-h" => { print_usage(); return Ok(()); @@ -37,11 +49,28 @@ fn main() -> anyhow::Result<()> { } let rule_source_root = rule_source_root.ok_or_else(|| anyhow::anyhow!("--cfn-lint-root is required"))?; + if let Some(aws_cli_root) = &aws_cli_root { + anyhow::ensure!(aws_cli_root.is_dir(), "AWS CLI checkout not found at {}", aws_cli_root.display()); + } let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let upstream_dir = manifest.join("upstream"); let generated_dir = manifest.join("generated"); let handwritten_dir = manifest.join("handwritten"); + // The AWS CLI operation catalog lives in `generated/data`, which is cleared + // below. Without an AWS CLI checkout to regenerate it from, the committed + // catalog is carried across the sync; the build script cannot compile the + // data-source crate without one, so a first sync must supply the checkout. + let catalog_step = match aws_cli_root { + Some(aws_cli_root) => CatalogStep::Generate(aws_cli_root), + None => CatalogStep::Restore(preserve_aws_cli_catalog(&generated_dir)?.ok_or_else(|| { + anyhow::anyhow!( + "no AWS CLI operation catalog exists at generated/data/{AWS_CLI_OPERATION_CATALOG_FILE}; \ + pass --aws-cli-root so sync can generate it" + ) + })?), + }; + // Every file under these directories is rewritten by a full sync, so clear // them first: a source that stops being produced must not linger as a stale // artifact. `generated/data` is shared by the sync and generate phases and can @@ -52,11 +81,23 @@ fn main() -> anyhow::Result<()> { sync_upstream(&upstream_dir, &rule_source_root)?; generate_all(&upstream_dir, &generated_dir, &handwritten_dir)?; + match catalog_step { + CatalogStep::Generate(aws_cli_root) => generate_aws_cli_catalog(&upstream_dir, &generated_dir, &aws_cli_root)?, + CatalogStep::Restore(preserved) => restore_aws_cli_catalog(&generated_dir, preserved)?, + } info!("Sync and generation complete"); Ok(()) } +/// How the sync ends up with an AWS CLI operation catalog in `generated/data`. +enum CatalogStep { + /// Derive a fresh catalog from the botocore models bundled in this AWS CLI checkout. + Generate(PathBuf), + /// Carry the committed catalog across the sync and re-verify it against the refreshed schemas. + Restore(PreservedAwsCliCatalog), +} + fn clear_cache_directory(cache_directory: &Path) -> anyhow::Result<()> { fs::remove_dir_all(cache_directory) .or_else(|error| if error.kind() == ErrorKind::NotFound { Ok(()) } else { Err(error) }) @@ -65,12 +106,16 @@ fn clear_cache_directory(cache_directory: &Path) -> anyhow::Result<()> { fn print_usage() { eprintln!( - "Usage: cargo run -p cloudformation-validate-data-source --features maintenance --example sync -- --cfn-lint-root + "Usage: cargo run -p cloudformation-validate-data-source --features maintenance --example sync -- --cfn-lint-root [--aws-cli-root ] Refreshes all upstream sources, records their versions, and generates every output. +With --aws-cli-root, the AWS CLI operation catalog is regenerated from that checkout's +bundled botocore models as the final step; without it, the committed catalog is kept +and re-verified against the refreshed schemas. Options: --cfn-lint-root Path to cfn-lint repo (required) + --aws-cli-root Path to a local aws-cli checkout (optional) -h, --help Show this help" ); } diff --git a/src/validation-engine/API.md b/src/validation-engine/API.md index 4979384c..1c89b042 100644 --- a/src/validation-engine/API.md +++ b/src/validation-engine/API.md @@ -103,12 +103,14 @@ represent the full caller-supplied state: no parameter is ever silently omitted **API-valid values are never reported as CloudFormation violations.** A same-named API input and CloudFormation property can differ in value domain: the service may accept an enum member, numeric bound, string length, list size, -or tag key/value length that the CloudFormation schema rejects. The generator records that API-only domain on each -mapping as `unrepresentable`, and the runtime skips synthesis — again naming the parameter and the value — whenever a -supplied value falls inside it, so a command the service would accept is never modeled as a template that -CloudFormation would reject. Same-named inputs whose meaning differs from the property (for example an API resource ID -where the CloudFormation property carries the resource ARN) are removed from the catalog by a reviewed denylist and -therefore skip synthesis as unmapped parameters. +or tag key/value length that the CloudFormation schema rejects, or its `pattern` may differ from the CloudFormation +`pattern`. The generator records that API-only domain on each mapping as `unrepresentable` (a differing regex is +recorded as the API/CloudFormation pattern pair and settled per value: a value the API pattern accepts and the +CloudFormation pattern rejects is unrepresentable), and the runtime skips synthesis — again naming the parameter and +the value — whenever a supplied value falls inside it, so a command the service would accept is never modeled as a +template that CloudFormation would reject. Same-named inputs whose meaning differs from the property (for example an +API resource ID where the CloudFormation property carries the resource ARN) are removed from the catalog by a reviewed +denylist and therefore skip synthesis as unmapped parameters. **Template-authoring advice is not reported for modeled state.** Synthesized templates and wrapped Cloud Control desired state never had a template author, so rules whose only remediation is a template construct — replace a literal diff --git a/src/validation-engine/src/aws_cli.rs b/src/validation-engine/src/aws_cli.rs index 2a926730..ea738b5c 100644 --- a/src/validation-engine/src/aws_cli.rs +++ b/src/validation-engine/src/aws_cli.rs @@ -4,6 +4,7 @@ use schema_validator::{PropertyValueType, ResourceSchemaMetadata, SchemaValidato use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::sync::LazyLock; +use template_model::compile_pattern; use crate::{ValidateConfig, ValidationEngine, ValidationError, validate_bytes_with_path}; @@ -294,40 +295,104 @@ struct UnrepresentableDomain { maximum: Option, min_length: Option, max_length: Option, + pattern: Option, items: Option>, tag_key: Option>, tag_value: Option>, } +/// An API `pattern` and the CloudFormation `pattern` for the same value that +/// differ textually. Whether the two admit different values cannot be decided +/// from the regexes alone, so the divergence is settled per request value: a +/// value the API pattern accepts and the CloudFormation pattern rejects has no +/// CloudFormation representation. +#[derive(Debug, Clone, PartialEq, Deserialize)] +struct PatternDivergence { + /// The botocore pattern anchored to the whole value, as the service applies it. + api: String, + /// The compiled-schema pattern, applied as the schema validator applies it. + cloudformation: String, +} + +impl PatternDivergence { + /// Whether `value` is inside the API pattern but outside the CloudFormation + /// pattern. Errs when either pattern is not a regex this tool can evaluate, + /// because a divergence that cannot be settled must not be silently ignored. + fn rejects(&self, value: &str) -> Result { + let api = compile_pattern(&self.api).ok_or_else(|| format!("API pattern '{}' cannot be compiled", self.api))?; + let cloudformation = compile_pattern(&self.cloudformation) + .ok_or_else(|| format!("CloudFormation pattern '{}' cannot be compiled", self.cloudformation))?; + Ok(api.is_match(value) && !cloudformation.is_match(value)) + } +} + impl UnrepresentableDomain { /// Describes why `value` has no CloudFormation representation, or `None` - /// when every part of it is representable. - fn rejection(&self, value: &AwsCliValue) -> Option { + /// when every part of it is representable. Errs only when the domain itself + /// cannot be evaluated, which is a defect in the generated catalog. + fn rejection(&self, value: &AwsCliValue) -> Result, String> { if let Some(json_value) = value.json_value() && self.rejected_values.contains(&json_value) { - return Some(format!("value {json_value} is valid for the API but not for CloudFormation")); + return Ok(Some(format!("value {json_value} is valid for the API but not for CloudFormation"))); } match value { - AwsCliValue::String { value } => self.length_rejection(value.chars().count() as u64, "length"), - AwsCliValue::Integer { value } => self.numeric_rejection(*value as f64), - AwsCliValue::UnsignedInteger { value } => self.numeric_rejection(*value as f64), - AwsCliValue::Number { value } => self.numeric_rejection(*value), - AwsCliValue::Array { items } => self.length_rejection(items.len() as u64, "item count").or_else(|| { - self.items.as_ref().and_then(|domain| items.iter().find_map(|item| domain.rejection(item))) - }), - AwsCliValue::Object { entries } => entries.iter().find_map(|(key, entry)| { - let key_rejection = self - .tag_key - .as_ref() - .and_then(|domain| domain.rejection(&AwsCliValue::String { value: key.clone() })); - key_rejection.or_else(|| self.tag_value.as_ref().and_then(|domain| domain.rejection(entry))) - }), + AwsCliValue::String { value } => { + if let Some(rejection) = self.length_rejection(value.chars().count() as u64, "length") { + return Ok(Some(rejection)); + } + self.pattern_rejection(value) + } + AwsCliValue::Integer { value } => Ok(self.numeric_rejection(*value as f64)), + AwsCliValue::UnsignedInteger { value } => Ok(self.numeric_rejection(*value as f64)), + AwsCliValue::Number { value } => Ok(self.numeric_rejection(*value)), + AwsCliValue::Array { items } => { + if let Some(rejection) = self.length_rejection(items.len() as u64, "item count") { + return Ok(Some(rejection)); + } + let Some(domain) = self.items.as_ref() else { + return Ok(None); + }; + for item in items { + if let Some(rejection) = domain.rejection(item)? { + return Ok(Some(rejection)); + } + } + Ok(None) + } + AwsCliValue::Object { entries } => { + for (key, entry) in entries { + if let Some(domain) = self.tag_key.as_ref() + && let Some(rejection) = domain.rejection(&AwsCliValue::String { value: key.clone() })? + { + return Ok(Some(rejection)); + } + if let Some(domain) = self.tag_value.as_ref() + && let Some(rejection) = domain.rejection(entry)? + { + return Ok(Some(rejection)); + } + } + Ok(None) + } AwsCliValue::Null | AwsCliValue::Boolean { .. } | AwsCliValue::Bytes { .. } - | AwsCliValue::Unsupported { .. } => None, + | AwsCliValue::Unsupported { .. } => Ok(None), + } + } + + fn pattern_rejection(&self, value: &str) -> Result, String> { + let Some(divergence) = self.pattern.as_ref() else { + return Ok(None); + }; + if divergence.rejects(value)? { + return Ok(Some(format!( + "value {value:?} is valid for the API but does not match the CloudFormation pattern '{}'", + divergence.cloudformation + ))); } + Ok(None) } fn numeric_rejection(&self, value: f64) -> Option { @@ -388,8 +453,9 @@ struct OperationCatalog { /// derived from the resource type's own provider handler metadata, resolved /// against botocore service models, and structurally verified against the /// compiled CloudFormation schemas. Each mapping also records the API value -/// domain the schema cannot represent, so a value that is valid for the service -/// but not for CloudFormation skips synthesis instead of producing a finding. +/// domain the schema cannot represent (enum members, bounds, lengths, and a +/// diverging regex pattern pair), so a value that is valid for the service but +/// not for CloudFormation skips synthesis instead of producing a finding. /// Only exact service+operation keys resolve; unregistered operations stay /// unmapped. static ADAPTER_REGISTRY: LazyLock, String>> = @@ -994,13 +1060,19 @@ fn map_adapter_properties( continue; }; mapped_sources.insert(&mapping.source); - if let Some(domain) = mapping.unrepresentable.as_ref() - && let Some(rejection) = domain.rejection(value) - { - return Ok(AdapterMappingResult::Skip(format!( - "parameter '{}' {rejection}, so it has no representation as property '{}' on {}", - mapping.source, mapping.target, adapter.cfn_type - ))); + if let Some(domain) = mapping.unrepresentable.as_ref() { + let rejection = domain.rejection(value).map_err(|error| { + ValidationError::Engine(format!( + "adapter {}:{} mapping '{}' -> '{}' has an unevaluable unrepresentable domain: {error}", + adapter.service, adapter.operation, mapping.source, mapping.target + )) + })?; + if let Some(rejection) = rejection { + return Ok(AdapterMappingResult::Skip(format!( + "parameter '{}' {rejection}, so it has no representation as property '{}' on {}", + mapping.source, mapping.target, adapter.cfn_type + ))); + } } match mapped_value(value, accepted_types, &mapping.target) { Some(json_value) => { @@ -1023,8 +1095,9 @@ fn map_adapter_properties( } // All-or-nothing: every supplied parameter must either be mapped or - // in the ignored set. - for param_name in parameters.keys() { + // in the ignored set. Parameters are visited in name order so the skip + // reason names the same parameter on every run. + for param_name in parameters.keys().collect::>() { if mapped_sources.contains(param_name.as_str()) { continue; } @@ -1291,7 +1364,9 @@ mod tests { {"source": "Bucket", "target": "BucketName", "unrepresentable": {"rejected_values": ["legacy"], "min_length": 3, "max_length": 63}}, {"source": "Tags", "target": "Tags", - "unrepresentable": {"tag_value": {"min_length": 1}, "items": {"maximum": 10.0}}} + "unrepresentable": {"tag_value": {"min_length": 1}, "items": {"maximum": 10.0}}}, + {"source": "Region", "target": "Region", + "unrepresentable": {"pattern": {"api": "^[a-z]{2,4}$", "cloudformation": "^[a-z]{2}$"}}} ] }] }"#; @@ -1309,6 +1384,132 @@ mod tests { let tags = adapter.mappings[1].unrepresentable.as_ref().expect("tags domain parses"); assert_eq!(tags.tag_value.as_deref().and_then(|domain| domain.min_length), Some(1)); assert_eq!(tags.items.as_deref().and_then(|domain| domain.maximum), Some(10.0)); + assert_eq!( + adapter.mappings[2].unrepresentable.as_ref().and_then(|domain| domain.pattern.clone()), + Some(PatternDivergence { api: "^[a-z]{2,4}$".into(), cloudformation: "^[a-z]{2}$".into() }) + ); + } + + #[test] + fn pattern_divergence_rejects_only_values_the_api_accepts_and_cloudformation_rejects() { + let sqs_arn = UnrepresentableDomain { + pattern: Some(PatternDivergence { + api: r"^arn:aws[a-z-]*:sqs:[a-z]{2,4}(-[a-z]+)+-\d:\d{12}:.+$".into(), + cloudformation: r"^arn:aws[a-z-]*:sqs:[a-z]{2}(-[a-z]+)+-\d:\d{12}:.+$".into(), + }), + ..UnrepresentableDomain::default() + }; + let sovereign = value(serde_json::json!("arn:aws-eusc:sqs:eusc-de-east-1:123456789012:queue")); + let rejection = sqs_arn.rejection(&sovereign).expect("domain evaluates").expect("value is unrepresentable"); + assert!(rejection.contains("valid for the API"), "{rejection}"); + assert!(rejection.contains("CloudFormation pattern"), "{rejection}"); + assert!( + sqs_arn + .rejection(&value(serde_json::json!("arn:aws:sqs:us-east-1:123456789012:queue"))) + .expect("domain evaluates") + .is_none(), + "a value both patterns accept is representable" + ); + assert!( + sqs_arn.rejection(&value(serde_json::json!("not-an-arn"))).expect("domain evaluates").is_none(), + "a value both patterns reject keeps its CloudFormation finding" + ); + assert!( + sqs_arn + .rejection(&value(serde_json::json!(" arn:aws:sqs:eusc-de-east-1:123456789012:q"))) + .expect("domain evaluates") + .is_none(), + "the API pattern applies to the whole value" + ); + + let nested = UnrepresentableDomain { + items: Some(Box::new(sqs_arn.clone())), + tag_value: Some(Box::new(sqs_arn)), + ..UnrepresentableDomain::default() + }; + assert!( + nested + .rejection(&value(serde_json::json!([sovereign.json_value().unwrap()]))) + .expect("domain evaluates") + .is_some() + ); + assert!( + nested + .rejection(&value(serde_json::json!({"Queue": sovereign.json_value().unwrap()}))) + .expect("domain evaluates") + .is_some() + ); + } + + #[test] + fn uncompilable_pattern_divergence_is_a_catalog_error_not_a_silent_skip() { + let broken = UnrepresentableDomain { + pattern: Some(PatternDivergence { api: "^(unclosed$".into(), cloudformation: "^[a-z]+$".into() }), + ..UnrepresentableDomain::default() + }; + let error = broken.rejection(&value(serde_json::json!("abc"))).expect_err("broken pattern must error"); + assert!(error.contains("cannot be compiled"), "{error}"); + + let adapter = s3_create_bucket_adapter(vec![PropertyMapping { + source: "Bucket".into(), + target: "BucketName".into(), + unrepresentable: Some(broken), + }]); + let schema_validator = SchemaValidator::default(); + let schema = schema_validator.resource_schema_metadata("AWS::S3::Bucket").expect("S3 bucket schema must exist"); + let parameters = HashMap::from([("Bucket".to_string(), value(serde_json::json!("abc")))]); + match map_adapter_properties(¶meters, &schema, &adapter).expect_err("unevaluable domain must error") { + ValidationError::Engine(message) => { + assert!(message.contains("unevaluable unrepresentable domain"), "{message}") + } + error => panic!("expected engine error, got {error:?}"), + } + } + + #[test] + fn adapter_skips_when_supplied_value_diverges_from_the_cloudformation_pattern() { + let adapter = s3_create_bucket_adapter(vec![PropertyMapping { + source: "Bucket".into(), + target: "BucketName".into(), + unrepresentable: Some(UnrepresentableDomain { + pattern: Some(PatternDivergence { api: "^[a-z0-9.-]+$".into(), cloudformation: "^[a-z0-9-]+$".into() }), + ..UnrepresentableDomain::default() + }), + }]); + let reason = skip_reason(map_s3_create_bucket(&adapter, serde_json::json!({"Bucket": "my.bucket"}))); + assert!(reason.contains("parameter 'Bucket'"), "{reason}"); + assert!(reason.contains("does not match the CloudFormation pattern"), "{reason}"); + match map_s3_create_bucket(&adapter, serde_json::json!({"Bucket": "my-bucket"})) { + AdapterMappingResult::Mapped(properties) => { + assert_eq!(properties.get("BucketName"), Some(&serde_json::json!("my-bucket"))); + } + AdapterMappingResult::Skip(reason) => panic!("value inside both patterns must map: {reason}"), + } + } + + fn collect_patterns<'a>(domain: &'a UnrepresentableDomain, patterns: &mut Vec<&'a str>) { + if let Some(divergence) = domain.pattern.as_ref() { + patterns.push(divergence.api.as_str()); + patterns.push(divergence.cloudformation.as_str()); + } + for nested in [&domain.items, &domain.tag_key, &domain.tag_value].into_iter().flatten() { + collect_patterns(nested, patterns); + } + } + + #[test] + fn every_pattern_in_the_embedded_catalog_compiles() { + let mut patterns = Vec::new(); + for adapter in adapter_registry().expect("embedded catalog parses").values() { + for mapping in &adapter.mappings { + if let Some(domain) = mapping.unrepresentable.as_ref() { + collect_patterns(domain, &mut patterns); + } + } + } + let uncompilable: Vec<&str> = + patterns.iter().copied().filter(|pattern| compile_pattern(pattern).is_none()).collect(); + assert!(uncompilable.is_empty(), "catalog patterns must compile: {uncompilable:?}"); } #[test] @@ -1317,24 +1518,29 @@ mod tests { rejected_values: vec![serde_json::json!("MANAGED_INSTANCES")], ..UnrepresentableDomain::default() }; - assert!(enumeration.rejection(&value(serde_json::json!("MANAGED_INSTANCES"))).is_some()); - assert!(enumeration.rejection(&value(serde_json::json!("FARGATE"))).is_none()); + assert!( + enumeration.rejection(&value(serde_json::json!("MANAGED_INSTANCES"))).expect("domain evaluates").is_some() + ); + assert!(enumeration.rejection(&value(serde_json::json!("FARGATE"))).expect("domain evaluates").is_none()); let numeric = UnrepresentableDomain { minimum: Some(1.0), maximum: Some(10.0), ..UnrepresentableDomain::default() }; - assert!(numeric.rejection(&value(serde_json::json!(0))).is_some()); - assert!(numeric.rejection(&value(serde_json::json!(50))).is_some()); - assert!(numeric.rejection(&value(serde_json::json!(10))).is_none()); - assert!(numeric.rejection(&value(serde_json::json!(1))).is_none()); - assert!(numeric.rejection(&AwsCliValue::UnsignedInteger { value: 11 }).is_some()); - assert!(numeric.rejection(&AwsCliValue::Number { value: 0.5 }).is_some()); + assert!(numeric.rejection(&value(serde_json::json!(0))).expect("domain evaluates").is_some()); + assert!(numeric.rejection(&value(serde_json::json!(50))).expect("domain evaluates").is_some()); + assert!(numeric.rejection(&value(serde_json::json!(10))).expect("domain evaluates").is_none()); + assert!(numeric.rejection(&value(serde_json::json!(1))).expect("domain evaluates").is_none()); + assert!(numeric.rejection(&AwsCliValue::UnsignedInteger { value: 11 }).expect("domain evaluates").is_some()); + assert!(numeric.rejection(&AwsCliValue::Number { value: 0.5 }).expect("domain evaluates").is_some()); let text_length = UnrepresentableDomain { min_length: Some(5), max_length: Some(6), ..UnrepresentableDomain::default() }; - assert!(text_length.rejection(&value(serde_json::json!("abc"))).is_some()); - assert!(text_length.rejection(&value(serde_json::json!("abcdefg"))).is_some()); - assert!(text_length.rejection(&value(serde_json::json!("abcde"))).is_none()); - assert!(text_length.rejection(&value(serde_json::json!("ééééé"))).is_none(), "length counts characters"); + assert!(text_length.rejection(&value(serde_json::json!("abc"))).expect("domain evaluates").is_some()); + assert!(text_length.rejection(&value(serde_json::json!("abcdefg"))).expect("domain evaluates").is_some()); + assert!(text_length.rejection(&value(serde_json::json!("abcde"))).expect("domain evaluates").is_none()); + assert!( + text_length.rejection(&value(serde_json::json!("ééééé"))).expect("domain evaluates").is_none(), + "length counts characters" + ); let list = UnrepresentableDomain { max_length: Some(2), @@ -1344,9 +1550,15 @@ mod tests { })), ..UnrepresentableDomain::default() }; - assert!(list.rejection(&value(serde_json::json!(["a", "b", "c"]))).is_some(), "item count above maximum"); - assert!(list.rejection(&value(serde_json::json!(["SELECT", "SUPER_USER"]))).is_some(), "rejected item"); - assert!(list.rejection(&value(serde_json::json!(["SELECT", "ALTER"]))).is_none()); + assert!( + list.rejection(&value(serde_json::json!(["a", "b", "c"]))).expect("domain evaluates").is_some(), + "item count above maximum" + ); + assert!( + list.rejection(&value(serde_json::json!(["SELECT", "SUPER_USER"]))).expect("domain evaluates").is_some(), + "rejected item" + ); + assert!(list.rejection(&value(serde_json::json!(["SELECT", "ALTER"]))).expect("domain evaluates").is_none()); let tags = UnrepresentableDomain { tag_key: Some(Box::new(UnrepresentableDomain { max_length: Some(3), ..UnrepresentableDomain::default() })), @@ -1356,13 +1568,19 @@ mod tests { })), ..UnrepresentableDomain::default() }; - assert!(tags.rejection(&value(serde_json::json!({"Team": "cli"}))).is_some(), "key too long"); - assert!(tags.rejection(&value(serde_json::json!({"Env": ""}))).is_some(), "empty value"); - assert!(tags.rejection(&value(serde_json::json!({"Env": "prod"}))).is_none()); + assert!( + tags.rejection(&value(serde_json::json!({"Team": "cli"}))).expect("domain evaluates").is_some(), + "key too long" + ); + assert!( + tags.rejection(&value(serde_json::json!({"Env": ""}))).expect("domain evaluates").is_some(), + "empty value" + ); + assert!(tags.rejection(&value(serde_json::json!({"Env": "prod"}))).expect("domain evaluates").is_none()); let empty = UnrepresentableDomain::default(); - assert!(empty.rejection(&value(serde_json::json!(true))).is_none()); - assert!(empty.rejection(&value(serde_json::json!(null))).is_none()); + assert!(empty.rejection(&value(serde_json::json!(true))).expect("domain evaluates").is_none()); + assert!(empty.rejection(&value(serde_json::json!(null))).expect("domain evaluates").is_none()); } #[test] From 8941469fd67314eb56b5204938e016960f5635ad Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Fri, 11 Sep 2026 14:52:05 -0400 Subject: [PATCH 15/19] update catalog and data source --- .../data/aws_cli_operation_catalog.json | 4095 +++++++++++++++-- .../generated/data/getatt_attributes.json | 16 + .../generated/data/known_resource_types.json | 1 + .../generated/data/region_resource_types.json | 2 + .../generated/data/schema_metadata.json | 99 + .../generated/data/source_versions.json | 2 +- .../schema-validator/compiled_schemas.json | 119 + .../generated/schema-validator/ref_types.json | 13 + 8 files changed, 3837 insertions(+), 510 deletions(-) diff --git a/src/data-source/generated/data/aws_cli_operation_catalog.json b/src/data-source/generated/data/aws_cli_operation_catalog.json index 3780f862..ac187ee2 100644 --- a/src/data-source/generated/data/aws_cli_operation_catalog.json +++ b/src/data-source/generated/data/aws_cli_operation_catalog.json @@ -271,7 +271,13 @@ }, { "source": "kmsKeyArn", - "target": "KmsKeyArn" + "target": "KmsKeyArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws[-a-z]*:kms:[-a-z0-9]+:[0-9]{12}:key/[-a-f0-9]+)$", + "cloudformation": "^arn:aws[-a-z]*:kms:[-a-z0-9]+:[0-9]{12}:key/.+$" + } + } }, { "source": "tags", @@ -381,7 +387,13 @@ "mappings": [ { "source": "analyzerName", - "target": "AnalyzerName" + "target": "AnalyzerName", + "unrepresentable": { + "pattern": { + "api": "^(?:[A-Za-z_][A-Za-z0-9_.-]*)$", + "cloudformation": "^[A-Za-z][A-Za-z0-9_.-]*$" + } + } }, { "source": "ruleName", @@ -400,7 +412,13 @@ "mappings": [ { "source": "analyzerName", - "target": "AnalyzerName" + "target": "AnalyzerName", + "unrepresentable": { + "pattern": { + "api": "^(?:[A-Za-z_][A-Za-z0-9_.-]*)$", + "cloudformation": "^[A-Za-z][A-Za-z0-9_.-]*$" + } + } }, { "source": "ruleName", @@ -753,7 +771,13 @@ }, { "source": "autoSubDomainIAMRole", - "target": "AutoSubDomainIAMRole" + "target": "AutoSubDomainIAMRole", + "unrepresentable": { + "pattern": { + "api": "^(?:^$|^arn:aws:iam::\\d{12}:role.+)$", + "cloudformation": "^$|^arn:.+:iam::\\d{12}:role.+" + } + } }, { "source": "domainName", @@ -1022,7 +1046,13 @@ }, { "source": "Type", - "target": "Type" + "target": "Type", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9\\.\\-]+)$", + "cloudformation": "^[a-zA-Z\\.]+" + } + } } ], "operation": "CreateConfigurationProfile", @@ -1347,7 +1377,13 @@ }, { "source": "VersionLabel", - "target": "VersionLabel" + "target": "VersionLabel", + "unrepresentable": { + "pattern": { + "api": "^(?:.*[^0-9].*)$", + "cloudformation": "^$|.*[^0-9].*" + } + } } ], "operation": "CreateHostedConfigurationVersion", @@ -1405,7 +1441,13 @@ }, { "source": "connectorLabel", - "target": "ConnectorLabel" + "target": "ConnectorLabel", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9][\\w!@#.-]+)$", + "cloudformation": "[\\w!@#.-]+" + } + } }, { "source": "connectorProfileName", @@ -1516,7 +1558,13 @@ }, { "source": "Namespace", - "target": "Namespace" + "target": "Namespace", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9\\/\\._\\-]+$)$", + "cloudformation": "^[a-zA-Z0-9/\\._\\-]+$" + } + } }, { "source": "Permissions", @@ -1557,7 +1605,13 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9\\/\\._\\-]+$)$", + "cloudformation": "^[a-zA-Z0-9/\\._\\-]+$" + } + } }, { "source": "SourceURI", @@ -1594,11 +1648,23 @@ }, { "source": "EventBridgeBus", - "target": "EventBridgeBus" + "target": "EventBridgeBus", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9\\/\\._\\-]+$)$", + "cloudformation": "^[a-zA-Z0-9/\\._\\-]+$" + } + } }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9\\/\\._\\-]+$)$", + "cloudformation": "^[a-zA-Z0-9/\\._\\-]+$" + } + } }, { "source": "Tags", @@ -1614,7 +1680,13 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9\\/\\._\\-]+$)$", + "cloudformation": "^[a-zA-Z0-9/\\._\\-]+$" + } + } } ], "operation": "DeleteEventIntegration", @@ -1707,7 +1779,13 @@ }, { "source": "VpcConnectorName", - "target": "VpcConnectorName" + "target": "VpcConnectorName", + "unrepresentable": { + "pattern": { + "api": "^(?:[A-Za-z0-9][A-Za-z0-9\\-_]{3,39})$", + "cloudformation": "^[A-Za-z0-9][A-Za-z0-9-\\\\_]{3,39}$" + } + } } ], "operation": "CreateVpcConnector", @@ -1726,7 +1804,13 @@ "mappings": [ { "source": "ServiceArn", - "target": "ServiceArn" + "target": "ServiceArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws(-[\\w]+)*:[a-z0-9-\\\\.]{0,63}:[a-z0-9-\\\\.]{0,63}:[0-9]{12}:(\\w|\\/|-){1,1011})$", + "cloudformation": "arn:aws(-[\\w]+)*:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[0-9]{12}:(\\w|/|-){1,1011}" + } + } }, { "source": "VpcIngressConnectionName", @@ -2316,7 +2400,13 @@ "mappings": [ { "source": "certificateArn", - "target": "CertificateArn" + "target": "CertificateArn", + "unrepresentable": { + "pattern": { + "api": "^(?:^arn:[a-z-]*:(acm|iam):[a-z0-9-]*:\\d{12}:(certificate|server-certificate)/[0-9A-Za-z_/-]*$)$", + "cloudformation": "^arn:[a-z-]*:acm:[a-z0-9-]*:\\d{12}:certificate/[0-9A-Za-z_/-]*$" + } + } }, { "source": "description", @@ -2324,7 +2414,13 @@ }, { "source": "domainName", - "target": "DomainName" + "target": "DomainName", + "unrepresentable": { + "pattern": { + "api": "^(?:^(\\*[\\w\\d-]*\\.)?([\\w\\d-]+\\.)+[\\w\\d-]+$)$", + "cloudformation": "^(\\*[a-z\\d-]*\\.)?([a-z\\d-]+\\.)+[a-z\\d-]+$" + } + } }, { "source": "tags", @@ -2340,7 +2436,13 @@ "mappings": [ { "source": "domainName", - "target": "DomainName" + "target": "DomainName", + "unrepresentable": { + "pattern": { + "api": "^(?:^(\\*[\\w\\d-]*\\.)?([\\w\\d-]+\\.)+[\\w\\d-]+$)$", + "cloudformation": "^(\\*[a-z\\d-]*\\.)?([a-z\\d-]+\\.)+[a-z\\d-]+$" + } + } } ], "operation": "DeleteDomainName", @@ -2356,7 +2458,13 @@ }, { "source": "domainName", - "target": "DomainName" + "target": "DomainName", + "unrepresentable": { + "pattern": { + "api": "^(?:^(\\*[\\w\\d-]*\\.)?([\\w\\d-]+\\.)+[\\w\\d-]+$)$", + "cloudformation": "^(\\*[a-z\\d-]*\\.)?([a-z\\d-]+\\.)+[a-z\\d-]+$" + } + } } ], "operation": "AssociateApi", @@ -2694,15 +2802,33 @@ }, { "source": "OpsItemSNSTopicArn", - "target": "OpsItemSNSTopicArn" + "target": "OpsItemSNSTopicArn", + "unrepresentable": { + "pattern": { + "api": "^(?:^arn:aws(-\\w+)*:[\\w\\d-]+:([\\w\\d-]*)?:[\\w\\d_-]*([:/].+)*$)$", + "cloudformation": "^arn:aws(-[\\w]+)*:[\\w\\d-]+:([\\w\\d-]*)?:[\\w\\d_-]*([:/].+)*$" + } + } }, { "source": "ResourceGroupName", - "target": "ResourceGroupName" + "target": "ResourceGroupName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9\\.\\-_]*)$", + "cloudformation": "[a-zA-Z0-9.-_]*" + } + } }, { "source": "SNSNotificationArn", - "target": "SNSNotificationArn" + "target": "SNSNotificationArn", + "unrepresentable": { + "pattern": { + "api": "^(?:^arn:aws(-\\w+)*:[\\w\\d-]+:([\\w\\d-]*)?:[\\w\\d_-]*([:/].+)*$)$", + "cloudformation": "^arn:aws(-[\\w]+)*:[\\w\\d-]+:([\\w\\d-]*)?:[\\w\\d_-]*([:/].+)*$" + } + } } ], "operation": "CreateApplication", @@ -2714,7 +2840,13 @@ "mappings": [ { "source": "ResourceGroupName", - "target": "ResourceGroupName" + "target": "ResourceGroupName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9\\.\\-_]*)$", + "cloudformation": "[a-zA-Z0-9.-_]*" + } + } } ], "operation": "DeleteApplication", @@ -2756,7 +2888,13 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9._-]+)$", + "cloudformation": "[a-zA-Z0-9._-]{1,128}" + } + } }, { "source": "TargetDpus", @@ -2772,7 +2910,13 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9._-]+)$", + "cloudformation": "[a-zA-Z0-9._-]{1,128}" + } + } } ], "operation": "DeleteCapacityReservation", @@ -2929,13 +3073,23 @@ }, { "source": "frameworkId", - "target": "FrameworkId" + "target": "FrameworkId", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$)$", + "cloudformation": "^([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}|.*\\S.*)$" + } + } }, { "source": "name", "target": "Name", "unrepresentable": { - "max_length": 127 + "max_length": 127, + "pattern": { + "api": "^(?:^[^\\\\]*$)$", + "cloudformation": "^[a-zA-Z0-9-_\\.]+$" + } } }, { @@ -3622,7 +3776,13 @@ "mappings": [ { "source": "FrameworkDescription", - "target": "FrameworkDescription" + "target": "FrameworkDescription", + "unrepresentable": { + "pattern": { + "api": "^(?:.*\\S.*)$", + "cloudformation": "\\S" + } + } }, { "source": "FrameworkName", @@ -4074,7 +4234,13 @@ "mappings": [ { "source": "costCategoryGroupSharingPreferenceArn", - "target": "CostCategoryGroupSharingPreferenceArn" + "target": "CostCategoryGroupSharingPreferenceArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws[-a-z0-9]*:ce::[0-9]{12}:costcategory/[a-f0-9-]{36})$", + "cloudformation": "^arn:aws[a-z-]*:ce::[0-9]{12}:costcategory/[a-f0-9-]{36}$" + } + } }, { "source": "groupSharingPreference", @@ -4199,7 +4365,13 @@ }, { "source": "policyArn", - "target": "PolicyArn" + "target": "PolicyArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws(-[^:]+)?:bedrock:[a-z0-9-]{1,20}:[0-9]{12}:automated-reasoning-policy/[a-z0-9]{12}(:([1-9][0-9]{0,11}))?)$", + "cloudformation": "^arn:aws(-[^:]+)?:bedrock:[a-z0-9-]{1,20}:[0-9]{12}:automated-reasoning-policy\\/[a-z0-9]{12}$" + } + } } ], "operation": "CreateAutomatedReasoningPolicyVersion", @@ -4652,7 +4824,13 @@ }, { "source": "executionRoleArn", - "target": "ExecutionRoleArn" + "target": "ExecutionRoleArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws(-[^:]+)?:iam::([0-9]{12})?:role/.+)$", + "cloudformation": "^arn:(aws(?:-cn|-us-gov|-iso(?:-[bef])?)?):iam::[0-9]{12}:role/.+$" + } + } }, { "source": "name", @@ -4759,7 +4937,13 @@ }, { "source": "kmsKeyArn", - "target": "KmsKeyArn" + "target": "KmsKeyArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws(|-cn|-us-gov):kms:[a-zA-Z0-9-]*:[0-9]{12}:key/[a-zA-Z0-9-]{36})$", + "cloudformation": "^arn:aws(|-cn|-us-gov):kms:[a-zA-Z0-9-]+:[0-9]{12}:key/[a-zA-Z0-9-]{36}$" + } + } }, { "source": "level", @@ -4948,7 +5132,13 @@ }, { "source": "executionRoleArn", - "target": "ExecutionRoleArn" + "target": "ExecutionRoleArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws(-[^:]+)?:iam::([0-9]{12})?:role/.+)$", + "cloudformation": "^arn:aws(-[^:]+)?:iam::[0-9]{12}:role/.+$" + } + } }, { "source": "harnessName", @@ -4964,7 +5154,21 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_key": { + "pattern": { + "api": "^(?:[a-zA-Z0-9\\s._:/=+@-]*)$", + "cloudformation": "^[a-zA-Z0-9\\s_.:/=+\\-@]*$" + } + }, + "tag_value": { + "pattern": { + "api": "^(?:[a-zA-Z0-9\\s._:/=+@-]*)$", + "cloudformation": "^[a-zA-Z0-9\\s_.:/=+\\-@]*$" + } + } + } }, { "source": "timeoutSeconds", @@ -5005,7 +5209,21 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_key": { + "pattern": { + "api": "^(?:[a-zA-Z0-9\\s._:/=+@-]*)$", + "cloudformation": "^[a-zA-Z0-9\\s_.:/=+\\-@]*$" + } + }, + "tag_value": { + "pattern": { + "api": "^(?:[a-zA-Z0-9\\s._:/=+@-]*)$", + "cloudformation": "^[a-zA-Z0-9\\s_.:/=+\\-@]*$" + } + } + } }, { "source": "targetVersion", @@ -5047,7 +5265,13 @@ }, { "source": "encryptionKeyArn", - "target": "EncryptionKeyArn" + "target": "EncryptionKeyArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:[a-z0-9-\\.]{1,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[^/].{0,1023})$", + "cloudformation": "^arn:(aws(?:-cn|-us-gov|-iso(?:-[bef])?)?):[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[^/].{0,1023}$" + } + } }, { "source": "eventExpiryDuration", @@ -5055,7 +5279,13 @@ }, { "source": "memoryExecutionRoleArn", - "target": "MemoryExecutionRoleArn" + "target": "MemoryExecutionRoleArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:[a-z0-9-\\.]{1,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[^/].{0,1023})$", + "cloudformation": "^arn:(aws(?:-cn|-us-gov|-iso(?:-[bef])?)?):[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[^/].{0,1023}$" + } + } }, { "source": "name", @@ -5128,7 +5358,15 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_key": { + "pattern": { + "api": "^(?:[a-zA-Z0-9\\s._:/=+@-]*)$", + "cloudformation": "^(?!aws:)[a-zA-Z+-=._:/]+$" + } + } + } } ], "operation": "CreateOnlineEvaluationConfig", @@ -5437,7 +5675,15 @@ "mappings": [ { "source": "allowedResourceOauth2ReturnUrls", - "target": "AllowedResourceOauth2ReturnUrls" + "target": "AllowedResourceOauth2ReturnUrls", + "unrepresentable": { + "items": { + "pattern": { + "api": "^(?:\\w+:(\\/?\\/?)[^\\s]+)$", + "cloudformation": "\\w+:(/?/?)[^\\s]+" + } + } + } }, { "source": "name", @@ -5445,7 +5691,15 @@ }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_key": { + "pattern": { + "api": "^(?:[a-zA-Z0-9\\s._:/=+@-]*)$", + "cloudformation": "^(?!aws:)[a-zA-Z+-=._:/]+$" + } + } + } } ], "operation": "CreateWorkloadIdentity", @@ -5476,11 +5730,25 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[ a-zA-Z0-9_\\+=\\.\\-@]+)$", + "cloudformation": "[a-zA-Z0-9_\\+=\\.\\-@]+" + } + } }, { "source": "sourceViews", - "target": "SourceViews" + "target": "SourceViews", + "unrepresentable": { + "items": { + "pattern": { + "api": "^(?:arn:aws[a-z-]*:(billing)::[0-9]{12}:billingview/[a-zA-Z0-9/:_\\+=\\.\\-@]{0,75}[a-zA-Z0-9])$", + "cloudformation": "arn:aws[a-z-]*:(billing)::[0-9]{12}:billingview/[a-zA-Z0-9_+=.@-]{1,75}" + } + } + } } ], "operation": "CreateBillingView", @@ -5545,7 +5813,13 @@ }, { "source": "BillingGroupArn", - "target": "BillingGroupArn" + "target": "BillingGroupArn", + "unrepresentable": { + "pattern": { + "api": "^(?:(arn:aws(-cn)?:billingconductor::[0-9]{12}:billinggroup/)?[a-zA-Z0-9]{10,12})$", + "cloudformation": "arn:aws(-cn)?:billingconductor::[0-9]{12}:billinggroup/?[a-zA-Z0-9]{10,12}" + } + } }, { "source": "ComputationRule", @@ -5596,7 +5870,15 @@ }, { "source": "PricingRuleArns", - "target": "PricingRuleArns" + "target": "PricingRuleArns", + "unrepresentable": { + "items": { + "pattern": { + "api": "^(?:(arn:aws(-cn)?:billingconductor::[0-9]{12}:pricingrule/)?[a-zA-Z0-9]{10})$", + "cloudformation": "arn:aws(-cn)?:billingconductor::[0-9]{12}:pricingrule/[a-zA-Z0-9]{10}" + } + } + } }, { "source": "Tags", @@ -5651,7 +5933,13 @@ }, { "source": "Service", - "target": "Service" + "target": "Service", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9]+)$", + "cloudformation": "[a-zA-Z0-9\\.\\-]+" + } + } }, { "source": "Tags", @@ -6092,7 +6380,13 @@ }, { "source": "IamRoleArn", - "target": "IamRoleArn" + "target": "IamRoleArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws:[A-Za-z0-9][A-Za-z0-9_/.-]{0,62}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9][A-Za-z0-9:_/+=,@.-]{0,1023})$", + "cloudformation": "^arn:(aws[a-zA-Z-]*)?:[A-Za-z0-9][A-Za-z0-9_/.-]{0,62}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9][A-Za-z0-9:_/+=,@.-]{0,1023}$" + } + } }, { "source": "LoggingLevel", @@ -6100,7 +6394,15 @@ }, { "source": "SnsTopicArns", - "target": "SnsTopicArns" + "target": "SnsTopicArns", + "unrepresentable": { + "items": { + "pattern": { + "api": "^(?:arn:aws:[A-Za-z0-9][A-Za-z0-9_/.-]{0,62}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9][A-Za-z0-9:_/+=,@.-]{0,1023})$", + "cloudformation": "^arn:(aws[a-zA-Z-]*)?:[A-Za-z0-9][A-Za-z0-9_/.-]{0,62}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9][A-Za-z0-9:_/+=,@.-]{0,1023}$" + } + } + } }, { "source": "TeamId", @@ -6127,7 +6429,13 @@ }, { "source": "IamRoleArn", - "target": "IamRoleArn" + "target": "IamRoleArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws:[A-Za-z0-9][A-Za-z0-9_/.-]{0,62}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9][A-Za-z0-9:_/+=,@.-]{0,1023})$", + "cloudformation": "^arn:(aws[a-zA-Z-]*)?:[A-Za-z0-9][A-Za-z0-9_/.-]{0,62}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9][A-Za-z0-9:_/+=,@.-]{0,1023}$" + } + } }, { "source": "LoggingLevel", @@ -6139,7 +6447,15 @@ }, { "source": "SnsTopicArns", - "target": "SnsTopicArns" + "target": "SnsTopicArns", + "unrepresentable": { + "items": { + "pattern": { + "api": "^(?:arn:aws:[A-Za-z0-9][A-Za-z0-9_/.-]{0,62}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9][A-Za-z0-9:_/+=,@.-]{0,1023})$", + "cloudformation": "^arn:(aws[a-zA-Z-]*)?:[A-Za-z0-9][A-Za-z0-9_/.-]{0,62}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9][A-Za-z0-9:_/+=,@.-]{0,1023}$" + } + } + } } ], "operation": "CreateSlackChannelConfiguration", @@ -6229,7 +6545,13 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:.*\\S.*)$", + "cloudformation": "^[\\u0009\\u000A\\u000D\\u0020-\\u007E\\u0085\\u00A0-\\uD7FF\\uE000-\\uFFFD\\u10000-\\u10FFFF]*$" + } + } } ], "operation": "CreateAppInstanceUser", @@ -6390,7 +6712,11 @@ "source": "description", "target": "Description", "unrepresentable": { - "min_length": 1 + "min_length": 1, + "pattern": { + "api": "^(?:(?!\\s+$)[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDBFF-\\uDC00\\uDFFF\\t\\r\\n]*)$", + "cloudformation": "^(?!\\s*$)[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDBFF-\\uDC00\\uDFFF\\t\\r\\n]*$" + } } }, { @@ -7206,11 +7532,23 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9._\\-]+$)$", + "cloudformation": "(^[a-zA-Z0-9._\\-]+$)" + } + } }, { "source": "Source", - "target": "Source" + "target": "Source", + "unrepresentable": { + "pattern": { + "api": "^(?:.*)$", + "cloudformation": "(.*)" + } + } } ], "operation": "CreateChannel", @@ -7435,7 +7773,13 @@ }, { "source": "Namespace", - "target": "Namespace" + "target": "Namespace", + "unrepresentable": { + "pattern": { + "api": "^(?:[^:].*)$", + "cloudformation": "^[^:]" + } + } }, { "source": "OKActions", @@ -7709,7 +8053,13 @@ "mappings": [ { "source": "domain", - "target": "DomainName" + "target": "DomainName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9\\-]{0,48}[a-z0-9])$", + "cloudformation": "^([a-z][a-z0-9\\-]{0,48}[a-z0-9])$" + } + } } ], "operation": "CreateDomain", @@ -7721,7 +8071,13 @@ "mappings": [ { "source": "domain", - "target": "DomainName" + "target": "DomainName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9\\-]{0,48}[a-z0-9])$", + "cloudformation": "^([a-z][a-z0-9\\-]{0,48}[a-z0-9])$" + } + } } ], "operation": "DeleteDomain", @@ -7741,7 +8097,13 @@ }, { "source": "domain", - "target": "DomainName" + "target": "DomainName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9\\-]{0,48}[a-z0-9])$", + "cloudformation": "^([a-z][a-z0-9\\-]{0,48}[a-z0-9])$" + } + } }, { "source": "domainOwner", @@ -7757,7 +8119,13 @@ "mappings": [ { "source": "domain", - "target": "DomainName" + "target": "DomainName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9\\-]{0,48}[a-z0-9])$", + "cloudformation": "^([a-z][a-z0-9\\-]{0,48}[a-z0-9])$" + } + } }, { "source": "domainOwner", @@ -7777,11 +8145,23 @@ }, { "source": "domain", - "target": "DomainName" + "target": "DomainName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9\\-]{0,48}[a-z0-9])$", + "cloudformation": "^([a-z][a-z0-9\\-]{0,48}[a-z0-9])$" + } + } }, { "source": "repository", - "target": "RepositoryName" + "target": "RepositoryName", + "unrepresentable": { + "pattern": { + "api": "^(?:[A-Za-z0-9][A-Za-z0-9._\\-]{1,99})$", + "cloudformation": "^([A-Za-z0-9][A-Za-z0-9._\\-]{1,99})$" + } + } } ], "operation": "CreateRepository", @@ -7793,11 +8173,23 @@ "mappings": [ { "source": "domain", - "target": "DomainName" + "target": "DomainName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9\\-]{0,48}[a-z0-9])$", + "cloudformation": "^([a-z][a-z0-9\\-]{0,48}[a-z0-9])$" + } + } }, { "source": "repository", - "target": "RepositoryName" + "target": "RepositoryName", + "unrepresentable": { + "pattern": { + "api": "^(?:[A-Za-z0-9][A-Za-z0-9._\\-]{1,99})$", + "cloudformation": "^([A-Za-z0-9][A-Za-z0-9._\\-]{1,99})$" + } + } } ], "operation": "DeleteRepository", @@ -7907,7 +8299,13 @@ }, { "source": "repositoryName", - "target": "RepositoryName" + "target": "RepositoryName", + "unrepresentable": { + "pattern": { + "api": "^(?:[\\w\\.-]+)$", + "cloudformation": "^[a-zA-Z0-9._\\-]+(??@^_`{|}~\\/: {},\\\\\"]+)$", + "cloudformation": "^[0-9A-Za-z!#$%&()*\\+\\-;<=>?@^_`{|}~\\/: ]+$" + } + } }, { "source": "AuthenticationMaterialType", @@ -26894,7 +28035,13 @@ "mappings": [ { "source": "CaCertificate", - "target": "CaCertificate" + "target": "CaCertificate", + "unrepresentable": { + "pattern": { + "api": "^(?:-----BEGIN CERTIFICATE-----.*(.|\\n)*-----END CERTIFICATE-----\\n?)$", + "cloudformation": "^-----BEGIN CERTIFICATE-----.*(.|\\\n)*-----END CERTIFICATE-----\\n?$" + } + } }, { "source": "ClaimCertificate", @@ -27039,7 +28186,13 @@ }, { "source": "computationModelName", - "target": "ComputationModelName" + "target": "ComputationModelName", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9 _\\-#$*!@.]+$)$", + "cloudformation": "^[a-zA-Z0-9 _\\-#$*!@]+$" + } + } }, { "source": "tags", @@ -27438,7 +28591,13 @@ }, { "source": "entityName", - "target": "EntityName" + "target": "EntityName", + "unrepresentable": { + "pattern": { + "api": "^(?:[^\\u0000-\\u001F\\u007F]+)$", + "cloudformation": "[a-zA-Z_0-9-.][a-zA-Z_0-9-. ]*[a-zA-Z0-9]+" + } + } }, { "source": "parentEntityId", @@ -27568,7 +28727,13 @@ }, { "source": "s3Location", - "target": "S3Location" + "target": "S3Location", + "unrepresentable": { + "pattern": { + "api": "^(?:.*(^arn:((aws)|(aws-cn)|(aws-us-gov)):s3:::)([a-zA-Z0-9_-]+$).*)$", + "cloudformation": "(^arn:((aws)|(aws-cn)|(aws-us-gov)):s3:::)([a-zA-Z0-9_-]+$)" + } + } }, { "source": "workspaceId", @@ -27611,7 +28776,13 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9-_]+)$", + "cloudformation": "[a-zA-Z0-9:_-]+" + } + } }, { "source": "RoleArn", @@ -27627,7 +28798,13 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9-_]+)$", + "cloudformation": "[a-zA-Z0-9:_-]+" + } + } } ], "operation": "DeleteDestination", @@ -27914,7 +29091,13 @@ "mappings": [ { "source": "AliasName", - "target": "AliasName" + "target": "AliasName", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9:/_-]+$)$", + "cloudformation": "^(alias/)[a-zA-Z0-9:/_-]+$" + } + } }, { "source": "TargetKeyId", @@ -27933,7 +29116,13 @@ "mappings": [ { "source": "AliasName", - "target": "AliasName" + "target": "AliasName", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9:/_-]+$)$", + "cloudformation": "^(alias/)[a-zA-Z0-9:/_-]+$" + } + } } ], "operation": "DeleteAlias", @@ -28350,7 +29539,13 @@ "mappings": [ { "source": "ResourceARN", - "target": "ResourceArn" + "target": "ResourceArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws.*:kinesis:.*:\\d{12}:.*stream/\\S+)$", + "cloudformation": "arn:aws.*:kinesis:.*:\\d{12}:stream/\\S+" + } + } } ], "operation": "PutResourcePolicy", @@ -28362,7 +29557,13 @@ "mappings": [ { "source": "ResourceARN", - "target": "ResourceArn" + "target": "ResourceArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws.*:kinesis:.*:\\d{12}:.*stream/\\S+)$", + "cloudformation": "arn:aws.*:kinesis:.*:\\d{12}:stream/\\S+" + } + } } ], "operation": "DeleteResourcePolicy", @@ -28491,7 +29692,13 @@ "mappings": [ { "source": "DeliveryStreamName", - "target": "DeliveryStreamName" + "target": "DeliveryStreamName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9_.-]+)$", + "cloudformation": "[a-zA-Z0-9._-]+" + } + } }, { "source": "DeliveryStreamType", @@ -28507,7 +29714,13 @@ "mappings": [ { "source": "DeliveryStreamName", - "target": "DeliveryStreamName" + "target": "DeliveryStreamName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9_.-]+)$", + "cloudformation": "[a-zA-Z0-9._-]+" + } + } } ], "operation": "DeleteDeliveryStream", @@ -28709,7 +29922,13 @@ }, { "source": "FunctionVersion", - "target": "FunctionVersion" + "target": "FunctionVersion", + "unrepresentable": { + "pattern": { + "api": "^(?:(\\$LATEST(\\.PUBLISHED)?|[0-9]+))$", + "cloudformation": "^(\\$LATEST(\\.PUBLISHED)?|[0-9]*)$" + } + } }, { "source": "Name", @@ -28745,7 +29964,13 @@ }, { "source": "KmsKeyArn", - "target": "KmsKeyArn" + "target": "KmsKeyArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:(aws[a-zA-Z-]*)?:[a-z0-9-.]+:.*)$", + "cloudformation": "^(arn:(aws[a-zA-Z-]*)?:[a-z0-9-.]+:.*)|()$" + } + } }, { "source": "Tags", @@ -28796,7 +30021,13 @@ "mappings": [ { "source": "FunctionName", - "target": "FunctionName" + "target": "FunctionName", + "unrepresentable": { + "pattern": { + "api": "^(?:(arn:(aws[a-zA-Z-]*)?:lambda:(eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:|(((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:)?(\\d{12}:)?))(function:)?([a-zA-Z0-9-_\\.]+)(:(\\$LATEST(\\.PUBLISHED)?|[a-zA-Z0-9-_]+))?)$", + "cloudformation": "^(arn:(aws[a-zA-Z-]*)?:lambda:)?([a-z]+(-[a-z]+)+-\\d{1}:)?(\\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\\$LATEST(\\.PUBLISHED)?|[a-zA-Z0-9-_]+))?$" + } + } }, { "source": "MaximumEventAgeInSeconds", @@ -28808,7 +30039,13 @@ }, { "source": "Qualifier", - "target": "Qualifier" + "target": "Qualifier", + "unrepresentable": { + "pattern": { + "api": "^(?:\\$(LATEST(\\.PUBLISHED)?)|[a-zA-Z0-9-_$]+)$", + "cloudformation": "^\\$(LATEST(\\.PUBLISHED)?)|[a-zA-Z0-9$_-]{1,129}$" + } + } } ], "operation": "PutFunctionEventInvokeConfig", @@ -28820,11 +30057,23 @@ "mappings": [ { "source": "FunctionName", - "target": "FunctionName" + "target": "FunctionName", + "unrepresentable": { + "pattern": { + "api": "^(?:(arn:(aws[a-zA-Z-]*)?:lambda:(eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:|(((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:)?(\\d{12}:)?))(function:)?([a-zA-Z0-9-_\\.]+)(:(\\$LATEST(\\.PUBLISHED)?|[a-zA-Z0-9-_]+))?)$", + "cloudformation": "^(arn:(aws[a-zA-Z-]*)?:lambda:)?([a-z]+(-[a-z]+)+-\\d{1}:)?(\\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\\$LATEST(\\.PUBLISHED)?|[a-zA-Z0-9-_]+))?$" + } + } }, { "source": "Qualifier", - "target": "Qualifier" + "target": "Qualifier", + "unrepresentable": { + "pattern": { + "api": "^(?:\\$(LATEST(\\.PUBLISHED)?)|[a-zA-Z0-9-_$]+)$", + "cloudformation": "^\\$(LATEST(\\.PUBLISHED)?)|[a-zA-Z0-9$_-]{1,129}$" + } + } } ], "operation": "DeleteFunctionEventInvokeConfig", @@ -28858,7 +30107,11 @@ "source": "FunctionName", "target": "FunctionName", "unrepresentable": { - "max_length": 140 + "max_length": 140, + "pattern": { + "api": "^(?:(arn:(aws[a-zA-Z-]*)?:lambda:(eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:|(((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:)?(\\d{12}:)?))(function:)?([a-zA-Z0-9-_\\.]+)(:(\\$LATEST(\\.PUBLISHED)?|[a-zA-Z0-9-_]+))?)$", + "cloudformation": "(arn:(aws[a-zA-Z-]*)?:lambda:)?((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:)?(\\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\\$LATEST(\\.PUBLISHED)?|[a-zA-Z0-9-_]+))?" + } } }, { @@ -28938,7 +30191,13 @@ }, { "source": "FunctionName", - "target": "FunctionName" + "target": "FunctionName", + "unrepresentable": { + "pattern": { + "api": "^(?:(arn:(aws[a-zA-Z-]*)?:lambda:)?((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:)?(\\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\\$LATEST|[a-zA-Z0-9-_]+))?)$", + "cloudformation": "^([a-zA-Z0-9_-]{1,64}|arn:[a-zA-Z0-9-]+:lambda:[a-zA-Z0-9-]+:\\d{12}:function:[a-zA-Z0-9_-]{1,64})$" + } + } }, { "source": "Handler", @@ -28950,7 +30209,15 @@ }, { "source": "Layers", - "target": "Layers" + "target": "Layers", + "unrepresentable": { + "items": { + "pattern": { + "api": "^(?:((arn:(aws[a-zA-Z-]*)?:lambda:(eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:layer:[a-zA-Z0-9-_]+:[0-9]+)|(arn:[a-zA-Z0-9-]+:lambda:::awslayer:[a-zA-Z0-9-_]+)))$", + "cloudformation": "^(arn:[a-zA-Z0-9-]+:lambda:[a-zA-Z0-9-]+:\\d{12}:layer:[a-zA-Z0-9-_]+:[0-9]+|arn:[a-zA-Z0-9-]+:lambda:::awslayer:[a-zA-Z0-9-_]+)$" + } + } + } }, { "source": "MemorySize", @@ -28986,7 +30253,13 @@ "mappings": [ { "source": "FunctionName", - "target": "FunctionName" + "target": "FunctionName", + "unrepresentable": { + "pattern": { + "api": "^(?:(arn:(aws[a-zA-Z-]*)?:lambda:(eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:|(((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:)?(\\d{12}:)?))(function:)?([a-zA-Z0-9-_\\.]+)(:(\\$LATEST(\\.PUBLISHED)?|[a-zA-Z0-9-_]+))?)$", + "cloudformation": "^([a-zA-Z0-9_-]{1,64}|arn:[a-zA-Z0-9-]+:lambda:[a-zA-Z0-9-]+:\\d{12}:function:[a-zA-Z0-9_-]{1,64})$" + } + } } ], "operation": "DeleteFunction", @@ -29115,7 +30388,13 @@ }, { "source": "buildRoleArn", - "target": "BuildRoleArn" + "target": "BuildRoleArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws[a-z\\-]*:iam::[0-9]{12}:role/?[a-zA-Z_0-9+=,.@\\-_/]+)$", + "cloudformation": "^arn:aws[a-zA-Z-]*:iam::\\d{12}:role/?[a-zA-Z_0-9+=,.@\\-_/]+$" + } + } }, { "source": "description", @@ -29197,7 +30476,11 @@ "source": "FunctionName", "target": "FunctionName", "unrepresentable": { - "max_length": 140 + "max_length": 140, + "pattern": { + "api": "^(?:(arn:(aws[a-zA-Z-]*)?:lambda:(eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:|(((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:)?(\\d{12}:)?))(function:)?([a-zA-Z0-9-_\\.]+)(:(\\$LATEST(\\.PUBLISHED)?|[a-zA-Z0-9-_]+))?)$", + "cloudformation": "^(arn:(aws[a-zA-Z-]*)?:lambda:)?((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:)?(\\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\\$LATEST(\\.PUBLISHED)?|[a-zA-Z0-9-_]+))?$" + } } }, { @@ -29213,7 +30496,11 @@ "target": "Principal", "unrepresentable": { "max_length": 256, - "min_length": 1 + "min_length": 1, + "pattern": { + "api": "^(?:[^\\s]+)$", + "cloudformation": "^.*$" + } } }, { @@ -29247,7 +30534,11 @@ "source": "FunctionName", "target": "FunctionName", "unrepresentable": { - "max_length": 140 + "max_length": 140, + "pattern": { + "api": "^(?:(arn:(aws[a-zA-Z-]*)?:lambda:(eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:|(((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:)?(\\d{12}:)?))(function:)?([a-zA-Z0-9-_\\.]+)(:(\\$LATEST(\\.PUBLISHED)?|[a-zA-Z0-9-_]+))?)$", + "cloudformation": "^(arn:(aws[a-zA-Z-]*)?:lambda:)?((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:)?(\\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\\$LATEST(\\.PUBLISHED)?|[a-zA-Z0-9-_]+))?$" + } } } ], @@ -29268,7 +30559,13 @@ }, { "source": "Qualifier", - "target": "Qualifier" + "target": "Qualifier", + "unrepresentable": { + "pattern": { + "api": "^(?:((?!^\\d+$)^[0-9a-zA-Z-_]+$))$", + "cloudformation": "((?!^[0-9]+$)([a-zA-Z0-9-_]+))" + } + } } ], "operation": "CreateFunctionUrlConfig", @@ -29304,11 +30601,25 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[A-Za-z0-9_\\.-]+)$", + "cloudformation": "^[A-Za-z0-9_\\s\\.-]+$" + } + } }, { "source": "tags", - "target": "Tags" + "target": "Tags", + "unrepresentable": { + "tag_key": { + "pattern": { + "api": "^(?:(?!aws:)[a-zA-Z+-=._:/]+)$", + "cloudformation": "^[a-zA-Z+-=._:/]+$" + } + } + } }, { "source": "workloadName", @@ -29343,7 +30654,13 @@ }, { "source": "roleArn", - "target": "RoleArn" + "target": "RoleArn", + "unrepresentable": { + "pattern": { + "api": "^(?:^arn:aws:iam::[0-9]{12}:role/.*$)$", + "cloudformation": "^arn:aws[a-zA-Z-]*:iam::[0-9]{12}:role/.*$" + } + } } ], "operation": "CreateBot", @@ -29355,7 +30672,13 @@ "mappings": [ { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:^([A-Za-z]_?)+$)$", + "cloudformation": "^([0-9a-zA-Z][_-]?)+$" + } + } } ], "operation": "DeleteBot", @@ -29367,7 +30690,13 @@ "mappings": [ { "source": "botAliasName", - "target": "BotAliasName" + "target": "BotAliasName", + "unrepresentable": { + "pattern": { + "api": "^(?:^([0-9a-zA-Z][_-]?){1,100}$)$", + "cloudformation": "^([0-9a-zA-Z][_-]?)+$" + } + } }, { "source": "botId", @@ -29375,7 +30704,13 @@ }, { "source": "botVersion", - "target": "BotVersion" + "target": "BotVersion", + "unrepresentable": { + "pattern": { + "api": "^(?:^[0-9]+$)$", + "cloudformation": "^(DRAFT|[0-9]+)$" + } + } }, { "source": "description", @@ -29394,7 +30729,13 @@ "mappings": [ { "source": "name", - "target": "BotAliasName" + "target": "BotAliasName", + "unrepresentable": { + "pattern": { + "api": "^(?:^([A-Za-z]_?)+$)$", + "cloudformation": "^([0-9a-zA-Z][_-]?)+$" + } + } } ], "operation": "DeleteBotAlias", @@ -29635,7 +30976,13 @@ }, { "source": "bundleId", - "target": "BundleId" + "target": "BundleId", + "unrepresentable": { + "pattern": { + "api": "^(?:.*\\S.*)$", + "cloudformation": "\\S" + } + } } ], "operation": "CreateBucket", @@ -29827,11 +31174,23 @@ "mappings": [ { "source": "availabilityZone", - "target": "AvailabilityZone" + "target": "AvailabilityZone", + "unrepresentable": { + "pattern": { + "api": "^(?:.*\\S.*)$", + "cloudformation": "\\S" + } + } }, { "source": "diskName", - "target": "DiskName" + "target": "DiskName", + "unrepresentable": { + "pattern": { + "api": "^(?:\\w[\\w\\-]*\\w)$", + "cloudformation": "^[a-zA-Z0-9][\\w\\-.]*[a-zA-Z0-9]$" + } + } }, { "source": "sizeInGb", @@ -29847,7 +31206,13 @@ "mappings": [ { "source": "diskName", - "target": "DiskName" + "target": "DiskName", + "unrepresentable": { + "pattern": { + "api": "^(?:\\w[\\w\\-]*\\w)$", + "cloudformation": "^[a-zA-Z0-9][\\w\\-.]*[a-zA-Z0-9]$" + } + } } ], "operation": "DeleteDisk", @@ -29951,11 +31316,23 @@ }, { "source": "blueprintId", - "target": "BlueprintId" + "target": "BlueprintId", + "unrepresentable": { + "pattern": { + "api": "^(?:.*\\S.*)$", + "cloudformation": "\\S" + } + } }, { "source": "bundleId", - "target": "BundleId" + "target": "BundleId", + "unrepresentable": { + "pattern": { + "api": "^(?:.*\\S.*)$", + "cloudformation": "\\S" + } + } }, { "source": "keyPairName", @@ -29975,7 +31352,13 @@ "mappings": [ { "source": "instanceName", - "target": "InstanceName" + "target": "InstanceName", + "unrepresentable": { + "pattern": { + "api": "^(?:\\w[\\w\\-]*\\w)$", + "cloudformation": "^[a-zA-Z0-9][\\w\\-.]*[a-zA-Z0-9]$" + } + } } ], "operation": "DeleteInstance", @@ -30560,7 +31943,13 @@ "mappings": [ { "source": "logType", - "target": "LogType" + "target": "LogType", + "unrepresentable": { + "pattern": { + "api": "^(?:[\\w]*)$", + "cloudformation": "[\\w-]*$" + } + } }, { "source": "name", @@ -30596,7 +31985,13 @@ "mappings": [ { "source": "destinationName", - "target": "DestinationName" + "target": "DestinationName", + "unrepresentable": { + "pattern": { + "api": "^(?:[^:*]*)$", + "cloudformation": "^[^:*]{1,512}$" + } + } }, { "source": "roleArn", @@ -30620,7 +32015,13 @@ "mappings": [ { "source": "destinationName", - "target": "DestinationName" + "target": "DestinationName", + "unrepresentable": { + "pattern": { + "api": "^(?:[^:*]*)$", + "cloudformation": "^[^:*]{1,512}$" + } + } } ], "operation": "DeleteDestination", @@ -30688,7 +32089,11 @@ "target": "LogGroupArnList", "unrepresentable": { "items": { - "min_length": 20 + "min_length": 20, + "pattern": { + "api": "^(?:[\\w#+=/:,.@-]*)$", + "cloudformation": "^[\\w#+=/:,.@*-]*$" + } } } } @@ -30721,7 +32126,13 @@ }, { "source": "logGroupName", - "target": "LogGroupName" + "target": "LogGroupName", + "unrepresentable": { + "pattern": { + "api": "^(?:[\\.\\-_/#A-Za-z0-9]+)$", + "cloudformation": "^[.\\-_/#A-Za-z0-9]{1,512}\\Z" + } + } }, { "source": "tags", @@ -30737,7 +32148,13 @@ "mappings": [ { "source": "logGroupName", - "target": "LogGroupName" + "target": "LogGroupName", + "unrepresentable": { + "pattern": { + "api": "^(?:[\\.\\-_/#A-Za-z0-9]+)$", + "cloudformation": "^[.\\-_/#A-Za-z0-9]{1,512}\\Z" + } + } } ], "operation": "DeleteLogGroup", @@ -30793,7 +32210,13 @@ }, { "source": "filterName", - "target": "FilterName" + "target": "FilterName", + "unrepresentable": { + "pattern": { + "api": "^(?:[^:*]*)$", + "cloudformation": "^[^:*]{1,512}" + } + } }, { "source": "filterPattern", @@ -30801,7 +32224,13 @@ }, { "source": "logGroupName", - "target": "LogGroupName" + "target": "LogGroupName", + "unrepresentable": { + "pattern": { + "api": "^(?:[\\.\\-_/#A-Za-z0-9]+)$", + "cloudformation": "^[.\\-_/#A-Za-z0-9]{1,512}" + } + } } ], "operation": "PutMetricFilter", @@ -30813,11 +32242,23 @@ "mappings": [ { "source": "filterName", - "target": "FilterName" + "target": "FilterName", + "unrepresentable": { + "pattern": { + "api": "^(?:[^:*]*)$", + "cloudformation": "^[^:*]{1,512}" + } + } }, { "source": "logGroupName", - "target": "LogGroupName" + "target": "LogGroupName", + "unrepresentable": { + "pattern": { + "api": "^(?:[\\.\\-_/#A-Za-z0-9]+)$", + "cloudformation": "^[.\\-_/#A-Za-z0-9]{1,512}" + } + } } ], "operation": "DeleteMetricFilter", @@ -31564,11 +33005,23 @@ }, { "source": "ExecutionRoleArn", - "target": "ExecutionRoleArn" + "target": "ExecutionRoleArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws(-[a-z]+)?:iam::\\d{12}:role/?[a-zA-Z_0-9+=,.@\\-_/]+)$", + "cloudformation": "^arn:(aws|aws-us-gov|aws-cn|aws-iso|aws-iso-b)(-[a-z]+)?:iam::\\d{12}:role/?[a-zA-Z_0-9+=,.@\\-_/]+$" + } + } }, { "source": "KmsKey", - "target": "KmsKey" + "target": "KmsKey", + "unrepresentable": { + "pattern": { + "api": "^(?:(((arn:aws(-[a-z]+)?:kms:[a-z]{2}-[a-z]+-\\d:\\d+:)?key\\/)?[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}|(arn:aws(-[a-z]+)?:kms:[a-z]{2}-[a-z]+-\\d:\\d+:)?alias/.+))$", + "cloudformation": "^(((arn:(aws|aws-us-gov|aws-cn|aws-iso|aws-iso-b)(-[a-z]+)?:kms:[a-z]{2}-[a-z]+-\\d:\\d+:)?key\\/)?[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}|(arn:(aws|aws-us-gov|aws-cn|aws-iso|aws-iso-b):kms:[a-z]{2}-[a-z]+-\\d:\\d+:)?alias/.+)$" + } + } }, { "source": "MaxWebservers", @@ -31588,7 +33041,13 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z][0-9a-zA-Z-_]*)$", + "cloudformation": "^[a-zA-Z][0-9a-zA-Z\\-_]*$" + } + } }, { "source": "PluginsS3ObjectVersion", @@ -31612,7 +33071,13 @@ }, { "source": "SourceBucketArn", - "target": "SourceBucketArn" + "target": "SourceBucketArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws(-[a-z]+)?:s3:::[a-z0-9.\\-]+)$", + "cloudformation": "^arn:(aws|aws-us-gov|aws-cn|aws-iso|aws-iso-b)(-[a-z]+)?:s3:::[a-z0-9.\\-]+$" + } + } }, { "source": "StartupScriptS3ObjectVersion", @@ -31628,7 +33093,13 @@ }, { "source": "WeeklyMaintenanceWindowStart", - "target": "WeeklyMaintenanceWindowStart" + "target": "WeeklyMaintenanceWindowStart", + "unrepresentable": { + "pattern": { + "api": "^(?:.*(MON|TUE|WED|THU|FRI|SAT|SUN):([01]\\d|2[0-3]):(00|30).*)$", + "cloudformation": "(MON|TUE|WED|THU|FRI|SAT|SUN):([01]\\d|2[0-3]):(00|30)" + } + } } ], "operation": "CreateEnvironment", @@ -31640,7 +33111,13 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z][0-9a-zA-Z-_]*)$", + "cloudformation": "^[a-zA-Z][0-9a-zA-Z\\-_]*$" + } + } } ], "operation": "DeleteEnvironment", @@ -31663,7 +33140,13 @@ }, { "source": "RoleArn", - "target": "RoleArn" + "target": "RoleArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws(?:-(?:cn|us-gov|iso|iso-b|iso-e|iso-f))?:iam::[0-9]{12}:role(/[a-zA-Z0-9+=,.@_\\-]{1,512})*?/[a-zA-Z0-9+=,.@_\\-]{1,64})$", + "cloudformation": "^arn:aws(?:-(?:cn|us-gov|iso|iso-b|iso-e|iso-f))?:iam::[0-9]{12}:role(/[a-zA-Z0-9+=,.@_-]{1,512})*?/[a-zA-Z0-9+=,.@_-]{1,64}$" + } + } }, { "source": "TriggerMode", @@ -31980,7 +33463,13 @@ "mappings": [ { "source": "FlowArn", - "target": "FlowArn" + "target": "FlowArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:.+:mediaconnect.+:flow:.+)$", + "cloudformation": "^arn:(aws[a-zA-Z-]*):mediaconnect:[a-z0-9-]+:[0-9]{12}:flow:[a-zA-Z0-9-]+:[a-zA-Z0-9_-]+$" + } + } } ], "operation": "AddFlowOutputs", @@ -31992,7 +33481,13 @@ "mappings": [ { "source": "FlowArn", - "target": "FlowArn" + "target": "FlowArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:.+:mediaconnect.+:flow:.+)$", + "cloudformation": "^arn:(aws[a-zA-Z-]*):mediaconnect:[a-z0-9-]+:[0-9]{12}:flow:[a-zA-Z0-9-]+:[a-zA-Z0-9_-]+$" + } + } } ], "operation": "RemoveFlowOutput", @@ -32004,7 +33499,13 @@ "mappings": [ { "source": "FlowArn", - "target": "FlowArn" + "target": "FlowArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:.+:mediaconnect.+:flow:.+)$", + "cloudformation": "^arn:(aws[a-zA-Z-]*):mediaconnect:[a-z0-9-]+:[0-9]{12}:flow:[a-zA-Z0-9-]+:[a-zA-Z0-9_-]+$" + } + } } ], "operation": "AddFlowSources", @@ -32016,7 +33517,13 @@ "mappings": [ { "source": "FlowArn", - "target": "FlowArn" + "target": "FlowArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:.+:mediaconnect.+:flow:.+)$", + "cloudformation": "^arn:(aws[a-zA-Z-]*):mediaconnect:[a-z0-9-]+:[0-9]{12}:flow:[a-zA-Z0-9-]+:[a-zA-Z0-9_-]+$" + } + } } ], "operation": "RemoveFlowSource", @@ -32028,7 +33535,13 @@ "mappings": [ { "source": "FlowArn", - "target": "FlowArn" + "target": "FlowArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:.+:mediaconnect.+:flow:.+)$", + "cloudformation": "^arn:(aws[a-zA-Z-]*):mediaconnect:[a-z0-9-]+:[0-9]{12}:flow:[a-zA-Z0-9-]+:[a-zA-Z0-9_-]+$" + } + } } ], "operation": "AddFlowVpcInterfaces", @@ -32040,7 +33553,13 @@ "mappings": [ { "source": "FlowArn", - "target": "FlowArn" + "target": "FlowArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:.+:mediaconnect.+:flow:.+)$", + "cloudformation": "^arn:(aws[a-zA-Z-]*):mediaconnect:[a-z0-9-]+:[0-9]{12}:flow:[a-zA-Z0-9-]+:[a-zA-Z0-9_-]+$" + } + } } ], "operation": "RemoveFlowVpcInterface", @@ -33649,11 +35168,23 @@ "mappings": [ { "source": "AccessString", - "target": "AccessString" + "target": "AccessString", + "unrepresentable": { + "pattern": { + "api": "^(?:.*\\S.*)$", + "cloudformation": "\\S" + } + } }, { "source": "UserName", - "target": "UserName" + "target": "UserName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z][a-zA-Z0-9\\-]*)$", + "cloudformation": "[a-z][a-z0-9\\\\-]*" + } + } } ], "operation": "CreateUser", @@ -33665,7 +35196,13 @@ "mappings": [ { "source": "UserName", - "target": "UserName" + "target": "UserName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z][a-zA-Z0-9\\-]*)$", + "cloudformation": "[a-z][a-z0-9\\\\-]*" + } + } } ], "operation": "DeleteUser", @@ -33979,11 +35516,23 @@ }, { "source": "graphName", - "target": "GraphName" + "target": "GraphName", + "unrepresentable": { + "pattern": { + "api": "^(?:(?!g-)[a-z][a-z0-9]*(-[a-z0-9]+)*)$", + "cloudformation": "^[a-zA-z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*$" + } + } }, { "source": "kmsKeyIdentifier", - "target": "KmsKeyIdentifier" + "target": "KmsKeyIdentifier", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws[^:]*:kms:[a-zA-Z0-9-]*:[0-9]{12}:key/[a-zA-Z0-9-]{36})$", + "cloudformation": "arn:aws(|-cn|-us-gov|-iso|-iso-b|-iso-e|-iso-f|-eusc):kms:[a-zA-Z0-9-]*:[0-9]{12}:key/[a-zA-Z0-9-]{36}" + } + } }, { "source": "provisionedMemory", @@ -34257,7 +35806,13 @@ }, { "source": "FirewallArn", - "target": "FirewallArn" + "target": "FirewallArn", + "unrepresentable": { + "pattern": { + "api": "^(?:^arn:aws.*)$", + "cloudformation": "^(arn:aws.*)$" + } + } }, { "source": "VpcId", @@ -34815,11 +36370,23 @@ "mappings": [ { "source": "arn", - "target": "Arn" + "target": "Arn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws:(chatbot|consoleapp|notifications-contacts):[a-zA-Z0-9-]*:[0-9]{12}:[a-zA-Z0-9-_.@]+/[a-zA-Z0-9/_.@:-]+)$", + "cloudformation": "^arn:[a-z-]{3,10}:(chatbot|consoleapp|notifications-contacts):[a-zA-Z0-9-]*:[0-9]{12}:[a-zA-Z0-9-_.@]+/[a-zA-Z0-9/_.@:-]+$" + } + } }, { "source": "notificationConfigurationArn", - "target": "NotificationConfigurationArn" + "target": "NotificationConfigurationArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws:notifications::[0-9]{12}:configuration/[a-z0-9]{27})$", + "cloudformation": "^arn:[a-z-]{3,10}:notifications::[0-9]{12}:configuration/[a-z0-9]{27}$" + } + } } ], "operation": "AssociateChannel", @@ -34831,11 +36398,23 @@ "mappings": [ { "source": "arn", - "target": "Arn" + "target": "Arn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws:(chatbot|consoleapp|notifications-contacts):[a-zA-Z0-9-]*:[0-9]{12}:[a-zA-Z0-9-_.@]+/[a-zA-Z0-9/_.@:-]+)$", + "cloudformation": "^arn:[a-z-]{3,10}:(chatbot|consoleapp|notifications-contacts):[a-zA-Z0-9-]*:[0-9]{12}:[a-zA-Z0-9-_.@]+/[a-zA-Z0-9/_.@:-]+$" + } + } }, { "source": "notificationConfigurationArn", - "target": "NotificationConfigurationArn" + "target": "NotificationConfigurationArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws:notifications::[0-9]{12}:configuration/[a-z0-9]{27})$", + "cloudformation": "^arn:[a-z-]{3,10}:notifications::[0-9]{12}:configuration/[a-z0-9]{27}$" + } + } } ], "operation": "DisassociateChannel", @@ -34855,7 +36434,13 @@ }, { "source": "notificationConfigurationArn", - "target": "NotificationConfigurationArn" + "target": "NotificationConfigurationArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws:notifications::[0-9]{12}:configuration/[a-z0-9]{27})$", + "cloudformation": "^arn:[a-z-]{3,10}:notifications::[0-9]{12}:configuration/[a-z0-9]{27}$" + } + } }, { "source": "regions", @@ -34886,7 +36471,13 @@ }, { "source": "managedNotificationConfigurationArn", - "target": "ManagedNotificationConfigurationArn" + "target": "ManagedNotificationConfigurationArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:[-.a-z0-9]{1,63}:notifications::[0-9]{12}:managed-notification-configuration/category/[a-zA-Z0-9\\-]{3,64}/sub-category/[a-zA-Z0-9\\-]{3,64})$", + "cloudformation": "^arn:[a-z-]{3,10}:notifications::([0-9]{12}|):managed-notification-configuration/category/[a-zA-Z0-9\\-]{3,64}/sub-category/[a-zA-Z0-9\\-]{3,64}$" + } + } } ], "operation": "AssociateManagedNotificationAccountContact", @@ -34902,7 +36493,13 @@ }, { "source": "managedNotificationConfigurationArn", - "target": "ManagedNotificationConfigurationArn" + "target": "ManagedNotificationConfigurationArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:[-.a-z0-9]{1,63}:notifications::[0-9]{12}:managed-notification-configuration/category/[a-zA-Z0-9\\-]{3,64}/sub-category/[a-zA-Z0-9\\-]{3,64})$", + "cloudformation": "^arn:[a-z-]{3,10}:notifications::([0-9]{12}|):managed-notification-configuration/category/[a-zA-Z0-9\\-]{3,64}/sub-category/[a-zA-Z0-9\\-]{3,64}$" + } + } } ], "operation": "DisassociateManagedNotificationAccountContact", @@ -34914,11 +36511,23 @@ "mappings": [ { "source": "channelArn", - "target": "ChannelArn" + "target": "ChannelArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws:(chatbot|consoleapp|notifications-contacts):[a-zA-Z0-9-]*:[0-9]{12}:[a-zA-Z0-9-_.@]+/[a-zA-Z0-9/_.@:-]+)$", + "cloudformation": "^arn:[a-z-]{3,10}:(chatbot|consoleapp|notifications-contacts):[a-zA-Z0-9-]*:[0-9]{12}:[a-zA-Z0-9-_.@]+/[a-zA-Z0-9/_.@:-]+$" + } + } }, { "source": "managedNotificationConfigurationArn", - "target": "ManagedNotificationConfigurationArn" + "target": "ManagedNotificationConfigurationArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:[-.a-z0-9]{1,63}:notifications::[0-9]{12}:managed-notification-configuration/category/[a-zA-Z0-9\\-]{3,64}/sub-category/[a-zA-Z0-9\\-]{3,64})$", + "cloudformation": "^arn:[a-z-]{3,10}:notifications::([0-9]{12}|):managed-notification-configuration/category/[a-zA-Z0-9\\-]{3,64}/sub-category/[a-zA-Z0-9\\-]{3,64}$" + } + } } ], "operation": "AssociateManagedNotificationAdditionalChannel", @@ -34930,11 +36539,23 @@ "mappings": [ { "source": "channelArn", - "target": "ChannelArn" + "target": "ChannelArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws:(chatbot|consoleapp|notifications-contacts):[a-zA-Z0-9-]*:[0-9]{12}:[a-zA-Z0-9-_.@]+/[a-zA-Z0-9/_.@:-]+)$", + "cloudformation": "^arn:[a-z-]{3,10}:(chatbot|consoleapp|notifications-contacts):[a-zA-Z0-9-]*:[0-9]{12}:[a-zA-Z0-9-_.@]+/[a-zA-Z0-9/_.@:-]+$" + } + } }, { "source": "managedNotificationConfigurationArn", - "target": "ManagedNotificationConfigurationArn" + "target": "ManagedNotificationConfigurationArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:[-.a-z0-9]{1,63}:notifications::[0-9]{12}:managed-notification-configuration/category/[a-zA-Z0-9\\-]{3,64}/sub-category/[a-zA-Z0-9\\-]{3,64})$", + "cloudformation": "^arn:[a-z-]{3,10}:notifications::([0-9]{12}|):managed-notification-configuration/category/[a-zA-Z0-9\\-]{3,64}/sub-category/[a-zA-Z0-9\\-]{3,64}$" + } + } } ], "operation": "DisassociateManagedNotificationAdditionalChannel", @@ -34984,11 +36605,23 @@ "mappings": [ { "source": "notificationConfigurationArn", - "target": "NotificationConfigurationArn" + "target": "NotificationConfigurationArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws:notifications::[0-9]{12}:configuration/[a-z0-9]{27})$", + "cloudformation": "^arn:[a-z-]{3,10}:notifications::[0-9]{12}:configuration/[a-z0-9]{27}$" + } + } }, { "source": "organizationalUnitId", - "target": "OrganizationalUnitId" + "target": "OrganizationalUnitId", + "unrepresentable": { + "pattern": { + "api": "^(?:(Root|r-[0-9a-z]{4,32}|ou-[0-9a-z]{4,32}-[a-z0-9]{8,32}))$", + "cloudformation": "^(r-[0-9a-z]{4,32})|(ou-[0-9a-z]{4,32}-[a-z0-9]{8,32})$" + } + } } ], "operation": "AssociateOrganizationalUnit", @@ -35000,11 +36633,23 @@ "mappings": [ { "source": "notificationConfigurationArn", - "target": "NotificationConfigurationArn" + "target": "NotificationConfigurationArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws:notifications::[0-9]{12}:configuration/[a-z0-9]{27})$", + "cloudformation": "^arn:[a-z-]{3,10}:notifications::[0-9]{12}:configuration/[a-z0-9]{27}$" + } + } }, { "source": "organizationalUnitId", - "target": "OrganizationalUnitId" + "target": "OrganizationalUnitId", + "unrepresentable": { + "pattern": { + "api": "^(?:(Root|r-[0-9a-z]{4,32}|ou-[0-9a-z]{4,32}-[a-z0-9]{8,32}))$", + "cloudformation": "^(r-[0-9a-z]{4,32})|(ou-[0-9a-z]{4,32}-[a-z0-9]{8,32})$" + } + } } ], "operation": "DisassociateOrganizationalUnit", @@ -35486,7 +37131,13 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9_\\.\\-]{1,255}$)$", + "cloudformation": "^[a-zA-Z0-9_.-]+$" + } + } } ], "operation": "CreateSink", @@ -35505,7 +37156,13 @@ "mappings": [ { "source": "RuleName", - "target": "RuleName" + "target": "RuleName", + "unrepresentable": { + "pattern": { + "api": "^(?:[0-9A-Za-z-_.#/]+)$", + "cloudformation": "^[0-9A-Za-z-]+$" + } + } }, { "source": "Tags", @@ -35521,7 +37178,13 @@ "mappings": [ { "source": "RuleName", - "target": "RuleName" + "target": "RuleName", + "unrepresentable": { + "pattern": { + "api": "^(?:[0-9A-Za-z-_.#/]+)$", + "cloudformation": "^[0-9A-Za-z-]+$" + } + } }, { "source": "Tags", @@ -35560,7 +37223,13 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:.*[a-z][a-z0-9\\-]+.*)$", + "cloudformation": "[a-z][a-z0-9\\-]+" + } + } }, { "source": "Tags", @@ -35576,7 +37245,13 @@ "mappings": [ { "source": "RuleName", - "target": "RuleName" + "target": "RuleName", + "unrepresentable": { + "pattern": { + "api": "^(?:[0-9A-Za-z-_.#/]+)$", + "cloudformation": "^[0-9A-Za-z-]+$" + } + } }, { "source": "Tags", @@ -36048,11 +37723,23 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9-]+)$", + "cloudformation": "^[a-z][a-z0-9-]{2,31}$" + } + } }, { "source": "policy", - "target": "Policy" + "target": "Policy", + "unrepresentable": { + "pattern": { + "api": "^(?:.*[\\u0009\\u000A\\u000D\\u0020-\\u007E\\u00A1-\\u00FF]+.*)$", + "cloudformation": "[\\u0009\\u000A\\u000D\\u0020-\\u007E\\u00A1-\\u00FF]+" + } + } }, { "source": "type", @@ -36071,7 +37758,13 @@ "mappings": [ { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9-]+)$", + "cloudformation": "^[a-z][a-z0-9-]{2,31}$" + } + } }, { "source": "type", @@ -36102,7 +37795,13 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9-]+)$", + "cloudformation": "^[a-z][a-z0-9-]{2,63}$" + } + } }, { "source": "standbyReplicas", @@ -36143,7 +37842,13 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9-]+)$", + "cloudformation": "^[a-z][a-z0-9-]{2,31}$" + } + } }, { "source": "standbyReplicas", @@ -36212,7 +37917,13 @@ }, { "source": "policy", - "target": "Policy" + "target": "Policy", + "unrepresentable": { + "pattern": { + "api": "^(?:.*[\\u0009\\u000A\\u000D\\u0020-\\u007E\\u00A1-\\u00FF]+.*)$", + "cloudformation": "[\\u0009\\u000A\\u000D\\u0020-\\u007E\\u00A1-\\u00FF]+" + } + } }, { "source": "type", @@ -36254,7 +37965,13 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9-]+)$", + "cloudformation": "^[a-z][a-z0-9-]{2,31}$" + } + } }, { "source": "type", @@ -36290,11 +38007,23 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9-]+)$", + "cloudformation": "^[a-z][a-z0-9-]{2,31}$" + } + } }, { "source": "policy", - "target": "Policy" + "target": "Policy", + "unrepresentable": { + "pattern": { + "api": "^(?:.*[\\u0009\\u000A\\u000D\\u0020-\\u007E\\u00A1-\\u00FF]+.*)$", + "cloudformation": "[\\u0009\\u000A\\u000D\\u0020-\\u007E\\u00A1-\\u00FF]+" + } + } }, { "source": "type", @@ -36313,7 +38042,13 @@ "mappings": [ { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9-]+)$", + "cloudformation": "^[a-z][a-z0-9-]{2,31}$" + } + } }, { "source": "type", @@ -36332,7 +38067,13 @@ "mappings": [ { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-z][a-z0-9-]+)$", + "cloudformation": "^[a-z][a-z0-9-]{2,31}$" + } + } }, { "source": "securityGroupIds", @@ -36579,11 +38320,23 @@ "mappings": [ { "source": "ConnectorArn", - "target": "ConnectorArn" + "target": "ConnectorArn", + "unrepresentable": { + "pattern": { + "api": "^(?:^arn:[\\w-]+:pca-connector-ad:[\\w-]+:[0-9]+:connector\\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$)$", + "cloudformation": "^arn:[\\w-]+:pca-connector-ad:[\\w-]+:[0-9]+:connector(\\/[\\w-]+)$" + } + } }, { "source": "DirectoryRegistrationArn", - "target": "DirectoryRegistrationArn" + "target": "DirectoryRegistrationArn", + "unrepresentable": { + "pattern": { + "api": "^(?:^arn:[\\w-]+:pca-connector-ad:[\\w-]+:[0-9]+:directory-registration\\/d-[0-9a-f]{10}$)$", + "cloudformation": "^arn:[\\w-]+:pca-connector-ad:[\\w-]+:[0-9]+:directory-registration(\\/[\\w-]+)$" + } + } } ], "operation": "CreateServicePrincipalName", @@ -36595,11 +38348,23 @@ "mappings": [ { "source": "ConnectorArn", - "target": "ConnectorArn" + "target": "ConnectorArn", + "unrepresentable": { + "pattern": { + "api": "^(?:^arn:[\\w-]+:pca-connector-ad:[\\w-]+:[0-9]+:connector\\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$)$", + "cloudformation": "^arn:[\\w-]+:pca-connector-ad:[\\w-]+:[0-9]+:connector(\\/[\\w-]+)$" + } + } }, { "source": "DirectoryRegistrationArn", - "target": "DirectoryRegistrationArn" + "target": "DirectoryRegistrationArn", + "unrepresentable": { + "pattern": { + "api": "^(?:^arn:[\\w-]+:pca-connector-ad:[\\w-]+:[0-9]+:directory-registration\\/d-[0-9a-f]{10}$)$", + "cloudformation": "^arn:[\\w-]+:pca-connector-ad:[\\w-]+:[0-9]+:directory-registration(\\/[\\w-]+)$" + } + } } ], "operation": "DeleteServicePrincipalName", @@ -36648,7 +38413,13 @@ }, { "source": "TemplateArn", - "target": "TemplateArn" + "target": "TemplateArn", + "unrepresentable": { + "pattern": { + "api": "^(?:^arn:[\\w-]+:pca-connector-ad:[\\w-]+:[0-9]+:connector\\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}\\/template\\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$)$", + "cloudformation": "^arn:[\\w-]+:pca-connector-ad:[\\w-]+:[0-9]+:connector(\\/[\\w-]+)\\/template(\\/[\\w-]+)$" + } + } } ], "operation": "CreateTemplateGroupAccessControlEntry", @@ -36664,7 +38435,13 @@ }, { "source": "TemplateArn", - "target": "TemplateArn" + "target": "TemplateArn", + "unrepresentable": { + "pattern": { + "api": "^(?:^arn:[\\w-]+:pca-connector-ad:[\\w-]+:[0-9]+:connector\\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}\\/template\\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$)$", + "cloudformation": "^arn:[\\w-]+:pca-connector-ad:[\\w-]+:[0-9]+:connector(\\/[\\w-]+)\\/template(\\/[\\w-]+)$" + } + } } ], "operation": "DeleteTemplateGroupAccessControlEntry", @@ -36756,7 +38533,13 @@ }, { "source": "iamInstanceProfileArn", - "target": "IamInstanceProfileArn" + "target": "IamInstanceProfileArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws([a-zA-Z-]{0,10})?:iam::[0-9]{12}:instance-profile/([!-~]{1,510}/)?([\\w+=,.@-]{1,128}))$", + "cloudformation": "^arn:aws([a-zA-Z-]{0,10})?:iam::[0-9]{12}:instance-profile/.{1,128}$" + } + } }, { "source": "purchaseOption", @@ -37068,7 +38851,13 @@ }, { "source": "Enrichment", - "target": "Enrichment" + "target": "Enrichment", + "unrepresentable": { + "pattern": { + "api": "^(?:$|arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\\-]+):([a-z]{2,4}((-gov)|(-de)|(-iso([a-z]?)))?-[a-z]+(-\\d{1})?)?:(\\d{12})?:(.+))$", + "cloudformation": "^$|arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\\-]+):([a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1})?:(\\d{12})?:(.+)$" + } + } }, { "source": "KmsKeyIdentifier", @@ -37084,11 +38873,23 @@ }, { "source": "Source", - "target": "Source" + "target": "Source", + "unrepresentable": { + "pattern": { + "api": "^(?:smk://(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9]):[0-9]{1,5}|arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\\-]+):([a-z]{2,4}((-gov)|(-de)|(-iso([a-z]?)))?-[a-z]+(-\\d{1})?)?:(\\d{12})?:(.+))$", + "cloudformation": "^smk://(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9]):[0-9]{1,5}|arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\\-]+):([a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1})?:(\\d{12})?:(.+)$" + } + } }, { "source": "Target", - "target": "Target" + "target": "Target", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\\-]+):([a-z]{2,4}((-gov)|(-de)|(-iso([a-z]?)))?-[a-z]+(-\\d{1})?)?:(\\d{12})?:(.+))$", + "cloudformation": "^arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\\-]+):([a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1})?:(\\d{12})?:(.+)$" + } + } } ], "operation": "CreatePipe", @@ -37291,11 +39092,23 @@ }, { "source": "iamIdentityProviderArn", - "target": "IamIdentityProviderArn" + "target": "IamIdentityProviderArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:[a-z0-9-\\.]{1,63}:iam::\\d{12}:(oidc-provider|saml-provider)/[a-zA-Z0-9_\\.\\/@\\-]+)$", + "cloudformation": "^arn:(aws|aws-us-gov|aws-cn|aws-iso|aws-iso-b):iam::\\d{12}:(oidc-provider|saml-provider)/[a-zA-Z0-9_\\.\\/@\\-]+$" + } + } }, { "source": "identityCenterInstanceArn", - "target": "IdentityCenterInstanceArn" + "target": "IdentityCenterInstanceArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:[a-z0-9-\\.]{1,63}:sso:::instance/(sso)?ins-[a-zA-Z0-9-.]{16})$", + "cloudformation": "^arn:(aws|aws-us-gov|aws-cn|aws-iso|aws-iso-b):sso:::instance/(sso)?ins-[a-zA-Z0-9-.]{16}$" + } + } }, { "source": "identityType", @@ -37333,7 +39146,13 @@ }, { "source": "principal", - "target": "Principal" + "target": "Principal", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:[a-z0-9-\\.]{1,63}:iam::[0-9]{12}:role/[a-zA-Z0-9_/+=,.@-]+)$", + "cloudformation": "^arn:aws:iam::[0-9]{12}:role/[a-zA-Z0-9_/+=,.@-]+$" + } + } } ], "operation": "CreateDataAccessor", @@ -37455,7 +39274,13 @@ }, { "source": "principal", - "target": "Principal" + "target": "Principal", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:[a-z0-9-\\.]{1,63}:iam::[0-9]{12}:role/[a-zA-Z0-9_/+=,.@-]+)$", + "cloudformation": "^arn:aws:iam::[0-9]{12}:role/[a-zA-Z0-9_/+=,.@-]+$" + } + } }, { "source": "statementId", @@ -37578,7 +39403,11 @@ "target": "Origins", "unrepresentable": { "items": { - "max_length": 64 + "max_length": 64, + "pattern": { + "api": "^(?:(http://|https://)[a-zA-Z0-9-_.]+(?::[0-9]{1,5})?)$", + "cloudformation": "^(http:\\/\\/|https:\\/\\/)[a-zA-Z0-9-_.]+(?::[0-9]{1,5})?$" + } } } }, @@ -38625,7 +40454,11 @@ "source": "GlobalClusterIdentifier", "target": "GlobalClusterIdentifier", "unrepresentable": { - "max_length": 63 + "max_length": 63, + "pattern": { + "api": "^(?:[A-Za-z][0-9A-Za-z-:._]*)$", + "cloudformation": "^$|^[a-zA-Z]{1}(?:-?[a-zA-Z0-9]){0,62}$" + } } }, { @@ -39042,7 +40875,13 @@ "mappings": [ { "source": "DBProxyName", - "target": "DBProxyName" + "target": "DBProxyName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z](?:-?[a-zA-Z0-9]+)*)$", + "cloudformation": "[0-z]*" + } + } }, { "source": "DebugLogging", @@ -39094,7 +40933,13 @@ "mappings": [ { "source": "DBProxyName", - "target": "DBProxyName" + "target": "DBProxyName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z](?:-?[a-zA-Z0-9]+)*)$", + "cloudformation": "[0-z]*" + } + } } ], "operation": "DeleteDBProxy", @@ -39106,11 +40951,23 @@ "mappings": [ { "source": "DBProxyEndpointName", - "target": "DBProxyEndpointName" + "target": "DBProxyEndpointName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z](?:-?[a-zA-Z0-9]+)*)$", + "cloudformation": "[0-z]*" + } + } }, { "source": "DBProxyName", - "target": "DBProxyName" + "target": "DBProxyName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z](?:-?[a-zA-Z0-9]+)*)$", + "cloudformation": "[0-z]*" + } + } }, { "source": "EndpointNetworkType", @@ -39138,7 +40995,13 @@ "mappings": [ { "source": "DBProxyEndpointName", - "target": "DBProxyEndpointName" + "target": "DBProxyEndpointName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z](?:-?[a-zA-Z0-9]+)*)$", + "cloudformation": "[0-z]*" + } + } } ], "operation": "DeleteDBProxyEndpoint", @@ -39158,7 +41021,13 @@ }, { "source": "DBProxyName", - "target": "DBProxyName" + "target": "DBProxyName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z](?:-?[a-zA-Z0-9]+)*)$", + "cloudformation": "[A-z][0-z]*" + } + } }, { "source": "TargetGroupName", @@ -39182,7 +41051,13 @@ }, { "source": "DBProxyName", - "target": "DBProxyName" + "target": "DBProxyName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z](?:-?[a-zA-Z0-9]+)*)$", + "cloudformation": "[A-z][0-z]*" + } + } }, { "source": "TargetGroupName", @@ -39364,7 +41239,11 @@ "source": "GlobalClusterIdentifier", "target": "GlobalClusterIdentifier", "unrepresentable": { - "max_length": 63 + "max_length": 63, + "pattern": { + "api": "^(?:[A-Za-z][0-9A-Za-z-:._]*)$", + "cloudformation": "^[a-zA-Z]{1}(?:-?[a-zA-Z0-9]){0,62}$" + } } }, { @@ -39387,7 +41266,11 @@ "source": "GlobalClusterIdentifier", "target": "GlobalClusterIdentifier", "unrepresentable": { - "max_length": 63 + "max_length": 63, + "pattern": { + "api": "^(?:[A-Za-z][0-9A-Za-z-:._]*)$", + "cloudformation": "^[a-zA-Z]{1}(?:-?[a-zA-Z0-9]){0,62}$" + } } } ], @@ -39688,7 +41571,13 @@ }, { "source": "domainName", - "target": "DomainName" + "target": "DomainName", + "unrepresentable": { + "pattern": { + "api": "^(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\\.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))+)$", + "cloudformation": "^(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(?:\\.(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?))+$" + } + } }, { "source": "gatewayType", @@ -39752,7 +41641,13 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:(?!\\.)[\\.\\-_#A-Za-z0-9]+)$", + "cloudformation": "[\\.\\-_/#A-Za-z0-9]+" + } + } }, { "source": "Platform", @@ -39772,7 +41667,13 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:(?!\\.)[\\.\\-_#A-Za-z0-9]+)$", + "cloudformation": "[\\.\\-_/#A-Za-z0-9]+" + } + } } ], "operation": "DeleteAppMonitor", @@ -40338,7 +42239,13 @@ }, { "source": "namespaceName", - "target": "NamespaceName" + "target": "NamespaceName", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-z0-9-]+$)$", + "cloudformation": "^(?=^[a-z0-9-]+$).{3,64}$" + } + } }, { "source": "port", @@ -40362,7 +42269,13 @@ }, { "source": "workgroupName", - "target": "WorkgroupName" + "target": "WorkgroupName", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-z0-9-]+$)$", + "cloudformation": "^(?=^[a-z0-9-]+$).{3,64}$" + } + } } ], "operation": "CreateWorkgroup", @@ -40374,7 +42287,13 @@ "mappings": [ { "source": "workgroupName", - "target": "WorkgroupName" + "target": "WorkgroupName", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-z0-9-]+$)$", + "cloudformation": "^(?=^[a-z0-9-]+$).{3,64}$" + } + } } ], "operation": "DeleteWorkgroup", @@ -40389,7 +42308,13 @@ "mappings": [ { "source": "EnvironmentIdentifier", - "target": "EnvironmentIdentifier" + "target": "EnvironmentIdentifier", + "unrepresentable": { + "pattern": { + "api": "^(?:^env-[0-9A-Za-z]{10}$)$", + "cloudformation": "^env-([0-9A-Za-z]{10}$)" + } + } }, { "source": "Name", @@ -40417,7 +42342,13 @@ "mappings": [ { "source": "EnvironmentIdentifier", - "target": "EnvironmentIdentifier" + "target": "EnvironmentIdentifier", + "unrepresentable": { + "pattern": { + "api": "^(?:^env-[0-9A-Za-z]{10}$)$", + "cloudformation": "^env-([0-9A-Za-z]{10}$)" + } + } } ], "operation": "DeleteApplication", @@ -40466,11 +42397,23 @@ "mappings": [ { "source": "ApplicationIdentifier", - "target": "ApplicationIdentifier" + "target": "ApplicationIdentifier", + "unrepresentable": { + "pattern": { + "api": "^(?:^app-[0-9A-Za-z]{10}$)$", + "cloudformation": "^app-([0-9A-Za-z]{10}$)" + } + } }, { "source": "EnvironmentIdentifier", - "target": "EnvironmentIdentifier" + "target": "EnvironmentIdentifier", + "unrepresentable": { + "pattern": { + "api": "^(?:^env-[0-9A-Za-z]{10}$)$", + "cloudformation": "^env-([0-9A-Za-z]{10}$)" + } + } }, { "source": "RouteType", @@ -40478,7 +42421,13 @@ }, { "source": "ServiceIdentifier", - "target": "ServiceIdentifier" + "target": "ServiceIdentifier", + "unrepresentable": { + "pattern": { + "api": "^(?:^svc-[0-9A-Za-z]{10}$)$", + "cloudformation": "^svc-([0-9A-Za-z]{10}$)" + } + } }, { "source": "Tags", @@ -40494,11 +42443,23 @@ "mappings": [ { "source": "ApplicationIdentifier", - "target": "ApplicationIdentifier" + "target": "ApplicationIdentifier", + "unrepresentable": { + "pattern": { + "api": "^(?:^app-[0-9A-Za-z]{10}$)$", + "cloudformation": "^app-([0-9A-Za-z]{10}$)" + } + } }, { "source": "EnvironmentIdentifier", - "target": "EnvironmentIdentifier" + "target": "EnvironmentIdentifier", + "unrepresentable": { + "pattern": { + "api": "^(?:^env-[0-9A-Za-z]{10}$)$", + "cloudformation": "^env-([0-9A-Za-z]{10}$)" + } + } } ], "operation": "DeleteRoute", @@ -40513,7 +42474,13 @@ "mappings": [ { "source": "ApplicationIdentifier", - "target": "ApplicationIdentifier" + "target": "ApplicationIdentifier", + "unrepresentable": { + "pattern": { + "api": "^(?:^app-[0-9A-Za-z]{10}$)$", + "cloudformation": "^app-([0-9A-Za-z]{10}$)" + } + } }, { "source": "Description", @@ -40525,7 +42492,13 @@ }, { "source": "EnvironmentIdentifier", - "target": "EnvironmentIdentifier" + "target": "EnvironmentIdentifier", + "unrepresentable": { + "pattern": { + "api": "^(?:^env-[0-9A-Za-z]{10}$)$", + "cloudformation": "^env-([0-9A-Za-z]{10}$)" + } + } }, { "source": "Name", @@ -40549,11 +42522,23 @@ "mappings": [ { "source": "ApplicationIdentifier", - "target": "ApplicationIdentifier" + "target": "ApplicationIdentifier", + "unrepresentable": { + "pattern": { + "api": "^(?:^app-[0-9A-Za-z]{10}$)$", + "cloudformation": "^app-([0-9A-Za-z]{10}$)" + } + } }, { "source": "EnvironmentIdentifier", - "target": "EnvironmentIdentifier" + "target": "EnvironmentIdentifier", + "unrepresentable": { + "pattern": { + "api": "^(?:^env-[0-9A-Za-z]{10}$)$", + "cloudformation": "^env-([0-9A-Za-z]{10}$)" + } + } } ], "operation": "DeleteService", @@ -40565,7 +42550,13 @@ "mappings": [ { "source": "CollectionId", - "target": "CollectionId" + "target": "CollectionId", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9_.\\-]+)$", + "cloudformation": "\\A[a-zA-Z0-9_\\.\\-]+$" + } + } }, { "source": "Tags", @@ -40581,7 +42572,13 @@ "mappings": [ { "source": "CollectionId", - "target": "CollectionId" + "target": "CollectionId", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9_.\\-]+)$", + "cloudformation": "\\A[a-zA-Z0-9_\\.\\-]+$" + } + } } ], "operation": "DeleteCollection", @@ -40620,7 +42617,13 @@ "mappings": [ { "source": "ProjectName", - "target": "ProjectName" + "target": "ProjectName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9_.\\-]+)$", + "cloudformation": "[a-zA-Z0-9][a-zA-Z0-9_\\-]*" + } + } }, { "source": "Tags", @@ -40651,7 +42654,13 @@ }, { "source": "RoleArn", - "target": "RoleArn" + "target": "RoleArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws:iam::\\d{12}:role/?[a-zA-Z_0-9+=,.@\\-_/]+)$", + "cloudformation": "arn:aws(-[\\w]+)*:iam::[0-9]{12}:role/.*" + } + } }, { "source": "Tags", @@ -40841,7 +42850,13 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[A-Za-z0-9][A-Za-z0-9 _\\-]{1,59})$", + "cloudformation": "^[A-Za-z0-9][A-Za-z0-9_\\-]{1,59}$" + } + } }, { "source": "serviceArn", @@ -41005,7 +43020,11 @@ "source": "Group", "target": "Group", "unrepresentable": { - "min_length": 12 + "min_length": 12, + "pattern": { + "api": "^(?:[a-zA-Z0-9_\\.-]{1,300}|[a-zA-Z0-9_\\.-]{1,150}/[a-z0-9]{26}|arn:aws(-[a-z]+)*:resource-groups:[a-z]{2}(-[a-z]+)+-\\d{1}:[0-9]{12}:group/([a-zA-Z0-9_\\.-]{1,300}|[a-zA-Z0-9_\\.-]{1,150}/[a-z0-9]{26}))$", + "cloudformation": "([a-zA-Z0-9_\\\\.-]{1,150}/[a-z0-9]{26})|(arn:aws(-[a-z]+)*:resource-groups(-(test|beta|gamma))?:[a-z]{2}(-[a-z]+)+-\\d{1}:[0-9]{12}:group/[a-zA-Z0-9_\\\\.-]{1,150}/[a-z0-9]{26})" + } } }, { @@ -41253,7 +43272,13 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:(?!^[0-9]+$)([a-zA-Z0-9-_/' ']+))$", + "cloudformation": "(?!^[0-9]+$)([a-zA-Z0-9-_' ']+)" + } + } }, { "source": "protocol", @@ -41291,7 +43316,13 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:(?!^[0-9]+$)([a-zA-Z0-9-_/' ']+))$", + "cloudformation": "(?!^[0-9]+$)([a-zA-Z0-9-_' ']+)" + } + } }, { "source": "tags", @@ -41341,7 +43372,13 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:(?!^[0-9]+$)([a-zA-Z0-9-_/' ']+))$", + "cloudformation": "(?!^[0-9]+$)([a-zA-Z0-9-_' ']+)" + } + } }, { "source": "tags", @@ -41379,7 +43416,13 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:(?!^[0-9]+$)([a-zA-Z0-9-_/' ']+))$", + "cloudformation": "(?!^[0-9]+$)([a-zA-Z0-9-_' ']+)" + } + } }, { "source": "tags", @@ -41413,7 +43456,13 @@ }, { "source": "blockOverrideDomain", - "target": "BlockOverrideDomain" + "target": "BlockOverrideDomain", + "unrepresentable": { + "pattern": { + "api": "^(?:\\*?[a-zA-Z0-9!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~-]+)$", + "cloudformation": "\\*?[-a-zA-Z0-9.]+" + } + } }, { "source": "blockOverrideTtl", @@ -41449,7 +43498,13 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:(?!^[0-9]+$)([a-zA-Z0-9-_/' ']+))$", + "cloudformation": "(?!^[0-9]+$)([a-zA-Z0-9-_' ']+)" + } + } }, { "source": "priority", @@ -41491,7 +43546,13 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:(?!^[0-9]+$)([a-zA-Z0-9-_/' ']+))$", + "cloudformation": "(?!^[0-9]+$)([a-zA-Z0-9-_' ']+)" + } + } }, { "source": "observabilityRegion", @@ -41534,7 +43595,13 @@ }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:(?!^[0-9]+$)([a-zA-Z0-9-_/' ']+))$", + "cloudformation": "(?!^[0-9]+$)([a-zA-Z0-9-_' ']+)" + } + } }, { "source": "resourceArn", @@ -42317,7 +44384,13 @@ "mappings": [ { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9\\-\\_]+)$", + "cloudformation": "^[a-zA-Z0-9\\-_]+$" + } + } } ], "operation": "DeleteStorageLensGroup", @@ -43250,7 +45323,11 @@ "source": "OptOutListName", "target": "OptOutListName", "unrepresentable": { - "max_length": 64 + "max_length": 64, + "pattern": { + "api": "^(?:[A-Za-z0-9_:/-]+)$", + "cloudformation": "^[A-Za-z0-9_-]+$" + } } } ], @@ -43374,7 +45451,13 @@ "mappings": [ { "source": "ResourceArn", - "target": "ResourceArn" + "target": "ResourceArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:[A-Za-z0-9_:/-]+)$", + "cloudformation": "^arn:\\S+$" + } + } } ], "operation": "PutResourcePolicy", @@ -43386,7 +45469,13 @@ "mappings": [ { "source": "ResourceArn", - "target": "ResourceArn" + "target": "ResourceArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:[A-Za-z0-9_:/-]+)$", + "cloudformation": "^arn:\\S+$" + } + } } ], "operation": "DeleteResourcePolicy", @@ -43409,7 +45498,13 @@ }, { "source": "SenderId", - "target": "SenderId" + "target": "SenderId", + "unrepresentable": { + "pattern": { + "api": "^(?:[A-Za-z0-9_-]+)$", + "cloudformation": "^[A-Z0-9_-]+$" + } + } } ], "operation": "RequestSenderId", @@ -43425,7 +45520,13 @@ }, { "source": "SenderId", - "target": "SenderId" + "target": "SenderId", + "unrepresentable": { + "pattern": { + "api": "^(?:[A-Za-z0-9_:/-]+)$", + "cloudformation": "^[A-Z0-9_-]+$" + } + } } ], "operation": "ReleaseSenderId", @@ -43535,15 +45636,33 @@ }, { "source": "MaxConcurrency", - "target": "MaxConcurrency" + "target": "MaxConcurrency", + "unrepresentable": { + "pattern": { + "api": "^(?:^([1-9][0-9]*|[1-9][0-9]%|[1-9]%|100%)$)$", + "cloudformation": "^([1-9][0-9]{0,6}|[1-9][0-9]%|[1-9]%|100%)$" + } + } }, { "source": "MaxErrors", - "target": "MaxErrors" + "target": "MaxErrors", + "unrepresentable": { + "pattern": { + "api": "^(?:^([1-9][0-9]*|[0]|[1-9][0-9]%|[0-9]%|100%)$)$", + "cloudformation": "^([1-9][0-9]{0,6}|[0]|[1-9][0-9]%|[0-9]%|100%)$" + } + } }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9_\\-.:/]{3,128}$)$", + "cloudformation": "^[a-zA-Z0-9_\\-.:/]{3,200}$" + } + } }, { "source": "ScheduleExpression", @@ -43571,7 +45690,13 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9_\\-.:/]{3,128}$)$", + "cloudformation": "^[a-zA-Z0-9_\\-.:/]{3,200}$" + } + } } ], "operation": "DeleteAssociation", @@ -43583,7 +45708,13 @@ "mappings": [ { "source": "ConfigConnectorArn", - "target": "ConfigConnectorArn" + "target": "ConfigConnectorArn", + "unrepresentable": { + "pattern": { + "api": "^(?:^arn:aws(-cn|-us-gov)?:config:([^:]+):\\d{12}:connector/.+$)$", + "cloudformation": "^arn:aws(-cn|-us-gov)?:config:[a-z0-9-]+:\\d{12}:connector/.+$" + } + } }, { "source": "Description", @@ -44024,7 +46155,13 @@ "mappings": [ { "source": "Alias", - "target": "Alias" + "target": "Alias", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-z0-9_\\-]*$)$", + "cloudformation": "^[a-z0-9_\\-\\.]*$" + } + } }, { "source": "DisplayName", @@ -44057,7 +46194,13 @@ "mappings": [ { "source": "ContactId", - "target": "ContactId" + "target": "ContactId", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:(aws|aws-cn|aws-us-gov):ssm-contacts:[-\\w+=\\/,.@]*:[0-9]+:([\\w+=\\/,.@:-]+)*)$", + "cloudformation": "arn:[-\\w+=\\/,.@]+:[-\\w+=\\/,.@]+:[-\\w+=\\/,.@]*:[0-9]+:([\\w+=\\/,.@:-]+)*" + } + } }, { "source": "DeferActivation", @@ -44087,7 +46230,13 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9_\\-\\s\\.]*$)$", + "cloudformation": "^[a-zA-Z0-9_]*$" + } + } }, { "source": "TimeZoneId", @@ -44125,6 +46274,12 @@ "source": "tags", "target": "Tags", "unrepresentable": { + "tag_key": { + "pattern": { + "api": "^(?:^(?!aws:)[A-Za-z0-9 _=@:.+-/]+$)$", + "cloudformation": "^(?!aws:)[a-zA-Z+-=._:/]+$" + } + }, "tag_value": { "min_length": 1 } @@ -44160,18 +46315,34 @@ "target": "Engagements", "unrepresentable": { "items": { - "max_length": 1000 + "max_length": 1000, + "pattern": { + "api": "^(?:^arn:aws(-cn|-us-gov)?:ssm-contacts:[a-z0-9-]*:([0-9]{12}):contact/[a-z0-9_-]+$)$", + "cloudformation": "^arn:aws(-(cn|us-gov))?:ssm-contacts:(([a-z]+-)+[0-9])?:([0-9]{12})?:[^.]+$" + } } } }, { "source": "name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:^[a-zA-Z0-9-_]*$)$", + "cloudformation": "^[a-zA-Z0-9_-]*$" + } + } }, { "source": "tags", "target": "Tags", "unrepresentable": { + "tag_key": { + "pattern": { + "api": "^(?:^(?!aws:)[A-Za-z0-9 _=@:.+-/]+$)$", + "cloudformation": "^(?!aws:)[a-zA-Z+-=._:/]+$" + } + }, "tag_value": { "min_length": 1 } @@ -44198,7 +46369,13 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:^[ A-Za-z0-9._-]{0,120}$)$", + "cloudformation": "^[ A-Za-z0-9_-]{1,50}$" + } + } } ], "operation": "CreateConfigurationManager", @@ -44232,7 +46409,13 @@ }, { "source": "Name", - "target": "Name" + "target": "Name", + "unrepresentable": { + "pattern": { + "api": "^(?:[\\S\\s]*)$", + "cloudformation": "^[\\w+=,.@-]+$" + } + } }, { "source": "Status", @@ -44652,7 +46835,13 @@ "mappings": [ { "source": "ClusterName", - "target": "ClusterName" + "target": "ClusterName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9])*)$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62}$" + } + } }, { "source": "ClusterRole", @@ -44679,7 +46868,11 @@ "target": "ClusterName", "unrepresentable": { "max_length": 63, - "min_length": 1 + "min_length": 1, + "pattern": { + "api": "^(?:(arn:aws[a-z\\-]*:sagemaker:[a-z0-9\\-]*:[0-9]{12}:cluster/[a-z0-9]{12})|([a-zA-Z0-9](-*[a-zA-Z0-9]){0,62}))$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62}$" + } } } ], @@ -44724,7 +46917,13 @@ "mappings": [ { "source": "JobDefinitionName", - "target": "JobDefinitionName" + "target": "JobDefinitionName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9])*$" + } + } }, { "source": "RoleArn", @@ -44740,7 +46939,13 @@ "mappings": [ { "source": "JobDefinitionName", - "target": "JobDefinitionName" + "target": "JobDefinitionName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9])*$" + } + } } ], "operation": "DeleteDataQualityJobDefinition", @@ -44752,7 +46957,13 @@ "mappings": [ { "source": "DeviceFleetName", - "target": "DeviceFleetName" + "target": "DeviceFleetName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*_*[a-zA-Z0-9])*$" + } + } } ], "operation": "RegisterDevices", @@ -44764,7 +46975,13 @@ "mappings": [ { "source": "DeviceFleetName", - "target": "DeviceFleetName" + "target": "DeviceFleetName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*_*[a-zA-Z0-9])*$" + } + } } ], "operation": "DeregisterDevices", @@ -44776,11 +46993,23 @@ "mappings": [ { "source": "Description", - "target": "Description" + "target": "Description", + "unrepresentable": { + "pattern": { + "api": "^(?:[-a-zA-Z0-9_.,;:! ]*)$", + "cloudformation": "[\\S\\s]+" + } + } }, { "source": "DeviceFleetName", - "target": "DeviceFleetName" + "target": "DeviceFleetName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*_*[a-zA-Z0-9])*$" + } + } }, { "source": "RoleArn", @@ -44796,7 +47025,13 @@ "mappings": [ { "source": "DeviceFleetName", - "target": "DeviceFleetName" + "target": "DeviceFleetName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*_*[a-zA-Z0-9])*$" + } + } } ], "operation": "DeleteDeviceFleet", @@ -44828,7 +47063,13 @@ }, { "source": "KmsKeyId", - "target": "KmsKeyId" + "target": "KmsKeyId", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9:/_-]*)$", + "cloudformation": ".*" + } + } }, { "source": "SubnetIds", @@ -44974,15 +47215,33 @@ }, { "source": "EventTimeFeatureName", - "target": "EventTimeFeatureName" + "target": "EventTimeFeatureName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9]([-_]*[a-zA-Z0-9]){0,63})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,63}" + } + } }, { "source": "FeatureGroupName", - "target": "FeatureGroupName" + "target": "FeatureGroupName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9]([_-]*[a-zA-Z0-9]){0,63})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,63}" + } + } }, { "source": "RecordIdentifierFeatureName", - "target": "RecordIdentifierFeatureName" + "target": "RecordIdentifierFeatureName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9]([-_]*[a-zA-Z0-9]){0,63})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,63}" + } + } }, { "source": "RoleArn", @@ -44998,7 +47257,13 @@ "mappings": [ { "source": "FeatureGroupName", - "target": "FeatureGroupName" + "target": "FeatureGroupName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9]([_-]*[a-zA-Z0-9]){0,63})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,63}" + } + } } ], "operation": "DeleteFeatureGroup", @@ -45034,7 +47299,13 @@ "mappings": [ { "source": "HubName", - "target": "HubName" + "target": "HubName", + "unrepresentable": { + "pattern": { + "api": "^(?:(arn:[a-z0-9-\\.]{1,63}:sagemaker:\\w+(?:-\\w+)+:(\\d{12}|aws):hub\\/)?[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62}$" + } + } } ], "operation": "DeleteHub", @@ -45070,7 +47341,13 @@ "mappings": [ { "source": "ImageName", - "target": "ImageName" + "target": "ImageName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9]([-.]?[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9]([-.]?[a-zA-Z0-9])*$" + } + } } ], "operation": "CreateImage", @@ -45082,7 +47359,13 @@ "mappings": [ { "source": "ImageName", - "target": "ImageName" + "target": "ImageName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9]([-.]?[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9]([-.]?[a-zA-Z0-9])*$" + } + } } ], "operation": "DeleteImage", @@ -45101,7 +47384,13 @@ }, { "source": "BaseImage", - "target": "BaseImage" + "target": "BaseImage", + "unrepresentable": { + "pattern": { + "api": "^(?:.*)$", + "cloudformation": ".+" + } + } }, { "source": "Horovod", @@ -45109,7 +47398,13 @@ }, { "source": "ImageName", - "target": "ImageName" + "target": "ImageName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9]([-.]?[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[A-Za-z0-9]([-.]?[A-Za-z0-9])*$" + } + } }, { "source": "JobType", @@ -45149,7 +47444,13 @@ }, { "source": "ImageName", - "target": "ImageName" + "target": "ImageName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9]([-.]?[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[A-Za-z0-9]([-.]?[A-Za-z0-9])*$" + } + } } ], "operation": "DeleteImageVersion", @@ -45161,15 +47462,33 @@ "mappings": [ { "source": "EndpointName", - "target": "EndpointName" + "target": "EndpointName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9])*$" + } + } }, { "source": "InferenceComponentName", - "target": "InferenceComponentName" + "target": "InferenceComponentName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9]([\\-a-zA-Z0-9]*[a-zA-Z0-9])?)$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9])*$" + } + } }, { "source": "VariantName", - "target": "VariantName" + "target": "VariantName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9])*$" + } + } } ], "operation": "CreateInferenceComponent", @@ -45181,7 +47500,13 @@ "mappings": [ { "source": "InferenceComponentName", - "target": "InferenceComponentName" + "target": "InferenceComponentName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9]([\\-a-zA-Z0-9]*[a-zA-Z0-9])?)$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9])*$" + } + } } ], "operation": "DeleteInferenceComponent", @@ -45200,11 +47525,23 @@ }, { "source": "EndpointName", - "target": "EndpointName" + "target": "EndpointName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9])*" + } + } }, { "source": "KmsKey", - "target": "KmsKey" + "target": "KmsKey", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9:/_-]*)$", + "cloudformation": ".*" + } + } }, { "source": "Name", @@ -45277,7 +47614,11 @@ "source": "ArtifactStoreUri", "target": "ArtifactStoreUri", "unrepresentable": { - "min_length": 1 + "min_length": 1, + "pattern": { + "api": "^(?:(https|s3)://([^/]+)/?(.*))$", + "cloudformation": "^s3:\\/\\/([^\\/]+)\\/?(.*)$" + } } }, { @@ -45288,12 +47629,22 @@ "source": "MlflowVersion", "target": "MlflowVersion", "unrepresentable": { - "min_length": 1 + "min_length": 1, + "pattern": { + "api": "^(?:[0-9]*.[0-9]*.[0-9]*)$", + "cloudformation": "^\\d+(\\.\\d+)+$" + } } }, { "source": "RoleArn", - "target": "RoleArn" + "target": "RoleArn", + "unrepresentable": { + "pattern": { + "api": "^(?:arn:aws[a-z\\-]*:iam::\\d{12}:role/?[a-zA-Z_0-9+=,.@\\-_/]+)$", + "cloudformation": "^arn:aws[a-z\\-]*:iam::\\d{12}:role\\/?[a-zA-Z_0-9+=,.@\\-_\\/]+$" + } + } }, { "source": "TrackingServerName", @@ -45361,7 +47712,13 @@ "mappings": [ { "source": "JobDefinitionName", - "target": "JobDefinitionName" + "target": "JobDefinitionName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9])*$" + } + } }, { "source": "RoleArn", @@ -45377,7 +47734,13 @@ "mappings": [ { "source": "JobDefinitionName", - "target": "JobDefinitionName" + "target": "JobDefinitionName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9])*$" + } + } } ], "operation": "DeleteModelBiasJobDefinition", @@ -45417,7 +47780,13 @@ "mappings": [ { "source": "JobDefinitionName", - "target": "JobDefinitionName" + "target": "JobDefinitionName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9])*$" + } + } }, { "source": "RoleArn", @@ -45433,7 +47802,13 @@ "mappings": [ { "source": "JobDefinitionName", - "target": "JobDefinitionName" + "target": "JobDefinitionName", + "unrepresentable": { + "pattern": { + "api": "^(?:[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62})$", + "cloudformation": "^[a-zA-Z0-9](-*[a-zA-Z0-9])*$" + } + } } ], "operation": "DeleteModelExplainabilityJobDefinition", @@ -45502,7 +47877,11 @@ "source": "ModelPackageName", "target": "ModelPackageName", "unrepresentable": { - "max_length": 63 + "max_length": 63, + "pattern": { + "api": "^(?:(arn:aws[a-z\\-]*:sagemaker:[a-z0-9\\-]*:[0-9]{12}:[a-z\\-]*\\/)?([a-zA-Z0-9]([a-zA-Z0-9-]){0,62})(? Date: Fri, 11 Sep 2026 20:12:44 +0000 Subject: [PATCH 16/19] Update readmes --- README.md | 3 ++ src/bindings-go/README.md | 93 +++-------------------------------- src/bindings-jvm/README.md | 46 +---------------- src/bindings-python/README.md | 87 -------------------------------- src/bindings-wasm/README.md | 51 +++---------------- src/data-source/README.md | 12 +++-- src/validation-engine/API.md | 44 ++++++++--------- 7 files changed, 47 insertions(+), 289 deletions(-) diff --git a/README.md b/README.md index 707d46a2..ad221073 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,9 @@ JVM library (Kotlin/Java) - all backed by the same validation core. (`--additional-schema`, or `EngineConfig.schema_validator_config.additional_schemas` when embedding). - **Custom rules.** Extend validation with your own rules in CEL (JSON), Rego, or [CloudFormation Guard](https://docs.aws.amazon.com/cfn-guard/latest/ug/what-is-guard.html) DSL. +- **AWS CLI command validation.** Model a create or update API call as CloudFormation resource state and validate it + offline before it is sent; any call that cannot be modeled exactly is skipped, never guessed (see + [validation-engine/API.md](src/validation-engine/API.md#validating-an-aws-cli-command)). - **Embeddable everywhere.** Use it from the CLI, Rust, Node.js, Python, Go, or the JVM. - **Sub-second** validation for typical templates. diff --git a/src/bindings-go/README.md b/src/bindings-go/README.md index 9ff8240d..e64b2808 100644 --- a/src/bindings-go/README.md +++ b/src/bindings-go/README.md @@ -59,14 +59,13 @@ diagnostics for the same template and config. A `nil` config uses only the built external Rego and Guard rules with another, producing the same built-in diagnostics plus any external findings. See [CompositeEngineConfig](#compositeengineconfig). -| Method | Returns | Description | -|------------------------------------------------------------------------------|-------------------------------------|-------------------------------------------------------------------------------------------------| -| `ValidateTemplate(template []byte, config *ValidateConfig, filePath string)` | `(*ValidationReport, error)` | Validates bytes; `config.DetailLevel` selects the detail level; `filePath` labels the report. | -| `ValidateTemplateFile(path string, config *ValidateConfig)` | `(*ValidationReport, error)` | Reads a template from disk, then validates it | -| `ValidateAWSCLICommand(request AWSCLICommand, config *ValidateConfig)` | `(*AWSCLICommandValidation, error)` | Classifies and validates an AWS CLI command offline | -| `ListRules()` | `([]RuleInfo, error)` | Returns metadata for every built-in and loaded custom rule | -| `EngineName()` | `string` | `"rego"`, `"cel"`, or `"composite"` | -| `Destroy()` | - | Releases the native engine; the engine must not be used afterwards | +| Method | Returns | Description | +|------------------------------------------------------------------------------|------------------------------|-------------------------------------------------------------------------------------------------| +| `ValidateTemplate(template []byte, config *ValidateConfig, filePath string)` | `(*ValidationReport, error)` | Validates bytes; `config.DetailLevel` selects the detail level; `filePath` labels the report. | +| `ValidateTemplateFile(path string, config *ValidateConfig)` | `(*ValidationReport, error)` | Reads a template from disk, then validates it | +| `ListRules()` | `([]RuleInfo, error)` | Returns metadata for every built-in and loaded custom rule | +| `EngineName()` | `string` | `"rego"`, `"cel"`, or `"composite"` | +| `Destroy()` | - | Releases the native engine; the engine must not be used afterwards | Validation returns a `*ValidationReport`. `config.DetailLevel` selects the detail: `DETAILED` (the default when unset) enriches each diagnostic with documentation URLs, rule descriptions, phase tags, and `ViolationContext`, @@ -225,84 +224,6 @@ type PseudoParameterOverrides struct { } ``` -## AWS CLI Command Validation - -Validates an AWS CLI command by classifying the operation, inferring the CloudFormation resource type, and running -schema and rule validation against a synthesized template - entirely offline. The method returns classification -metadata and an optional `ValidationReport` when the request was validated (not skipped for read-only operations). The -report is projected at the `STANDARD` detail level: enrichment fields are nil because a synthesized template has no -user-authored source to annotate. - -```go -engine, _ := cfnvalidate.NewRegoEngine(nil) -defer engine.Destroy() - -result, err := engine.ValidateAWSCLICommand(cfnvalidate.AWSCLICommand{ - ServiceName: "s3", - OperationName: "CreateBucket", - Parameters: map[string]any{"Bucket": "my-bucket"}, - HTTPMethod: "PUT", -}, nil) -if err != nil { - log.Fatal(err) -} -fmt.Printf("Kind: %s Status: %s Types: %v\n", - result.OperationKind, result.Status, result.ResourceTypes) -if result.Report != nil { - for _, d := range result.Report.Diagnostics { - fmt.Printf(" [%s] %s: %s\n", d.Severity, d.RuleID, d.Message) - } -} -``` - -### AWSCLICommand - -```go -type AWSCLICommand struct { - ServiceName string // canonical botocore service name (e.g. "s3") - ASCII case-insensitive - OperationName string // operation name (e.g. "CreateBucket") - case-sensitive - Parameters map[string]any // request parameters: strings, numbers, booleans, []byte, maps, slices, nil - ServicePrefix string // optional signing prefix (e.g. "cloudcontrolapi") - HTTPMethod string // optional HTTP method hint for classification - IsReadOnly *bool // explicit read-only flag - skips validation when true -} -``` - -`Parameters` values are recursively encoded into the core's tagged value representation. Supported Go types: `nil`, -`bool`, all signed/unsigned integer widths, `float32`/`float64` (finite only), `string`, `[]byte` (as byte arrays), -`time.Time` (as an RFC 3339 UTC string), `json.Number`, slices/arrays, and `map[string]any`. Integer-valued -`json.Number` inputs are preserved across the full signed and unsigned 64-bit range; integer literals outside that range -are represented as unsupported rather than rounded through `float64`. SDK-defined type aliases (e.g. -`types.InstanceType` which is `type InstanceType string`) are handled transparently via their underlying kind. -Non-finite floats, maps with non-string keys, and unsupported types are represented as `UNSUPPORTED` rather than -coerced. - -The canonical `ServiceName` is authoritative; the optional `ServicePrefix` is context only and cannot override it. -`ServiceName` must be the exact canonical botocore service name, normalized only for ASCII case. The core does not -guess signing, endpoint, or punctuation aliases and never matches on substrings. Any caller, including a future AWS -SDK adapter in any language, must translate its native service identity to the canonical botocore `ServiceName` before -invoking this API. `TemplateBody` validation is restricted to CloudFormation operations that accept it, and -`TypeName`+`DesiredState` wrapping applies only to exact Cloud Control `CreateResource`. - -### AWSCLICommandValidation - -```go -type AWSCLICommandValidation struct { - OperationKind AWSCLIOperationKind // READ_ONLY, CLOUD_FORMATION_CREATE, etc. - Status AWSCLICommandValidationStatus // VALIDATED or SKIPPED - TemplateSource *AWSCLITemplateSource // TEMPLATE_BODY, SYNTHESIZED_CREATE, etc. - ResourceTypes []string // inferred CloudFormation resource types - Reason string // human-readable explanation - Report *ValidationReport // present only when Status is VALIDATED - Template []byte // exact validated/synthesized template bytes; nil when SKIPPED -} -``` - -`Template` carries the exact bytes that were validated - the caller's original `TemplateBody` without reserializing, or -the synthesized JSON template for adapter-mapped requests - so consumers can display the modeled template that produced -the diagnostics. It is nil when the request was skipped. The core serializes these bytes as a JSON integer array, which -the Go decoder converts back into a `[]byte`. - ## TemplateModel Parses a template into the resolved `SemanticModel` for direct inspection - the same model the engines evaluate rules diff --git a/src/bindings-jvm/README.md b/src/bindings-jvm/README.md index 5582b30c..7aae044f 100644 --- a/src/bindings-jvm/README.md +++ b/src/bindings-jvm/README.md @@ -13,8 +13,8 @@ All types live in the `software.amazon.cloudformation.validate` package. Available on [Maven Central](https://central.sonatype.com/artifact/software.amazon.cloudformation/cloudformation-validate) -as `software.amazon.cloudformation:cloudformation-validate`. The library requires Java 8 or later. Both snippets below -resolve the latest published version; substitute a specific version to pin one. +as `software.amazon.cloudformation:cloudformation-validate`. Both snippets below resolve the latest published version; +substitute a specific version to pin one. Gradle: @@ -75,48 +75,6 @@ interface Engine { `template` is a `java.io.File` - the engine reads the bytes and uses the file path for diagnostic source locations. -### AWS CLI command validation - -Use `validateAwsCliCommand` for an AWS CLI command rather than a complete template. The validator classifies -the operation, selects a CloudFormation resource type, models representable create/update state, and validates the -resulting template entirely offline: - -```kotlin -val result = RegoEngine().validateAwsCliCommand( - AwsCliCommand( - serviceName = "s3", - servicePrefix = "s3", - operationName = "CreateBucket", - httpMethod = "PUT", - parameters = mapOf( - "Bucket" to "example-bucket", - ), - ), -) - -result.report?.diagnostics?.forEach { diagnostic -> - println("${diagnostic.ruleId}: ${diagnostic.message}") -} ?: println("${result.status}: ${result.reason}") -``` - -`AwsCliCommand.parameters` accepts nested maps, iterables and arrays, scalars, byte arrays, and Java temporal values -without mutating the supplied map. `TemplateBody` bytes are validated exactly; `TemplateURL` is skipped because the -validator does not perform network requests. Every result reports `status`, `operationKind`, `templateSource`, -`resourceTypes`, and `reason`; skipped commands have a null `report`. -The same classes and methods are callable from Java with conventional generated getters. - -Operation-to-resource mapping uses a deterministic closed adapter catalog generated from each resource type's own -provider handler metadata and verified against botocore models and the compiled CloudFormation schemas: only -verified service+operation pairs produce inferred resource types and synthesized templates. Unregistered operations are classified as -`UNMAPPED_MUTATION` or `DATA_PLANE_MUTATION` with `SKIPPED` status and no inferred resource types. Cloud Control -`UpdateResource` and `DeleteResource` may echo a known `TypeName` supplied by the request, but never synthesize state. -The canonical `serviceName` is authoritative; the optional `servicePrefix` is context only and cannot override it. -`serviceName` must be the exact canonical botocore service name, normalized only for ASCII case. The core does not -guess signing, endpoint, or punctuation aliases and never matches on substrings. Any caller, including a future AWS -SDK adapter in any language, must translate its native service identity to the canonical botocore `serviceName` before -invoking this API. `TemplateBody` validation is restricted to CloudFormation operations that accept it, and -`TypeName`+`DesiredState` wrapping applies only to exact Cloud Control `CreateResource`. - ### `EngineConfig` Passed to the constructor. All fields default to empty lists. diff --git a/src/bindings-python/README.md b/src/bindings-python/README.md index 9a3168f7..c019409a 100644 --- a/src/bindings-python/README.md +++ b/src/bindings-python/README.md @@ -56,93 +56,6 @@ the same template and config. `template` is a file path (`str` / `os.PathLike`) or raw `bytes`; `config` is an optional `ValidateConfig`. -### AWS CLI command validation - -Use `validate_aws_cli_command` when the input is an AWS SDK-style request rather than a complete template. The -validator classifies the operation, selects a CloudFormation resource type, models representable create/update state, -and validates the resulting template entirely offline: - -```python -from cloudformation_validate import AwsCliCommand, RegoEngine - -engine = RegoEngine() -result = engine.validate_aws_cli_command( - AwsCliCommand( - service_name="s3", - service_prefix="s3", - operation_name="CreateBucket", - http_method="PUT", - parameters={"Bucket": "example-bucket"}, - ) -) - -if result.report is not None: - for diagnostic in result.report.diagnostics: - print(diagnostic.rule_id, diagnostic.message) -else: - print(result.status.name, result.reason) -``` - -`AwsCliCommand.parameters` accepts nested mappings and sequences, scalars, `bytes`, and `datetime.datetime` values -without mutating the supplied mapping. `TemplateBody` bytes are validated exactly; `TemplateURL` is skipped because the -validator does not perform network requests. The result always reports `status`, `operation_kind`, `template_source`, -`resource_types`, and `reason`; skipped requests have `report is None`. The `template` field carries the exact bytes -validated — the caller's original `TemplateBody` without reserializing, or the synthesized JSON for adapter-mapped -requests — so consumers can display the modeled template that produced the diagnostics. Skipped requests have -`template is None`. - -Operation-to-resource mapping uses a deterministic closed adapter catalog generated from each resource type's own -provider handler metadata and verified against botocore models and the compiled CloudFormation schemas: only -verified service+operation pairs produce inferred resource types and synthesized templates. Unregistered operations are classified as -`UNMAPPED_MUTATION` or `DATA_PLANE_MUTATION` with `SKIPPED` status and no inferred resource types. Cloud Control -`UpdateResource` and `DeleteResource` may echo a known `TypeName` supplied by the request, but never synthesize state. -The canonical `service_name` is authoritative; the optional `service_prefix` is context only and cannot override it. -`service_name` must be the exact canonical botocore service name, normalized only for ASCII case. The core does not -guess signing, endpoint, or punctuation aliases and never matches on substrings. Any caller, including a future AWS -SDK adapter in any language, must translate its native service identity to the canonical botocore `service_name` -before invoking this API. `TemplateBody` validation is restricted to CloudFormation operations that accept it, and -`TypeName`+`DesiredState` wrapping applies only to exact Cloud Control `CreateResource`. - -#### AWS CLI integration - -AWS CLI emits `provide-client-params..` before serializing or sending each request. Register a -handler on the CLI's botocore session to validate the exact parameter dictionary without making another network call: - -```python -from cloudformation_validate import AwsCliCommand, RegoEngine - -engine = RegoEngine() # construct once and reuse - - -def validate_create_stack(params, model, **_kwargs): - service = model.service_model - result = engine.validate_aws_cli_command( - AwsCliCommand( - service_name=service.service_name, - service_prefix=service.signing_name, - operation_name=model.name, - http_method=model.http.get("method"), - parameters=params, - ) - ) - if result.report is not None: - for diagnostic in result.report.diagnostics: - print(diagnostic.rule_id, diagnostic.message) - else: - print(result.status.name, result.reason) - - -# `session` is the botocore session owned by the AWS CLI driver or plugin. -session.register( - "provide-client-params.cloudformation.CreateStack", - validate_create_stack, -) -``` - -The callback receives the real botocore `OperationModel`, so it does not need a duplicate service model. For -`CreateStack` and `UpdateStack`, the request's `TemplateBody` string or bytes are validated exactly. A handler that -must prevent the API call can raise after applying its own policy to the returned diagnostics. - ### EngineConfig Passed to the constructor. All fields default to empty lists. diff --git a/src/bindings-wasm/README.md b/src/bindings-wasm/README.md index 585435e0..521c8c30 100644 --- a/src/bindings-wasm/README.md +++ b/src/bindings-wasm/README.md @@ -44,13 +44,12 @@ Guard rules on top of the built-in rules - see [`CompositeEngine`](#compositeeng ### `Engine` interface -| Method | Returns | Description | -|-------------------------------------------|---------------------------|------------------------------------------------------------------------------------------------------------------| -| `validateTemplate(template, config?)` | `ValidationReport` | Validates and returns diagnostics. `config.detailLevel` (default `DETAILED`) sets how much per-diagnostic context is attached: `DETAILED` adds documentation URLs, rule descriptions, phase tags, and `ViolationContext`; `STANDARD` omits those enrichment fields | -| `validateAwsCliCommand(request, config?)` | `AwsCliCommandValidation` | Classifies, models, and validates one AWS CLI command entirely offline | -| `listRules()` | `RuleInfo[]` | Returns metadata for every built-in and loaded custom rule | -| `engineName()` | `string` | `"rego"`, `"cel"`, or `"composite"` | -| `free()` | `void` | Releases the engine's off-heap memory | +| Method | Returns | Description | +|---------------------------------------|------------------|------------------------------------------------------------------------------------------------------------------| +| `validateTemplate(template, config?)` | `ValidationReport` | Validates and returns diagnostics. `config.detailLevel` (default `DETAILED`) sets how much per-diagnostic context is attached: `DETAILED` adds documentation URLs, rule descriptions, phase tags, and `ViolationContext`; `STANDARD` omits those enrichment fields | +| `listRules()` | `RuleInfo[]` | Returns metadata for every built-in and loaded custom rule | +| `engineName()` | `string` | `"rego"`, `"cel"`, or `"composite"` | +| `free()` | `void` | Releases the engine's off-heap memory | ### `EngineConfig` @@ -138,44 +137,6 @@ try { } ``` -## AWS CLI command validation - -`validateAwsCliCommand` classifies and validates an AWS CLI command against the same bundled CloudFormation schemas and -rules, without credentials or network access. It is available on every engine, including `CompositeEngine`: - -```typescript -import { AwsCliCommand, RegoEngine } from "@aws/cloudformation-validate"; - -const engine = new RegoEngine(); -try { - const request = new AwsCliCommand("s3", "CreateBucket", { - Bucket: "my-bucket", - }); - const validation = engine.validateAwsCliCommand(request); - console.log(validation.status, validation.resourceTypes); - if (validation.template !== null) { - console.log(Buffer.from(validation.template).toString("utf8")); - } -} finally { - engine.free(); -} -``` - -`serviceName` is the exact canonical botocore service name (for example, `"s3"`, `"cloudwatch"`, or -`"cloudformation"`) and is normalized for ASCII case only. `servicePrefix` is optional context and never overrides -`serviceName`. The core does not guess endpoint prefixes, signing names, punctuation variants, or other aliases. A -future SDK adapter in any language must translate its native SDK identity to this canonical name before calling the -API. - -Request parameters accept JavaScript strings, booleans, finite numbers, 64-bit `bigint` values, `null`, `Date`, nested -arrays and plain objects, and `Uint8Array` (including Node.js `Buffer`) for byte strings. Unsupported values are marked -explicitly rather than coerced. Synthesis is all-or-nothing: if any supplied resource-state field cannot be represented -or has no proven mapping, the result is `SKIPPED` with no report or template. - -`AwsCliCommandValidation.template` is a `Uint8Array | null`. For CloudFormation `TemplateBody`, it contains the exact -caller-provided bytes; synthesized requests contain the generated JSON template bytes. A `VALIDATED` result includes a -standard report, while a `SKIPPED` result explains why validation was not attempted. - ## ValidateConfig Controls filtering, severity, parameter overrides, and behavior. All fields optional - omitting the config or passing diff --git a/src/data-source/README.md b/src/data-source/README.md index 07f91e7a..f853320d 100644 --- a/src/data-source/README.md +++ b/src/data-source/README.md @@ -36,11 +36,13 @@ against the refreshed compiled schemas, failing with a request for `--aws-cli-ro exists or became read-only. A repository that has never generated a catalog must pass `--aws-cli-root`, because the build script embeds the catalog. -Besides the operation-to-type and parameter-to-property pairs, each mapping records the API value domain that the -compiled CloudFormation schema cannot represent (`unrepresentable`: enum members, numeric bounds, string lengths, list -sizes, tag key/value lengths, and the API/CloudFormation regex `pattern` pair when the two differ), derived by comparing -the botocore input shape against the compiled property schema. The -runtime uses it to skip synthesis for a command whose values the service accepts but CloudFormation would reject. +Besides the operation-to-type and parameter-to-property pairs, each mapping records the CloudFormation constraints +the API does not enforce at least as strictly (`unrepresentable`: API enum members CloudFormation rejects or the +CloudFormation enum when the API declares none, numeric bounds, string lengths, list and tag-map sizes, tag key/value +lengths, and the CloudFormation regex `pattern` paired with the anchored API pattern when the API declares a different +one), derived by comparing the botocore input shape against the compiled property schema. The runtime settles them per +value and skips synthesis for a command whose value CloudFormation would reject without the service being known to +reject it too. Same-named inputs whose meaning differs from the CloudFormation property are excluded by the reviewed `PROPERTY_SEMANTIC_DENYLIST` in the script. Pass `--aws-cli-root` after changing the generator's mapping rules or after updating the AWS CLI checkout. diff --git a/src/validation-engine/API.md b/src/validation-engine/API.md index 1c89b042..81d197a8 100644 --- a/src/validation-engine/API.md +++ b/src/validation-engine/API.md @@ -42,9 +42,7 @@ mapping, and diagnostic scoping to explicitly modeled properties: ```rust use rego_engine::RegoEngine; use schema_validator::SchemaValidator; -use validation_engine::{ - AwsCliCommand, AwsCliValue, EngineConfig, ValidateConfig, validate_aws_cli_command, -}; +use validation_engine::{AwsCliCommand, AwsCliValue, EngineConfig, validate_aws_cli_command}; let engine = RegoEngine::new(EngineConfig::default())?; let schema_validator = SchemaValidator::default(); @@ -58,12 +56,7 @@ let request = AwsCliCommand::new( .with_service_prefix("s3") .with_http_method("PUT"); -let result = validate_aws_cli_command( - &engine, - &schema_validator, - &request, - ValidateConfig::default(), -)?; +let result = validate_aws_cli_command(&engine, &schema_validator, &request)?; if let Some(report) = &result.report { for diagnostic in &report.diagnostics { println!("{}: {}", diagnostic.rule_id, diagnostic.message); @@ -77,9 +70,13 @@ if let Some(report) = &result.report { bytes are validated without rewriting; `TemplateURL` is skipped because validation is offline. Every result includes an operation kind, validation status, optional template source, resource candidates, and reason. `Validated` means the modeled template reached the normal validation pipeline; `Skipped` has no report and explains why. -`AwsCliCommandValidation` carries an `Option` projected at the `STANDARD` detail -level — detailed enrichment is not supported for synthesized API-request templates because there is no user-authored -source to annotate with context. +The modeled template runs through the normal template-validation pipeline (`validate_bytes_with_path`) with a fixed +configuration that callers cannot tune, so every embedding reports the same findings for the same command: the default +`ValidateConfig` at the `STANDARD` detail level — detailed enrichment is not supported for synthesized API-request +templates because there is no user-authored source to annotate with context — gated at `Warn` severity, because a +command is a deployment action rather than template authoring and Info-level guidance (best practices, +replacement-on-update notes) has nothing for the caller to act on. `AwsCliCommandValidation` carries the resulting +`Option`. The `template` field carries the exact bytes that were validated — the caller's original `TemplateBody` without reserializing, or the synthesized JSON template for adapter-mapped requests — so consumers can display the modeled template that produced the diagnostics. It is `None` when the request was skipped. @@ -101,16 +98,19 @@ fails both conditions — because it has no mapping, or its value cannot be type synthesis is SKIPPED and the reason names the offending parameter. This guarantees that validated templates faithfully represent the full caller-supplied state: no parameter is ever silently omitted from the synthesized template. -**API-valid values are never reported as CloudFormation violations.** A same-named API input and CloudFormation -property can differ in value domain: the service may accept an enum member, numeric bound, string length, list size, -or tag key/value length that the CloudFormation schema rejects, or its `pattern` may differ from the CloudFormation -`pattern`. The generator records that API-only domain on each mapping as `unrepresentable` (a differing regex is -recorded as the API/CloudFormation pattern pair and settled per value: a value the API pattern accepts and the -CloudFormation pattern rejects is unrepresentable), and the runtime skips synthesis — again naming the parameter and -the value — whenever a supplied value falls inside it, so a command the service would accept is never modeled as a -template that CloudFormation would reject. Same-named inputs whose meaning differs from the property (for example an -API resource ID where the CloudFormation property carries the resource ARN) are removed from the catalog by a reviewed -denylist and therefore skip synthesis as unmapped parameters. +**A CloudFormation constraint the API does not enforce is never reported against a command.** A same-named API input +and CloudFormation property can differ in value domain: the botocore model may declare a wider domain than the +CloudFormation schema (an extra enum member, a looser numeric bound, string length, list or tag-map size, or a +different `pattern`), or it may declare no corresponding constraint at all, in which case nothing proves that the +service rejects what CloudFormation rejects. The generator records every such constraint on the mapping as +`unrepresentable` — the CloudFormation enum, bounds, sizes, and `pattern` (paired with the anchored API pattern when +the API declares a different one) — and the runtime settles it per value: a value outside the CloudFormation +constraint skips synthesis, naming the parameter, the value, and the constraint the API does not enforce, so a command +the service may accept is never modeled as a template that CloudFormation would reject. A value inside every +CloudFormation constraint validates fully, and a constraint the API enforces at least as strictly keeps its +CloudFormation finding. Same-named inputs whose meaning differs from the property (for example an API resource ID +where the CloudFormation property carries the resource ARN) are removed from the catalog by a reviewed denylist and +therefore skip synthesis as unmapped parameters. **Template-authoring advice is not reported for modeled state.** Synthesized templates and wrapped Cloud Control desired state never had a template author, so rules whose only remediation is a template construct — replace a literal From ca366bc0048e623dd6435bb20f7338658f1757bf Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Fri, 11 Sep 2026 20:14:04 +0000 Subject: [PATCH 17/19] update how validation works --- INSTALLATION.md | 2 +- src/bindings-go/go/cfnvalidate.go | 19 +- src/bindings-go/src/lib.rs | 17 +- src/bindings-go/tests/smoke_test.go | 10 +- src/bindings-jvm/src/lib.rs | 12 +- .../amazon/cloudformation/validate/Api.kt | 23 +- .../cloudformation_validate/__init__.py | 15 +- src/bindings-python/src/lib.rs | 12 +- src/bindings-wasm/src/lib.rs | 12 +- src/bindings-wasm/ts/index.ts | 10 +- .../scripts/generate_aws_cli_catalog.py | 178 +++++++---- src/validation-engine/src/aws_cli.rs | 285 +++++++++++++----- 12 files changed, 373 insertions(+), 222 deletions(-) diff --git a/INSTALLATION.md b/INSTALLATION.md index b9990763..a84f591b 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -113,7 +113,7 @@ See the [Go API and examples](src/bindings-go/README.md). The JVM library is published to [Maven Central as `software.amazon.cloudformation:cloudformation-validate`](https://central.sonatype.com/artifact/software.amazon.cloudformation/cloudformation-validate) -and requires Java 8 or later. The jar includes native libraries for all supported platforms; Maven or Gradle resolves +and requires JDK 21 or later. The jar includes native libraries for all supported platforms; Maven or Gradle resolves JNA, Gson, and the Kotlin standard library. Gradle (Kotlin DSL): diff --git a/src/bindings-go/go/cfnvalidate.go b/src/bindings-go/go/cfnvalidate.go index db361e92..2a82b68c 100644 --- a/src/bindings-go/go/cfnvalidate.go +++ b/src/bindings-go/go/cfnvalidate.go @@ -89,7 +89,7 @@ func decodeInto[T any](data string, what string) (*T, error) { // nativeEngine is the method set shared by the generated engine objects. type nativeEngine interface { ValidateTemplateJson(template []byte, optionsJson string, filePath string) (string, error) - ValidateAwsCliCommandJson(requestJson string, optionsJson string) (string, error) + ValidateAwsCliCommandJson(requestJson string) (string, error) ListRulesJson() (string, error) EngineName() string Destroy() @@ -202,20 +202,17 @@ func (e *Engine) Destroy() { } // ValidateAWSCLICommand classifies and validates an AWS CLI command against -// CloudFormation schemas and rules entirely offline. The result contains -// operation classification, resource type inference, and an optional -// ValidationReport when the request was validated (not skipped). The report -// carries only the shared diagnostic fields; enrichment fields are left nil. -func (e *Engine) ValidateAWSCLICommand(request AWSCLICommand, config *ValidateConfig) (*AWSCLICommandValidation, error) { - optionsJSON, err := validateConfigJSON(config) - if err != nil { - return nil, err - } +// CloudFormation schemas and rules entirely offline, with a validation +// configuration fixed by the library. The result contains operation +// classification, resource type inference, and an optional ValidationReport +// when the request was validated (not skipped). The report carries only the +// shared diagnostic fields; enrichment fields are left nil. +func (e *Engine) ValidateAWSCLICommand(request AWSCLICommand) (*AWSCLICommandValidation, error) { requestJSON, err := marshalAWSCLICommand(request) if err != nil { return nil, err } - data, err := e.inner.ValidateAwsCliCommandJson(requestJSON, optionsJSON) + data, err := e.inner.ValidateAwsCliCommandJson(requestJSON) if err != nil { return nil, err } diff --git a/src/bindings-go/src/lib.rs b/src/bindings-go/src/lib.rs index 4ccfa594..5564cc3f 100644 --- a/src/bindings-go/src/lib.rs +++ b/src/bindings-go/src/lib.rs @@ -369,22 +369,13 @@ macro_rules! impl_go_engine { } /// Validates an AWS CLI command and returns the canonical result as JSON. - pub fn validate_aws_cli_command_json( - &self, - request_json: String, - options_json: String, - ) -> Result { + pub fn validate_aws_cli_command_json(&self, request_json: String) -> Result { catch_panics( || { let request = AwsCliCommandWire::parse(&request_json)?.into_context(); - let config = ValidateOptions::parse(&options_json)?.to_core(); - let result = validation_engine::validate_aws_cli_command( - &self.engine, - &self.schema_validator, - &request, - config, - ) - .map_err(ValidationError::new)?; + let result = + validation_engine::validate_aws_cli_command(&self.engine, &self.schema_validator, &request) + .map_err(ValidationError::new)?; to_json(&result) }, panic_to_error, diff --git a/src/bindings-go/tests/smoke_test.go b/src/bindings-go/tests/smoke_test.go index 2ba08a26..e2709fbf 100644 --- a/src/bindings-go/tests/smoke_test.go +++ b/src/bindings-go/tests/smoke_test.go @@ -654,7 +654,7 @@ func TestValidateAWSCLICommandSynthesizesS3CreateBucketOnAllEngines(t *testing.T perEngine := map[string]*cfnvalidate.AWSCLICommandValidation{} for name, engine := range allEngines(t) { - validation, err := engine.ValidateAWSCLICommand(request, nil) + validation, err := engine.ValidateAWSCLICommand(request) if err != nil { t.Fatalf("%s: ValidateAWSCLICommand failed: %v", name, err) } @@ -706,7 +706,7 @@ func TestValidateAWSCLICommandPreservesExactTemplateBodyBytes(t *testing.T) { } for name, engine := range allEngines(t) { - validation, err := engine.ValidateAWSCLICommand(request, nil) + validation, err := engine.ValidateAWSCLICommand(request) if err != nil { t.Fatalf("%s: ValidateAWSCLICommand failed: %v", name, err) } @@ -735,7 +735,7 @@ func TestValidateAWSCLICommandConservativelySkipsNestedDynamoDbFields(t *testing } for name, engine := range allEngines(t) { - validation, err := engine.ValidateAWSCLICommand(request, nil) + validation, err := engine.ValidateAWSCLICommand(request) if err != nil { t.Fatalf("%s: ValidateAWSCLICommand failed: %v", name, err) } @@ -773,7 +773,7 @@ func TestValidateAWSCLICommandDoesNotGuessNoncanonicalServiceAlias(t *testing.T) } for name, engine := range allEngines(t) { - canonicalValidation, err := engine.ValidateAWSCLICommand(canonical, nil) + canonicalValidation, err := engine.ValidateAWSCLICommand(canonical) if err != nil { t.Fatalf("%s: canonical ValidateAWSCLICommand failed: %v", name, err) } @@ -782,7 +782,7 @@ func TestValidateAWSCLICommandDoesNotGuessNoncanonicalServiceAlias(t *testing.T) name, canonicalValidation.ResourceTypes) } - aliasValidation, err := engine.ValidateAWSCLICommand(alias, nil) + aliasValidation, err := engine.ValidateAWSCLICommand(alias) if err != nil { t.Fatalf("%s: alias ValidateAWSCLICommand failed: %v", name, err) } diff --git a/src/bindings-jvm/src/lib.rs b/src/bindings-jvm/src/lib.rs index 4d34c2c6..abb73d1a 100644 --- a/src/bindings-jvm/src/lib.rs +++ b/src/bindings-jvm/src/lib.rs @@ -184,18 +184,12 @@ macro_rules! impl_jvm_engine { pub fn validate_aws_cli_command( &self, request: AwsCliCommand, - config: ValidateConfig, ) -> Result { validation_engine::catch_panics( || { - let core_config = config.to_core(); - let validation = validation_engine::validate_aws_cli_command( - &self.engine, - &self.schema_validator, - &request, - core_config, - ) - .map_err(|e| ValidationError::Engine { msg: e.to_string() })?; + let validation = + validation_engine::validate_aws_cli_command(&self.engine, &self.schema_validator, &request) + .map_err(|e| ValidationError::Engine { msg: e.to_string() })?; Ok(validation) }, panic_to_error, diff --git a/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt b/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt index 34cca2ee..7ce4ed67 100644 --- a/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt +++ b/src/bindings-jvm/src/main/kotlin/software/amazon/cloudformation/validate/Api.kt @@ -15,10 +15,7 @@ import java.io.File interface Engine { fun validateTemplate(template: File, config: ValidateConfig = ValidateConfig()): ValidationReport - fun validateAwsCliCommand( - request: AwsCliCommand, - config: ValidateConfig = ValidateConfig(), - ): AwsCliCommandValidation + fun validateAwsCliCommand(request: AwsCliCommand): AwsCliCommandValidation fun listRules(): List fun engineName(): String } @@ -150,10 +147,8 @@ class RegoEngine( override fun validateTemplate(template: File, config: ValidateConfig): ValidationReport = inner.validateTemplate(template.readBytes(), config, template.path) - override fun validateAwsCliCommand( - request: AwsCliCommand, - config: ValidateConfig, - ): AwsCliCommandValidation = inner.validateAwsCliCommand(request.toNative(), config) + override fun validateAwsCliCommand(request: AwsCliCommand): AwsCliCommandValidation = + inner.validateAwsCliCommand(request.toNative()) override fun listRules(): List = inner.listRules() override fun engineName(): String = inner.engineName() @@ -167,10 +162,8 @@ class CelEngine( override fun validateTemplate(template: File, config: ValidateConfig): ValidationReport = inner.validateTemplate(template.readBytes(), config, template.path) - override fun validateAwsCliCommand( - request: AwsCliCommand, - config: ValidateConfig, - ): AwsCliCommandValidation = inner.validateAwsCliCommand(request.toNative(), config) + override fun validateAwsCliCommand(request: AwsCliCommand): AwsCliCommandValidation = + inner.validateAwsCliCommand(request.toNative()) override fun listRules(): List = inner.listRules() override fun engineName(): String = inner.engineName() @@ -190,10 +183,8 @@ class CompositeEngine( override fun validateTemplate(template: File, config: ValidateConfig): ValidationReport = inner.validateTemplate(template.readBytes(), config, template.path) - override fun validateAwsCliCommand( - request: AwsCliCommand, - config: ValidateConfig, - ): AwsCliCommandValidation = inner.validateAwsCliCommand(request.toNative(), config) + override fun validateAwsCliCommand(request: AwsCliCommand): AwsCliCommandValidation = + inner.validateAwsCliCommand(request.toNative()) override fun listRules(): List = inner.listRules() override fun engineName(): String = inner.engineName() diff --git a/src/bindings-python/python/cloudformation_validate/__init__.py b/src/bindings-python/python/cloudformation_validate/__init__.py index fb5cf7cf..d5aa17b9 100644 --- a/src/bindings-python/python/cloudformation_validate/__init__.py +++ b/src/bindings-python/python/cloudformation_validate/__init__.py @@ -338,20 +338,17 @@ def validate_template(self, template: Template, config: typing.Optional[Validate content, path = _template_bytes(template) return self._inner.validate_template(content, config if config is not None else ValidateConfig(), path) - def validate_aws_cli_command( - self, request: AwsCliCommand, config: typing.Optional[ValidateConfig] = None - ) -> AwsCliCommandValidation: + def validate_aws_cli_command(self, request: AwsCliCommand) -> AwsCliCommandValidation: """Classifies, models, and validates an AWS CLI command. - A skipped command has ``report is None`` and an explicit status and reason. - The ``template`` field carries the exact bytes validated (the caller's - original ``TemplateBody`` or the synthesized JSON), or ``None`` when skipped. + The validation configuration is fixed by the library. A skipped command + has ``report is None`` and an explicit status and reason. The ``template`` + field carries the exact bytes validated (the caller's original + ``TemplateBody`` or the synthesized JSON), or ``None`` when skipped. """ if not isinstance(request, AwsCliCommand): raise TypeError("request must be an AwsCliCommand") - return self._inner.validate_aws_cli_command( - request._to_native(), config if config is not None else ValidateConfig() - ) + return self._inner.validate_aws_cli_command(request._to_native()) def list_rules(self) -> typing.List[RuleInfo]: """Lists every rule this engine evaluates, sorted by rule ID.""" diff --git a/src/bindings-python/src/lib.rs b/src/bindings-python/src/lib.rs index 04d8c902..f855abb6 100644 --- a/src/bindings-python/src/lib.rs +++ b/src/bindings-python/src/lib.rs @@ -184,18 +184,12 @@ macro_rules! impl_py_engine { pub fn validate_aws_cli_command( &self, request: AwsCliCommand, - config: ValidateConfig, ) -> Result { validation_engine::catch_panics( || { - let core_config = config.to_core(); - let validation = validation_engine::validate_aws_cli_command( - &self.engine, - &self.schema_validator, - &request, - core_config, - ) - .map_err(|e| ValidationError::Engine { msg: e.to_string() })?; + let validation = + validation_engine::validate_aws_cli_command(&self.engine, &self.schema_validator, &request) + .map_err(|e| ValidationError::Engine { msg: e.to_string() })?; Ok(validation) }, panic_to_error, diff --git a/src/bindings-wasm/src/lib.rs b/src/bindings-wasm/src/lib.rs index 04c3d40b..ee4531ff 100644 --- a/src/bindings-wasm/src/lib.rs +++ b/src/bindings-wasm/src/lib.rs @@ -169,19 +169,13 @@ macro_rules! wasm_engine { } #[wasm_bindgen(js_name = "validateAwsCliCommand")] - pub fn validate_aws_cli_command( - &self, - request: JsValue, - options: ValidateConfig, - ) -> Result { + pub fn validate_aws_cli_command(&self, request: JsValue) -> Result { catch_panics( || { let request: AwsCliCommand = serde_wasm_bindgen::from_value(request) .map_err(|error| JsValue::from_str(&format!("invalid AWS CLI command: {error}")))?; - let config = build_core_config(options); - let validation = - validate_aws_cli_command(&self.engine, &self.schema_validator, &request, config) - .map_err(to_js_err)?; + let validation = validate_aws_cli_command(&self.engine, &self.schema_validator, &request) + .map_err(to_js_err)?; to_js(&validation) }, wasm_panic_err, diff --git a/src/bindings-wasm/ts/index.ts b/src/bindings-wasm/ts/index.ts index 85cf2cc0..894c148e 100644 --- a/src/bindings-wasm/ts/index.ts +++ b/src/bindings-wasm/ts/index.ts @@ -329,7 +329,7 @@ function fromWireAwsCliCommandValidation(validation: WireAwsCliCommandValidation export interface Engine { validateTemplate(template: TemplateFile, config?: ValidateConfig): ValidationReport; - validateAwsCliCommand(request: AwsCliCommand, config?: ValidateConfig): AwsCliCommandValidation; + validateAwsCliCommand(request: AwsCliCommand): AwsCliCommandValidation; listRules(): RuleInfo[]; engineName(): string; free(): void; @@ -526,7 +526,7 @@ export class SchemaValidator { interface WasmEngineInstance { validateTemplate(template: Uint8Array, options: ValidateConfig, filePath: string): ValidationReport; - validateAwsCliCommand(request: WireAwsCliCommand, options: ValidateConfig): WireAwsCliCommandValidation; + validateAwsCliCommand(request: WireAwsCliCommand): WireAwsCliCommandValidation; listRules(): RuleInfo[]; engineName(): string; free(): void; @@ -547,13 +547,11 @@ function createEngineClass( return this.inner.validateTemplate(template.readBytes(), config ?? {}, template.path); } - validateAwsCliCommand(request: AwsCliCommand, config?: ValidateConfig): AwsCliCommandValidation { + validateAwsCliCommand(request: AwsCliCommand): AwsCliCommandValidation { if (!(request instanceof AwsCliCommand)) { throw new TypeError('request must be an AwsCliCommand'); } - return fromWireAwsCliCommandValidation( - this.inner.validateAwsCliCommand(toWireAwsCliCommand(request), config ?? {}), - ); + return fromWireAwsCliCommandValidation(this.inner.validateAwsCliCommand(toWireAwsCliCommand(request))); } listRules(): RuleInfo[] { diff --git a/src/data-source/scripts/generate_aws_cli_catalog.py b/src/data-source/scripts/generate_aws_cli_catalog.py index d0bfdc3c..e29ddf8e 100644 --- a/src/data-source/scripts/generate_aws_cli_catalog.py +++ b/src/data-source/scripts/generate_aws_cli_catalog.py @@ -37,13 +37,13 @@ accepted only for a fully reviewed cfn_type/service/operation/source/target context; a same-named member whose meaning differs from the property (``PROPERTY_SEMANTIC_DENYLIST``) never maps -- value-domain verification: an API input value that the service accepts but - the CloudFormation schema rejects (enum members, numeric bounds, string - lengths, list sizes, tag key/value lengths, and values inside a botocore - ``pattern`` that the CloudFormation ``pattern`` rejects) is recorded per - mapping as an ``unrepresentable`` domain so the runtime skips synthesis for - such a value instead of reporting a CloudFormation finding against a valid - API call +- value-domain verification: every CloudFormation constraint the botocore + model does not enforce at least as strictly (enum members, numeric bounds, + string lengths, list and tag-map sizes, tag key/value lengths, and a + ``pattern`` the API lacks or states differently) is recorded per mapping as + an ``unrepresentable`` domain so the runtime skips synthesis for a value + outside it instead of reporting a CloudFormation finding against a call the + service may accept - noun agreement or property-overlap thresholds; ties are dropped entirely - global reverse uniqueness: one (service, operation) key maps to exactly one catalog entry; unresolvable collisions are dropped entirely @@ -724,31 +724,68 @@ def _strip_anchors(pattern): def _pattern_divergence(source_shape, node): - """The API/CloudFormation ``pattern`` pair when the two regexes differ. - - Regex inclusion is undecidable in general, so a textual difference (beyond - the ``^``/``$`` anchors, which the schema validator and the service both - apply to whole values) is recorded as a candidate divergence and settled - per value at runtime: a value the API pattern accepts and the CloudFormation - pattern rejects skips synthesis. The API pattern is recorded anchored to the - whole value, as the service applies it; the CloudFormation pattern is - recorded as written, as the schema validator applies it. Returns ``None`` - when either side has no pattern, when the two are the same pattern, or when - the API pattern is not a regex Python can compile, so a mapping is never - restricted on evidence the generator cannot read. + """The CloudFormation ``pattern`` the API does not enforce, if any. + + When the API declares no ``pattern``, the CloudFormation pattern alone is + recorded: every value it rejects is unrepresentable, because the service may + still accept it. When both sides declare a pattern, regex inclusion is + undecidable in general, so a textual difference (beyond the ``^``/``$`` + anchors, which the schema validator and the service both apply to whole + values) is recorded as a candidate divergence and settled per value at + runtime: a value the API pattern accepts and the CloudFormation pattern + rejects skips synthesis. The API pattern is recorded anchored to the whole + value, as the service applies it; the CloudFormation pattern is recorded as + written, as the schema validator applies it, and is not pre-checked here: + the runtime compiles it exactly as the schema validator does, so a pattern + neither can compile guards nothing, which matches the validator reporting + nothing for it. Returns ``None`` when CloudFormation has no pattern, when the + two are the same pattern, or when the API pattern is not a regex Python can + compile, so a mapping is never restricted on evidence the generator cannot + read. """ api_pattern = (getattr(source_shape, 'metadata', None) or {}).get('pattern') cfn_pattern = node.get('pattern') - if not api_pattern or not cfn_pattern: + if not cfn_pattern: return None + if not api_pattern: + return {'cloudformation': cfn_pattern} if _strip_anchors(api_pattern) == _strip_anchors(cfn_pattern): return None anchored_api_pattern = _anchor_pattern(api_pattern) + if not _compiles(anchored_api_pattern): + return None + return {'api': anchored_api_pattern, 'cloudformation': cfn_pattern} + + +def _compiles(pattern): try: - re.compile(anchored_api_pattern) + re.compile(pattern) except re.error: + return False + return True + + +def _unenforced_maximum(api_maximum, cfn_maximum): + """``cfn_maximum`` when the API allows more: a higher maximum or none at all.""" + if cfn_maximum is None: return None - return {'api': anchored_api_pattern, 'cloudformation': cfn_pattern} + if api_maximum is None or api_maximum > cfn_maximum: + return cfn_maximum + return None + + +def _unenforced_minimum(api_minimum, cfn_minimum): + """``cfn_minimum`` when the API allows less: a lower minimum or none at all.""" + if cfn_minimum is None: + return None + if api_minimum is None or api_minimum < cfn_minimum: + return cfn_minimum + return None + + +def _size_minimum(cfn_minimum): + """A length or item-count minimum, with the vacuous zero treated as absent.""" + return cfn_minimum or None def _string_unrepresentable(source_shape, target_schema, definitions): @@ -760,9 +797,9 @@ def _string_unrepresentable(source_shape, target_schema, definitions): api_min, api_max = _shape_bounds(source_shape) cfn_min = node.get('min_length') cfn_max = node.get('max_length') + cfn_enum = node.get('enum') api_enum = list(getattr(source_shape, 'enum', None) or []) if api_enum: - cfn_enum = node.get('enum') pattern = node.get('pattern') rejected = sorted( value for value in api_enum @@ -774,10 +811,14 @@ def _string_unrepresentable(source_shape, target_schema, definitions): if rejected: domain['rejected_values'] = rejected return domain - if api_max is not None and cfn_max is not None and api_max > cfn_max: - domain['max_length'] = cfn_max - if api_min is not None and cfn_min is not None and api_min < cfn_min: - domain['min_length'] = cfn_min + if isinstance(cfn_enum, list) and cfn_enum: + domain['allowed_values'] = list(cfn_enum) + max_length = _unenforced_maximum(api_max, cfn_max) + if max_length is not None: + domain['max_length'] = max_length + min_length = _unenforced_minimum(api_min, _size_minimum(cfn_min)) + if min_length is not None: + domain['min_length'] = min_length pattern_divergence = _pattern_divergence(source_shape, node) if pattern_divergence: domain['pattern'] = pattern_divergence @@ -792,12 +833,25 @@ def _numeric_unrepresentable(source_shape, target_schema, definitions): ) domain = {} api_min, api_max = _shape_bounds(source_shape) - cfn_min = node.get('minimum') - cfn_max = node.get('maximum') - if api_max is not None and cfn_max is not None and api_max > cfn_max: - domain['maximum'] = cfn_max - if api_min is not None and cfn_min is not None and api_min < cfn_min: - domain['minimum'] = cfn_min + maximum = _unenforced_maximum(api_max, node.get('maximum')) + if maximum is not None: + domain['maximum'] = maximum + minimum = _unenforced_minimum(api_min, node.get('minimum')) + if minimum is not None: + domain['minimum'] = minimum + return domain + + +def _size_unrepresentable(source_shape, array_schema): + """List-size or tag-map-size bounds of ``array_schema`` the API does not enforce.""" + domain = {} + api_min, api_max = _shape_bounds(source_shape) + max_items = _unenforced_maximum(api_max, array_schema.get('max_items')) + if max_items is not None: + domain['max_length'] = max_items + min_items = _unenforced_minimum(api_min, _size_minimum(array_schema.get('min_items'))) + if min_items is not None: + domain['min_length'] = min_items return domain @@ -816,19 +870,23 @@ def _tag_field_schema(item_schema, definitions, field): def _unrepresentable_domain(source_shape, target_schema, definitions, target): - """API-valid values of ``source_shape`` that ``target_schema`` rejects. - - Only provable exclusions are recorded: an API enum member absent from the - CloudFormation enum or rejected by its pattern, and API numeric, length, or - size bounds that exceed the CloudFormation bounds. The returned mapping is - empty when every API-valid value has a CloudFormation representation. - Keys name the CloudFormation constraint the API range exceeds: - ``rejected_values`` (API enum members CloudFormation rejects), - ``minimum``/``maximum`` (numeric bounds), ``min_length``/``max_length`` - (string length or list size), ``pattern`` (the anchored API regex and the - CloudFormation regex when they differ; settled per value at runtime), - ``items`` (nested domain of list elements), ``tag_key``/``tag_value`` - (nested string domains for tag maps). + """Values of ``source_shape`` that ``target_schema`` rejects without the API + model proving that the service rejects them too. + + A CloudFormation constraint is recorded whenever the API declares a wider + one or none at all, because a value outside it may still be legal for the + service and must not be reported as a CloudFormation violation. A + constraint the API enforces at least as strictly is not recorded, so a + value outside it keeps its CloudFormation finding. The returned mapping is + empty when the API enforces every CloudFormation constraint. Keys name the + CloudFormation constraint the API does not enforce: ``rejected_values`` + (API enum members CloudFormation rejects), ``allowed_values`` (the + CloudFormation enum when the API declares none), ``minimum``/``maximum`` + (numeric bounds), ``min_length``/``max_length`` (string length, list size, + or tag-map size), ``pattern`` (the CloudFormation regex, paired with the + anchored API regex when the API declares a different one; settled per + value at runtime), ``items`` (nested domain of list elements), + ``tag_key``/``tag_value`` (nested string domains for tag maps). """ source_type = source_shape.type_name if source_type == 'string': @@ -846,20 +904,14 @@ def _unrepresentable_domain(source_shape, target_schema, definitions, target): ) if item_domain: domain['items'] = item_domain - api_min, api_max = _shape_bounds(source_shape) - cfn_min = array_schema.get('min_items') - cfn_max = array_schema.get('max_items') - if api_max is not None and cfn_max is not None and api_max > cfn_max: - domain['max_length'] = cfn_max - if api_min is not None and cfn_min is not None and api_min < cfn_min: - domain['min_length'] = cfn_min + domain.update(_size_unrepresentable(source_shape, array_schema)) return domain if source_type == 'map' and target == 'Tags': array_schema = _schema_node_for_type( target_schema, definitions, 'array' ) or {} item_schema = array_schema.get('items') or {} - domain = {} + domain = _size_unrepresentable(source_shape, array_schema) for field, key in (('Key', 'tag_key'), ('Value', 'tag_value')): field_domain = _string_unrepresentable( source_shape.key if field == 'Key' else source_shape.value, @@ -1139,12 +1191,12 @@ def _verify_curated_updates(compiled_schemas, index): return verified_adapters -def _count_pattern_pairs(domain): - """Number of API/CloudFormation ``pattern`` pairs in ``domain`` and its nested domains.""" +def _count_patterns(domain): + """Number of CloudFormation ``pattern`` guards in ``domain`` and its nested domains.""" count = 1 if domain.get('pattern') else 0 for nested in ('items', 'tag_key', 'tag_value'): if domain.get(nested): - count += _count_pattern_pairs(domain[nested]) + count += _count_patterns(domain[nested]) return count @@ -1190,17 +1242,17 @@ def _compute_coverage(unique_adapters, index, compiled_schemas): phases[adapter['phase']] += 1 guarded_mappings = 0 - pattern_pairs = 0 + patterns = 0 for adapter in unique_adapters: for mapping in adapter.get('mappings', []): domain = mapping.get('unrepresentable') if domain: guarded_mappings += 1 - pattern_pairs += _count_pattern_pairs(domain) + patterns += _count_patterns(domain) return { 'guarded_mappings': guarded_mappings, - 'pattern_pairs': pattern_pairs, + 'patterns': patterns, 'catalog_services': { 'covered': len({a['service'] for a in unique_adapters}), 'total': botocore_services, @@ -1312,9 +1364,9 @@ def _render_coverage(coverage, dropped_count): 'maps to exactly one resource type' ), ( - f" {coverage['guarded_mappings']:,} property mappings record values the API accepts but " - f"CloudFormation rejects ({coverage['pattern_pairs']:,} as regex pattern pairs settled per value); " - 'such a value skips validation instead of producing a finding' + f" {coverage['guarded_mappings']:,} property mappings record CloudFormation constraints the API " + f"does not enforce ({coverage['patterns']:,} as regex patterns settled per value); " + 'a value outside them skips validation instead of producing a finding' ), '', 'What an AWS CLI call can be checked against', diff --git a/src/validation-engine/src/aws_cli.rs b/src/validation-engine/src/aws_cli.rs index ea738b5c..a3f27ef7 100644 --- a/src/validation-engine/src/aws_cli.rs +++ b/src/validation-engine/src/aws_cli.rs @@ -188,9 +188,9 @@ pub enum AwsCliTemplateSource { /// Canonical result for AWS CLI command validation. /// -/// The report is projected at the `STANDARD` detail level — detailed enrichment -/// is not meaningful for synthesized command templates because there is no -/// user-authored source to annotate with context. +/// The report is projected at the `STANDARD` detail level and gated at the +/// `WARN` severity floor: a command is a deployment action, so Info-level +/// guidance has nothing for the caller to act on. #[derive(Debug, Clone, Serialize)] #[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))] #[serde(rename_all = "camelCase")] @@ -213,13 +213,15 @@ pub struct AwsCliCommandValidation { } /// Classifies, models, and validates one AWS CLI command entirely offline. +/// +/// The template-validation configuration is fixed — `STANDARD` detail, `WARN` +/// severity gate, default filters — and cannot be tuned by callers. pub fn validate_aws_cli_command( engine: &dyn ValidationEngine, schema_validator: &SchemaValidator, request: &AwsCliCommand, - config: ValidateConfig, ) -> Result { - validate_aws_cli_command_with_path(engine, schema_validator, request, config, request.default_file_path()) + validate_aws_cli_command_with_path(engine, schema_validator, request, request.default_file_path()) } /// Same as [`validate_aws_cli_command`], with an explicit report path supplied @@ -228,7 +230,6 @@ pub fn validate_aws_cli_command_with_path( engine: &dyn ValidationEngine, schema_validator: &SchemaValidator, request: &AwsCliCommand, - config: ValidateConfig, file_path: String, ) -> Result { let classification = classify_operation(request, schema_validator)?; @@ -245,10 +246,8 @@ pub fn validate_aws_cli_command_with_path( }); }; - // Force standard detail level — detailed enrichment is not meaningful for - // synthesized command templates (no user-authored source to annotate). - let standard_config = ValidateConfig { detail_level: DetailLevel::Standard, ..config }; - let mut report = validate_bytes_with_path(engine, schema_validator, &template, standard_config, file_path)?; + let mut report = + validate_bytes_with_path(engine, schema_validator, &template, aws_cli_validate_config(), file_path)?; if synthesis.source != Some(AwsCliTemplateSource::TemplateBody) { suppress_template_authoring_advice(&mut report); } @@ -265,6 +264,17 @@ pub fn validate_aws_cli_command_with_path( template: Some(template), }) } + +/// The fixed template-validation configuration every AWS CLI command runs +/// with: the defaults, at `STANDARD` detail because a modeled command has no +/// user-authored source to annotate, gated at `Warn` because a command is a +/// deployment action, not template authoring, so Info-level guidance (best +/// practices, replacement-on-update notes) has nothing for the caller to act +/// on. It is not caller-configurable, so every embedding reports the same +/// findings for the same command. +fn aws_cli_validate_config() -> ValidateConfig { + ValidateConfig { detail_level: DetailLevel::Standard, severity_level: Severity::Warn, ..ValidateConfig::default() } +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)] #[serde(rename_all = "lowercase")] enum AdapterPhase { @@ -277,20 +287,28 @@ enum AdapterPhase { struct PropertyMapping { source: String, target: String, - /// API-valid input values the CloudFormation schema cannot represent. + /// Input values the CloudFormation schema rejects without the API model + /// proving that the service rejects them too. #[serde(default)] unrepresentable: Option, } /// The part of an API input's value domain that the mapped CloudFormation -/// property rejects. A request value inside this domain is legal for the -/// service, so reporting the CloudFormation constraint against it would be a -/// false finding; synthesis is skipped instead. Each bound is the CloudFormation -/// constraint that the API range exceeds. +/// property rejects but the service is not known to reject: either the botocore +/// model declares a wider domain (an extra enum member, a looser bound or +/// pattern) or it declares no corresponding constraint at all. A request value +/// inside this domain may be legal for the service, so reporting the +/// CloudFormation constraint against it could be a false finding; synthesis is +/// skipped instead. Each bound is the CloudFormation constraint the API does not +/// enforce. #[derive(Debug, Clone, PartialEq, Default, Deserialize)] #[serde(default)] struct UnrepresentableDomain { + /// API enum members the CloudFormation schema rejects. rejected_values: Vec, + /// The CloudFormation enum when the API declares none: any other value is + /// unrepresentable. + allowed_values: Vec, minimum: Option, maximum: Option, min_length: Option, @@ -301,40 +319,68 @@ struct UnrepresentableDomain { tag_value: Option>, } -/// An API `pattern` and the CloudFormation `pattern` for the same value that -/// differ textually. Whether the two admit different values cannot be decided -/// from the regexes alone, so the divergence is settled per request value: a -/// value the API pattern accepts and the CloudFormation pattern rejects has no -/// CloudFormation representation. +/// A CloudFormation `pattern` the API does not enforce. When the API declares +/// its own `pattern` that differs textually, whether the two admit different +/// values cannot be decided from the regexes alone, so the divergence is +/// settled per request value: a value the API pattern accepts and the +/// CloudFormation pattern rejects has no CloudFormation representation. When +/// the API declares no `pattern`, every value the CloudFormation pattern +/// rejects is unrepresentable. #[derive(Debug, Clone, PartialEq, Deserialize)] struct PatternDivergence { - /// The botocore pattern anchored to the whole value, as the service applies it. - api: String, + /// The botocore pattern anchored to the whole value, as the service applies + /// it; absent when the API declares no pattern for the input. + #[serde(default)] + api: Option, /// The compiled-schema pattern, applied as the schema validator applies it. cloudformation: String, } impl PatternDivergence { - /// Whether `value` is inside the API pattern but outside the CloudFormation - /// pattern. Errs when either pattern is not a regex this tool can evaluate, - /// because a divergence that cannot be settled must not be silently ignored. + /// Whether `value` is outside the CloudFormation pattern while the API + /// pattern, if any, accepts it. A CloudFormation pattern this tool cannot + /// compile rejects nothing, because the schema validator compiles patterns + /// the same way and reports no finding for one it cannot compile. Errs when + /// the API pattern is not a regex this tool can evaluate, because a + /// divergence that cannot be settled must not be silently ignored. fn rejects(&self, value: &str) -> Result { - let api = compile_pattern(&self.api).ok_or_else(|| format!("API pattern '{}' cannot be compiled", self.api))?; - let cloudformation = compile_pattern(&self.cloudformation) - .ok_or_else(|| format!("CloudFormation pattern '{}' cannot be compiled", self.cloudformation))?; - Ok(api.is_match(value) && !cloudformation.is_match(value)) + let Some(cloudformation) = compile_pattern(&self.cloudformation) else { + return Ok(false); + }; + let api = self + .api + .as_deref() + .map(|api_pattern| { + compile_pattern(api_pattern).ok_or_else(|| format!("API pattern '{api_pattern}' cannot be compiled")) + }) + .transpose()?; + let api_accepts = api.is_none_or(|api| api.is_match(value)); + Ok(api_accepts && !cloudformation.is_match(value)) } } +/// Suffix shared by every rejection message so a skip reason states why the +/// constraint cannot be reported against the command: the API either declares a +/// wider domain or none at all. +const API_DOES_NOT_ENFORCE: &str = "a constraint the API does not enforce"; + impl UnrepresentableDomain { /// Describes why `value` has no CloudFormation representation, or `None` /// when every part of it is representable. Errs only when the domain itself /// cannot be evaluated, which is a defect in the generated catalog. fn rejection(&self, value: &AwsCliValue) -> Result, String> { - if let Some(json_value) = value.json_value() - && self.rejected_values.contains(&json_value) - { - return Ok(Some(format!("value {json_value} is valid for the API but not for CloudFormation"))); + if let Some(json_value) = value.json_value() { + if self.rejected_values.contains(&json_value) { + return Ok(Some(format!( + "value {json_value} is outside the CloudFormation enum, {API_DOES_NOT_ENFORCE}" + ))); + } + if !self.allowed_values.is_empty() && !self.allowed_values.contains(&json_value) { + return Ok(Some(format!( + "value {json_value} is not one of the CloudFormation values {}, {API_DOES_NOT_ENFORCE}", + serde_json::Value::Array(self.allowed_values.clone()) + ))); + } } match value { AwsCliValue::String { value } => { @@ -361,6 +407,9 @@ impl UnrepresentableDomain { Ok(None) } AwsCliValue::Object { entries } => { + if let Some(rejection) = self.length_rejection(entries.len() as u64, "entry count") { + return Ok(Some(rejection)); + } for (key, entry) in entries { if let Some(domain) = self.tag_key.as_ref() && let Some(rejection) = domain.rejection(&AwsCliValue::String { value: key.clone() })? @@ -388,7 +437,7 @@ impl UnrepresentableDomain { }; if divergence.rejects(value)? { return Ok(Some(format!( - "value {value:?} is valid for the API but does not match the CloudFormation pattern '{}'", + "value {value:?} does not match the CloudFormation pattern '{}', {API_DOES_NOT_ENFORCE}", divergence.cloudformation ))); } @@ -399,12 +448,16 @@ impl UnrepresentableDomain { if let Some(minimum) = self.minimum && value < minimum { - return Some(format!("value {value} is valid for the API but below the CloudFormation minimum {minimum}")); + return Some(format!( + "value {value} is below the CloudFormation minimum {minimum}, {API_DOES_NOT_ENFORCE}" + )); } if let Some(maximum) = self.maximum && value > maximum { - return Some(format!("value {value} is valid for the API but above the CloudFormation maximum {maximum}")); + return Some(format!( + "value {value} is above the CloudFormation maximum {maximum}, {API_DOES_NOT_ENFORCE}" + )); } None } @@ -414,14 +467,14 @@ impl UnrepresentableDomain { && length < min_length { return Some(format!( - "{measure} {length} is valid for the API but below the CloudFormation minimum {min_length}" + "{measure} {length} is below the CloudFormation minimum {min_length}, {API_DOES_NOT_ENFORCE}" )); } if let Some(max_length) = self.max_length && length > max_length { return Some(format!( - "{measure} {length} is valid for the API but above the CloudFormation maximum {max_length}" + "{measure} {length} is above the CloudFormation maximum {max_length}, {API_DOES_NOT_ENFORCE}" )); } None @@ -452,10 +505,11 @@ struct OperationCatalog { /// Generated by `data-source/scripts/generate_aws_cli_catalog.py`: each entry is /// derived from the resource type's own provider handler metadata, resolved /// against botocore service models, and structurally verified against the -/// compiled CloudFormation schemas. Each mapping also records the API value -/// domain the schema cannot represent (enum members, bounds, lengths, and a -/// diverging regex pattern pair), so a value that is valid for the service but -/// not for CloudFormation skips synthesis instead of producing a finding. +/// compiled CloudFormation schemas. Each mapping also records the value domain +/// the schema rejects without the API enforcing the same constraint (enum +/// members, bounds, lengths, sizes, and a regex pattern), so a value the +/// service may accept but CloudFormation rejects skips synthesis instead of +/// producing a finding. /// Only exact service+operation keys resolve; unregistered operations stay /// unmapped. static ADAPTER_REGISTRY: LazyLock, String>> = @@ -1386,7 +1440,7 @@ mod tests { assert_eq!(tags.items.as_deref().and_then(|domain| domain.maximum), Some(10.0)); assert_eq!( adapter.mappings[2].unrepresentable.as_ref().and_then(|domain| domain.pattern.clone()), - Some(PatternDivergence { api: "^[a-z]{2,4}$".into(), cloudformation: "^[a-z]{2}$".into() }) + Some(PatternDivergence { api: Some("^[a-z]{2,4}$".into()), cloudformation: "^[a-z]{2}$".into() }) ); } @@ -1394,15 +1448,15 @@ mod tests { fn pattern_divergence_rejects_only_values_the_api_accepts_and_cloudformation_rejects() { let sqs_arn = UnrepresentableDomain { pattern: Some(PatternDivergence { - api: r"^arn:aws[a-z-]*:sqs:[a-z]{2,4}(-[a-z]+)+-\d:\d{12}:.+$".into(), + api: Some(r"^arn:aws[a-z-]*:sqs:[a-z]{2,4}(-[a-z]+)+-\d:\d{12}:.+$".into()), cloudformation: r"^arn:aws[a-z-]*:sqs:[a-z]{2}(-[a-z]+)+-\d:\d{12}:.+$".into(), }), ..UnrepresentableDomain::default() }; let sovereign = value(serde_json::json!("arn:aws-eusc:sqs:eusc-de-east-1:123456789012:queue")); let rejection = sqs_arn.rejection(&sovereign).expect("domain evaluates").expect("value is unrepresentable"); - assert!(rejection.contains("valid for the API"), "{rejection}"); assert!(rejection.contains("CloudFormation pattern"), "{rejection}"); + assert!(rejection.contains(API_DOES_NOT_ENFORCE), "{rejection}"); assert!( sqs_arn .rejection(&value(serde_json::json!("arn:aws:sqs:us-east-1:123456789012:queue"))) @@ -1441,10 +1495,91 @@ mod tests { ); } + #[test] + fn cloudformation_only_pattern_rejects_every_value_outside_it() { + let table_name = UnrepresentableDomain { + pattern: Some(PatternDivergence { api: None, cloudformation: "^[a-zA-Z0-9_.-]+$".into() }), + ..UnrepresentableDomain::default() + }; + let table_arn = value(serde_json::json!("arn:aws:dynamodb:us-east-1:123456789012:table/Orders")); + let rejection = table_name.rejection(&table_arn).expect("domain evaluates").expect("ARN is unrepresentable"); + assert!(rejection.contains("does not match the CloudFormation pattern"), "{rejection}"); + assert!(rejection.contains(API_DOES_NOT_ENFORCE), "{rejection}"); + assert!( + table_name.rejection(&value(serde_json::json!("Orders"))).expect("domain evaluates").is_none(), + "a value inside the CloudFormation pattern is representable" + ); + } + + #[test] + fn cloudformation_only_enum_rejects_every_value_outside_it() { + let data_type = UnrepresentableDomain { + allowed_values: vec![serde_json::json!("text"), serde_json::json!("aws:ec2:image")], + ..UnrepresentableDomain::default() + }; + let rejection = data_type + .rejection(&value(serde_json::json!("aws:ssm:integration"))) + .expect("domain evaluates") + .expect("value outside the CloudFormation enum is unrepresentable"); + assert!(rejection.contains("is not one of the CloudFormation values"), "{rejection}"); + assert!(rejection.contains("aws:ec2:image"), "{rejection}"); + assert!(rejection.contains(API_DOES_NOT_ENFORCE), "{rejection}"); + assert!(data_type.rejection(&value(serde_json::json!("text"))).expect("domain evaluates").is_none()); + assert!( + data_type.rejection(&value(serde_json::json!(7))).expect("domain evaluates").is_some(), + "a non-string value is outside a string enum" + ); + } + + #[test] + fn tag_map_entry_count_outside_cloudformation_item_bounds_is_unrepresentable() { + let tags = + UnrepresentableDomain { min_length: Some(1), max_length: Some(2), ..UnrepresentableDomain::default() }; + let empty = tags.rejection(&value(serde_json::json!({}))).expect("domain evaluates").expect("empty map"); + assert!(empty.contains("entry count 0"), "{empty}"); + assert!(empty.contains("minimum 1"), "{empty}"); + let crowded = tags + .rejection(&value(serde_json::json!({"a": "1", "b": "2", "c": "3"}))) + .expect("domain evaluates") + .expect("three entries above the maximum"); + assert!(crowded.contains("entry count 3"), "{crowded}"); + assert!(crowded.contains("maximum 2"), "{crowded}"); + assert!(tags.rejection(&value(serde_json::json!({"a": "1"}))).expect("domain evaluates").is_none()); + } + + #[test] + fn aws_cli_config_is_fixed_at_standard_detail_and_warn_severity() { + let preset = aws_cli_validate_config(); + assert_eq!(preset.detail_level, DetailLevel::Standard); + assert_eq!(preset.severity_level, Severity::Warn); + assert!(!preset.strict); + assert!(!preset.disable_builtin_rules); + } + + #[test] + fn synthesized_create_reports_omit_info_findings() { + let schema_validator = SchemaValidator::default(); + let engine = NoopEngine::default(); + let create_bucket = request("s3", "CreateBucket", serde_json::json!({"Bucket": "my-bucket"})); + let validation = + validate_aws_cli_command(&engine, &schema_validator, &create_bucket).expect("validation succeeds"); + let report = validation.report.expect("bucket request is validated"); + assert_eq!(report.metadata.severity_level, Severity::Warn); + assert!( + report.diagnostics.iter().all(|diagnostic| diagnostic.severity >= Severity::Warn), + "info findings must be gated: {:?}", + report.diagnostics + ); + assert!( + report.metadata.suppressed >= 1, + "the create-only replacement note on BucketName is an Info finding that the floor removes" + ); + } + #[test] fn uncompilable_pattern_divergence_is_a_catalog_error_not_a_silent_skip() { let broken = UnrepresentableDomain { - pattern: Some(PatternDivergence { api: "^(unclosed$".into(), cloudformation: "^[a-z]+$".into() }), + pattern: Some(PatternDivergence { api: Some("^(unclosed$".into()), cloudformation: "^[a-z]+$".into() }), ..UnrepresentableDomain::default() }; let error = broken.rejection(&value(serde_json::json!("abc"))).expect_err("broken pattern must error"); @@ -1472,7 +1607,10 @@ mod tests { source: "Bucket".into(), target: "BucketName".into(), unrepresentable: Some(UnrepresentableDomain { - pattern: Some(PatternDivergence { api: "^[a-z0-9.-]+$".into(), cloudformation: "^[a-z0-9-]+$".into() }), + pattern: Some(PatternDivergence { + api: Some("^[a-z0-9.-]+$".into()), + cloudformation: "^[a-z0-9-]+$".into(), + }), ..UnrepresentableDomain::default() }), }]); @@ -1487,29 +1625,40 @@ mod tests { } } - fn collect_patterns<'a>(domain: &'a UnrepresentableDomain, patterns: &mut Vec<&'a str>) { + fn collect_api_patterns<'a>(domain: &'a UnrepresentableDomain, patterns: &mut Vec<&'a str>) { if let Some(divergence) = domain.pattern.as_ref() { - patterns.push(divergence.api.as_str()); - patterns.push(divergence.cloudformation.as_str()); + patterns.extend(divergence.api.as_deref()); } for nested in [&domain.items, &domain.tag_key, &domain.tag_value].into_iter().flatten() { - collect_patterns(nested, patterns); + collect_api_patterns(nested, patterns); } } #[test] - fn every_pattern_in_the_embedded_catalog_compiles() { + fn every_api_pattern_in_the_embedded_catalog_compiles() { let mut patterns = Vec::new(); for adapter in adapter_registry().expect("embedded catalog parses").values() { for mapping in &adapter.mappings { if let Some(domain) = mapping.unrepresentable.as_ref() { - collect_patterns(domain, &mut patterns); + collect_api_patterns(domain, &mut patterns); } } } let uncompilable: Vec<&str> = patterns.iter().copied().filter(|pattern| compile_pattern(pattern).is_none()).collect(); - assert!(uncompilable.is_empty(), "catalog patterns must compile: {uncompilable:?}"); + assert!(uncompilable.is_empty(), "catalog API patterns must compile: {uncompilable:?}"); + } + + #[test] + fn uncompilable_cloudformation_pattern_rejects_nothing_like_the_schema_validator() { + let unclosed_group = UnrepresentableDomain { + pattern: Some(PatternDivergence { api: None, cloudformation: "^(unclosed$".into() }), + ..UnrepresentableDomain::default() + }; + assert!( + unclosed_group.rejection(&value(serde_json::json!("anything"))).expect("domain evaluates").is_none(), + "a pattern the schema validator cannot apply produces no finding to guard against" + ); } #[test] @@ -1597,7 +1746,8 @@ mod tests { let reason = skip_reason(map_s3_create_bucket(&adapter, serde_json::json!({"Bucket": "legacy"}))); assert!(reason.contains("parameter 'Bucket'"), "{reason}"); - assert!(reason.contains("valid for the API"), "{reason}"); + assert!(reason.contains("is outside the CloudFormation enum"), "{reason}"); + assert!(reason.contains(API_DOES_NOT_ENFORCE), "{reason}"); assert!(reason.contains("property 'BucketName' on AWS::S3::Bucket"), "{reason}"); let reason = skip_reason(map_s3_create_bucket(&adapter, serde_json::json!({"Bucket": "ab"}))); @@ -2333,8 +2483,7 @@ mod tests { let schema_validator = SchemaValidator::default(); let mut exact = request("cloudformation", "CreateChangeSet", serde_json::json!({})); exact.parameters.insert("TemplateBody".into(), AwsCliValue::Bytes { value: br#"{"Resources":{}}"#.to_vec() }); - let validation = validate_aws_cli_command(&engine, &schema_validator, &exact, ValidateConfig::default()) - .expect("validation succeeds"); + let validation = validate_aws_cli_command(&engine, &schema_validator, &exact).expect("validation succeeds"); assert_eq!(validation.status, AwsCliCommandValidationStatus::Validated); assert_eq!(validation.template_source, Some(AwsCliTemplateSource::TemplateBody)); assert!(validation.report.is_some()); @@ -2345,8 +2494,7 @@ mod tests { ); let read = request("iam", "GetRole", serde_json::json!({"RoleName": "Synthetic"})); - let validation = validate_aws_cli_command(&engine, &schema_validator, &read, ValidateConfig::default()) - .expect("classification succeeds"); + let validation = validate_aws_cli_command(&engine, &schema_validator, &read).expect("classification succeeds"); assert_eq!(validation.status, AwsCliCommandValidationStatus::Skipped); assert_eq!(validation.operation_kind, AwsCliOperationKind::ReadOnly); assert!(validation.report.is_none()); @@ -2416,16 +2564,15 @@ mod tests { }, ); let validation = - validate_aws_cli_command(&engine, &schema_validator, &template_body, ValidateConfig::default()) - .expect("validation succeeds"); + validate_aws_cli_command(&engine, &schema_validator, &template_body).expect("validation succeeds"); assert_eq!(validation.template_source, Some(AwsCliTemplateSource::TemplateBody)); let reported = reported_rule_ids(&validation); assert!(reported.contains("W9002"), "a real template keeps authoring advice: {reported:?}"); assert!(reported.contains("W3030"), "{reported:?}"); let synthesized = request("sqs", "CreateQueue", serde_json::json!({"QueueName": "orders"})); - let validation = validate_aws_cli_command(&engine, &schema_validator, &synthesized, ValidateConfig::default()) - .expect("validation succeeds"); + let validation = + validate_aws_cli_command(&engine, &schema_validator, &synthesized).expect("validation succeeds"); assert_eq!(validation.template_source, Some(AwsCliTemplateSource::SynthesizedCreate)); let reported = reported_rule_ids(&validation); assert!(!reported.contains("W9002"), "modeled API state drops authoring advice: {reported:?}"); @@ -2440,8 +2587,7 @@ mod tests { serde_json::json!({"TypeName": "AWS::SQS::Queue", "DesiredState": "{\"QueueName\":\"orders\"}"}), ); let validation = - validate_aws_cli_command(&engine, &schema_validator, &desired_state, ValidateConfig::default()) - .expect("validation succeeds"); + validate_aws_cli_command(&engine, &schema_validator, &desired_state).expect("validation succeeds"); assert_eq!(validation.template_source, Some(AwsCliTemplateSource::CloudControlDesiredState)); let reported = reported_rule_ids(&validation); assert!(!reported.contains("W9002"), "Cloud Control desired state drops authoring advice: {reported:?}"); @@ -2457,8 +2603,7 @@ mod tests { "UpdateFunctionConfiguration", serde_json::json!({"FunctionName": "Synthetic", "MemorySize": 0}), ); - let validation = validate_aws_cli_command(&engine, &schema_validator, &update, ValidateConfig::default()) - .expect("validation succeeds"); + let validation = validate_aws_cli_command(&engine, &schema_validator, &update).expect("validation succeeds"); let report = validation.report.expect("update is validated"); assert!( report @@ -2565,8 +2710,7 @@ mod tests { let engine = NoopEngine::default(); let schema_validator = SchemaValidator::default(); let req = request("lambda", "CreateFunction", serde_json::json!({"MemorySize": 0})); - let validation = validate_aws_cli_command(&engine, &schema_validator, &req, ValidateConfig::default()) - .expect("validation succeeds"); + let validation = validate_aws_cli_command(&engine, &schema_validator, &req).expect("validation succeeds"); assert_eq!(validation.status, AwsCliCommandValidationStatus::Validated); assert_eq!(validation.template_source, Some(AwsCliTemplateSource::SynthesizedCreate)); let report = validation.report.expect("create is validated"); @@ -2641,8 +2785,7 @@ mod tests { let parameters: HashMap = [("TemplateBody".to_string(), AwsCliValue::Bytes { value: template.clone() })].into_iter().collect(); let req = AwsCliCommand::new("cloudformation", operation, parameters); - let validation = validate_aws_cli_command(&engine, &schema_validator, &req, ValidateConfig::default()) - .expect("validation succeeds"); + let validation = validate_aws_cli_command(&engine, &schema_validator, &req).expect("validation succeeds"); assert_eq!( validation.status, AwsCliCommandValidationStatus::Validated, From ec2fd123822f827d54f4af0d5cb5621cefcb6150 Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Mon, 14 Sep 2026 12:55:37 +0000 Subject: [PATCH 18/19] Revert java downgrade and use python 3.9 --- .github/workflows/configs.yml | 2 +- INSTALLATION.md | 2 +- src/bindings-jvm/build.gradle.kts | 9 --------- src/bindings-jvm/build.sh | 8 +------- src/bindings-jvm/uniffi.toml | 1 - src/bindings-python/README.md | 2 +- src/bindings-python/build.sh | 4 ++-- src/bindings-python/pyproject.toml | 3 ++- src/data-source/uniffi.toml | 1 - src/diagnostics/uniffi.toml | 1 - src/rules/uniffi.toml | 1 - src/schema-validator/uniffi.toml | 1 - src/template-model/uniffi.toml | 1 - src/validation-engine/uniffi.toml | 1 - 14 files changed, 8 insertions(+), 29 deletions(-) diff --git a/.github/workflows/configs.yml b/.github/workflows/configs.yml index 53855e5e..eb12a352 100644 --- a/.github/workflows/configs.yml +++ b/.github/workflows/configs.yml @@ -75,7 +75,7 @@ jobs: WORKING_DIR: 'src' RUST_TOOLCHAIN: '1.96.0' # keep in sync with src/rust-toolchain.toml NODE_VERSION: '22.x' - PYTHON_VERSION: '3.10' + PYTHON_VERSION: '3.9' GO_VERSION: '1.26' UNIFFI_BINDGEN_GO_TAG: 'v0.7.1+v0.31.0' # keep in sync with bindings-go/README.md and the uniffi pin in bindings-go/Cargo.toml JAVA_VERSION: '21' diff --git a/INSTALLATION.md b/INSTALLATION.md index a84f591b..6da60f8e 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -71,7 +71,7 @@ See the [Node.js API and examples](src/bindings-wasm/README.md). ### Python Production versions are published to [PyPI](https://pypi.org/project/cloudformation-validate/); prereleases are -published to [TestPyPI](https://test.pypi.org/project/cloudformation-validate/). The package requires Python 3.10 or +published to [TestPyPI](https://test.pypi.org/project/cloudformation-validate/). The package requires Python 3.9 or later, and its platform-specific wheels have no runtime package dependencies. ```bash diff --git a/src/bindings-jvm/build.gradle.kts b/src/bindings-jvm/build.gradle.kts index 0eacabb8..63a757ac 100644 --- a/src/bindings-jvm/build.gradle.kts +++ b/src/bindings-jvm/build.gradle.kts @@ -1,4 +1,3 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import java.util.Properties plugins { @@ -45,14 +44,6 @@ dependencies { kotlin { jvmToolchain(21) // keep in sync with configs.yml java-version - compilerOptions { - jvmTarget.set(JvmTarget.JVM_1_8) - } -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 } // ── Source layout ─────────────────────────────────────────────────────────────── diff --git a/src/bindings-jvm/build.sh b/src/bindings-jvm/build.sh index 3639807b..b8b466e3 100755 --- a/src/bindings-jvm/build.sh +++ b/src/bindings-jvm/build.sh @@ -122,12 +122,6 @@ if [ "$CLASS_COUNT" -eq 0 ] || [ "$KT_COUNT" -eq 0 ]; then echo "Error: $JAR_FILE is missing compiled output - $CLASS_COUNT .class and $KT_COUNT .kt entries (both must be non-zero)." >&2 exit 1 fi -PUBLIC_API_CLASS="software.amazon.cloudformation.validate.ApiKt" -CLASS_FILE_MAJOR=$(javap -classpath "$JAR_FILE" -verbose "$PUBLIC_API_CLASS" | awk '/major version:/ { print $3; exit }') -if [ "$CLASS_FILE_MAJOR" != "52" ]; then - echo "Error: $JAR_FILE must target Java 8 classfile version 52, found $CLASS_FILE_MAJOR." >&2 - exit 1 -fi for required_metadata in LICENSE NOTICE README.md THIRD-PARTY-LICENSES.txt; do if ! jar tf "$JAR_FILE" | grep -Fxq "META-INF/$required_metadata"; then echo "Error: $JAR_FILE is missing META-INF/$required_metadata" >&2 @@ -143,7 +137,7 @@ JAR_SIZE=$(du -sh "$JAR_FILE" | cut -f1) echo "" echo "Build complete: $GENERATED_DIR" echo " Kotlin sources: $KT_SIZE ($KT_COUNT .kt files bundled)" -echo " Compiled classes: $CLASS_COUNT .class entries bundled (Java 8 bytecode)" +echo " Compiled classes: $CLASS_COUNT .class entries bundled" echo " Native library: $LIB_SIZE ($LIB_NAME, bundled in jar)" echo " JAR: $JAR_SIZE ($(basename "$JAR_FILE"))" echo "" diff --git a/src/bindings-jvm/uniffi.toml b/src/bindings-jvm/uniffi.toml index b8964653..5d7d9ef6 100644 --- a/src/bindings-jvm/uniffi.toml +++ b/src/bindings-jvm/uniffi.toml @@ -1,7 +1,6 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate" generate_immutable_records = true -disable_java_cleaner = true [bindings.kotlin.external_packages] diagnostics = "software.amazon.cloudformation.validate.diagnostics" diff --git a/src/bindings-python/README.md b/src/bindings-python/README.md index c019409a..10cdd411 100644 --- a/src/bindings-python/README.md +++ b/src/bindings-python/README.md @@ -17,7 +17,7 @@ Available on [PyPI](https://pypi.org/project/cloudformation-validate/) as `cloud pip install cloudformation-validate ``` -Requires Python 3.10+ and has no runtime dependencies. PyPI publishes a separate wheel for every supported native +Requires Python 3.9+ and has no runtime dependencies. PyPI publishes a separate wheel for every supported native target. Each wheel carries exactly one native library and an accurate platform tag, so pip downloads only the artifact compatible with the installer host. diff --git a/src/bindings-python/build.sh b/src/bindings-python/build.sh index a1d3662d..1d25b878 100755 --- a/src/bindings-python/build.sh +++ b/src/bindings-python/build.sh @@ -68,8 +68,8 @@ EOF # ── Prerequisites ───────────────────────────────────────────────────────────── command -v "$PYTHON" &>/dev/null || { echo "Error: $PYTHON not found on PATH" >&2; exit 1; } command -v unzip &>/dev/null || { echo "Error: unzip not found on PATH" >&2; exit 1; } -"$PYTHON" -c 'import sys; sys.exit(0 if sys.version_info >= (3, 10) else 1)' \ - || { echo "Error: Python 3.10+ required, found $("$PYTHON" --version)" >&2; exit 1; } +"$PYTHON" -c 'import sys; sys.exit(0 if sys.version_info >= (3, 9) else 1)' \ + || { echo "Error: Python 3.9+ required, found $("$PYTHON" --version)" >&2; exit 1; } "$PYTHON" -m pip --version &>/dev/null \ || { echo "Error: pip not available ($PYTHON -m pip failed)" >&2; exit 1; } diff --git a/src/bindings-python/pyproject.toml b/src/bindings-python/pyproject.toml index 96d6f15f..17615115 100644 --- a/src/bindings-python/pyproject.toml +++ b/src/bindings-python/pyproject.toml @@ -9,7 +9,7 @@ description = "Fast, offline, embeddable validation for AWS CloudFormation templ readme = "README.md" license = "Apache-2.0" license-files = ["LICENSE", "NOTICE", "THIRD-PARTY-LICENSES.txt"] -requires-python = ">=3.10" +requires-python = ">=3.9" authors = [{ name = "Amazon Web Services" }] keywords = [ "aws", @@ -30,6 +30,7 @@ classifiers = [ "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Rust", "Topic :: Software Development :: Libraries :: Python Modules", diff --git a/src/data-source/uniffi.toml b/src/data-source/uniffi.toml index f0c156ff..f7802e9c 100644 --- a/src/data-source/uniffi.toml +++ b/src/data-source/uniffi.toml @@ -1,4 +1,3 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate.datasource" generate_immutable_records = true -disable_java_cleaner = true diff --git a/src/diagnostics/uniffi.toml b/src/diagnostics/uniffi.toml index db9d160e..00a38af0 100644 --- a/src/diagnostics/uniffi.toml +++ b/src/diagnostics/uniffi.toml @@ -1,7 +1,6 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate.diagnostics" generate_immutable_records = true -disable_java_cleaner = true [bindings.kotlin.external_packages] rules = "software.amazon.cloudformation.validate.rules" diff --git a/src/rules/uniffi.toml b/src/rules/uniffi.toml index 96a21b0b..e1357541 100644 --- a/src/rules/uniffi.toml +++ b/src/rules/uniffi.toml @@ -1,4 +1,3 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate.rules" generate_immutable_records = true -disable_java_cleaner = true diff --git a/src/schema-validator/uniffi.toml b/src/schema-validator/uniffi.toml index b1d7c48d..cee50680 100644 --- a/src/schema-validator/uniffi.toml +++ b/src/schema-validator/uniffi.toml @@ -1,7 +1,6 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate.schemavalidator" generate_immutable_records = true -disable_java_cleaner = true [bindings.kotlin.external_packages] data_source = "software.amazon.cloudformation.validate.datasource" diff --git a/src/template-model/uniffi.toml b/src/template-model/uniffi.toml index 2c7a4983..297e2738 100644 --- a/src/template-model/uniffi.toml +++ b/src/template-model/uniffi.toml @@ -1,7 +1,6 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate.templatemodel" generate_immutable_records = true -disable_java_cleaner = true [bindings.kotlin.external_packages] diagnostics = "software.amazon.cloudformation.validate.diagnostics" diff --git a/src/validation-engine/uniffi.toml b/src/validation-engine/uniffi.toml index 6aa4f76f..dee40e93 100644 --- a/src/validation-engine/uniffi.toml +++ b/src/validation-engine/uniffi.toml @@ -1,7 +1,6 @@ [bindings.kotlin] package_name = "software.amazon.cloudformation.validate.engine" generate_immutable_records = true -disable_java_cleaner = true [bindings.kotlin.external_packages] rules = "software.amazon.cloudformation.validate.rules" From afe9bd3abf7e06f29dde3b927dcb157c3c067c3a Mon Sep 17 00:00:00 2001 From: Satyaki Ghosh Date: Mon, 14 Sep 2026 13:03:03 +0000 Subject: [PATCH 19/19] fix dep --- src/bindings-python/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings-python/pyproject.toml b/src/bindings-python/pyproject.toml index 17615115..de897d50 100644 --- a/src/bindings-python/pyproject.toml +++ b/src/bindings-python/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools==83.0.0"] +requires = ["setuptools==82.0.1"] build-backend = "setuptools.build_meta" [project]