Skip to content

Commit

Permalink
users can retype the mappings on import error
Browse files Browse the repository at this point in the history
  • Loading branch information
magodo committed Sep 8, 2021
1 parent b555edf commit 55d73b6
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 31 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.16
require (
github.com/Azure/azure-sdk-for-go v56.1.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.19
github.com/fatih/color v1.12.0
github.com/hashicorp/go-azure-helpers v0.16.5
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-version v1.3.0
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
Expand Down Expand Up @@ -264,10 +266,14 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
Expand Down Expand Up @@ -448,6 +454,7 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
98 changes: 75 additions & 23 deletions internal/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"path/filepath"
"strings"

"github.com/fatih/color"
"github.com/magodo/aztfy/internal/armtemplate"
"github.com/magodo/aztfy/schema"

Expand Down Expand Up @@ -147,9 +148,19 @@ func (meta *Meta) ExportArmTemplate(ctx context.Context) error {
if err := json.Unmarshal(raw, &meta.armTemplate); err != nil {
return fmt.Errorf("unmarshalling the template: %w", err)
}

return nil
}

func (meta *Meta) ListAzureResourceIDs() []string {
var ids []string
for _, res := range meta.armTemplate.Resources {
ids = append(ids, res.ID(meta.subscriptionId, meta.resourceGroup))
}
ids = append(ids, armtemplate.ResourceGroupId.ID(meta.subscriptionId, meta.resourceGroup))
return ids
}

type ImportList []ImportItem

func (l ImportList) NonSkipped() ImportList {
Expand All @@ -163,10 +174,42 @@ func (l ImportList) NonSkipped() ImportList {
return out
}

func (l ImportList) ImportErrored() ImportList {
var out ImportList
for _, item := range l {
if item.ImportError == nil {
continue
}
out = append(out, item)
}
return out
}

func (l ImportList) Imported() ImportList {
var out ImportList
for _, item := range l.NonSkipped() {
if item.ImportError != nil {
continue
}
out = append(out, item)
}
return out
}

type ImportItem struct {
ResourceID string
Skip bool
// The azure resource id
ResourceID string

// Wether this azure resource should be skipped from importing
Skip bool

// Whether this azure resource failed to import into terraform (this might due to the TFResourceType doesn't match the resource)
ImportError error

// The terraform resource type
TFResourceType string

// The terraform resource name
TFResourceName string
}

Expand All @@ -177,12 +220,7 @@ func (item *ImportItem) TFAddr() string {
return item.TFResourceType + "." + item.TFResourceName
}

func (meta *Meta) ResolveImportList(ctx context.Context) (ImportList, error) {
var ids []string
for _, res := range meta.armTemplate.Resources {
ids = append(ids, res.ID(meta.subscriptionId, meta.resourceGroup))
}
ids = append(ids, armtemplate.ResourceGroupId.ID(meta.subscriptionId, meta.resourceGroup))
func (meta *Meta) ResolveImportList(ids []string, ctx context.Context) (ImportList, error) {

// schema, err := meta.tf.ProvidersSchema(ctx)
// if err != nil {
Expand All @@ -196,7 +234,7 @@ func (meta *Meta) ResolveImportList(ctx context.Context) (ImportList, error) {
// userResourceMap is used to track the resource types and resource names that are specified by users.
userResourceMap := map[string]map[string]bool{}
reader := bufio.NewReader(os.Stdin)
fmt.Println(`Please input the Terraform resource type and name for each Azure resource in form of "<resource type>.<resource name>. Press enter with no input will skip importing that resource.`)
color.Cyan("\nPlease input either the Terraform resource type and name in the form of \"<resource type>.<resource name>\", or simply enter to skip\n")
for idx, id := range ids {
item := ImportItem{
ResourceID: id,
Expand Down Expand Up @@ -244,7 +282,13 @@ func (meta *Meta) ResolveImportList(ctx context.Context) (ImportList, error) {
return list, nil
}

func (meta *Meta) Import(ctx context.Context, list ImportList) error {
func (meta *Meta) Import(ctx context.Context, list ImportList) (ImportList, error) {
if len(list.NonSkipped()) == 0 {
return nil, nil
}

color.Cyan("\nImport Azure Resources\n")

// Generate a temp Terraform config to include the empty template for each resource.
// This is required for the following importing.
cfgFile := filepath.Join(meta.workspace, "main.tf")
Expand All @@ -253,27 +297,39 @@ func (meta *Meta) Import(ctx context.Context, list ImportList) error {
for _, item := range list.NonSkipped() {
tpl, err := meta.tf.Add(ctx, item.TFAddr())
if err != nil {
return fmt.Errorf("generating resource template for %s: %w", item.TFAddr(), err)
return nil, fmt.Errorf("generating resource template for %s: %w", item.TFAddr(), err)
}
tpls = append(tpls, tpl)
}
if err := os.WriteFile(cfgFile, []byte(strings.Join(tpls, "\n")), 0644); err != nil {
return fmt.Errorf("generating resource template cfgFile file: %w", err)
return nil, fmt.Errorf("generating resource template cfgFile file: %w", err)
}
// Remove the temp Terraform config once resources are imported.
// This is due to the fact that "terraform add" will complain the resource to be added already exist in the config, even we are outputting to stdout.
// This should be resolved once hashicorp/terraform#29220 is addressed.
defer os.Remove(cfgFile)

// Import resources
out := ImportList{}
for idx, item := range list.NonSkipped() {
fmt.Printf("[%d/%d] Importing %q as %s\n", idx+1, len(list.NonSkipped()), item.ResourceID, item.TFAddr())
if err := meta.tf.Import(ctx, item.TFAddr(), item.ResourceID); err != nil {
return err
fmt.Printf("[%d/%d] Importing %q as %s...\n", idx+1, len(list.NonSkipped()), item.ResourceID, item.TFAddr())
err := meta.tf.Import(ctx, item.TFAddr(), item.ResourceID)
if err != nil {
emsg := []string{}
for _, el := range strings.Split(err.Error(), "\n") {
el := strings.TrimSpace(el)
if el == "" {
continue
}
emsg = append(emsg, "\t"+el)
}
color.Red(strings.Join(emsg, "\n"))
}
item.ImportError = err
out = append(out, item)
}

return nil
return out, nil
}

type ConfigInfos []ConfigInfo
Expand All @@ -290,10 +346,7 @@ func (cfg ConfigInfo) DumpHCL(w io.Writer) (int, error) {
func (meta *Meta) StateToConfig(ctx context.Context, list ImportList) (ConfigInfos, error) {
out := ConfigInfos{}

for _, item := range list.NonSkipped() {
if item.Skip {
continue
}
for _, item := range list.Imported() {
tpl, err := meta.tf.Add(ctx, item.TFAddr(), tfexec.FromState(true))
if err != nil {
return nil, fmt.Errorf("converting terraform state to config for resource %s: %w", item.TFAddr(), err)
Expand Down Expand Up @@ -335,7 +388,7 @@ func (meta *Meta) ResolveDependency(ctx context.Context, configs ConfigInfos) (C
out = append(out, cfg)
continue
}
// This should never happen
// This should never happen as we always ensure there is at least one implicit dependency on the resource group for each resource.
if _, ok := depInfo[armId]; !ok {
return nil, fmt.Errorf("can't find resource %q in the arm template", armId.ID(meta.subscriptionId, meta.resourceGroup))
}
Expand All @@ -360,7 +413,6 @@ func (meta *Meta) GenerateConfig(cfgs ConfigInfos) error {
return fmt.Errorf("generating main configuration file: %w", err)
}

fmt.Printf("Please find the Terraform state and the config at: %s\n", meta.workspace)
return nil
}

Expand All @@ -369,7 +421,7 @@ func (meta *Meta) hclBlockAppendDependency(body *hclwrite.Body, armIds []armtemp
for _, armid := range armIds {
cfg, ok := cfgset[armid]
if !ok {
dependencies = append(dependencies, fmt.Sprintf("# Depending on %q, but it is not imported by Terraform. Please fix it manually.", armid.ID(meta.subscriptionId, meta.resourceGroup)))
dependencies = append(dependencies, fmt.Sprintf("# Depending on %q, which is not imported by Terraform.", armid.ID(meta.subscriptionId, meta.resourceGroup)))
continue
}
dependencies = append(dependencies, cfg.TFAddr()+",")
Expand Down
35 changes: 28 additions & 7 deletions internal/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"io"
"log"

"github.com/fatih/color"
)

func Run(ctx context.Context, rg string) error {
Expand All @@ -23,16 +25,33 @@ func Run(ctx context.Context, rg string) error {
return err
}

importList, err := meta.ResolveImportList(ctx)
if err != nil {
return err
}
ids := meta.ListAzureResourceIDs()

if err := meta.Import(ctx, importList); err != nil {
return err
// Repeat importing resources here to avoid the user incorrectly maps an azure resource to an incorrect terraform resource
var importedList ImportList
for len(ids) != 0 {
l, err := meta.ResolveImportList(ids, ctx)
if err != nil {
return err
}

l, err = meta.Import(ctx, l)
if err != nil {
return err
}

for _, item := range l.Imported() {
importedList = append(importedList, item)
}

importErroredList := l.ImportErrored()
ids = make([]string, 0, len(importErroredList))
for _, item := range importErroredList {
ids = append(ids, item.ResourceID)
}
}

configs, err := meta.StateToConfig(ctx, importList)
configs, err := meta.StateToConfig(ctx, importedList)
if err != nil {
return err
}
Expand All @@ -46,5 +65,7 @@ func Run(ctx context.Context, rg string) error {
return err
}

color.Cyan("\nPlease find the Terraform state and the config at: %s\n", meta.workspace)

return nil
}
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var (
)

func init() {
flagVersion = flag.Bool("v", false, "print version")
flagVersion = flag.Bool("v", false, "Print version")
}

const usage = `aztfy [option] <resource group name>
Expand Down

0 comments on commit 55d73b6

Please sign in to comment.