Skip to content

Commit ba581e9

Browse files
kayman-mkwata727
andauthored
feat: add aws_security_group_inline_rules rule (#793)
* add rule AwsSecurityGroupEgressAndIngressBlocksDeprecatedRule * Update README.md * Update README.md.tmpl * update docs * Update aws_security_group_egress_and_ingress_blocks_deprecated.md * Update rules/aws_security_group_egress_and_ingress_blocks_deprecated.go Co-authored-by: Kazuma Watanabe <[email protected]> * address review comments * docs reworked --------- Co-authored-by: Kazuma Watanabe <[email protected]>
1 parent 1c7b473 commit ba581e9

File tree

6 files changed

+225
-0
lines changed

6 files changed

+225
-0
lines changed

docs/rules/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ These rules enforce best practices and naming conventions:
7575
|[aws_lambda_function_deprecated_runtime](aws_lambda_function_deprecated_runtime.md)|Disallow deprecated runtimes for Lambda Function||
7676
|[aws_resource_missing_tags](aws_resource_missing_tags.md)|Require specific tags for all AWS resource types that support them||
7777
|[aws_s3_bucket_name](aws_s3_bucket_name.md)|Ensures all S3 bucket names match the naming rules||
78+
|[aws_security_group_inline_rules](aws_security_group_inline_rules.md)|Disallow `ingress` and `egress` arguments of the `aws_security_group` resource||
7879
|[aws_security_group_rule_deprecated](aws_security_group_rule_deprecated.md)|Disallow using `aws_security_group_rule` resource||
7980
|[aws_provider_missing_default_tags](aws_provider_missing_default_tags.md)|Require specific tags for all AWS providers default tags||
8081

docs/rules/README.md.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ These rules enforce best practices and naming conventions:
7575
|[aws_lambda_function_deprecated_runtime](aws_lambda_function_deprecated_runtime.md)|Disallow deprecated runtimes for Lambda Function|✔|
7676
|[aws_resource_missing_tags](aws_resource_missing_tags.md)|Require specific tags for all AWS resource types that support them||
7777
|[aws_s3_bucket_name](aws_s3_bucket_name.md)|Ensures all S3 bucket names match the naming rules|✔|
78+
|[aws_security_group_inline_rules](aws_security_group_inline_rules.md)|Disallow `ingress` and `egress` arguments of the `aws_security_group` resource||
7879
|[aws_security_group_rule_deprecated](aws_security_group_rule_deprecated.md)|Disallow using `aws_security_group_rule` resource||
7980
|[aws_provider_missing_default_tags](aws_provider_missing_default_tags.md)|Require specific tags for all AWS providers default tags||
8081

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# aws_security_group_inline_rules
2+
3+
Disallow `ingress` and `egress` arguments of the `aws_security_group` resource.
4+
5+
6+
## Example
7+
8+
```hcl
9+
resource "aws_security_group" "foo" {
10+
name = "test"
11+
12+
egress {
13+
from_port = 0
14+
to_port = 0
15+
protocol = "-1"
16+
cidr_blocks = ["0.0.0.0/0"]
17+
}
18+
19+
ingress {
20+
from_port = 443
21+
to_port = 443
22+
protocol = "tcp"
23+
cidr_blocks = ["0.0.0.0/0"]
24+
}
25+
}
26+
```
27+
28+
```
29+
$ tflint
30+
2 issue(s) found:
31+
32+
Notice: Replace this egress block with aws_vpc_security_group_egress_rule. (aws_security_group_inline_rules)
33+
34+
on test.tf line 4:
35+
4: egress {
36+
37+
Notice: Replace this ingress block with aws_vpc_security_group_ingress_rule. (aws_security_group_inline_rules)
38+
39+
on test.tf line 11:
40+
11: ingress {
41+
42+
```
43+
44+
## Why
45+
46+
In-line rules are difficult to manage and maintain, especially when multiple CIDR blocks are used. They lack unique IDs, tags, and descriptions, which makes it hard to identify and manage them.
47+
48+
See [best practices](https://registry.terraform.io/providers/hashicorp/aws/5.82.2/docs/resources/security_group).
49+
50+
## How To Fix
51+
52+
Replace an `egress` block by
53+
54+
```hcl
55+
resource "aws_vpc_security_group_egress_rule" "example" {
56+
security_group_id = aws_security_group.example.id
57+
58+
cidr_ipv4 = "0.0.0.0/0"
59+
from_port = 443
60+
ip_protocol = "tcp"
61+
to_port = 443
62+
}
63+
```
64+
65+
using the attributes according to your code. For `ingress` blocks use `aws_vpc_security_group_ingress_rule` in the same way.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package rules
2+
3+
import (
4+
"fmt"
5+
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
6+
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
7+
"github.com/terraform-linters/tflint-ruleset-aws/project"
8+
)
9+
10+
// AwsSecurityGroupInlineRulesRule checks that egress and ingress blocks are not used in aws_security_group
11+
type AwsSecurityGroupInlineRulesRule struct {
12+
tflint.DefaultRule
13+
14+
resourceType string
15+
}
16+
17+
// NewAwsSecurityGroupInlineRulesRule returns new rule with default attributes
18+
func NewAwsSecurityGroupInlineRulesRule() *AwsSecurityGroupInlineRulesRule {
19+
return &AwsSecurityGroupInlineRulesRule{
20+
resourceType: "aws_security_group",
21+
}
22+
}
23+
24+
// Name returns the rule name
25+
func (r *AwsSecurityGroupInlineRulesRule) Name() string {
26+
return "aws_security_group_inline_rules"
27+
}
28+
29+
// Enabled returns whether the rule is enabled by default
30+
func (r *AwsSecurityGroupInlineRulesRule) Enabled() bool {
31+
return false
32+
}
33+
34+
// Severity returns the rule severity
35+
func (r *AwsSecurityGroupInlineRulesRule) Severity() tflint.Severity {
36+
return tflint.WARNING
37+
}
38+
39+
// Link returns the rule reference link
40+
func (r *AwsSecurityGroupInlineRulesRule) Link() string {
41+
return project.ReferenceLink(r.Name())
42+
}
43+
44+
// Check that no egress and ingress blocks are used in a aws_security_group
45+
func (r *AwsSecurityGroupInlineRulesRule) Check(runner tflint.Runner) error {
46+
resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{
47+
Blocks: []hclext.BlockSchema{
48+
{Type: "egress"},
49+
{Type: "ingress"},
50+
},
51+
}, nil)
52+
if err != nil {
53+
return err
54+
}
55+
56+
for _, resource := range resources.Blocks {
57+
for _, block := range resource.Body.Blocks {
58+
runner.EmitIssue(
59+
r,
60+
fmt.Sprintf("Replace this %s block with aws_vpc_security_group_%s_rule.", block.Type, block.Type),
61+
block.DefRange,
62+
)
63+
}
64+
}
65+
66+
return nil
67+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package rules
2+
3+
import (
4+
"testing"
5+
6+
hcl "github.com/hashicorp/hcl/v2"
7+
"github.com/terraform-linters/tflint-plugin-sdk/helper"
8+
)
9+
10+
func Test_AwsSecurityGroupInlineRulesRule(t *testing.T) {
11+
cases := []struct {
12+
Name string
13+
Content string
14+
Expected helper.Issues
15+
}{
16+
{
17+
Name: "finds deprecated ingress block",
18+
Content: `
19+
resource "aws_security_group" "test" {
20+
name = "test"
21+
22+
ingress {
23+
from_port = 0
24+
to_port = 0
25+
protocol = "-1"
26+
cidr_blocks = ["0.0.0.0/0"]
27+
}
28+
}
29+
`,
30+
Expected: helper.Issues{
31+
{
32+
Rule: NewAwsSecurityGroupInlineRulesRule(),
33+
Message: "Replace this ingress block with aws_vpc_security_group_ingress_rule.",
34+
Range: hcl.Range{
35+
Filename: "resource.tf",
36+
Start: hcl.Pos{Line: 5, Column: 3},
37+
End: hcl.Pos{Line: 5, Column: 10},
38+
},
39+
},
40+
},
41+
},
42+
{
43+
Name: "finds deprecated egress block",
44+
Content: `
45+
resource "aws_security_group" "test" {
46+
name = "test"
47+
48+
egress {
49+
from_port = 0
50+
to_port = 0
51+
protocol = "-1"
52+
cidr_blocks = ["0.0.0.0/0"]
53+
}
54+
}
55+
`,
56+
Expected: helper.Issues{
57+
{
58+
Rule: NewAwsSecurityGroupInlineRulesRule(),
59+
Message: "Replace this egress block with aws_vpc_security_group_egress_rule.",
60+
Range: hcl.Range{
61+
Filename: "resource.tf",
62+
Start: hcl.Pos{Line: 5, Column: 3},
63+
End: hcl.Pos{Line: 5, Column: 9},
64+
},
65+
},
66+
},
67+
},
68+
{
69+
Name: "everything is fine",
70+
Content: `
71+
resource "aws_security_group" "test" {
72+
name = "test"
73+
}
74+
`,
75+
Expected: helper.Issues{},
76+
},
77+
}
78+
79+
rule := NewAwsSecurityGroupInlineRulesRule()
80+
81+
for _, tc := range cases {
82+
runner := helper.TestRunner(t, map[string]string{"resource.tf": tc.Content})
83+
84+
if err := rule.Check(runner); err != nil {
85+
t.Fatalf("Unexpected error occurred: %s", err)
86+
}
87+
88+
helper.AssertIssues(t, tc.Expected, runner.Issues)
89+
}
90+
}

rules/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ var manualRules = []tflint.Rule{
4141
NewAwsSecurityGroupInvalidProtocolRule(),
4242
NewAwsSecurityGroupRuleInvalidProtocolRule(),
4343
NewAwsProviderMissingDefaultTagsRule(),
44+
NewAwsSecurityGroupInlineRulesRule(),
4445
NewAwsSecurityGroupRuleDeprecatedRule(),
4546
}
4647

0 commit comments

Comments
 (0)