Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aws_elastic_beanstalk_environment_invalid_name_format #315

Merged
merged 15 commits into from
Mar 27, 2022
1 change: 1 addition & 0 deletions docs/rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ These rules warn of possible errors that can occur at `terraform apply`. Rules m
|[aws_db_instance_invalid_type](aws_db_instance_invalid_type.md)|Disallow using invalid instance class||✔|
|aws_db_instance_invalid_vpc_security_group|Disallow using invalid VPC security groups|✔|✔|
|aws_dynamodb_table_invalid_stream_view_type|Disallow using invalid stream view types for DynamoDB||✔|
|[aws_elastic_beanstalk_environment_invalid_name_format](aws_elastic_beanstalk_environment_invalid_name_format.md)|Disallow invalid environment name||✔|
|aws_elasticache_cluster_invalid_parameter_group|Disallow using invalid parameter group|✔|✔|
|aws_elasticache_cluster_invalid_security_group|Disallow using invalid security groups|✔|✔|
|aws_elasticache_cluster_invalid_subnet_group|Disallow using invalid subnet group|✔|✔|
Expand Down
1 change: 1 addition & 0 deletions docs/rules/README.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ These rules warn of possible errors that can occur at `terraform apply`. Rules m
|[aws_db_instance_invalid_type](aws_db_instance_invalid_type.md)|Disallow using invalid instance class||✔|
|aws_db_instance_invalid_vpc_security_group|Disallow using invalid VPC security groups|✔|✔|
|aws_dynamodb_table_invalid_stream_view_type|Disallow using invalid stream view types for DynamoDB||✔|
|[aws_elastic_beanstalk_environment_invalid_name_format](aws_elastic_beanstalk_environment_invalid_name_format.md)|Disallow invalid environment name||✔|
|aws_elasticache_cluster_invalid_parameter_group|Disallow using invalid parameter group|✔|✔|
|aws_elasticache_cluster_invalid_security_group|Disallow using invalid security groups|✔|✔|
|aws_elasticache_cluster_invalid_subnet_group|Disallow using invalid subnet group|✔|✔|
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# aws_elastic_beanstalk_environment_invalid_name_format

Disallow invalid Elastic Beanstalk environment name

## Example

```hcl
resource "aws_elastic_beanstalk_environment" "tfenvtest" {
name = "env_name_underscores"
application = "example-app"
solution_stack_name = "64bit Amazon Linux 2015.03 v2.0.3 running Go 1.4"
}
```

```
$ tflint
1 issue(s) found:

Error: env_name_underscores does not match constraint: must contain only letters, digits, and
the dash character and may not start or end with a dash
(^[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]$) (aws_elastic_beanstalk_environment_invalid_name_format)

on example.tf line 2:
2: name = "env_name_underscores"

```

## Why

When attempting to create the resource, Terraform will return the error:
```
Error: InvalidParameterValue: Value env_name_underscores at 'EnvironmentName' failed to satisfy
constraint: Member must contain only letters, digits, and the dash character and may not start
or end with a dash
status code: 400
```

## How To Fix

Ensure your environment name consists only of letters, digits, and the dash character, and does
not start or end with a dash.
The regex used is `^[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]$`
85 changes: 85 additions & 0 deletions rules/aws_elastic_beanstalk_environment_invalid_name_format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package rules

import (
"fmt"
"regexp"

"github.com/terraform-linters/tflint-plugin-sdk/hclext"
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
"github.com/terraform-linters/tflint-ruleset-aws/project"
)

// AwsElasticBeanstalkEnvironmentInvalidNameFormatRule checks EB environment name matches a pattern
type AwsElasticBeanstalkEnvironmentInvalidNameFormatRule struct {
tflint.DefaultRule

resourceType string
attributeName string
pattern *regexp.Regexp
}

// NewAwsElasticBeanstalkEnvironmentInvalidNameFormatRule returns new rule with default attributes
func NewAwsElasticBeanstalkEnvironmentInvalidNameFormatRule() *AwsElasticBeanstalkEnvironmentInvalidNameFormatRule {
return &AwsElasticBeanstalkEnvironmentInvalidNameFormatRule{
resourceType: "aws_elastic_beanstalk_environment",
attributeName: "name",
pattern: regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]$"),
}
}

