Skip to content

Generate validation rules for map attributes - #1020

Open
bendrucker wants to merge 14 commits into
masterfrom
generate-tag-validation-rules
Open

Generate validation rules for map attributes#1020
bendrucker wants to merge 14 commits into
masterfrom
generate-tag-validation-rules

Conversation

@bendrucker

@bendrucker bendrucker commented Dec 20, 2025

Copy link
Copy Markdown
Member

Generates validation rules for tags and other map(string) attributes by extracting key/value constraints from AWS Smithy models. This catches invalid tags at plan time rather than waiting for API errors at apply.

For example, given:

resource "aws_ecs_service" "example" {
  tags = {
    ""             = "value"   # key must be at least 1 character
    "invalid!key"  = "value"   # key contains invalid character "!"
    "aws:reserved" = "value"   # "aws:" prefix is reserved (select services)
    "Name"         = "x<y>"    # value contains invalid characters
  }
}

The rule reports each violation with the specific constraint (pattern, length, or prefix) that failed. The same approach applies to other map attributes like labels, parameters, attributes, and default_run_properties.

In total, this adds 157 rules: 145 tag rules across 48 services, plus 12 rules for other map attributes (EKS labels, Glue parameters, X-Ray attributes, etc.).

Changes

  • Adds map(ItemsShape, KeyShape, ValueShape) function to mapping file syntax for services that define tags as list-of-structure types
  • Shapes in eval context are rich objects carrying all constraint info (pattern, length, enum)
  • Transform functions (uppercase, replace) operate on shape enum values
  • Adds map_rule.go.tmpl template for map validation rules
  • Validates max tag count per resource using Smithy length traits (e.g., 50 for ECS, 200 for S3)
  • Extracts prefix deny patterns from negative lookaheads (e.g., (?!aws:)) and generates strings.HasPrefix checks, since Go's regexp doesn't support lookahead
  • Skips match-all patterns and Smithy sentinel max values (2147483647) to avoid generating no-op checks
  • Uses attribute name in error messages (e.g., "definition key" for non-tag map rules)
  • Adds gofmt check to the generated code CI workflow

Implementation

AWS services define tags using two different patterns in their Smithy models.

Smithy map Type

Some services use a map type with explicit key and value targets:

TagMap (map)
  ├── key → TagKey (string with constraints)
  └── value → TagValue (string with constraints)

The generator traverses this automatically via traverseToMapConstraints(). No mapping file changes are needed.

List of Structures

Most services use a list containing a structure with named members:

TagList (list)
  └── member → Tag (structure)
                 ├── Key → TagKey (string with constraints)
                 └── Value → TagValue (string with constraints)

This pattern cannot be inferred automatically because member names vary across services. The map() function explicitly specifies which shapes provide items, key, and value constraints:

tags = map(Tags, TagKey, TagValue)

Prefix Deny Patterns

Tag key patterns often include negative lookahead like (?!aws:) to prevent reserved prefixes. Since Go's regexp doesn't support lookahead, the generator extracts literal prefix strings and emits strings.HasPrefix checks in the template. This preserves the validation rather than silently dropping it.

Testing

Handwritten tests cover three representative generated rules, each exercising a distinct constraint shape:

  • ECS: full constraints — items max, key/value pattern and length, invalid characters
  • Signer: prefix deny (aws:) combined with key pattern validation
  • S3: minimal constraints — key min length only, no pattern or value checks

Generator-level tests cover shape traversal (map, list, structure types), replacePattern transforms (unicode escapes, lookahead extraction, anchors), and shape object composition.

References

Extend the generator to traverse Smithy model structure types (Tags → Tag →
TagKey/TagValue) to extract pattern and length constraints for map attributes.

- Add shape traversal functions to follow list/map/structure types
- Add generateMapRuleFile for map[string]string validation rules
- Add map_rule.go.tmpl template for generated map rules
- Handle PCRE negative lookaheads by stripping (Go regexp incompatible)
- Skip malformed Unicode patterns with error message

Generates 149 new tag validation rules plus additional map rules for
environment_variables, attributes, labels, etc.

