From 57cd57a1bfa1c89843ff1ae17d485592c02ec63f Mon Sep 17 00:00:00 2001 From: Alexander Fisher Date: Tue, 25 Feb 2025 09:39:40 +0000 Subject: [PATCH] Add aws_iam_role_deprecated_policy_attributes rule --- docs/rules/README.md | 1 + docs/rules/README.md.tmpl | 1 + ...s_iam_role_deprecated_policy_attributes.md | 64 +++++++++++ ...s_iam_role_deprecated_policy_attributes.go | 80 +++++++++++++ ..._role_deprecated_policy_attributes_test.go | 106 ++++++++++++++++++ rules/provider.go | 1 + 6 files changed, 253 insertions(+) create mode 100644 docs/rules/aws_iam_role_deprecated_policy_attributes.md create mode 100644 rules/aws_iam_role_deprecated_policy_attributes.go create mode 100644 rules/aws_iam_role_deprecated_policy_attributes_test.go diff --git a/docs/rules/README.md b/docs/rules/README.md index 637d6318..70d872ef 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -71,6 +71,7 @@ These rules enforce best practices and naming conventions: |[aws_iam_policy_attachment_exclusive_attachment](aws_iam_policy_attachment_exclusive_attachment.md)|Consider alternative resources to `aws_iam_policy_attachment`|| |[aws_iam_policy_document_gov_friendly_arns](aws_iam_policy_document_gov_friendly_arns.md)|Ensure `iam_policy_document` data sources do not contain `arn:aws:` ARN's|| |[aws_iam_policy_gov_friendly_arns](aws_iam_policy_gov_friendly_arns.md)|Ensure `iam_policy` resources do not contain `arn:aws:` ARN's|| +|[aws_iam_role_deprecated_policy_attributes](aws_iam_role_deprecated_policy_attributes.md)|Disallow using deprecated policy attributes of `aws_iam_role`|| |[aws_iam_role_policy_gov_friendly_arns](aws_iam_role_policy_gov_friendly_arns.md)|Ensure `iam_role_policy` resources do not contain `arn:aws:` ARN's|| |[aws_lambda_function_deprecated_runtime](aws_lambda_function_deprecated_runtime.md)|Disallow deprecated runtimes for Lambda Function|✔| |[aws_resource_missing_tags](aws_resource_missing_tags.md)|Require specific tags for all AWS resource types that support them|| diff --git a/docs/rules/README.md.tmpl b/docs/rules/README.md.tmpl index d1e5d565..be079725 100644 --- a/docs/rules/README.md.tmpl +++ b/docs/rules/README.md.tmpl @@ -71,6 +71,7 @@ These rules enforce best practices and naming conventions: |[aws_iam_policy_attachment_exclusive_attachment](aws_iam_policy_attachment_exclusive_attachment.md)|Consider alternative resources to `aws_iam_policy_attachment`|| |[aws_iam_policy_document_gov_friendly_arns](aws_iam_policy_document_gov_friendly_arns.md)|Ensure `iam_policy_document` data sources do not contain `arn:aws:` ARN's|| |[aws_iam_policy_gov_friendly_arns](aws_iam_policy_gov_friendly_arns.md)|Ensure `iam_policy` resources do not contain `arn:aws:` ARN's|| +|[aws_iam_role_deprecated_policy_attributes](aws_iam_role_deprecated_policy_attributes.md)|Disallow using deprecated policy attributes of `aws_iam_role`|| |[aws_iam_role_policy_gov_friendly_arns](aws_iam_role_policy_gov_friendly_arns.md)|Ensure `iam_role_policy` resources do not contain `arn:aws:` ARN's|| |[aws_lambda_function_deprecated_runtime](aws_lambda_function_deprecated_runtime.md)|Disallow deprecated runtimes for Lambda Function|✔| |[aws_resource_missing_tags](aws_resource_missing_tags.md)|Require specific tags for all AWS resource types that support them|| diff --git a/docs/rules/aws_iam_role_deprecated_policy_attributes.md b/docs/rules/aws_iam_role_deprecated_policy_attributes.md new file mode 100644 index 00000000..420c3927 --- /dev/null +++ b/docs/rules/aws_iam_role_deprecated_policy_attributes.md @@ -0,0 +1,64 @@ +# aws_iam_role_deprecated_policy_attributes + +Disallow `inline_policy` configuration blocks and `managed_policy_arns` argument of the `aws_iam_role` resource. + +## Example + +```hcl +resource "aws_iam_role" "example" { + name = "example" + assume_role_policy = data.aws_iam_policy_document.instance_assume_role_policy.json # (not shown) + + inline_policy { + name = "my_inline_policy" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = ["ec2:Describe*"] + Effect = "Allow" + Resource = "*" + }, + ] + }) + } + + inline_policy { + name = "policy-8675309" + policy = data.aws_iam_policy_document.inline_policy.json # (not shown) + } + + managed_policy_arns = [] +} +``` + +``` +$ tflint + +3 issue(s) found: + +Warning: The inline_policy argument is deprecated. Use the aws_iam_role_policy resource instead. If Terraform should exclusively manage all inline policy associations (the current behavior of this argument), use the aws_iam_role_policies_exclusive resource as well. (aws_iam_role_deprecated_policy_attributes) + + on test.tf line 5: + 5: inline_policy { + +Warning: The inline_policy argument is deprecated. Use the aws_iam_role_policy resource instead. If Terraform should exclusively manage all inline policy associations (the current behavior of this argument), use the aws_iam_role_policies_exclusive resource as well. (aws_iam_role_deprecated_policy_attributes) + + on test.tf line 20: + 20: inline_policy { + +Warning: The managed_policy_arns argument is deprecated. Use the aws_iam_role_policy_attachment resource instead. If Terraform should exclusively manage all managed policy attachments (the current behavior of this argument), use the aws_iam_role_policy_attachments_exclusive resource as well. (aws_iam_role_deprecated_policy_attributes) + + on test.tf line 25: + 25: managed_policy_arns = [] +``` + +## Why + +The `inline_policy` and `managed_policy_arns` arguments are both deprecated since late 2024. + +## How To Fix + +For managing a role's inline policy, use the [aws_iam_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) resource instead. +For attaching managed policies to a role, use the [aws_iam_role_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) resource. diff --git a/rules/aws_iam_role_deprecated_policy_attributes.go b/rules/aws_iam_role_deprecated_policy_attributes.go new file mode 100644 index 00000000..0b4445ec --- /dev/null +++ b/rules/aws_iam_role_deprecated_policy_attributes.go @@ -0,0 +1,80 @@ +package rules + +import ( + "github.com/terraform-linters/tflint-plugin-sdk/hclext" + "github.com/terraform-linters/tflint-plugin-sdk/tflint" + "github.com/terraform-linters/tflint-ruleset-aws/project" +) + +// AwsIAMRoleDeprecatedPolicyAttributesRule checks that neither `inline_policy` or `managed_policy_arns` are used in an `aws_iam_role` +type AwsIAMRoleDeprecatedPolicyAttributesRule struct { + tflint.DefaultRule + + resourceType string +} + +// NewAwsIAMRoleDeprecatedPolicyAttributesRule returns new rule with default attributes +func NewAwsIAMRoleDeprecatedPolicyAttributesRule() *AwsIAMRoleDeprecatedPolicyAttributesRule { + return &AwsIAMRoleDeprecatedPolicyAttributesRule{ + resourceType: "aws_iam_role", + } +} + +// Name returns the rule name +func (r *AwsIAMRoleDeprecatedPolicyAttributesRule) Name() string { + return "aws_iam_role_deprecated_policy_attributes" +} + +// Enabled returns whether the rule is enabled by default +func (r *AwsIAMRoleDeprecatedPolicyAttributesRule) Enabled() bool { + // `inline_policy` and `managed_policy_arns` were deprecated in 5.68.0 and 5.72.0 respectively + // At time of writing, this is quite recent, so don't automatically enable the rule. + return false +} + +// Severity returns the rule severity +func (r *AwsIAMRoleDeprecatedPolicyAttributesRule) Severity() tflint.Severity { + return tflint.WARNING +} + +// Link returns the rule reference link +func (r *AwsIAMRoleDeprecatedPolicyAttributesRule) Link() string { + return project.ReferenceLink(r.Name()) +} + +// Check checks that aws_iam_role resources don't have any `inline_policy` blocks or the `managed_policy_arns` attribute +func (r *AwsIAMRoleDeprecatedPolicyAttributesRule) Check(runner tflint.Runner) error { + resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{ + Blocks: []hclext.BlockSchema{ + { + Type: "inline_policy", + }, + }, + Attributes: []hclext.AttributeSchema{ + { + Name: "managed_policy_arns", + }, + }, + }, nil) + if err != nil { + return err + } + + for _, resource := range resources.Blocks { + attribute, exists := resource.Body.Attributes["managed_policy_arns"] + + if exists { + if err := runner.EmitIssue(r, "The managed_policy_arns argument is deprecated. Use the aws_iam_role_policy_attachment resource instead. If Terraform should exclusively manage all managed policy attachments (the current behavior of this argument), use the aws_iam_role_policy_attachments_exclusive resource as well.", attribute.Range); err != nil { + return err + } + } + + for _, rule := range resource.Body.Blocks { + if err := runner.EmitIssue(r, "The inline_policy argument is deprecated. Use the aws_iam_role_policy resource instead. If Terraform should exclusively manage all inline policy associations (the current behavior of this argument), use the aws_iam_role_policies_exclusive resource as well.", rule.DefRange); err != nil { + return err + } + } + } + + return nil +} diff --git a/rules/aws_iam_role_deprecated_policy_attributes_test.go b/rules/aws_iam_role_deprecated_policy_attributes_test.go new file mode 100644 index 00000000..5d10b6e7 --- /dev/null +++ b/rules/aws_iam_role_deprecated_policy_attributes_test.go @@ -0,0 +1,106 @@ +package rules + +import ( + "testing" + + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/helper" +) + +func Test_AwsIAMRoleDeprecatedPolicyAttributes(t *testing.T) { + cases := []struct { + Name string + Content string + Expected helper.Issues + }{ + { + Name: "inline_policies", + Content: ` +resource "aws_iam_role" "example" { + name = "yak_role" + assume_role_policy = data.aws_iam_policy_document.instance_assume_role_policy.json # (not shown) + + inline_policy { + name = "my_inline_policy" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = ["ec2:Describe*"] + Effect = "Allow" + Resource = "*" + }, + ] + }) + } + + inline_policy { + name = "policy-8675309" + policy = data.aws_iam_policy_document.inline_policy.json + } +} + +data "aws_iam_policy_document" "inline_policy" { + statement { + actions = ["ec2:DescribeAccountAttributes"] + resources = ["*"] + } +} +`, + Expected: helper.Issues{ + { + Rule: NewAwsIAMRoleDeprecatedPolicyAttributesRule(), + Message: "The inline_policy argument is deprecated. Use the aws_iam_role_policy resource instead. If Terraform should exclusively manage all inline policy associations (the current behavior of this argument), use the aws_iam_role_policies_exclusive resource as well.", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 6, Column: 3}, + End: hcl.Pos{Line: 6, Column: 16}, + }, + }, + { + Rule: NewAwsIAMRoleDeprecatedPolicyAttributesRule(), + Message: "The inline_policy argument is deprecated. Use the aws_iam_role_policy resource instead. If Terraform should exclusively manage all inline policy associations (the current behavior of this argument), use the aws_iam_role_policies_exclusive resource as well.", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 21, Column: 3}, + End: hcl.Pos{Line: 21, Column: 16}, + }, + }, + }, + }, + { + Name: "managed_policy_arns", + Content: ` +resource "aws_iam_role" "example" { + name = "yak_role" + assume_role_policy = data.aws_iam_policy_document.instance_assume_role_policy.json # (not shown) + managed_policy_arns = [] +} +`, + Expected: helper.Issues{ + { + Rule: NewAwsIAMRoleDeprecatedPolicyAttributesRule(), + Message: "The managed_policy_arns argument is deprecated. Use the aws_iam_role_policy_attachment resource instead. If Terraform should exclusively manage all managed policy attachments (the current behavior of this argument), use the aws_iam_role_policy_attachments_exclusive resource as well.", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 5, Column: 3}, + End: hcl.Pos{Line: 5, Column: 27}, + }, + }, + }, + }, + } + + rule := NewAwsIAMRoleDeprecatedPolicyAttributesRule() + + for _, tc := range cases { + runner := helper.TestRunner(t, map[string]string{"resource.tf": tc.Content}) + + if err := rule.Check(runner); err != nil { + t.Fatalf("Unexpected error occurred: %s", err) + } + + helper.AssertIssues(t, tc.Expected, runner.Issues) + } +} diff --git a/rules/provider.go b/rules/provider.go index 3b4dff65..654407b8 100644 --- a/rules/provider.go +++ b/rules/provider.go @@ -43,6 +43,7 @@ var manualRules = []tflint.Rule{ NewAwsProviderMissingDefaultTagsRule(), NewAwsSecurityGroupInlineRulesRule(), NewAwsSecurityGroupRuleDeprecatedRule(), + NewAwsIAMRoleDeprecatedPolicyAttributesRule(), } // Rules is a list of all rules