From 77f2c5d4d2a1333568b46afcb9b5ffcfe9a544b1 Mon Sep 17 00:00:00 2001 From: magodo Date: Tue, 27 Sep 2022 08:04:33 +0800 Subject: [PATCH] Refactor resource mapping file (#234) --- README.md | 39 ++++++++---- internal/meta/meta_group_impl.go | 36 +++++------ internal/resmap/resmap.go | 44 +++----------- .../cases/case_applicationinsight_webtest.go | 28 +++++---- internal/test/cases/case_compute_vm_disk.go | 59 ++++++++++++++----- internal/test/cases/case_function_app_slot.go | 46 +++++++++++---- .../test/cases/case_key_vault_nested_items.go | 45 ++++++++++---- internal/test/cases/case_signalr_service.go | 24 ++++---- .../test/cases/case_storage_file_share.go | 31 ++++++---- internal/test/utils.go | 48 ++++++++++++++- 10 files changed, 258 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index dbe447c7..6da53a13 100644 --- a/README.md +++ b/README.md @@ -148,12 +148,20 @@ As the last step, `aztfy` will leverage the ARM template to inject dependencies #### Batch Mode -In batch mode, instead of interactively specifying the mapping from Azurem resource id to the Terraform resource address, users are expected to provide that mapping via the resource mapping file, with the following format: +In batch mode, instead of interactively specifying the mapping from Azurem resource id to the Terraform resource info, users are expected to provide that mapping via the resource mapping file, with the following format: ```json { - "": ".", - "": ".", + "": { + "resource_type" : "", + "resource_name" : "", + "resource_id" : "" + }, + "": { + "resource_type" : "", + "resource_name" : "", + "resource_id" : "" + }, ... } ``` @@ -162,19 +170,30 @@ Example: ```json { - "/subscriptions/0-0-0-0/resourceGroups/tfy-vm/providers/Microsoft.Network/virtualNetworks/example-network": "azurerm_virtual_network.res-0", - "/subscriptions/0-0-0-0/resourceGroups/tfy-vm/providers/Microsoft.Compute/virtualMachines/example-machine": "azurerm_linux_virtual_machine.res-1", - "/subscriptions/0-0-0-0/resourceGroups/tfy-vm/providers/Microsoft.Network/networkInterfaces/example-nic": "azurerm_network_interface.res-2", - "/subscriptions/0-0-0-0/resourceGroups/tfy-vm/providers/Microsoft.Network/networkInterfaces/example-nic1": "azurerm_network_interface.res-3", - "/subscriptions/0-0-0-0/resourceGroups/tfy-vm/providers/Microsoft.Network/virtualNetworks/example-network/subnets/internal": "azurerm_subnet.res-4" + "/SUBSCRIPTIONS/0000/RESOURCEGROUPS/AZTFY-VMDISK": { + "resource_id": "/subscriptions/0000/resourceGroups/aztfy-vmdisk", + "resource_type": "azurerm_resource_group", + "resource_name": "res-1" + }, + "/SUBSCRIPTIONS/0000/RESOURCEGROUPS/AZTFY-VMDISK/PROVIDERS/MICROSOFT.COMPUTE/DISKS/AZTFY-TEST-TEST": { + "resource_id": "/subscriptions/0000/resourceGroups/aztfy-vmdisk/providers/Microsoft.Compute/disks/aztfy-test-test", + "resource_type": "azurerm_managed_disk", + "resource_name": "res-2" + }, + "/SUBSCRIPTIONS/0000/RESOURCEGROUPS/AZTFY-VMDISK/PROVIDERS/MICROSOFT.COMPUTE/VIRTUALMACHINES/AZTFY-TEST-TEST": { + "resource_id": "/subscriptions/0000/resourceGroups/aztfy-vmdisk/providers/Microsoft.Compute/virtualMachines/aztfy-test-test", + "resource_type": "azurerm_linux_virtual_machine", + "resource_name": "res-3" + }, + ... } ``` Then the tool will import each specified resource in the mapping file (if exists) and skip the others. -Especially if the no resource mapping file is specified, `aztfy` will only import the "recognized" resources for you, based on its limited knowledge on the ARM and Terraform resource mappings. +Especially if the no resource mapping file is specified, `aztfy` will only import the "recognized" resources for you, based on its knowledge on the ARM and Terraform resource mappings. -In the batch import mode, users can further specify the `--continue`/`-k` option to make the tool continue even on hitting import error(s) on any resource. +In the batch import mode, users can further specify the `--continue`/`-k` option to make the tool continue even on hitting import error of any resource. ### Remote Backend diff --git a/internal/meta/meta_group_impl.go b/internal/meta/meta_group_impl.go index a794ed89..ee846bb3 100644 --- a/internal/meta/meta_group_impl.go +++ b/internal/meta/meta_group_impl.go @@ -92,20 +92,6 @@ func (meta *MetaGroupImpl) ListResource() (ImportList, error) { rl := rset.ToTFResources() - // Some RP will flip the casing on fields like resource group name, causing the TF ID is inconsistent on casing. - // We shall check the existance of the resource id case insensitively. - type MapInfo struct { - id string - addr tfaddr.TFAddr - } - caseInsensitiveMapping := map[string]MapInfo{} - for k, v := range meta.resourceMapping { - caseInsensitiveMapping[strings.ToUpper(k)] = MapInfo{ - id: k, - addr: v, - } - } - var l ImportList for i, res := range rl { item := ImportItem{ @@ -120,12 +106,13 @@ func (meta *MetaGroupImpl) ListResource() (ImportList, error) { item.Recommendations = []string{res.TFType} } - if len(caseInsensitiveMapping) != 0 { - // TODO: There is a potential issue here that the more than one Azure resources might have the same TF resource id (e.g. a parent resource and its singleton child resource). - // Ideally, we should refactor the resource mapping file to make the Azure resource id as key, and record the TF id and TF address (type + name) as its value. - if info, ok := caseInsensitiveMapping[strings.ToUpper(res.TFId)]; ok { - item.TFResourceId = info.id - item.TFAddr = info.addr + if len(meta.resourceMapping) != 0 { + if entity, ok := meta.resourceMapping[strings.ToUpper(res.AzureId.String())]; ok { + item.TFResourceId = entity.ResourceId + item.TFAddr = tfaddr.TFAddr{ + Type: entity.ResourceType, + Name: entity.ResourceName, + } } } else { // Only auto deduce the TF resource type from recommendations when there is no resource mapping file specified. @@ -142,7 +129,14 @@ func (meta *MetaGroupImpl) ListResource() (ImportList, error) { func (meta MetaGroupImpl) ExportResourceMapping(l ImportList) error { m := resmap.ResourceMapping{} for _, item := range l { - m[item.TFResourceId] = item.TFAddr + if item.TFAddr.Type == "" { + continue + } + m[strings.ToUpper(item.AzureResourceID.String())] = resmap.ResourceMapEntity{ + ResourceId: item.TFResourceId, + ResourceType: item.TFAddr.Type, + ResourceName: item.TFAddr.Name, + } } output := filepath.Join(meta.Workspace(), ResourceMappingFileName) b, err := json.MarshalIndent(m, "", "\t") diff --git a/internal/resmap/resmap.go b/internal/resmap/resmap.go index 09988464..6969c7cd 100644 --- a/internal/resmap/resmap.go +++ b/internal/resmap/resmap.go @@ -1,39 +1,13 @@ package resmap -import ( - "encoding/json" - "fmt" - - "github.com/Azure/aztfy/internal/tfaddr" -) - -type ResourceMapping map[string]tfaddr.TFAddr - -func (res ResourceMapping) MarshalJSON() ([]byte, error) { - m := map[string]string{} - for id, addr := range res { - m[id] = addr.String() - } - return json.Marshal(m) +type ResourceMapEntity struct { + // TF resource ID + ResourceId string `json:"resource_id"` + // TF resource type + ResourceType string `json:"resource_type"` + // TF resource name + ResourceName string `json:"resource_name"` } -func (res *ResourceMapping) UnmarshalJSON(b []byte) error { - out := ResourceMapping{} - var m map[string]string - if err := json.Unmarshal(b, &m); err != nil { - return err - } - for id, addr := range m { - var tfAddr tfaddr.TFAddr - if addr != "" { - pTFAddr, err := tfaddr.ParseTFResourceAddr(addr) - if err != nil { - return fmt.Errorf("parsing TF address %q: %v", addr, err) - } - tfAddr = *pTFAddr - } - out[id] = tfAddr - } - *res = out - return nil -} +// ResourceMapping is the resource mapping file, the key is the Azure resource Id in uppercase. +type ResourceMapping map[string]ResourceMapEntity diff --git a/internal/test/cases/case_applicationinsight_webtest.go b/internal/test/cases/case_applicationinsight_webtest.go index fc454bc9..4d987713 100644 --- a/internal/test/cases/case_applicationinsight_webtest.go +++ b/internal/test/cases/case_applicationinsight_webtest.go @@ -1,7 +1,6 @@ package cases import ( - "encoding/json" "fmt" "github.com/Azure/aztfy/internal/test" @@ -54,17 +53,24 @@ XML } func (CaseApplicationInsightWebTest) ResourceMapping(d test.Data) (resmap.ResourceMapping, error) { - rm := fmt.Sprintf(`{ -"/subscriptions/%[1]s/resourceGroups/%[2]s": "azurerm_resource_group.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Insights/components/test-%[3]s": "azurerm_application_insights.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Insights/webTests/test-%[3]s": "azurerm_application_insights_web_test.test" + return test.ResourceMapping(fmt.Sprintf(`{ +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_resource_group", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s" +}, +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.insights/components/test-%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_application_insights", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Insights/components/test-%[3]s" +}, +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.insights/webtests/test-%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_application_insights_web_test", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Insights/webTests/test-%[3]s" } -`, d.SubscriptionId, d.RandomRgName(), d.RandomStringOfLength(8)) - m := resmap.ResourceMapping{} - if err := json.Unmarshal([]byte(rm), &m); err != nil { - return nil, err - } - return m, nil +} +`, d.SubscriptionId, d.RandomRgName(), d.RandomStringOfLength(8))) } func (CaseApplicationInsightWebTest) SingleResourceContext(d test.Data) ([]SingleResourceContext, error) { diff --git a/internal/test/cases/case_compute_vm_disk.go b/internal/test/cases/case_compute_vm_disk.go index b5d9ad2b..26c5d407 100644 --- a/internal/test/cases/case_compute_vm_disk.go +++ b/internal/test/cases/case_compute_vm_disk.go @@ -1,7 +1,6 @@ package cases import ( - "encoding/json" "fmt" "github.com/Azure/aztfy/internal/test" @@ -90,21 +89,51 @@ resource "azurerm_virtual_machine_data_disk_attachment" "test" { } func (CaseComputeVMDisk) ResourceMapping(d test.Data) (resmap.ResourceMapping, error) { - rm := fmt.Sprintf(`{ -"/subscriptions/%[1]s/resourceGroups/%[2]s": "azurerm_resource_group.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Compute/disks/aztfy-test-%[3]s": "azurerm_managed_disk.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Compute/virtualMachines/aztfy-test-%[3]s/dataDisks/aztfy-test-%[3]s": "azurerm_virtual_machine_data_disk_attachment.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Compute/virtualMachines/aztfy-test-%[3]s": "azurerm_linux_virtual_machine.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Network/networkInterfaces/aztfy-test-%[3]s": "azurerm_network_interface.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Network/virtualNetworks/aztfy-test-%[3]s": "azurerm_virtual_network.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Network/virtualNetworks/aztfy-test-%[3]s/subnets/internal": "azurerm_subnet.test" + return test.ResourceMapping(fmt.Sprintf(`{ +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_resource_group", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.compute/disks/aztfy-test-%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_managed_disk", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Compute/disks/aztfy-test-%[3]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.compute/virtualmachines/aztfy-test-%[3]s/datadisks/aztfy-test-%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_virtual_machine_data_disk_attachment", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Compute/virtualMachines/aztfy-test-%[3]s/dataDisks/aztfy-test-%[3]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.compute/virtualmachines/aztfy-test-%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_linux_virtual_machine", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Compute/virtualMachines/aztfy-test-%[3]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.network/networkinterfaces/aztfy-test-%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_network_interface", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Network/networkInterfaces/aztfy-test-%[3]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.network/virtualnetworks/aztfy-test-%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_virtual_network", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Network/virtualNetworks/aztfy-test-%[3]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.network/virtualnetworks/aztfy-test-%[3]s/subnets/internal" | ToUpper | Quote }}: { + "resource_type": "azurerm_subnet", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Network/virtualNetworks/aztfy-test-%[3]s/subnets/internal" +} + } -`, d.SubscriptionId, d.RandomRgName(), d.RandomStringOfLength(8)) - m := resmap.ResourceMapping{} - if err := json.Unmarshal([]byte(rm), &m); err != nil { - return nil, err - } - return m, nil +`, d.SubscriptionId, d.RandomRgName(), d.RandomStringOfLength(8))) } func (CaseComputeVMDisk) SingleResourceContext(d test.Data) ([]SingleResourceContext, error) { diff --git a/internal/test/cases/case_function_app_slot.go b/internal/test/cases/case_function_app_slot.go index cac8d919..61a25ead 100644 --- a/internal/test/cases/case_function_app_slot.go +++ b/internal/test/cases/case_function_app_slot.go @@ -1,7 +1,6 @@ package cases import ( - "encoding/json" "fmt" "github.com/Azure/aztfy/internal/test" @@ -61,20 +60,41 @@ resource "azurerm_windows_function_app_slot" "test" { } func (CaseFunctionAppSlot) ResourceMapping(d test.Data) (resmap.ResourceMapping, error) { - rm := fmt.Sprintf(`{ -"/subscriptions/%[1]s/resourceGroups/%[2]s": "azurerm_resource_group.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Storage/storageAccounts/aztfytest%[3]s": "azurerm_storage_account.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Web/serverfarms/aztfy-test-%[3]s": "azurerm_service_plan.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Web/sites/aztfy-test-%[3]s": "azurerm_windows_function_app.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Web/sites/aztfy-test-%[3]s/slots/aztfy-test-%[3]s": "azurerm_windows_function_app_slot.test" + return test.ResourceMapping(fmt.Sprintf(`{ +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_resource_group", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.storage/storageaccounts/aztfytest%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_storage_account", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Storage/storageAccounts/aztfytest%[3]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.web/serverfarms/aztfy-test-%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_service_plan", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Web/serverfarms/aztfy-test-%[3]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.web/sites/aztfy-test-%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_windows_function_app", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Web/sites/aztfy-test-%[3]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.web/sites/aztfy-test-%[3]s/slots/aztfy-test-%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_windows_function_app_slot", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Web/sites/aztfy-test-%[3]s/slots/aztfy-test-%[3]s" } -`, d.SubscriptionId, d.RandomRgName(), d.RandomStringOfLength(8)) - m := resmap.ResourceMapping{} - if err := json.Unmarshal([]byte(rm), &m); err != nil { - return nil, err - } - return m, nil + +} +`, d.SubscriptionId, d.RandomRgName(), d.RandomStringOfLength(8))) } + func (CaseFunctionAppSlot) SingleResourceContext(d test.Data) ([]SingleResourceContext, error) { return []SingleResourceContext{ { diff --git a/internal/test/cases/case_key_vault_nested_items.go b/internal/test/cases/case_key_vault_nested_items.go index 39e9122a..62c55c21 100644 --- a/internal/test/cases/case_key_vault_nested_items.go +++ b/internal/test/cases/case_key_vault_nested_items.go @@ -2,7 +2,6 @@ package cases import ( "context" - "encoding/json" "fmt" "os" "strings" @@ -181,19 +180,39 @@ func (c CaseKeyVaultNestedItems) ResourceMapping(d test.Data) (resmap.ResourceMa if err != nil { return nil, err } - rm := fmt.Sprintf(`{ -"/subscriptions/%[1]s/resourceGroups/%[2]s": "azurerm_resource_group.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.KeyVault/vaults/aztfy-test-%[3]s": "azurerm_key_vault.test", -%[4]q: "azurerm_key_vault_key.test", -%[5]q: "azurerm_key_vault_secret.test", -%[6]q: "azurerm_key_vault_certificate.test" + return test.ResourceMapping(fmt.Sprintf(`{ +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_resource_group", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.keyvault/vaults/aztfy-test-%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_key_vault", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.KeyVault/vaults/aztfy-test-%[3]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.keyvault/vaults/aztfy-test-%[3]s/keys/key-%[3]s" | ToUpper | Quote }} : { + "resource_type": "azurerm_key_vault_key", + "resource_name": "test", + "resource_id": %[4]q +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.keyvault/vaults/aztfy-test-%[3]s/secrets/secret-%[3]s" | ToUpper | Quote }} : { + "resource_type": "azurerm_key_vault_secret", + "resource_name": "test", + "resource_id": %[5]q +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.keyvault/vaults/aztfy-test-%[3]s/certificates/cert-%[3]s" | ToUpper | Quote }} : { + "resource_type": "azurerm_key_vault_certificate", + "resource_name": "test", + "resource_id": %[6]q } -`, d.SubscriptionId, d.RandomRgName(), d.RandomStringOfLength(8), keyId, secretId, certId) - m := resmap.ResourceMapping{} - if err := json.Unmarshal([]byte(rm), &m); err != nil { - return nil, err - } - return m, nil + +} +`, d.SubscriptionId, d.RandomRgName(), d.RandomStringOfLength(8), keyId, secretId, certId)) } func (c CaseKeyVaultNestedItems) SingleResourceContext(d test.Data) ([]SingleResourceContext, error) { diff --git a/internal/test/cases/case_signalr_service.go b/internal/test/cases/case_signalr_service.go index c6b9e489..e42939e8 100644 --- a/internal/test/cases/case_signalr_service.go +++ b/internal/test/cases/case_signalr_service.go @@ -1,7 +1,6 @@ package cases import ( - "encoding/json" "fmt" "github.com/Azure/aztfy/internal/test" @@ -39,16 +38,21 @@ resource "azurerm_signalr_service" "test" { } func (CaseSignalRService) ResourceMapping(d test.Data) (resmap.ResourceMapping, error) { - rm := fmt.Sprintf(`{ -"/subscriptions/%[1]s/resourceGroups/%[2]s": "azurerm_resource_group.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.SignalRService/signalR/test-%[3]s": "azurerm_signalr_service.test" + return test.ResourceMapping(fmt.Sprintf(`{ +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_resource_group", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.signalrservice/signalr/test-%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_signalr_service", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.SignalRService/signalR/test-%[3]s" +} + } -`, d.SubscriptionId, d.RandomRgName(), d.RandomStringOfLength(8)) - m := resmap.ResourceMapping{} - if err := json.Unmarshal([]byte(rm), &m); err != nil { - return nil, err - } - return m, nil +`, d.SubscriptionId, d.RandomRgName(), d.RandomStringOfLength(8))) } func (CaseSignalRService) SingleResourceContext(d test.Data) ([]SingleResourceContext, error) { diff --git a/internal/test/cases/case_storage_file_share.go b/internal/test/cases/case_storage_file_share.go index d1b75200..2fac7883 100644 --- a/internal/test/cases/case_storage_file_share.go +++ b/internal/test/cases/case_storage_file_share.go @@ -1,7 +1,6 @@ package cases import ( - "encoding/json" "fmt" "github.com/Azure/aztfy/internal/test" @@ -43,17 +42,27 @@ resource "azurerm_storage_share" "test" { } func (CaseStorageFileShare) ResourceMapping(d test.Data) (resmap.ResourceMapping, error) { - rm := fmt.Sprintf(`{ -"/subscriptions/%[1]s/resourceGroups/%[2]s": "azurerm_resource_group.test", -"/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Storage/storageAccounts/aztfy%[3]s": "azurerm_storage_account.test", -"https://aztfy%[3]s.file.core.windows.net/aztfy%[3]s": "azurerm_storage_share.test" + return test.ResourceMapping(fmt.Sprintf(`{ +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_resource_group", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.storage/storageaccounts/aztfy%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_storage_account", + "resource_name": "test", + "resource_id": "/subscriptions/%[1]s/resourceGroups/%[2]s/providers/Microsoft.Storage/storageAccounts/aztfy%[3]s" +}, + +{{ "/subscriptions/%[1]s/resourcegroups/%[2]s/providers/microsoft.storage/storageaccounts/aztfy%[3]s/fileservices/default/shares/aztfy%[3]s" | ToUpper | Quote }}: { + "resource_type": "azurerm_storage_share", + "resource_name": "test", + "resource_id": "https://aztfy%[3]s.file.core.windows.net/aztfy%[3]s" +} + } -`, d.SubscriptionId, d.RandomRgName(), d.RandomStringOfLength(8)) - m := resmap.ResourceMapping{} - if err := json.Unmarshal([]byte(rm), &m); err != nil { - return nil, err - } - return m, nil +`, d.SubscriptionId, d.RandomRgName(), d.RandomStringOfLength(8))) } func (CaseStorageFileShare) SingleResourceContext(d test.Data) ([]SingleResourceContext, error) { diff --git a/internal/test/utils.go b/internal/test/utils.go index 6de38fc6..ec559227 100644 --- a/internal/test/utils.go +++ b/internal/test/utils.go @@ -1,11 +1,17 @@ package test import ( + "bytes" "context" + "encoding/json" "os" "path/filepath" + "strconv" + "strings" "testing" + "text/template" + "github.com/Azure/aztfy/internal/resmap" "github.com/hashicorp/go-version" install "github.com/hashicorp/hc-install" "github.com/hashicorp/hc-install/fs" @@ -63,11 +69,23 @@ func Verify(t *testing.T, ctx context.Context, aztfyDir, tfexecPath string, expe t.Fatalf("terraform plan in the generated workspace failed: %v", err) } if diff { - b, err := os.ReadFile(planFile) + plan, err := tf.ShowPlanFile(ctx, planFile) if err != nil { - t.Log(err) + t.Logf("failed to show plan file %s: %v", planFile, err) + } else { + for _, change := range plan.ResourceChanges { + if change == nil { + continue + } + b, err := json.MarshalIndent(change.Change, "", " ") + if err != nil { + t.Logf("failed to marshal plan for %s: %v", change.Address, err) + } else { + t.Logf("%s\n%s\n", change.Address, string(b)) + } + } } - t.Fatalf("terraform plan shows diff:\n%s\n", string(b)) + t.Fatalf("terraform plan has diff") } t.Log("Running: terraform show") state, err := tf.ShowStateFile(ctx, filepath.Join(aztfyDir, "terraform.tfstate")) @@ -78,3 +96,27 @@ func Verify(t *testing.T, ctx context.Context, aztfyDir, tfexecPath string, expe t.Fatalf("expected terrafied resource: %d, got=%d", expectResCnt, n) } } + +func ResourceMapping(tpl string) (resmap.ResourceMapping, error) { + funcMap := template.FuncMap{ + "ToUpper": strings.ToUpper, + "Quote": strconv.Quote, + } + + gotpl, err := template.New("myTemplate").Funcs(funcMap).Parse(tpl) + if err != nil { + return nil, err + } + + var result bytes.Buffer + if err := gotpl.Execute(&result, nil); err != nil { + return nil, err + } + + m := resmap.ResourceMapping{} + if err := json.Unmarshal(result.Bytes(), &m); err != nil { + return nil, err + } + + return m, nil +}