Closes #1019
- Extract tagStructureMembers config with godoc explaining case variations
- Add getMemberCaseInsensitive for case-insensitive member lookup
- Add getTarget helper to simplify shape reference extraction
- Extract resolveKeyValuePair to deduplicate map/structure handling
- Reduce traverseToTagConstraints from 80 to 30 lines
@bendrucker bendrucker changed the title Generate validation rules for tag keys and values Generate validation rules for map attributes Dec 20, 2025
- Add shapes_test.go with tests for extractShapeName, getTarget, and traverseToMapConstraints
- Cover map, list, and structure shape types
- Test case variations (Key/Value vs key/value vs Name/Value)
- Test edge cases (non-string members, mixed members, list values)
- Rename traverseToTagConstraints to traverseToMapConstraints
Adds `listmap()` function to HCL mapping syntax for explicitly specifying
list, key, and value constraint shapes. This enables tag validation rules
for services where Smithy models use list-of-structure patterns.

Changes:
- Extends generator to support `listmap(ListShape, KeyShape, ValueShape)`
  syntax in mapping files
- Shapes in eval context are now rich objects carrying all constraint info
- Transform functions (`uppercase`, `replace`) operate on shape enum values
- Adds 124 new tag validation rules across 48 services

Closes #1019
- Add safe type assertions in validMapping, findRawShape, fetchNumber,
  fetchString to prevent panics on unexpected types
- Add nil check for schema in generateRuleFile
- Remove dead code: validListMapping, resolveStructureMembers,
  parseListMapExpr, makeListTransformFunction
- Remove tests for deleted functions
- Fix variable shadowing: rename shapeType to typeStr in makeShapeValue
- Update HCL Transform System documentation comments
- Remove unused hclsyntax import
- Add safe type assertions and error logging throughout generator
- Compose map result type from nested shapes instead of flat fields
- Rename listmap() to map() for Terraform-idiomatic syntax
- Validate max tag count per resource using Smithy length traits
- Collapse listmap rule generation into thin adapter over generateMapRuleFile
- Add table-driven tests for replacePattern
- Move compiled regexps to package level
- Replace deprecated ioutil.ReadFile with os.ReadFile
- Write pattern validation errors to stderr consistently
Resolve conflicts:
- Adopt genutils package rename (generator-utils → genutils)
- Adopt generatedFiles tracking with CleanDir
- Drop removed elastictranscoder rules
- Regenerate provider.go and docs/rules/README.md
@bendrucker
bendrucker marked this pull request as ready for review March 31, 2026 01:11
Instead of silently stripping negative lookahead patterns like (?\!aws:),
extract literal prefix strings and generate strings.HasPrefix checks in
the map rule template. 18 tag rules across 8 services now enforce the
aws: prefix restriction.

Also improves generator code quality:
- Return nil from convertSmithyShape on marshal/unmarshal error
- Remove unused KeyEnum/ValueEnum fields from mapRuleMeta
- Fix "1 characters" grammar in map rule template messages
- Update TestTransformComposition to test shape object path
- Suppress repeated extractServiceNamespace warnings
- Simplify schema lookup, rule meta construction, and compatibility
  transforms via extracted helpers
- Warn and skip incompatible map rule patterns (e.g., surrogate pairs)
Cover three representative rule shapes:
- ECS: items max, key/value pattern and length constraints
- Signer: prefix deny (aws:) and key pattern
- S3: minimal constraints (key min length only)
@bendrucker
bendrucker marked this pull request as draft March 31, 2026 01:46
@bendrucker
bendrucker marked this pull request as ready for review March 31, 2026 01:51
@bendrucker
bendrucker requested a review from Copilot March 31, 2026 01:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands the rule generator to emit validation rules for map(string) attributes (notably tags, plus other map attributes like labels, parameters, and attributes) by extracting key/value constraints from AWS Smithy models, resulting in a large set of newly generated rule files and a few related rule updates.

Changes:

  • Adds many generated *_invalid_tags.go and other map(string) validation rule files across AWS resources/services.
  • Adds/updates a small set of tests for representative generated rules (e.g., Signer, S3) and updates ECR pattern expectations.
  • Updates supporting utilities and models (API models submodule bump; LoadProviderSchema uses os.ReadFile; some enum/pattern/limit adjustments in existing rules).

Reviewed changes

