From 3791747217b9a099a6648e4a7c79302ff08115d0 Mon Sep 17 00:00:00 2001 From: Bruno Schaatsbergen Date: Thu, 2 Jan 2025 23:40:37 +0100 Subject: [PATCH 01/10] s3: remove experimental verbiage from `use_lockfile` --- internal/backend/remote-state/s3/backend.go | 2 +- website/docs/language/backend/s3.mdx | 2 +- website/docs/language/upgrade-guides/index.mdx | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/internal/backend/remote-state/s3/backend.go b/internal/backend/remote-state/s3/backend.go index 415315605b5b..3244695de8f1 100644 --- a/internal/backend/remote-state/s3/backend.go +++ b/internal/backend/remote-state/s3/backend.go @@ -156,7 +156,7 @@ func (b *Backend) ConfigSchema() *configschema.Block { "use_lockfile": { Type: cty.Bool, Optional: true, - Description: "(Experimental) Whether to use a lockfile for locking the state file.", + Description: "Whether to use a lockfile for locking the state file.", }, "profile": { Type: cty.String, diff --git a/website/docs/language/backend/s3.mdx b/website/docs/language/backend/s3.mdx index d927fa0e651b..ef04f4efe783 100644 --- a/website/docs/language/backend/s3.mdx +++ b/website/docs/language/backend/s3.mdx @@ -169,7 +169,7 @@ The following configuration is required: The following configuration is optional: -* `use_lockfile` - (Experimental, Optional) Whether to use a lockfile for locking the state file. Defaults to `false`. +* `use_lockfile` - (Optional) Whether to use a lockfile for locking the state file. Defaults to `false`. * `access_key` - (Optional) AWS access key. If configured, must also configure `secret_key`. This can also be sourced from the `AWS_ACCESS_KEY_ID` environment variable, AWS shared credentials file (e.g. `~/.aws/credentials`), or AWS shared configuration file (e.g. `~/.aws/config`). * `allowed_account_ids` - (Optional) List of allowed AWS account IDs to prevent potential destruction of a live environment. Conflicts with `forbidden_account_ids`. * `custom_ca_bundle` - (Optional) File containing custom root and intermediate certificates. Can also be set using the `AWS_CA_BUNDLE` environment variable. Setting ca_bundle in the shared config file is not supported. diff --git a/website/docs/language/upgrade-guides/index.mdx b/website/docs/language/upgrade-guides/index.mdx index 2c3f259ce516..74ffde4b0d04 100644 --- a/website/docs/language/upgrade-guides/index.mdx +++ b/website/docs/language/upgrade-guides/index.mdx @@ -33,7 +33,7 @@ Executing `terraform init -reconfigure` is required after updating to Terraform ### S3 Native State Locking -The S3 backend now supports S3 native state locking as an opt-in, experimental feature. +The S3 backend supports S3 native state locking as an opt-in feature. An S3 lock can be used alongside a DynamoDB lock, or independently. When both locking mechanisms are configured, a lock must be successfully acquired from both locations before subsequent operations will proceed. @@ -52,8 +52,6 @@ With S3 locking enabled, a lock file will be placed in the same location as the The lock file will be named identically to the state file, but with a `.tflock` extension. **S3 bucket policies and IAM policies attached to the calling principal may need to be adjusted to include permissions for the new lock file.** -In a future minor version of Terraform the experimental label will be removed from the `use_lockfile` attribute and attributes related to DynamoDB based locking will be deprecated. - ### Root Assume Role Attribute Removal Several root level attributes related to IAM role assumption which were previously deprecated have been removed. From 6906df1fe1f1cfdb3e0fe2079f15f17e849f61d9 Mon Sep 17 00:00:00 2001 From: Bruno Schaatsbergen Date: Fri, 3 Jan 2025 00:17:51 +0100 Subject: [PATCH 02/10] s3: deprecate DynamoDB-related arguments in favor of S3-native state locking --- internal/backend/remote-state/s3/backend.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/backend/remote-state/s3/backend.go b/internal/backend/remote-state/s3/backend.go index 3244695de8f1..f53c22c3d3fd 100644 --- a/internal/backend/remote-state/s3/backend.go +++ b/internal/backend/remote-state/s3/backend.go @@ -152,6 +152,7 @@ func (b *Backend) ConfigSchema() *configschema.Block { Type: cty.String, Optional: true, Description: "DynamoDB table for state locking and consistency", + Deprecated: true, }, "use_lockfile": { Type: cty.Bool, @@ -534,6 +535,7 @@ var endpointsSchema = singleNestedAttribute{ Type: cty.String, Optional: true, Description: "A custom endpoint for the DynamoDB API", + Deprecated: true, }, validateString{ Validators: []stringValidator{ @@ -689,6 +691,11 @@ func (b *Backend) PrepareConfig(obj cty.Value) (cty.Value, tfdiags.Diagnostics) diags = diags.Append(deprecatedAttrDiag(attrPath, cty.GetAttrPath("shared_credentials_files"))) } + attrPath = cty.GetAttrPath("dynamodb_table") + if val := obj.GetAttr("dynamodb_table"); !val.IsNull() { + diags = diags.Append(deprecatedAttrDiag(attrPath, cty.GetAttrPath("use_lockfile"))) + } + endpointFields := map[string]string{ "dynamodb_endpoint": "dynamodb", "iam_endpoint": "iam", From c59bb63ec082895cfc7683b2cbfee1d283e5e31b Mon Sep 17 00:00:00 2001 From: Bruno Schaatsbergen Date: Fri, 3 Jan 2025 00:39:58 +0100 Subject: [PATCH 03/10] s3: refine documentation for S3-native state locking and DynamoDB deprecation --- website/docs/language/backend/s3.mdx | 139 +++++++++------------------ 1 file changed, 47 insertions(+), 92 deletions(-) diff --git a/website/docs/language/backend/s3.mdx b/website/docs/language/backend/s3.mdx index ef04f4efe783..23585795036d 100644 --- a/website/docs/language/backend/s3.mdx +++ b/website/docs/language/backend/s3.mdx @@ -1,14 +1,12 @@ --- page_title: 'Backend Type: s3' -description: Terraform can store state remotely in S3 and lock that state with DynamoDB. +description: Terraform can store and lock state remotely in Amazon S3. --- # S3 Stores the state as a given key in a given bucket on [Amazon S3](https://aws.amazon.com/s3/). -This backend also supports state locking and consistency checking via [Dynamo DB](https://aws.amazon.com/dynamodb/), which can be enabled by setting the `dynamodb_table` field to an existing DynamoDB table name. -A single DynamoDB table can be used to lock multiple remote state files. -Terraform generates key names that include the values of the `bucket` and `key` variables. +This backend also supports state locking which can be enabled by setting the `use_lockfile` argument to `true`. ~> **Warning!** It is highly recommended that you enable [Bucket Versioning](https://docs.aws.amazon.com/AmazonS3/latest/userguide/manage-versioning-examples.html) @@ -40,6 +38,26 @@ Other workspaces are stored using the path `//` instead of `https://.`). * `workspace_key_prefix` - (Optional) Prefix applied to the state path inside the bucket. This is only relevant when using a non-default workspace. Defaults to `env:`. -### State Locking - -State locking is an opt-in feature of the S3 backend. - -Locking can be enabled via an S3 "lockfile" (introduced as **experimental** in Terraform 1.10) or DynamoDB. -To support migration from older versions of Terraform which only support DynamoDB-based locking, the S3 and DynamoDB arguments below can be configured simultaneously. -In a future minor version the DynamoDB locking mechanism will be removed. - -To enable S3 state locking, use the following optional argument: - -* `use_lockfile` - (Optional, Experimental) Whether to use a lockfile for locking the state file. Defaults to `false`. - -To enable DynamoDB state locking, use the following optional arguments: - -* `dynamodb_endpoint` - (Optional, **Deprecated**) Custom endpoint URL for the AWS DynamoDB API. - Use `endpoints.dynamodb` instead. -* `dynamodb_table` - (Optional) Name of DynamoDB Table to use for state locking and consistency. The table must have a partition key named `LockID` with type of `String`. - ## Multi-account AWS Architecture A common architectural pattern is for an organization to use a number of @@ -389,15 +389,11 @@ Your administrative AWS account will contain at least the following items: levels of access to the other AWS accounts. * An [S3 bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingBucket.html) that will contain the Terraform state files for each workspace. -* A [DynamoDB table](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.TablesItemsAttributes) - that will be used for locking to prevent concurrent operations on a single - workspace. -Provide the S3 bucket name and DynamoDB table name to Terraform within the -S3 backend configuration using the `bucket` and `dynamodb_table` arguments -respectively, and configure a suitable `workspace_key_prefix` to contain -the states of the various workspaces that will subsequently be created for -this configuration. +Provide the S3 bucket name to Terraform in the S3 backend configuration +using the `bucket` argument. Set `use_lockfile` to true to enable state locking. +Configure a suitable `workspace_key_prefix` to manage states of workspaces that +will be created for this configuration. ### Environment Account Setup @@ -526,12 +522,14 @@ services, such as ECS. ### Protecting Access to Workspace State -In a simple implementation of the pattern described in the prior sections, -all users have access to read and write states for all workspaces. In many -cases it is desirable to apply more precise access constraints to the -Terraform state objects in S3, so that for example only trusted administrators -are allowed to modify the production state, or to control _reading_ of a state -that contains sensitive information. +In a simple implementation of the pattern described earlier, +all users can read and write states for all workspaces. +In many cases, it is desirable to apply precise access controls +to the Terraform state objects stored in S3. For example, only +trusted administrators should modify the production state. +It is also important to control access to _reading_ the state file. +If state locking is enabled, the lock file (`.tflock`) +must also be included in the access controls. Amazon S3 supports fine-grained access control on a per-object-path basis using IAM policy. A full description of S3's access control mechanism is @@ -555,71 +553,28 @@ to only a single state object within an S3 bucket is shown below: { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject"], - "Resource": "arn:aws:s3:::example-bucket/myapp/production/tfstate" - } - ] -} -``` - -The example backend configuration below documents the corresponding `bucket` and `key` arguments: - -```hcl -terraform { - backend "s3" { - bucket = "example-bucket" - key = "path/to/state" - region = "us-east-1" - } -} -``` - -Refer to the [AWS documentation on S3 access control](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-access-control.html) for more details. - -DynamoDB does not assign a separate resource ARN to each key in a table, but you can write more precise policies for a DynamoDB table [using an IAM `Condition` element](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/specifying-conditions.html). -For example, you can use the `dynamodb:LeadingKeys` condition key to match on the partition key values that the S3 backend will use: - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "dynamodb:DeleteItem", - "dynamodb:GetItem", - "dynamodb:PutItem" - ], - "Resource": "arn:aws:dynamodb:us-east-1:12341234:table/example-table", - "Condition": { - "ForAllValues:StringEquals": { - "dynamodb:LeadingKeys": [ - "example-bucket/path/to/state", - "example-bucket/path/to/state-md5" - ] - } - } + "Resource": [ + "arn:aws:s3:::example-bucket/myapp/production/tfstate", + "arn:aws:s3:::example-bucket/myapp/production/tfstate.tflock" + ] } ] } ``` -Note that DynamoDB ARNs are regional and account-specific, unlike S3 bucket ARNs, so you must also specify the correct region and AWS account ID for your DynamoDB table in the `Resource` element. - -The example backend configuration below documents the corresponding arguments: +The example backend configuration below documents the corresponding `bucket`, `key` and `use_lockfile` arguments: ```hcl terraform { backend "s3" { - bucket = "example-bucket" - key = "path/to/state" - region = "us-east-1" - dynamodb_table = "example-table" + bucket = "example-bucket" + key = "path/to/state" + use_lockfile = true + region = "us-east-1" } } ``` -Refer to the [AWS documentation on DynamoDB fine-grained locking](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/specifying-conditions.html) for more details. - ### Configuring Custom User-Agent Information Note this feature is optional and only available in Terraform v0.13.1+. From ed0fe03eba3c621dc7c34e1c074ee6a18032e4af Mon Sep 17 00:00:00 2001 From: Bruno Schaatsbergen Date: Tue, 7 Jan 2025 15:33:29 +0100 Subject: [PATCH 04/10] s3: revert upgrade guide verbiage change --- website/docs/language/upgrade-guides/index.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/docs/language/upgrade-guides/index.mdx b/website/docs/language/upgrade-guides/index.mdx index 74ffde4b0d04..2c3f259ce516 100644 --- a/website/docs/language/upgrade-guides/index.mdx +++ b/website/docs/language/upgrade-guides/index.mdx @@ -33,7 +33,7 @@ Executing `terraform init -reconfigure` is required after updating to Terraform ### S3 Native State Locking -The S3 backend supports S3 native state locking as an opt-in feature. +The S3 backend now supports S3 native state locking as an opt-in, experimental feature. An S3 lock can be used alongside a DynamoDB lock, or independently. When both locking mechanisms are configured, a lock must be successfully acquired from both locations before subsequent operations will proceed. @@ -52,6 +52,8 @@ With S3 locking enabled, a lock file will be placed in the same location as the The lock file will be named identically to the state file, but with a `.tflock` extension. **S3 bucket policies and IAM policies attached to the calling principal may need to be adjusted to include permissions for the new lock file.** +In a future minor version of Terraform the experimental label will be removed from the `use_lockfile` attribute and attributes related to DynamoDB based locking will be deprecated. + ### Root Assume Role Attribute Removal Several root level attributes related to IAM role assumption which were previously deprecated have been removed. From 299c82dbed399ec2629f4494e9178723a57879a9 Mon Sep 17 00:00:00 2001 From: Bruno Schaatsbergen Date: Tue, 7 Jan 2025 15:34:02 +0100 Subject: [PATCH 05/10] s3: apply grammar suggestion from @jar-b Co-authored-by: Jared Baker --- website/docs/language/backend/s3.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/language/backend/s3.mdx b/website/docs/language/backend/s3.mdx index 23585795036d..9ede1b6c3615 100644 --- a/website/docs/language/backend/s3.mdx +++ b/website/docs/language/backend/s3.mdx @@ -43,7 +43,7 @@ Using the example above, the state for the workspace `development` would be stor State locking is an opt-in feature of the S3 backend. -Locking can be enabled via an S3 or DynamoDB. However, **DynamoDB-based locking is deprecated** and will be removed in a future minor version. To support migration from older versions of Terraform that only support DynamoDB-based locking, the S3 and DynamoDB arguments can be configured simultaneously. +Locking can be enabled via S3 or DynamoDB. However, **DynamoDB-based locking is deprecated** and will be removed in a future minor version. To support migration from older versions of Terraform that only support DynamoDB-based locking, the S3 and DynamoDB arguments can be configured simultaneously. #### Enabling S3 State Locking From 12163f154518b46376d4bd8ef9eb15944e0fb33b Mon Sep 17 00:00:00 2001 From: Bruno Schaatsbergen Date: Tue, 7 Jan 2025 15:34:21 +0100 Subject: [PATCH 06/10] s3: apply grammar suggestion from @jar-b Co-authored-by: Jared Baker --- website/docs/language/backend/s3.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/language/backend/s3.mdx b/website/docs/language/backend/s3.mdx index 9ede1b6c3615..1537654ed118 100644 --- a/website/docs/language/backend/s3.mdx +++ b/website/docs/language/backend/s3.mdx @@ -183,7 +183,7 @@ This backend requires the configuration of the AWS Region and S3 state storage. The following configuration is required: -* `region` - (Required) AWS Region of the S3 Bucket and DynamoDB Table (if used, **deprecated**). This can also be sourced from the `AWS_DEFAULT_REGION` and `AWS_REGION` environment variables. +* `region` - (Required) AWS Region of the S3 Bucket and DynamoDB Table (if used). This can also be sourced from the `AWS_DEFAULT_REGION` and `AWS_REGION` environment variables. The following configuration is optional: From d27513212ed9f836bbb7d40b14e32c28ae372039 Mon Sep 17 00:00:00 2001 From: Bruno Schaatsbergen Date: Tue, 7 Jan 2025 15:36:52 +0100 Subject: [PATCH 07/10] s3: preserve S3 access control link --- website/docs/language/backend/s3.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/language/backend/s3.mdx b/website/docs/language/backend/s3.mdx index 1537654ed118..3bb19081d89c 100644 --- a/website/docs/language/backend/s3.mdx +++ b/website/docs/language/backend/s3.mdx @@ -574,6 +574,7 @@ terraform { } } ``` +Refer to the [AWS documentation on S3 access control](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-access-control.html) for more details. ### Configuring Custom User-Agent Information From 164d1e2fdce6f6d587412b3c25f784bb12ee6c57 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Tue, 7 Jan 2025 09:41:38 -0500 Subject: [PATCH 08/10] backend/s3(test): add `dynamodb_table` deprecation case ```console % go test -count=1 -run="TestBackendConfig_PrepareConfigValidation" ./... ok github.com/hashicorp/terraform/internal/backend/remote-state/s3 0.507s ``` --- internal/backend/remote-state/s3/backend_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/internal/backend/remote-state/s3/backend_test.go b/internal/backend/remote-state/s3/backend_test.go index 8211060149ed..6e391327dc6d 100644 --- a/internal/backend/remote-state/s3/backend_test.go +++ b/internal/backend/remote-state/s3/backend_test.go @@ -1409,6 +1409,22 @@ func TestBackendConfig_PrepareConfigValidation(t *testing.T) { ), }, }, + + "dynamodb_table deprecation": { + config: cty.ObjectVal(map[string]cty.Value{ + "bucket": cty.StringVal("test"), + "key": cty.StringVal("test"), + "region": cty.StringVal("us-west-2"), + "dynamodb_table": cty.StringVal("test"), + }), + expectedDiags: tfdiags.Diagnostics{ + attributeWarningDiag( + "Deprecated Parameter", + `The parameter "dynamodb_table" is deprecated. Use parameter "use_lockfile" instead.`, + cty.GetAttrPath("dynamodb_table"), + ), + }, + }, } for name, tc := range cases { From cd4a181e928086850021be1aa7478438bd9582a2 Mon Sep 17 00:00:00 2001 From: Bruno Schaatsbergen Date: Tue, 7 Jan 2025 16:03:46 +0100 Subject: [PATCH 09/10] s3: add verbiage regarding `s3:DeleteObject` permission Clarify S3 permissions when using S3-native state locking. --- website/docs/language/backend/s3.mdx | 41 ++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/website/docs/language/backend/s3.mdx b/website/docs/language/backend/s3.mdx index 3bb19081d89c..f9f00baecfa2 100644 --- a/website/docs/language/backend/s3.mdx +++ b/website/docs/language/backend/s3.mdx @@ -65,10 +65,14 @@ To enable DynamoDB state locking, use the following optional arguments: When not using [workspaces](/terraform/language/state/workspaces)(or when only using the `default` workspace), Terraform will need the following AWS IAM permissions on the target backend bucket: * `s3:ListBucket` on `arn:aws:s3:::mybucket`. At a minimum, this must be able to list the path where the state is stored. -* `s3:GetObject` on `arn:aws:s3:::mybucket/path/to/my/key` and `arn:aws:s3:::mybucket/path/to/my/key.tflock` -* `s3:PutObject` on `arn:aws:s3:::mybucket/path/to/my/key` and `arn:aws:s3:::mybucket/path/to/my/key.tflock` +* `s3:GetObject` on `arn:aws:s3:::mybucket/path/to/my/key` +* `s3:PutObject` on `arn:aws:s3:::mybucket/path/to/my/key` -Note: `s3:DeleteObject` is not needed, as Terraform will not delete the state storage. +-> **Note:** If `use_lockfile` is set, `s3:GetObject`, `s3:PutObject`, +and `s3:DeleteObject` are required on the lock file, e.g., +`arn:aws:s3:::mybucket/path/to/my/key.tflock`. + +-> **Note:** `s3:DeleteObject` is not required on the state file, as Terraform does not delete it. This is seen in the following AWS IAM Statement: @@ -80,12 +84,23 @@ This is seen in the following AWS IAM Statement: "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::mybucket" + "Condition": { + "StringEquals": { + "s3:prefix": "mybucket/path/to/my/key" + } + } }, { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject"], "Resource": [ - "arn:aws:s3:::mybucket/path/to/my/key", + "arn:aws:s3:::mybucket/path/to/my/key" + ] + }, + { + "Effect": "Allow", + "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], + "Resource": [ "arn:aws:s3:::mybucket/path/to/my/key.tflock" ] } @@ -93,12 +108,16 @@ This is seen in the following AWS IAM Statement: } ``` -When using [workspaces](/terraform/language/state/workspaces), Terraform will also need permissions to create, list, read, update, and delete the workspace state storage: +When using [workspaces](/terraform/language/state/workspaces), Terraform will also need permissions to create, list, read, update, and delete the workspace state file: * `s3:ListBucket` on `arn:aws:s3:::mybucket`. At a minumum, this must be able to list the path where the `default` workspace is stored as well as the other workspaces. -* `s3:GetObject` on `arn:aws:s3:::mybucket/path/to/my/key`, `arn:aws:s3:::mybucket//*/path/to/my/key` and `arn:aws:s3:::mybucket//*/path/to/my/key.tflock` -* `s3:PutObject` on `arn:aws:s3:::mybucket/path/to/my/key`, `arn:aws:s3:::mybucket//*/path/to/my/key` and `arn:aws:s3:::mybucket//*/path/to/my/key.tflock` -* `s3:DeleteObject` on `arn:aws:s3:::mybucket//*/path/to/my/key` and `arn:aws:s3:::mybucket//*/path/to/my/key.tflock` +* `s3:GetObject` on `arn:aws:s3:::mybucket/path/to/my/key`, `arn:aws:s3:::mybucket//*/path/to/my/key` +* `s3:PutObject` on `arn:aws:s3:::mybucket/path/to/my/key`, `arn:aws:s3:::mybucket//*/path/to/my/key` +* `s3:DeleteObject` on `arn:aws:s3:::mybucket//*/path/to/my/key` + +-> **Note:** If `use_lockfile` is set, `s3:GetObject`, `s3:PutObject`, +and `s3:DeleteObject` are required on the lock file, e.g., +`arn:aws:s3:::mybucket//*/path/to/my/key.tflock`. -> **Note:** AWS can control access to S3 buckets with either IAM policies attached to users/groups/roles (like the example above) or resource policies @@ -555,6 +574,12 @@ to only a single state object within an S3 bucket is shown below: "Action": ["s3:GetObject", "s3:PutObject"], "Resource": [ "arn:aws:s3:::example-bucket/myapp/production/tfstate", + ] + }, + { + "Effect": "Allow", + "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], + "Resource": [ "arn:aws:s3:::example-bucket/myapp/production/tfstate.tflock" ] } From 9609070998cac5965bf2c898a22fb3febce478e5 Mon Sep 17 00:00:00 2001 From: Bruno Schaatsbergen Date: Tue, 7 Jan 2025 19:33:59 +0100 Subject: [PATCH 10/10] s3: add missing comma in IAM policy Co-authored-by: Ruben Nic --- website/docs/language/backend/s3.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/language/backend/s3.mdx b/website/docs/language/backend/s3.mdx index f9f00baecfa2..9d34247ce316 100644 --- a/website/docs/language/backend/s3.mdx +++ b/website/docs/language/backend/s3.mdx @@ -83,7 +83,7 @@ This is seen in the following AWS IAM Statement: { "Effect": "Allow", "Action": "s3:ListBucket", - "Resource": "arn:aws:s3:::mybucket" + "Resource": "arn:aws:s3:::mybucket", "Condition": { "StringEquals": { "s3:prefix": "mybucket/path/to/my/key"