Skip to content

Commit 1538e44

Browse files
kayman-mkLiam Clancy (metafeather)metafeather
authored
feat: add aws_iam_policy_attachment_exclusive_attachment rule (#786)
* Add aws_iam_policy_attachment_has_alternatives rule * fix: Since the name is not used, it seems better to simply EmitIssue if the resource exists. * fix: It would be nice to have a fixed case with no warnings. * fix: Or aws_iam_policy_attachment_exclusive_attachment may be better. I prefer names that are descriptive of what issue we are warning about. Additionally, the prefix should preferably match the resource name. What do you think? * rename in docs * fix check comment * fix docs * fix review issues * restore .gitignore * fix test * add updated warning message to docs * fetch the resource only --------- Co-authored-by: Liam Clancy (metafeather) <[email protected]> Co-authored-by: Liam Clancy <[email protected]>
1 parent 83ceb86 commit 1538e44

6 files changed

+166
-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_exclusive_attachment](aws_iam_policy_attachment_exclusive_attachment.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_exclusive_attachment](aws_iam_policy_attachment_exclusive_attachment.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: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# aws_iam_policy_attachment_exclusive_attachment
2+
3+
This rule checks whether the `aws_iam_policy_attachment` resource is used.
4+
5+
The `aws_iam_policy_attachment` resource creates exclusive attachments for IAM policies. Within the entire AWS account, all users, roles, and groups that a single policy is attached to must be specified by a single aws_iam_policy_attachment resource.
6+
7+
## Configuration
8+
9+
```hcl
10+
rule "aws_iam_policy_attachment_exclusive_attachment" {
11+
enabled = true
12+
}
13+
```
14+
15+
## Example
16+
17+
```hcl
18+
resource "aws_iam_policy_attachment" "attachment" {
19+
name = "test_attachment"
20+
}
21+
```
22+
23+
```shell
24+
$ tflint
25+
1 issue(s) found:
26+
Warning: Within the entire AWS account, all users, roles, and groups that a single policy is attached to must be specified by a single aws_iam_policy_attachment resource. Consider aws_iam_role_policy_attachment, aws_iam_user_policy_attachment, or aws_iam_group_policy_attachment instead. (aws_iam_policy_attachment_has_alternatives)
27+
on template.tf line 2:
28+
2: resource "aws_iam_policy_attachment" "attachment" {
29+
```
30+
31+
## Why
32+
33+
The [`aws_iam_policy_attachment`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) resource creates exclusive attachments of IAM policies. Across the entire AWS account, all 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.
34+
35+
## How To Fix
36+
37+
Consider using `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: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
// AwsIAMPolicyAttachmentExclusiveAttachmentRule warns that the resource has alternatives recommended
10+
type AwsIAMPolicyAttachmentExclusiveAttachmentRule struct {
11+
tflint.DefaultRule
12+
13+
resourceType string
14+
attributeName string
15+
}
16+
17+
// AwsIAMPolicyAttachmentExclusiveAttachmentRule returns new rule with default attributes
18+
func NewAwsIAMPolicyAttachmentExclusiveAttachmentRule() *AwsIAMPolicyAttachmentExclusiveAttachmentRule {
19+
return &AwsIAMPolicyAttachmentExclusiveAttachmentRule{
20+
resourceType: "aws_iam_policy_attachment",
21+
attributeName: "name",
22+
}
23+
}
24+
25+
// Name returns the rule name
26+
func (r *AwsIAMPolicyAttachmentExclusiveAttachmentRule) Name() string {
27+
return "aws_iam_policy_attachment_exclusive_attachment"
28+
}
29+
30+
// Enabled returns whether the rule is enabled by default
31+
func (r *AwsIAMPolicyAttachmentExclusiveAttachmentRule) Enabled() bool {
32+
return false
33+
}
34+
35+
// Severity returns the rule severity
36+
func (r *AwsIAMPolicyAttachmentExclusiveAttachmentRule) Severity() tflint.Severity {
37+
return tflint.WARNING
38+
}
39+
40+
// Link returns the rule reference link
41+
func (r *AwsIAMPolicyAttachmentExclusiveAttachmentRule) Link() string {
42+
return project.ReferenceLink(r.Name())
43+
}
44+
45+
// Check that the resource is not used
46+
func (r *AwsIAMPolicyAttachmentExclusiveAttachmentRule) Check(runner tflint.Runner) error {
47+
resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{}, nil)
48+
if err != nil {
49+
return err
50+
}
51+
52+
for _, resource := range resources.Blocks {
53+
runner.EmitIssue(
54+
r,
55+
"Within the entire AWS account, all users, roles, and groups that a single policy is attached to must be specified by a single aws_iam_policy_attachment resource. Consider aws_iam_role_policy_attachment, aws_iam_user_policy_attachment, or aws_iam_group_policy_attachment instead.",
56+
resource.DefRange,
57+
)
58+
59+
if err != nil {
60+
return err
61+
}
62+
}
63+
64+
return nil
65+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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_AwsIAMPolicyAttachmentExclusiveAttachmentRule(t *testing.T) {
13+
rand.Seed(time.Now().UnixNano())
14+
15+
cases := []struct {
16+
Name string
17+
Content string
18+
Expected helper.Issues
19+
}{
20+
{
21+
Name: "resource has alternatives",
22+
Content: `
23+
resource "aws_iam_policy_attachment" "attachment" {
24+
name = "test_attachment"
25+
}
26+
`,
27+
Expected: helper.Issues{
28+
{
29+
Rule: NewAwsIAMPolicyAttachmentExclusiveAttachmentRule(),
30+
Message: "Within the entire AWS account, all users, roles, and groups that a single policy is attached to must be specified by a single aws_iam_policy_attachment resource. Consider aws_iam_role_policy_attachment, aws_iam_user_policy_attachment, or aws_iam_group_policy_attachment instead.",
31+
Range: hcl.Range{
32+
Filename: "resource.tf",
33+
Start: hcl.Pos{Line: 2, Column: 1},
34+
End: hcl.Pos{Line: 2, Column: 50},
35+
},
36+
},
37+
},
38+
},
39+
{
40+
Name: "no issues with resource",
41+
Content: `
42+
resource "aws_iam_role_policy_attachment" "attachment" {
43+
role = "test_role"
44+
}
45+
`,
46+
Expected: helper.Issues{},
47+
},
48+
}
49+
50+
rule := NewAwsIAMPolicyAttachmentExclusiveAttachmentRule()
51+
52+
for _, tc := range cases {
53+
runner := helper.TestRunner(t, map[string]string{"resource.tf": tc.Content})
54+
55+
if err := rule.Check(runner); err != nil {
56+
t.Fatalf("Unexpected error occurred: %s", err)
57+
}
58+
59+
helper.AssertIssues(t, tc.Expected, runner.Issues)
60+
}
61+
}

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+
NewAwsIAMPolicyAttachmentExclusiveAttachmentRule(),
3435
NewAwsIAMPolicySidInvalidCharactersRule(),
3536
NewAwsIAMPolicyTooLongPolicyRule(),
3637
NewAwsLambdaFunctionDeprecatedRuntimeRule(),

0 commit comments

Comments
 (0)