From abad7c3d03ef1b6afcd5b76c1c6c9efc16721fa6 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 26 Mar 2024 16:00:54 -0400 Subject: [PATCH 1/8] iam/policy: Allow IAM policy leading whitespace --- internal/verify/validate.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/internal/verify/validate.go b/internal/verify/validate.go index 9bf6284dec8..a790244c592 100644 --- a/internal/verify/validate.go +++ b/internal/verify/validate.go @@ -165,9 +165,13 @@ func ValidCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []err func ValidIAMPolicyJSON(v interface{}, k string) (ws []string, errors []error) { // IAM Policy documents need to be valid JSON, and pass legacy parsing value := v.(string) + value = strings.TrimSpace(value) if len(value) < 1 { errors = append(errors, fmt.Errorf("%q is an empty string, which is not a valid JSON value", k)) - } else if first := value[:1]; first != "{" { + return + } + + if first := value[:1]; first != "{" { switch value[:1] { case " ", "\t", "\r", "\n": errors = append(errors, fmt.Errorf("%q contains an invalid JSON policy: leading space characters are not allowed", k)) @@ -193,14 +197,21 @@ func ValidIAMPolicyJSON(v interface{}, k string) (ws []string, errors []error) { // Generic error for if we didn't find something more specific to say. errors = append(errors, fmt.Errorf("%q contains an invalid JSON policy: not a JSON object", k)) } - } else if _, err := structure.NormalizeJsonString(v); err != nil { + return + } + + if _, err := structure.NormalizeJsonString(v); err != nil { errStr := err.Error() if err, ok := errs.As[*json.SyntaxError](err); ok { errStr = fmt.Sprintf("%s, at byte offset %d", errStr, err.Offset) } errors = append(errors, fmt.Errorf("%q contains an invalid JSON policy: %s", k, errStr)) - } else if err := basevalidation.JSONNoDuplicateKeys(value); err != nil { + return + } + + if err := basevalidation.JSONNoDuplicateKeys(value); err != nil { errors = append(errors, fmt.Errorf("%q contains duplicate JSON keys: %s", k, err)) + return } return //nolint:nakedret // Just a long function. From b9fc6d35428b6bff44c754f704ffb9de29747088 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 26 Mar 2024 16:12:30 -0400 Subject: [PATCH 2/8] Add changelog --- .changelog/36597.txt | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .changelog/36597.txt diff --git a/.changelog/36597.txt b/.changelog/36597.txt new file mode 100644 index 00000000000..45c28883382 --- /dev/null +++ b/.changelog/36597.txt @@ -0,0 +1,39 @@ +```release-note:enhancement +resource/aws_glacier_vault_lock: Allow `policy` to have leading whitespace +``` + +```release-note:enhancement +resource/aws_iam_group_policy: Allow `policy` to have leading whitespace +``` + +```release-note:enhancement +resource/aws_iam_policy: Allow `policy` to have leading whitespace +``` + +```release-note:enhancement +resource/aws_iam_role_policy: Allow `policy` to have leading whitespace +``` + +```release-note:enhancement +resource/aws_iam_role: Allow `assume_role_policy` and `inline_policy.*.policy` to have leading whitespace +``` + +```release-note:enhancement +resource/aws_iam_user_policy: Allow `policy` to have leading whitespace +``` + +```release-note:enhancement +resource/aws_media_store_container_policy: Allow `policy` to have leading whitespace +``` + +```release-note:enhancement +resource/aws_ssoadmin_permission_set_inline_policy: Allow `inline_policy` to have leading whitespace +``` + +```release-note:enhancement +resource/aws_transfer_access: Allow `policy` to have leading whitespace +``` + +```release-note:enhancement +resource/aws_transfer_user: Allow `policy` to have leading whitespace +``` From 46461855b613cdafc4210171c58fd2e6ddc8a4ab Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 26 Mar 2024 16:13:04 -0400 Subject: [PATCH 3/8] iam/policy: Add leading whitespace test --- internal/service/iam/policy_test.go | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/internal/service/iam/policy_test.go b/internal/service/iam/policy_test.go index 7bc7fc6c9bf..21df711f52a 100644 --- a/internal/service/iam/policy_test.go +++ b/internal/service/iam/policy_test.go @@ -83,6 +83,44 @@ func TestAccIAMPolicy_description(t *testing.T) { }) } +func TestAccIAMPolicy_whitespace(t *testing.T) { + ctx := acctest.Context(t) + var out iam.Policy + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_policy.test" + + 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_whitespace(rName, "", "", ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &out), + ), + }, + { + Config: testAccPolicyConfig_whitespace(rName, " ", "", ""), + PlanOnly: true, + }, + { + Config: testAccPolicyConfig_whitespace(rName, " ", "\n", ""), + PlanOnly: true, + }, + { + Config: testAccPolicyConfig_whitespace(rName, " ", "\n", " "), + PlanOnly: true, + }, + { + Config: testAccPolicyConfig_whitespace(rName, " \n", "\n", "\t "), + PlanOnly: true, + }, + }, + }) +} + func TestAccIAMPolicy_disappears(t *testing.T) { ctx := acctest.Context(t) var out iam.Policy @@ -408,6 +446,29 @@ EOF `, rName) } +func testAccPolicyConfig_whitespace(rName, ws1, ws2, ws3 string) string { + return fmt.Sprintf(` +resource "aws_iam_policy" "test" { + name = %[1]q + + policy = < Date: Tue, 26 Mar 2024 16:13:35 -0400 Subject: [PATCH 4/8] iam/policy: Add note on recommended usage --- website/docs/r/iam_policy.html.markdown | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/website/docs/r/iam_policy.html.markdown b/website/docs/r/iam_policy.html.markdown index f133459c0f0..f03412b230f 100644 --- a/website/docs/r/iam_policy.html.markdown +++ b/website/docs/r/iam_policy.html.markdown @@ -10,6 +10,8 @@ description: |- Provides an IAM policy. +~> **NOTE:** We suggest using [`jsonencode()`](https://developer.hashicorp.com/terraform/language/functions/jsonencode) when assigning a value to `policy`. This function seamlessly translates Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. By employing `jsonencode()`, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON. + ## Example Usage ```terraform @@ -40,24 +42,19 @@ resource "aws_iam_policy" "policy" { This resource supports the following arguments: * `description` - (Optional, Forces new resource) Description of the IAM policy. -* `name` - (Optional, Forces new resource) The name of the policy. If omitted, Terraform will assign a random, unique name. * `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`. -* `path` - (Optional, default "/") Path in which to create the policy. - See [IAM Identifiers](https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) for more information. -* `policy` - (Required) The policy document. This is a JSON formatted string. For more information about building AWS IAM policy documents with Terraform, see the [AWS IAM Policy Document Guide](https://learn.hashicorp.com/terraform/aws/iam-policy) +* `name` - (Optional, Forces new resource) Name of the policy. If omitted, Terraform will assign a random, unique name. +* `path` - (Optional, default "/") Path in which to create the policy. See [IAM Identifiers](https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) for more information. +* `policy` - (Required) Policy document. This is a JSON formatted string. For more information about building AWS IAM policy documents with Terraform, see the [AWS IAM Policy Document Guide](https://learn.hashicorp.com/terraform/aws/iam-policy) * `tags` - (Optional) Map of resource tags for the IAM Policy. 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. ## Attribute Reference This resource exports the following attributes in addition to the arguments above: -* `id` - The ARN assigned by AWS to this policy. -* `arn` - The ARN assigned by AWS to this policy. -* `description` - The description of the policy. -* `name` - The name of the policy. -* `path` - The path of the policy in IAM. -* `policy` - The policy document. -* `policy_id` - The policy's ID. +* `arn` - ARN assigned by AWS to this policy. +* `id` - ARN assigned by AWS to this policy. +* `policy_id` - Policy's ID. * `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 From 056173a785a1ebc1472c2ec8418f26e5ee7d62eb Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 26 Mar 2024 16:51:34 -0400 Subject: [PATCH 5/8] validate: Naked returns --- internal/verify/validate.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/verify/validate.go b/internal/verify/validate.go index a790244c592..3c1ac8dbddd 100644 --- a/internal/verify/validate.go +++ b/internal/verify/validate.go @@ -168,7 +168,7 @@ func ValidIAMPolicyJSON(v interface{}, k string) (ws []string, errors []error) { value = strings.TrimSpace(value) if len(value) < 1 { errors = append(errors, fmt.Errorf("%q is an empty string, which is not a valid JSON value", k)) - return + return //nolint:nakedret // Naked return due to legacy, non-idiomatic Go function, error handling } if first := value[:1]; first != "{" { @@ -197,7 +197,7 @@ func ValidIAMPolicyJSON(v interface{}, k string) (ws []string, errors []error) { // Generic error for if we didn't find something more specific to say. errors = append(errors, fmt.Errorf("%q contains an invalid JSON policy: not a JSON object", k)) } - return + return //nolint:nakedret // Naked return due to legacy, non-idiomatic Go function, error handling } if _, err := structure.NormalizeJsonString(v); err != nil { @@ -206,12 +206,12 @@ func ValidIAMPolicyJSON(v interface{}, k string) (ws []string, errors []error) { errStr = fmt.Sprintf("%s, at byte offset %d", errStr, err.Offset) } errors = append(errors, fmt.Errorf("%q contains an invalid JSON policy: %s", k, errStr)) - return + return //nolint:nakedret // Naked return due to legacy, non-idiomatic Go function, error handling } if err := basevalidation.JSONNoDuplicateKeys(value); err != nil { errors = append(errors, fmt.Errorf("%q contains duplicate JSON keys: %s", k, err)) - return + return //nolint:nakedret // Naked return due to legacy, non-idiomatic Go function, error handling } return //nolint:nakedret // Just a long function. From 93af107ce89600bc48b77a2a788f643d6582b61b Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 26 Mar 2024 17:15:48 -0400 Subject: [PATCH 6/8] validate: Fix test --- internal/verify/validate_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/verify/validate_test.go b/internal/verify/validate_test.go index c3cb819ddd5..3052363e598 100644 --- a/internal/verify/validate_test.go +++ b/internal/verify/validate_test.go @@ -391,10 +391,6 @@ func TestValidIAMPolicyJSONString(t *testing.T) { Value: ``, WantError: `"json" is an empty string, which is not a valid JSON value`, }, - { - Value: ` {"xyz": "foo"}`, - WantError: `"json" contains an invalid JSON policy: leading space characters are not allowed`, - }, { Value: `"blub"`, WantError: `"json" contains an invalid JSON policy: contains a JSON-encoded string, not a JSON-encoded object`, From cd9b81037418bf66abfdaa46f8e6c443e74616e3 Mon Sep 17 00:00:00 2001 From: Dirk Avery <31492422+YakDriver@users.noreply.github.com> Date: Tue, 26 Mar 2024 17:24:23 -0400 Subject: [PATCH 7/8] Update internal/verify/validate.go Co-authored-by: Kit Ewbank --- internal/verify/validate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/verify/validate.go b/internal/verify/validate.go index 3c1ac8dbddd..427254b8077 100644 --- a/internal/verify/validate.go +++ b/internal/verify/validate.go @@ -172,7 +172,7 @@ func ValidIAMPolicyJSON(v interface{}, k string) (ws []string, errors []error) { } if first := value[:1]; first != "{" { - switch value[:1] { + switch first { case " ", "\t", "\r", "\n": errors = append(errors, fmt.Errorf("%q contains an invalid JSON policy: leading space characters are not allowed", k)) case `"`: From c1f2c7fbb4a4eb11921d56db9650328084555ee4 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 26 Mar 2024 17:50:11 -0400 Subject: [PATCH 8/8] docs: Add suggestions --- website/docs/r/glacier_vault_lock.html.markdown | 2 ++ website/docs/r/iam_group_policy.html.markdown | 2 ++ website/docs/r/iam_policy.html.markdown | 2 +- website/docs/r/iam_role.html.markdown | 2 ++ website/docs/r/iam_role_policy.html.markdown | 2 ++ website/docs/r/iam_user_policy.html.markdown | 2 ++ website/docs/r/media_store_container_policy.html.markdown | 2 ++ .../docs/r/ssoadmin_permission_set_inline_policy.html.markdown | 2 ++ website/docs/r/transfer_access.html.markdown | 2 ++ website/docs/r/transfer_user.html.markdown | 2 ++ 10 files changed, 19 insertions(+), 1 deletion(-) diff --git a/website/docs/r/glacier_vault_lock.html.markdown b/website/docs/r/glacier_vault_lock.html.markdown index 8d355d24da5..9e3a6af7076 100644 --- a/website/docs/r/glacier_vault_lock.html.markdown +++ b/website/docs/r/glacier_vault_lock.html.markdown @@ -12,6 +12,8 @@ Manages a Glacier Vault Lock. You can refer to the [Glacier Developer Guide](htt ~> **NOTE:** This resource allows you to test Glacier Vault Lock policies by setting the `complete_lock` argument to `false`. When testing policies in this manner, the Glacier Vault Lock automatically expires after 24 hours and Terraform will show this resource as needing recreation after that time. To permanently apply the policy, set the `complete_lock` argument to `true`. When changing `complete_lock` to `true`, it is expected the resource will show as recreating. +~> **NOTE:** We suggest using [`jsonencode()`](https://developer.hashicorp.com/terraform/language/functions/jsonencode) or [`aws_iam_policy_document`](/docs/providers/aws/d/iam_policy_document.html) when assigning a value to `policy`. They seamlessly translate Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. Also, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON. + !> **WARNING:** Once a Glacier Vault Lock is completed, it is immutable. The deletion of the Glacier Vault Lock is not be possible and attempting to remove it from Terraform will return an error. Set the `ignore_deletion_error` argument to `true` and apply this configuration before attempting to delete this resource via Terraform or use `terraform state rm` to remove this resource from Terraform management. ## Example Usage diff --git a/website/docs/r/iam_group_policy.html.markdown b/website/docs/r/iam_group_policy.html.markdown index 912157cc7a4..2ef042bcb30 100644 --- a/website/docs/r/iam_group_policy.html.markdown +++ b/website/docs/r/iam_group_policy.html.markdown @@ -10,6 +10,8 @@ description: |- Provides an IAM policy attached to a group. +~> **NOTE:** We suggest using [`jsonencode()`](https://developer.hashicorp.com/terraform/language/functions/jsonencode) or [`aws_iam_policy_document`](/docs/providers/aws/d/iam_policy_document.html) when assigning a value to `policy`. They seamlessly translate Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. Also, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON. + ## Example Usage ```terraform diff --git a/website/docs/r/iam_policy.html.markdown b/website/docs/r/iam_policy.html.markdown index f03412b230f..b8667e4fd50 100644 --- a/website/docs/r/iam_policy.html.markdown +++ b/website/docs/r/iam_policy.html.markdown @@ -10,7 +10,7 @@ description: |- Provides an IAM policy. -~> **NOTE:** We suggest using [`jsonencode()`](https://developer.hashicorp.com/terraform/language/functions/jsonencode) when assigning a value to `policy`. This function seamlessly translates Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. By employing `jsonencode()`, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON. +~> **NOTE:** We suggest using [`jsonencode()`](https://developer.hashicorp.com/terraform/language/functions/jsonencode) or [`aws_iam_policy_document`](/docs/providers/aws/d/iam_policy_document.html) when assigning a value to `policy`. They seamlessly translate Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. Also, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON. ## Example Usage diff --git a/website/docs/r/iam_role.html.markdown b/website/docs/r/iam_role.html.markdown index bc37ca990ff..299b54bc374 100644 --- a/website/docs/r/iam_role.html.markdown +++ b/website/docs/r/iam_role.html.markdown @@ -14,6 +14,8 @@ Provides an IAM role. ~> **NOTE:** If you use this resource's `managed_policy_arns` argument or `inline_policy` configuration blocks, this resource will take over exclusive management of the role's respective policy types (e.g., both policy types if both arguments are used). These arguments are incompatible with other ways of managing a role's policies, such as [`aws_iam_policy_attachment`](/docs/providers/aws/r/iam_policy_attachment.html), [`aws_iam_role_policy_attachment`](/docs/providers/aws/r/iam_role_policy_attachment.html), and [`aws_iam_role_policy`](/docs/providers/aws/r/iam_role_policy.html). If you attempt to manage a role's policies by multiple means, you will get resource cycling and/or errors. +~> **NOTE:** We suggest using [`jsonencode()`](https://developer.hashicorp.com/terraform/language/functions/jsonencode) or [`aws_iam_policy_document`](/docs/providers/aws/d/iam_policy_document.html) when assigning a value to `assume_role_policy` or `inline_policy.*.policy`. They seamlessly translate Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. Also, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON. + ## Example Usage ### Basic Example diff --git a/website/docs/r/iam_role_policy.html.markdown b/website/docs/r/iam_role_policy.html.markdown index 04b7aa81856..93fab81f9fa 100644 --- a/website/docs/r/iam_role_policy.html.markdown +++ b/website/docs/r/iam_role_policy.html.markdown @@ -12,6 +12,8 @@ Provides an IAM role inline policy. ~> **NOTE:** For a given role, this resource is incompatible with using the [`aws_iam_role` resource](/docs/providers/aws/r/iam_role.html) `inline_policy` argument. When using that argument and this resource, both will attempt to manage the role's inline policies and Terraform will show a permanent difference. +~> **NOTE:** We suggest using [`jsonencode()`](https://developer.hashicorp.com/terraform/language/functions/jsonencode) or [`aws_iam_policy_document`](/docs/providers/aws/d/iam_policy_document.html) when assigning a value to `policy`. They seamlessly translate Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. Also, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON. + ## Example Usage ```terraform diff --git a/website/docs/r/iam_user_policy.html.markdown b/website/docs/r/iam_user_policy.html.markdown index b7a3003f2b7..97913a2ecd2 100644 --- a/website/docs/r/iam_user_policy.html.markdown +++ b/website/docs/r/iam_user_policy.html.markdown @@ -10,6 +10,8 @@ description: |- Provides an IAM policy attached to a user. +~> **NOTE:** We suggest using [`jsonencode()`](https://developer.hashicorp.com/terraform/language/functions/jsonencode) or [`aws_iam_policy_document`](/docs/providers/aws/d/iam_policy_document.html) when assigning a value to `policy`. They seamlessly translate Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. Also, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON. + ## Example Usage ```terraform diff --git a/website/docs/r/media_store_container_policy.html.markdown b/website/docs/r/media_store_container_policy.html.markdown index 29477d348f9..6e2e5818683 100644 --- a/website/docs/r/media_store_container_policy.html.markdown +++ b/website/docs/r/media_store_container_policy.html.markdown @@ -10,6 +10,8 @@ description: |- Provides a MediaStore Container Policy. +~> **NOTE:** We suggest using [`jsonencode()`](https://developer.hashicorp.com/terraform/language/functions/jsonencode) or [`aws_iam_policy_document`](/docs/providers/aws/d/iam_policy_document.html) when assigning a value to `policy`. They seamlessly translate Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. Also, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON. + ## Example Usage ```terraform diff --git a/website/docs/r/ssoadmin_permission_set_inline_policy.html.markdown b/website/docs/r/ssoadmin_permission_set_inline_policy.html.markdown index b2a930d5f46..e17df787871 100644 --- a/website/docs/r/ssoadmin_permission_set_inline_policy.html.markdown +++ b/website/docs/r/ssoadmin_permission_set_inline_policy.html.markdown @@ -13,6 +13,8 @@ Provides an IAM inline policy for a Single Sign-On (SSO) Permission Set resource ~> **NOTE:** AWS Single Sign-On (SSO) only supports one IAM inline policy per [`aws_ssoadmin_permission_set`](ssoadmin_permission_set.html) resource. Creating or updating this resource will automatically [Provision the Permission Set](https://docs.aws.amazon.com/singlesignon/latest/APIReference/API_ProvisionPermissionSet.html) to apply the corresponding updates to all assigned accounts. +~> **NOTE:** We suggest using [`jsonencode()`](https://developer.hashicorp.com/terraform/language/functions/jsonencode) or [`aws_iam_policy_document`](/docs/providers/aws/d/iam_policy_document.html) when assigning a value to `inline_policy`. They seamlessly translate Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. Also, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON. + ## Example Usage ```terraform diff --git a/website/docs/r/transfer_access.html.markdown b/website/docs/r/transfer_access.html.markdown index 4f1203c6b46..486115a6d12 100644 --- a/website/docs/r/transfer_access.html.markdown +++ b/website/docs/r/transfer_access.html.markdown @@ -10,6 +10,8 @@ description: |- Provides a AWS Transfer Access resource. +~> **NOTE:** We suggest using [`jsonencode()`](https://developer.hashicorp.com/terraform/language/functions/jsonencode) or [`aws_iam_policy_document`](/docs/providers/aws/d/iam_policy_document.html) when assigning a value to `policy`. They seamlessly translate Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. Also, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON. + ## Example Usage ### Basic S3 diff --git a/website/docs/r/transfer_user.html.markdown b/website/docs/r/transfer_user.html.markdown index 8c40c85cc63..53237daceda 100644 --- a/website/docs/r/transfer_user.html.markdown +++ b/website/docs/r/transfer_user.html.markdown @@ -10,6 +10,8 @@ description: |- Provides a AWS Transfer User resource. Managing SSH keys can be accomplished with the [`aws_transfer_ssh_key` resource](/docs/providers/aws/r/transfer_ssh_key.html). +~> **NOTE:** We suggest using [`jsonencode()`](https://developer.hashicorp.com/terraform/language/functions/jsonencode) or [`aws_iam_policy_document`](/docs/providers/aws/d/iam_policy_document.html) when assigning a value to `policy`. They seamlessly translate Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. Also, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON. + ## Example Usage ```terraform