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

Branch 241009 #132

Merged
merged 6 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 42 additions & 57 deletions cmd/migrate_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"log"
"os"
"path"
"path/filepath"
"strings"

Expand All @@ -16,7 +17,7 @@ import (

const filenameImport = "imports.tf"

const tempDir = "temp"
const tempFolderName = "aztfmigrate_temp"

type MigrateCommand struct {
Ui cli.Ui
Expand All @@ -31,7 +32,7 @@ func (c *MigrateCommand) flags() *flag.FlagSet {
fs.BoolVar(&c.Verbose, "v", false, "whether show terraform logs")
fs.BoolVar(&c.Strict, "strict", false, "strict mode: API versions must be matched")
fs.StringVar(&c.workingDir, "working-dir", "", "path to Terraform configuration files")
fs.StringVar(&c.TargetProvider, "target-provider", "", "Specify the provider to migrate to. The allowed values are: azurerm and azapi. Default is azurerm.")
fs.StringVar(&c.TargetProvider, "to", "", "Specify the provider to migrate to. The allowed values are: azurerm and azapi. Default is azurerm.")

fs.Usage = func() { c.Ui.Error(c.Help()) }
return fs
Expand All @@ -41,6 +42,11 @@ func (c *MigrateCommand) Run(args []string) int {
// AzureRM provider will honor env.var "AZURE_HTTP_USER_AGENT" when constructing for HTTP "User-Agent" header.
// #nosec G104
_ = os.Setenv("AZURE_HTTP_USER_AGENT", "mig")
// The following env.vars are used to disable enhanced validation and skip provider registration, to speed up the process.
// #nosec G104
_ = os.Setenv("ARM_PROVIDER_ENHANCED_VALIDATION", "false")
// #nosec G104
_ = os.Setenv("ARM_SKIP_PROVIDER_REGISTRATION", "true")
f := c.flags()
if err := f.Parse(args); err != nil {
c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s", err))
Expand Down Expand Up @@ -96,16 +102,24 @@ func (c *MigrateCommand) MigrateResources(terraform *tf.Terraform, resources []t

workingDirectory := terraform.GetWorkingDirectory()
// write empty config to temp dir for import
tempDirectoryCreate(workingDirectory)
tempPath := filepath.Join(workingDirectory, tempDir)
tempTerraform, err := tf.NewTerraform(tempPath, c.Verbose)
tempDir := filepath.Join(workingDirectory, tempFolderName)
if err := os.MkdirAll(tempDir, 0750); err != nil {
log.Fatalf("creating temp workspace %q: %+v", tempDir, err)
}
defer func() {
err := os.RemoveAll(path.Join(tempDir, "terraform.tfstate"))
if err != nil {
log.Printf("[ERROR] removing temp workspace %q: %+v", tempDir, err)
}
}()
tempTerraform, err := tf.NewTerraform(tempDir, c.Verbose)
if err != nil {
log.Fatal(err)
}

log.Printf("[INFO] generating import config...")
config := importConfig(resources, c.TargetProvider)
if err = os.WriteFile(filepath.Join(tempPath, filenameImport), []byte(config), 0600); err != nil {
config := importConfig(resources)
if err = os.WriteFile(filepath.Join(tempDir, filenameImport), []byte(config), 0600); err != nil {
log.Fatal(err)
}

Expand All @@ -117,8 +131,6 @@ func (c *MigrateCommand) MigrateResources(terraform *tf.Terraform, resources []t
}
}

tempDirectoryCleanup(workingDirectory)

log.Printf("[INFO] updating config...")
updateResources := make([]types.AzapiUpdateResource, 0)
for _, r := range resources {
Expand Down Expand Up @@ -169,14 +181,8 @@ func (c *MigrateCommand) MigrateResources(terraform *tf.Terraform, resources []t
}
}

func importConfig(resources []types.AzureResource, targetProvider string) string {
func importConfig(resources []types.AzureResource) string {
const providerConfig = `
provider "azurerm" {
features {}
subscription_id = "%s"
}
`
const AzapiProviderConfig = `
terraform {
required_providers {
azapi = {
Expand All @@ -185,6 +191,11 @@ terraform {
}
}

provider "azurerm" {
features {}
subscription_id = "%s"
}

provider "azapi" {
}
`
Expand All @@ -193,52 +204,26 @@ provider "azapi" {
for _, r := range resources {
config += r.EmptyImportConfig()
}
if targetProvider == "azurerm" {
subscriptionId := ""
for _, r := range resources {
switch resource := r.(type) {
case *types.AzapiResource:
for _, instance := range resource.Instances {
if strings.HasPrefix(instance.ResourceId, "/subscriptions/") {
subscriptionId = strings.Split(instance.ResourceId, "/")[2]
break
}
}
case *types.AzapiUpdateResource:
if strings.HasPrefix(resource.Id, "/subscriptions/") {
subscriptionId = strings.Split(resource.Id, "/")[2]
subscriptionId := ""
for _, r := range resources {
switch resource := r.(type) {
case *types.AzapiResource:
for _, instance := range resource.Instances {
if strings.HasPrefix(instance.ResourceId, "/subscriptions/") {
subscriptionId = strings.Split(instance.ResourceId, "/")[2]
break
}
}
if subscriptionId != "" {
break
case *types.AzapiUpdateResource:
if strings.HasPrefix(resource.Id, "/subscriptions/") {
subscriptionId = strings.Split(resource.Id, "/")[2]
}
}
config = fmt.Sprintf(providerConfig, subscriptionId) + config
} else {
config = AzapiProviderConfig + config
}

return config
}

func tempDirectoryCreate(workingDirectory string) {
tempPath := filepath.Join(workingDirectory, tempDir)
if _, err := os.Stat(tempPath); !os.IsNotExist(err) {
if err := os.RemoveAll(tempPath); err != nil {
log.Fatalf("error deleting %s: %+v", tempPath, err)
if subscriptionId != "" {
break
}
}
if err := os.MkdirAll(tempPath, 0750); err != nil {
log.Fatalf("creating temp workspace %q: %+v", tempPath, err)
}
}
config = fmt.Sprintf(providerConfig, subscriptionId) + config

func tempDirectoryCleanup(workingDirectory string) {
tempPath := filepath.Join(workingDirectory, tempDir)
// cleanup temp folder
if _, err := os.Stat(tempPath); !os.IsNotExist(err) {
if err := os.RemoveAll(tempPath); err != nil {
log.Printf("[WARN] error deleting %s: %+v", tempPath, err)
}
}
return config
}
18 changes: 6 additions & 12 deletions cmd/migrate_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,19 @@ package cmd_test

import (
"fmt"
"log"
"math/rand"
"os"
"path/filepath"
"strings"
"testing"
"time"

"github.com/Azure/aztfmigrate/azurerm"
"github.com/Azure/aztfmigrate/cmd"
"github.com/Azure/aztfmigrate/tf"
"github.com/Azure/aztfmigrate/types"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/mitchellh/cli"
"log"
"math/rand"
"os"
"path/filepath"
"strings"
"testing"
)

func TestMigrate_basic(t *testing.T) {
Expand Down Expand Up @@ -194,10 +192,6 @@ provider "azurerm" {

}

func init() {
rand.Seed(time.Now().UnixNano())
}

func tempDir(t *testing.T) string {
tmpDir := filepath.Join(os.TempDir(), "aztfmigrate", t.Name())
err := os.MkdirAll(tmpDir, 0o755)
Expand Down
2 changes: 1 addition & 1 deletion cmd/plan_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (c *PlanCommand) flags() *flag.FlagSet {
fs.BoolVar(&c.Verbose, "v", false, "whether show terraform logs")
fs.BoolVar(&c.Strict, "strict", false, "strict mode: API versions must be matched")
fs.StringVar(&c.workingDir, "working-dir", "", "path to Terraform configuration files")
fs.StringVar(&c.TargetProvider, "target-provider", "", "Specify the provider to migrate to. The allowed values are: azurerm and azapi. Default is azurerm.")
fs.StringVar(&c.TargetProvider, "to", "", "Specify the provider to migrate to. The allowed values are: azurerm and azapi. Default is azurerm.")
fs.Usage = func() { c.Ui.Error(c.Help()) }
return fs
}
Expand Down
13 changes: 3 additions & 10 deletions cmd/plan_command_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package cmd_test

import (
"math/rand"
"os"
"path/filepath"
"testing"
"time"

"github.com/Azure/aztfmigrate/cmd"
"github.com/Azure/aztfmigrate/tf"
"github.com/mitchellh/cli"
"os"
"path/filepath"
"testing"
)

func TestPlan_basic(t *testing.T) {
Expand Down Expand Up @@ -95,7 +92,3 @@ func planTestCase(t *testing.T, content string, expectMigratedAddresses []string
}
}
}

func init() {
rand.Seed(time.Now().UnixNano())
}
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ module github.com/Azure/aztfmigrate

go 1.22.0

toolchain go1.23.1

require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2
github.com/gertd/go-pluralize v0.2.1
Expand Down
4 changes: 2 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Available commands are:
version Displays the version of the migration tool
```

1. Run `aztfmigrate plan -target-provider=azurerm` under your terraform working directory,
1. Run `aztfmigrate plan -to=azurerm` under your terraform working directory,
it will list all resources that can be migrated from `azapi` provider to `azurerm` provider.
The Terraform addresses listed in file `aztfmigrate.ignore` will be ignored during migration.
```
Expand All @@ -33,7 +33,7 @@ azapi_resource.test: input properties not supported: [], output properties not s

The following resources will be ignored in migration:
```
2. Run `aztfmigrate migrate -target-provider=azurerm` under your terraform working directory,
2. Run `aztfmigrate migrate -to=azurerm` under your terraform working directory,
it will migrate above resources from `azapi` provider to `azurerm` provider,
both terraform configuration and state.
The Terraform addresses listed in file `aztfmigrate.ignore` will be ignored during migration.
Expand Down
Loading