// Name returns the rule name
func (r *AwsElasticBeanstalkEnvironmentInvalidNameFormatRule) Name() string {
return "aws_elastic_beanstalk_environment_invalid_name_format"
}

// Enabled returns whether the rule is enabled by default
func (r *AwsElasticBeanstalkEnvironmentInvalidNameFormatRule) Enabled() bool {
return true
}

// Severity returns the rule severity
func (r *AwsElasticBeanstalkEnvironmentInvalidNameFormatRule) Severity() tflint.Severity {
return tflint.ERROR
}

// Link returns the rule reference link
func (r *AwsElasticBeanstalkEnvironmentInvalidNameFormatRule) Link() string {
return project.ReferenceLink(r.Name())
}

// Check checks the environment name matches the pattern provided
func (r *AwsElasticBeanstalkEnvironmentInvalidNameFormatRule) Check(runner tflint.Runner) error {
resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{
Attributes: []hclext.AttributeSchema{{Name: r.attributeName}},
}, nil)
if err != nil {
return err
}

for _, resource := range resources.Blocks {
attribute, exists := resource.Body.Attributes[r.attributeName]
if !exists {
continue
}

var val string
err := runner.EvaluateExpr(attribute.Expr, &val, nil)

err = runner.EnsureNoError(err, func() error {
if !r.pattern.MatchString(val) {
runner.EmitIssue(
r,
fmt.Sprintf(`%s does not match constraint: must contain only letters, digits, and the dash `+
`character and may not start or end with a dash (^[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]$)`, val),
attribute.Expr.Range(),
)
}
return nil
})
if err != nil {
return err
}
}

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package rules

import (
"testing"

hcl "github.com/hashicorp/hcl/v2"
"github.com/terraform-linters/tflint-plugin-sdk/helper"
)

func Test_AwsElasticBeanstalkEnvironmentInvalidNameFormat(t *testing.T) {
cases := []struct {
Name string
Content string
Expected helper.Issues
}{
{
Name: "tf-test-name dash valid",
Content: `
resource "aws_elastic_beanstalk_environment" "tfenvtest" {
name = "tf-test-name"
application = "tf-test-name"
solution_stack_name = "64bit Amazon Linux 2015.03 v2.0.3 running Go 1.4"
}
`,
Expected: helper.Issues{},
},
{
Name: "underscores invalid",
Content: `
resource "aws_elastic_beanstalk_environment" "tfenvtest" {
name = "tf_test_name"
application = "tf-test-name"
solution_stack_name = "64bit Amazon Linux 2015.03 v2.0.3 running Go 1.4"
}
`,
Expected: helper.Issues{
{
Rule: NewAwsElasticBeanstalkEnvironmentInvalidNameFormatRule(),
Message: "tf_test_name does not match constraint: must contain only letters, digits, and " +
"the dash character and may not start or end with a dash (^[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]$)",
Range: hcl.Range{
Filename: "resource.tf",
Start: hcl.Pos{Line: 3, Column: 24},
End: hcl.Pos{Line: 3, Column: 38},
},
},
},
},
{
Name: "end with dash invalid",
Content: `
resource "aws_elastic_beanstalk_environment" "tfenvtest" {
name = "tf-test-name-"
application = "tf-test-name"
solution_stack_name = "64bit Amazon Linux 2015.03 v2.0.3 running Go 1.4"
}
`,
Expected: helper.Issues{
{
Rule: NewAwsElasticBeanstalkEnvironmentInvalidNameFormatRule(),
Message: "tf-test-name- does not match constraint: must contain only letters, digits, and " +
"the dash character and may not start or end with a dash (^[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]$)",
Range: hcl.Range{
Filename: "resource.tf",
Start: hcl.Pos{Line: 3, Column: 24},
End: hcl.Pos{Line: 3, Column: 39},
},
},
},
},
}

rule := NewAwsElasticBeanstalkEnvironmentInvalidNameFormatRule()

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)
}
}
1 change: 1 addition & 0 deletions rules/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var rules = [][]tflint.Rule{
NewAwsLambdaFunctionDeprecatedRuntimeRule(),
NewAwsIAMGroupPolicyTooLongRule(),
NewAwsAcmCertificateLifecycleRule(),
NewAwsElasticBeanstalkEnvironmentInvalidNameFormatRule(),
},
models.Rules,
api.Rules,
Expand Down