diff --git a/.changelog/39585.txt b/.changelog/39585.txt new file mode 100644 index 00000000000..54ad8c993f9 --- /dev/null +++ b/.changelog/39585.txt @@ -0,0 +1,11 @@ +```release-note:enhancement +resource/aws_lb_listener: Add `tcp_idle_timeout_seconds` argument +``` + +```release-note:enhancement +resource/aws_lb: Add `enable_zonal_shift` argument +``` + +```release-note:enhancement +data-source/aws_lb: Add `enable_zonal_shift` attribute +``` diff --git a/.changelog/39745.txt b/.changelog/39745.txt new file mode 100644 index 00000000000..0e89ee81ccd --- /dev/null +++ b/.changelog/39745.txt @@ -0,0 +1,27 @@ +```release-note:enhancement +resource/aws_elasticache_cluster: Support `valkey` as valid value for `engine` +``` + +```release-note:enhancement +resource/aws_elasticache_global_replication_group: Support Valkey versions for `engine_version` +``` + +```release-note:enhancement +resource/aws_elasticache_replication_group: Support `valkey` as valid value for `engine` +``` + +```release-note:enhancement +resource/aws_elasticache_replication_group: Support Valkey versions for `engine_version` +``` + +```release-note:enhancement +resource/aws_elasticache_serverless_cache: Support `valkey` as valid value for `engine` +``` + +```release-note:bug +resource/aws_elasticache_serverless_cache: Fix `InvalidParameterValue: This API supports only cross-engine upgrades to Valkey engine currently` errors on Update +``` + +```release-note:enhancement +data-source/aws_elasticache_reserved_cache_node_offering: Support `valkey` as valid value for `product_description` +``` diff --git a/.changelog/39782.txt b/.changelog/39782.txt new file mode 100644 index 00000000000..5ba0ea53cda --- /dev/null +++ b/.changelog/39782.txt @@ -0,0 +1,11 @@ +```release-note:enhancement +resource/aws_s3_bucket_object: Remove the call to `kms:DescribeKey` for the S3 default AWS managed key (`alias/aws/s3`) on Read +``` + +```release-note:enhancement +resource/aws_s3_object: Remove the call to `kms:DescribeKey` for the S3 default AWS managed key (`alias/aws/s3`) on Read +``` + +```release-note:enhancement +resource/aws_s3_object_copy: Remove the call to `kms:DescribeKey` for the S3 default AWS managed key (`alias/aws/s3`) on Read +``` \ No newline at end of file diff --git a/.changelog/39797.txt b/.changelog/39797.txt new file mode 100644 index 00000000000..96281531759 --- /dev/null +++ b/.changelog/39797.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_batch_job_definition: Ensure that new revisions are created with tags +``` \ No newline at end of file diff --git a/.changelog/39807.txt b/.changelog/39807.txt new file mode 100644 index 00000000000..0bcb12ff6ae --- /dev/null +++ b/.changelog/39807.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_sagemaker_hub +``` \ No newline at end of file diff --git a/.changelog/39826.txt b/.changelog/39826.txt new file mode 100644 index 00000000000..5dd6fe5eb43 --- /dev/null +++ b/.changelog/39826.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_lakeformation_data_lake_settings: Add `parameters` map argument enabling `CROSS_ACCOUNT_VERSION` to be set +``` + +```release-note:enhancement +data-source/aws_lakeformation_data_lake_settings: Add `parameters` map attribute to read `CROSS_ACCOUNT_VERSION` +``` \ No newline at end of file diff --git a/.changelog/39834.txt b/.changelog/39834.txt new file mode 100644 index 00000000000..7a367a0f7fc --- /dev/null +++ b/.changelog/39834.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_security_group_rule: Remove from state when rule not found. This fixes a regression introduced in [v5.60.0](https://github.com/hashicorp/terraform-provider-aws/blob/main/CHANGELOG.md#5600-july-25-2024) +``` \ No newline at end of file diff --git a/.changelog/39842.txt b/.changelog/39842.txt new file mode 100644 index 00000000000..39bad80a8ab --- /dev/null +++ b/.changelog/39842.txt @@ -0,0 +1,19 @@ +```release-note:bug +resource/aws_iam_policy: Fix persistent validation errors when malformed `policy` content is written to state +``` +```release-note:bug +resource/aws_iam_role_policy: Fix persistent validation errors when malformed `policy` content is written to state +``` +```release-note:bug +resource/aws_ecr_repository_policy: Fix persistent validation errors when malformed `policy` content is written to state +``` +```release-note:bug +resource/aws_secretsmanager_secret: Fix persistent validation errors when malformed `policy` content is written to state +``` +```release-note:bug +resource/aws_s3_bucket_policy: Fix persistent validation errors when malformed `policy` content is written to state +``` +```release-note:bug +resource/aws_kms_key: Fix persistent validation errors when malformed `policy` content is written to state +``` + diff --git a/.changelog/39843.txt b/.changelog/39843.txt new file mode 100644 index 00000000000..18832df3119 --- /dev/null +++ b/.changelog/39843.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_apprunner_auto_scaling_configuration_version: Remove the upper limit on `min_size` and `max_size` +``` \ No newline at end of file diff --git a/.ci/tools/go.mod b/.ci/tools/go.mod index 25294e7566c..f02cd0794c9 100644 --- a/.ci/tools/go.mod +++ b/.ci/tools/go.mod @@ -3,7 +3,7 @@ module github.com/hashicorp/terraform-provider-aws/tools go 1.23.2 require ( - github.com/YakDriver/tfproviderdocs v0.14.3 + github.com/YakDriver/tfproviderdocs v0.15.0 github.com/client9/misspell v0.3.4 github.com/golangci/golangci-lint v1.61.0 github.com/hashicorp/copywrite v0.19.0 @@ -292,7 +292,7 @@ require ( github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.3.0 // indirect github.com/ykadowak/zerologlint v0.1.5 // indirect - github.com/yuin/goldmark v1.7.4 // indirect + github.com/yuin/goldmark v1.7.8 // indirect github.com/yuin/goldmark-meta v1.1.0 // indirect github.com/zclconf/go-cty v1.15.0 // indirect github.com/zclconf/go-cty-yaml v1.0.3 // indirect diff --git a/.ci/tools/go.sum b/.ci/tools/go.sum index 943c229659b..34a2d14a652 100644 --- a/.ci/tools/go.sum +++ b/.ci/tools/go.sum @@ -238,8 +238,8 @@ github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJP github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ= github.com/ProtonMail/go-crypto v1.1.0-alpha.5-proton h1:KVBEgU3CJpmzLChnLiSuEyCuhGhcMt3eOST+7A+ckto= github.com/ProtonMail/go-crypto v1.1.0-alpha.5-proton/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/YakDriver/tfproviderdocs v0.14.3 h1:zzFPrQeKKTIbrqBuz3CY+HKnQ/GNhwQRB+9NcJyGta4= -github.com/YakDriver/tfproviderdocs v0.14.3/go.mod h1:pse9nenVl0LY7sqJFfn92takFWhGm+aEcssMtrig/YI= +github.com/YakDriver/tfproviderdocs v0.15.0 h1:4vp/0sYuC+0HFj3alyk/KNXeVhFCbtvM3WYPfNCuDxI= +github.com/YakDriver/tfproviderdocs v0.15.0/go.mod h1:mGchLKSWwB1Z0Jj+RSluxn6QpsL3+pEuUyPnt6tfL3o= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= @@ -1192,8 +1192,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= -github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= +github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0= github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= diff --git a/.github/workflows/acctest-terraform-embedded-lint.yml b/.github/workflows/acctest-terraform-embedded-lint.yml index 6d70f33ffdc..8e934d0785a 100644 --- a/.github/workflows/acctest-terraform-embedded-lint.yml +++ b/.github/workflows/acctest-terraform-embedded-lint.yml @@ -27,7 +27,7 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: @@ -55,13 +55,13 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-pkg-mod-${{ hashFiles('go.sum') }} - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 name: Cache plugin dir continue-on-error: true timeout-minutes: 2 diff --git a/.github/workflows/acctest-terraform-lint.yml b/.github/workflows/acctest-terraform-lint.yml index 9d2a34304f5..73c9406935c 100644 --- a/.github/workflows/acctest-terraform-lint.yml +++ b/.github/workflows/acctest-terraform-lint.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: @@ -45,13 +45,13 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-pkg-mod-${{ hashFiles('go.sum') }} - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 name: Cache plugin dir continue-on-error: true timeout-minutes: 2 diff --git a/.github/workflows/changelog_misspell.yml b/.github/workflows/changelog_misspell.yml index e0d19d5a962..b58f795ad49 100644 --- a/.github/workflows/changelog_misspell.yml +++ b/.github/workflows/changelog_misspell.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: diff --git a/.github/workflows/copyright.yml b/.github/workflows/copyright.yml index b471de8a934..8fa909f7a48 100644 --- a/.github/workflows/copyright.yml +++ b/.github/workflows/copyright.yml @@ -33,13 +33,13 @@ jobs: - name: go env run: | echo "GOCACHE=$(go env GOCACHE)" >> $GITHUB_ENV - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: path: ${{ env.GOCACHE }} key: ${{ runner.os }}-GOCACHE-${{ hashFiles('go.sum') }}-${{ hashFiles('internal/**') }} - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index d7d34227572..4416ab5f5fb 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -47,7 +47,7 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index a92d788085c..a4c3e13c799 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -27,7 +27,7 @@ jobs: - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 0 - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-pkg-mod-${{ hashFiles('go.sum') }} @@ -38,7 +38,7 @@ jobs: - name: install tflint run: cd .ci/tools && go install github.com/terraform-linters/tflint - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 name: Cache plugin dir with: path: ~/.tflint.d/plugins @@ -64,7 +64,7 @@ jobs: - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 0 - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-pkg-mod-${{ hashFiles('go.sum') }} diff --git a/.github/workflows/goreleaser-ci.yml b/.github/workflows/goreleaser-ci.yml index e2be5bf77e8..68125506cff 100644 --- a/.github/workflows/goreleaser-ci.yml +++ b/.github/workflows/goreleaser-ci.yml @@ -41,7 +41,7 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: @@ -61,7 +61,7 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: diff --git a/.github/workflows/provider.yml b/.github/workflows/provider.yml index 3a9cc073468..528d3be1eca 100644 --- a/.github/workflows/provider.yml +++ b/.github/workflows/provider.yml @@ -41,7 +41,7 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true id: cache-go-pkg-mod timeout-minutes: 2 @@ -57,7 +57,7 @@ jobs: runs-on: custom-linux-medium steps: - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true id: cache-terraform-plugin-dir timeout-minutes: 2 @@ -74,12 +74,12 @@ jobs: run: | echo "GOCACHE=$(go env GOCACHE)" >> $GITHUB_ENV - if: steps.cache-terraform-plugin-dir.outputs.cache-hit != 'true' || steps.cache-terraform-plugin-dir.outcome == 'failure' - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 with: path: ${{ env.GOCACHE }} key: ${{ runner.os }}-GOCACHE-${{ hashFiles('go.sum') }}-${{ hashFiles('internal/**') }} - if: steps.cache-terraform-plugin-dir.outputs.cache-hit != 'true' || steps.cache-terraform-plugin-dir.outcome == 'failure' - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-pkg-mod-${{ hashFiles('go.sum') }} @@ -100,13 +100,13 @@ jobs: - name: go env run: | echo "GOCACHE=$(go env GOCACHE)" >> $GITHUB_ENV - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: path: ${{ env.GOCACHE }} key: ${{ runner.os }}-GOCACHE-${{ hashFiles('go.sum') }}-${{ hashFiles('internal/**') }} - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: @@ -134,13 +134,13 @@ jobs: - name: go env run: | echo "GOCACHE=$(go env GOCACHE)" >> $GITHUB_ENV - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: path: ${{ env.GOCACHE }} key: ${{ runner.os }}-GOCACHE-${{ hashFiles('go.sum') }}-${{ hashFiles('internal/**') }} - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: @@ -161,13 +161,13 @@ jobs: - name: go env run: | echo "GOCACHE=$(go env GOCACHE)" >> $GITHUB_ENV - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: path: ${{ env.GOCACHE }} key: ${{ runner.os }}-GOCACHE-${{ hashFiles('go.sum') }}-${{ hashFiles('internal/**') }} - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: @@ -194,13 +194,13 @@ jobs: - name: go env run: | echo "GOCACHE=$(go env GOCACHE)" >> $GITHUB_ENV - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: path: ${{ env.GOCACHE }} key: ${{ runner.os }}-GOCACHE-${{ hashFiles('go.sum') }}-${{ hashFiles('internal/**') }} - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: @@ -216,7 +216,7 @@ jobs: (echo; echo "Expected `strings` to detect sweeper function names in sweeper binary."; exit 1) # Use cached provider or rebuild - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a continue-on-error: true id: cache-terraform-plugin-dir timeout-minutes: 2 @@ -243,7 +243,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true id: cache-terraform-providers-schema timeout-minutes: 2 @@ -251,7 +251,7 @@ jobs: path: terraform-providers-schema key: ${{ runner.os }}-terraform-providers-schema-${{ hashFiles('go.sum') }}-${{ hashFiles('internal/**') }} - if: steps.cache-terraform-providers-schema.outputs.cache-hit != 'true' || steps.cache-terraform-providers-schema.outcome == 'failure' - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 timeout-minutes: 2 with: path: terraform-plugin-dir @@ -285,14 +285,14 @@ jobs: with: terraform_version: ${{ env.TERRAFORM_VERSION }} terraform_wrapper: false - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-pkg-mod-${{ hashFiles('go.sum') }} - run: cd .ci/tools && go install github.com/YakDriver/tfproviderdocs - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 timeout-minutes: 2 with: path: terraform-providers-schema @@ -318,7 +318,7 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 3 with: diff --git a/.github/workflows/providerlint.yml b/.github/workflows/providerlint.yml index 6ff9351e031..5c794a00aee 100644 --- a/.github/workflows/providerlint.yml +++ b/.github/workflows/providerlint.yml @@ -29,13 +29,13 @@ jobs: go-version-file: go.mod - name: go env run: echo "GOCACHE=$(go env GOCACHE)" >> $GITHUB_ENV - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: path: ${{ env.GOCACHE }} key: ${{ runner.os }}-GOCACHE-${{ hashFiles('go.sum') }}-${{ hashFiles('internal/**') }} - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: diff --git a/.github/workflows/skaff.yml b/.github/workflows/skaff.yml index 27f35382a09..168d47e7200 100644 --- a/.github/workflows/skaff.yml +++ b/.github/workflows/skaff.yml @@ -31,13 +31,13 @@ jobs: - name: go env run: | echo "GOCACHE=$(go env GOCACHE)" >> $GITHUB_ENV - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: path: ${{ env.GOCACHE }} key: ${{ runner.os }}-GOCACHE-${{ hashFiles('go.sum') }}-${{ hashFiles('internal/**') }} - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index db0f45928ae..5ba8aa04052 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index dce97a44641..bfb255ad53c 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -90,7 +90,7 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: .ci/tools/go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: @@ -106,7 +106,7 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: .ci/tools/go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: @@ -124,7 +124,7 @@ jobs: - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: .ci/tools/go.mod - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 continue-on-error: true timeout-minutes: 2 with: @@ -134,7 +134,7 @@ jobs: - run: cd .ci/tools && go install github.com/katbyte/terrafmt - run: cd .ci/tools && go install github.com/terraform-linters/tflint - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 name: Cache plugin dir with: path: ~/.tflint.d/plugins diff --git a/CHANGELOG.md b/CHANGELOG.md index 45ce2bdbc98..103a365fae2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,23 @@ FEATURES: * **New Data Source:** `aws_ssm_patch_baselines` ([#39779](https://github.com/hashicorp/terraform-provider-aws/issues/39779)) * **New Resource:** `aws_imagebuilder_lifecycle_policy` ([#35674](https://github.com/hashicorp/terraform-provider-aws/issues/35674)) * **New Resource:** `aws_resiliencehub_resiliency_policy` ([#38913](https://github.com/hashicorp/terraform-provider-aws/issues/38913)) +* **New Resource:** `aws_sagemaker_hub` ([#39807](https://github.com/hashicorp/terraform-provider-aws/issues/39807)) * **New Resource:** `aws_sagemaker_mlflow_tracking_server` ([#39796](https://github.com/hashicorp/terraform-provider-aws/issues/39796)) ENHANCEMENTS: +* data-source/aws_elasticache_reserved_cache_node_offering: Support `valkey` as valid value for `product_description` ([#39745](https://github.com/hashicorp/terraform-provider-aws/issues/39745)) +* data-source/aws_lakeformation_data_lake_settings: Add `parameters` map attribute to read `CROSS_ACCOUNT_VERSION` ([#39826](https://github.com/hashicorp/terraform-provider-aws/issues/39826)) +* resource/aws_batch_job_definition: Ensure that new revisions are created with tags ([#39797](https://github.com/hashicorp/terraform-provider-aws/issues/39797)) +* resource/aws_elasticache_cluster: Support `valkey` as valid value for `engine` ([#39745](https://github.com/hashicorp/terraform-provider-aws/issues/39745)) +* resource/aws_elasticache_global_replication_group: Support Valkey versions for `engine_version` ([#39745](https://github.com/hashicorp/terraform-provider-aws/issues/39745)) +* resource/aws_elasticache_replication_group: Support Valkey versions for `engine_version` ([#39745](https://github.com/hashicorp/terraform-provider-aws/issues/39745)) +* resource/aws_elasticache_replication_group: Support `valkey` as valid value for `engine` ([#39745](https://github.com/hashicorp/terraform-provider-aws/issues/39745)) +* resource/aws_elasticache_serverless_cache: Support `valkey` as valid value for `engine` ([#39745](https://github.com/hashicorp/terraform-provider-aws/issues/39745)) +* resource/aws_lakeformation_data_lake_settings: Add `parameters` map argument enabling `CROSS_ACCOUNT_VERSION` to be set ([#39826](https://github.com/hashicorp/terraform-provider-aws/issues/39826)) * resource/aws_sagemaker_domain: Add `default_user_settings.jupyter_lab_app_settings.app_lifecycle_management`, `default_user_settings.jupyter_lab_app_settings.built_in_lifecycle_config_arn`, `default_user_settings.jupyter_lab_app_settings.emr_settings`, `default_space_settings.jupyter_lab_app_settings.app_lifecycle_management`, `default_space_settings.jupyter_lab_app_settings.built_in_lifecycle_config_arn`, `default_space_settings.jupyter_lab_app_settings.emr_settings`, `default_user_settings.auto_mount_home_efs`, `default_user_settings.canvas_app_settings.emr_serverless_settings`, `default_user_settings.studio_web_portal_settings.hidden_instance_types`, `default_user_settings.code_editor_app_settings.app_lifecycle_management`, `default_user_settings.code_editor_app_settings.built_in_lifecycle_config_arn`, and `tag_propagation` arguments ([#39774](https://github.com/hashicorp/terraform-provider-aws/issues/39774)) * resource/aws_sagemaker_domain: Allow `app_network_access_type` and `app_security_group_management` to be updated in-place ([#39774](https://github.com/hashicorp/terraform-provider-aws/issues/39774)) +* resource/aws_sagemaker_feature_group: Add `feature_definition.collection_config`, `feature_definition.collection_type`, and `throughput_config` arguments ([#39805](https://github.com/hashicorp/terraform-provider-aws/issues/39805)) * resource/aws_sagemaker_space: Add `space_settings.code_editor_app_settings.app_lifecycle_management` and `space_settings.jupyter_lab_app_settings.app_lifecycle_management` arguments ([#39800](https://github.com/hashicorp/terraform-provider-aws/issues/39800)) * resource/aws_sagemaker_user_profile: Add `user_settings.auto_mount_home_efs`, `user_settings.canvas_app_settings.emr_serverless_settings`, `user_settings.code_editor_app_settings.app_lifecycle_management`, `user_settings.code_editor_app_settings.built_in_lifecycle_config_arn`, `user_settings.jupyter_lab_app_settings.app_lifecycle_management`, `user_settings.jupyter_lab_app_settings.built_in_lifecycle_config_arn`, `user_settings.jupyter_lab_app_settings.emr_settings` and `user_settings.studio_web_portal_settings.hidden_instance_types` arguments ([#39774](https://github.com/hashicorp/terraform-provider-aws/issues/39774)) @@ -18,8 +29,16 @@ BUG FIXES: * data-source/aws_workspaces_bundle: Return the first matching bundle when searching by `name`. This fixes a regression introduced in [v5.72.0](https://github.com/hashicorp/terraform-provider-aws/blob/main/CHANGELOG.md#5720-october-15-2024) causing `multiple WorkSpaces Bundles matched; use additional constraints to reduce matches to a single WorkSpaces Bundle` errors ([#39777](https://github.com/hashicorp/terraform-provider-aws/issues/39777)) * resource/aws_dynamodb_table: Fix validation error when optional attribute in `on_demand_throughput` is excluded ([#39784](https://github.com/hashicorp/terraform-provider-aws/issues/39784)) +* resource/aws_ecr_repository_policy: Fix persistent validation errors when malformed `policy` content is written to state ([#39842](https://github.com/hashicorp/terraform-provider-aws/issues/39842)) +* resource/aws_elasticache_serverless_cache: Fix `InvalidParameterValue: This API supports only cross-engine upgrades to Valkey engine currently` errors on Update ([#39745](https://github.com/hashicorp/terraform-provider-aws/issues/39745)) +* resource/aws_iam_policy: Fix persistent validation errors when malformed `policy` content is written to state ([#39842](https://github.com/hashicorp/terraform-provider-aws/issues/39842)) +* resource/aws_iam_role_policy: Fix persistent validation errors when malformed `policy` content is written to state ([#39842](https://github.com/hashicorp/terraform-provider-aws/issues/39842)) +* resource/aws_kms_key: Fix persistent validation errors when malformed `policy` content is written to state ([#39842](https://github.com/hashicorp/terraform-provider-aws/issues/39842)) * resource/aws_quicksight_data_set: Fix `InvalidParameterValueException: Invalid RowLevelPermissionDataSet. Namespace parameter should not be specified for Version 2` errors on Create and Update ([#39778](https://github.com/hashicorp/terraform-provider-aws/issues/39778)) * resource/aws_route53_record: Allow creation of records with `ttl=0` ([#39728](https://github.com/hashicorp/terraform-provider-aws/issues/39728)) +* resource/aws_s3_bucket_policy: Fix persistent validation errors when malformed `policy` content is written to state ([#39842](https://github.com/hashicorp/terraform-provider-aws/issues/39842)) +* resource/aws_secretsmanager_secret: Fix persistent validation errors when malformed `policy` content is written to state ([#39842](https://github.com/hashicorp/terraform-provider-aws/issues/39842)) +* resource/aws_security_group_rule: Remove from state when rule not found. This fixes a regression introduced in [v5.60.0](https://github.com/hashicorp/terraform-provider-aws/blob/main/CHANGELOG.md#5600-july-25-2024) ([#39834](https://github.com/hashicorp/terraform-provider-aws/issues/39834)) ## 5.72.1 (October 16, 2024) diff --git a/ROADMAP.md b/ROADMAP.md index 402c6dcd5ff..58cdb98d663 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -24,7 +24,7 @@ From Feb - April 2024, we will be prioritizing the following areas of work: Issue: [#36033](https://github.com/hashicorp/terraform-provider-aws/issues/36033) -[Resource Explorer](https://aws.amazon.com/resourceexplorer/) Use AWS Resource Explorer to more easily search for and discover your resources across AWS Regions and accounts, such as Amazon Elastic Compute Cloud (Amazon EC2) instances, Amazon Kinesis streams, and Amazon DynamoDB tables.. +[Resource Explorer](https://aws.amazon.com/resourceexplorer/) Use AWS Resource Explorer to more easily search for and discover your resources across AWS Regions and accounts, such as Amazon Elastic Compute Cloud (Amazon EC2) instances, Amazon Kinesis streams, and Amazon DynamoDB tables. Support for additional Resource explorer resources may include: diff --git a/docs/index.md b/docs/index.md index 498e765eb0e..7ad80bcce8a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -29,8 +29,8 @@ Follow the guide for your contribution type and refer to the Development Referen | [Resources](add-a-new-resource.md) | Allow the management of a logical resource within AWS by adding a new resource to the Terraform AWS Provider. | | [Data Source](add-a-new-datasource.md) | Let your Terraform configurations use data from resources not under local management by creating ready only data sources. | | [Services](add-a-new-service.md) | Allow Terraform (via the AWS Provider) to manage an entirely new AWS service by introducing the resources and data sources required to manage configuration of the service. | -| [AWS Region](add-a-new-region.md) | New regions are immediately usable with the provider with the caveat that a configuration workaround is required to skip validation of the region during cli operations. A small set of changes are required to makes this workaround necessary. | -| [Resource Name Generation](resource-name-generation.md) | Allow a resource to either fully, or partially, generate its own resource names. This can be useful in cases where the resource name uniquely identifes the resource and it needs to be recreated. It can also be used when a name is required, but the specific name is not important. | +| [AWS Region](add-a-new-region.md) | New regions are immediately usable with the provider with the caveat that a configuration workaround is required to skip validation of the region during cli operations. A small set of changes are required to make this workaround necessary. | +| [Resource Name Generation](resource-name-generation.md) | Allow a resource to either fully, or partially, generate its own resource names. This can be useful in cases where the resource name uniquely identifies the resource and it needs to be recreated. It can also be used when a name is required, but the specific name is not important. | | [Tagging Support](resource-tagging.md) | Many AWS resources allow assigning metadata via tags. However, frequently AWS services are launched without tagging support so this will often need to be added later. | | [Import Support](add-import-support.md) | Adding import support allows `terraform import` to be run targeting an existing unmanaged resource and pulling its configuration into Terraform state. Typically import support is added during initial resource implementation but in some cases this will need to be added later. | | [Documentation Changes](documentation-changes.md)| The provider documentation is displayed on the [Terraform Registry](https://registry.terraform.io/providers/hashicorp/aws/latest) and is sourced and refreshed from the provider repository during the release process. | diff --git a/docs/resource-tagging.md b/docs/resource-tagging.md index 2e2b83a687b..2b2e3ef1d02 100644 --- a/docs/resource-tagging.md +++ b/docs/resource-tagging.md @@ -543,8 +543,10 @@ In that case, add the annotations `@Testing(existsTakesT=true)` and `@Testing(de The generated acceptance tests use `ImportState` steps. In most cases, these will work as-is. To ignore the values of certain parameters when importing, set the annotation `@Testing(importIgnore="...")` to a list of the parameter names separated by semi-colons (`;`). -To override the import ID, use the annotation `@Testing(importStateId=)` if it can be retrieved from an existing variable, -or use `@Testing(importStateIdFunc=)` to reference a function that returns a `resource.ImportStateIdFunc`. +There are multiple methods for overriding the import ID, if needed. +To use the value of an existing variable, use the annotation `@Testing(importStateId=)`. +If the identifier can be retrieved from a specific resource attribute, use the annotation `@Testing(importStateIdAttribute=)`. +If the identifier can be retrieved from a `resource.ImportStateIdFunc`, use the annotation `@Testing(importStateIdFunc=)`. If the resource type does not support importing, use the annotation `@Testing(noImport=true)`. If the tests need to be serialized, use the annotion `@Testing(serialize=true)`. diff --git a/go.mod b/go.mod index b2616287fa7..2190d2feeb9 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/appflow v1.45.3 github.com/aws/aws-sdk-go-v2/service/appintegrations v1.30.2 github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.33.2 - github.com/aws/aws-sdk-go-v2/service/applicationinsights v1.28.2 + github.com/aws/aws-sdk-go-v2/service/applicationinsights v1.29.0 github.com/aws/aws-sdk-go-v2/service/applicationsignals v1.6.2 github.com/aws/aws-sdk-go-v2/service/appmesh v1.29.2 github.com/aws/aws-sdk-go-v2/service/apprunner v1.32.2 @@ -37,7 +37,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/appsync v1.38.2 github.com/aws/aws-sdk-go-v2/service/athena v1.48.0 github.com/aws/aws-sdk-go-v2/service/auditmanager v1.37.2 - github.com/aws/aws-sdk-go-v2/service/autoscaling v1.45.2 + github.com/aws/aws-sdk-go-v2/service/autoscaling v1.46.0 github.com/aws/aws-sdk-go-v2/service/autoscalingplans v1.24.2 github.com/aws/aws-sdk-go-v2/service/backup v1.39.3 github.com/aws/aws-sdk-go-v2/service/batch v1.46.2 @@ -78,12 +78,12 @@ require ( github.com/aws/aws-sdk-go-v2/service/configservice v1.50.2 github.com/aws/aws-sdk-go-v2/service/connect v1.113.2 github.com/aws/aws-sdk-go-v2/service/connectcases v1.21.2 - github.com/aws/aws-sdk-go-v2/service/controltower v1.18.2 + github.com/aws/aws-sdk-go-v2/service/controltower v1.18.3 github.com/aws/aws-sdk-go-v2/service/costandusagereportservice v1.28.2 github.com/aws/aws-sdk-go-v2/service/costexplorer v1.43.2 github.com/aws/aws-sdk-go-v2/service/costoptimizationhub v1.10.2 github.com/aws/aws-sdk-go-v2/service/customerprofiles v1.42.2 - github.com/aws/aws-sdk-go-v2/service/databasemigrationservice v1.43.0 + github.com/aws/aws-sdk-go-v2/service/databasemigrationservice v1.44.0 github.com/aws/aws-sdk-go-v2/service/databrew v1.33.2 github.com/aws/aws-sdk-go-v2/service/dataexchange v1.33.0 github.com/aws/aws-sdk-go-v2/service/datapipeline v1.25.2 @@ -100,12 +100,12 @@ require ( github.com/aws/aws-sdk-go-v2/service/docdbelastic v1.13.2 github.com/aws/aws-sdk-go-v2/service/drs v1.30.2 github.com/aws/aws-sdk-go-v2/service/dynamodb v1.36.2 - github.com/aws/aws-sdk-go-v2/service/ec2 v1.183.0 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.184.0 github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2 github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.27.2 github.com/aws/aws-sdk-go-v2/service/ecs v1.47.4 github.com/aws/aws-sdk-go-v2/service/efs v1.33.2 - github.com/aws/aws-sdk-go-v2/service/eks v1.50.2 + github.com/aws/aws-sdk-go-v2/service/eks v1.51.0 github.com/aws/aws-sdk-go-v2/service/elasticache v1.43.0 github.com/aws/aws-sdk-go-v2/service/elasticbeanstalk v1.28.2 github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.28.2 @@ -120,7 +120,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/finspace v1.28.2 github.com/aws/aws-sdk-go-v2/service/firehose v1.34.2 github.com/aws/aws-sdk-go-v2/service/fis v1.30.2 - github.com/aws/aws-sdk-go-v2/service/fms v1.37.2 + github.com/aws/aws-sdk-go-v2/service/fms v1.38.0 github.com/aws/aws-sdk-go-v2/service/fsx v1.49.2 github.com/aws/aws-sdk-go-v2/service/gamelift v1.36.2 github.com/aws/aws-sdk-go-v2/service/glacier v1.26.2 @@ -133,7 +133,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/healthlake v1.28.2 github.com/aws/aws-sdk-go-v2/service/iam v1.37.2 github.com/aws/aws-sdk-go-v2/service/identitystore v1.27.2 - github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.37.3 + github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.38.0 github.com/aws/aws-sdk-go-v2/service/inspector v1.25.2 github.com/aws/aws-sdk-go-v2/service/inspector2 v1.32.2 github.com/aws/aws-sdk-go-v2/service/internetmonitor v1.19.2 @@ -160,7 +160,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.2 github.com/aws/aws-sdk-go-v2/service/location v1.42.2 github.com/aws/aws-sdk-go-v2/service/lookoutmetrics v1.31.2 - github.com/aws/aws-sdk-go-v2/service/m2 v1.17.2 + github.com/aws/aws-sdk-go-v2/service/m2 v1.18.0 github.com/aws/aws-sdk-go-v2/service/macie2 v1.43.2 github.com/aws/aws-sdk-go-v2/service/mediaconnect v1.35.2 github.com/aws/aws-sdk-go-v2/service/mediaconvert v1.61.2 @@ -196,7 +196,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/quicksight v1.77.0 github.com/aws/aws-sdk-go-v2/service/ram v1.29.2 github.com/aws/aws-sdk-go-v2/service/rbin v1.20.2 - github.com/aws/aws-sdk-go-v2/service/rds v1.87.3 + github.com/aws/aws-sdk-go-v2/service/rds v1.88.0 github.com/aws/aws-sdk-go-v2/service/redshift v1.50.0 github.com/aws/aws-sdk-go-v2/service/redshiftdata v1.30.2 github.com/aws/aws-sdk-go-v2/service/redshiftserverless v1.23.2 @@ -253,7 +253,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/vpclattice v1.12.2 github.com/aws/aws-sdk-go-v2/service/waf v1.25.2 github.com/aws/aws-sdk-go-v2/service/wafregional v1.25.2 - github.com/aws/aws-sdk-go-v2/service/wafv2 v1.54.2 + github.com/aws/aws-sdk-go-v2/service/wafv2 v1.55.0 github.com/aws/aws-sdk-go-v2/service/wellarchitected v1.34.2 github.com/aws/aws-sdk-go-v2/service/worklink v1.23.2 github.com/aws/aws-sdk-go-v2/service/workspaces v1.48.2 diff --git a/go.sum b/go.sum index 2faa4f9366b..bea9491a6d5 100644 --- a/go.sum +++ b/go.sum @@ -70,8 +70,8 @@ github.com/aws/aws-sdk-go-v2/service/appintegrations v1.30.2 h1:r/++KdfZx8Woi3pI github.com/aws/aws-sdk-go-v2/service/appintegrations v1.30.2/go.mod h1:bcwWgN13kk3Y7iq5stSA+joIygO0/QK7d18ohTEF188= github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.33.2 h1:douQmMbPFGG5AxFDoWdImN3T+8N1z32hkQXPIL7NKGs= github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.33.2/go.mod h1:yg5Zb3dlzZMV7FsmL5fI82xPgUa62sQP4MeQwTS3NRk= -github.com/aws/aws-sdk-go-v2/service/applicationinsights v1.28.2 h1:gOn6bFXWTsGHSBMdMUU5hwmZzop650HoYPq0JSqM+so= -github.com/aws/aws-sdk-go-v2/service/applicationinsights v1.28.2/go.mod h1:6igoPKTMLmlS5UzkIaVsJYLXQjSQAVs75HiY2xdJyKI= +github.com/aws/aws-sdk-go-v2/service/applicationinsights v1.29.0 h1:eI1i6YZDDwAd+wN2v40yWwzRd7DMVPO3227233q9vgA= +github.com/aws/aws-sdk-go-v2/service/applicationinsights v1.29.0/go.mod h1:6igoPKTMLmlS5UzkIaVsJYLXQjSQAVs75HiY2xdJyKI= github.com/aws/aws-sdk-go-v2/service/applicationsignals v1.6.2 h1:VhZVJ0HiiWieoahI0LC+/rrAkgwPpuWw37HlVxs8OrM= github.com/aws/aws-sdk-go-v2/service/applicationsignals v1.6.2/go.mod h1:GzJ9Fze7pxMh9r8wIjFWWDpR7LidenXT5/xEAlZjBkQ= github.com/aws/aws-sdk-go-v2/service/appmesh v1.29.2 h1:YWAxOgkdNG0+K25He+mU3QnxoB7HAGoqDOSLkIA4Ix8= @@ -86,8 +86,8 @@ github.com/aws/aws-sdk-go-v2/service/athena v1.48.0 h1:oGuR6wuok/T96ISXwQZ64jA83 github.com/aws/aws-sdk-go-v2/service/athena v1.48.0/go.mod h1:bmGCoIiNCRnfKa6T64nCTKrWOtw362tPUKDpq/fjMDU= github.com/aws/aws-sdk-go-v2/service/auditmanager v1.37.2 h1:n6IT2Q6DJg+540tWwIBnJHMrO3Vl5nu+HafGNNo077c= github.com/aws/aws-sdk-go-v2/service/auditmanager v1.37.2/go.mod h1:IZovD0WWpMx5oFy87RwgPPQcnMCQsiFHir7C9SCV50E= -github.com/aws/aws-sdk-go-v2/service/autoscaling v1.45.2 h1:NurelK9YO5OB0HlIUj1ycEZJIFNvkuG5iJjo7/r6AJ4= -github.com/aws/aws-sdk-go-v2/service/autoscaling v1.45.2/go.mod h1:YmWinWbpoVdOgnBZQFeZJ2l4kT97lvnRlTvX2zyyBfc= +github.com/aws/aws-sdk-go-v2/service/autoscaling v1.46.0 h1:HQ0OvPxqTh2mYKRx4BappkCeLBU+E6oWAKSJ5JpP03c= +github.com/aws/aws-sdk-go-v2/service/autoscaling v1.46.0/go.mod h1:YmWinWbpoVdOgnBZQFeZJ2l4kT97lvnRlTvX2zyyBfc= github.com/aws/aws-sdk-go-v2/service/autoscalingplans v1.24.2 h1:adYgaCwIbN816Nxyjhfh4P+YkbgoWX5u/+195qOeDH8= github.com/aws/aws-sdk-go-v2/service/autoscalingplans v1.24.2/go.mod h1:FJIWPT79C+J0TC0svBOcVZi3agyv/t8ej+nZekrTItU= github.com/aws/aws-sdk-go-v2/service/backup v1.39.3 h1:mjYDN7CPl0CHrTBe36EKqAAGGXAKy8MXuNF+2qHkVdM= @@ -168,8 +168,8 @@ github.com/aws/aws-sdk-go-v2/service/connect v1.113.2 h1:h45hg2xWdzhr+eFn7aqfg26 github.com/aws/aws-sdk-go-v2/service/connect v1.113.2/go.mod h1:Cr/NBm+YdRHJg0uHNTIDEF++8IQL207oUsjhTAyS4zE= github.com/aws/aws-sdk-go-v2/service/connectcases v1.21.2 h1:BtBIsvj3F0LrsDoLPSZO9qanyXYoDFmXCNJ3I+X80XI= github.com/aws/aws-sdk-go-v2/service/connectcases v1.21.2/go.mod h1:ZrU0ZA81zcKXOUnC8rvjYddEFB3pl7t9tYCdtIRFAb4= -github.com/aws/aws-sdk-go-v2/service/controltower v1.18.2 h1:IZi4ivxDbOAfCZvEeNhvYwrBANiYF9RE35yczZb4f0Y= -github.com/aws/aws-sdk-go-v2/service/controltower v1.18.2/go.mod h1:15mOn+tmGhx9+TidoQaa8gSBItjFx/A5XT9GV1L+LPc= +github.com/aws/aws-sdk-go-v2/service/controltower v1.18.3 h1:v4aBi2Jw4vGmLv58u5YZduJbbErX8a0ML1+RRWI7Sko= +github.com/aws/aws-sdk-go-v2/service/controltower v1.18.3/go.mod h1:15mOn+tmGhx9+TidoQaa8gSBItjFx/A5XT9GV1L+LPc= github.com/aws/aws-sdk-go-v2/service/costandusagereportservice v1.28.2 h1:i7yTib7O8olbSmTyW2c5C2asN6GUfMI2RLmdKsc/3e4= github.com/aws/aws-sdk-go-v2/service/costandusagereportservice v1.28.2/go.mod h1:2Ux5wul+bF0/JIAATf1cv3PWJatZqi/dFt7bRSeYEcU= github.com/aws/aws-sdk-go-v2/service/costexplorer v1.43.2 h1:Tg7kuCaHMCFOW+HhK/maIH7qtkeDbzahqQOUO9c+/L8= @@ -178,8 +178,8 @@ github.com/aws/aws-sdk-go-v2/service/costoptimizationhub v1.10.2 h1:3MtTruznyGml github.com/aws/aws-sdk-go-v2/service/costoptimizationhub v1.10.2/go.mod h1:khyqDEa/5mT22FwodCCtZN4n+9C62VXwqNiPeAkqYlY= github.com/aws/aws-sdk-go-v2/service/customerprofiles v1.42.2 h1:dHQXIHPlijJIBUubNpShhIUooNDUKB/B8nJ6OSsJfmQ= github.com/aws/aws-sdk-go-v2/service/customerprofiles v1.42.2/go.mod h1:n9F5nRLYyqBz1khsBptXyceq6ZJG9xqzl2pIyVqYq/8= -github.com/aws/aws-sdk-go-v2/service/databasemigrationservice v1.43.0 h1:jb9QBxC7Xh0cSpprVOtzJtlyVbJrnLs0oAplby2sZ78= -github.com/aws/aws-sdk-go-v2/service/databasemigrationservice v1.43.0/go.mod h1:+JQlP0jMyl8iWGcUcGzTf+qculjIY+08UN7RKiljmps= +github.com/aws/aws-sdk-go-v2/service/databasemigrationservice v1.44.0 h1:u/zESCIEwz9UoEHFZgITa097TkQZWyrqR5bNHZn/Ozw= +github.com/aws/aws-sdk-go-v2/service/databasemigrationservice v1.44.0/go.mod h1:+JQlP0jMyl8iWGcUcGzTf+qculjIY+08UN7RKiljmps= github.com/aws/aws-sdk-go-v2/service/databrew v1.33.2 h1:M18nqFfXtAgxfGoUaaeJS7x8vfZum5SeGq1+5Iu0DLQ= github.com/aws/aws-sdk-go-v2/service/databrew v1.33.2/go.mod h1:CVFxoBbEUNT6MbNBrDAv2rAZJSuYYLUssU9pyXVQbEc= github.com/aws/aws-sdk-go-v2/service/dataexchange v1.33.0 h1:k6mEvId7/e7VFQHDYERVSIINa/b+9rTs8kASzsS5aQ0= @@ -212,8 +212,8 @@ github.com/aws/aws-sdk-go-v2/service/drs v1.30.2 h1:x3Npo3YynC7zY+2NBo7BGpyJ2FQ6 github.com/aws/aws-sdk-go-v2/service/drs v1.30.2/go.mod h1:J+ZP4mpSfSaWNFekoJULiHE2yZBzjYRMtWSlQhjZUQY= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.36.2 h1:kJqyYcGqhWFmXqjRrtFFD4Oc9FXiskhsll2xnlpe8Do= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.36.2/go.mod h1:+t2Zc5VNOzhaWzpGE+cEYZADsgAAQT5v55AO+fhU+2s= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.183.0 h1:LgwYvo4kycfT/UD7vjQhSVZSatxHAI41/54q9O6jljI= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.183.0/go.mod h1:kYXaB4FzyhEJjvrJ84oPnMElLiEAjGxxUunVW2tBSng= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.184.0 h1:SZnuDlml1uFv5ojh+QTxS+Yru89Hr3QYIUwWoY71frI= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.184.0/go.mod h1:kYXaB4FzyhEJjvrJ84oPnMElLiEAjGxxUunVW2tBSng= github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2 h1:VDQaVwGOokbd3VUbHF+wupiffdrbAZPdQnr5XZMJqrs= github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2/go.mod h1:lvUlMghKYmSxSfv0vU7pdU/8jSY+s0zpG8xXhaGKCw0= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.27.2 h1:Zru9Iy2JPM5+uRnFnoqeOZzi8JIVIHJ0ua6JdeDHcyg= @@ -222,8 +222,8 @@ github.com/aws/aws-sdk-go-v2/service/ecs v1.47.4 h1:CTkPGE8fiElvLtYWl/U+Eu5+1fVX github.com/aws/aws-sdk-go-v2/service/ecs v1.47.4/go.mod h1:sMFLFhL27cKYa/eQYZp4asvIwHsnJWrAzTUpy9AQdnU= github.com/aws/aws-sdk-go-v2/service/efs v1.33.2 h1:ePgaKeaN4kXAqzxvDsSilIIq+jftioJb4KXRobIM6ko= github.com/aws/aws-sdk-go-v2/service/efs v1.33.2/go.mod h1:ZEnNWIxtJp/3H1pPE+IZE4exGTJkAdoGfpM5m5/evxo= -github.com/aws/aws-sdk-go-v2/service/eks v1.50.2 h1:vL3RqZ4x6uqpKswp5gB0KyGcsrgkUdKmLTXCDHDUUZw= -github.com/aws/aws-sdk-go-v2/service/eks v1.50.2/go.mod h1:oaPCqTzAe8C5RQZJGRD4RENcV7A4n99uGxbD4rULbNg= +github.com/aws/aws-sdk-go-v2/service/eks v1.51.0 h1:BYyB+byjQ7oyupe3v+YjTp1yfmfNEwChYA2naCc85xI= +github.com/aws/aws-sdk-go-v2/service/eks v1.51.0/go.mod h1:oaPCqTzAe8C5RQZJGRD4RENcV7A4n99uGxbD4rULbNg= github.com/aws/aws-sdk-go-v2/service/elasticache v1.43.0 h1:rebYexCOCdOpR1zU1ObUIn+or4pS38W1IVQGKzZcSos= github.com/aws/aws-sdk-go-v2/service/elasticache v1.43.0/go.mod h1:pfx/wDobZvEpxE96P1i0nHbTYEJMCCMv3uMk7UPvdsE= github.com/aws/aws-sdk-go-v2/service/elasticbeanstalk v1.28.2 h1:La4LqsDHSMdCKoYzKs5b20wSYHfvc6xFBb8OAPDFuGg= @@ -252,8 +252,8 @@ github.com/aws/aws-sdk-go-v2/service/firehose v1.34.2 h1:9uMAwYszi6P6FTbUVq1X6MM github.com/aws/aws-sdk-go-v2/service/firehose v1.34.2/go.mod h1:yGI4W+5bau/K+JNUGepk4lwqLPt77Le6bLL8XrmKAMY= github.com/aws/aws-sdk-go-v2/service/fis v1.30.2 h1:qw7ZkSCy0akQJbJdIgRQaqXEHe7PrA3DHvE4VvemFJw= github.com/aws/aws-sdk-go-v2/service/fis v1.30.2/go.mod h1:CArS66NFuL1fBiSLVfWZV6oQjicsdViLm7Ic9Lte7x4= -github.com/aws/aws-sdk-go-v2/service/fms v1.37.2 h1:TwT5oa4EUI2O97pPSzLQmJH/4R1aqV+ucnMatvksjXE= -github.com/aws/aws-sdk-go-v2/service/fms v1.37.2/go.mod h1:sWvDxD23qcn9nbwvVfCmXOQx2HufC9n76agqoio9pfg= +github.com/aws/aws-sdk-go-v2/service/fms v1.38.0 h1:GcVv0jgW1fXkNkDFYVQTfpkYB+J4UBXXQ69Epx8MzyE= +github.com/aws/aws-sdk-go-v2/service/fms v1.38.0/go.mod h1:sWvDxD23qcn9nbwvVfCmXOQx2HufC9n76agqoio9pfg= github.com/aws/aws-sdk-go-v2/service/fsx v1.49.2 h1:Wc2N860CGBc3GJTqfucK7IvQCtGECZXOrHACyFth3sI= github.com/aws/aws-sdk-go-v2/service/fsx v1.49.2/go.mod h1:sk7ZHm49x4rJzJAfeo1dl43sCL26lZx5/Yx2b2VUJkU= github.com/aws/aws-sdk-go-v2/service/gamelift v1.36.2 h1:YN63wWC/rOHKr2sKdCQ7d8GA1xkWpiAqxDeZEBRxvqg= @@ -278,8 +278,8 @@ github.com/aws/aws-sdk-go-v2/service/iam v1.37.2 h1:E7vCDUFeDN8uOk8Nb2d4E1howWS1 github.com/aws/aws-sdk-go-v2/service/iam v1.37.2/go.mod h1:QzMecFrIFYJ1cyxjlUoIFRzYSDX19gdqYUd0Tyws2J8= github.com/aws/aws-sdk-go-v2/service/identitystore v1.27.2 h1:QSrf6HsounqUtlFAwArhVNHPt3WXmSm0pz7RtojjBdo= github.com/aws/aws-sdk-go-v2/service/identitystore v1.27.2/go.mod h1:PtkL4CXOQy84zudggyFtyJFXCGDRY8igg9Nfo9df1sU= -github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.37.3 h1:Pdh3M/+3nyVJIEqD7UrbiBrtUIcC1uTk/NIrI6M/d3I= -github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.37.3/go.mod h1:hqe5ZWGAMgZC14I2GrXc+tUPpucDfGb94RB/7IOdQ/o= +github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.38.0 h1:+gsGZbypyP+atqTDsrtaKe26TgrONg3z6rLTm5RmIhg= +github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.38.0/go.mod h1:hqe5ZWGAMgZC14I2GrXc+tUPpucDfGb94RB/7IOdQ/o= github.com/aws/aws-sdk-go-v2/service/inspector v1.25.2 h1:xiQ70pTvXs9PFYmewDgeICVxRpiQP+cWQZmunV5uYKk= github.com/aws/aws-sdk-go-v2/service/inspector v1.25.2/go.mod h1:sDcAla3dh7DO6AAdh+29e+rowLaIcw2fxuwNFCIlBuA= github.com/aws/aws-sdk-go-v2/service/inspector2 v1.32.2 h1:D0nDW7y3KLPGShqF7gaKFRswY8ekG8jsfN4r3CWqAjQ= @@ -342,8 +342,8 @@ github.com/aws/aws-sdk-go-v2/service/location v1.42.2 h1:QeEPlMSVn/GtUGVz/G9FBFV github.com/aws/aws-sdk-go-v2/service/location v1.42.2/go.mod h1:nCDXuPUnJLC/PvDh6iXjM/4LIUak0MTzXamdVn9Typk= github.com/aws/aws-sdk-go-v2/service/lookoutmetrics v1.31.2 h1:xq/m847nPu/qjcGWfT5AMmLfmb0Z8RzYmsiPQCnqWRg= github.com/aws/aws-sdk-go-v2/service/lookoutmetrics v1.31.2/go.mod h1:sWTm2Cszymy7WYqCuLvw/Zvzy1t08ceZP6Brpo7COY0= -github.com/aws/aws-sdk-go-v2/service/m2 v1.17.2 h1:2U0/1YSsugrRPnSa9WSPQqBEt5/8ZuTPwYhSFjndIzY= -github.com/aws/aws-sdk-go-v2/service/m2 v1.17.2/go.mod h1:HWB51wlVsdbT1BrpH4BYrgpxhc012ja8je2TTwjzr/0= +github.com/aws/aws-sdk-go-v2/service/m2 v1.18.0 h1:4yncbqHQ5TmL+AElqjSIppgEvCiNgboMDk0FcQ5qSW8= +github.com/aws/aws-sdk-go-v2/service/m2 v1.18.0/go.mod h1:HWB51wlVsdbT1BrpH4BYrgpxhc012ja8je2TTwjzr/0= github.com/aws/aws-sdk-go-v2/service/macie2 v1.43.2 h1:MbR0vRNd7am1So5hcYho+N11dxzhZbB4qdsi+cmcBp0= github.com/aws/aws-sdk-go-v2/service/macie2 v1.43.2/go.mod h1:B2FFzz9qQQ8l3MZV/MjMi4ua9VbqcQK8Huuv6066RZ8= github.com/aws/aws-sdk-go-v2/service/mediaconnect v1.35.2 h1:AibWQdB6x5CTlJ11/C0wp+fAUUKmCFLwU5H4h9kov0c= @@ -414,8 +414,8 @@ github.com/aws/aws-sdk-go-v2/service/ram v1.29.2 h1:MzE8+xBV5omQ8aP7I2XarPS1bORO github.com/aws/aws-sdk-go-v2/service/ram v1.29.2/go.mod h1:/rvsX6270oRGq4zBWQlFAenU4144KSJcaOFrDffHsjk= github.com/aws/aws-sdk-go-v2/service/rbin v1.20.2 h1:U+BnnWf3/yxyTQO5GBXkFO9S2lxJwtEcHskMSNIqqE0= github.com/aws/aws-sdk-go-v2/service/rbin v1.20.2/go.mod h1:B7r89tuqcg/tnDE5rtukNU1irjRhC+S10GjEFIFAj1M= -github.com/aws/aws-sdk-go-v2/service/rds v1.87.3 h1:IA338QOtCFeKTUvhuWkFg0yjjYwFFip4AzTSjcsTGuI= -github.com/aws/aws-sdk-go-v2/service/rds v1.87.3/go.mod h1:KziDa/w2AVz3dfANxwuBV0XqoQjxTKbVQyLNH5BRvO4= +github.com/aws/aws-sdk-go-v2/service/rds v1.88.0 h1:QdpwmIB0ZZN/devOnw+dJSF2VFnmn3LM5kuEKQ0kpj0= +github.com/aws/aws-sdk-go-v2/service/rds v1.88.0/go.mod h1:KziDa/w2AVz3dfANxwuBV0XqoQjxTKbVQyLNH5BRvO4= github.com/aws/aws-sdk-go-v2/service/redshift v1.50.0 h1:AT056ID/JR3pCAhwpak11ATQ6wRtaQpFqoJYjoEG7aM= github.com/aws/aws-sdk-go-v2/service/redshift v1.50.0/go.mod h1:LuUSvbRK6lNleFaeXOm3gJxnnau2qoZd1wPU7DwjS4w= github.com/aws/aws-sdk-go-v2/service/redshiftdata v1.30.2 h1:nghfgMfAqF3xl783g5Ig0UZafPMNAHm+Bx8G2l63aEg= @@ -530,8 +530,8 @@ github.com/aws/aws-sdk-go-v2/service/waf v1.25.2 h1:mGMfHPEXIR1G2mRJ5BGQnksDTt7J github.com/aws/aws-sdk-go-v2/service/waf v1.25.2/go.mod h1:g7m/OfmrD3MK7JHjJ/NBxMy87Mffp1KhNhQIKskMp2U= github.com/aws/aws-sdk-go-v2/service/wafregional v1.25.2 h1:PMBOZsPm34AE5+0FUkBD94hoEdAsRiVNO+V1NcVstWk= github.com/aws/aws-sdk-go-v2/service/wafregional v1.25.2/go.mod h1:Zw9xd/Zg0wMX3V57Pi/KAiQdqXg9ikfdEd0IK83v9B0= -github.com/aws/aws-sdk-go-v2/service/wafv2 v1.54.2 h1:SXwBXwm13cbQKjV3nt7Fkkxs/blH3lrbV9aKdjT+Zmk= -github.com/aws/aws-sdk-go-v2/service/wafv2 v1.54.2/go.mod h1:0omlXhQY21zKfGkdIfufpV7kLt564XtjQcywixaNXrM= +github.com/aws/aws-sdk-go-v2/service/wafv2 v1.55.0 h1:sS3SDk8EugdW+KW56VR6qcEg8JpC8pNLVICVBr8mMb4= +github.com/aws/aws-sdk-go-v2/service/wafv2 v1.55.0/go.mod h1:0omlXhQY21zKfGkdIfufpV7kLt564XtjQcywixaNXrM= github.com/aws/aws-sdk-go-v2/service/wellarchitected v1.34.2 h1:uhOu5pbceq96a/0nWtf/2Drt/M9hh94ic5d4LaEdFzE= github.com/aws/aws-sdk-go-v2/service/wellarchitected v1.34.2/go.mod h1:VJNJ9aES48jXBIc74SZnP0KmQr6Fku2eYHSV8854qpc= github.com/aws/aws-sdk-go-v2/service/worklink v1.23.2 h1:VN3Qydtdl3UlJRHVxQxSP1d8I5gtvT5zdaCCAfZST7Y= diff --git a/internal/acctest/state_id.go b/internal/acctest/state_id.go new file mode 100644 index 00000000000..eff847873c3 --- /dev/null +++ b/internal/acctest/state_id.go @@ -0,0 +1,23 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package acctest + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +// AttrImportStateIdFunc is a resource.ImportStateIdFunc that returns the value of the specified attribute +func AttrImportStateIdFunc(resourceName, attrName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not found: %s", resourceName) + } + + return rs.Primary.Attributes[attrName], nil + } +} diff --git a/internal/generate/tagstests/main.go b/internal/generate/tagstests/main.go index 5fb52fa9c15..8a855d5a150 100644 --- a/internal/generate/tagstests/main.go +++ b/internal/generate/tagstests/main.go @@ -371,6 +371,7 @@ type ResourceDatum struct { Generator string NoImport bool ImportStateID string + importStateIDAttribute string ImportStateIDFunc string ImportIgnore []string Implementation implementation @@ -400,6 +401,14 @@ func (d ResourceDatum) AdditionalTfVars() map[string]string { }) } +func (d ResourceDatum) HasImportStateIDAttribute() bool { + return d.importStateIDAttribute != "" +} + +func (d ResourceDatum) ImportStateIDAttribute() string { + return namesgen.ConstOrQuote(d.importStateIDAttribute) +} + func (d ResourceDatum) OverrideIdentifier() bool { return d.overrideIdentifierAttribute != "" } @@ -643,6 +652,9 @@ func (v *visitor) processFuncDecl(funcDecl *ast.FuncDecl) { if attr, ok := args.Keyword["importStateId"]; ok { d.ImportStateID = attr } + if attr, ok := args.Keyword["importStateIdAttribute"]; ok { + d.importStateIDAttribute = attr + } if attr, ok := args.Keyword["importStateIdFunc"]; ok { d.ImportStateIDFunc = attr } diff --git a/internal/generate/tagstests/resource_test.go.gtpl b/internal/generate/tagstests/resource_test.go.gtpl index 8cac07e8f1a..a01af9aa495 100644 --- a/internal/generate/tagstests/resource_test.go.gtpl +++ b/internal/generate/tagstests/resource_test.go.gtpl @@ -52,15 +52,20 @@ plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), know {{ if gt (len .ImportStateID) 0 -}} ImportStateId: {{ .ImportStateID }}, {{ end -}} -{{ if gt (len .ImportStateIDFunc) 0 -}} +{{ if .HasImportStateIDAttribute -}} + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, {{ .ImportStateIDAttribute }}), +{{ else if gt (len .ImportStateIDFunc) 0 -}} ImportStateIdFunc: {{ .ImportStateIDFunc }}(resourceName), {{ end -}} ImportStateVerify: true, +{{ if .HasImportStateIDAttribute -}} + ImportStateVerifyIdentifierAttribute: {{ .ImportStateIDAttribute }}, +{{ end }} {{- end }} {{ define "ImportBody" }} -{{ template "CommonImportBody" . }} -{{ if gt (len .ImportIgnore) 0 -}} +{{ template "CommonImportBody" . -}} +{{- if gt (len .ImportIgnore) 0 -}} ImportStateVerifyIgnore: []string{ {{ range $i, $v := .ImportIgnore }}{{ $v }},{{ end }} }, @@ -68,7 +73,7 @@ plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), know {{ end }} {{ define "ImportBodyIgnoreKey1" }} -{{ template "CommonImportBody" . }} +{{ template "CommonImportBody" . -}} {{ if eq .Implementation "framework" -}} ImportStateVerifyIgnore: []string{ acctest.CtTagsKey1, // The canonical value returned by the AWS API is "" @@ -84,7 +89,7 @@ plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), know {{ end }} {{ define "ImportBodyIgnoreResourceKey1" }} -{{ template "CommonImportBody" . }} +{{ template "CommonImportBody" . -}} {{ if eq .Implementation "framework" -}} ImportStateVerifyIgnore: []string{ "tags.resourcekey1", // The canonical value returned by the AWS API is "" diff --git a/internal/service/apprunner/auto_scaling_configuration_version.go b/internal/service/apprunner/auto_scaling_configuration_version.go index 58907292868..2118d902b0a 100644 --- a/internal/service/apprunner/auto_scaling_configuration_version.go +++ b/internal/service/apprunner/auto_scaling_configuration_version.go @@ -76,14 +76,14 @@ func resourceAutoScalingConfigurationVersion() *schema.Resource { Optional: true, Default: 25, ForceNew: true, - ValidateFunc: validation.IntBetween(1, 25), + ValidateFunc: validation.IntAtLeast(1), }, "min_size": { Type: schema.TypeInt, Optional: true, Default: 1, ForceNew: true, - ValidateFunc: validation.IntBetween(1, 25), + ValidateFunc: validation.IntAtLeast(1), }, names.AttrStatus: { Type: schema.TypeString, diff --git a/internal/service/batch/job_definition.go b/internal/service/batch/job_definition.go index a9671f441ad..da7ef0dd6e1 100644 --- a/internal/service/batch/job_definition.go +++ b/internal/service/batch/job_definition.go @@ -782,6 +782,7 @@ func resourceJobDefinitionUpdate(ctx context.Context, d *schema.ResourceData, me jobDefinitionType := awstypes.JobDefinitionType(d.Get(names.AttrType).(string)) input := &batch.RegisterJobDefinitionInput{ JobDefinitionName: aws.String(name), + Tags: getTagsIn(ctx), Type: jobDefinitionType, } diff --git a/internal/service/batch/job_definition_data_source_test.go b/internal/service/batch/job_definition_data_source_test.go index 7ee93599169..14275f1600c 100644 --- a/internal/service/batch/job_definition_data_source_test.go +++ b/internal/service/batch/job_definition_data_source_test.go @@ -188,7 +188,7 @@ resource "aws_batch_job_definition" "test" { func testAccJobDefinitionDataSourceConfig_basicARNNode(rName string) string { return acctest.ConfigCompose( - testAccJobDefinitionConfig_NodeProperties(rName), ` + testAccJobDefinitionConfig_nodeProperties(rName), ` data "aws_batch_job_definition" "test" { arn = aws_batch_job_definition.test.arn }`) diff --git a/internal/service/batch/job_definition_test.go b/internal/service/batch/job_definition_test.go index 1aaaa0755ea..15d92938ff7 100644 --- a/internal/service/batch/job_definition_test.go +++ b/internal/service/batch/job_definition_test.go @@ -16,7 +16,11 @@ import ( awstypes "github.com/aws/aws-sdk-go-v2/service/batch/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/statecheck" "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfbatch "github.com/hashicorp/terraform-provider-aws/internal/service/batch" @@ -37,7 +41,7 @@ func TestAccBatchJobDefinition_basic(t *testing.T) { CheckDestroy: testAccCheckJobDefinitionDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccJobDefinitionConfig_name(rName), + Config: testAccJobDefinitionConfig_basic(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckJobDefinitionExists(ctx, resourceName, &jd), acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "batch", regexache.MustCompile(fmt.Sprintf(`job-definition/%s:\d+`, rName))), @@ -122,7 +126,7 @@ func TestAccBatchJobDefinition_attributes(t *testing.T) { ), }, { - Config: testAccJobDefinitionConfig_name(rName), + Config: testAccJobDefinitionConfig_basic(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckJobDefinitionExists(ctx, resourceName, &jd), testAccCheckJobDefinitionPreviousDeregistered(ctx, resourceName), @@ -185,7 +189,7 @@ func TestAccBatchJobDefinition_disappears(t *testing.T) { CheckDestroy: testAccCheckJobDefinitionDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccJobDefinitionConfig_name(rName), + Config: testAccJobDefinitionConfig_basic(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckJobDefinitionExists(ctx, resourceName, &jd), acctest.CheckResourceDisappears(ctx, acctest.Provider, tfbatch.ResourceJobDefinition(), resourceName), @@ -589,7 +593,7 @@ func TestAccBatchJobDefinition_NodeProperties_basic(t *testing.T) { CheckDestroy: testAccCheckJobDefinitionDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccJobDefinitionConfig_NodeProperties(rName), + Config: testAccJobDefinitionConfig_nodeProperties(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckJobDefinitionExists(ctx, resourceName, &jd), acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "batch", regexache.MustCompile(fmt.Sprintf(`job-definition/%s:\d+`, rName))), @@ -1014,6 +1018,64 @@ func TestAccBatchJobDefinition_ECSProperties_update(t *testing.T) { }) } +func TestAccBatchJobDefinition_updateWithTags(t *testing.T) { + ctx := acctest.Context(t) + var jd awstypes.JobDefinition + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_batch_job_definition.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.BatchServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckJobDefinitionDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccJobDefinitionConfig_simpleWithTags(rName, "echo", "test1"), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrName), knownvalue.StringExact(rName)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + "Name": knownvalue.StringExact(rName), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + "Name": knownvalue.StringExact(rName), + })), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckJobDefinitionExists(ctx, resourceName, &jd), + ), + }, + // Ensure that tags are put on the new revision. + { + Config: testAccJobDefinitionConfig_simpleWithTags(rName, "echo", "test2"), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectUnknownValue(resourceName, tfjsonpath.New("revision")), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrName), knownvalue.StringExact(rName)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + "Name": knownvalue.StringExact(rName), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + "Name": knownvalue.StringExact(rName), + })), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckJobDefinitionExists(ctx, resourceName, &jd), + ), + }, + }, + }) +} + func testAccCheckJobDefinitionExists(ctx context.Context, n string, v *awstypes.JobDefinition) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -1156,6 +1218,21 @@ func testAccCheckJobDefinitionDestroy(ctx context.Context) resource.TestCheckFun } } +func testAccJobDefinitionConfig_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_batch_job_definition" "test" { + container_properties = jsonencode({ + command = ["echo", "test"] + image = "busybox" + memory = 128 + vcpus = 1 + }) + name = %[1]q + type = "container" +} +`, rName) +} + func testAccJobDefinitionConfig_containerPropertiesAdvanced(rName, param string, retries, timeout int) string { return fmt.Sprintf(` resource "aws_batch_job_definition" "test" { @@ -1332,21 +1409,6 @@ resource "aws_batch_job_definition" "test" { `, rName, subcommand)) } -func testAccJobDefinitionConfig_name(rName string) string { - return fmt.Sprintf(` -resource "aws_batch_job_definition" "test" { - container_properties = jsonencode({ - command = ["echo", "test"] - image = "busybox" - memory = 128 - vcpus = 1 - }) - name = %[1]q - type = "container" -} -`, rName) -} - func testAccJobDefinitionConfig_capabilitiesEC2(rName string) string { return fmt.Sprintf(` resource "aws_batch_job_definition" "test" { @@ -1510,7 +1572,7 @@ resource "aws_batch_job_definition" "test" { `, rName) } -func testAccJobDefinitionConfig_NodeProperties(rName string) string { +func testAccJobDefinitionConfig_nodeProperties(rName string) string { return fmt.Sprintf(` resource "aws_batch_job_definition" "test" { name = %[1]q @@ -2144,3 +2206,22 @@ resource "aws_batch_job_definition" "test" { } `, rName, acctest.Region()) } + +func testAccJobDefinitionConfig_simpleWithTags(rName, command1, command2 string) string { + return fmt.Sprintf(` +resource "aws_batch_job_definition" "test" { + container_properties = jsonencode({ + command = [%[2]q, %[3]q] + image = "busybox" + memory = 128 + vcpus = 1 + }) + name = %[1]q + type = "container" + + tags = { + Name = %[1]q + } +} +`, rName, command1, command2) +} diff --git a/internal/service/ec2/exports_test.go b/internal/service/ec2/exports_test.go index f5d4973cad4..c63a605bc66 100644 --- a/internal/service/ec2/exports_test.go +++ b/internal/service/ec2/exports_test.go @@ -69,6 +69,7 @@ var ( ResourceRouteTableAssociation = resourceRouteTableAssociation ResourceSecurityGroupEgressRule = newSecurityGroupEgressRuleResource ResourceSecurityGroupIngressRule = newSecurityGroupIngressRuleResource + ResourceSecurityGroupRule = resourceSecurityGroupRule ResourceSnapshotCreateVolumePermission = resourceSnapshotCreateVolumePermission ResourceSpotDataFeedSubscription = resourceSpotDataFeedSubscription ResourceSpotFleetRequest = resourceSpotFleetRequest diff --git a/internal/service/ec2/vpc_security_group_rule.go b/internal/service/ec2/vpc_security_group_rule.go index 54f4ed20fa8..fc99f2975f6 100644 --- a/internal/service/ec2/vpc_security_group_rule.go +++ b/internal/service/ec2/vpc_security_group_rule.go @@ -518,7 +518,7 @@ func resourceSecurityGroupRuleImport(_ context.Context, d *schema.ResourceData, } func findRuleMatch(p awstypes.IpPermission, rules []awstypes.IpPermission) (*awstypes.IpPermission, *string) { - var rule awstypes.IpPermission + var rule *awstypes.IpPermission var description *string for _, r := range rules { @@ -616,10 +616,10 @@ func findRuleMatch(p awstypes.IpPermission, rules []awstypes.IpPermission) (*aws continue } - rule = r + rule = &r } - return &rule, description + return rule, description } func findSecurityGroupRuleMatch(p awstypes.IpPermission, securityGroupRules []awstypes.SecurityGroupRule, ruleType securityGroupRuleType) string { diff --git a/internal/service/ec2/vpc_security_group_rule_test.go b/internal/service/ec2/vpc_security_group_rule_test.go index 8ec6d5ef343..8aad6ec26a5 100644 --- a/internal/service/ec2/vpc_security_group_rule_test.go +++ b/internal/service/ec2/vpc_security_group_rule_test.go @@ -360,6 +360,31 @@ func TestAccVPCSecurityGroupRule_egress(t *testing.T) { }) } +func TestAccVPCSecurityGroupRule_disappears(t *testing.T) { + ctx := acctest.Context(t) + var group awstypes.SecurityGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_security_group_rule.test" + sgResourceName := "aws_security_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSecurityGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccVPCSecurityGroupRuleConfig_egress(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckSecurityGroupExists(ctx, sgResourceName, &group), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfec2.ResourceSecurityGroupRule(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccVPCSecurityGroupRule_selfReference(t *testing.T) { ctx := acctest.Context(t) var group awstypes.SecurityGroup @@ -992,10 +1017,11 @@ func TestAccVPCSecurityGroupRule_DescriptionAllPorts_nonZeroPorts(t *testing.T) ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccSecurityGroupRuleImportStateIdFunc(resourceName), - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccSecurityGroupRuleImportStateIdFunc(resourceName), + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"from_port", "to_port"}, }, { Config: testAccVPCSecurityGroupRuleConfig_descriptionAllPortsNonZeroPorts(rName, "description2"), @@ -1065,16 +1091,18 @@ func TestAccVPCSecurityGroupRule_MultipleRuleSearching_allProtocolCrash(t *testi ), }, { - ResourceName: resource1Name, - ImportState: true, - ImportStateIdFunc: testAccSecurityGroupRuleImportStateIdFunc(resource1Name), - ImportStateVerify: true, + ResourceName: resource1Name, + ImportState: true, + ImportStateIdFunc: testAccSecurityGroupRuleImportStateIdFunc(resource1Name), + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"to_port"}, }, { - ResourceName: resource2Name, - ImportState: true, - ImportStateIdFunc: testAccSecurityGroupRuleImportStateIdFunc(resource2Name), - ImportStateVerify: true, + ResourceName: resource2Name, + ImportState: true, + ImportStateIdFunc: testAccSecurityGroupRuleImportStateIdFunc(resource2Name), + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"to_port"}, }, }, }) diff --git a/internal/service/elasticache/cluster.go b/internal/service/elasticache/cluster.go index d53ab5416ae..690b40d7831 100644 --- a/internal/service/elasticache/cluster.go +++ b/internal/service/elasticache/cluster.go @@ -141,7 +141,7 @@ func resourceCluster() *schema.Resource { Computed: true, ForceNew: true, ExactlyOneOf: []string{names.AttrEngine, "replication_group_id"}, - ValidateFunc: validation.StringInSlice(engine_Values(), false), + ValidateFunc: validation.StringInSlice([]string{engineMemcached, engineRedis}, false), }, names.AttrEngineVersion: { Type: schema.TypeString, @@ -627,6 +627,11 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int requestUpdate = true } + if d.HasChange(names.AttrEngine) { + input.Engine = aws.String(d.Get(names.AttrEngine).(string)) + requestUpdate = true + } + if d.HasChange(names.AttrEngineVersion) { input.EngineVersion = aws.String(d.Get(names.AttrEngineVersion).(string)) requestUpdate = true @@ -973,12 +978,18 @@ func (b byCacheNodeId) Less(i, j int) bool { func setFromCacheCluster(d *schema.ResourceData, c *awstypes.CacheCluster) error { d.Set("node_type", c.CacheNodeType) - d.Set(names.AttrEngine, c.Engine) - if aws.ToString(c.Engine) == engineRedis { + engine := aws.ToString(c.Engine) + d.Set(names.AttrEngine, engine) + switch engine { + case engineValkey: + if err := setEngineVersionValkey(d, c.EngineVersion); err != nil { + return err // nosemgrep:ci.bare-error-returns + } + case engineRedis: if err := setEngineVersionRedis(d, c.EngineVersion); err != nil { return err // nosemgrep:ci.bare-error-returns } - } else { + default: setEngineVersionMemcached(d, c.EngineVersion) } d.Set(names.AttrAutoMinorVersionUpgrade, strconv.FormatBool(aws.ToBool(c.AutoMinorVersionUpgrade))) @@ -1010,13 +1021,14 @@ func clusterValidateAZMode(_ context.Context, diff *schema.ResourceDiff, v inter // clusterValidateNumCacheNodes validates that `num_cache_nodes` is 1 when `engine` is "redis" func clusterValidateNumCacheNodes(_ context.Context, diff *schema.ResourceDiff, v interface{}) error { - if v, ok := diff.GetOk(names.AttrEngine); !ok || v.(string) == engineMemcached { + engine, ok := diff.GetOk(names.AttrEngine) + if !ok || engine.(string) == engineMemcached { return nil } if v, ok := diff.GetOk("num_cache_nodes"); !ok || v.(int) == 1 { return nil } - return errors.New(`engine "redis" does not support num_cache_nodes > 1`) + return fmt.Errorf(`engine "%s" does not support num_cache_nodes > 1`, engine.(string)) } // clusterForceNewOnMemcachedNodeTypeChange causes re-creation when `node_type` is changed and `engine` is "memcached" @@ -1026,7 +1038,7 @@ func clusterForceNewOnMemcachedNodeTypeChange(_ context.Context, diff *schema.Re if diff.Id() == "" || !diff.HasChange("node_type") { return nil } - if v, ok := diff.GetOk(names.AttrEngine); !ok || v.(string) == engineRedis { + if v, ok := diff.GetOk(names.AttrEngine); !ok || v.(string) == engineRedis || v.(string) == engineValkey { return nil } return diff.ForceNew("node_type") @@ -1034,7 +1046,7 @@ func clusterForceNewOnMemcachedNodeTypeChange(_ context.Context, diff *schema.Re // clusterValidateMemcachedSnapshotIdentifier validates that `final_snapshot_identifier` is not set when `engine` is "memcached" func clusterValidateMemcachedSnapshotIdentifier(_ context.Context, diff *schema.ResourceDiff, v interface{}) error { - if v, ok := diff.GetOk(names.AttrEngine); !ok || v.(string) == engineRedis { + if v, ok := diff.GetOk(names.AttrEngine); !ok || v.(string) == engineRedis || v.(string) == engineValkey { return nil } if _, ok := diff.GetOk(names.AttrFinalSnapshotIdentifier); !ok { diff --git a/internal/service/elasticache/cluster_test.go b/internal/service/elasticache/cluster_test.go index fce05ffa537..f126088542b 100644 --- a/internal/service/elasticache/cluster_test.go +++ b/internal/service/elasticache/cluster_test.go @@ -1245,10 +1245,10 @@ func TestAccElastiCacheCluster_tagWithOtherModification(t *testing.T) { CheckDestroy: testAccCheckClusterDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccClusterConfig_versionAndTag(rName, "5.0.5", acctest.CtKey1, acctest.CtValue1), + Config: testAccClusterConfig_versionAndTag(rName, "6.0", acctest.CtKey1, acctest.CtValue1), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckClusterExists(ctx, resourceName, &cluster), - resource.TestCheckResourceAttr(resourceName, names.AttrEngineVersion, "5.0.5"), + resource.TestCheckResourceAttr(resourceName, names.AttrEngineVersion, "6.0"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsAllPercent, acctest.Ct1), @@ -1256,10 +1256,10 @@ func TestAccElastiCacheCluster_tagWithOtherModification(t *testing.T) { ), }, { - Config: testAccClusterConfig_versionAndTag(rName, "5.0.6", acctest.CtKey1, acctest.CtValue1Updated), + Config: testAccClusterConfig_versionAndTag(rName, "6.2", acctest.CtKey1, acctest.CtValue1Updated), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckClusterExists(ctx, resourceName, &cluster), - resource.TestCheckResourceAttr(resourceName, names.AttrEngineVersion, "5.0.6"), + resource.TestCheckResourceAttr(resourceName, names.AttrEngineVersion, "6.2"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1Updated), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsAllPercent, acctest.Ct1), diff --git a/internal/service/elasticache/consts.go b/internal/service/elasticache/consts.go index 0c8e2dd96d0..c3b07248b65 100644 --- a/internal/service/elasticache/consts.go +++ b/internal/service/elasticache/consts.go @@ -6,6 +6,7 @@ package elasticache const ( engineMemcached = "memcached" engineRedis = "redis" + engineValkey = "valkey" ) // engine_Values returns all elements of the Engine enum @@ -13,6 +14,7 @@ func engine_Values() []string { return []string{ engineMemcached, engineRedis, + engineValkey, } } diff --git a/internal/service/elasticache/engine_version.go b/internal/service/elasticache/engine_version.go index 448069ed2f4..cbc82057637 100644 --- a/internal/service/elasticache/engine_version.go +++ b/internal/service/elasticache/engine_version.go @@ -56,6 +56,24 @@ func validRedisVersionString(v any, k string) (ws []string, errors []error) { return } +const ( + valkeyVersionRegexpPattern = `^[7-9]\.[[:digit:]]+$` +) + +var ( + valkeyVersionRegexp = regexache.MustCompile(valkeyVersionRegexpPattern) +) + +func validValkeyVersionString(v any, k string) (ws []string, errors []error) { + value := v.(string) + + if !valkeyVersionRegexp.MatchString(value) { + errors = append(errors, fmt.Errorf("%s: %s is invalid. For Valkey use ..", k, value)) + } + + return +} + // customizeDiffValidateClusterEngineVersion validates the correct format for `engine_version`, based on `engine` func customizeDiffValidateClusterEngineVersion(_ context.Context, diff *schema.ResourceDiff, _ any) error { engineVersion, ok := diff.GetOk(names.AttrEngineVersion) @@ -70,11 +88,15 @@ func customizeDiffValidateClusterEngineVersion(_ context.Context, diff *schema.R func validateClusterEngineVersion(engine, engineVersion string) error { // Memcached: Versions in format .. // Redis: Starting with version 6, must match ., prior to version 6, .. + // Valkey: Versions in format . var validator schema.SchemaValidateFunc - if engine == "" || engine == engineMemcached { + switch engine { + case "", engineMemcached: validator = validMemcachedVersionString - } else { + case engineRedis: validator = validRedisVersionString + case engineValkey: + validator = validValkeyVersionString } _, errs := validator(engineVersion, names.AttrEngineVersion) @@ -187,6 +209,17 @@ func setEngineVersionRedis(d *schema.ResourceData, version *string) error { return nil } +func setEngineVersionValkey(d *schema.ResourceData, version *string) error { + engineVersion, err := gversion.NewVersion(aws.ToString(version)) + if err != nil { + return fmt.Errorf("reading engine version: %w", err) + } + d.Set(names.AttrEngineVersion, fmt.Sprintf("%d.%d", engineVersion.Segments()[0], engineVersion.Segments()[1])) + d.Set("engine_version_actual", engineVersion.String()) + + return nil +} + type versionDiff [3]int // diffVersion returns a diff of the versions, component by component. diff --git a/internal/service/elasticache/engine_version_test.go b/internal/service/elasticache/engine_version_test.go index 7a607033a0d..f22ec0ee5d5 100644 --- a/internal/service/elasticache/engine_version_test.go +++ b/internal/service/elasticache/engine_version_test.go @@ -279,6 +279,22 @@ func TestValidateClusterEngineVersion(t *testing.T) { version: "7.0", valid: true, }, + + { + engine: tfelasticache.EngineValkey, + version: "7.x", + valid: false, + }, + { + engine: tfelasticache.EngineValkey, + version: "7.2", + valid: true, + }, + { + engine: tfelasticache.EngineValkey, + version: "7.2.6", + valid: false, + }, } for _, testcase := range testcases { diff --git a/internal/service/elasticache/exports_test.go b/internal/service/elasticache/exports_test.go index c256fac3107..0a277862484 100644 --- a/internal/service/elasticache/exports_test.go +++ b/internal/service/elasticache/exports_test.go @@ -36,6 +36,7 @@ var ( EmptyDescription = emptyDescription EngineMemcached = engineMemcached EngineRedis = engineRedis + EngineValkey = engineValkey EngineVersionForceNewOnDowngrade = engineVersionForceNewOnDowngrade EngineVersionIsDowngrade = engineVersionIsDowngrade GlobalReplicationGroupRegionPrefixFormat = globalReplicationGroupRegionPrefixFormat @@ -44,6 +45,7 @@ var ( ValidateClusterEngineVersion = validateClusterEngineVersion ValidMemcachedVersionString = validMemcachedVersionString ValidRedisVersionString = validRedisVersionString + ValidValkeyVersionString = validValkeyVersionString ) type ( diff --git a/internal/service/elasticache/global_replication_group.go b/internal/service/elasticache/global_replication_group.go index 7043cc75a64..f10fa7c46a0 100644 --- a/internal/service/elasticache/global_replication_group.go +++ b/internal/service/elasticache/global_replication_group.go @@ -94,10 +94,13 @@ func resourceGlobalReplicationGroup() *schema.Resource { Computed: true, }, names.AttrEngineVersion: { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validRedisVersionString, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.Any( + validRedisVersionString, + validValkeyVersionString, + ), DiffSuppressFunc: func(_, old, new string, _ *schema.ResourceData) bool { if t, _ := regexp.MatchString(`[6-9]\.x`, new); t && old != "" { oldVersion, err := gversion.NewVersion(old) @@ -399,7 +402,13 @@ func resourceGlobalReplicationGroupRead(ctx context.Context, d *schema.ResourceD d.Set("global_replication_group_id", globalReplicationGroup.GlobalReplicationGroupId) d.Set("transit_encryption_enabled", globalReplicationGroup.TransitEncryptionEnabled) - if err := setEngineVersionRedis(d, globalReplicationGroup.EngineVersion); err != nil { + switch aws.ToString(globalReplicationGroup.Engine) { + case engineValkey: + err = setEngineVersionValkey(d, globalReplicationGroup.EngineVersion) + default: + err = setEngineVersionRedis(d, globalReplicationGroup.EngineVersion) + } + if err != nil { return sdkdiag.AppendErrorf(diags, "reading ElastiCache Replication Group (%s): %s", d.Id(), err) } diff --git a/internal/service/elasticache/global_replication_group_test.go b/internal/service/elasticache/global_replication_group_test.go index 399bd8b19f9..35fd594f32e 100644 --- a/internal/service/elasticache/global_replication_group_test.go +++ b/internal/service/elasticache/global_replication_group_test.go @@ -24,7 +24,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccElastiCacheGlobalReplicationGroup_basic(t *testing.T) { +func TestAccElastiCacheGlobalReplicationGroup_Redis_basic(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -48,7 +48,61 @@ func TestAccElastiCacheGlobalReplicationGroup_basic(t *testing.T) { ), Steps: []resource.TestStep{ { - Config: testAccGlobalReplicationGroupConfig_basic(rName, primaryReplicationGroupId), + Config: testAccGlobalReplicationGroupConfig_Redis_basic(rName, primaryReplicationGroupId), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckGlobalReplicationGroupExists(ctx, resourceName, &globalReplicationGroup), + testAccCheckReplicationGroupExists(ctx, primaryReplicationGroupResourceName, &primaryReplicationGroup), + acctest.MatchResourceAttrGlobalARN(resourceName, names.AttrARN, "elasticache", regexache.MustCompile(`globalreplicationgroup:`+tfelasticache.GlobalReplicationGroupRegionPrefixFormat+rName)), + resource.TestCheckResourceAttrPair(resourceName, "at_rest_encryption_enabled", primaryReplicationGroupResourceName, "at_rest_encryption_enabled"), + resource.TestCheckResourceAttr(resourceName, "auth_token_enabled", acctest.CtFalse), + resource.TestCheckResourceAttrPair(resourceName, "automatic_failover_enabled", primaryReplicationGroupResourceName, "automatic_failover_enabled"), + resource.TestCheckResourceAttrPair(resourceName, "cache_node_type", primaryReplicationGroupResourceName, "node_type"), + resource.TestCheckResourceAttrPair(resourceName, "cluster_enabled", primaryReplicationGroupResourceName, "cluster_enabled"), + resource.TestCheckResourceAttrPair(resourceName, names.AttrEngine, primaryReplicationGroupResourceName, names.AttrEngine), + resource.TestCheckResourceAttrPair(resourceName, "engine_version_actual", primaryReplicationGroupResourceName, "engine_version_actual"), + resource.TestCheckResourceAttr(resourceName, "global_replication_group_id_suffix", rName), + resource.TestMatchResourceAttr(resourceName, "global_replication_group_id", regexache.MustCompile(tfelasticache.GlobalReplicationGroupRegionPrefixFormat+rName)), + resource.TestCheckResourceAttr(resourceName, "global_replication_group_description", tfelasticache.EmptyDescription), + resource.TestCheckResourceAttr(resourceName, "global_node_groups.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "num_node_groups", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "primary_replication_group_id", primaryReplicationGroupId), + resource.TestCheckResourceAttr(resourceName, "transit_encryption_enabled", acctest.CtFalse), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccElastiCacheGlobalReplicationGroup_Valkey_basic(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var globalReplicationGroup awstypes.GlobalReplicationGroup + var primaryReplicationGroup awstypes.ReplicationGroup + + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + primaryReplicationGroupId := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resourceName := "aws_elasticache_global_replication_group.test" + primaryReplicationGroupResourceName := "aws_elasticache_replication_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheckGlobalReplicationGroup(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: resource.ComposeAggregateTestCheckFunc( + testAccCheckGlobalReplicationGroupDestroy(ctx), + ), + Steps: []resource.TestStep{ + { + Config: testAccGlobalReplicationGroupConfig_Valkey_basic(rName, primaryReplicationGroupId), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckGlobalReplicationGroupExists(ctx, resourceName, &globalReplicationGroup), testAccCheckReplicationGroupExists(ctx, primaryReplicationGroupResourceName, &primaryReplicationGroup), @@ -96,7 +150,7 @@ func TestAccElastiCacheGlobalReplicationGroup_disappears(t *testing.T) { CheckDestroy: testAccCheckGlobalReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccGlobalReplicationGroupConfig_basic(rName, primaryReplicationGroupId), + Config: testAccGlobalReplicationGroupConfig_Redis_basic(rName, primaryReplicationGroupId), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckGlobalReplicationGroupExists(ctx, resourceName, &globalReplicationGroup), acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelasticache.ResourceGlobalReplicationGroup(), resourceName), @@ -813,7 +867,7 @@ func TestAccElastiCacheGlobalReplicationGroup_SetNumNodeGroupsOnUpdate_Decrease( }) } -func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_v6(t *testing.T) { +func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_Redis_v6(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -831,7 +885,7 @@ func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_ CheckDestroy: testAccCheckGlobalReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccGlobalReplicationGroupConfig_engineVersion(rName, primaryReplicationGroupId, "6.2", "6.2"), + Config: testAccGlobalReplicationGroupConfig_Redis_engineVersion(rName, primaryReplicationGroupId, "6.2", "6.2"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckGlobalReplicationGroupExists(ctx, resourceName, &globalReplicationGroup), resource.TestMatchResourceAttr(resourceName, "engine_version_actual", regexache.MustCompile(`^6\.2\.[[:digit:]]+$`)), @@ -846,7 +900,7 @@ func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_ }) } -func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_v6x(t *testing.T) { +func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_Redis_v6x(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -864,7 +918,7 @@ func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_ CheckDestroy: testAccCheckGlobalReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccGlobalReplicationGroupConfig_engineVersion(rName, primaryReplicationGroupId, "6.2", "6.x"), + Config: testAccGlobalReplicationGroupConfig_Redis_engineVersion(rName, primaryReplicationGroupId, "6.2", "6.x"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckGlobalReplicationGroupExists(ctx, resourceName, &globalReplicationGroup), resource.TestMatchResourceAttr(resourceName, "engine_version_actual", regexache.MustCompile(`^6\.2\.[[:digit:]]+$`)), @@ -880,7 +934,7 @@ func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_ }) } -func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_v5(t *testing.T) { +func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_Redis_v5(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -898,7 +952,7 @@ func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_ CheckDestroy: testAccCheckGlobalReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccGlobalReplicationGroupConfig_engineVersion(rName, primaryReplicationGroupId, "5.0.6", "5.0.6"), + Config: testAccGlobalReplicationGroupConfig_Redis_engineVersion(rName, primaryReplicationGroupId, "5.0.6", "5.0.6"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckGlobalReplicationGroupExists(ctx, resourceName, &globalReplicationGroup), resource.TestCheckResourceAttr(resourceName, "engine_version_actual", "5.0.6"), @@ -913,6 +967,39 @@ func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_ }) } +func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_NoChange_Valkey_v7(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var globalReplicationGroup awstypes.GlobalReplicationGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + primaryReplicationGroupId := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elasticache_global_replication_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheckGlobalReplicationGroup(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckGlobalReplicationGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccGlobalReplicationGroupConfig_Valkey_engineVersion(rName, primaryReplicationGroupId, "7.2", "7.2"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckGlobalReplicationGroupExists(ctx, resourceName, &globalReplicationGroup), + resource.TestMatchResourceAttr(resourceName, "engine_version_actual", regexache.MustCompile(`^7\.2\.[[:digit:]]+$`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_MinorUpgrade(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { @@ -934,7 +1021,7 @@ func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_MinorUpgr CheckDestroy: testAccCheckGlobalReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccGlobalReplicationGroupConfig_engineVersion(rName, primaryReplicationGroupId, "6.0", "6.2"), + Config: testAccGlobalReplicationGroupConfig_Redis_engineVersion(rName, primaryReplicationGroupId, "6.0", "6.2"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckGlobalReplicationGroupExists(ctx, resourceName, &globalReplicationGroup), testAccCheckReplicationGroupExists(ctx, primaryReplicationGroupResourceName, &rg), @@ -972,7 +1059,7 @@ func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_MinorUpgr CheckDestroy: testAccCheckGlobalReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccGlobalReplicationGroupConfig_engineVersion(rName, primaryReplicationGroupId, "6.0", "6.x"), + Config: testAccGlobalReplicationGroupConfig_Redis_engineVersion(rName, primaryReplicationGroupId, "6.0", "6.x"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckGlobalReplicationGroupExists(ctx, resourceName, &globalReplicationGroup), testAccCheckReplicationGroupExists(ctx, primaryReplicationGroupResourceName, &rg), @@ -1078,7 +1165,7 @@ func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnCreate_MinorDown CheckDestroy: testAccCheckGlobalReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccGlobalReplicationGroupConfig_engineVersion(rName, primaryReplicationGroupId, "6.2", "6.0"), + Config: testAccGlobalReplicationGroupConfig_Redis_engineVersion(rName, primaryReplicationGroupId, "6.2", "6.0"), ExpectError: regexache.MustCompile(`cannot downgrade version when creating, is 6.2.[[:digit:]]+, want 6.0.[[:digit:]]+`), }, }, @@ -1151,7 +1238,7 @@ func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnUpdate_MinorUpgr ), }, { - Config: testAccGlobalReplicationGroupConfig_engineVersion(rName, primaryReplicationGroupId, "6.0", "6.2"), + Config: testAccGlobalReplicationGroupConfig_Redis_engineVersion(rName, primaryReplicationGroupId, "6.0", "6.2"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckGlobalReplicationGroupExists(ctx, resourceName, &globalReplicationGroup), resource.TestMatchResourceAttr(resourceName, "engine_version_actual", regexache.MustCompile(`^6\.2\.[[:digit:]]+$`)), @@ -1188,7 +1275,7 @@ func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnUpdate_MinorUpgr ), }, { - Config: testAccGlobalReplicationGroupConfig_engineVersion(rName, primaryReplicationGroupId, "6.0", "6.x"), + Config: testAccGlobalReplicationGroupConfig_Redis_engineVersion(rName, primaryReplicationGroupId, "6.0", "6.x"), PlanOnly: true, }, }, @@ -1222,7 +1309,7 @@ func TestAccElastiCacheGlobalReplicationGroup_SetEngineVersionOnUpdate_MinorDown ), }, { - Config: testAccGlobalReplicationGroupConfig_engineVersion(rName, primaryReplicationGroupId, "6.2", "6.0"), + Config: testAccGlobalReplicationGroupConfig_Redis_engineVersion(rName, primaryReplicationGroupId, "6.2", "6.0"), ExpectError: regexache.MustCompile(`Downgrading ElastiCache Global Replication Group \(.*\) engine version requires replacement`), }, // This step fails with: Error running pre-apply refresh @@ -1515,7 +1602,7 @@ func testAccMatchReplicationGroupActualVersion(ctx context.Context, j *awstypes. } } -func testAccGlobalReplicationGroupConfig_basic(rName, primaryReplicationGroupId string) string { +func testAccGlobalReplicationGroupConfig_Redis_basic(rName, primaryReplicationGroupId string) string { return fmt.Sprintf(` resource "aws_elasticache_global_replication_group" "test" { global_replication_group_id_suffix = %[1]q @@ -1534,6 +1621,25 @@ resource "aws_elasticache_replication_group" "test" { `, rName, primaryReplicationGroupId) } +func testAccGlobalReplicationGroupConfig_Valkey_basic(rName, primaryReplicationGroupId string) string { + return fmt.Sprintf(` +resource "aws_elasticache_global_replication_group" "test" { + global_replication_group_id_suffix = %[1]q + primary_replication_group_id = aws_elasticache_replication_group.test.id +} + +resource "aws_elasticache_replication_group" "test" { + replication_group_id = %[2]q + description = "test" + + engine = "valkey" + engine_version = "7.2" + node_type = "cache.m5.large" + num_cache_clusters = 1 +} +`, rName, primaryReplicationGroupId) +} + func testAccGlobalReplicationGroupConfig_basic_nodeType(rName, primaryReplicationGroupId, nodeType string) string { return fmt.Sprintf(` resource "aws_elasticache_global_replication_group" "test" { @@ -1974,7 +2080,7 @@ resource "aws_elasticache_replication_group" "test" { `, rName, primaryReplicationGroupId, repGroupEngineVersion) } -func testAccGlobalReplicationGroupConfig_engineVersion(rName, primaryReplicationGroupId, repGroupEngineVersion, globalEngineVersion string) string { +func testAccGlobalReplicationGroupConfig_Redis_engineVersion(rName, primaryReplicationGroupId, repGroupEngineVersion, globalEngineVersion string) string { return fmt.Sprintf(` resource "aws_elasticache_global_replication_group" "test" { global_replication_group_id_suffix = %[1]q @@ -1999,6 +2105,31 @@ resource "aws_elasticache_replication_group" "test" { `, rName, primaryReplicationGroupId, repGroupEngineVersion, globalEngineVersion) } +func testAccGlobalReplicationGroupConfig_Valkey_engineVersion(rName, primaryReplicationGroupId, repGroupEngineVersion, globalEngineVersion string) string { + return fmt.Sprintf(` +resource "aws_elasticache_global_replication_group" "test" { + global_replication_group_id_suffix = %[1]q + primary_replication_group_id = aws_elasticache_replication_group.test.id + + engine_version = %[4]q +} + +resource "aws_elasticache_replication_group" "test" { + replication_group_id = %[2]q + description = "test" + + engine = "valkey" + engine_version = %[3]q + node_type = "cache.m5.large" + num_cache_clusters = 1 + + lifecycle { + ignore_changes = [engine_version] + } +} +`, rName, primaryReplicationGroupId, repGroupEngineVersion, globalEngineVersion) +} + func testAccGlobalReplicationGroupConfig_engineVersionParam(rName, primaryReplicationGroupId, repGroupEngineVersion, globalEngineVersion, parameterGroup string) string { return fmt.Sprintf(` resource "aws_elasticache_global_replication_group" "test" { diff --git a/internal/service/elasticache/parameter_group_test.go b/internal/service/elasticache/parameter_group_test.go index 3ca46ae3cb9..f7a0ab57f8a 100644 --- a/internal/service/elasticache/parameter_group_test.go +++ b/internal/service/elasticache/parameter_group_test.go @@ -22,7 +22,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccElastiCacheParameterGroup_basic(t *testing.T) { +func TestAccElastiCacheParameterGroup_Redis_basic(t *testing.T) { ctx := acctest.Context(t) var v awstypes.CacheParameterGroup resourceName := "aws_elasticache_parameter_group.test" @@ -35,10 +35,9 @@ func TestAccElastiCacheParameterGroup_basic(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_basic(rName), + Config: testAccParameterGroupConfig_Redis_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - testAccCheckParameterGroupAttributes(&v, rName), resource.TestCheckResourceAttr(resourceName, names.AttrDescription, "Managed by Terraform"), resource.TestCheckResourceAttr(resourceName, names.AttrFamily, "redis2.8"), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), @@ -54,6 +53,37 @@ func TestAccElastiCacheParameterGroup_basic(t *testing.T) { }) } +func TestAccElastiCacheParameterGroup_Valkey_basic(t *testing.T) { + ctx := acctest.Context(t) + var v awstypes.CacheParameterGroup + resourceName := "aws_elasticache_parameter_group.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckParameterGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccParameterGroupConfig_Valkey_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckParameterGroupExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, names.AttrDescription, "Managed by Terraform"), + resource.TestCheckResourceAttr(resourceName, names.AttrFamily, "valkey7"), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccElastiCacheParameterGroup_disappears(t *testing.T) { ctx := acctest.Context(t) var v awstypes.CacheParameterGroup @@ -67,7 +97,7 @@ func TestAccElastiCacheParameterGroup_disappears(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_basic(rName), + Config: testAccParameterGroupConfig_Redis_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelasticache.ResourceParameterGroup(), resourceName), @@ -154,7 +184,7 @@ func TestAccElastiCacheParameterGroup_removeAllParameters(t *testing.T) { ), }, { - Config: testAccParameterGroupConfig_basic(rName), + Config: testAccParameterGroupConfig_Redis_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "parameter.#", acctest.Ct0), @@ -190,7 +220,7 @@ func TestAccElastiCacheParameterGroup_RemoveReservedMemoryParameter_allParameter ), }, { - Config: testAccParameterGroupConfig_basic(rName), + Config: testAccParameterGroupConfig_Redis_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &cacheParameterGroup1), resource.TestCheckResourceAttr(resourceName, "parameter.#", acctest.Ct0), @@ -431,7 +461,7 @@ func TestAccElastiCacheParameterGroup_tags(t *testing.T) { ), }, { - Config: testAccParameterGroupConfig_basic(rName), + Config: testAccParameterGroupConfig_Redis_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &cacheParameterGroup1), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), @@ -497,24 +527,19 @@ func testAccCheckParameterGroupExists(ctx context.Context, n string, v *awstypes } } -func testAccCheckParameterGroupAttributes(v *awstypes.CacheParameterGroup, rName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *v.CacheParameterGroupName != rName { - return fmt.Errorf("bad name: %#v", v.CacheParameterGroupName) - } - - if *v.CacheParameterGroupFamily != "redis2.8" { - return fmt.Errorf("bad family: %#v", v.CacheParameterGroupFamily) - } - - return nil - } +func testAccParameterGroupConfig_Redis_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_elasticache_parameter_group" "test" { + family = "redis2.8" + name = %[1]q +} +`, rName) } -func testAccParameterGroupConfig_basic(rName string) string { +func testAccParameterGroupConfig_Valkey_basic(rName string) string { return fmt.Sprintf(` resource "aws_elasticache_parameter_group" "test" { - family = "redis2.8" + family = "valkey7" name = %[1]q } `, rName) diff --git a/internal/service/elasticache/replication_group.go b/internal/service/elasticache/replication_group.go index ba8870a3583..7c8fd300009 100644 --- a/internal/service/elasticache/replication_group.go +++ b/internal/service/elasticache/replication_group.go @@ -124,15 +124,17 @@ func resourceReplicationGroup() *schema.Resource { names.AttrEngine: { Type: schema.TypeString, Optional: true, - ForceNew: true, Default: engineRedis, - ValidateFunc: validation.StringInSlice([]string{engineRedis}, true), + ValidateFunc: validation.StringInSlice([]string{engineRedis, engineValkey}, true), }, names.AttrEngineVersion: { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validRedisVersionString, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.Any( + validRedisVersionString, + validValkeyVersionString, + ), }, "engine_version_actual": { Type: schema.TypeString, @@ -396,6 +398,15 @@ func resourceReplicationGroup() *schema.Resource { CustomizeDiff: customdiff.All( replicationGroupValidateMultiAZAutomaticFailover, customizeDiffEngineVersionForceNewOnDowngrade, + customdiff.ForceNewIf(names.AttrEngine, func(_ context.Context, diff *schema.ResourceDiff, meta interface{}) bool { + if !diff.HasChange(names.AttrEngine) { + return false + } + if old, new := diff.GetChange(names.AttrEngine); old.(string) == engineRedis && new.(string) == engineValkey { + return false + } + return true + }), customdiff.ComputedIf("member_clusters", func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) bool { return diff.HasChange("num_cache_clusters") || diff.HasChange("num_node_groups") || @@ -464,6 +475,7 @@ func resourceReplicationGroupCreate(ctx context.Context, d *schema.ResourceData, input.AutomaticFailoverEnabled = aws.Bool(d.Get("automatic_failover_enabled").(bool)) input.CacheNodeType = aws.String(nodeType) input.Engine = aws.String(d.Get(names.AttrEngine).(string)) + input.TransitEncryptionEnabled = aws.Bool(d.Get("transit_encryption_enabled").(bool)) } if v, ok := d.GetOk("ip_discovery"); ok { @@ -569,10 +581,6 @@ func resourceReplicationGroupCreate(ctx context.Context, d *schema.ResourceData, input.SnapshotWindow = aws.String(v.(string)) } - if v, ok := d.GetOk("transit_encryption_enabled"); ok { - input.TransitEncryptionEnabled = aws.Bool(v.(bool)) - } - if v, ok := d.GetOk("transit_encryption_mode"); ok { input.TransitEncryptionMode = awstypes.TransitEncryptionMode(v.(string)) } @@ -656,6 +664,8 @@ func resourceReplicationGroupRead(ctx context.Context, d *schema.ResourceData, m d.Set("global_replication_group_id", rgp.GlobalReplicationGroupInfo.GlobalReplicationGroupId) } + d.Set(names.AttrEngine, rgp.Engine) + switch rgp.AutomaticFailover { case awstypes.AutomaticFailoverStatusDisabled, awstypes.AutomaticFailoverStatusDisabling: d.Set("automatic_failover_enabled", false) @@ -823,6 +833,14 @@ func resourceReplicationGroupUpdate(ctx context.Context, d *schema.ResourceData, requestUpdate = true } + if old, new := d.GetChange(names.AttrEngine); old.(string) == engineRedis && new.(string) == engineValkey { + if !d.HasChange(names.AttrEngineVersion) { + return sdkdiag.AppendErrorf(diags, "must explicitly set '%s' attribute for Replication Group (%s) when updating engine to 'valkey'", names.AttrEngineVersion, d.Id()) + } + input.Engine = aws.String(d.Get(names.AttrEngine).(string)) + requestUpdate = true + } + if d.HasChange(names.AttrEngineVersion) { input.EngineVersion = aws.String(d.Get(names.AttrEngineVersion).(string)) requestUpdate = true diff --git a/internal/service/elasticache/replication_group_migrate.go b/internal/service/elasticache/replication_group_migrate.go index bf4b6526629..a002ccdafe2 100644 --- a/internal/service/elasticache/replication_group_migrate.go +++ b/internal/service/elasticache/replication_group_migrate.go @@ -9,6 +9,7 @@ import ( awstypes "github.com/aws/aws-sdk-go-v2/service/elasticache/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" tfmaps "github.com/hashicorp/terraform-provider-aws/internal/maps" "github.com/hashicorp/terraform-provider-aws/internal/sdkv2/types/nullable" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -91,10 +92,11 @@ func resourceReplicationGroupConfigV1() *schema.Resource { Computed: true, }, names.AttrEngine: { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: engineRedis, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: engineRedis, + ValidateFunc: validation.StringInSlice([]string{engineRedis, engineValkey}, true), }, names.AttrEngineVersion: { Type: schema.TypeString, diff --git a/internal/service/elasticache/replication_group_test.go b/internal/service/elasticache/replication_group_test.go index 89993f67396..b55b4c90424 100644 --- a/internal/service/elasticache/replication_group_test.go +++ b/internal/service/elasticache/replication_group_test.go @@ -31,7 +31,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccElastiCacheReplicationGroup_basic(t *testing.T) { +func TestAccElastiCacheReplicationGroup_Redis_basic(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -48,7 +48,7 @@ func TestAccElastiCacheReplicationGroup_basic(t *testing.T) { CheckDestroy: testAccCheckReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccReplicationGroupConfig_basic(rName), + Config: testAccReplicationGroupConfig_basic_engine(rName, "redis"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "redis"), @@ -60,7 +60,7 @@ func TestAccElastiCacheReplicationGroup_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "num_node_groups", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "replicas_per_node_group", acctest.Ct0), resource.TestCheckResourceAttr(resourceName, "cluster_enabled", acctest.CtFalse), - testCheckEngineStuffDefault(ctx, resourceName), + testCheckEngineStuffRedisDefault(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, names.AttrAutoMinorVersionUpgrade, acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, "data_tiering_enabled", acctest.CtFalse), ), @@ -75,7 +75,7 @@ func TestAccElastiCacheReplicationGroup_basic(t *testing.T) { }) } -func TestAccElastiCacheReplicationGroup_basic_v5(t *testing.T) { +func TestAccElastiCacheReplicationGroup_Redis_basic_v5(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -92,7 +92,7 @@ func TestAccElastiCacheReplicationGroup_basic_v5(t *testing.T) { CheckDestroy: testAccCheckReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccReplicationGroupConfig_v5(rName), + Config: testAccReplicationGroupConfig_Redis_v5(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "redis"), @@ -112,6 +112,50 @@ func TestAccElastiCacheReplicationGroup_basic_v5(t *testing.T) { }) } +func TestAccElastiCacheReplicationGroup_Valkey_basic(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var rg awstypes.ReplicationGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elasticache_replication_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckReplicationGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccReplicationGroupConfig_basic_engine(rName, "valkey"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckReplicationGroupExists(ctx, resourceName, &rg), + resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "valkey"), + acctest.CheckResourceAttrRegionalARN(resourceName, names.AttrARN, "elasticache", fmt.Sprintf("replicationgroup:%s", rName)), + resource.TestCheckResourceAttr(resourceName, "num_cache_clusters", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "multi_az_enabled", acctest.CtFalse), + resource.TestCheckResourceAttr(resourceName, "automatic_failover_enabled", acctest.CtFalse), + resource.TestCheckResourceAttr(resourceName, "member_clusters.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "num_node_groups", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "replicas_per_node_group", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "cluster_enabled", acctest.CtFalse), + testCheckEngineStuffValkeyDefault(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, names.AttrAutoMinorVersionUpgrade, acctest.CtTrue), + resource.TestCheckResourceAttr(resourceName, "data_tiering_enabled", acctest.CtFalse), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{names.AttrApplyImmediately, "auth_token_update_strategy"}, + }, + }, + }) +} + func TestAccElastiCacheReplicationGroup_uppercase(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { @@ -145,7 +189,7 @@ func TestAccElastiCacheReplicationGroup_uppercase(t *testing.T) { }) } -func TestAccElastiCacheReplicationGroup_EngineVersion_v7(t *testing.T) { +func TestAccElastiCacheReplicationGroup_Redis_EngineVersion_v7(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -298,6 +342,50 @@ func TestAccElastiCacheReplicationGroup_EngineVersion_6xToRealVersion(t *testing }) } +func TestAccElastiCacheReplicationGroup_Engine_RedisToValkey(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var v1, v2 awstypes.ReplicationGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elasticache_replication_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckReplicationGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccReplicationGroupConfig_basic_engine(rName, "redis"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckReplicationGroupExists(ctx, resourceName, &v1), + resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "redis"), + ), + }, + { + Config: testAccReplicationGroupConfig_basic_engine(rName, "valkey"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckReplicationGroupExists(ctx, resourceName, &v2), + testAccCheckReplicationGroupNotRecreated(&v1, &v2), + resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "valkey"), + ), + ExpectError: regexache.MustCompile("must explicitly set 'engine_version' attribute"), + }, + { + Config: testAccReplicationGroupConfig_update_Valkey(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckReplicationGroupExists(ctx, resourceName, &v2), + testAccCheckReplicationGroupNotRecreated(&v1, &v2), + resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "valkey"), + ), + }, + }, + }) +} + func TestAccElastiCacheReplicationGroup_disappears(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { @@ -315,7 +403,7 @@ func TestAccElastiCacheReplicationGroup_disappears(t *testing.T) { CheckDestroy: testAccCheckReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccReplicationGroupConfig_basic(rName), + Config: testAccReplicationGroupConfig_basic_engine(rName, "redis"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelasticache.ResourceReplicationGroup(), resourceName), @@ -343,7 +431,7 @@ func TestAccElastiCacheReplicationGroup_updateDescription(t *testing.T) { CheckDestroy: testAccCheckReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccReplicationGroupConfig_basic(rName), + Config: testAccReplicationGroupConfig_basic_engine(rName, "redis"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), resource.TestCheckResourceAttr(resourceName, "num_cache_clusters", acctest.Ct1), @@ -385,7 +473,7 @@ func TestAccElastiCacheReplicationGroup_updateMaintenanceWindow(t *testing.T) { CheckDestroy: testAccCheckReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccReplicationGroupConfig_basic(rName), + Config: testAccReplicationGroupConfig_basic_engine(rName, "redis"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), resource.TestCheckResourceAttr(resourceName, "maintenance_window", "tue:06:30-tue:07:30"), @@ -469,7 +557,7 @@ func TestAccElastiCacheReplicationGroup_updateNodeSize(t *testing.T) { CheckDestroy: testAccCheckReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccReplicationGroupConfig_basic(rName), + Config: testAccReplicationGroupConfig_basic_engine(rName, "redis"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), resource.TestCheckResourceAttr(resourceName, "num_cache_clusters", acctest.Ct1), @@ -631,14 +719,14 @@ func TestAccElastiCacheReplicationGroup_stateUpgrade5270(t *testing.T) { VersionConstraint: "5.26.0", }, }, - Config: testAccReplicationGroupConfig_basic(rName), + Config: testAccReplicationGroupConfig_basic_engine(rName, "redis"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), ), }, { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - Config: testAccReplicationGroupConfig_basic(rName), + Config: testAccReplicationGroupConfig_basic_engine(rName, "redis"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), ), @@ -670,14 +758,14 @@ func TestAccElastiCacheReplicationGroup_stateUpgrade5590(t *testing.T) { VersionConstraint: "4.67.0", }, }, - Config: testAccReplicationGroupConfig_basic(rName), + Config: testAccReplicationGroupConfig_basic_engine(rName, "redis"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), ), }, { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - Config: testAccReplicationGroupConfig_basic(rName), + Config: testAccReplicationGroupConfig_basic_engine(rName, "redis"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), ), @@ -1483,7 +1571,7 @@ func TestAccElastiCacheReplicationGroup_enableSnapshotting(t *testing.T) { CheckDestroy: testAccCheckReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccReplicationGroupConfig_basic(rName), + Config: testAccReplicationGroupConfig_basic_engine(rName, "redis"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), resource.TestCheckResourceAttr(resourceName, "snapshot_retention_limit", acctest.Ct0), @@ -2262,20 +2350,20 @@ func TestAccElastiCacheReplicationGroup_tagWithOtherModification(t *testing.T) { CheckDestroy: testAccCheckReplicationGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccReplicationGroupConfig_versionAndTag(rName, "5.0.5", acctest.CtKey1, acctest.CtValue1), + Config: testAccReplicationGroupConfig_versionAndTag(rName, "6.0", acctest.CtKey1, acctest.CtValue1), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), - resource.TestCheckResourceAttr(resourceName, names.AttrEngineVersion, "5.0.5"), + resource.TestCheckResourceAttr(resourceName, names.AttrEngineVersion, "6.0"), testAccReplicationGroupCheckMemberClusterTags(resourceName, clusterDataSourcePrefix, 2, []kvp{ {acctest.CtKey1, acctest.CtValue1}, }), ), }, { - Config: testAccReplicationGroupConfig_versionAndTag(rName, "5.0.6", acctest.CtKey1, acctest.CtValue1Updated), + Config: testAccReplicationGroupConfig_versionAndTag(rName, "6.2", acctest.CtKey1, acctest.CtValue1Updated), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), - resource.TestCheckResourceAttr(resourceName, names.AttrEngineVersion, "5.0.6"), + resource.TestCheckResourceAttr(resourceName, names.AttrEngineVersion, "6.2"), testAccReplicationGroupCheckMemberClusterTags(resourceName, clusterDataSourcePrefix, 2, []kvp{ {acctest.CtKey1, acctest.CtValue1Updated}, }), @@ -2658,7 +2746,7 @@ func TestAccElastiCacheReplicationGroup_dataTiering(t *testing.T) { Config: testAccReplicationGroupConfig_dataTiering(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckReplicationGroupExists(ctx, resourceName, &rg), - testCheckRedisEngineVersionLatest(ctx, &version), + testCheckEngineVersionLatest(ctx, "redis", &version), resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "redis"), func(s *terraform.State) error { return resource.TestCheckResourceAttr(resourceName, names.AttrEngineVersion, *version.EngineVersion)(s) @@ -3023,15 +3111,38 @@ func testAccCheckReplicationGroupNotRecreated(i, j *awstypes.ReplicationGroup) r } } -func testCheckEngineStuffDefault(ctx context.Context, resourceName string) resource.TestCheckFunc { +func testCheckEngineStuffRedisDefault(ctx context.Context, resourceName string) resource.TestCheckFunc { + var ( + version awstypes.CacheEngineVersion + parameterGroup awstypes.CacheParameterGroup + ) + + checks := []resource.TestCheckFunc{ + testCheckEngineVersionLatest(ctx, "redis", &version), + testCheckParameterGroupDefault(ctx, &version, ¶meterGroup), + func(s *terraform.State) error { + return resource.TestCheckResourceAttr(resourceName, names.AttrEngineVersion, *version.EngineVersion)(s) + }, + func(s *terraform.State) error { + return resource.TestMatchResourceAttr(resourceName, "engine_version_actual", regexache.MustCompile(fmt.Sprintf(`^%s\.[[:digit:]]+$`, *version.EngineVersion)))(s) + }, + func(s *terraform.State) error { + return resource.TestCheckResourceAttr(resourceName, names.AttrParameterGroupName, *parameterGroup.CacheParameterGroupName)(s) + }, + } + + return resource.ComposeAggregateTestCheckFunc(checks...) +} + +func testCheckEngineStuffValkeyDefault(ctx context.Context, resourceName string) resource.TestCheckFunc { var ( version awstypes.CacheEngineVersion parameterGroup awstypes.CacheParameterGroup ) checks := []resource.TestCheckFunc{ - testCheckRedisEngineVersionLatest(ctx, &version), - testCheckRedisParameterGroupDefault(ctx, &version, ¶meterGroup), + testCheckEngineVersionLatest(ctx, "valkey", &version), + testCheckParameterGroupDefault(ctx, &version, ¶meterGroup), func(s *terraform.State) error { return resource.TestCheckResourceAttr(resourceName, names.AttrEngineVersion, *version.EngineVersion)(s) }, @@ -3046,12 +3157,12 @@ func testCheckEngineStuffDefault(ctx context.Context, resourceName string) resou return resource.ComposeAggregateTestCheckFunc(checks...) } -func testCheckRedisEngineVersionLatest(ctx context.Context, v *awstypes.CacheEngineVersion) resource.TestCheckFunc { +func testCheckEngineVersionLatest(ctx context.Context, engine string, v *awstypes.CacheEngineVersion) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ElastiCacheClient(ctx) versions, err := conn.DescribeCacheEngineVersions(ctx, &elasticache.DescribeCacheEngineVersionsInput{ - Engine: aws.String("redis"), + Engine: aws.String(engine), DefaultOnly: aws.Bool(true), }) if err != nil { @@ -3070,7 +3181,7 @@ func testCheckRedisEngineVersionLatest(ctx context.Context, v *awstypes.CacheEng } } -func testCheckRedisParameterGroupDefault(ctx context.Context, version *awstypes.CacheEngineVersion, v *awstypes.CacheParameterGroup) resource.TestCheckFunc { +func testCheckParameterGroupDefault(ctx context.Context, version *awstypes.CacheEngineVersion, v *awstypes.CacheParameterGroup) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ElastiCacheClient(ctx) @@ -3101,7 +3212,7 @@ func testCheckEngineStuffClusterEnabledDefault(ctx context.Context, resourceName ) checks := []resource.TestCheckFunc{ - testCheckRedisEngineVersionLatest(ctx, &version), + testCheckEngineVersionLatest(ctx, "redis", &version), testCheckRedisParameterGroupClusterEnabledDefault(ctx, &version, ¶meterGroup), func(s *terraform.State) error { return resource.TestCheckResourceAttr(resourceName, names.AttrEngineVersion, *version.EngineVersion)(s) @@ -3166,7 +3277,22 @@ func testAccCheckResourceTags(resourceName string, kvs []kvp) []resource.TestChe return checks } -func testAccReplicationGroupConfig_basic(rName string) string { +func testAccReplicationGroupConfig_basic_engine(rName string, engine string) string { + return fmt.Sprintf(` +resource "aws_elasticache_replication_group" "test" { + replication_group_id = %[1]q + description = "test description" + node_type = "cache.t3.small" + port = 6379 + apply_immediately = true + maintenance_window = "tue:06:30-tue:07:30" + snapshot_window = "01:00-02:00" + engine = %[2]q +} +`, rName, engine) +} + +func testAccReplicationGroupConfig_update_Valkey(rName string) string { return fmt.Sprintf(` resource "aws_elasticache_replication_group" "test" { replication_group_id = %[1]q @@ -3176,6 +3302,9 @@ resource "aws_elasticache_replication_group" "test" { apply_immediately = true maintenance_window = "tue:06:30-tue:07:30" snapshot_window = "01:00-02:00" + engine = "valkey" + engine_version = "7.2" + #parameter_group_name = "default.valkey7" } `, rName) } @@ -3194,7 +3323,7 @@ resource "aws_elasticache_replication_group" "test" { `, rName) } -func testAccReplicationGroupConfig_v5(rName string) string { +func testAccReplicationGroupConfig_Redis_v5(rName string) string { return fmt.Sprintf(` resource "aws_elasticache_replication_group" "test" { replication_group_id = %[1]q diff --git a/internal/service/elasticache/reserved_cache_node_offering_data_source_test.go b/internal/service/elasticache/reserved_cache_node_offering_data_source_test.go index 1e45423e480..ce0074601fe 100644 --- a/internal/service/elasticache/reserved_cache_node_offering_data_source_test.go +++ b/internal/service/elasticache/reserved_cache_node_offering_data_source_test.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccElastiCacheReservedNodeOffering_basic(t *testing.T) { +func TestAccElastiCacheReservedNodeOffering_Redis_basic(t *testing.T) { ctx := acctest.Context(t) dataSourceName := "data.aws_elasticache_reserved_cache_node_offering.test" @@ -22,7 +22,7 @@ func TestAccElastiCacheReservedNodeOffering_basic(t *testing.T) { ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), Steps: []resource.TestStep{ { - Config: testAccReservedNodeOfferingConfig_basic(), + Config: testAccReservedNodeOfferingConfig_Redis_basic(), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(dataSourceName, "cache_node_type", "cache.t4g.small"), resource.TestCheckResourceAttr(dataSourceName, names.AttrDuration, "P1Y"), @@ -36,7 +36,32 @@ func TestAccElastiCacheReservedNodeOffering_basic(t *testing.T) { }) } -func testAccReservedNodeOfferingConfig_basic() string { +func TestAccElastiCacheReservedNodeOffering_Valkey_basic(t *testing.T) { + ctx := acctest.Context(t) + dataSourceName := "data.aws_elasticache_reserved_cache_node_offering.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: nil, + ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), + Steps: []resource.TestStep{ + { + Config: testAccReservedNodeOfferingConfig_Valkey_basic(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "cache_node_type", "cache.t4g.small"), + resource.TestCheckResourceAttr(dataSourceName, names.AttrDuration, "P1Y"), + resource.TestCheckResourceAttrSet(dataSourceName, "fixed_price"), + resource.TestCheckResourceAttrSet(dataSourceName, "offering_id"), + resource.TestCheckResourceAttr(dataSourceName, "offering_type", "No Upfront"), + resource.TestCheckResourceAttr(dataSourceName, "product_description", "valkey"), + ), + }, + }, + }) +} + +func testAccReservedNodeOfferingConfig_Redis_basic() string { return ` data "aws_elasticache_reserved_cache_node_offering" "test" { cache_node_type = "cache.t4g.small" @@ -46,3 +71,14 @@ data "aws_elasticache_reserved_cache_node_offering" "test" { } ` } + +func testAccReservedNodeOfferingConfig_Valkey_basic() string { + return ` +data "aws_elasticache_reserved_cache_node_offering" "test" { + cache_node_type = "cache.t4g.small" + duration = "P1Y" + offering_type = "No Upfront" + product_description = "valkey" +} +` +} diff --git a/internal/service/elasticache/reserved_cache_node_test.go b/internal/service/elasticache/reserved_cache_node_test.go index 47b22253e9e..e51779e572f 100644 --- a/internal/service/elasticache/reserved_cache_node_test.go +++ b/internal/service/elasticache/reserved_cache_node_test.go @@ -23,7 +23,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccElastiCacheReservedCacheNode_basic(t *testing.T) { +func TestAccElastiCacheReservedCacheNode_Redis_basic(t *testing.T) { ctx := acctest.Context(t) if os.Getenv("TF_TEST_ELASTICACHE_RESERVED_CACHE_NODE") == "" { t.Skip("Environment variable TF_TEST_ELASTICACHE_RESERVED_CACHE_NODE is not set") @@ -40,7 +40,50 @@ func TestAccElastiCacheReservedCacheNode_basic(t *testing.T) { ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), Steps: []resource.TestStep{ { - Config: testAccReservedInstanceConfig_basic(), + Config: testAccReservedInstanceConfig_Redis_basic(), + Check: resource.ComposeTestCheckFunc( + testAccReservedInstanceExists(ctx, resourceName, &reservation), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "elasticache", regexache.MustCompile(`reserved-instance:.+`)), + resource.TestCheckResourceAttr(resourceName, "cache_node_count", acctest.Ct1), + resource.TestCheckResourceAttrPair(dataSourceName, "cache_node_type", resourceName, "cache_node_type"), + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrDuration, resourceName, names.AttrDuration), + resource.TestCheckResourceAttrPair(dataSourceName, "fixed_price", resourceName, "fixed_price"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrID), + resource.TestCheckResourceAttrPair(dataSourceName, "reserved_cache_nodes_offering_id", resourceName, "offering_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "offering_type", resourceName, "offering_type"), + resource.TestCheckResourceAttrPair(dataSourceName, "product_description", resourceName, "product_description"), + resource.TestCheckResourceAttrSet(resourceName, "recurring_charges"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrStartTime), + resource.TestCheckResourceAttrSet(resourceName, names.AttrState), + resource.TestCheckResourceAttrSet(resourceName, "usage_price"), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{})), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + }, + }, + }) +} + +func TestAccElastiCacheReservedCacheNode_Valkey_basic(t *testing.T) { + ctx := acctest.Context(t) + if os.Getenv("TF_TEST_ELASTICACHE_RESERVED_CACHE_NODE") == "" { + t.Skip("Environment variable TF_TEST_ELASTICACHE_RESERVED_CACHE_NODE is not set") + } + + var reservation awstypes.ReservedCacheNode + resourceName := "aws_elasticache_reserved_cache_node.test" + dataSourceName := "data.aws_elasticache_reserved_cache_node_offering.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: nil, + ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), + Steps: []resource.TestStep{ + { + Config: testAccReservedInstanceConfig_Valkey_basic(), Check: resource.ComposeTestCheckFunc( testAccReservedInstanceExists(ctx, resourceName, &reservation), acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "elasticache", regexache.MustCompile(`reserved-instance:.+`)), @@ -119,7 +162,7 @@ func testAccReservedInstanceExists(ctx context.Context, n string, reservation *a } } -func testAccReservedInstanceConfig_basic() string { +func testAccReservedInstanceConfig_Redis_basic() string { return ` resource "aws_elasticache_reserved_cache_node" "test" { offering_id = data.aws_elasticache_reserved_cache_node_offering.test.offering_id @@ -134,6 +177,21 @@ data "aws_elasticache_reserved_cache_node_offering" "test" { ` } +func testAccReservedInstanceConfig_Valkey_basic() string { + return ` +resource "aws_elasticache_reserved_cache_node" "test" { + offering_id = data.aws_elasticache_reserved_cache_node_offering.test.offering_id +} + +data "aws_elasticache_reserved_cache_node_offering" "test" { + cache_node_type = "cache.t4g.small" + duration = 31536000 + offering_type = "No Upfront" + product_description = "valkey" +} +` +} + func testAccReservedInstanceConfig_ID(rName string) string { return fmt.Sprintf(` resource "aws_elasticache_reserved_cache_node" "test" { diff --git a/internal/service/elasticache/serverless_cache.go b/internal/service/elasticache/serverless_cache.go index 48cfd927771..bb7f36bf1ea 100644 --- a/internal/service/elasticache/serverless_cache.go +++ b/internal/service/elasticache/serverless_cache.go @@ -24,6 +24,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/errs" @@ -94,7 +95,11 @@ func (r *serverlessCacheResource) Schema(ctx context.Context, request resource.S names.AttrEngine: schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), + stringplanmodifier.RequiresReplaceIf(func(ctx context.Context, sr planmodifier.StringRequest, rrifr *stringplanmodifier.RequiresReplaceIfFuncResponse) { + configIsRedis := sr.ConfigValue.Equal(basetypes.NewStringValue(engineRedis)) + planIsValkey := sr.PlanValue.Equal(basetypes.NewStringValue(engineValkey)) + rrifr.RequiresReplace = !(configIsRedis && planIsValkey) + }, "Replace engine diff", "Replace engine diff"), }, }, "full_engine_version": schema.StringAttribute{ @@ -338,6 +343,14 @@ func (r *serverlessCacheResource) Update(ctx context.Context, request resource.U if serverlessCacheHasChanges(ctx, new, old) { input := &elasticache.ModifyServerlessCacheInput{} response.Diagnostics.Append(fwflex.Expand(ctx, new, input)...) + // Unset engine related stuff to prevent the following error: + // This API supports only cross-engine upgrades to Valkey engine currently. + if new.Engine.Equal(old.Engine) { + input.Engine = nil + } + if new.MajorEngineVersion.Equal(old.MajorEngineVersion) { + input.MajorEngineVersion = nil + } if response.Diagnostics.HasError() { return } @@ -357,7 +370,7 @@ func (r *serverlessCacheResource) Update(ctx context.Context, request resource.U } } - // AWS returns null values for certain values that are available on redis only. + // AWS returns null values for certain values that are available on redis/valkey only. // always set these values to the state value to avoid unnecessary diff failures on computed values. output, err := findServerlessCacheByID(ctx, conn, old.ID.ValueString()) diff --git a/internal/service/elasticache/serverless_cache_data_source_test.go b/internal/service/elasticache/serverless_cache_data_source_test.go index a6b6c23bd05..12da207d9d9 100644 --- a/internal/service/elasticache/serverless_cache_data_source_test.go +++ b/internal/service/elasticache/serverless_cache_data_source_test.go @@ -13,7 +13,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccElastiCacheServerlessCacheDataSource_basic(t *testing.T) { +func TestAccElastiCacheServerlessCacheDataSource_Redis_basic(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -32,7 +32,7 @@ func TestAccElastiCacheServerlessCacheDataSource_basic(t *testing.T) { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { - Config: testAccServerlessCacheDataSourceConfig_basic(rName), + Config: testAccServerlessCacheDataSourceConfig_Redis_basic(rName), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPair(dataSourceName, names.AttrARN, resourceName, names.AttrARN), resource.TestCheckResourceAttrPair(dataSourceName, names.AttrCreateTime, resourceName, names.AttrCreateTime), @@ -59,7 +59,53 @@ func TestAccElastiCacheServerlessCacheDataSource_basic(t *testing.T) { }) } -func testAccServerlessCacheDataSourceConfig_basic(rName string) string { +func TestAccElastiCacheServerlessCacheDataSource_Valkey_basic(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elasticache_serverless_cache.test" + dataSourceName := "data.aws_elasticache_serverless_cache.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.ElastiCacheEndpointID) + }, + ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccServerlessCacheDataSourceConfig_Valkey_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrARN, resourceName, names.AttrARN), + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrCreateTime, resourceName, names.AttrCreateTime), + resource.TestCheckResourceAttrPair(dataSourceName, "daily_snapshot_time", resourceName, "daily_snapshot_time"), + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrDescription, resourceName, names.AttrDescription), + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrEngine, resourceName, names.AttrEngine), + resource.TestCheckResourceAttrPair(dataSourceName, "full_engine_version", resourceName, "full_engine_version"), + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrKMSKeyID, resourceName, names.AttrKMSKeyID), + resource.TestCheckResourceAttrPair(dataSourceName, "major_engine_version", resourceName, "major_engine_version"), + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrName, resourceName, names.AttrName), + resource.TestCheckResourceAttrPair(dataSourceName, "security_group_ids.#", resourceName, "security_group_ids.#"), + resource.TestCheckResourceAttrPair(dataSourceName, "snapshot_retention_limit", resourceName, "snapshot_retention_limit"), + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrStatus, resourceName, names.AttrStatus), + resource.TestCheckResourceAttrPair(dataSourceName, "subnet_ids.#", resourceName, "subnet_ids.#"), + resource.TestCheckResourceAttrPair(dataSourceName, "user_group_id", resourceName, "user_group_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "cache_usage_limits.#", resourceName, "cache_usage_limits.#"), + resource.TestCheckResourceAttrPair(dataSourceName, "endpoint.address", resourceName, "endpoint.0.address"), + resource.TestCheckResourceAttrPair(dataSourceName, "endpoint.port", resourceName, "endpoint.0.port"), + resource.TestCheckResourceAttrPair(dataSourceName, "reader_endpoint.address", resourceName, "reader_endpoint.0.address"), + resource.TestCheckResourceAttrPair(dataSourceName, "reader_endpoint.port", resourceName, "reader_endpoint.0.port"), + ), + }, + }, + }) +} + +func testAccServerlessCacheDataSourceConfig_Redis_basic(rName string) string { return fmt.Sprintf(` resource "aws_elasticache_serverless_cache" "test" { engine = "redis" @@ -71,3 +117,15 @@ data "aws_elasticache_serverless_cache" "test" { } `, rName) } +func testAccServerlessCacheDataSourceConfig_Valkey_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_elasticache_serverless_cache" "test" { + engine = "valkey" + name = %[1]q +} + +data "aws_elasticache_serverless_cache" "test" { + name = aws_elasticache_serverless_cache.test.name +} +`, rName) +} diff --git a/internal/service/elasticache/serverless_cache_test.go b/internal/service/elasticache/serverless_cache_test.go index eb7b2f82203..e11316b07f5 100644 --- a/internal/service/elasticache/serverless_cache_test.go +++ b/internal/service/elasticache/serverless_cache_test.go @@ -21,7 +21,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccElastiCacheServerlessCache_basic(t *testing.T) { +func TestAccElastiCacheServerlessCache_basicRedis(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -40,12 +40,13 @@ func TestAccElastiCacheServerlessCache_basic(t *testing.T) { ), Steps: []resource.TestStep{ { - Config: testAccServerlessCacheConfig_basic(rName), + Config: testAccServerlessCacheConfig_basicRedis(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckServerlessCacheExists(ctx, resourceName, &serverlessElasticCache), resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), resource.TestCheckResourceAttrSet(resourceName, "cache_usage_limits.#"), resource.TestCheckResourceAttrSet(resourceName, names.AttrCreateTime), + resource.TestCheckResourceAttrSet(resourceName, "daily_snapshot_time"), resource.TestCheckResourceAttrSet(resourceName, "endpoint.#"), resource.TestCheckResourceAttrSet(resourceName, names.AttrEngine), resource.TestCheckResourceAttrSet(resourceName, "full_engine_version"), @@ -64,7 +65,7 @@ func TestAccElastiCacheServerlessCache_basic(t *testing.T) { }) } -func TestAccElastiCacheServerlessCache_basicRedis(t *testing.T) { +func TestAccElastiCacheServerlessCache_basicValkey(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -83,7 +84,7 @@ func TestAccElastiCacheServerlessCache_basicRedis(t *testing.T) { ), Steps: []resource.TestStep{ { - Config: testAccServerlessCacheConfig_basicRedis(rName), + Config: testAccServerlessCacheConfig_basicValkey(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckServerlessCacheExists(ctx, resourceName, &serverlessElasticCache), resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), @@ -199,6 +200,49 @@ func TestAccElastiCacheServerlessCache_fullRedis(t *testing.T) { }) } +func TestAccElastiCacheServerlessCache_fullValkey(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elasticache_serverless_cache.test" + var serverlessElasticCache awstypes.ServerlessCache + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: resource.ComposeAggregateTestCheckFunc( + testAccCheckServerlessCacheDestroy(ctx), + ), + Steps: []resource.TestStep{ + { + Config: testAccServerlessCacheConfig_fullValkey(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckServerlessCacheExists(ctx, resourceName, &serverlessElasticCache), + resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), + resource.TestCheckResourceAttrSet(resourceName, "cache_usage_limits.#"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrCreateTime), + resource.TestCheckResourceAttrSet(resourceName, "endpoint.#"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrEngine), + resource.TestCheckResourceAttrSet(resourceName, "full_engine_version"), + resource.TestCheckResourceAttrSet(resourceName, "reader_endpoint.#"), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + resource.TestCheckResourceAttrSet(resourceName, names.AttrStatus), + resource.TestCheckResourceAttrSet(resourceName, "subnet_ids.#"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccElastiCacheServerlessCache_update(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { @@ -362,6 +406,42 @@ func TestAccElastiCacheServerlessCache_updatesc(t *testing.T) { }) } +func TestAccElastiCacheServerlessCache_update_RedisToValkey(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elasticache_serverless_cache.test" + var v1, v2 awstypes.ServerlessCache + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: resource.ComposeAggregateTestCheckFunc( + testAccCheckServerlessCacheDestroy(ctx), + ), + Steps: []resource.TestStep{ + { + Config: testAccServerlessCacheConfig_basicRedis(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckServerlessCacheExists(ctx, resourceName, &v1), + resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "redis"), + ), + }, + { + Config: testAccServerlessCacheConfig_updateValkey(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckServerlessCacheExists(ctx, resourceName, &v2), + resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "valkey"), + ), + }, + }, + }) +} + func TestAccElastiCacheServerlessCache_disappears(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { @@ -528,6 +608,24 @@ resource "aws_elasticache_serverless_cache" "test" { `, rName) } +func testAccServerlessCacheConfig_basicValkey(rName string) string { + return fmt.Sprintf(` +resource "aws_elasticache_serverless_cache" "test" { + engine = "valkey" + name = %[1]q +} +`, rName) +} + +func testAccServerlessCacheConfig_updateValkey(rName string) string { + return fmt.Sprintf(` +resource "aws_elasticache_serverless_cache" "test" { + engine = "valkey" + name = %[1]q +} +`, rName) +} + func testAccServerlessCacheConfig_full(rName string) string { return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 2), fmt.Sprintf(` resource "aws_elasticache_serverless_cache" "test" { @@ -626,6 +724,57 @@ resource "aws_security_group" "test" { `, rName)) } +func testAccServerlessCacheConfig_fullValkey(rName string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 2), fmt.Sprintf(` +resource "aws_elasticache_serverless_cache" "test" { + engine = "valkey" + name = %[1]q + + cache_usage_limits { + data_storage { + maximum = 10 + unit = "GB" + } + ecpu_per_second { + maximum = 1000 + } + } + + daily_snapshot_time = "09:00" + description = "Test Full Valkey Attributes" + kms_key_id = aws_kms_key.test.arn + major_engine_version = "7" + snapshot_retention_limit = 1 + security_group_ids = [aws_security_group.test.id] + subnet_ids = aws_subnet.test[*].id + + tags = { + Name = %[1]q + } +} + +resource "aws_kms_key" "test" { + description = %[1]q +} + +resource "aws_security_group" "test" { + name = %[1]q + vpc_id = aws_vpc.test.id + + ingress { + from_port = -1 + to_port = -1 + protocol = "icmp" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = %[1]q + } +} +`, rName)) +} + func testAccServerlessCacheConfig_update(rName, desc string) string { return fmt.Sprintf(` resource "aws_elasticache_serverless_cache" "test" { diff --git a/internal/service/elbv2/const.go b/internal/service/elbv2/const.go index 4747bac3073..6791aa1d04e 100644 --- a/internal/service/elbv2/const.go +++ b/internal/service/elbv2/const.go @@ -41,6 +41,7 @@ const ( loadBalancerAttributeRoutingHTTPXFFHeaderProcessingMode = "routing.http.xff_header_processing.mode" loadBalancerAttributeRoutingHTTP2Enabled = "routing.http2.enabled" loadBalancerAttributeWAFFailOpenEnabled = "waf.fail_open.enabled" + loadBalancerAttributeZonalShiftConfigEnabled = "zonal_shift.config.enabled" // The following attributes are supported by only Network Load Balancers: loadBalancerAttributeDNSRecordClientRoutingPolicy = "dns_record.client_routing_policy" diff --git a/internal/service/elbv2/listener.go b/internal/service/elbv2/listener.go index 021f0596f7f..379c360936a 100644 --- a/internal/service/elbv2/listener.go +++ b/internal/service/elbv2/listener.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "log" + "slices" "sort" "strconv" "strings" @@ -389,6 +390,14 @@ func resourceListener() *schema.Resource { }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), + "tcp_idle_timeout_seconds": { + Type: schema.TypeInt, + Optional: true, + Default: 350, + ValidateFunc: validation.IntBetween(60, 6000), + // Attribute only valid for TCP (NLB) and GENEVE (GWLB) listeners + DiffSuppressFunc: suppressIfListenerProtocolNot(awstypes.ProtocolEnumGeneve, awstypes.ProtocolEnumTcp), + }, }, CustomizeDiff: customdiff.All( @@ -413,6 +422,21 @@ func suppressIfDefaultActionTypeNot(t awstypes.ActionTypeEnum) schema.SchemaDiff } } +func suppressIfListenerProtocolNot(protocols ...awstypes.ProtocolEnum) schema.SchemaDiffSuppressFunc { + return func(k string, old string, new string, d *schema.ResourceData) bool { + protocolType := awstypes.ProtocolEnum(d.Get(names.AttrProtocol).(string)) + + // GENEVE protocol for GWLB listeners cannot be specified on create + // If protocol is blank (i.e. GWLB listener on plan) or load balancer ARN contains 'gwy', + // assume GENEVE protocol. + if protocolType == awstypes.ProtocolEnum("") || strings.Contains(d.Get("load_balancer_arn").(string), "gwy") { + protocolType = awstypes.ProtocolEnumGeneve + } + + return !slices.Contains(protocols, protocolType) + } +} + func resourceListenerCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBV2Client(ctx) @@ -504,6 +528,24 @@ func resourceListenerCreate(ctx context.Context, d *schema.ResourceData, meta in } } + listenerProtocolType := awstypes.ProtocolEnum(d.Get(names.AttrProtocol).(string)) + // Protocol does not need to be explicitly set with GWLB listeners, nor is it returned by the API + // If protocol is not set, use the load balancer ARN to determine if listener is gateway type and set protocol appropriately + if listenerProtocolType == awstypes.ProtocolEnum("") && strings.Contains(lbARN, "loadbalancer/gwy/") { + listenerProtocolType = awstypes.ProtocolEnumGeneve + } + + // Listener attributes like TCP idle timeout are not supported on create + var attributes []awstypes.ListenerAttribute + + attributes = append(attributes, listenerAttributes.expand(d, listenerProtocolType, false)...) + + if len(attributes) > 0 { + if err := modifyListenerAttributes(ctx, conn, d.Id(), attributes); err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + } + return append(diags, resourceListenerRead(ctx, d, meta)...) } @@ -544,6 +586,17 @@ func resourceListenerRead(ctx context.Context, d *schema.ResourceData, meta inte d.Set(names.AttrProtocol, listener.Protocol) d.Set("ssl_policy", listener.SslPolicy) + // DescribeListenerAttributes is not supported for 'TLS' protocol listeners. + if listener.Protocol != awstypes.ProtocolEnumTls { + attributes, err := findListenerAttributesByARN(ctx, conn, d.Id()) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading ELBv2 Listener (%s) attributes: %s", d.Id(), err) + } + + listenerAttributes.flatten(d, attributes) + } + return diags } @@ -551,6 +604,8 @@ func resourceListenerUpdate(ctx context.Context, d *schema.ResourceData, meta in var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBV2Client(ctx) + var attributes []awstypes.ListenerAttribute + if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { input := &elasticloadbalancingv2.ModifyListenerInput{ ListenerArn: aws.String(d.Id()), @@ -589,6 +644,14 @@ func resourceListenerUpdate(ctx context.Context, d *schema.ResourceData, meta in input.SslPolicy = aws.String(v.(string)) } + attributes = append(attributes, listenerAttributes.expand(d, awstypes.ProtocolEnum(d.Get(names.AttrProtocol).(string)), true)...) + + if len(attributes) > 0 { + if err := modifyListenerAttributes(ctx, conn, d.Id(), attributes); err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + } + _, err := tfresource.RetryWhenIsA[*awstypes.CertificateNotFoundException](ctx, d.Timeout(schema.TimeoutUpdate), func() (interface{}, error) { return conn.ModifyListener(ctx, input) }) @@ -617,6 +680,103 @@ func resourceListenerDelete(ctx context.Context, d *schema.ResourceData, meta in return diags } +func findListenerAttributesByARN(ctx context.Context, conn *elasticloadbalancingv2.Client, listenerARN string) ([]awstypes.ListenerAttribute, error) { + input := &elasticloadbalancingv2.DescribeListenerAttributesInput{ + ListenerArn: aws.String(listenerARN), + } + + output, err := conn.DescribeListenerAttributes(ctx, input) + + if errs.IsA[*awstypes.ListenerNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.Attributes, nil +} + +func modifyListenerAttributes(ctx context.Context, conn *elasticloadbalancingv2.Client, arn string, attributes []awstypes.ListenerAttribute) error { + input := &elasticloadbalancingv2.ModifyListenerAttributesInput{ + Attributes: attributes, + ListenerArn: aws.String(arn), + } + + _, err := conn.ModifyListenerAttributes(ctx, input) + + if err != nil { + return fmt.Errorf("modifying ELBv2 Listener (%s) attributes: %w", arn, err) + } + + return nil +} + +type listenerAttributeInfo struct { + apiAttributeKey string + listenerTypesSupported []awstypes.ProtocolEnum + tfType schema.ValueType +} + +type listenerAttributeMap map[string]listenerAttributeInfo + +var listenerAttributes = listenerAttributeMap(map[string]listenerAttributeInfo{ + "tcp_idle_timeout_seconds": { + // Attribute only supported on TCP and GENEVE listeners + apiAttributeKey: "tcp.idle_timeout.seconds", + listenerTypesSupported: []awstypes.ProtocolEnum{awstypes.ProtocolEnumTcp, awstypes.ProtocolEnumGeneve}, + tfType: schema.TypeInt, + }, +}) + +func (m listenerAttributeMap) expand(d *schema.ResourceData, listenerType awstypes.ProtocolEnum, update bool) []awstypes.ListenerAttribute { + var attributes []awstypes.ListenerAttribute + + for tfAttributeName, attributeInfo := range m { + if update && !d.HasChange(tfAttributeName) { + continue + } + + // Not all attributes are supported on all listener types + if !slices.Contains(attributeInfo.listenerTypesSupported, listenerType) { + continue + } + + if attributeInfo.tfType == schema.TypeInt { + v := (d.Get(tfAttributeName)).(int) + attributes = append(attributes, awstypes.ListenerAttribute{ + Key: aws.String(attributeInfo.apiAttributeKey), + Value: flex.IntValueToString(v), + }) + } + } + + return attributes +} + +func (m listenerAttributeMap) flatten(d *schema.ResourceData, apiObjects []awstypes.ListenerAttribute) { + for tfAttributeName, attributeInfo := range m { + k := attributeInfo.apiAttributeKey + i := slices.IndexFunc(apiObjects, func(v awstypes.ListenerAttribute) bool { + return aws.ToString(v.Key) == k + }) + + if i == -1 { + continue + } + + d.Set(tfAttributeName, flex.StringToIntValue(apiObjects[i].Value)) + } +} + func retryListenerCreate(ctx context.Context, conn *elasticloadbalancingv2.Client, input *elasticloadbalancingv2.CreateListenerInput, timeout time.Duration) (*elasticloadbalancingv2.CreateListenerOutput, error) { outputRaw, err := tfresource.RetryWhenIsA[*awstypes.CertificateNotFoundException](ctx, timeout, func() (interface{}, error) { return conn.CreateListener(ctx, input) diff --git a/internal/service/elbv2/listener_test.go b/internal/service/elbv2/listener_test.go index 0f828b52a2f..3d52e74e400 100644 --- a/internal/service/elbv2/listener_test.go +++ b/internal/service/elbv2/listener_test.go @@ -70,6 +70,7 @@ func TestAccELBV2Listener_Application_basic(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{ "default_action.0.forward", + "tcp_idle_timeout_seconds", }, }, }, @@ -1005,6 +1006,100 @@ func TestAccELBV2Listener_Forward_ignoreFields(t *testing.T) { }) } +func TestAccELBV2Listener_attributes_gwlb_TCPIdleTimeoutSeconds(t *testing.T) { + ctx := acctest.Context(t) + var conf awstypes.Listener + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + lbResourceName := "aws_lb.test" + resourceName := "aws_lb_listener.test" + tcpTimeout1 := 60 + // tcpTimeout2 := 6000 + + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ELBV2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckListenerDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccListenerConfig_attributes_gwlbTCPIdleTimeoutSeconds(rName, tcpTimeout1), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckListenerExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttrPair(resourceName, "load_balancer_arn", lbResourceName, names.AttrARN), + resource.TestCheckResourceAttr(resourceName, names.AttrProtocol, ""), + resource.TestCheckResourceAttr(resourceName, names.AttrPort, acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "tcp_idle_timeout_seconds", "60"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "default_action.0.forward", + }, + }, + }, + }) +} + +func TestAccELBV2Listener_attributes_nlb_TCPIdleTimeoutSeconds(t *testing.T) { + ctx := acctest.Context(t) + var conf awstypes.Listener + resourceName := "aws_lb_listener.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ELBV2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckListenerDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccListenerConfig_attributes_nlbTCPIdleTimeoutSeconds(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckListenerExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr("aws_lb.test", "load_balancer_type", "network"), + resource.TestCheckResourceAttrPair(resourceName, "load_balancer_arn", "aws_lb.test", names.AttrARN), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "elasticloadbalancing", regexache.MustCompile("listener/.+$")), + resource.TestCheckNoResourceAttr(resourceName, "alpn_policy"), + resource.TestCheckNoResourceAttr(resourceName, names.AttrCertificateARN), + resource.TestCheckResourceAttr(resourceName, "default_action.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "default_action.0.order", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "default_action.0.type", "forward"), + resource.TestCheckResourceAttrPair(resourceName, "default_action.0.target_group_arn", "aws_lb_target_group.test", names.AttrARN), + resource.TestCheckResourceAttr(resourceName, "default_action.0.authenticate_cognito.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "default_action.0.authenticate_oidc.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "default_action.0.fixed_response.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "default_action.0.forward.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "default_action.0.redirect.#", acctest.Ct0), + resource.TestCheckResourceAttrPair(resourceName, names.AttrID, resourceName, names.AttrARN), + resource.TestCheckResourceAttr(resourceName, "mutual_authentication.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, names.AttrProtocol, "TCP"), + resource.TestCheckResourceAttr(resourceName, names.AttrPort, "80"), + resource.TestCheckResourceAttr(resourceName, "ssl_policy", ""), + resource.TestCheckResourceAttr(resourceName, "tcp_idle_timeout_seconds", "60"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsAllPercent, acctest.Ct0), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "default_action.0.forward", + }, + }, + }, + }) +} + func TestAccELBV2Listener_Protocol_upd(t *testing.T) { ctx := acctest.Context(t) var conf awstypes.Listener @@ -2809,6 +2904,117 @@ resource "aws_iam_server_certificate" "test" { `, rName, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key))) } +func testAccListenerConfig_attributes_gwlbTCPIdleTimeoutSeconds(rName string, seconds int) string { + return acctest.ConfigCompose( + acctest.ConfigAvailableAZsNoOptIn(), + fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.10.10.0/25" + + tags = { + Name = %[1]q + } +} + +resource "aws_subnet" "test" { + availability_zone = data.aws_availability_zones.available.names[0] + cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 2, 0) + vpc_id = aws_vpc.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_lb" "test" { + load_balancer_type = "gateway" + name = %[1]q + + subnet_mapping { + subnet_id = aws_subnet.test.id + } +} + +resource "aws_lb_target_group" "test" { + name = %[1]q + port = 6081 + protocol = "GENEVE" + vpc_id = aws_vpc.test.id + + health_check { + port = 80 + protocol = "HTTP" + } +} + +resource "aws_lb_listener" "test" { + load_balancer_arn = aws_lb.test.id + + default_action { + target_group_arn = aws_lb_target_group.test.id + type = "forward" + } + + tags = { + Name = %[1]q + } + + tcp_idle_timeout_seconds = %[2]d +} +`, rName, seconds)) +} + +func testAccListenerConfig_attributes_nlbTCPIdleTimeoutSeconds(rName string) string { + return acctest.ConfigCompose( + testAccListenerConfig_base(rName), fmt.Sprintf(` +resource "aws_lb_listener" "test" { + load_balancer_arn = aws_lb.test.id + protocol = "TCP" + port = "80" + + default_action { + target_group_arn = aws_lb_target_group.test.id + type = "forward" + } + tcp_idle_timeout_seconds = 60 +} + +resource "aws_lb" "test" { + name = %[1]q + load_balancer_type = "network" + internal = true + security_groups = [aws_security_group.test.id] + subnets = aws_subnet.test[*].id + + idle_timeout = 30 + enable_deletion_protection = false + + tags = { + Name = %[1]q + } +} + +resource "aws_lb_target_group" "test" { + name = %[1]q + port = 8080 + protocol = "TCP" + vpc_id = aws_vpc.test.id + + health_check { + interval = 10 + port = 8081 + protocol = "TCP" + healthy_threshold = 3 + unhealthy_threshold = 3 + } + + tags = { + Name = %[1]q + } +} +`, rName)) +} + func testAccListenerConfig_Forward_changeWeightedToBasic(rName, rName2 string) string { return acctest.ConfigCompose(testAccListenerConfig_base(rName), fmt.Sprintf(` resource "aws_lb_listener" "test" { diff --git a/internal/service/elbv2/load_balancer.go b/internal/service/elbv2/load_balancer.go index 95f7a8dfe99..51f0199af57 100644 --- a/internal/service/elbv2/load_balancer.go +++ b/internal/service/elbv2/load_balancer.go @@ -201,6 +201,12 @@ func resourceLoadBalancer() *schema.Resource { Default: false, DiffSuppressFunc: suppressIfLBTypeNot(awstypes.LoadBalancerTypeEnumApplication), }, + "enable_zonal_shift": { + Type: schema.TypeBool, + Optional: true, + Default: false, + DiffSuppressFunc: suppressIfLBTypeNot(awstypes.LoadBalancerTypeEnumNetwork), + }, "enforce_security_group_inbound_rules_on_private_link_traffic": { Type: schema.TypeString, Optional: true, @@ -761,6 +767,11 @@ var loadBalancerAttributes = loadBalancerAttributeMap(map[string]loadBalancerAtt tfType: schema.TypeBool, loadBalancerTypesSupported: []awstypes.LoadBalancerTypeEnum{awstypes.LoadBalancerTypeEnumApplication}, }, + "enable_zonal_shift": { + apiAttributeKey: loadBalancerAttributeZonalShiftConfigEnabled, + tfType: schema.TypeBool, + loadBalancerTypesSupported: []awstypes.LoadBalancerTypeEnum{awstypes.LoadBalancerTypeEnumNetwork}, + }, "idle_timeout": { apiAttributeKey: loadBalancerAttributeIdleTimeoutTimeoutSeconds, tfType: schema.TypeInt, diff --git a/internal/service/elbv2/load_balancer_data_source.go b/internal/service/elbv2/load_balancer_data_source.go index 9f3b5bac88f..72ab32ad679 100644 --- a/internal/service/elbv2/load_balancer_data_source.go +++ b/internal/service/elbv2/load_balancer_data_source.go @@ -130,6 +130,10 @@ func dataSourceLoadBalancer() *schema.Resource { Type: schema.TypeBool, Computed: true, }, + "enable_zonal_shift": { + Type: schema.TypeBool, + Computed: true, + }, "enforce_security_group_inbound_rules_on_private_link_traffic": { Type: schema.TypeString, Computed: true, diff --git a/internal/service/elbv2/load_balancer_data_source_test.go b/internal/service/elbv2/load_balancer_data_source_test.go index 8f74d473772..501fede0519 100644 --- a/internal/service/elbv2/load_balancer_data_source_test.go +++ b/internal/service/elbv2/load_balancer_data_source_test.go @@ -46,6 +46,7 @@ func TestAccELBV2LoadBalancerDataSource_basic(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceName, "subnet_mapping.#", resourceName, "subnet_mapping.#"), resource.TestCheckResourceAttrPair(dataSourceName, "desync_mitigation_mode", resourceName, "desync_mitigation_mode"), resource.TestCheckResourceAttrPair(dataSourceName, "enforce_security_group_inbound_rules_on_private_link_traffic", resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic"), + resource.TestCheckResourceAttrPair(dataSourceName, "enable_zonal_shift", resourceName, "enable_zonal_shift"), resource.TestCheckResourceAttrPair(dataSourceName2, names.AttrName, resourceName, names.AttrName), resource.TestCheckResourceAttrPair(dataSourceName2, "internal", resourceName, "internal"), resource.TestCheckResourceAttrPair(dataSourceName2, "subnets.#", resourceName, "subnets.#"), @@ -63,6 +64,7 @@ func TestAccELBV2LoadBalancerDataSource_basic(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceName2, "subnet_mapping.#", resourceName, "subnet_mapping.#"), resource.TestCheckResourceAttrPair(dataSourceName2, "desync_mitigation_mode", resourceName, "desync_mitigation_mode"), resource.TestCheckResourceAttrPair(dataSourceName2, "enforce_security_group_inbound_rules_on_private_link_traffic", resourceName, "enforce_security_group_inbound_rules_on_private_link_traffic"), + resource.TestCheckResourceAttrPair(dataSourceName2, "enable_zonal_shift", resourceName, "enable_zonal_shift"), resource.TestCheckResourceAttrPair(dataSourceName3, names.AttrName, resourceName, names.AttrName), resource.TestCheckResourceAttrPair(dataSourceName3, "internal", resourceName, "internal"), resource.TestCheckResourceAttrPair(dataSourceName3, "subnets.#", resourceName, "subnets.#"), @@ -83,6 +85,7 @@ func TestAccELBV2LoadBalancerDataSource_basic(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceName3, "enable_tls_version_and_cipher_suite_headers", resourceName, "enable_tls_version_and_cipher_suite_headers"), resource.TestCheckResourceAttrPair(dataSourceName3, "enable_xff_client_port", resourceName, "enable_xff_client_port"), resource.TestCheckResourceAttrPair(dataSourceName3, "xff_header_processing_mode", resourceName, "xff_header_processing_mode"), + resource.TestCheckResourceAttrPair(dataSourceName3, "enable_zonal_shift", resourceName, "enable_zonal_shift"), ), }, }, diff --git a/internal/service/elbv2/load_balancer_test.go b/internal/service/elbv2/load_balancer_test.go index 6046d24ea8d..4fb4935a48a 100644 --- a/internal/service/elbv2/load_balancer_test.go +++ b/internal/service/elbv2/load_balancer_test.go @@ -95,6 +95,7 @@ func TestAccELBV2LoadBalancer_ALB_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "enable_deletion_protection", acctest.CtFalse), resource.TestCheckResourceAttr(resourceName, "enable_tls_version_and_cipher_suite_headers", acctest.CtFalse), resource.TestCheckResourceAttr(resourceName, "enable_xff_client_port", acctest.CtFalse), + resource.TestCheckNoResourceAttr(resourceName, "enable_zonal_shift"), resource.TestCheckResourceAttr(resourceName, "idle_timeout", "30"), resource.TestCheckResourceAttr(resourceName, "internal", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, names.AttrIPAddressType, "ipv4"), @@ -136,6 +137,7 @@ func TestAccELBV2LoadBalancer_NLB_basic(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, names.AttrDNSName), resource.TestCheckResourceAttr(resourceName, "dns_record_client_routing_policy", "any_availability_zone"), resource.TestCheckResourceAttr(resourceName, "enable_deletion_protection", acctest.CtFalse), + resource.TestCheckResourceAttr(resourceName, "enable_zonal_shift", acctest.CtFalse), resource.TestCheckResourceAttr(resourceName, "internal", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, names.AttrIPAddressType, "ipv4"), resource.TestCheckResourceAttr(resourceName, "load_balancer_type", "network"), @@ -604,6 +606,48 @@ func TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateCrossZone(t *testing.T) }) } +func TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateZonalShift(t *testing.T) { + ctx := acctest.Context(t) + var pre, mid, post awstypes.LoadBalancer + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lb.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ELBV2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckLoadBalancerDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccLoadBalancerConfig_nlbZonalShift(rName, true), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLoadBalancerExists(ctx, resourceName, &pre), + testAccCheckLoadBalancerAttribute(ctx, resourceName, "zonal_shift.config.enabled", acctest.CtTrue), + resource.TestCheckResourceAttr(resourceName, "enable_zonal_shift", acctest.CtTrue), + ), + }, + { + Config: testAccLoadBalancerConfig_nlbZonalShift(rName, false), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLoadBalancerExists(ctx, resourceName, &mid), + testAccCheckLoadBalancerAttribute(ctx, resourceName, "zonal_shift.config.enabled", acctest.CtFalse), + resource.TestCheckResourceAttr(resourceName, "enable_zonal_shift", acctest.CtFalse), + testAccCheckLoadBalancerNotRecreated(&pre, &mid), + ), + }, + { + Config: testAccLoadBalancerConfig_nlbZonalShift(rName, true), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLoadBalancerExists(ctx, resourceName, &post), + testAccCheckLoadBalancerAttribute(ctx, resourceName, "zonal_shift.config.enabled", acctest.CtTrue), + resource.TestCheckResourceAttr(resourceName, "enable_zonal_shift", acctest.CtTrue), + testAccCheckLoadBalancerNotRecreated(&mid, &post), + ), + }, + }, + }) +} + func TestAccELBV2LoadBalancer_ApplicationLoadBalancer_updateHTTP2(t *testing.T) { ctx := acctest.Context(t) var pre, mid, post awstypes.LoadBalancer @@ -949,7 +993,7 @@ func TestAccELBV2LoadBalancer_ApplicationLoadBalancer_addSubnet(t *testing.T) { CheckDestroy: testAccCheckLoadBalancerDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccLoadBalancerConfig_subnetCount(rName, 2), + Config: testAccLoadBalancerConfig_subnetCount(rName, 3, 2), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &pre), resource.TestCheckResourceAttr(resourceName, "subnet_mapping.#", acctest.Ct2), @@ -957,7 +1001,7 @@ func TestAccELBV2LoadBalancer_ApplicationLoadBalancer_addSubnet(t *testing.T) { ), }, { - Config: testAccLoadBalancerConfig_subnetCount(rName, 3), + Config: testAccLoadBalancerConfig_subnetCount(rName, 3, 3), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &post), testAccCheckLoadBalancerNotRecreated(&pre, &post), @@ -986,7 +1030,7 @@ func TestAccELBV2LoadBalancer_ApplicationLoadBalancer_deleteSubnet(t *testing.T) CheckDestroy: testAccCheckLoadBalancerDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccLoadBalancerConfig_subnetCount(rName, 3), + Config: testAccLoadBalancerConfig_subnetCount(rName, 3, 3), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &pre), resource.TestCheckResourceAttr(resourceName, "subnet_mapping.#", acctest.Ct3), @@ -994,7 +1038,7 @@ func TestAccELBV2LoadBalancer_ApplicationLoadBalancer_deleteSubnet(t *testing.T) ), }, { - Config: testAccLoadBalancerConfig_subnetCount(rName, 2), + Config: testAccLoadBalancerConfig_subnetCount(rName, 3, 2), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &post), testAccCheckLoadBalancerNotRecreated(&pre, &post), @@ -1778,7 +1822,7 @@ func TestAccELBV2LoadBalancer_NetworkLoadBalancer_addSubnetMapping(t *testing.T) CheckDestroy: testAccCheckLoadBalancerDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, false, 2), + Config: testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, false, false, 2), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &pre), resource.TestCheckResourceAttr(resourceName, "subnet_mapping.#", acctest.Ct2), @@ -1786,7 +1830,7 @@ func TestAccELBV2LoadBalancer_NetworkLoadBalancer_addSubnetMapping(t *testing.T) ), }, { - Config: testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, false, 3), + Config: testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, false, false, 3), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &post), testAccCheckLoadBalancerNotRecreated(&pre, &post), @@ -1815,7 +1859,7 @@ func TestAccELBV2LoadBalancer_NetworkLoadBalancer_deleteSubnetMapping(t *testing CheckDestroy: testAccCheckLoadBalancerDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, false, 3), + Config: testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, false, false, 3), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &pre), resource.TestCheckResourceAttr(resourceName, "subnet_mapping.#", acctest.Ct3), @@ -1823,7 +1867,7 @@ func TestAccELBV2LoadBalancer_NetworkLoadBalancer_deleteSubnetMapping(t *testing ), }, { - Config: testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, false, 2), + Config: testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, false, false, 2), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &post), testAccCheckLoadBalancerRecreated(&pre, &post), @@ -2168,21 +2212,21 @@ resource "aws_security_group" "test" { } func testAccLoadBalancerConfig_basic(rName string) string { - return testAccLoadBalancerConfig_subnetCount(rName, 2) + return testAccLoadBalancerConfig_subnetCount(rName, 2, 2) } -func testAccLoadBalancerConfig_subnetCount(rName string, subnetCount int) string { - return acctest.ConfigCompose(testAccLoadBalancerConfig_baseInternal(rName, subnetCount), fmt.Sprintf(` +func testAccLoadBalancerConfig_subnetCount(rName string, nSubnets, nSubnetsReferenced int) string { + return acctest.ConfigCompose(testAccLoadBalancerConfig_baseInternal(rName, nSubnets), fmt.Sprintf(` resource "aws_lb" "test" { name = %[1]q internal = true security_groups = [aws_security_group.test.id] - subnets = aws_subnet.test[*].id + subnets = slice(aws_subnet.test[*].id, 0, %[2]d) idle_timeout = 30 enable_deletion_protection = false } -`, rName)) +`, rName, nSubnetsReferenced)) } func testAccLoadBalancerConfig_subnetMappingCount(rName string, subnetCount int) string { @@ -2547,14 +2591,18 @@ resource "aws_lb" "test" { } func testAccLoadBalancerConfig_nlbBasic(rName string) string { - return testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, false, 1) + return testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, false, false, 1) } func testAccLoadBalancerConfig_nlbCrossZone(rName string, cz bool) string { - return testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, cz, 1) + return testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, cz, false, 1) +} + +func testAccLoadBalancerConfig_nlbZonalShift(rName string, zs bool) string { + return testAccLoadBalancerConfig_nlbSubnetMappingCount(rName, true, zs, 1) } -func testAccLoadBalancerConfig_nlbSubnetMappingCount(rName string, cz bool, subnetCount int) string { +func testAccLoadBalancerConfig_nlbSubnetMappingCount(rName string, cz, zs bool, subnetCount int) string { return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, subnetCount), fmt.Sprintf(` resource "aws_lb" "test" { name = %[1]q @@ -2563,6 +2611,7 @@ resource "aws_lb" "test" { enable_deletion_protection = false enable_cross_zone_load_balancing = %[2]t + enable_zonal_shift = %[3]t dynamic "subnet_mapping" { for_each = aws_subnet.test[*] @@ -2575,7 +2624,7 @@ resource "aws_lb" "test" { Name = %[1]q } } -`, rName, cz)) +`, rName, cz, zs)) } func testAccLoadBalancerConfig_typeGateway(rName string) string { diff --git a/internal/service/iam/policy_test.go b/internal/service/iam/policy_test.go index 93f3e091d74..4af018f9cde 100644 --- a/internal/service/iam/policy_test.go +++ b/internal/service/iam/policy_test.go @@ -349,6 +349,57 @@ func TestAccIAMPolicy_policyDuplicateKeys(t *testing.T) { }) } +// TestAccIAMPolicy_malformedCondition verifies that malformed policy content +// that is stored in state does not prevent subsequent plan and apply operations +// from proceeding. +// +// Ref: https://github.com/hashicorp/terraform-provider-aws/issues/39833 +func TestAccIAMPolicy_malformedCondition(t *testing.T) { + ctx := acctest.Context(t) + var out awstypes.Policy + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_policy.test" + expectedPolicyText1 := `{"Statement":[{"Action":["s3:ListBucket"],"Effect":"Allow","Resource":"*"}],"Version":"2012-10-17"}` + expectedPolicyText2 := `{"Statement":[{"Action":["s3:ListBucket"],"Condition":{"StringLike":["demo-prefix/"]}",Effect":"Allow","Resource":"*"}],"Version":"2012-10-17"}` + expectedPolicyText3 := `{"Statement":[{"Action":["s3:ListBucket"],"Condition":{"StringLike":{"s3:prefix":["demo-prefix/"]}},"Effect":"Allow","Resource":"*"}],"Version":"2012-10-17"}` + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.IAMServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPolicyConfig_MalformedCondition_setup(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &out), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + resource.TestCheckResourceAttr(resourceName, names.AttrPolicy, expectedPolicyText1), + ), + }, + { + Config: testAccPolicyConfig_MalformedCondition_failure(rName), + ExpectError: regexache.MustCompile(`MalformedPolicyDocument: Syntax errors in policy`), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &out), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + // Because this is a Plugin SDK V2-based resource, the malformed content + // is stored in state despite the failed update. + resource.TestCheckResourceAttr(resourceName, names.AttrPolicy, expectedPolicyText2), + ), + }, + { + Config: testAccPolicyConfig_MalformedCondition_fix(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &out), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + resource.TestCheckResourceAttr(resourceName, names.AttrPolicy, expectedPolicyText3), + ), + }, + }, + }) +} + func testAccCheckPolicyExists(ctx context.Context, n string, v *awstypes.Policy) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -624,3 +675,74 @@ EOF } `, rName) } + +func testAccPolicyConfig_MalformedCondition_setup(rName string) string { + return fmt.Sprintf(` +resource "aws_iam_policy" "test" { + name = %q + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Resource = "*" + Action = [ + "s3:ListBucket", + ] + }, + ] + }) +} +`, rName) +} + +func testAccPolicyConfig_MalformedCondition_failure(rName string) string { + return fmt.Sprintf(` +resource "aws_iam_policy" "test" { + name = %q + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Resource = "*" + Action = [ + "s3:ListBucket", + ] + "Condition" : { + "StringLike" : ["demo-prefix/"] + } + }, + ] + }) +} +`, rName) +} + +func testAccPolicyConfig_MalformedCondition_fix(rName string) string { + return fmt.Sprintf(` +resource "aws_iam_policy" "test" { + name = %q + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Resource = "*" + Action = [ + "s3:ListBucket", + ] + "Condition" : { + "StringLike" : { + "s3:prefix" : ["demo-prefix/"] + } + } + }, + ] + }) +} +`, rName) +} diff --git a/internal/service/kms/exports.go b/internal/service/kms/exports.go index cd4d4c22f1e..dc49d80335a 100644 --- a/internal/service/kms/exports.go +++ b/internal/service/kms/exports.go @@ -9,7 +9,6 @@ var ( DiffSuppressKeyOrAlias = diffSuppressKeyOrAlias FindAliasByName = findAliasByName FindDefaultKeyARNForService = findDefaultKeyARNForService - FindKeyByID = findKeyByID ValidateKey = validateKey ValidateKeyOrAlias = validateKeyOrAlias ) diff --git a/internal/service/kms/exports_test.go b/internal/service/kms/exports_test.go index 88bff98afcf..084d50094c4 100644 --- a/internal/service/kms/exports_test.go +++ b/internal/service/kms/exports_test.go @@ -19,6 +19,7 @@ var ( AliasNamePrefix = aliasNamePrefix FindCustomKeyStoreByID = findCustomKeyStoreByID FindGrantByTwoPartKey = findGrantByTwoPartKey + FindKeyByID = findKeyByID FindKeyPolicyByTwoPartKey = findKeyPolicyByTwoPartKey GrantParseResourceID = grantParseResourceID KeyARNOrIDEqual = keyARNOrIDEqual diff --git a/internal/service/lakeformation/data_lake_settings.go b/internal/service/lakeformation/data_lake_settings.go index b0653e285b8..4db40512958 100644 --- a/internal/service/lakeformation/data_lake_settings.go +++ b/internal/service/lakeformation/data_lake_settings.go @@ -39,6 +39,17 @@ func ResourceDataLakeSettings() *schema.Resource { }, Schema: map[string]*schema.Schema{ + // admins + // allow_external_data_filtering + // allow_full_table_external_data_access + // authorized_session_tag_value_list + // catalog_id + // create_database_default_permissions + // create_table_default_permissions + // external_data_filtering_allow_list + // parameters + // read_only_admins + // trusted_resource_owners "admins": { Type: schema.TypeSet, Computed: true, @@ -48,15 +59,6 @@ func ResourceDataLakeSettings() *schema.Resource { ValidateFunc: verify.ValidARN, }, }, - "read_only_admins": { - Type: schema.TypeSet, - Computed: true, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: verify.ValidARN, - }, - }, "allow_external_data_filtering": { Type: schema.TypeBool, Optional: true, @@ -135,6 +137,32 @@ func ResourceDataLakeSettings() *schema.Resource { ValidateFunc: validPrincipal, }, }, + names.AttrParameters: { + Type: schema.TypeMap, + Computed: true, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + // In fresh account, with empty config, API returns map[CROSS_ACCOUNT_VERSION:1 SET_CONTEXT:TRUE] by default + if k == "parameters.SET_CONTEXT" && old == "TRUE" && new == "" { + return true + } + if k == "parameters.CROSS_ACCOUNT_VERSION" && old == "1" && new == "" { + return true + } + + return old == new + }, + }, + "read_only_admins": { + Type: schema.TypeSet, + Computed: true, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: verify.ValidARN, + }, + }, "trusted_resource_owners": { Type: schema.TypeList, Computed: true, @@ -164,14 +192,14 @@ func resourceDataLakeSettingsCreate(ctx context.Context, d *schema.ResourceData, settings.DataLakeAdmins = expandDataLakeSettingsAdmins(v.(*schema.Set)) } - if v, ok := d.GetOk("read_only_admins"); ok { - settings.ReadOnlyAdmins = expandDataLakeSettingsAdmins(v.(*schema.Set)) - } - if v, ok := d.GetOk("allow_external_data_filtering"); ok { settings.AllowExternalDataFiltering = aws.Bool(v.(bool)) } + if v, ok := d.GetOk("allow_full_table_external_data_access"); ok { + settings.AllowFullTableExternalDataAccess = aws.Bool(v.(bool)) + } + if v, ok := d.GetOk("authorized_session_tag_value_list"); ok { settings.AuthorizedSessionTagValueList = flex.ExpandStringValueList(v.([]interface{})) } @@ -188,12 +216,16 @@ func resourceDataLakeSettingsCreate(ctx context.Context, d *schema.ResourceData, settings.ExternalDataFilteringAllowList = expandDataLakeSettingsDataFilteringAllowList(v.(*schema.Set)) } - if v, ok := d.GetOk("trusted_resource_owners"); ok { - settings.TrustedResourceOwners = flex.ExpandStringValueList(v.([]interface{})) + if v, ok := d.GetOk(names.AttrParameters); ok { + settings.Parameters = flex.ExpandStringValueMap(v.(map[string]interface{})) } - if v, ok := d.GetOk("allow_full_table_external_data_access"); ok { - settings.AllowFullTableExternalDataAccess = aws.Bool(v.(bool)) + if v, ok := d.GetOk("read_only_admins"); ok { + settings.ReadOnlyAdmins = expandDataLakeSettingsAdmins(v.(*schema.Set)) + } + + if v, ok := d.GetOk("trusted_resource_owners"); ok { + settings.TrustedResourceOwners = flex.ExpandStringValueList(v.([]interface{})) } input.DataLakeSettings = settings @@ -262,14 +294,15 @@ func resourceDataLakeSettingsRead(ctx context.Context, d *schema.ResourceData, m settings := output.DataLakeSettings d.Set("admins", flattenDataLakeSettingsAdmins(settings.DataLakeAdmins)) - d.Set("read_only_admins", flattenDataLakeSettingsAdmins(settings.ReadOnlyAdmins)) d.Set("allow_external_data_filtering", settings.AllowExternalDataFiltering) + d.Set("allow_full_table_external_data_access", settings.AllowFullTableExternalDataAccess) d.Set("authorized_session_tag_value_list", flex.FlattenStringValueList(settings.AuthorizedSessionTagValueList)) d.Set("create_database_default_permissions", flattenDataLakeSettingsCreateDefaultPermissions(settings.CreateDatabaseDefaultPermissions)) d.Set("create_table_default_permissions", flattenDataLakeSettingsCreateDefaultPermissions(settings.CreateTableDefaultPermissions)) d.Set("external_data_filtering_allow_list", flattenDataLakeSettingsDataFilteringAllowList(settings.ExternalDataFilteringAllowList)) + d.Set(names.AttrParameters, flex.FlattenStringValueMap(settings.Parameters)) + d.Set("read_only_admins", flattenDataLakeSettingsAdmins(settings.ReadOnlyAdmins)) d.Set("trusted_resource_owners", flex.FlattenStringValueList(settings.TrustedResourceOwners)) - d.Set("allow_full_table_external_data_access", settings.AllowFullTableExternalDataAccess) return diags } @@ -284,7 +317,11 @@ func resourceDataLakeSettingsDelete(ctx context.Context, d *schema.ResourceData, CreateTableDefaultPermissions: make([]awstypes.PrincipalPermissions, 0), DataLakeAdmins: make([]awstypes.DataLakePrincipal, 0), ReadOnlyAdmins: make([]awstypes.DataLakePrincipal, 0), - TrustedResourceOwners: make([]string, 0), + Parameters: map[string]string{ // In fresh account, with empty config, API returns map[CROSS_ACCOUNT_VERSION:1 SET_CONTEXT:TRUE] by default + "CROSS_ACCOUNT_VERSION": "1", + "SET_CONTEXT": "TRUE", + }, + TrustedResourceOwners: make([]string, 0), }, } diff --git a/internal/service/lakeformation/data_lake_settings_data_source.go b/internal/service/lakeformation/data_lake_settings_data_source.go index 2ca622ab6db..e620c215f24 100644 --- a/internal/service/lakeformation/data_lake_settings_data_source.go +++ b/internal/service/lakeformation/data_lake_settings_data_source.go @@ -32,11 +32,6 @@ func DataSourceDataLakeSettings() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "read_only_admins": { - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, "allow_external_data_filtering": { Type: schema.TypeBool, Computed: true, @@ -93,6 +88,16 @@ func DataSourceDataLakeSettings() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + names.AttrParameters: { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "read_only_admins": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, "trusted_resource_owners": { Type: schema.TypeList, Computed: true, @@ -132,14 +137,15 @@ func dataSourceDataLakeSettingsRead(ctx context.Context, d *schema.ResourceData, settings := output.DataLakeSettings d.Set("admins", flattenDataLakeSettingsAdmins(settings.DataLakeAdmins)) - d.Set("read_only_admins", flattenDataLakeSettingsAdmins(settings.ReadOnlyAdmins)) d.Set("allow_external_data_filtering", settings.AllowExternalDataFiltering) + d.Set("allow_full_table_external_data_access", settings.AllowFullTableExternalDataAccess) d.Set("authorized_session_tag_value_list", flex.FlattenStringValueList(settings.AuthorizedSessionTagValueList)) d.Set("create_database_default_permissions", flattenDataLakeSettingsCreateDefaultPermissions(settings.CreateDatabaseDefaultPermissions)) d.Set("create_table_default_permissions", flattenDataLakeSettingsCreateDefaultPermissions(settings.CreateTableDefaultPermissions)) d.Set("external_data_filtering_allow_list", flattenDataLakeSettingsDataFilteringAllowList(settings.ExternalDataFilteringAllowList)) + d.Set(names.AttrParameters, flex.FlattenStringValueMap(settings.Parameters)) + d.Set("read_only_admins", flattenDataLakeSettingsAdmins(settings.ReadOnlyAdmins)) d.Set("trusted_resource_owners", flex.FlattenStringyValueList(settings.TrustedResourceOwners)) - d.Set("allow_full_table_external_data_access", settings.AllowFullTableExternalDataAccess) return diags } diff --git a/internal/service/lakeformation/data_lake_settings_test.go b/internal/service/lakeformation/data_lake_settings_test.go index 19bf9736b66..fcce850363f 100644 --- a/internal/service/lakeformation/data_lake_settings_test.go +++ b/internal/service/lakeformation/data_lake_settings_test.go @@ -123,6 +123,53 @@ func testAccDataLakeSettings_readOnlyAdmins(t *testing.T) { }) } +func testAccDataLakeSettings_parameters(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_lakeformation_data_lake_settings.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.LakeFormationServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDataLakeSettingsDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDataLakeSettingsConfig_parametersEmpty(), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataLakeSettingsExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "parameters.%", acctest.Ct2), // In fresh account, with empty config, API returns map[CROSS_ACCOUNT_VERSION:1 SET_CONTEXT:TRUE] + resource.TestCheckResourceAttr(resourceName, "parameters.SET_CONTEXT", acctest.CtTrueCaps), + resource.TestCheckResourceAttr(resourceName, "parameters.CROSS_ACCOUNT_VERSION", acctest.Ct1), + ), + }, + { + Config: testAccDataLakeSettingsConfig_parameters("CROSS_ACCOUNT_VERSION", acctest.Ct3), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataLakeSettingsExists(ctx, resourceName), + // resource.TestCheckResourceAttr(resourceName, "parameters.%", acctest.Ct2), // this is 2 because SET_CONTEXT:TRUE is here but it's not important for the test and if AWS changes things and adds more parameters, this test would fail + resource.TestCheckResourceAttr(resourceName, "parameters.CROSS_ACCOUNT_VERSION", acctest.Ct3), + ), + }, + { + Config: testAccDataLakeSettingsConfig_parameters("CROSS_ACCOUNT_VERSION", acctest.Ct1), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataLakeSettingsExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "parameters.CROSS_ACCOUNT_VERSION", acctest.Ct1), + ), + }, + { + Config: testAccDataLakeSettingsConfig_parametersEmpty(), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataLakeSettingsExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "parameters.%", acctest.Ct2), // In fresh account, with empty config, API returns map[CROSS_ACCOUNT_VERSION:1 SET_CONTEXT:TRUE] + resource.TestCheckResourceAttr(resourceName, "parameters.SET_CONTEXT", acctest.CtTrueCaps), + resource.TestCheckResourceAttr(resourceName, "parameters.CROSS_ACCOUNT_VERSION", acctest.Ct1), + ), + }, + }, + }) +} + func testAccCheckDataLakeSettingsDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).LakeFormationClient(ctx) @@ -240,3 +287,30 @@ resource "aws_lakeformation_data_lake_settings" "test" { read_only_admins = [data.aws_iam_session_context.current.issuer_arn] } ` + +func testAccDataLakeSettingsConfig_parametersEmpty() string { + return ` +data "aws_caller_identity" "current" {} + +resource "aws_lakeformation_data_lake_settings" "test" { + catalog_id = data.aws_caller_identity.current.account_id + + parameters = { + } +} +` +} + +func testAccDataLakeSettingsConfig_parameters(key1, value1 string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +resource "aws_lakeformation_data_lake_settings" "test" { + catalog_id = data.aws_caller_identity.current.account_id + + parameters = { + %[1]q = %[2]q + } +} +`, key1, value1) +} diff --git a/internal/service/lakeformation/lakeformation_test.go b/internal/service/lakeformation/lakeformation_test.go index b0dcae89ef8..772555f9dca 100644 --- a/internal/service/lakeformation/lakeformation_test.go +++ b/internal/service/lakeformation/lakeformation_test.go @@ -18,6 +18,7 @@ func TestAccLakeFormation_serial(t *testing.T) { acctest.CtDisappears: testAccDataLakeSettings_disappears, "withoutCatalogId": testAccDataLakeSettings_withoutCatalogID, "readOnlyAdmins": testAccDataLakeSettings_readOnlyAdmins, + "parameters": testAccDataLakeSettings_parameters, }, "DataCellsFilter": { acctest.CtBasic: testAccDataCellsFilter_basic, diff --git a/internal/service/resiliencehub/generate.go b/internal/service/resiliencehub/generate.go index fe43e1481b6..5699dd6a0f8 100644 --- a/internal/service/resiliencehub/generate.go +++ b/internal/service/resiliencehub/generate.go @@ -3,6 +3,7 @@ //go:generate go run ../../generate/tags/main.go -AWSSDKVersion=2 -ServiceTagsMap -KVTValues -SkipTypesImp -ListTags -ListTagsInIDElem=ResourceArn -ListTagsOutTagsElem=Tags -TagOp=TagResource -TagInIDElem=ResourceArn -UntagOp=UntagResource -UpdateTags //go:generate go run ../../generate/servicepackage/main.go +//go:generate go run ../../generate/tagstests/main.go // ONLY generate directives and package declaration! Do not add anything else to this file. package resiliencehub diff --git a/internal/service/resiliencehub/resiliency_policy.go b/internal/service/resiliencehub/resiliency_policy.go index 5c7571d5019..9f349c81ffb 100644 --- a/internal/service/resiliencehub/resiliency_policy.go +++ b/internal/service/resiliencehub/resiliency_policy.go @@ -38,6 +38,8 @@ import ( // @FrameworkResource("aws_resiliencehub_resiliency_policy", name="Resiliency Policy") // @Tags(identifierAttribute="arn") +// @Testing(existsType="github.com/aws/aws-sdk-go-v2/service/resiliencehub;resiliencehub.DescribeResiliencyPolicyOutput") +// @Testing(importStateIdAttribute="arn") func newResourceResiliencyPolicy(_ context.Context) (resource.ResourceWithConfigure, error) { r := &resourceResiliencyPolicy{} diff --git a/internal/service/resiliencehub/resiliency_policy_tags_gen_test.go b/internal/service/resiliencehub/resiliency_policy_tags_gen_test.go new file mode 100644 index 00000000000..5eafbaa1bef --- /dev/null +++ b/internal/service/resiliencehub/resiliency_policy_tags_gen_test.go @@ -0,0 +1,2321 @@ +// Code generated by internal/generate/tagstests/main.go; DO NOT EDIT. + +package resiliencehub_test + +import ( + "testing" + + "github.com/aws/aws-sdk-go-v2/service/resiliencehub" + "github.com/hashicorp/terraform-plugin-testing/config" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccResilienceHubResiliencyPolicy_tags(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1Updated), + acctest.CtKey2: config.StringVariable(acctest.CtValue2), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1Updated), + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1Updated), + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1Updated), + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1Updated), + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1Updated), + acctest.CtKey2: config.StringVariable(acctest.CtValue2), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey2: config.StringVariable(acctest.CtValue2), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey2: config.StringVariable(acctest.CtValue2), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: nil, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: nil, + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_null(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: nil, + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.Null(), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.Null(), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: nil, + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + ImportStateVerifyIgnore: []string{ + acctest.CtTagsKey1, // The canonical value returned by the AWS API is "" + }, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_EmptyMap(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{}), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{})), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{})), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{}), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + ImportStateVerifyIgnore: []string{ + acctest.CtTagsKey1, // The canonical value returned by the AWS API is "" + }, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_AddOnUpdate(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: nil, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_EmptyTag_OnCreate(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(""), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(""), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: nil, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: nil, + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_EmptyTag_OnUpdate_Add(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + acctest.CtKey2: config.StringVariable(""), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + acctest.CtKey2: knownvalue.StringExact(""), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + acctest.CtKey2: knownvalue.StringExact(""), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + acctest.CtKey2: knownvalue.StringExact(""), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + acctest.CtKey2: knownvalue.StringExact(""), + })), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + acctest.CtKey2: config.StringVariable(""), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_EmptyTag_OnUpdate_Replace(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(""), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + }, + }, + }, + { + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(""), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_DefaultTags_providerOnly(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + acctest.CtResourceTags: nil, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + acctest.CtResourceTags: nil, + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1Updated), + acctest.CtKey2: config.StringVariable(acctest.CtValue2), + }), + acctest.CtResourceTags: nil, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1Updated), + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1Updated), + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1Updated), + acctest.CtKey2: config.StringVariable(acctest.CtValue2), + }), + acctest.CtResourceTags: nil, + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey2: config.StringVariable(acctest.CtValue2), + }), + acctest.CtResourceTags: nil, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey2: config.StringVariable(acctest.CtValue2), + }), + acctest.CtResourceTags: nil, + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: nil, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: nil, + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_DefaultTags_nonOverlapping(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtProviderKey1: config.StringVariable(acctest.CtProviderValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtResourceKey1: config.StringVariable(acctest.CtResourceValue1), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtProviderKey1: knownvalue.StringExact(acctest.CtProviderValue1), + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtProviderKey1: knownvalue.StringExact(acctest.CtProviderValue1), + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtProviderKey1: config.StringVariable(acctest.CtProviderValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtResourceKey1: config.StringVariable(acctest.CtResourceValue1), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtProviderKey1: config.StringVariable(acctest.CtProviderValue1Updated), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtResourceKey1: config.StringVariable(acctest.CtResourceValue1Updated), + acctest.CtResourceKey2: config.StringVariable(acctest.CtResourceValue2), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Updated), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtProviderKey1: knownvalue.StringExact(acctest.CtProviderValue1Updated), + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Updated), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Updated), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtProviderKey1: knownvalue.StringExact(acctest.CtProviderValue1Updated), + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Updated), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtProviderKey1: config.StringVariable(acctest.CtProviderValue1Updated), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtResourceKey1: config.StringVariable(acctest.CtResourceValue1Updated), + acctest.CtResourceKey2: config.StringVariable(acctest.CtResourceValue2), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: nil, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: nil, + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_DefaultTags_overlapping(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtOverlapKey1: config.StringVariable(acctest.CtProviderValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtOverlapKey1: config.StringVariable(acctest.CtResourceValue1), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtOverlapKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtOverlapKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtOverlapKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtOverlapKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtOverlapKey1: config.StringVariable(acctest.CtProviderValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtOverlapKey1: config.StringVariable(acctest.CtResourceValue1), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtOverlapKey1: config.StringVariable(acctest.CtProviderValue1), + acctest.CtOverlapKey2: config.StringVariable("providervalue2"), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtOverlapKey1: config.StringVariable(acctest.CtResourceValue1), + acctest.CtOverlapKey2: config.StringVariable(acctest.CtResourceValue2), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtOverlapKey1: knownvalue.StringExact(acctest.CtResourceValue1), + acctest.CtOverlapKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtOverlapKey1: knownvalue.StringExact(acctest.CtResourceValue1), + acctest.CtOverlapKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtOverlapKey1: knownvalue.StringExact(acctest.CtResourceValue1), + acctest.CtOverlapKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtOverlapKey1: knownvalue.StringExact(acctest.CtResourceValue1), + acctest.CtOverlapKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtOverlapKey1: config.StringVariable(acctest.CtProviderValue1), + acctest.CtOverlapKey2: config.StringVariable("providervalue2"), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtOverlapKey1: config.StringVariable(acctest.CtResourceValue1), + acctest.CtOverlapKey2: config.StringVariable(acctest.CtResourceValue2), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtOverlapKey1: config.StringVariable(acctest.CtProviderValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtOverlapKey1: config.StringVariable(acctest.CtResourceValue2), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtOverlapKey1: knownvalue.StringExact(acctest.CtResourceValue2), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtOverlapKey1: knownvalue.StringExact(acctest.CtResourceValue2), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtOverlapKey1: knownvalue.StringExact(acctest.CtResourceValue2), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtOverlapKey1: knownvalue.StringExact(acctest.CtResourceValue2), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtOverlapKey1: config.StringVariable(acctest.CtProviderValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtOverlapKey1: config.StringVariable(acctest.CtResourceValue2), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_DefaultTags_updateToProviderOnly(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + acctest.CtResourceTags: nil, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + acctest.CtResourceTags: nil, + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_DefaultTags_updateToResourceOnly(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + acctest.CtResourceTags: nil, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_DefaultTags_emptyResourceTag(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(""), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(""), + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_DefaultTags_emptyProviderOnlyTag(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(""), + }), + acctest.CtResourceTags: nil, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(""), + }), + acctest.CtResourceTags: nil, + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_DefaultTags_nullOverlappingResourceTag(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtProviderValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: nil, + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.Null(), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.Null(), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(""), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtProviderValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: nil, + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + ImportStateVerifyIgnore: []string{ + acctest.CtTagsKey1, // The canonical value returned by the AWS API is "" + }, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_DefaultTags_nullNonOverlappingResourceTag(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtProviderKey1: config.StringVariable(acctest.CtProviderValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtResourceKey1: nil, + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.Null(), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(""), + acctest.CtProviderKey1: knownvalue.StringExact(acctest.CtProviderValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.Null(), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(""), + acctest.CtProviderKey1: knownvalue.StringExact(acctest.CtProviderValue1), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_defaults/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtProviderKey1: config.StringVariable(acctest.CtProviderValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtResourceKey1: nil, + }), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + ImportStateVerifyIgnore: []string{ + "tags.resourcekey1", // The canonical value returned by the AWS API is "" + }, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_ComputedTag_OnCreate(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tagsComputed1/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + "unknownTagKey": config.StringVariable("computedkey1"), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "tags.computedkey1", "null_resource.test", names.AttrID), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapSizeExact(1)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapSizeExact(1)), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectUnknownValue(resourceName, tfjsonpath.New(names.AttrTags).AtMapKey("computedkey1")), + plancheck.ExpectUnknownValue(resourceName, tfjsonpath.New(names.AttrTagsAll)), + }, + PostApplyPreRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tagsComputed1/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + "unknownTagKey": config.StringVariable("computedkey1"), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_ComputedTag_OnUpdate_Add(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tagsComputed2/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + "unknownTagKey": config.StringVariable("computedkey1"), + "knownTagKey": config.StringVariable(acctest.CtKey1), + "knownTagValue": config.StringVariable(acctest.CtValue1), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "tags.computedkey1", "null_resource.test", names.AttrID), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapSizeExact(2)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapPartial(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapSizeExact(2)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapPartial(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectUnknownValue(resourceName, tfjsonpath.New(names.AttrTags).AtMapKey("computedkey1")), + plancheck.ExpectUnknownValue(resourceName, tfjsonpath.New(names.AttrTagsAll)), + }, + PostApplyPreRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tagsComputed2/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + "unknownTagKey": config.StringVariable("computedkey1"), + "knownTagKey": config.StringVariable(acctest.CtKey1), + "knownTagValue": config.StringVariable(acctest.CtValue1), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_ComputedTag_OnUpdate_Replace(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tagsComputed1/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + "unknownTagKey": config.StringVariable(acctest.CtKey1), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, acctest.CtTagsKey1, "null_resource.test", names.AttrID), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapSizeExact(1)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapSizeExact(1)), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectUnknownValue(resourceName, tfjsonpath.New(names.AttrTags).AtMapKey(acctest.CtKey1)), + plancheck.ExpectUnknownValue(resourceName, tfjsonpath.New(names.AttrTagsAll)), + }, + PostApplyPreRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tagsComputed1/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + "unknownTagKey": config.StringVariable(acctest.CtKey1), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrARN, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_IgnoreTags_Overlap_DefaultTag(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + // 1: Create + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_ignore/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtProviderKey1: config.StringVariable(acctest.CtProviderValue1), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtResourceKey1: config.StringVariable(acctest.CtResourceValue1), + }), + "ignore_tag_keys": config.SetVariable( + config.StringVariable(acctest.CtProviderKey1), + ), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + expectFullResourceTags(resourceName, knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtProviderKey1: knownvalue.StringExact(acctest.CtProviderValue1), // TODO: Should not be set + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + }, + PostApplyPreRefresh: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + }, + // 2: Update ignored tag only + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_ignore/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtProviderKey1: config.StringVariable(acctest.CtProviderValue1Updated), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtResourceKey1: config.StringVariable(acctest.CtResourceValue1), + }), + "ignore_tag_keys": config.SetVariable( + config.StringVariable(acctest.CtProviderKey1), + ), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + expectFullResourceTags(resourceName, knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtProviderKey1: knownvalue.StringExact(acctest.CtProviderValue1), // TODO: Should not be set + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + })), + }, + PostApplyPreRefresh: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + }, + // 3: Update both tags + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_ignore/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtProviderTags: config.MapVariable(map[string]config.Variable{ + acctest.CtProviderKey1: config.StringVariable(acctest.CtProviderValue1Again), + }), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtResourceKey1: config.StringVariable(acctest.CtResourceValue1Updated), + }), + "ignore_tag_keys": config.SetVariable( + config.StringVariable(acctest.CtProviderKey1), + ), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Updated), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Updated), + })), + expectFullResourceTags(resourceName, knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtProviderKey1: knownvalue.StringExact(acctest.CtProviderValue1), // TODO: Should not be set + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Updated), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Updated), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Updated), + })), + }, + PostApplyPreRefresh: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + }, + }, + }) +} + +func TestAccResilienceHubResiliencyPolicy_tags_IgnoreTags_Overlap_ResourceTag(t *testing.T) { + ctx := acctest.Context(t) + var v resiliencehub.DescribeResiliencyPolicyOutput + resourceName := "aws_resiliencehub_resiliency_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), + CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), + Steps: []resource.TestStep{ + // 1: Create + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_ignore/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtResourceKey1: config.StringVariable(acctest.CtResourceValue1), + acctest.CtResourceKey2: config.StringVariable(acctest.CtResourceValue2), + }), + "ignore_tag_keys": config.SetVariable( + config.StringVariable(acctest.CtResourceKey1), + ), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + expectFullResourceTags(resourceName, knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), // TODO: Should not be set + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + }, + PostApplyPreRefresh: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), // TODO: Should be NoOp + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + }, + }, + ExpectNonEmptyPlan: true, + }, + // 2: Update ignored tag + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_ignore/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtResourceKey1: config.StringVariable(acctest.CtResourceValue1Updated), + acctest.CtResourceKey2: config.StringVariable(acctest.CtResourceValue2), + }), + "ignore_tag_keys": config.SetVariable( + config.StringVariable(acctest.CtResourceKey1), + ), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Updated), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + expectFullResourceTags(resourceName, knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), // TODO: Should not be set + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Updated), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + }, + PostApplyPreRefresh: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), // TODO: Should be NoOp + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Updated), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2), + })), + }, + }, + ExpectNonEmptyPlan: true, + }, + // 3: Update both tags + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/ResiliencyPolicy/tags_ignore/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtResourceKey1: config.StringVariable(acctest.CtResourceValue1Again), + acctest.CtResourceKey2: config.StringVariable(acctest.CtResourceValue2Updated), + }), + "ignore_tag_keys": config.SetVariable( + config.StringVariable(acctest.CtResourceKey1), + ), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckResiliencyPolicyExists(ctx, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Again), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2Updated), + })), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2Updated), + })), + expectFullResourceTags(resourceName, knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1), // TODO: Should not be set + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2Updated), + })), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Again), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2Updated), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2Updated), + })), + }, + PostApplyPreRefresh: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), // TODO: Should be NoOp + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey1: knownvalue.StringExact(acctest.CtResourceValue1Again), + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2Updated), + })), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtResourceKey2: knownvalue.StringExact(acctest.CtResourceValue2Updated), + })), + }, + }, + ExpectNonEmptyPlan: true, + }, + }, + }) +} diff --git a/internal/service/resiliencehub/resiliency_policy_test.go b/internal/service/resiliencehub/resiliency_policy_test.go index b0bc5fb6a61..b91f56aac2b 100644 --- a/internal/service/resiliencehub/resiliency_policy_test.go +++ b/internal/service/resiliencehub/resiliency_policy_test.go @@ -70,7 +70,7 @@ func TestAccResilienceHubResiliencyPolicy_basic(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -118,7 +118,7 @@ func TestAccResilienceHubResiliencyPolicy_dataLocationConstraint(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -140,7 +140,7 @@ func TestAccResilienceHubResiliencyPolicy_dataLocationConstraint(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -193,7 +193,7 @@ func TestAccResilienceHubResiliencyPolicy_description(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -215,7 +215,7 @@ func TestAccResilienceHubResiliencyPolicy_description(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -264,7 +264,7 @@ func TestAccResilienceHubResiliencyPolicy_name(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -286,7 +286,7 @@ func TestAccResilienceHubResiliencyPolicy_name(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -346,7 +346,7 @@ func TestAccResilienceHubResiliencyPolicy_policy(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -375,7 +375,7 @@ func TestAccResilienceHubResiliencyPolicy_policy(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -404,7 +404,7 @@ func TestAccResilienceHubResiliencyPolicy_policy(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -464,7 +464,7 @@ func TestAccResilienceHubResiliencyPolicy_policyWithRegion(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -493,7 +493,7 @@ func TestAccResilienceHubResiliencyPolicy_policyWithRegion(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -522,7 +522,7 @@ func TestAccResilienceHubResiliencyPolicy_policyWithRegion(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -570,7 +570,7 @@ func TestAccResilienceHubResiliencyPolicy_tier(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -592,7 +592,7 @@ func TestAccResilienceHubResiliencyPolicy_tier(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccAttrImportStateIdFunc(resourceName, names.AttrARN), + ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrARN), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrARN, }, @@ -600,52 +600,6 @@ func TestAccResilienceHubResiliencyPolicy_tier(t *testing.T) { }) } -func TestAccResilienceHubResiliencyPolicy_tags(t *testing.T) { - ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } - - var policy1, policy2 resiliencehub.DescribeResiliencyPolicyOutput - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_resiliencehub_resiliency_policy.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionNot(t, names.USGovCloudPartitionID) - testAccPreCheck(ctx, t) - }, - ErrorCheck: acctest.ErrorCheck(t, names.ResilienceHubServiceID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckResiliencyPolicyDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccResiliencyPolicyConfig_tags1(rName, acctest.CtKey1, acctest.CtValue1), - Check: resource.ComposeTestCheckFunc( - testAccCheckResiliencyPolicyExists(ctx, resourceName, &policy1), - resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, names.ResilienceHubServiceID, regexache.MustCompile(`resiliency-policy/.+`)), - ), - }, - { - Config: testAccResiliencyPolicyConfig_tag2(rName, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, acctest.CtValue2), - Check: resource.ComposeTestCheckFunc( - testAccCheckResiliencyPolicyExists(ctx, resourceName, &policy2), - testAccCheckResiliencyPolicyNotRecreated(&policy1, &policy2), - resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1Updated), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, names.ResilienceHubServiceID, regexache.MustCompile(`resiliency-policy/.+`)), - ), - }, - }, - }) -} - func TestAccResilienceHubResiliencyPolicy_disappears(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { @@ -731,17 +685,6 @@ func testAccCheckResiliencyPolicyExists(ctx context.Context, name string, policy } } -func testAccAttrImportStateIdFunc(resourceName, attrName string) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return "", fmt.Errorf("Not found: %s", resourceName) - } - - return rs.Primary.Attributes[attrName], nil - } -} - func testAccPreCheck(ctx context.Context, t *testing.T) { conn := acctest.Provider.Meta().(*conns.AWSClient).ResilienceHubClient(ctx) @@ -756,16 +699,6 @@ func testAccPreCheck(ctx context.Context, t *testing.T) { } } -func testAccCheckResiliencyPolicyNotRecreated(before, after *resiliencehub.DescribeResiliencyPolicyOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - if before, after := aws.ToString(before.Policy.PolicyArn), aws.ToString(after.Policy.PolicyArn); before != after { - return create.Error(names.ResilienceHub, create.ErrActionCheckingNotRecreated, tfresiliencehub.ResNameResiliencyPolicy, before, errors.New("recreated")) - } - - return nil - } -} - func testAccResiliencyPolicyConfig_basic(rName string) string { return fmt.Sprintf(` resource "aws_resiliencehub_resiliency_policy" "test" { @@ -925,78 +858,3 @@ resource "aws_resiliencehub_resiliency_policy" "test" { } `, rName, duration) } - -func testAccResiliencyPolicyConfig_tags1(rName, tagKey1, tagValue1 string) string { - return fmt.Sprintf(` -resource "aws_resiliencehub_resiliency_policy" "test" { - name = %[1]q - - description = %[1]q - - tier = "NotApplicable" - - data_location_constraint = "AnyLocation" - - policy { - region { - rpo = "1h0m0s" - rto = "1h0m0s" - } - az { - rpo = "1h0m0s" - rto = "1h0m0s" - } - hardware { - rpo = "1h0m0s" - rto = "1h0m0s" - } - software { - rpo = "1h0m0s" - rto = "1h0m0s" - } - } - - tags = { - %[2]q = %[3]q - } -} -`, rName, tagKey1, tagValue1) -} - -func testAccResiliencyPolicyConfig_tag2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return fmt.Sprintf(` -resource "aws_resiliencehub_resiliency_policy" "test" { - name = %[1]q - - description = %[1]q - - tier = "NotApplicable" - - data_location_constraint = "AnyLocation" - - policy { - region { - rpo = "1h0m0s" - rto = "1h0m0s" - } - az { - rpo = "1h0m0s" - rto = "1h0m0s" - } - hardware { - rpo = "1h0m0s" - rto = "1h0m0s" - } - software { - rpo = "1h0m0s" - rto = "1h0m0s" - } - } - - tags = { - %[2]q = %[3]q - %[4]q = %[5]q - } -} -`, rName, tagKey1, tagValue1, tagKey2, tagValue2) -} diff --git a/internal/service/resiliencehub/tags_gen_test.go b/internal/service/resiliencehub/tags_gen_test.go new file mode 100644 index 00000000000..a1a461daab3 --- /dev/null +++ b/internal/service/resiliencehub/tags_gen_test.go @@ -0,0 +1,16 @@ +// Code generated by internal/generate/tagstests/main.go; DO NOT EDIT. + +package resiliencehub_test + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + tfstatecheck "github.com/hashicorp/terraform-provider-aws/internal/acctest/statecheck" + tfresiliencehub "github.com/hashicorp/terraform-provider-aws/internal/service/resiliencehub" +) + +func expectFullResourceTags(resourceAddress string, knownValue knownvalue.Check) statecheck.StateCheck { + return tfstatecheck.ExpectFullResourceTags(tfresiliencehub.ServicePackage(context.Background()), resourceAddress, knownValue) +} diff --git a/internal/service/resiliencehub/testdata/ResiliencyPolicy/tags/main_gen.tf b/internal/service/resiliencehub/testdata/ResiliencyPolicy/tags/main_gen.tf new file mode 100644 index 00000000000..e7f1f549dbe --- /dev/null +++ b/internal/service/resiliencehub/testdata/ResiliencyPolicy/tags/main_gen.tf @@ -0,0 +1,38 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resource "aws_resiliencehub_resiliency_policy" "test" { + name = var.rName + + tier = "NotApplicable" + + policy { + az { + rpo = "1h0m0s" + rto = "1h0m0s" + } + hardware { + rpo = "1h0m0s" + rto = "1h0m0s" + } + software { + rpo = "1h0m0s" + rto = "1h0m0s" + } + } + + tags = var.resource_tags +} + +variable "rName" { + description = "Name for resource" + type = string + nullable = false +} + +variable "resource_tags" { + description = "Tags to set on resource. To specify no tags, set to `null`" + # Not setting a default, so that this must explicitly be set to `null` to specify no tags + type = map(string) + nullable = true +} diff --git a/internal/service/resiliencehub/testdata/ResiliencyPolicy/tagsComputed1/main_gen.tf b/internal/service/resiliencehub/testdata/ResiliencyPolicy/tagsComputed1/main_gen.tf new file mode 100644 index 00000000000..0aa34d6444a --- /dev/null +++ b/internal/service/resiliencehub/testdata/ResiliencyPolicy/tagsComputed1/main_gen.tf @@ -0,0 +1,42 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +provider "null" {} + +resource "aws_resiliencehub_resiliency_policy" "test" { + name = var.rName + + tier = "NotApplicable" + + policy { + az { + rpo = "1h0m0s" + rto = "1h0m0s" + } + hardware { + rpo = "1h0m0s" + rto = "1h0m0s" + } + software { + rpo = "1h0m0s" + rto = "1h0m0s" + } + } + + tags = { + (var.unknownTagKey) = null_resource.test.id + } +} + +resource "null_resource" "test" {} + +variable "rName" { + description = "Name for resource" + type = string + nullable = false +} + +variable "unknownTagKey" { + type = string + nullable = false +} diff --git a/internal/service/resiliencehub/testdata/ResiliencyPolicy/tagsComputed2/main_gen.tf b/internal/service/resiliencehub/testdata/ResiliencyPolicy/tagsComputed2/main_gen.tf new file mode 100644 index 00000000000..6125bc50ad9 --- /dev/null +++ b/internal/service/resiliencehub/testdata/ResiliencyPolicy/tagsComputed2/main_gen.tf @@ -0,0 +1,53 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +provider "null" {} + +resource "aws_resiliencehub_resiliency_policy" "test" { + name = var.rName + + tier = "NotApplicable" + + policy { + az { + rpo = "1h0m0s" + rto = "1h0m0s" + } + hardware { + rpo = "1h0m0s" + rto = "1h0m0s" + } + software { + rpo = "1h0m0s" + rto = "1h0m0s" + } + } + + tags = { + (var.unknownTagKey) = null_resource.test.id + (var.knownTagKey) = var.knownTagValue + } +} + +resource "null_resource" "test" {} + +variable "rName" { + description = "Name for resource" + type = string + nullable = false +} + +variable "unknownTagKey" { + type = string + nullable = false +} + +variable "knownTagKey" { + type = string + nullable = false +} + +variable "knownTagValue" { + type = string + nullable = false +} diff --git a/internal/service/resiliencehub/testdata/ResiliencyPolicy/tags_defaults/main_gen.tf b/internal/service/resiliencehub/testdata/ResiliencyPolicy/tags_defaults/main_gen.tf new file mode 100644 index 00000000000..a048cee6406 --- /dev/null +++ b/internal/service/resiliencehub/testdata/ResiliencyPolicy/tags_defaults/main_gen.tf @@ -0,0 +1,49 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +provider "aws" { + default_tags { + tags = var.provider_tags + } +} + +resource "aws_resiliencehub_resiliency_policy" "test" { + name = var.rName + + tier = "NotApplicable" + + policy { + az { + rpo = "1h0m0s" + rto = "1h0m0s" + } + hardware { + rpo = "1h0m0s" + rto = "1h0m0s" + } + software { + rpo = "1h0m0s" + rto = "1h0m0s" + } + } + + tags = var.resource_tags +} + +variable "rName" { + description = "Name for resource" + type = string + nullable = false +} + +variable "resource_tags" { + description = "Tags to set on resource. To specify no tags, set to `null`" + # Not setting a default, so that this must explicitly be set to `null` to specify no tags + type = map(string) + nullable = true +} + +variable "provider_tags" { + type = map(string) + nullable = false +} diff --git a/internal/service/resiliencehub/testdata/ResiliencyPolicy/tags_ignore/main_gen.tf b/internal/service/resiliencehub/testdata/ResiliencyPolicy/tags_ignore/main_gen.tf new file mode 100644 index 00000000000..e136c6d3c5f --- /dev/null +++ b/internal/service/resiliencehub/testdata/ResiliencyPolicy/tags_ignore/main_gen.tf @@ -0,0 +1,58 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +provider "aws" { + default_tags { + tags = var.provider_tags + } + ignore_tags { + keys = var.ignore_tag_keys + } +} + +resource "aws_resiliencehub_resiliency_policy" "test" { + name = var.rName + + tier = "NotApplicable" + + policy { + az { + rpo = "1h0m0s" + rto = "1h0m0s" + } + hardware { + rpo = "1h0m0s" + rto = "1h0m0s" + } + software { + rpo = "1h0m0s" + rto = "1h0m0s" + } + } + + tags = var.resource_tags +} + +variable "rName" { + description = "Name for resource" + type = string + nullable = false +} + +variable "resource_tags" { + description = "Tags to set on resource. To specify no tags, set to `null`" + # Not setting a default, so that this must explicitly be set to `null` to specify no tags + type = map(string) + nullable = true +} + +variable "provider_tags" { + type = map(string) + nullable = true + default = null +} + +variable "ignore_tag_keys" { + type = set(string) + nullable = false +} diff --git a/internal/service/resiliencehub/testdata/tmpl/resiliency_policy_tags.gtpl b/internal/service/resiliencehub/testdata/tmpl/resiliency_policy_tags.gtpl new file mode 100644 index 00000000000..5251d571e5f --- /dev/null +++ b/internal/service/resiliencehub/testdata/tmpl/resiliency_policy_tags.gtpl @@ -0,0 +1,22 @@ +resource "aws_resiliencehub_resiliency_policy" "test" { + name = var.rName + + tier = "NotApplicable" + + policy { + az { + rpo = "1h0m0s" + rto = "1h0m0s" + } + hardware { + rpo = "1h0m0s" + rto = "1h0m0s" + } + software { + rpo = "1h0m0s" + rto = "1h0m0s" + } + } + +{{- template "tags" . }} +} diff --git a/internal/service/s3/bucket_object.go b/internal/service/s3/bucket_object.go index 7030b9cdb16..631a49255c5 100644 --- a/internal/service/s3/bucket_object.go +++ b/internal/service/s3/bucket_object.go @@ -29,7 +29,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" "github.com/hashicorp/terraform-provider-aws/internal/sdkv2" - tfkms "github.com/hashicorp/terraform-provider-aws/internal/service/kms" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" itypes "github.com/hashicorp/terraform-provider-aws/internal/types" @@ -139,13 +138,6 @@ func resourceBucketObject() *schema.Resource { Optional: true, Computed: true, ValidateFunc: verify.ValidARN, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - // ignore diffs where the user hasn't specified a kms_key_id but the bucket has a default KMS key configured - if new == "" && d.Get("server_side_encryption") == types.ServerSideEncryptionAwsKms { - return true - } - return false - }, }, "metadata": { Type: schema.TypeMap, @@ -242,6 +234,9 @@ func resourceBucketObjectRead(ctx context.Context, d *schema.ResourceData, meta d.Set(names.AttrContentType, output.ContentType) // See https://forums.aws.amazon.com/thread.jspa?threadID=44003 d.Set("etag", strings.Trim(aws.ToString(output.ETag), `"`)) + if output.SSEKMSKeyId != nil { // nosemgrep:ci.helper-schema-ResourceData-Set-extraneous-nil-check + d.Set(names.AttrKMSKeyID, output.SSEKMSKeyId) + } d.Set("metadata", output.Metadata) d.Set("object_lock_legal_hold_status", output.ObjectLockLegalHoldStatus) d.Set("object_lock_mode", output.ObjectLockMode) @@ -256,10 +251,6 @@ func resourceBucketObjectRead(ctx context.Context, d *schema.ResourceData, meta d.Set("version_id", output.VersionId) d.Set("website_redirect", output.WebsiteRedirectLocation) - if err := resourceBucketObjectSetKMS(ctx, d, meta, output.SSEKMSKeyId); err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - return diags } @@ -507,25 +498,6 @@ func resourceBucketObjectUpload(ctx context.Context, d *schema.ResourceData, met return append(diags, resourceBucketObjectRead(ctx, d, meta)...) } -func resourceBucketObjectSetKMS(ctx context.Context, d *schema.ResourceData, meta interface{}, sseKMSKeyId *string) error { - // Only set non-default KMS key ID (one that doesn't match default) - if sseKMSKeyId != nil { - // retrieve S3 KMS Default Master Key - conn := meta.(*conns.AWSClient).KMSClient(ctx) - keyMetadata, err := tfkms.FindKeyByID(ctx, conn, defaultKMSKeyAlias) - if err != nil { - return fmt.Errorf("Failed to describe default S3 KMS key (%s): %s", defaultKMSKeyAlias, err) - } - - if kmsKeyID := aws.ToString(sseKMSKeyId); kmsKeyID != aws.ToString(keyMetadata.Arn) { - log.Printf("[DEBUG] S3 object is encrypted using a non-default KMS Key ID: %s", kmsKeyID) - d.Set(names.AttrKMSKeyID, sseKMSKeyId) - } - } - - return nil -} - func resourceBucketObjectCustomizeDiff(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { if hasBucketObjectContentChanges(d) { return d.SetNewComputed("version_id") diff --git a/internal/service/s3/enum.go b/internal/service/s3/enum.go index 66435fe7885..dea45d082ad 100644 --- a/internal/service/s3/enum.go +++ b/internal/service/s3/enum.go @@ -9,8 +9,6 @@ import ( "github.com/aws/aws-sdk-go-v2/aws/arn" ) -const defaultKMSKeyAlias = "alias/aws/s3" - type bucketNameType int const ( diff --git a/internal/service/s3/object.go b/internal/service/s3/object.go index a9d35e3176b..274df8ee1b0 100644 --- a/internal/service/s3/object.go +++ b/internal/service/s3/object.go @@ -31,7 +31,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" "github.com/hashicorp/terraform-provider-aws/internal/sdkv2" - tfkms "github.com/hashicorp/terraform-provider-aws/internal/service/kms" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" itypes "github.com/hashicorp/terraform-provider-aws/internal/types" @@ -165,13 +164,6 @@ func resourceObject() *schema.Resource { Optional: true, Computed: true, ValidateFunc: verify.ValidARN, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - // ignore diffs where the user hasn't specified a kms_key_id but the bucket has a default KMS key configured - if new == "" && d.Get("server_side_encryption") == types.ServerSideEncryptionAwsKms { - return true - } - return false - }, }, "metadata": { Type: schema.TypeMap, @@ -302,6 +294,9 @@ func resourceObjectRead(ctx context.Context, d *schema.ResourceData, meta interf d.Set(names.AttrContentType, output.ContentType) // See https://forums.aws.amazon.com/thread.jspa?threadID=44003 d.Set("etag", strings.Trim(aws.ToString(output.ETag), `"`)) + if output.SSEKMSKeyId != nil { // nosemgrep:ci.helper-schema-ResourceData-Set-extraneous-nil-check + d.Set(names.AttrKMSKeyID, output.SSEKMSKeyId) + } d.Set("metadata", output.Metadata) d.Set("object_lock_legal_hold_status", output.ObjectLockLegalHoldStatus) d.Set("object_lock_mode", output.ObjectLockMode) @@ -316,10 +311,6 @@ func resourceObjectRead(ctx context.Context, d *schema.ResourceData, meta interf d.Set("version_id", output.VersionId) d.Set("website_redirect", output.WebsiteRedirectLocation) - if err := setObjectKMSKeyID(ctx, meta, d, aws.ToString(output.SSEKMSKeyId)); err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - return diags } @@ -601,25 +592,6 @@ func resourceObjectUpload(ctx context.Context, d *schema.ResourceData, meta inte return append(diags, resourceObjectRead(ctx, d, meta)...) } -func setObjectKMSKeyID(ctx context.Context, meta interface{}, d *schema.ResourceData, sseKMSKeyID string) error { - // Only set non-default KMS key ID (one that doesn't match default). - if sseKMSKeyID != "" { - // Read S3 KMS default master key. - keyMetadata, err := tfkms.FindKeyByID(ctx, meta.(*conns.AWSClient).KMSClient(ctx), defaultKMSKeyAlias) - - if err != nil { - return fmt.Errorf("reading default S3 KMS key (%s): %s", defaultKMSKeyAlias, err) - } - - if sseKMSKeyID != aws.ToString(keyMetadata.Arn) { - log.Printf("[DEBUG] S3 object is encrypted using a non-default KMS key: %s", sseKMSKeyID) - d.Set(names.AttrKMSKeyID, sseKMSKeyID) - } - } - - return nil -} - func validateMetadataIsLowerCase(v interface{}, k string) (ws []string, errors []error) { value := v.(map[string]interface{}) diff --git a/internal/service/s3/object_copy.go b/internal/service/s3/object_copy.go index a7e739f336f..b6fedba198d 100644 --- a/internal/service/s3/object_copy.go +++ b/internal/service/s3/object_copy.go @@ -396,10 +396,6 @@ func resourceObjectCopyRead(ctx context.Context, d *schema.ResourceData, meta in d.Set("version_id", output.VersionId) d.Set("website_redirect", output.WebsiteRedirectLocation) - if err := setObjectKMSKeyID(ctx, meta, d, aws.ToString(output.SSEKMSKeyId)); err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - return diags } diff --git a/internal/service/s3/object_test.go b/internal/service/s3/object_test.go index a56a6ed35cb..0fda26be0a0 100644 --- a/internal/service/s3/object_test.go +++ b/internal/service/s3/object_test.go @@ -23,7 +23,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/statecheck" "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfs3 "github.com/hashicorp/terraform-provider-aws/internal/service/s3" @@ -1883,6 +1887,130 @@ func TestAccS3Object_optInRegion(t *testing.T) { }) } +func TestAccS3Object_defaultKMS(t *testing.T) { + ctx := acctest.Context(t) + var obj s3.GetObjectOutput + resourceName := "aws_s3_object.object" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.S3ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckObjectDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccObjectConfig_defaultKMS(rName, "stuff"), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + }, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckObjectExists(ctx, resourceName, &obj), + testAccCheckObjectBody(&obj, "stuff"), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrKMSKeyID), knownvalue.NotNull()), + }, + }, + }, + }) +} + +func TestAccS3Object_defaultKMSUpgrade(t *testing.T) { + ctx := acctest.Context(t) + var obj s3.GetObjectOutput + resourceName := "aws_s3_object.object" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.S3ServiceID), + CheckDestroy: testAccCheckObjectDestroy(ctx), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.72.1", + }, + }, + Config: testAccObjectConfig_defaultKMS(rName, "stuff"), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + }, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckObjectExists(ctx, resourceName, &obj), + testAccCheckObjectBody(&obj, "stuff"), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrKMSKeyID), knownvalue.NotNull()), + }, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Config: testAccObjectConfig_defaultKMS(rName, "stuff"), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckObjectExists(ctx, resourceName, &obj), + testAccCheckObjectBody(&obj, "stuff"), + ), + }, + }, + }) +} + +func TestAccS3Object_basicUpgrade(t *testing.T) { + ctx := acctest.Context(t) + var obj s3.GetObjectOutput + resourceName := "aws_s3_object.object" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.S3ServiceID), + CheckDestroy: testAccCheckObjectDestroy(ctx), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.72.1", + }, + }, + Config: testAccObjectConfig_basic(rName), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + }, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckObjectExists(ctx, resourceName, &obj), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Config: testAccObjectConfig_basic(rName), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckObjectExists(ctx, resourceName, &obj), + ), + }, + }, + }) +} + func testAccCheckObjectVersionIDDiffers(first, second *s3.GetObjectOutput) resource.TestCheckFunc { return func(s *terraform.State) error { if aws.ToString(first.VersionId) == aws.ToString(second.VersionId) { @@ -3040,3 +3168,22 @@ resource "aws_s3_object" "object" { } `, rName)) } + +func testAccObjectConfig_defaultKMS(rName string, content string) string { + return fmt.Sprintf(` +data "aws_kms_key" "test" { + key_id = "alias/aws/s3" +} + +resource "aws_s3_bucket" "test" { + bucket = %[1]q +} + +resource "aws_s3_object" "object" { + bucket = aws_s3_bucket.test.bucket + key = "test-key" + content = %[2]q + kms_key_id = data.aws_kms_key.test.arn +} +`, rName, content) +} diff --git a/internal/service/sagemaker/exports_test.go b/internal/service/sagemaker/exports_test.go index 4628dca7a01..0fc25d98bc9 100644 --- a/internal/service/sagemaker/exports_test.go +++ b/internal/service/sagemaker/exports_test.go @@ -16,6 +16,7 @@ var ( ResourceEndpointConfiguration = resourceEndpointConfiguration ResourceFeatureGroup = resourceFeatureGroup ResourceFlowDefinition = resourceFlowDefinition + ResourceHub = resourceHub ResourceHumanTaskUI = resourceHumanTaskUI ResourceImage = resourceImage ResourceImageVersion = resourceImageVersion @@ -45,6 +46,7 @@ var ( FindEndpointConfigByName = findEndpointConfigByName FindFeatureGroupByName = findFeatureGroupByName FindFlowDefinitionByName = findFlowDefinitionByName + FindHubByName = findHubByName FindHumanTaskUIByName = findHumanTaskUIByName FindImageByName = findImageByName FindImageVersionByName = findImageVersionByName diff --git a/internal/service/sagemaker/hub.go b/internal/service/sagemaker/hub.go new file mode 100644 index 00000000000..d95623ee2c3 --- /dev/null +++ b/internal/service/sagemaker/hub.go @@ -0,0 +1,272 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package sagemaker + +import ( + "context" + "log" + + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/sagemaker" + awstypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @SDKResource("aws_sagemaker_hub", name="Hub") +// @Tags(identifierAttribute="arn") +func resourceHub() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceHubCreate, + ReadWithoutTimeout: resourceHubRead, + UpdateWithoutTimeout: resourceHubUpdate, + DeleteWithoutTimeout: resourceHubDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + names.AttrARN: { + Type: schema.TypeString, + Computed: true, + }, + "hub_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 63), + validation.StringMatch(regexache.MustCompile(`^[0-9A-Za-z](-*[0-9A-Za-z]){0,62}$`), + "Valid characters are a-z, A-Z, 0-9, and - (hyphen)."), + ), + }, + "hub_description": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 1024), + }, + "hub_display_name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 255), + }, + "hub_search_keywords": { + Type: schema.TypeSet, + MaxItems: 50, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "s3_storage_config": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "s3_output_path": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringMatch(regexache.MustCompile(`^(https|s3)://([^/])/?(.*)$`), ""), + validation.StringLenBetween(1, 1024), + ), + }, + }, + }, + }, + names.AttrTags: tftags.TagsSchema(), + names.AttrTagsAll: tftags.TagsSchemaComputed(), + }, + + CustomizeDiff: verify.SetTagsDiff, + } +} + +func resourceHubCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).SageMakerClient(ctx) + + name := d.Get("hub_name").(string) + input := &sagemaker.CreateHubInput{ + HubName: aws.String(name), + HubDescription: aws.String(d.Get("hub_description").(string)), + Tags: getTagsIn(ctx), + } + + if v, ok := d.GetOk("hub_display_name"); ok { + input.HubDisplayName = aws.String(v.(string)) + } + + if v, ok := d.GetOk("hub_search_keywords"); ok { + input.HubSearchKeywords = flex.ExpandStringValueSet(v.(*schema.Set)) + } + + if v, ok := d.GetOk("s3_storage_config"); ok { + input.S3StorageConfig = expandS3StorageConfig(v.([]interface{})) + } + + _, err := conn.CreateHub(ctx, input) + if err != nil { + return sdkdiag.AppendErrorf(diags, "creating SageMaker Hub %s: %s", name, err) + } + + d.SetId(name) + + if _, err := waitHubInService(ctx, conn, d.Id()); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for SageMaker Hub (%s) to be InService: %s", d.Id(), err) + } + + return append(diags, resourceHubRead(ctx, d, meta)...) +} + +func resourceHubRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).SageMakerClient(ctx) + + hub, err := findHubByName(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + d.SetId("") + log.Printf("[WARN] Unable to find SageMaker Hub (%s); removing from state", d.Id()) + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading SageMaker Hub (%s): %s", d.Id(), err) + } + + d.Set("hub_name", hub.HubName) + d.Set(names.AttrARN, hub.HubArn) + d.Set("hub_description", hub.HubDescription) + d.Set("hub_display_name", hub.HubDisplayName) + d.Set("hub_search_keywords", flex.FlattenStringValueSet(hub.HubSearchKeywords)) + + if err := d.Set("s3_storage_config", flattenS3StorageConfig(hub.S3StorageConfig)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting s3_storage_config for SageMaker Hub (%s): %s", d.Id(), err) + } + + return diags +} + +func resourceHubUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).SageMakerClient(ctx) + + if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { + modifyOpts := &sagemaker.UpdateHubInput{ + HubName: aws.String(d.Id()), + } + + if d.HasChange("hub_description") { + modifyOpts.HubDescription = aws.String(d.Get("hub_description").(string)) + } + + if d.HasChange("hub_display_name") { + modifyOpts.HubDisplayName = aws.String(d.Get("hub_display_name").(string)) + } + + if d.HasChange("hub_search_keywords") { + modifyOpts.HubSearchKeywords = flex.ExpandStringValueSet(d.Get("hub_search_keywords").(*schema.Set)) + } + + if _, err := conn.UpdateHub(ctx, modifyOpts); err != nil { + return sdkdiag.AppendErrorf(diags, "updating SageMaker Hub (%s): %s", d.Id(), err) + } + + if _, err := waitHubUpdated(ctx, conn, d.Id()); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for SageMaker Hub (%s) to be updated: %s", d.Id(), err) + } + } + + return append(diags, resourceHubRead(ctx, d, meta)...) +} + +func resourceHubDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).SageMakerClient(ctx) + + input := &sagemaker.DeleteHubInput{ + HubName: aws.String(d.Id()), + } + + if _, err := conn.DeleteHub(ctx, input); err != nil { + if errs.IsA[*awstypes.ResourceNotFound](err) { + return diags + } + return sdkdiag.AppendErrorf(diags, "deleting SageMaker Hub (%s): %s", d.Id(), err) + } + + if _, err := waitHubDeleted(ctx, conn, d.Id()); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for SageMaker Hub (%s) to delete: %s", d.Id(), err) + } + + return diags +} + +func findHubByName(ctx context.Context, conn *sagemaker.Client, name string) (*sagemaker.DescribeHubOutput, error) { + input := &sagemaker.DescribeHubInput{ + HubName: aws.String(name), + } + + output, err := conn.DescribeHub(ctx, input) + + if errs.IsA[*awstypes.ResourceNotFound](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} + +func expandS3StorageConfig(configured []interface{}) *awstypes.HubS3StorageConfig { + if len(configured) == 0 { + return nil + } + + m := configured[0].(map[string]interface{}) + + c := &awstypes.HubS3StorageConfig{} + + if v, ok := m["s3_output_path"].(string); ok && v != "" { + c.S3OutputPath = aws.String(v) + } + + return c +} + +func flattenS3StorageConfig(config *awstypes.HubS3StorageConfig) []map[string]interface{} { + if config == nil { + return []map[string]interface{}{} + } + + m := map[string]interface{}{} + + if config.S3OutputPath != nil { + m["s3_output_path"] = aws.ToString(config.S3OutputPath) + } + + return []map[string]interface{}{m} +} diff --git a/internal/service/sagemaker/hub_test.go b/internal/service/sagemaker/hub_test.go new file mode 100644 index 00000000000..7de795e7d90 --- /dev/null +++ b/internal/service/sagemaker/hub_test.go @@ -0,0 +1,340 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package sagemaker_test + +import ( + "context" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/sagemaker" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfsagemaker "github.com/hashicorp/terraform-provider-aws/internal/service/sagemaker" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccSageMakerHub_basic(t *testing.T) { + ctx := acctest.Context(t) + var mpg sagemaker.DescribeHubOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rNameUpdated := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_sagemaker_hub.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SageMakerServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckHubDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccHubConfig_basic(rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckHubExists(ctx, resourceName, &mpg), + resource.TestCheckResourceAttr(resourceName, "hub_name", rName), + resource.TestCheckResourceAttr(resourceName, "hub_description", rName), + resource.TestCheckResourceAttr(resourceName, "hub_search_keywords.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "s3_storage_config.#", acctest.Ct0), + acctest.CheckResourceAttrRegionalARN(resourceName, names.AttrARN, "sagemaker", fmt.Sprintf("hub/%s", rName)), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccHubConfig_basic(rName, rNameUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckHubExists(ctx, resourceName, &mpg), + resource.TestCheckResourceAttr(resourceName, "hub_name", rName), + resource.TestCheckResourceAttr(resourceName, "hub_description", rNameUpdated), + acctest.CheckResourceAttrRegionalARN(resourceName, names.AttrARN, "sagemaker", fmt.Sprintf("hub/%s", rName)), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), + ), + }, + }, + }) +} + +func TestAccSageMakerHub_searchKeywords(t *testing.T) { + ctx := acctest.Context(t) + var mpg sagemaker.DescribeHubOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_sagemaker_hub.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SageMakerServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckHubDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccHubConfig_searchKeywords(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckHubExists(ctx, resourceName, &mpg), + resource.TestCheckResourceAttr(resourceName, "hub_name", rName), + resource.TestCheckResourceAttr(resourceName, "hub_description", rName), + resource.TestCheckResourceAttr(resourceName, "hub_search_keywords.#", acctest.Ct1), + resource.TestCheckTypeSetElemAttr(resourceName, "hub_search_keywords.*", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccHubConfig_searchKeywordsUpdated(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckHubExists(ctx, resourceName, &mpg), + resource.TestCheckResourceAttr(resourceName, "hub_name", rName), + resource.TestCheckResourceAttr(resourceName, "hub_description", rName), + resource.TestCheckResourceAttr(resourceName, "hub_search_keywords.#", acctest.Ct2), + resource.TestCheckTypeSetElemAttr(resourceName, "hub_search_keywords.*", rName), + resource.TestCheckTypeSetElemAttr(resourceName, "hub_search_keywords.*", fmt.Sprintf("%s-1", rName)), + ), + }, + { + Config: testAccHubConfig_searchKeywords(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckHubExists(ctx, resourceName, &mpg), + resource.TestCheckResourceAttr(resourceName, "hub_name", rName), + resource.TestCheckResourceAttr(resourceName, "hub_description", rName), + resource.TestCheckResourceAttr(resourceName, "hub_search_keywords.#", acctest.Ct1), + resource.TestCheckTypeSetElemAttr(resourceName, "hub_search_keywords.*", rName), + ), + }, + }, + }) +} + +func TestAccSageMakerHub_s3(t *testing.T) { + ctx := acctest.Context(t) + var mpg sagemaker.DescribeHubOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_sagemaker_hub.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SageMakerServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckHubDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccHubConfig_s3(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckHubExists(ctx, resourceName, &mpg), + resource.TestCheckResourceAttr(resourceName, "hub_name", rName), + resource.TestCheckResourceAttr(resourceName, "s3_storage_config.#", acctest.Ct1), + resource.TestCheckResourceAttrSet(resourceName, "s3_storage_config.0.s3_output_path"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccSageMakerHub_tags(t *testing.T) { + ctx := acctest.Context(t) + var mpg sagemaker.DescribeHubOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_sagemaker_hub.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SageMakerServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckHubDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccHubConfig_tags1(rName, acctest.CtKey1, acctest.CtValue1), + Check: resource.ComposeTestCheckFunc( + testAccCheckHubExists(ctx, resourceName, &mpg), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccHubConfig_tags2(rName, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, acctest.CtValue2), + Check: resource.ComposeTestCheckFunc( + testAccCheckHubExists(ctx, resourceName, &mpg), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1Updated), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), + ), + }, + { + Config: testAccHubConfig_tags1(rName, acctest.CtKey2, acctest.CtValue2), + Check: resource.ComposeTestCheckFunc( + testAccCheckHubExists(ctx, resourceName, &mpg), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), + ), + }, + }, + }) +} + +func TestAccSageMakerHub_disappears(t *testing.T) { + ctx := acctest.Context(t) + var mpg sagemaker.DescribeHubOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_sagemaker_hub.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SageMakerServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckHubDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccHubConfig_basic(rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckHubExists(ctx, resourceName, &mpg), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfsagemaker.ResourceHub(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckHubDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).SageMakerClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_sagemaker_hub" { + continue + } + + _, err := tfsagemaker.FindHubByName(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return fmt.Errorf("reading SageMaker Hub (%s): %w", rs.Primary.ID, err) + } + + return fmt.Errorf("sagemaker Hub %s still exists", rs.Primary.ID) + } + + return nil + } +} + +func testAccCheckHubExists(ctx context.Context, n string, mpg *sagemaker.DescribeHubOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No sagmaker Hub ID is set") + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).SageMakerClient(ctx) + resp, err := tfsagemaker.FindHubByName(ctx, conn, rs.Primary.ID) + if err != nil { + return err + } + + *mpg = *resp + + return nil + } +} + +func testAccHubConfig_basic(rName, desc string) string { + return fmt.Sprintf(` +resource "aws_sagemaker_hub" "test" { + hub_name = %[1]q + hub_description = %[2]q +} +`, rName, desc) +} + +func testAccHubConfig_searchKeywords(rName string) string { + return fmt.Sprintf(` +resource "aws_sagemaker_hub" "test" { + hub_name = %[1]q + hub_description = %[1]q + + hub_search_keywords = ["%[1]s"] +} +`, rName) +} + +func testAccHubConfig_searchKeywordsUpdated(rName string) string { + return fmt.Sprintf(` +resource "aws_sagemaker_hub" "test" { + hub_name = %[1]q + hub_description = %[1]q + + hub_search_keywords = ["%[1]s", "%[1]s-1"] +} +`, rName) +} + +func testAccHubConfig_s3(rName string) string { + return fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + bucket = %[1]q +} + +resource "aws_sagemaker_hub" "test" { + hub_name = %[1]q + hub_description = %[1]q + + s3_storage_config { + s3_output_path = "s3://${aws_s3_bucket.test.bucket}/" + } +} +`, rName) +} + +func testAccHubConfig_tags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_sagemaker_hub" "test" { + hub_name = %[1]q + hub_description = %[1]q + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccHubConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_sagemaker_hub" "test" { + hub_name = %[1]q + hub_description = %[1]q + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/internal/service/sagemaker/image.go b/internal/service/sagemaker/image.go index a7ca5816098..80a35a2b563 100644 --- a/internal/service/sagemaker/image.go +++ b/internal/service/sagemaker/image.go @@ -197,7 +197,7 @@ func resourceImageDelete(ctx context.Context, d *schema.ResourceData, meta inter return sdkdiag.AppendErrorf(diags, "deleting SageMaker Image (%s): %s", d.Id(), err) } - if _, err := waitImageDeleted(ctx, conn, d.Id()); err != nil { + if err := waitImageDeleted(ctx, conn, d.Id()); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for SageMaker Image (%s) to delete: %s", d.Id(), err) } diff --git a/internal/service/sagemaker/service_package_gen.go b/internal/service/sagemaker/service_package_gen.go index 69ac59a0efb..f7239325c1d 100644 --- a/internal/service/sagemaker/service_package_gen.go +++ b/internal/service/sagemaker/service_package_gen.go @@ -119,6 +119,14 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka IdentifierAttribute: names.AttrARN, }, }, + { + Factory: resourceHub, + TypeName: "aws_sagemaker_hub", + Name: "Hub", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: names.AttrARN, + }, + }, { Factory: resourceHumanTaskUI, TypeName: "aws_sagemaker_human_task_ui", diff --git a/internal/service/sagemaker/status.go b/internal/service/sagemaker/status.go index 468f7f110fa..7f82e8b92cf 100644 --- a/internal/service/sagemaker/status.go +++ b/internal/service/sagemaker/status.go @@ -239,3 +239,19 @@ func statusMlflowTrackingServer(ctx context.Context, conn *sagemaker.Client, nam return output, string(output.TrackingServerStatus), nil } } + +func statusHub(ctx context.Context, conn *sagemaker.Client, name string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findHubByName(ctx, conn, name) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.HubStatus), nil + } +} diff --git a/internal/service/sagemaker/sweep.go b/internal/service/sagemaker/sweep.go index eb2a6c5f524..d0a9b5e7505 100644 --- a/internal/service/sagemaker/sweep.go +++ b/internal/service/sagemaker/sweep.go @@ -161,6 +161,11 @@ func RegisterSweepers() { Name: "aws_sagemaker_pipeline", F: sweepPipelines, }) + + resource.AddTestSweepers("aws_sagemaker_hub", &resource.Sweeper{ + Name: "aws_sagemaker_hub", + F: sweepHubs, + }) } func sweepAppImagesConfig(region string) error { @@ -1117,3 +1122,53 @@ func sweepMlflowTrackingServers(region string) error { return sweeperErrs.ErrorOrNil() } + +func sweepHubs(region string) error { + ctx := sweep.Context(region) + client, err := sweep.SharedRegionalSweepClient(ctx, region) + if err != nil { + return fmt.Errorf("getting client: %s", err) + } + conn := client.SageMakerClient(ctx) + + sweepResources := make([]sweep.Sweepable, 0) + in := &sagemaker.ListHubsInput{} + + for { + out, err := conn.ListHubs(ctx, in) + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping Sagemaker Hubs sweep for %s: %s", region, err) + return nil + } + if err != nil { + return fmt.Errorf("error retrieving Sagemaker Hubs: %w", err) + } + + for _, hub := range out.HubSummaries { + name := aws.ToString(hub.HubName) + log.Printf("[INFO] Deleting Sagemaker Hubs: %s", name) + + if !strings.HasPrefix(name, sweep.ResourcePrefix) { + log.Printf("[INFO] Skipping SageMaker Hub (%s): not in allow list", name) + continue + } + + r := resourceHub() + d := r.Data(nil) + d.SetId(name) + + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + } + + if aws.ToString(out.NextToken) == "" { + break + } + in.NextToken = out.NextToken + } + + if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { + return fmt.Errorf("error sweeping Sagemaker Hubs for %s: %w", region, err) + } + + return nil +} diff --git a/internal/service/sagemaker/wait.go b/internal/service/sagemaker/wait.go index 4fa6432cc38..4e6b44b5efc 100644 --- a/internal/service/sagemaker/wait.go +++ b/internal/service/sagemaker/wait.go @@ -43,6 +43,7 @@ const ( monitoringScheduleScheduledTimeout = 2 * time.Minute monitoringScheduleStoppedTimeout = 2 * time.Minute mlflowTrackingServerTimeout = 30 * time.Minute + hubTimeout = 10 * time.Minute notebookInstanceStatusNotFound = "NotFound" ) @@ -178,12 +179,20 @@ func waitImageCreated(ctx context.Context, conn *sagemaker.Client, name string) Timeout: imageCreatedTimeout, } - _, err := stateConf.WaitForStateContext(ctx) + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*sagemaker.DescribeImageOutput); ok { + if status, reason := output.ImageStatus, aws.ToString(output.FailureReason); (status == awstypes.ImageStatusCreateFailed || status == awstypes.ImageStatusUpdateFailed) && reason != "" { + tfresource.SetLastError(err, errors.New(reason)) + } + + return err + } return err } -func waitImageDeleted(ctx context.Context, conn *sagemaker.Client, name string) (*sagemaker.DescribeImageOutput, error) { +func waitImageDeleted(ctx context.Context, conn *sagemaker.Client, name string) error { stateConf := &retry.StateChangeConf{ Pending: enum.Slice(awstypes.ImageStatusDeleting), Target: []string{}, @@ -194,10 +203,14 @@ func waitImageDeleted(ctx context.Context, conn *sagemaker.Client, name string) outputRaw, err := stateConf.WaitForStateContext(ctx) if output, ok := outputRaw.(*sagemaker.DescribeImageOutput); ok { - return output, err + if status, reason := output.ImageStatus, aws.ToString(output.FailureReason); status == awstypes.ImageStatusDeleteFailed && reason != "" { + tfresource.SetLastError(err, errors.New(reason)) + } + + return err } - return nil, err + return err } func waitImageVersionCreated(ctx context.Context, conn *sagemaker.Client, name string) (*sagemaker.DescribeImageVersionOutput, error) { @@ -211,6 +224,10 @@ func waitImageVersionCreated(ctx context.Context, conn *sagemaker.Client, name s outputRaw, err := stateConf.WaitForStateContext(ctx) if output, ok := outputRaw.(*sagemaker.DescribeImageVersionOutput); ok { + if status, reason := output.ImageVersionStatus, aws.ToString(output.FailureReason); status == awstypes.ImageVersionStatusCreateFailed && reason != "" { + tfresource.SetLastError(err, errors.New(reason)) + } + return output, err } @@ -228,6 +245,10 @@ func waitImageVersionDeleted(ctx context.Context, conn *sagemaker.Client, name s outputRaw, err := stateConf.WaitForStateContext(ctx) if output, ok := outputRaw.(*sagemaker.DescribeImageVersionOutput); ok { + if status, reason := output.ImageVersionStatus, aws.ToString(output.FailureReason); status == awstypes.ImageVersionStatusDeleteFailed && reason != "" { + tfresource.SetLastError(err, errors.New(reason)) + } + return output, err } @@ -662,3 +683,66 @@ func waitMlflowTrackingServerDeleted(ctx context.Context, conn *sagemaker.Client return nil, err } + +func waitHubInService(ctx context.Context, conn *sagemaker.Client, name string) (*sagemaker.DescribeHubOutput, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.HubStatusCreating), + Target: enum.Slice(awstypes.HubStatusInService), + Refresh: statusHub(ctx, conn, name), + Timeout: hubTimeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*sagemaker.DescribeHubOutput); ok { + if status, reason := output.HubStatus, aws.ToString(output.FailureReason); status == awstypes.HubStatusCreateFailed && reason != "" { + tfresource.SetLastError(err, errors.New(reason)) + } + + return output, err + } + + return nil, err +} + +func waitHubDeleted(ctx context.Context, conn *sagemaker.Client, name string) (*sagemaker.DescribeHubOutput, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.HubStatusDeleting), + Target: []string{}, + Refresh: statusHub(ctx, conn, name), + Timeout: hubTimeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*sagemaker.DescribeHubOutput); ok { + if status, reason := output.HubStatus, aws.ToString(output.FailureReason); status == awstypes.HubStatusDeleteFailed && reason != "" { + tfresource.SetLastError(err, errors.New(reason)) + } + + return output, err + } + + return nil, err +} + +func waitHubUpdated(ctx context.Context, conn *sagemaker.Client, name string) (*sagemaker.DescribeHubOutput, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.HubStatusUpdating), + Target: enum.Slice(awstypes.HubStatusInService), + Refresh: statusHub(ctx, conn, name), + Timeout: hubTimeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*sagemaker.DescribeHubOutput); ok { + if status, reason := output.HubStatus, aws.ToString(output.FailureReason); status == awstypes.HubStatusUpdateFailed && reason != "" { + tfresource.SetLastError(err, errors.New(reason)) + } + + return output, err + } + + return nil, err +} diff --git a/internal/verify/json.go b/internal/verify/json.go index b7fc04b8108..4fedf7832ca 100644 --- a/internal/verify/json.go +++ b/internal/verify/json.go @@ -15,6 +15,7 @@ import ( awspolicy "github.com/hashicorp/awspolicyequivalence" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" + "github.com/hashicorp/terraform-provider-aws/internal/errs" ) // SuppressEquivalentPolicyDiffs returns a difference suppression function that compares @@ -126,6 +127,11 @@ func JSONBytesEqual(b1, b2 []byte) bool { return reflect.DeepEqual(o1, o2) } +// SecondJSONUnlessEquivalent returns the second JSON string unless +// the AWS policy content is deemed equivalent. +// +// If parsing of the policy content from the first argument fails, the +// second is returned and no error is raised. func SecondJSONUnlessEquivalent(old, new string) (string, error) { // valid empty JSON is "{}" not "" so handle special case to avoid // Error unmarshaling policy: unexpected end of JSON input @@ -144,6 +150,17 @@ func SecondJSONUnlessEquivalent(old, new string) (string, error) { equivalent, err := awspolicy.PoliciesAreEquivalent(old, new) if err != nil { + // Plugin SDK V2 based resources can set malformed policy content in state + // despite a failed update. In these cases, parsing the "old" content + // will fail. Surfacing this error during read operations causes a + // persistent plan-time validation error, so return the "new" content + // read directly from the remote resource instead. + // + // Ref: https://github.com/hashicorp/terraform-provider-aws/issues/39833 + if errs.Contains(err, "parsing policy 1") { + return new, nil + } + return "", err } diff --git a/internal/verify/json_test.go b/internal/verify/json_test.go index b99881a4f09..1034460a8c8 100644 --- a/internal/verify/json_test.go +++ b/internal/verify/json_test.go @@ -354,6 +354,48 @@ func TestSecondJSONUnlessEquivalent(t *testing.T) { newPolicy: "", want: "", }, + { + name: "malformed old", + oldPolicy: `{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "s3:ListBucket" + ], + "Condition" : { + "StringLike" : ["demo-prefix/"] + }, + "Resource": "*" + } + ] +}`, + newPolicy: `{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "s3:ListBucket" + ], + "Resource": "*" + } + ] +}`, + want: `{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "s3:ListBucket" + ], + "Resource": "*" + } + ] +}`, + }, } for _, v := range testCases { diff --git a/tools/tfsdk2fw/go.mod b/tools/tfsdk2fw/go.mod index 4cce3bc07a6..9a95aa895bf 100644 --- a/tools/tfsdk2fw/go.mod +++ b/tools/tfsdk2fw/go.mod @@ -42,7 +42,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/appflow v1.45.3 // indirect github.com/aws/aws-sdk-go-v2/service/appintegrations v1.30.2 // indirect github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.33.2 // indirect - github.com/aws/aws-sdk-go-v2/service/applicationinsights v1.28.2 // indirect + github.com/aws/aws-sdk-go-v2/service/applicationinsights v1.29.0 // indirect github.com/aws/aws-sdk-go-v2/service/applicationsignals v1.6.2 // indirect github.com/aws/aws-sdk-go-v2/service/appmesh v1.29.2 // indirect github.com/aws/aws-sdk-go-v2/service/apprunner v1.32.2 // indirect @@ -50,7 +50,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/appsync v1.38.2 // indirect github.com/aws/aws-sdk-go-v2/service/athena v1.48.0 // indirect github.com/aws/aws-sdk-go-v2/service/auditmanager v1.37.2 // indirect - github.com/aws/aws-sdk-go-v2/service/autoscaling v1.45.2 // indirect + github.com/aws/aws-sdk-go-v2/service/autoscaling v1.46.0 // indirect github.com/aws/aws-sdk-go-v2/service/autoscalingplans v1.24.2 // indirect github.com/aws/aws-sdk-go-v2/service/backup v1.39.3 // indirect github.com/aws/aws-sdk-go-v2/service/batch v1.46.2 // indirect @@ -91,12 +91,12 @@ require ( github.com/aws/aws-sdk-go-v2/service/configservice v1.50.2 // indirect github.com/aws/aws-sdk-go-v2/service/connect v1.113.2 // indirect github.com/aws/aws-sdk-go-v2/service/connectcases v1.21.2 // indirect - github.com/aws/aws-sdk-go-v2/service/controltower v1.18.2 // indirect + github.com/aws/aws-sdk-go-v2/service/controltower v1.18.3 // indirect github.com/aws/aws-sdk-go-v2/service/costandusagereportservice v1.28.2 // indirect github.com/aws/aws-sdk-go-v2/service/costexplorer v1.43.2 // indirect github.com/aws/aws-sdk-go-v2/service/costoptimizationhub v1.10.2 // indirect github.com/aws/aws-sdk-go-v2/service/customerprofiles v1.42.2 // indirect - github.com/aws/aws-sdk-go-v2/service/databasemigrationservice v1.43.0 // indirect + github.com/aws/aws-sdk-go-v2/service/databasemigrationservice v1.44.0 // indirect github.com/aws/aws-sdk-go-v2/service/databrew v1.33.2 // indirect github.com/aws/aws-sdk-go-v2/service/dataexchange v1.33.0 // indirect github.com/aws/aws-sdk-go-v2/service/datapipeline v1.25.2 // indirect @@ -113,12 +113,12 @@ require ( github.com/aws/aws-sdk-go-v2/service/docdbelastic v1.13.2 // indirect github.com/aws/aws-sdk-go-v2/service/drs v1.30.2 // indirect github.com/aws/aws-sdk-go-v2/service/dynamodb v1.36.2 // indirect - github.com/aws/aws-sdk-go-v2/service/ec2 v1.183.0 // indirect + github.com/aws/aws-sdk-go-v2/service/ec2 v1.184.0 // indirect github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2 // indirect github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.27.2 // indirect github.com/aws/aws-sdk-go-v2/service/ecs v1.47.4 // indirect github.com/aws/aws-sdk-go-v2/service/efs v1.33.2 // indirect - github.com/aws/aws-sdk-go-v2/service/eks v1.50.2 // indirect + github.com/aws/aws-sdk-go-v2/service/eks v1.51.0 // indirect github.com/aws/aws-sdk-go-v2/service/elasticache v1.43.0 // indirect github.com/aws/aws-sdk-go-v2/service/elasticbeanstalk v1.28.2 // indirect github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.28.2 // indirect @@ -133,7 +133,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/finspace v1.28.2 // indirect github.com/aws/aws-sdk-go-v2/service/firehose v1.34.2 // indirect github.com/aws/aws-sdk-go-v2/service/fis v1.30.2 // indirect - github.com/aws/aws-sdk-go-v2/service/fms v1.37.2 // indirect + github.com/aws/aws-sdk-go-v2/service/fms v1.38.0 // indirect github.com/aws/aws-sdk-go-v2/service/fsx v1.49.2 // indirect github.com/aws/aws-sdk-go-v2/service/gamelift v1.36.2 // indirect github.com/aws/aws-sdk-go-v2/service/glacier v1.26.2 // indirect @@ -146,7 +146,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/healthlake v1.28.2 // indirect github.com/aws/aws-sdk-go-v2/service/iam v1.37.2 // indirect github.com/aws/aws-sdk-go-v2/service/identitystore v1.27.2 // indirect - github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.37.3 // indirect + github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.38.0 // indirect github.com/aws/aws-sdk-go-v2/service/inspector v1.25.2 // indirect github.com/aws/aws-sdk-go-v2/service/inspector2 v1.32.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect @@ -178,7 +178,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.2 // indirect github.com/aws/aws-sdk-go-v2/service/location v1.42.2 // indirect github.com/aws/aws-sdk-go-v2/service/lookoutmetrics v1.31.2 // indirect - github.com/aws/aws-sdk-go-v2/service/m2 v1.17.2 // indirect + github.com/aws/aws-sdk-go-v2/service/m2 v1.18.0 // indirect github.com/aws/aws-sdk-go-v2/service/macie2 v1.43.2 // indirect github.com/aws/aws-sdk-go-v2/service/mediaconnect v1.35.2 // indirect github.com/aws/aws-sdk-go-v2/service/mediaconvert v1.61.2 // indirect @@ -214,7 +214,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/quicksight v1.77.0 // indirect github.com/aws/aws-sdk-go-v2/service/ram v1.29.2 // indirect github.com/aws/aws-sdk-go-v2/service/rbin v1.20.2 // indirect - github.com/aws/aws-sdk-go-v2/service/rds v1.87.3 // indirect + github.com/aws/aws-sdk-go-v2/service/rds v1.88.0 // indirect github.com/aws/aws-sdk-go-v2/service/redshift v1.50.0 // indirect github.com/aws/aws-sdk-go-v2/service/redshiftdata v1.30.2 // indirect github.com/aws/aws-sdk-go-v2/service/redshiftserverless v1.23.2 // indirect @@ -272,7 +272,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/vpclattice v1.12.2 // indirect github.com/aws/aws-sdk-go-v2/service/waf v1.25.2 // indirect github.com/aws/aws-sdk-go-v2/service/wafregional v1.25.2 // indirect - github.com/aws/aws-sdk-go-v2/service/wafv2 v1.54.2 // indirect + github.com/aws/aws-sdk-go-v2/service/wafv2 v1.55.0 // indirect github.com/aws/aws-sdk-go-v2/service/wellarchitected v1.34.2 // indirect github.com/aws/aws-sdk-go-v2/service/worklink v1.23.2 // indirect github.com/aws/aws-sdk-go-v2/service/workspaces v1.48.2 // indirect diff --git a/tools/tfsdk2fw/go.sum b/tools/tfsdk2fw/go.sum index 3a0f56a6501..069e67cd8d6 100644 --- a/tools/tfsdk2fw/go.sum +++ b/tools/tfsdk2fw/go.sum @@ -70,8 +70,8 @@ github.com/aws/aws-sdk-go-v2/service/appintegrations v1.30.2 h1:r/++KdfZx8Woi3pI github.com/aws/aws-sdk-go-v2/service/appintegrations v1.30.2/go.mod h1:bcwWgN13kk3Y7iq5stSA+joIygO0/QK7d18ohTEF188= github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.33.2 h1:douQmMbPFGG5AxFDoWdImN3T+8N1z32hkQXPIL7NKGs= github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.33.2/go.mod h1:yg5Zb3dlzZMV7FsmL5fI82xPgUa62sQP4MeQwTS3NRk= -github.com/aws/aws-sdk-go-v2/service/applicationinsights v1.28.2 h1:gOn6bFXWTsGHSBMdMUU5hwmZzop650HoYPq0JSqM+so= -github.com/aws/aws-sdk-go-v2/service/applicationinsights v1.28.2/go.mod h1:6igoPKTMLmlS5UzkIaVsJYLXQjSQAVs75HiY2xdJyKI= +github.com/aws/aws-sdk-go-v2/service/applicationinsights v1.29.0 h1:eI1i6YZDDwAd+wN2v40yWwzRd7DMVPO3227233q9vgA= +github.com/aws/aws-sdk-go-v2/service/applicationinsights v1.29.0/go.mod h1:6igoPKTMLmlS5UzkIaVsJYLXQjSQAVs75HiY2xdJyKI= github.com/aws/aws-sdk-go-v2/service/applicationsignals v1.6.2 h1:VhZVJ0HiiWieoahI0LC+/rrAkgwPpuWw37HlVxs8OrM= github.com/aws/aws-sdk-go-v2/service/applicationsignals v1.6.2/go.mod h1:GzJ9Fze7pxMh9r8wIjFWWDpR7LidenXT5/xEAlZjBkQ= github.com/aws/aws-sdk-go-v2/service/appmesh v1.29.2 h1:YWAxOgkdNG0+K25He+mU3QnxoB7HAGoqDOSLkIA4Ix8= @@ -86,8 +86,8 @@ github.com/aws/aws-sdk-go-v2/service/athena v1.48.0 h1:oGuR6wuok/T96ISXwQZ64jA83 github.com/aws/aws-sdk-go-v2/service/athena v1.48.0/go.mod h1:bmGCoIiNCRnfKa6T64nCTKrWOtw362tPUKDpq/fjMDU= github.com/aws/aws-sdk-go-v2/service/auditmanager v1.37.2 h1:n6IT2Q6DJg+540tWwIBnJHMrO3Vl5nu+HafGNNo077c= github.com/aws/aws-sdk-go-v2/service/auditmanager v1.37.2/go.mod h1:IZovD0WWpMx5oFy87RwgPPQcnMCQsiFHir7C9SCV50E= -github.com/aws/aws-sdk-go-v2/service/autoscaling v1.45.2 h1:NurelK9YO5OB0HlIUj1ycEZJIFNvkuG5iJjo7/r6AJ4= -github.com/aws/aws-sdk-go-v2/service/autoscaling v1.45.2/go.mod h1:YmWinWbpoVdOgnBZQFeZJ2l4kT97lvnRlTvX2zyyBfc= +github.com/aws/aws-sdk-go-v2/service/autoscaling v1.46.0 h1:HQ0OvPxqTh2mYKRx4BappkCeLBU+E6oWAKSJ5JpP03c= +github.com/aws/aws-sdk-go-v2/service/autoscaling v1.46.0/go.mod h1:YmWinWbpoVdOgnBZQFeZJ2l4kT97lvnRlTvX2zyyBfc= github.com/aws/aws-sdk-go-v2/service/autoscalingplans v1.24.2 h1:adYgaCwIbN816Nxyjhfh4P+YkbgoWX5u/+195qOeDH8= github.com/aws/aws-sdk-go-v2/service/autoscalingplans v1.24.2/go.mod h1:FJIWPT79C+J0TC0svBOcVZi3agyv/t8ej+nZekrTItU= github.com/aws/aws-sdk-go-v2/service/backup v1.39.3 h1:mjYDN7CPl0CHrTBe36EKqAAGGXAKy8MXuNF+2qHkVdM= @@ -168,8 +168,8 @@ github.com/aws/aws-sdk-go-v2/service/connect v1.113.2 h1:h45hg2xWdzhr+eFn7aqfg26 github.com/aws/aws-sdk-go-v2/service/connect v1.113.2/go.mod h1:Cr/NBm+YdRHJg0uHNTIDEF++8IQL207oUsjhTAyS4zE= github.com/aws/aws-sdk-go-v2/service/connectcases v1.21.2 h1:BtBIsvj3F0LrsDoLPSZO9qanyXYoDFmXCNJ3I+X80XI= github.com/aws/aws-sdk-go-v2/service/connectcases v1.21.2/go.mod h1:ZrU0ZA81zcKXOUnC8rvjYddEFB3pl7t9tYCdtIRFAb4= -github.com/aws/aws-sdk-go-v2/service/controltower v1.18.2 h1:IZi4ivxDbOAfCZvEeNhvYwrBANiYF9RE35yczZb4f0Y= -github.com/aws/aws-sdk-go-v2/service/controltower v1.18.2/go.mod h1:15mOn+tmGhx9+TidoQaa8gSBItjFx/A5XT9GV1L+LPc= +github.com/aws/aws-sdk-go-v2/service/controltower v1.18.3 h1:v4aBi2Jw4vGmLv58u5YZduJbbErX8a0ML1+RRWI7Sko= +github.com/aws/aws-sdk-go-v2/service/controltower v1.18.3/go.mod h1:15mOn+tmGhx9+TidoQaa8gSBItjFx/A5XT9GV1L+LPc= github.com/aws/aws-sdk-go-v2/service/costandusagereportservice v1.28.2 h1:i7yTib7O8olbSmTyW2c5C2asN6GUfMI2RLmdKsc/3e4= github.com/aws/aws-sdk-go-v2/service/costandusagereportservice v1.28.2/go.mod h1:2Ux5wul+bF0/JIAATf1cv3PWJatZqi/dFt7bRSeYEcU= github.com/aws/aws-sdk-go-v2/service/costexplorer v1.43.2 h1:Tg7kuCaHMCFOW+HhK/maIH7qtkeDbzahqQOUO9c+/L8= @@ -178,8 +178,8 @@ github.com/aws/aws-sdk-go-v2/service/costoptimizationhub v1.10.2 h1:3MtTruznyGml github.com/aws/aws-sdk-go-v2/service/costoptimizationhub v1.10.2/go.mod h1:khyqDEa/5mT22FwodCCtZN4n+9C62VXwqNiPeAkqYlY= github.com/aws/aws-sdk-go-v2/service/customerprofiles v1.42.2 h1:dHQXIHPlijJIBUubNpShhIUooNDUKB/B8nJ6OSsJfmQ= github.com/aws/aws-sdk-go-v2/service/customerprofiles v1.42.2/go.mod h1:n9F5nRLYyqBz1khsBptXyceq6ZJG9xqzl2pIyVqYq/8= -github.com/aws/aws-sdk-go-v2/service/databasemigrationservice v1.43.0 h1:jb9QBxC7Xh0cSpprVOtzJtlyVbJrnLs0oAplby2sZ78= -github.com/aws/aws-sdk-go-v2/service/databasemigrationservice v1.43.0/go.mod h1:+JQlP0jMyl8iWGcUcGzTf+qculjIY+08UN7RKiljmps= +github.com/aws/aws-sdk-go-v2/service/databasemigrationservice v1.44.0 h1:u/zESCIEwz9UoEHFZgITa097TkQZWyrqR5bNHZn/Ozw= +github.com/aws/aws-sdk-go-v2/service/databasemigrationservice v1.44.0/go.mod h1:+JQlP0jMyl8iWGcUcGzTf+qculjIY+08UN7RKiljmps= github.com/aws/aws-sdk-go-v2/service/databrew v1.33.2 h1:M18nqFfXtAgxfGoUaaeJS7x8vfZum5SeGq1+5Iu0DLQ= github.com/aws/aws-sdk-go-v2/service/databrew v1.33.2/go.mod h1:CVFxoBbEUNT6MbNBrDAv2rAZJSuYYLUssU9pyXVQbEc= github.com/aws/aws-sdk-go-v2/service/dataexchange v1.33.0 h1:k6mEvId7/e7VFQHDYERVSIINa/b+9rTs8kASzsS5aQ0= @@ -212,8 +212,8 @@ github.com/aws/aws-sdk-go-v2/service/drs v1.30.2 h1:x3Npo3YynC7zY+2NBo7BGpyJ2FQ6 github.com/aws/aws-sdk-go-v2/service/drs v1.30.2/go.mod h1:J+ZP4mpSfSaWNFekoJULiHE2yZBzjYRMtWSlQhjZUQY= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.36.2 h1:kJqyYcGqhWFmXqjRrtFFD4Oc9FXiskhsll2xnlpe8Do= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.36.2/go.mod h1:+t2Zc5VNOzhaWzpGE+cEYZADsgAAQT5v55AO+fhU+2s= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.183.0 h1:LgwYvo4kycfT/UD7vjQhSVZSatxHAI41/54q9O6jljI= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.183.0/go.mod h1:kYXaB4FzyhEJjvrJ84oPnMElLiEAjGxxUunVW2tBSng= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.184.0 h1:SZnuDlml1uFv5ojh+QTxS+Yru89Hr3QYIUwWoY71frI= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.184.0/go.mod h1:kYXaB4FzyhEJjvrJ84oPnMElLiEAjGxxUunVW2tBSng= github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2 h1:VDQaVwGOokbd3VUbHF+wupiffdrbAZPdQnr5XZMJqrs= github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2/go.mod h1:lvUlMghKYmSxSfv0vU7pdU/8jSY+s0zpG8xXhaGKCw0= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.27.2 h1:Zru9Iy2JPM5+uRnFnoqeOZzi8JIVIHJ0ua6JdeDHcyg= @@ -222,8 +222,8 @@ github.com/aws/aws-sdk-go-v2/service/ecs v1.47.4 h1:CTkPGE8fiElvLtYWl/U+Eu5+1fVX github.com/aws/aws-sdk-go-v2/service/ecs v1.47.4/go.mod h1:sMFLFhL27cKYa/eQYZp4asvIwHsnJWrAzTUpy9AQdnU= github.com/aws/aws-sdk-go-v2/service/efs v1.33.2 h1:ePgaKeaN4kXAqzxvDsSilIIq+jftioJb4KXRobIM6ko= github.com/aws/aws-sdk-go-v2/service/efs v1.33.2/go.mod h1:ZEnNWIxtJp/3H1pPE+IZE4exGTJkAdoGfpM5m5/evxo= -github.com/aws/aws-sdk-go-v2/service/eks v1.50.2 h1:vL3RqZ4x6uqpKswp5gB0KyGcsrgkUdKmLTXCDHDUUZw= -github.com/aws/aws-sdk-go-v2/service/eks v1.50.2/go.mod h1:oaPCqTzAe8C5RQZJGRD4RENcV7A4n99uGxbD4rULbNg= +github.com/aws/aws-sdk-go-v2/service/eks v1.51.0 h1:BYyB+byjQ7oyupe3v+YjTp1yfmfNEwChYA2naCc85xI= +github.com/aws/aws-sdk-go-v2/service/eks v1.51.0/go.mod h1:oaPCqTzAe8C5RQZJGRD4RENcV7A4n99uGxbD4rULbNg= github.com/aws/aws-sdk-go-v2/service/elasticache v1.43.0 h1:rebYexCOCdOpR1zU1ObUIn+or4pS38W1IVQGKzZcSos= github.com/aws/aws-sdk-go-v2/service/elasticache v1.43.0/go.mod h1:pfx/wDobZvEpxE96P1i0nHbTYEJMCCMv3uMk7UPvdsE= github.com/aws/aws-sdk-go-v2/service/elasticbeanstalk v1.28.2 h1:La4LqsDHSMdCKoYzKs5b20wSYHfvc6xFBb8OAPDFuGg= @@ -252,8 +252,8 @@ github.com/aws/aws-sdk-go-v2/service/firehose v1.34.2 h1:9uMAwYszi6P6FTbUVq1X6MM github.com/aws/aws-sdk-go-v2/service/firehose v1.34.2/go.mod h1:yGI4W+5bau/K+JNUGepk4lwqLPt77Le6bLL8XrmKAMY= github.com/aws/aws-sdk-go-v2/service/fis v1.30.2 h1:qw7ZkSCy0akQJbJdIgRQaqXEHe7PrA3DHvE4VvemFJw= github.com/aws/aws-sdk-go-v2/service/fis v1.30.2/go.mod h1:CArS66NFuL1fBiSLVfWZV6oQjicsdViLm7Ic9Lte7x4= -github.com/aws/aws-sdk-go-v2/service/fms v1.37.2 h1:TwT5oa4EUI2O97pPSzLQmJH/4R1aqV+ucnMatvksjXE= -github.com/aws/aws-sdk-go-v2/service/fms v1.37.2/go.mod h1:sWvDxD23qcn9nbwvVfCmXOQx2HufC9n76agqoio9pfg= +github.com/aws/aws-sdk-go-v2/service/fms v1.38.0 h1:GcVv0jgW1fXkNkDFYVQTfpkYB+J4UBXXQ69Epx8MzyE= +github.com/aws/aws-sdk-go-v2/service/fms v1.38.0/go.mod h1:sWvDxD23qcn9nbwvVfCmXOQx2HufC9n76agqoio9pfg= github.com/aws/aws-sdk-go-v2/service/fsx v1.49.2 h1:Wc2N860CGBc3GJTqfucK7IvQCtGECZXOrHACyFth3sI= github.com/aws/aws-sdk-go-v2/service/fsx v1.49.2/go.mod h1:sk7ZHm49x4rJzJAfeo1dl43sCL26lZx5/Yx2b2VUJkU= github.com/aws/aws-sdk-go-v2/service/gamelift v1.36.2 h1:YN63wWC/rOHKr2sKdCQ7d8GA1xkWpiAqxDeZEBRxvqg= @@ -278,8 +278,8 @@ github.com/aws/aws-sdk-go-v2/service/iam v1.37.2 h1:E7vCDUFeDN8uOk8Nb2d4E1howWS1 github.com/aws/aws-sdk-go-v2/service/iam v1.37.2/go.mod h1:QzMecFrIFYJ1cyxjlUoIFRzYSDX19gdqYUd0Tyws2J8= github.com/aws/aws-sdk-go-v2/service/identitystore v1.27.2 h1:QSrf6HsounqUtlFAwArhVNHPt3WXmSm0pz7RtojjBdo= github.com/aws/aws-sdk-go-v2/service/identitystore v1.27.2/go.mod h1:PtkL4CXOQy84zudggyFtyJFXCGDRY8igg9Nfo9df1sU= -github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.37.3 h1:Pdh3M/+3nyVJIEqD7UrbiBrtUIcC1uTk/NIrI6M/d3I= -github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.37.3/go.mod h1:hqe5ZWGAMgZC14I2GrXc+tUPpucDfGb94RB/7IOdQ/o= +github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.38.0 h1:+gsGZbypyP+atqTDsrtaKe26TgrONg3z6rLTm5RmIhg= +github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.38.0/go.mod h1:hqe5ZWGAMgZC14I2GrXc+tUPpucDfGb94RB/7IOdQ/o= github.com/aws/aws-sdk-go-v2/service/inspector v1.25.2 h1:xiQ70pTvXs9PFYmewDgeICVxRpiQP+cWQZmunV5uYKk= github.com/aws/aws-sdk-go-v2/service/inspector v1.25.2/go.mod h1:sDcAla3dh7DO6AAdh+29e+rowLaIcw2fxuwNFCIlBuA= github.com/aws/aws-sdk-go-v2/service/inspector2 v1.32.2 h1:D0nDW7y3KLPGShqF7gaKFRswY8ekG8jsfN4r3CWqAjQ= @@ -342,8 +342,8 @@ github.com/aws/aws-sdk-go-v2/service/location v1.42.2 h1:QeEPlMSVn/GtUGVz/G9FBFV github.com/aws/aws-sdk-go-v2/service/location v1.42.2/go.mod h1:nCDXuPUnJLC/PvDh6iXjM/4LIUak0MTzXamdVn9Typk= github.com/aws/aws-sdk-go-v2/service/lookoutmetrics v1.31.2 h1:xq/m847nPu/qjcGWfT5AMmLfmb0Z8RzYmsiPQCnqWRg= github.com/aws/aws-sdk-go-v2/service/lookoutmetrics v1.31.2/go.mod h1:sWTm2Cszymy7WYqCuLvw/Zvzy1t08ceZP6Brpo7COY0= -github.com/aws/aws-sdk-go-v2/service/m2 v1.17.2 h1:2U0/1YSsugrRPnSa9WSPQqBEt5/8ZuTPwYhSFjndIzY= -github.com/aws/aws-sdk-go-v2/service/m2 v1.17.2/go.mod h1:HWB51wlVsdbT1BrpH4BYrgpxhc012ja8je2TTwjzr/0= +github.com/aws/aws-sdk-go-v2/service/m2 v1.18.0 h1:4yncbqHQ5TmL+AElqjSIppgEvCiNgboMDk0FcQ5qSW8= +github.com/aws/aws-sdk-go-v2/service/m2 v1.18.0/go.mod h1:HWB51wlVsdbT1BrpH4BYrgpxhc012ja8je2TTwjzr/0= github.com/aws/aws-sdk-go-v2/service/macie2 v1.43.2 h1:MbR0vRNd7am1So5hcYho+N11dxzhZbB4qdsi+cmcBp0= github.com/aws/aws-sdk-go-v2/service/macie2 v1.43.2/go.mod h1:B2FFzz9qQQ8l3MZV/MjMi4ua9VbqcQK8Huuv6066RZ8= github.com/aws/aws-sdk-go-v2/service/mediaconnect v1.35.2 h1:AibWQdB6x5CTlJ11/C0wp+fAUUKmCFLwU5H4h9kov0c= @@ -414,8 +414,8 @@ github.com/aws/aws-sdk-go-v2/service/ram v1.29.2 h1:MzE8+xBV5omQ8aP7I2XarPS1bORO github.com/aws/aws-sdk-go-v2/service/ram v1.29.2/go.mod h1:/rvsX6270oRGq4zBWQlFAenU4144KSJcaOFrDffHsjk= github.com/aws/aws-sdk-go-v2/service/rbin v1.20.2 h1:U+BnnWf3/yxyTQO5GBXkFO9S2lxJwtEcHskMSNIqqE0= github.com/aws/aws-sdk-go-v2/service/rbin v1.20.2/go.mod h1:B7r89tuqcg/tnDE5rtukNU1irjRhC+S10GjEFIFAj1M= -github.com/aws/aws-sdk-go-v2/service/rds v1.87.3 h1:IA338QOtCFeKTUvhuWkFg0yjjYwFFip4AzTSjcsTGuI= -github.com/aws/aws-sdk-go-v2/service/rds v1.87.3/go.mod h1:KziDa/w2AVz3dfANxwuBV0XqoQjxTKbVQyLNH5BRvO4= +github.com/aws/aws-sdk-go-v2/service/rds v1.88.0 h1:QdpwmIB0ZZN/devOnw+dJSF2VFnmn3LM5kuEKQ0kpj0= +github.com/aws/aws-sdk-go-v2/service/rds v1.88.0/go.mod h1:KziDa/w2AVz3dfANxwuBV0XqoQjxTKbVQyLNH5BRvO4= github.com/aws/aws-sdk-go-v2/service/redshift v1.50.0 h1:AT056ID/JR3pCAhwpak11ATQ6wRtaQpFqoJYjoEG7aM= github.com/aws/aws-sdk-go-v2/service/redshift v1.50.0/go.mod h1:LuUSvbRK6lNleFaeXOm3gJxnnau2qoZd1wPU7DwjS4w= github.com/aws/aws-sdk-go-v2/service/redshiftdata v1.30.2 h1:nghfgMfAqF3xl783g5Ig0UZafPMNAHm+Bx8G2l63aEg= @@ -530,8 +530,8 @@ github.com/aws/aws-sdk-go-v2/service/waf v1.25.2 h1:mGMfHPEXIR1G2mRJ5BGQnksDTt7J github.com/aws/aws-sdk-go-v2/service/waf v1.25.2/go.mod h1:g7m/OfmrD3MK7JHjJ/NBxMy87Mffp1KhNhQIKskMp2U= github.com/aws/aws-sdk-go-v2/service/wafregional v1.25.2 h1:PMBOZsPm34AE5+0FUkBD94hoEdAsRiVNO+V1NcVstWk= github.com/aws/aws-sdk-go-v2/service/wafregional v1.25.2/go.mod h1:Zw9xd/Zg0wMX3V57Pi/KAiQdqXg9ikfdEd0IK83v9B0= -github.com/aws/aws-sdk-go-v2/service/wafv2 v1.54.2 h1:SXwBXwm13cbQKjV3nt7Fkkxs/blH3lrbV9aKdjT+Zmk= -github.com/aws/aws-sdk-go-v2/service/wafv2 v1.54.2/go.mod h1:0omlXhQY21zKfGkdIfufpV7kLt564XtjQcywixaNXrM= +github.com/aws/aws-sdk-go-v2/service/wafv2 v1.55.0 h1:sS3SDk8EugdW+KW56VR6qcEg8JpC8pNLVICVBr8mMb4= +github.com/aws/aws-sdk-go-v2/service/wafv2 v1.55.0/go.mod h1:0omlXhQY21zKfGkdIfufpV7kLt564XtjQcywixaNXrM= github.com/aws/aws-sdk-go-v2/service/wellarchitected v1.34.2 h1:uhOu5pbceq96a/0nWtf/2Drt/M9hh94ic5d4LaEdFzE= github.com/aws/aws-sdk-go-v2/service/wellarchitected v1.34.2/go.mod h1:VJNJ9aES48jXBIc74SZnP0KmQr6Fku2eYHSV8854qpc= github.com/aws/aws-sdk-go-v2/service/worklink v1.23.2 h1:VN3Qydtdl3UlJRHVxQxSP1d8I5gtvT5zdaCCAfZST7Y= diff --git a/website/docs/d/elasticache_reserved_cache_node_offering.html.markdown b/website/docs/d/elasticache_reserved_cache_node_offering.html.markdown index d9a9af4fba1..f69ea6c998b 100644 --- a/website/docs/d/elasticache_reserved_cache_node_offering.html.markdown +++ b/website/docs/d/elasticache_reserved_cache_node_offering.html.markdown @@ -35,7 +35,7 @@ The following arguments are supported: For other current generation nodes (i.e. T2, M3, M4, R3, or R4) the only valid value is `Heavy Utilization`. For previous generation modes (i.e. T1, M1, M2, or C1) valid values are `Heavy Utilization`, `Medium Utilization`, and `Light Utilization`. * `product_description` - (Required) Engine type for the reserved cache node. - Valid values are `redis` and `memcached`. + Valid values are `redis`, `valkey` and `memcached`. ## Attribute Reference diff --git a/website/docs/d/elasticache_serverless_cache.html.markdown b/website/docs/d/elasticache_serverless_cache.html.markdown index 28b3842de35..16f38e1e7a5 100644 --- a/website/docs/d/elasticache_serverless_cache.html.markdown +++ b/website/docs/d/elasticache_serverless_cache.html.markdown @@ -31,7 +31,7 @@ This data source exports the following attributes in addition to the arguments a * `arn` - The Amazon Resource Name (ARN) of the serverless cache. * `cache_usage_limits` - The cache usage limits for storage and ElastiCache Processing Units for the cache. See [`cache_usage_limits` Block](#cache_usage_limits-block) for details. * `create_time` - Timestamp of when the serverless cache was created. -* `daily_snapshot_time` - The daily time that snapshots will be created from the new serverless cache. Only available for engine type `"redis"`. +* `daily_snapshot_time` - The daily time that snapshots will be created from the new serverless cache. Only available for engine types `"redis"` and `"valkey"`. * `description` - Description of the serverless cache. * `endpoint` - Represents the information required for client programs to connect to the cache. See [`endpoint` Block](#endpoint-block) for details. * `engine` – Name of the cache engine. diff --git a/website/docs/d/lakeformation_data_lake_settings.html.markdown b/website/docs/d/lakeformation_data_lake_settings.html.markdown index 9a90d7e4938..c8023f2d641 100644 --- a/website/docs/d/lakeformation_data_lake_settings.html.markdown +++ b/website/docs/d/lakeformation_data_lake_settings.html.markdown @@ -29,14 +29,15 @@ The following arguments are optional: This data source exports the following attributes in addition to the arguments above: * `admins` – List of ARNs of AWS Lake Formation principals (IAM users or roles). -* `read_only_admins` – List of ARNs of AWS Lake Formation principals (IAM users or roles) with only view access to the resources. +* `allow_external_data_filtering` - Whether to allow Amazon EMR clusters to access data managed by Lake Formation. +* `allow_full_table_external_data_access` - Whether to allow a third-party query engine to get data access credentials without session tags when a caller has full data access permissions. +* `authorized_session_tag_value_list` - Lake Formation relies on a privileged process secured by Amazon EMR or the third party integrator to tag the user's role while assuming it. * `create_database_default_permissions` - Up to three configuration blocks of principal permissions for default create database permissions. Detailed below. * `create_table_default_permissions` - Up to three configuration blocks of principal permissions for default create table permissions. Detailed below. -* `trusted_resource_owners` – List of the resource-owning account IDs that the caller's account can use to share their user access details (user ARNs). -* `allow_external_data_filtering` - Whether to allow Amazon EMR clusters to access data managed by Lake Formation. * `external_data_filtering_allow_list` - A list of the account IDs of Amazon Web Services accounts with Amazon EMR clusters that are to perform data filtering. -* `authorized_session_tag_value_list` - Lake Formation relies on a privileged process secured by Amazon EMR or the third party integrator to tag the user's role while assuming it. -* `allow_full_table_external_data_access` - Whether to allow a third-party query engine to get data access credentials without session tags when a caller has full data access permissions. +* `parameters` - Key-value map of additional configuration. `CROSS_ACCOUNT_VERSION` will be set to values `"1"`, `"2"`, `"3"`, or `"4"`. `SET_CONTEXT` will also be returned with a value of `TRUE`. In a fresh account, prior to configuring, `CROSS_ACCOUNT_VERSION` is `"1"`. +* `read_only_admins` – List of ARNs of AWS Lake Formation principals (IAM users or roles) with only view access to the resources. +* `trusted_resource_owners` – List of the resource-owning account IDs that the caller's account can use to share their user access details (user ARNs). ### create_database_default_permissions diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 267cad78eb9..d48800489db 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -11,7 +11,7 @@ Use the Amazon Web Services (AWS) provider to interact with the many resources supported by AWS. You must configure the provider with the proper credentials before you can use it. -Use the navigation to the left to read about the available resources. There are currently 1439 resources and 588 data sources available in the provider. +Use the navigation to the left to read about the available resources. There are currently 1441 resources and 588 data sources available in the provider. To learn the basics of Terraform using this provider, follow the hands-on [get started tutorials](https://learn.hashicorp.com/tutorials/terraform/infrastructure-as-code?in=terraform/aws-get-started&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS). Interact with AWS services, diff --git a/website/docs/r/elasticache_cluster.html.markdown b/website/docs/r/elasticache_cluster.html.markdown index 2068bac4fa2..605b8544453 100644 --- a/website/docs/r/elasticache_cluster.html.markdown +++ b/website/docs/r/elasticache_cluster.html.markdown @@ -140,7 +140,7 @@ resource "aws_elasticache_cluster" "example" { The following arguments are required: * `cluster_id` – (Required) Group identifier. ElastiCache converts this name to lowercase. Changing this value will re-create the resource. -* `engine` – (Optional, Required if `replication_group_id` is not specified) Name of the cache engine to be used for this cache cluster. Valid values are `memcached` or `redis`. +* `engine` – (Optional, Required if `replication_group_id` is not specified) Name of the cache engine to be used for this cache cluster. Valid values are `memcached` and `redis`. * `node_type` – (Required unless `replication_group_id` is provided) The instance class used. See AWS documentation for information on [supported node types for Redis](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/CacheNodes.SupportedTypes.html) and [guidance on selecting node types for Redis](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/nodes-select-size.html). See AWS documentation for information on [supported node types for Memcached](https://docs.aws.amazon.com/AmazonElastiCache/latest/mem-ug/CacheNodes.SupportedTypes.html) and [guidance on selecting node types for Memcached](https://docs.aws.amazon.com/AmazonElastiCache/latest/mem-ug/nodes-select-size.html). diff --git a/website/docs/r/elasticache_global_replication_group.html.markdown b/website/docs/r/elasticache_global_replication_group.html.markdown index bd5bf2e48a1..2ec8c009c61 100644 --- a/website/docs/r/elasticache_global_replication_group.html.markdown +++ b/website/docs/r/elasticache_global_replication_group.html.markdown @@ -44,7 +44,7 @@ resource "aws_elasticache_replication_group" "secondary" { } ``` -### Managing Redis Engine Versions +### Managing Redis OOS/Valkey Engine Versions The initial Redis version is determined by the version set on the primary replication group. However, once it is part of a Global Replication Group, diff --git a/website/docs/r/elasticache_replication_group.html.markdown b/website/docs/r/elasticache_replication_group.html.markdown index 47d94a7b4be..432ec5aa091 100644 --- a/website/docs/r/elasticache_replication_group.html.markdown +++ b/website/docs/r/elasticache_replication_group.html.markdown @@ -31,7 +31,7 @@ for more information. ## Example Usage -### Redis Cluster Mode Disabled +### Redis OSS/Valkey Cluster Mode Disabled To create a single shard primary with single read replica: @@ -77,7 +77,7 @@ resource "aws_elasticache_cluster" "replica" { } ``` -### Redis Cluster Mode Enabled +### Redis OSS/Valkey Cluster Mode Enabled To create two shards with a primary and a single read replica each: @@ -198,12 +198,12 @@ The following arguments are optional: * `auth_token` - (Optional) Password used to access a password protected server. Can be specified only if `transit_encryption_enabled = true`. * `auth_token_update_strategy` - (Optional) Strategy to use when updating the `auth_token`. Valid values are `SET`, `ROTATE`, and `DELETE`. Defaults to `ROTATE`. * `auto_minor_version_upgrade` - (Optional) Specifies whether minor version engine upgrades will be applied automatically to the underlying Cache Cluster instances during the maintenance window. - Only supported for engine type `"redis"` and if the engine version is 6 or higher. + Only supported for engine types `"redis"` and `"valkey"` and if the engine version is 6 or higher. Defaults to `true`. * `automatic_failover_enabled` - (Optional) Specifies whether a read-only replica will be automatically promoted to read/write primary if the existing primary fails. If enabled, `num_cache_clusters` must be greater than 1. Must be enabled for Redis (cluster mode enabled) replication groups. Defaults to `false`. * `cluster_mode` - (Optional) Specifies whether cluster mode is enabled or disabled. Valid values are `enabled` or `disabled` or `compatible` * `data_tiering_enabled` - (Optional) Enables data tiering. Data tiering is only supported for replication groups using the r6gd node type. This parameter must be set to `true` when using r6gd nodes. -* `engine` - (Optional) Name of the cache engine to be used for the clusters in this replication group. The only valid value is `redis`. +* `engine` - (Optional) Name of the cache engine to be used for the clusters in this replication group. Valid values are `redis` or `valkey`. * `engine_version` - (Optional) Version number of the cache engine to be used for the cache clusters in this replication group. If the version is 7 or higher, the major and minor version should be set, e.g., `7.2`. If the version is 6, the major and minor version can be set, e.g., `6.2`, @@ -214,7 +214,7 @@ The following arguments are optional: * `global_replication_group_id` - (Optional) The ID of the global replication group to which this replication group should belong. If this parameter is specified, the replication group is added to the specified global replication group as a secondary replication group; otherwise, the replication group is not part of any global replication group. If `global_replication_group_id` is set, the `num_node_groups` parameter cannot be set. * `ip_discovery` - (Optional) The IP version to advertise in the discovery protocol. Valid values are `ipv4` or `ipv6`. * `kms_key_id` - (Optional) The ARN of the key that you wish to use if encrypting at rest. If not supplied, uses service managed encryption. Can be specified only if `at_rest_encryption_enabled = true`. -* `log_delivery_configuration` - (Optional, Redis only) Specifies the destination and format of Redis [SLOWLOG](https://redis.io/commands/slowlog) or Redis [Engine Log](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Log_Delivery.html#Log_contents-engine-log). See the documentation on [Amazon ElastiCache](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Log_Delivery.html#Log_contents-engine-log). See [Log Delivery Configuration](#log-delivery-configuration) below for more details. +* `log_delivery_configuration` - (Optional, Redis only) Specifies the destination and format of Redis OSS/Valkey [SLOWLOG](https://redis.io/commands/slowlog) or Redis OSS/Valkey [Engine Log](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Log_Delivery.html#Log_contents-engine-log). See the documentation on [Amazon ElastiCache](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Log_Delivery.html#Log_contents-engine-log). See [Log Delivery Configuration](#log-delivery-configuration) below for more details. * `maintenance_window` – (Optional) Specifies the weekly time range for when maintenance on the cache cluster is performed. The format is `ddd:hh24:mi-ddd:hh24:mi` (24H Clock UTC). The minimum maintenance window is a 60 minute period. Example: `sun:05:00-sun:09:00` * `multi_az_enabled` - (Optional) Specifies whether to enable Multi-AZ Support for the replication group. If `true`, `automatic_failover_enabled` must also be enabled. @@ -260,7 +260,7 @@ The following arguments are optional: ### Log Delivery Configuration -The `log_delivery_configuration` block allows the streaming of Redis [SLOWLOG](https://redis.io/commands/slowlog) or Redis [Engine Log](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Log_Delivery.html#Log_contents-engine-log) to CloudWatch Logs or Kinesis Data Firehose. Max of 2 blocks. +The `log_delivery_configuration` block allows the streaming of Redis OSS/Valkey [SLOWLOG](https://redis.io/commands/slowlog) or Redis OSS/Valkey [Engine Log](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Log_Delivery.html#Log_contents-engine-log) to CloudWatch Logs or Kinesis Data Firehose. Max of 2 blocks. * `destination` - Name of either the CloudWatch Logs LogGroup or Kinesis Data Firehose resource. * `destination_type` - For CloudWatch Logs use `cloudwatch-logs` or for Kinesis Data Firehose use `kinesis-firehose`. diff --git a/website/docs/r/elasticache_serverless_cache.html.markdown b/website/docs/r/elasticache_serverless_cache.html.markdown index 131b439b5af..9066a96ebc4 100644 --- a/website/docs/r/elasticache_serverless_cache.html.markdown +++ b/website/docs/r/elasticache_serverless_cache.html.markdown @@ -8,7 +8,7 @@ description: |- # Resource: aws_elasticache_serverless_cache -Provides an ElastiCache Serverless Cache resource which manages memcached or redis. +Provides an ElastiCache Serverless Cache resource which manages memcached, redis or valkey. ## Example Usage @@ -35,7 +35,7 @@ resource "aws_elasticache_serverless_cache" "example" { } ``` -### Redis Serverless +### Redis OSS Serverless ```terraform resource "aws_elasticache_serverless_cache" "example" { @@ -60,17 +60,42 @@ resource "aws_elasticache_serverless_cache" "example" { } ``` +### Valkey Serverless + +```terraform +resource "aws_elasticache_serverless_cache" "example" { + engine = "valkey" + name = "example" + cache_usage_limits { + data_storage { + maximum = 10 + unit = "GB" + } + ecpu_per_second { + maximum = 5000 + } + } + daily_snapshot_time = "09:00" + description = "Test Server" + kms_key_id = aws_kms_key.test.arn + major_engine_version = "7" + snapshot_retention_limit = 1 + security_group_ids = [aws_security_group.test.id] + subnet_ids = aws_subnet.test[*].id +} +``` + ## Argument Reference The following arguments are required: -* `engine` – (Required) Name of the cache engine to be used for this cache cluster. Valid values are `memcached` or `redis`. +* `engine` – (Required) Name of the cache engine to be used for this cache cluster. Valid values are `memcached`, `redis` or `valkey`. * `name` – (Required) The Cluster name which serves as a unique identifier to the serverless cache The following arguments are optional: * `cache_usage_limits` - (Optional) Sets the cache usage limits for storage and ElastiCache Processing Units for the cache. See [`cache_usage_limits` Block](#cache_usage_limits-block) for details. -* `daily_snapshot_time` - (Optional) The daily time that snapshots will be created from the new serverless cache. Only supported for engine type `"redis"`. Defaults to `0`. +* `daily_snapshot_time` - (Optional) The daily time that snapshots will be created from the new serverless cache. Only supported for engine types `"redis"` or `"valkey"`. Defaults to `0`. * `description` - (Optional) User-provided description for the serverless cache. The default is NULL. * `kms_key_id` - (Optional) ARN of the customer managed key for encrypting the data at rest. If no KMS key is provided, a default service key is used. * `major_engine_version` – (Optional) The version of the cache engine that will be used to create the serverless cache. diff --git a/website/docs/r/lakeformation_data_lake_settings.html.markdown b/website/docs/r/lakeformation_data_lake_settings.html.markdown index ed601b0af57..1e33376c51c 100644 --- a/website/docs/r/lakeformation_data_lake_settings.html.markdown +++ b/website/docs/r/lakeformation_data_lake_settings.html.markdown @@ -63,22 +63,33 @@ resource "aws_lakeformation_data_lake_settings" "example" { } ``` +### Change Cross Account Version + +```terraform +resource "aws_lakeformation_data_lake_settings" "example" { + parameters = { + "CROSS_ACCOUNT_VERSION" = "3" + } +} +``` + ## Argument Reference The following arguments are optional: * `admins` – (Optional) Set of ARNs of AWS Lake Formation principals (IAM users or roles). -* `read_only_admins` – (Optional) Set of ARNs of AWS Lake Formation principals (IAM users or roles) with only view access to the resources. +* `allow_external_data_filtering` - (Optional) Whether to allow Amazon EMR clusters to access data managed by Lake Formation. +* `allow_full_table_external_data_access` - (Optional) Whether to allow a third-party query engine to get data access credentials without session tags when a caller has full data access permissions. +* `authorized_session_tag_value_list` - (Optional) Lake Formation relies on a privileged process secured by Amazon EMR or the third party integrator to tag the user's role while assuming it. * `catalog_id` – (Optional) Identifier for the Data Catalog. By default, the account ID. * `create_database_default_permissions` - (Optional) Up to three configuration blocks of principal permissions for default create database permissions. Detailed below. * `create_table_default_permissions` - (Optional) Up to three configuration blocks of principal permissions for default create table permissions. Detailed below. -* `trusted_resource_owners` – (Optional) List of the resource-owning account IDs that the caller's account can use to share their user access details (user ARNs). -* `allow_external_data_filtering` - (Optional) Whether to allow Amazon EMR clusters to access data managed by Lake Formation. * `external_data_filtering_allow_list` - (Optional) A list of the account IDs of Amazon Web Services accounts with Amazon EMR clusters that are to perform data filtering. -* `authorized_session_tag_value_list` - (Optional) Lake Formation relies on a privileged process secured by Amazon EMR or the third party integrator to tag the user's role while assuming it. -* `allow_full_table_external_data_access` - (Optional) Whether to allow a third-party query engine to get data access credentials without session tags when a caller has full data access permissions. +* `parameters` - Key-value map of additional configuration. Valid values for the `CROSS_ACCOUNT_VERSION` key are `"1"`, `"2"`, `"3"`, or `"4"`. `SET_CONTEXT` is also returned with a value of `TRUE`. In a fresh account, prior to configuring, `CROSS_ACCOUNT_VERSION` is `"1"`. Destroying this resource sets the `CROSS_ACCOUNT_VERSION` to `"1"`. +* `read_only_admins` – (Optional) Set of ARNs of AWS Lake Formation principals (IAM users or roles) with only view access to the resources. +* `trusted_resource_owners` – (Optional) List of the resource-owning account IDs that the caller's account can use to share their user access details (user ARNs). -~> **NOTE:** Although optional, not including `admins`, `create_database_default_permissions`, `create_table_default_permissions`, and/or `trusted_resource_owners` results in the setting being cleared. +~> **NOTE:** Although optional, not including `admins`, `create_database_default_permissions`, `create_table_default_permissions`, `parameters`, and/or `trusted_resource_owners` results in the setting being cleared. ### create_database_default_permissions diff --git a/website/docs/r/lb.html.markdown b/website/docs/r/lb.html.markdown index d4811f663fb..2999e100e5d 100644 --- a/website/docs/r/lb.html.markdown +++ b/website/docs/r/lb.html.markdown @@ -114,6 +114,7 @@ This resource supports the following arguments: * `enable_tls_version_and_cipher_suite_headers` - (Optional) Whether the two headers (`x-amzn-tls-version` and `x-amzn-tls-cipher-suite`), which contain information about the negotiated TLS version and cipher suite, are added to the client request before sending it to the target. Only valid for Load Balancers of type `application`. Defaults to `false` * `enable_xff_client_port` - (Optional) Whether the X-Forwarded-For header should preserve the source port that the client used to connect to the load balancer in `application` load balancers. Defaults to `false`. * `enable_waf_fail_open` - (Optional) Whether to allow a WAF-enabled load balancer to route requests to targets if it is unable to forward the request to AWS WAF. Defaults to `false`. +* `enable_zonal_shift` - (Optional) Whether zonal shift is enabled. Defaults to `false`. * `enforce_security_group_inbound_rules_on_private_link_traffic` - (Optional) Whether inbound security group rules are enforced for traffic originating from a PrivateLink. Only valid for Load Balancers of type `network`. The possible values are `on` and `off`. * `idle_timeout` - (Optional) Time in seconds that the connection is allowed to be idle. Only valid for Load Balancers of type `application`. Default: 60. * `internal` - (Optional) If true, the LB will be internal. Defaults to `false`. diff --git a/website/docs/r/lb_listener.html.markdown b/website/docs/r/lb_listener.html.markdown index d9a2e596743..5c1867e302a 100644 --- a/website/docs/r/lb_listener.html.markdown +++ b/website/docs/r/lb_listener.html.markdown @@ -264,6 +264,7 @@ The following arguments are optional: * `port` - (Optional) Port on which the load balancer is listening. Not valid for Gateway Load Balancers. * `protocol` - (Optional) Protocol for connections from clients to the load balancer. For Application Load Balancers, valid values are `HTTP` and `HTTPS`, with a default of `HTTP`. For Network Load Balancers, valid values are `TCP`, `TLS`, `UDP`, and `TCP_UDP`. Not valid to use `UDP` or `TCP_UDP` if dual-stack mode is enabled. Not valid for Gateway Load Balancers. * `ssl_policy` - (Optional) Name of the SSL Policy for the listener. Required if `protocol` is `HTTPS` or `TLS`. Default is `ELBSecurityPolicy-2016-08`. +* `tcp_idle_timeout_seconds` - (Optional) TCP idle timeout value in seconds. Can only be set if protocol is `TCP` on Network Load Balancer, or with a Gateway Load Balancer. Not supported for Application Load Balancers. Valid values are between `60` and `6000` inclusive. Default: `350`. * `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. ### default_action diff --git a/website/docs/r/sagemaker_hub.html.markdown b/website/docs/r/sagemaker_hub.html.markdown new file mode 100644 index 00000000000..67ad4bfe2a4 --- /dev/null +++ b/website/docs/r/sagemaker_hub.html.markdown @@ -0,0 +1,62 @@ +--- +subcategory: "SageMaker" +layout: "aws" +page_title: "AWS: aws_sagemaker_hub" +description: |- + Provides a SageMaker Hub resource. +--- + +# Resource: aws_sagemaker_hub + +Provides a SageMaker Hub resource. + +## Example Usage + +### Basic usage + +```terraform +resource "aws_sagemaker_hub" "example" { + hub_name = "example" + hub_description = "example" +} +``` + +## Argument Reference + +This resource supports the following arguments: + +* `hub_name` - (Required) The name of the hub. +* `hub_description` - (Required) A description of the hub. +* `hub_display_name` - (Optional) The display name of the hub. +* `hub_search_keywords` - (Optional) The searchable keywords for the hub. +* `s3_storage_config` - (Optional) The Amazon S3 storage configuration for the hub. See [S3 Storage Config](#s3-storage-config) details below. +* `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + +### S3 Storage Config + +* `s3_output_path` - (Optional) The Amazon S3 bucket prefix for hosting hub content.interface. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `id` - The name of the Hub. +* `arn` - The Amazon Resource Name (ARN) assigned by AWS to this Hub. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import SageMaker Hubs using the `name`. For example: + +```terraform +import { + to = aws_sagemaker_hub.test_hub + id = "my-code-repo" +} +``` + +Using `terraform import`, import SageMaker Hubs using the `name`. For example: + +```console +% terraform import aws_sagemaker_hub.test_hub my-code-repo +```