diff --git a/rules/no_double_quotes_in_ignore_changes.go b/rules/no_double_quotes_in_ignore_changes.go new file mode 100644 index 0000000..5fba887 --- /dev/null +++ b/rules/no_double_quotes_in_ignore_changes.go @@ -0,0 +1,111 @@ +package rules + +import ( + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/terraform-linters/tflint-plugin-sdk/hclext" + "github.com/terraform-linters/tflint-plugin-sdk/tflint" +) + +var noDoubleQuotesInIgnoreChangesBodySchema = &hclext.BodySchema{ + Blocks: []hclext.BlockSchema{ + { + Type: "resource", + LabelNames: []string{"type", "name"}, + Body: &hclext.BodySchema{ + Blocks: []hclext.BlockSchema{ + { + Type: "lifecycle", + Body: &hclext.BodySchema{ + Attributes: []hclext.AttributeSchema{ + {Name: "ignore_changes"}, + }, + }, + }, + }, + }, + }, + }, +} + +var _ tflint.Rule = new(NoDoubleQuotesInIgnoreChangesRule) + +type NoDoubleQuotesInIgnoreChangesRule struct { + tflint.DefaultRule +} + +func NewNoDoubleQuotesInIgnoreChangesRule() *NoDoubleQuotesInIgnoreChangesRule { + return &NoDoubleQuotesInIgnoreChangesRule{} +} + +func (t *NoDoubleQuotesInIgnoreChangesRule) Name() string { + return "required_module_source_tfnfr10" +} + +func (t *NoDoubleQuotesInIgnoreChangesRule) Link() string { + return "https://azure.github.io/Azure-Verified-Modules/specs/terraform/#id-tfnfr10---category-code-style---no-double-quotes-in-ignore_changes" +} + +func (t *NoDoubleQuotesInIgnoreChangesRule) Enabled() bool { + return true +} + +func (t *NoDoubleQuotesInIgnoreChangesRule) Severity() tflint.Severity { + return tflint.ERROR +} + +func (t *NoDoubleQuotesInIgnoreChangesRule) Check(r tflint.Runner) error { + path, err := r.GetModulePath() + if err != nil { + return err + } + if !path.IsRoot() { + return nil + } + + body, err := r.GetModuleContent( + noDoubleQuotesInIgnoreChangesBodySchema, + &tflint.GetModuleContentOption{ExpandMode: tflint.ExpandModeNone}) + if err != nil { + return err + } + + var errList error + for _, block := range body.Blocks { + if block.Type != "resource" { + continue + } + + if subErr := t.checkBlock(r, block); subErr != nil { + errList = multierror.Append(errList, subErr) + } + } + + return errList +} + +func (t *NoDoubleQuotesInIgnoreChangesRule) checkBlock(r tflint.Runner, block *hclext.Block) error { + for _, subBlock := range block.Body.Blocks { + ignoreChanges, exists := subBlock.Body.Attributes["ignore_changes"] + if !exists { + return nil + } + + ignoreChangesExpr, ok := ignoreChanges.Expr.(*hclsyntax.TupleConsExpr) + if !ok { + return nil + } + + for _, itemExpr := range ignoreChangesExpr.Exprs { + if _, ok := itemExpr.(*hclsyntax.ScopeTraversalExpr); !ok { + return r.EmitIssue( + t, + "ignore_changes shouldn't include double quotes", + ignoreChanges.Range, + ) + } + } + } + + return nil +} diff --git a/rules/no_double_quotes_in_ignore_changes_test.go b/rules/no_double_quotes_in_ignore_changes_test.go new file mode 100644 index 0000000..2436858 --- /dev/null +++ b/rules/no_double_quotes_in_ignore_changes_test.go @@ -0,0 +1,80 @@ +package rules_test + +import ( + "testing" + + "github.com/Azure/tflint-ruleset-avm/rules" + "github.com/terraform-linters/tflint-plugin-sdk/helper" +) + +func TestNoDoubleQuotesInIgnoreChanges(t *testing.T) { + cases := []struct { + desc string + config string + issues helper.Issues + }{ + { + desc: "no double quotes, ok", + config: `resource "azurerm_resource_group" "test" { + name = "acctestRG" + location = "westeurope" + + lifecycle { + ignore_changes = [tags, location] + } +}`, + issues: helper.Issues{}, + }, + { + desc: "double quotes exist, not ok", + config: `resource "azurerm_resource_group" "test" { + name = "acctestRG" + location = "westeurope" + + lifecycle { + ignore_changes = ["tags", "location"] + } +}`, + issues: helper.Issues{ + { + Rule: rules.NewNoDoubleQuotesInIgnoreChangesRule(), + Message: "ignore_changes shouldn't include double quotes", + }, + }, + }, + { + desc: "some item includes double quotes, not ok", + config: `resource "azurerm_resource_group" "test" { + name = "acctestRG" + location = "westeurope" + + lifecycle { + ignore_changes = ["tags", location] + } +}`, + issues: helper.Issues{ + { + Rule: rules.NewNoDoubleQuotesInIgnoreChangesRule(), + Message: "ignore_changes shouldn't include double quotes", + }, + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.desc, func(t *testing.T) { + t.Parallel() + rule := rules.NewNoDoubleQuotesInIgnoreChangesRule() + filename := "terraform.tf" + + runner := helper.TestRunner(t, map[string]string{filename: tc.config}) + + if err := rule.Check(runner); err != nil { + t.Fatalf("Unexpected error occurred: %s", err) + } + + helper.AssertIssuesWithoutRange(t, tc.issues, runner.Issues) + }) + } +} diff --git a/rules/rule_register.go b/rules/rule_register.go index 11b9a54..2f50665 100644 --- a/rules/rule_register.go +++ b/rules/rule_register.go @@ -25,6 +25,7 @@ var Rules = func() []tflint.Rule { Wrap(azurerm.NewAzurermResourceTagRule()), NewTerraformDotTfRule(), NewModuleSourceRule(), + NewNoDoubleQuotesInIgnoreChangesRule(), NewProviderVersionRule("modtm", "Azure/modtm", "~> 0.3", []string{ "0.2.999", "1.0.0",