Copilot reviewed 166 out of 248 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
rules/models/aws_sns_topic_invalid_tags.go Adds generated tag map validation for aws_sns_topic.tags (length checks).
rules/models/aws_signer_signing_profile_invalid_tags.go Adds generated tag rule with prefix deny (aws:) and pattern enforcement.
rules/models/aws_signer_signing_profile_invalid_tags_test.go Adds tests for Signer tag prefix deny + pattern validation.
rules/models/aws_shield_protection_group_invalid_tags.go Adds generated tag rule including max tag count and key/value length checks.
rules/models/aws_sfn_state_machine_invalid_tags.go Adds generated tag map validation for Step Functions state machines.
rules/models/aws_sfn_activity_invalid_tags.go Adds generated tag map validation for Step Functions activities.
rules/models/aws_servicecatalog_service_action_invalid_definition.go Adds generated map validation for definition (currently uses tag-oriented messages).
rules/models/aws_servicecatalog_provisioned_product_invalid_provisioning_parameters.go Adds generated map validation for provisioning_parameters (currently uses tag-oriented messages).
rules/models/aws_service_discovery_instance_invalid_attributes.go Adds generated map validation for attributes including key/value patterns (currently uses tag-oriented messages).
rules/models/aws_secretsmanager_secret_invalid_tags.go Adds generated tag map validation for Secrets Manager secrets.
rules/models/aws_s3_bucket_object_invalid_tags.go Adds generated tag key min-length validation for S3 bucket objects.
rules/models/aws_s3_bucket_invalid_tags.go Adds generated tag key min-length validation for S3 buckets.
rules/models/aws_s3_bucket_invalid_tags_test.go Adds tests for S3 bucket tag key min-length behavior.
rules/models/aws_route53_zone_invalid_tags.go Adds generated tag rule including max tag count for Route53 zones.
rules/models/aws_route53_resolver_rule_invalid_tags.go Adds generated tag rule including max tag count for Route53 Resolver rules.
rules/models/aws_route53_resolver_query_log_config_invalid_tags.go Adds generated tag rule including max tag count for query log configs.
rules/models/aws_route53_resolver_firewall_rule_group_invalid_tags.go Adds generated tag rule including max tag count for firewall rule groups.
rules/models/aws_route53_resolver_firewall_rule_group_association_invalid_tags.go Adds generated tag rule including max tag count for firewall rule group associations.
rules/models/aws_route53_resolver_firewall_domain_list_invalid_tags.go Adds generated tag rule including max tag count for firewall domain lists.
rules/models/aws_route53_resolver_endpoint_invalid_tags.go Adds generated tag rule including max tag count for Resolver endpoints.
rules/models/aws_route53_health_check_invalid_tags.go Adds generated tag rule including max tag count for health checks.
rules/models/aws_redshift_subnet_group_invalid_tags.go Adds generated tag validation for Redshift subnet groups (very large max constraints).
rules/models/aws_redshift_snapshot_schedule_invalid_tags.go Adds generated tag validation for Redshift snapshot schedules (very large max constraints).
rules/models/aws_redshift_snapshot_copy_grant_invalid_tags.go Adds generated tag validation for Redshift snapshot copy grants (very large max constraints).
rules/models/aws_redshift_parameter_group_invalid_tags.go Adds generated tag validation for Redshift parameter groups (very large max constraints).
rules/models/aws_redshift_event_subscription_invalid_tags.go Adds generated tag validation for Redshift event subscriptions (very large max constraints).
rules/models/aws_redshift_cluster_invalid_tags.go Adds generated tag validation for Redshift clusters (very large max constraints).
rules/models/aws_quicksight_data_source_invalid_tags.go Adds generated tag validation for QuickSight data sources (includes value min-length).
rules/models/aws_kinesisanalyticsv2_application_invalid_tags.go Adds generated tag validation for Kinesis Analytics v2 applications (includes max tag count).
rules/models/aws_kinesis_stream_invalid_tags.go Adds generated tag validation for Kinesis streams (includes max tag count).
rules/models/aws_kinesis_analytics_application_invalid_tags.go Adds generated tag validation for Kinesis Analytics applications (includes max tag count).
rules/models/aws_iot_thing_invalid_attributes.go Adds generated map validation for attributes including key/value patterns (currently uses tag-oriented messages).
rules/models/aws_inspector_resource_group_invalid_tags.go Adds generated tag validation for Inspector resource groups (includes max tag count/value min-length).
rules/models/aws_imagebuilder_image_recipe_invalid_tags.go Adds generated tag rule with reserved prefix deny, pattern, and length checks.
rules/models/aws_imagebuilder_image_pipeline_invalid_tags.go Adds generated tag rule with reserved prefix deny, pattern, and length checks.
rules/models/aws_imagebuilder_image_invalid_tags.go Adds generated tag rule with reserved prefix deny, pattern, and length checks.
rules/models/aws_imagebuilder_distribution_configuration_invalid_tags.go Adds generated tag rule with reserved prefix deny, pattern, and length checks.
rules/models/aws_imagebuilder_component_invalid_tags.go Adds generated tag rule with reserved prefix deny, pattern, and length checks.
rules/models/aws_guardduty_filter_invalid_tags.go Adds generated tag rule with reserved prefix deny, pattern, and length checks.
rules/models/aws_glue_workflow_invalid_tags.go Adds generated tag validation for Glue workflows.
rules/models/aws_glue_workflow_invalid_default_run_properties.go Adds generated map validation for default_run_properties (currently uses tag-oriented messages).
rules/models/aws_glue_schema_invalid_tags.go Adds generated tag validation for Glue schemas.
rules/models/aws_glue_registry_invalid_tags.go Adds generated tag validation for Glue registries.
rules/models/aws_glue_partition_invalid_parameters.go Adds generated map validation for parameters (currently uses tag-oriented messages).
rules/models/aws_glue_ml_transform_invalid_tags.go Adds generated tag validation for Glue ML transforms.
rules/models/aws_glue_dev_endpoint_invalid_tags.go Adds generated tag validation for Glue dev endpoints.
rules/models/aws_glue_catalog_table_invalid_parameters.go Adds generated map validation for parameters (currently uses tag-oriented messages).
rules/models/aws_glue_catalog_database_invalid_parameters.go Adds generated map validation for parameters (currently uses tag-oriented messages).
rules/models/aws_elasticsearch_domain_invalid_tags.go Adds generated tag validation for Elasticsearch domains.
rules/models/aws_elastic_beanstalk_environment_invalid_tags.go Adds generated tag validation for Elastic Beanstalk environments (includes value min-length).
rules/models/aws_elastic_beanstalk_application_version_invalid_tags.go Adds generated tag validation for Elastic Beanstalk app versions (includes value min-length).
rules/models/aws_elastic_beanstalk_application_invalid_tags.go Adds generated tag validation for Elastic Beanstalk applications (includes value min-length).
rules/models/aws_eks_node_group_invalid_tags.go Adds generated tag validation for EKS node groups.
rules/models/aws_eks_node_group_invalid_labels.go Adds generated map validation for EKS node group labels (currently uses tag-oriented messages).
rules/models/aws_eks_node_group_invalid_ami_type.go Updates allowed AMI type enum values (removes several).
rules/models/aws_eks_identity_provider_config_invalid_tags.go Adds generated tag validation for EKS identity provider configs.
rules/models/aws_eks_fargate_profile_invalid_tags.go Adds generated tag validation for EKS Fargate profiles.
rules/models/aws_eks_addon_invalid_tags.go Adds generated tag validation for EKS addons.
rules/models/aws_ecs_account_setting_default_invalid_name.go Updates allowed ECS account setting names (removes fargateEventWindows).
rules/models/aws_ecr_repository_policy_invalid_repository.go Updates ECR repository name regex and emitted pattern string.
rules/models/aws_ecr_repository_invalid_name.go Updates ECR repository name regex and emitted pattern string.
rules/models/aws_ecr_pull_through_cache_rule_invalid_ecr_repository_prefix.go Updates pull-through cache repository prefix regex and emitted pattern string.
rules/models/aws_ecr_lifecycle_policy_invalid_repository.go Updates ECR lifecycle policy repository regex and emitted pattern string.
rules/models/aws_ecr_lifecycle_policy_invalid_repository_test.go Updates test expected pattern string to match new ECR regex.
rules/models/aws_dynamodb_table_invalid_tags.go Adds generated tag validation for DynamoDB tables.
rules/models/aws_devicefarm_network_profile_invalid_tags.go Adds generated tag validation for Device Farm network profiles (includes max tag count).
rules/models/aws_devicefarm_device_pool_invalid_tags.go Adds generated tag validation for Device Farm device pools (includes max tag count).
rules/models/aws_datapipeline_pipeline_invalid_tags.go Adds generated tag validation for Data Pipeline pipelines (includes max tag count).
rules/models/aws_connect_contact_flow_invalid_tags.go Adds generated tag validation for Connect contact flows (includes prefix deny + unicode pattern).
rules/models/aws_config_organization_managed_rule_invalid_input_parameters.go Adds generated string-length validation for input_parameters.
rules/models/aws_config_organization_custom_rule_invalid_input_parameters.go Adds generated string-length validation for input_parameters.
rules/models/aws_cloudwatch_log_resource_policy_invalid_policy_document.go Adjusts max length constraint and message for policy_document.
rules/models/aws_cloudwatch_event_bus_invalid_tags.go Adds generated tag validation for CloudWatch event buses.
rules/models/aws_athena_workgroup_invalid_tags.go Adds generated tag validation for Athena workgroups.
rules/models/aws_appmesh_virtual_gateway_invalid_tags.go Adds generated tag validation for App Mesh virtual gateways (includes max tag count).
rules/models/aws_appmesh_gateway_route_invalid_tags.go Adds generated tag validation for App Mesh gateway routes (includes max tag count).
rules/models/aws_appconfig_environment_invalid_tags.go Adds generated tag validation for AppConfig environments.
rules/models/aws_appconfig_deployment_strategy_invalid_tags.go Adds generated tag validation for AppConfig deployment strategies.
rules/models/aws_appconfig_deployment_invalid_tags.go Adds generated tag validation for AppConfig deployments.
rules/models/aws_appconfig_configuration_profile_invalid_tags.go Adds generated tag validation for AppConfig configuration profiles.
rules/models/aws_appconfig_application_invalid_tags.go Adds generated tag validation for AppConfig applications.
rules/models/aws_api_gateway_domain_name_invalid_security_policy.go Updates allowed security policy enum values (removes one).
rules/models/aws_amplify_branch_invalid_environment_variables.go Adds generated map validation for Amplify branch environment_variables (match-all patterns).
rules/models/aws_amplify_app_invalid_environment_variables.go Adds generated map validation for Amplify app environment_variables (match-all patterns).
rules/models/api-models-aws Updates the api-models-aws submodule revision used by the generator.
rules/genutils/schema.go Replaces deprecated ioutil.ReadFile with os.ReadFile.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread rules/models/aws_s3_bucket_invalid_tags.go Outdated
Comment thread rules/models/aws_ecr_repository_invalid_name.go
Comment thread rules/models/aws_config_organization_custom_rule_invalid_input_parameters.go Outdated
Comment thread rules/models/aws_amplify_app_invalid_environment_variables.go Outdated
Comment thread rules/models/aws_redshift_cluster_invalid_tags.go
Comment thread rules/models/aws_redshift_cluster_invalid_tags.go
- Use attribute name in messages instead of hardcoded "tag" (e.g.,
  "definition key" for non-tag map attributes)
