diff --git a/src/terraform/structure/terraform_block.go b/src/terraform/structure/terraform_block.go index 40b120fb..11fb22fc 100644 --- a/src/terraform/structure/terraform_block.go +++ b/src/terraform/structure/terraform_block.go @@ -13,7 +13,7 @@ type TerraformBlock struct { HclSyntaxBlock *hclsyntax.Block } -var ProviderToTagAttribute = map[string]string{"aws": "tags", "azurerm": "tags", "google": "labels", "oci": "freeform_tags", "alicloud": "tags"} +var ProviderToTagAttribute = map[string]string{"aws": "tags", "azurerm": "tags", "google": "labels", "oci": "freeform_tags", "alicloud": "tags", "kubernetes": "metadata.annotations"} const ResourceBlockType = "resource" const ModuleBlockType = "module" diff --git a/src/terraform/structure/terraform_parser.go b/src/terraform/structure/terraform_parser.go index 135c46e2..7bd60e79 100644 --- a/src/terraform/structure/terraform_parser.go +++ b/src/terraform/structure/terraform_parser.go @@ -32,6 +32,7 @@ var unsupportedTerraformBlocks = []string{ "aws_cloudwatch_log_destination", // This resource does not support tags, although docs state otherwise. "google_monitoring_notification_channel", // This resource uses labels for other purposes. "aws_secretsmanager_secret_rotation", // This resource does not support tags, although tfschema states otherwise. + "kubernetes_manifest", // This resource specifically supports tags with a different structure. } var taggableResourcesLock sync.RWMutex @@ -278,10 +279,54 @@ func (p *TerraformParser) WriteFile(readFilePath string, blocks []structure.IBlo return nil } +func GetTags(block *hclwrite.Block, tagsAttributeName string) *hclwrite.Attribute { + block = GetTagsBlock(block, tagsAttributeName) + + if strings.Contains(tagsAttributeName, ".") { + parts := strings.Split(tagsAttributeName, ".") + + if block == nil { + return nil + } + logger.Debug(fmt.Sprintf("Trying to get attribute %v from %v", parts[len(parts)-1], block)) + return block.Body().GetAttribute(parts[len(parts)-1]) + } else { + return block.Body().GetAttribute(tagsAttributeName); + } +} + +func SetTags(block *hclwrite.Block, tagsAttributeName string, newTags hclwrite.Tokens) { + block = GetTagsBlock(block, tagsAttributeName) + + if strings.Contains(tagsAttributeName, ".") { + parts := strings.Split(tagsAttributeName, ".") + + logger.Debug(fmt.Sprintf("Trying to set attribute %v on %v with value %v", parts[len(parts)-1], block, newTags)) + block.Body().SetAttributeRaw(parts[len(parts)-1], newTags) + } else { + block.Body().SetAttributeRaw(tagsAttributeName, newTags); + } +} + +func GetTagsBlock(block *hclwrite.Block, tagsAttributeName string) *hclwrite.Block { + if strings.Contains(tagsAttributeName, ".") { + parts := strings.Split(tagsAttributeName, ".") + + for _, part := range parts[0:len(parts)-1] { + logger.Debug(fmt.Sprintf("Trying to get block %v from %v", part, block)) + block = block.Body().FirstMatchingBlock(part, nil) + } + + return block + } else { + return block + } +} + func (p *TerraformParser) modifyBlockTags(rawBlock *hclwrite.Block, parsedBlock structure.IBlock) { mergedTags := parsedBlock.MergeTags() tagsAttributeName := parsedBlock.(*TerraformBlock).TagsAttributeName - tagsAttribute := rawBlock.Body().GetAttribute(tagsAttributeName) + tagsAttribute := GetTags(rawBlock, tagsAttributeName) // we don't add tags to data sources if rawBlock.Type() == "data" { @@ -291,7 +336,7 @@ func (p *TerraformParser) modifyBlockTags(rawBlock *hclwrite.Block, parsedBlock if tagsAttribute == nil { mergedTagsTokens := buildTagsTokens(mergedTags) if mergedTagsTokens != nil { - rawBlock.Body().SetAttributeRaw(tagsAttributeName, mergedTagsTokens) + SetTags(rawBlock, tagsAttributeName, mergedTagsTokens) } } else { rawTagsTokens := tagsAttribute.Expr().BuildTokens(hclwrite.Tokens{}) @@ -362,7 +407,7 @@ func (p *TerraformParser) modifyBlockTags(rawBlock *hclwrite.Block, parsedBlock } else { rawTagsTokens = InsertTokens(rawTagsTokens, newTagsTokens[2:len(newTagsTokens)-2]) // checkov:skip=CKV_SECRET_80 false positive } - rawBlock.Body().SetAttributeRaw(tagsAttributeName, rawTagsTokens) + SetTags(rawBlock, tagsAttributeName, rawTagsTokens) return } @@ -399,7 +444,7 @@ func (p *TerraformParser) modifyBlockTags(rawBlock *hclwrite.Block, parsedBlock } } // Set the body's tags to the new built tokens - rawBlock.Body().SetAttributeRaw(tagsAttributeName, rawTagsTokens) + SetTags(rawBlock, tagsAttributeName, rawTagsTokens) } } @@ -660,7 +705,8 @@ func (p *TerraformParser) getExistingTags(hclBlock *hclwrite.Block, tagsAttribut isTaggable := false existingTags := make([]tags.ITag, 0) - tagsAttribute := hclBlock.Body().GetAttribute(tagsAttributeName) + tagsAttribute := GetTags(hclBlock, tagsAttributeName) + if tagsAttribute != nil { // if tags exists in resource isTaggable, _ = p.isBlockTaggable(hclBlock) @@ -873,7 +919,7 @@ func (p *TerraformParser) getModuleTags(hclBlock *hclwrite.Block, tagsAttributeN isTaggable := false existingTags := make([]tags.ITag, 0) - tagsAttribute := hclBlock.Body().GetAttribute(tagsAttributeName) + tagsAttribute := GetTags(hclBlock, tagsAttributeName) if tagsAttribute != nil { // if tags exists in module isTaggable = true diff --git a/src/terraform/structure/tf_taggable.go b/src/terraform/structure/tf_taggable.go index bb2a8f15..f86c1c86 100644 --- a/src/terraform/structure/tf_taggable.go +++ b/src/terraform/structure/tf_taggable.go @@ -676,4 +676,26 @@ var TfTaggableResourceTypes = []string{ "google_tpu_node", "google_vertex_ai_dataset", "google_workflows_workflow", + "kubernetes_cluster_role", + "kubernetes_cluster_role_binding", + "kubernetes_config_map", + "kubernetes_config_map_v1", + "kubernetes_cron_job_v1", + "kubernetes_deployment", + "kubernetes_deployment_v1", + "kubernetes_horizontal_pod_autoscaler_v2", + "kubernetes_ingress_v1", + "kubernetes_namespace", + "kubernetes_namespace_v1", + "kubernetes_persistent_volume_claim", + "kubernetes_persistent_volume_claim_v1", + "kubernetes_role_binding_v1", + "kubernetes_role_v1", + "kubernetes_secret", + "kubernetes_secret_v1", + "kubernetes_service", + "kubernetes_service_account", + "kubernetes_service_account_v1", + "kubernetes_service_v1", + "kubernetes_stateful_set", }