Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iam/policy: Allow IAM policy leading whitespace #36597

Merged
merged 9 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .changelog/36597.txt
Original file line number Diff line number Diff line change
@@ -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
```
61 changes: 61 additions & 0 deletions internal/service/iam/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = <<EOF
%[2]s{
"Version": "2012-10-17",%[3]s
"Statement": [
{
"Action": [
"ec2:Describe*"
],
"Effect": %[3]s"Allow",
"Resource": "*"
}
]
}
EOF
}
`, rName, ws1, ws2, ws3)
}

func testAccPolicyConfig_namePrefix(namePrefix string) string {
return fmt.Sprintf(`
resource "aws_iam_policy" "test" {
Expand Down
19 changes: 15 additions & 4 deletions internal/verify/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,14 @@ 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 != "{" {
switch value[:1] {
return //nolint:nakedret // Naked return due to legacy, non-idiomatic Go function, error handling
}

if first := value[:1]; first != "{" {
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 `"`:
Expand All @@ -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 //nolint:nakedret // Naked return due to legacy, non-idiomatic Go function, error handling
}

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 //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 //nolint:nakedret // Naked return due to legacy, non-idiomatic Go function, error handling
}

return //nolint:nakedret // Just a long function.
Expand Down
4 changes: 0 additions & 4 deletions internal/verify/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/glacier_vault_lock.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/iam_group_policy.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 8 additions & 11 deletions website/docs/r/iam_policy.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ description: |-

Provides an IAM 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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/iam_role.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/iam_role_policy.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/iam_user_policy.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/media_store_container_policy.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/transfer_access.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/transfer_user.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading