From bfd59724b89310dda328fef6004f6a9c4cc2e4bd Mon Sep 17 00:00:00 2001 From: Heng Lu Date: Tue, 8 Apr 2025 13:36:15 +0800 Subject: [PATCH 1/3] improve error handling --- tf/terraform.go | 2 +- types/azapi_resource.go | 6 +++--- types/azurerm_resource.go | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tf/terraform.go b/tf/terraform.go index 290701d2..34cd0075 100644 --- a/tf/terraform.go +++ b/tf/terraform.go @@ -88,7 +88,7 @@ func (t *Terraform) ImportAdd(address string, id string) (string, error) { _ = t.Init() err := t.exec.Import(context.TODO(), address, id) if err != nil { - log.Fatal(err) + return "", fmt.Errorf("importing resource %s: %w", address, err) } outputs, err := tfadd.StateForTargets(context.TODO(), t.exec, []string{address}, tfadd.Full(true)) if err != nil { diff --git a/types/azapi_resource.go b/types/azapi_resource.go index af5f1442..74b77988 100644 --- a/types/azapi_resource.go +++ b/types/azapi_resource.go @@ -248,18 +248,18 @@ type Instance struct { func importAndGenerateConfig(terraform *tf.Terraform, address string, id string, resourceType string, skipTune bool) (*hclwrite.Block, error) { tpl, err := terraform.ImportAdd(address, id) if err != nil { - return nil, fmt.Errorf("[ERROR] error importing address: %s, id: %s: %+v", address, id, err) + return nil, err } f, diag := hclwrite.ParseConfig([]byte(tpl), "", hcl.InitialPos) if (diag != nil && diag.HasErrors()) || f == nil { - return nil, fmt.Errorf("[ERROR] parsing the HCL generated by \"terraform add\" of %s: %s", address, diag.Error()) + return nil, fmt.Errorf("parsing the HCL generated by \"terraform add\" of %s: %s", address, diag.Error()) } if !skipTune { rb := f.Body().Blocks()[0].Body() sch := schema.ProviderSchemaInfo.ResourceSchemas[resourceType] if err := azurerm.TuneHCLSchemaForResource(rb, sch); err != nil { - return nil, fmt.Errorf("[ERROR] tuning hcl config base on schema: %+v", err) + return nil, fmt.Errorf("tuning hcl config base on schema: %+v", err) } } diff --git a/types/azurerm_resource.go b/types/azurerm_resource.go index 480a66ae..cc07f0ef 100644 --- a/types/azurerm_resource.go +++ b/types/azurerm_resource.go @@ -61,17 +61,17 @@ func (r *AzurermResource) GenerateNewConfig(terraform *tf.Terraform) error { if !r.IsMultipleResources() { instance := r.Instances[0] log.Printf("[INFO] importing %s to %s and generating config...", instance.ResourceId, r.NewAddress(nil)) - if block, err := importAndGenerateConfig(terraform, r.NewAddress(nil), instance.ResourceId, "", true); err == nil { - r.Block = block - valuePropMap := GetValuePropMap(r.Block, r.NewAddress(nil)) - for i, output := range r.Instances[0].Outputs { - r.Instances[0].Outputs[i].NewName = valuePropMap[output.GetStringValue()] - } - r.Migrated = true - log.Printf("[INFO] resource %s has migrated to %s", r.OldAddress(nil), r.NewAddress(nil)) - } else { - log.Printf("[ERROR] %+v", err) + block, err := importAndGenerateConfig(terraform, r.NewAddress(nil), instance.ResourceId, "", true) + if err != nil { + return err + } + r.Block = block + valuePropMap := GetValuePropMap(r.Block, r.NewAddress(nil)) + for i, output := range r.Instances[0].Outputs { + r.Instances[0].Outputs[i].NewName = valuePropMap[output.GetStringValue()] } + r.Migrated = true + log.Printf("[INFO] resource %s has migrated to %s", r.OldAddress(nil), r.NewAddress(nil)) r.Block = InjectReference(r.Block, r.References) } else { // import and build combined block From d897413e7d5538c906bb2d8c003b0c4b562f39e7 Mon Sep 17 00:00:00 2001 From: Heng Lu Date: Tue, 8 Apr 2025 13:58:15 +0800 Subject: [PATCH 2/3] support version constraint in terraform block --- CHANGELOG.md | 5 +++++ cmd/migrate_command.go | 38 +++++++++++++++++++++----------------- helper/utils.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24b6613e..15e06f19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## v2.4.0 (unreleased) + +ENHANCEMENTS: +- Support specifying the provider version used in the migration in the `terraform` block. + ## v2.3.0 Target azurerm version: v4.20.0 diff --git a/cmd/migrate_command.go b/cmd/migrate_command.go index c1439789..57106649 100644 --- a/cmd/migrate_command.go +++ b/cmd/migrate_command.go @@ -9,6 +9,7 @@ import ( "path/filepath" "strings" + "github.com/Azure/aztfmigrate/helper" "github.com/Azure/aztfmigrate/tf" "github.com/Azure/aztfmigrate/types" "github.com/hashicorp/hcl/v2/hclwrite" @@ -125,7 +126,7 @@ func (c *MigrateCommand) MigrateResources(terraform *tf.Terraform, resources []t } log.Printf("[INFO] generating import config...") - config := importConfig(resources) + config := ImportConfig(resources, helper.FindHclBlock(workingDirectory, "terraform", nil)) if err = os.WriteFile(filepath.Join(tempDir, filenameImport), []byte(config), 0600); err != nil { log.Fatal(err) } @@ -190,26 +191,20 @@ func (c *MigrateCommand) MigrateResources(terraform *tf.Terraform, resources []t } } -func importConfig(resources []types.AzureResource) string { - const providerConfig = ` -terraform { +func ImportConfig(resources []types.AzureResource, terraformBlock *hclwrite.Block) string { + config := `terraform { required_providers { azapi = { source = "Azure/azapi" } } -} - -provider "azurerm" { - features {} - subscription_id = "%s" -} - -provider "azapi" { -} -` +}` + if terraformBlock != nil { + newFile := hclwrite.NewEmptyFile() + newFile.Body().AppendBlock(terraformBlock) + config = string(hclwrite.Format(newFile.Bytes())) + } - config := "" for _, r := range resources { config += r.EmptyImportConfig() } @@ -232,7 +227,16 @@ provider "azapi" { break } } - config = fmt.Sprintf(providerConfig, subscriptionId) + config - return config + const providerConfig = ` +provider "azurerm" { + features {} + subscription_id = "%s" +} + +provider "azapi" { +} +` + + return fmt.Sprintf(providerConfig, subscriptionId) + config } diff --git a/helper/utils.go b/helper/utils.go index a06f4b63..c52f464c 100644 --- a/helper/utils.go +++ b/helper/utils.go @@ -5,6 +5,7 @@ import ( "fmt" "io/fs" "os" + "path" "strings" "github.com/hashicorp/hcl/v2" @@ -79,6 +80,47 @@ func ListHclFiles(workingDirectory string) []fs.DirEntry { return res } +func ListHclBlocks(workingDirectory string) []*hclwrite.Block { + res := make([]*hclwrite.Block, 0) + files := ListHclFiles(workingDirectory) + for _, file := range files { + filePath := path.Join(workingDirectory, file.Name()) + f, err := os.ReadFile(filePath) + if err != nil { + continue + } + hclFile, diags := hclwrite.ParseConfig(f, file.Name(), hcl.InitialPos) + if diags.HasErrors() { + continue + } + res = append(res, hclFile.Body().Blocks()...) + } + return res +} + +func FindHclBlock(workingDirectory string, blockType string, labels []string) *hclwrite.Block { + blocks := ListHclBlocks(workingDirectory) + for _, block := range blocks { + if block.Type() != blockType { + continue + } + if len(block.Labels()) != len(labels) { + continue + } + isLabelsEqual := true + for i, label := range labels { + if block.Labels()[i] != label { + isLabelsEqual = false + break + } + } + if isLabelsEqual { + return block + } + } + return nil +} + // GetTokensForExpression convert a literal value to hclwrite.Tokens func GetTokensForExpression(expression string) hclwrite.Tokens { syntaxTokens, diags := hclsyntax.LexConfig([]byte(expression), "main.tf", hcl.InitialPos) From 2f4ebb840c38ed4e5945e9d8dc1d0282d7e9ffb7 Mon Sep 17 00:00:00 2001 From: Heng Lu Date: Tue, 8 Apr 2025 14:12:35 +0800 Subject: [PATCH 3/3] fix gosec --- helper/utils.go | 1 + 1 file changed, 1 insertion(+) diff --git a/helper/utils.go b/helper/utils.go index c52f464c..167889cc 100644 --- a/helper/utils.go +++ b/helper/utils.go @@ -85,6 +85,7 @@ func ListHclBlocks(workingDirectory string) []*hclwrite.Block { files := ListHclFiles(workingDirectory) for _, file := range files { filePath := path.Join(workingDirectory, file.Name()) + // #nosec G304 f, err := os.ReadFile(filePath) if err != nil { continue