Skip to content

Commit a637712

Browse files
author
Liam Clancy (metafeather)
committed
Add aws_iam_policy_attachment_has_alternatives rule
1 parent 032c405 commit a637712

File tree

6 files changed

+164
-0
lines changed

6 files changed

+164
-0
lines changed

docs/rules/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ These rules enforce best practices and naming conventions:
6868
|[aws_elasticache_replication_group_previous_type](aws_elasticache_replication_group_previous_type.md)|Disallow using previous node types||
6969
|[aws_elasticache_replication_group_default_parameter_group](aws_elasticache_replication_group_default_parameter_group.md)|Disallow using default parameter group||
7070
|[aws_instance_previous_type](aws_instance_previous_type.md)|Disallow using previous generation instance types||
71+
|[aws_iam_policy_attachment_has_alternatives](aws_iam_policy_attachment_has_alternatives.md)|Consider alternative resources to `aws_iam_policy_attachment`||
7172
|[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||
7273
|[aws_iam_policy_gov_friendly_arns](aws_iam_policy_gov_friendly_arns.md)|Ensure `iam_policy` resources do not contain `arn:aws:` ARN's||
7374
|[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||

docs/rules/README.md.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ These rules enforce best practices and naming conventions:
6868
|[aws_elasticache_replication_group_previous_type](aws_elasticache_replication_group_previous_type.md)|Disallow using previous node types|✔|
6969
|[aws_elasticache_replication_group_default_parameter_group](aws_elasticache_replication_group_default_parameter_group.md)|Disallow using default parameter group|✔|
7070
|[aws_instance_previous_type](aws_instance_previous_type.md)|Disallow using previous generation instance types|✔|
71+
|[aws_iam_policy_attachment_has_alternatives](aws_iam_policy_attachment_has_alternatives.md)|Consider alternative resources to `aws_iam_policy_attachment`||
7172
|[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||
7273
|[aws_iam_policy_gov_friendly_arns](aws_iam_policy_gov_friendly_arns.md)|Ensure `iam_policy` resources do not contain `arn:aws:` ARN's||
7374
|[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||
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# aws_iam_policy_attachment_has_alternatives
2+
3+
Consider alternative resources to `aws_iam_policy_attachment`.
4+
5+
## Configuration
6+
7+
```hcl
8+
rule "aws_iam_policy_attachment_has_alternatives" {
9+
enabled = true
10+
}
11+
```
12+
13+
## Example
14+
15+
```hcl
16+
resource "aws_iam_policy_attachment" "attachment" {
17+
name = "test_attachment"
18+
}
19+
```
20+
21+
```shell
22+
$ tflint
23+
1 issue(s) found:
24+
Warning: Consider aws_iam_role_policy_attachment, aws_iam_user_policy_attachment, or aws_iam_group_policy_attachment instead. (aws_iam_policy_attachment_has_alternatives)
25+
on template.tf line 2:
26+
2: name "test_attachment" // Consider alternatives!
27+
28+
```
29+
30+
## Why
31+
32+
The `aws_iam_policy_attachment` resource creates exclusive attachments of IAM policies. Across the entire AWS account, all of the users/roles/groups to which a single policy is attached must be declared by a single `aws_iam_policy_attachment` resource. This means that even any users/roles/groups that have the attached policy via any other mechanism (including other Terraform resources) will have that attached policy revoked by this resource. https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment
33+
34+
## How To Fix
35+
36+
Consider `aws_iam_role_policy_attachment`, `aws_iam_user_policy_attachment`, or `aws_iam_group_policy_attachment` instead. These resources do not enforce exclusive attachment of an IAM policy.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package rules
2+
3+
import (
4+
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
5+
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
6+
"github.com/terraform-linters/tflint-ruleset-aws/project"
7+
)
8+
9+
// AwsIAMPolicyAttachmentHasAlternativesRule warns that the resource has alternatives recommended
10+
type AwsIAMPolicyAttachmentHasAlternativesRule struct {
11+
tflint.DefaultRule
12+
13+
resourceType string
14+
attributeName string
15+
}
16+
17+
// AwsIAMPolicyAttachmentHasAlternativesRule returns new rule with default attributes
18+
func NewAwsIAMPolicyAttachmentHasAlternativesRule() *AwsIAMPolicyAttachmentHasAlternativesRule {
19+
return &AwsIAMPolicyAttachmentHasAlternativesRule{
20+
resourceType: "aws_iam_policy_attachment",
21+
attributeName: "name",
22+
}
23+
}
24+
25+
// Name returns the rule name
26+
func (r *AwsIAMPolicyAttachmentHasAlternativesRule) Name() string {
27+
return "aws_iam_policy_attachment_has_alternatives"
28+
}
29+
30+
// Enabled returns whether the rule is enabled by default
31+
func (r *AwsIAMPolicyAttachmentHasAlternativesRule) Enabled() bool {
32+
return false
33+
}
34+
35+
// Severity returns the rule severity
36+
func (r *AwsIAMPolicyAttachmentHasAlternativesRule) Severity() tflint.Severity {
37+
return tflint.WARNING
38+
}
39+
40+
// Link returns the rule reference link
41+
func (r *AwsIAMPolicyAttachmentHasAlternativesRule) Link() string {
42+
return project.ReferenceLink(r.Name())
43+
}
44+
45+
// Check checks the length of the policy
46+
func (r *AwsIAMPolicyAttachmentHasAlternativesRule) Check(runner tflint.Runner) error {
47+
resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{
48+
Attributes: []hclext.AttributeSchema{{Name: r.attributeName}},
49+
}, nil)
50+
if err != nil {
51+
return err
52+
}
53+
54+
for _, resource := range resources.Blocks {
55+
attribute, exists := resource.Body.Attributes[r.attributeName]
56+
if !exists {
57+
continue
58+
}
59+
60+
var name string
61+
err := runner.EvaluateExpr(attribute.Expr, &name, nil)
62+
runner.EmitIssue(
63+
r,
64+
"Consider aws_iam_role_policy_attachment, aws_iam_user_policy_attachment, or aws_iam_group_policy_attachment instead.",
65+
attribute.Expr.Range(),
66+
)
67+
68+
if err != nil {
69+
return err
70+
}
71+
}
72+
73+
return nil
74+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package rules
2+
3+
import (
4+
"math/rand"
5+
"testing"
6+
"time"
7+
8+
hcl "github.com/hashicorp/hcl/v2"
9+
"github.com/terraform-linters/tflint-plugin-sdk/helper"
10+
)
11+
12+
func Test_AwsIAMPolicyAttachmentHasAlternativesRule(t *testing.T) {
13+
rand.Seed(time.Now().UnixNano())
14+
cases := []struct {
15+
Name string
16+
Content string
17+
Expected helper.Issues
18+
}{
19+
{
20+
Name: "resource has alternatives",
21+
Content: `
22+
resource "aws_iam_policy_attachment" "attachment" {
23+
name = "test_attachment"
24+
}
25+
`,
26+
Expected: helper.Issues{
27+
{
28+
Rule: NewAwsIAMPolicyAttachmentHasAlternativesRule(),
29+
Message: "Consider aws_iam_role_policy_attachment, aws_iam_user_policy_attachment, or aws_iam_group_policy_attachment instead.",
30+
Range: hcl.Range{
31+
Filename: "resource.tf",
32+
Start: hcl.Pos{Line: 3, Column: 9},
33+
End: hcl.Pos{Line: 3, Column: 26},
34+
},
35+
},
36+
},
37+
},
38+
}
39+
40+
rule := NewAwsIAMPolicyAttachmentHasAlternativesRule()
41+
42+
for _, tc := range cases {
43+
runner := helper.TestRunner(t, map[string]string{"resource.tf": tc.Content})
44+
45+
if err := rule.Check(runner); err != nil {
46+
t.Fatalf("Unexpected error occurred: %s", err)
47+
}
48+
49+
helper.AssertIssues(t, tc.Expected, runner.Issues)
50+
}
51+
}

rules/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ var manualRules = []tflint.Rule{
3131
NewAwsElastiCacheReplicationGroupDefaultParameterGroupRule(),
3232
NewAwsElastiCacheReplicationGroupInvalidTypeRule(),
3333
NewAwsElastiCacheReplicationGroupPreviousTypeRule(),
34+
NewAwsIAMPolicyAttachmentHasAlternativesRule(),
3435
NewAwsIAMPolicySidInvalidCharactersRule(),
3536
NewAwsIAMPolicyTooLongPolicyRule(),
3637
NewAwsLambdaFunctionDeprecatedRuntimeRule(),

0 commit comments

Comments
 (0)