- Fix "1 characters" pluralization to "1 character"
- Skip match-all patterns (^.*$, ^(?s).*$) instead of compiling them
- Treat Smithy sentinel max (2147483647) as no constraint, removing
  6 Redshift rules that had no real validation
- Fix generated file header to use standard Go generate comment format
  so CleanDir can detect stale files
- Fix extra space in pattern_rule template (func (val → func(val)
- Fix import ordering in pattern_rule_test template
- Add gofmt check to generated_code_checks CI workflow
- Scope .gitignore generator entry to root directory only
@bendrucker
bendrucker requested a review from Copilot March 31, 2026 02:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 88 out of 1730 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread rules/models/aws_cloudwatch_event_bus_invalid_tags.go
Comment thread rules/models/aws_appstream_image_builder_invalid_tags.go Outdated
Comment thread rules/models/aws_appstream_image_builder_invalid_tags.go Outdated
Comment thread rules/models/aws_cloudwatch_event_bus_invalid_tags.go Outdated
Comment thread rules/models/aws_cloudwatch_event_bus_invalid_tags.go Outdated
Comment thread .github/workflows/generated_code_checks.yml
- Strip ^(^...$)$ patterns where outer anchors duplicate inner ones
- Update map rule template comments from "checks the pattern is valid"
  to "validates map keys and values"
- Keep gofmt -w in CI since git diff catches any unintended rewrites
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Validate tags attributes

2 participants