azurerm/hcl_schema.go (96 lines of code) (raw):
package azurerm
import (
"fmt"
"sort"
"strings"
"github.com/Azure/aztfmigrate/azurerm/schema"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/zclconf/go-cty/cty"
)
// TuneHCLSchemaForResource : Currently, some special attributes are output by `terraform add`, while should be excluded.
// This is tracked in: https://github.com/hashicorp/terraform/issues/29219
// We are manually excluding these special properties here by modifying the hcl.
func TuneHCLSchemaForResource(rb *hclwrite.Body, sch *schema.Schema) error {
rb.RemoveAttribute("id")
rb.RemoveBlock(rb.FirstMatchingBlock("timeouts", nil))
return tuneForBlock(rb, sch.Block, nil)
}
func tuneForBlock(rb *hclwrite.Body, sch *schema.SchemaBlock, parentAttrNames []string) error {
for attrName, attrVal := range rb.Attributes() {
schAttr, ok := sch.Attributes[attrName]
if !ok {
// This might because the provider under used is a newer one than the version where we ingest the schema information.
// This might happen when the user has a newer version provider installed in its local fs, and has set the "dev_overrides" for that provider.
// We simply remove that attribute from the config.
rb.RemoveAttribute(attrName)
continue
}
if schAttr.Required {
continue
}
if schAttr.Computed {
// Especially, we will keep O+C attribute who has "ExactlyOneOf" constraint, but only keep one.
// The one got picked is the first one in alphabetic order.
// TODO: We should tackle more cases for different kinds of constraints.
if schAttr.Optional && len(schAttr.ExactlyOneOf) != 0 {
l := make([]string, len(schAttr.ExactlyOneOf))
copy(l, schAttr.ExactlyOneOf)
sort.Strings(l)
addrs := append(parentAttrNames, attrName)
if l[0] != strings.Join(addrs, ".0.") {
rb.RemoveAttribute(attrName)
continue
}
} else {
rb.RemoveAttribute(attrName)
continue
}
}
// For optional only attributes, remove it from the output config if it holds the default value
var dstr string
switch schAttr.AttributeType {
case cty.Number:
dstr = "0"
case cty.Bool:
dstr = "false"
case cty.String:
dstr = `""`
default:
if schAttr.AttributeType.IsListType() || schAttr.AttributeType.IsSetType() {
dstr = "[]"
break
}
if schAttr.AttributeType.IsMapType() {
dstr = "{}"
break
}
}
if schAttr.Default != nil {
dstr = fmt.Sprintf("%#v", schAttr.Default)
}
attrExpr, diags := hclwrite.ParseConfig(attrVal.BuildTokens(nil).Bytes(), "generate_attr", hcl.InitialPos)
if diags.HasErrors() {
return fmt.Errorf(`building attribute %q attribute: %s`, attrName, diags.Error())
}
attrValLit := strings.TrimSpace(getAttributeExprString(attrExpr, attrName))
if attrValLit == dstr {
rb.RemoveAttribute(attrName)
continue
}
}
for _, blkVal := range rb.Blocks() {
if sch.NestedBlocks[blkVal.Type()].Computed {
rb.RemoveBlock(blkVal)
continue
}
if err := tuneForBlock(blkVal.Body(), sch.NestedBlocks[blkVal.Type()].Block, append(parentAttrNames, blkVal.Type())); err != nil {
return err
}
}
return nil
}
func getAttributeExprString(file *hclwrite.File, attrName string) string {
if file == nil {
return ""
}
if file.Body() == nil {
return ""
}
if attribute := file.Body().GetAttribute(attrName); attribute != nil {
if attribute.Expr() != nil {
return string(attribute.Expr().BuildTokens(nil).Bytes())
}
}
return ""
}