-
Notifications
You must be signed in to change notification settings - Fork 192
/
Copy pathazure_resource_set_hack.go
130 lines (116 loc) · 4.32 KB
/
azure_resource_set_hack.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package resourceset
import (
"encoding/json"
"fmt"
"strings"
"github.com/magodo/armid"
"github.com/magodo/aztft/aztft"
"github.com/tidwall/gjson"
)
// PopulateResourceTypesNeedsBody is a map to record resources that need API body to decide whether to populate.
// This is used in single resource mode to see whether an API call is needed.
var PopulateResourceTypesNeedsBody = map[string]bool{
"MICROSOFT.COMPUTE/VIRTUALMACHINES": true,
}
// PopulateResource populate single resource for certain Azure resouce type that is known might maps to more than one TF resources.
// In most cases, this step is used to populate the Azure managed resource, or the Terraform pesudo (i.e. association/property-like) resource.
func (rset *AzureResourceSet) PopulateResource() error {
// Populate managed data disk (and the association) for VMs that are missing from Azure exported resource set.
if err := rset.populateForVirtualMachine(); err != nil {
return err
}
return nil
}
// ReduceResource reduce the resource set for certain multiple Azure resources that are known to be mapped to only one TF resource.
func (rset *AzureResourceSet) ReduceResource() error {
// KeyVault certificate is a special resource that its data plane entity is composed of two control plane resources.
// Azure exports the control plane resource ids, while Terraform uses its data plane counterpart.
if err := rset.reduceForKeyVaultCertificate(); err != nil {
return err
}
return nil
}
func (rset *AzureResourceSet) reduceForKeyVaultCertificate() error {
newResoruces := []AzureResource{}
pending := map[string]AzureResource{}
for _, res := range rset.Resources {
if !strings.EqualFold(res.Id.RouteScopeString(), "/Microsoft.KeyVault/vaults/keys") && !strings.EqualFold(res.Id.RouteScopeString(), "/Microsoft.KeyVault/vaults/secrets") {
newResoruces = append(newResoruces, res)
continue
}
names := res.Id.Names()
certName := names[len(names)-1]
if _, ok := pending[certName]; !ok {
pending[certName] = res
continue
}
delete(pending, certName)
certId := res.Id.Clone().(*armid.ScopedResourceId)
certId.AttrTypes[len(certId.AttrTypes)-1] = "certificates"
newResoruces = append(newResoruces, AzureResource{
Id: certId,
})
}
for _, res := range pending {
newResoruces = append(newResoruces, res)
}
rset.Resources = newResoruces
return nil
}
func (rset *AzureResourceSet) populateForVirtualMachine() error {
for _, res := range rset.Resources[:] {
if strings.ToUpper(res.Id.RouteScopeString()) != "/MICROSOFT.COMPUTE/VIRTUALMACHINES" {
continue
}
disks, err := populateManagedResourcesByPath(res, "properties.storageProfile.dataDisks.#.managedDisk.id")
if err != nil {
return fmt.Errorf(`populating managed disks for %q: %v`, res.Id, err)
}
rset.Resources = append(rset.Resources, disks...)
// Add the association resource
for _, disk := range disks {
diskName := disk.Id.Names()[0]
// It doesn't matter using linux/windows below, as their resource ids are the same.
vmTFId, err := aztft.QueryId(res.Id.String(), "azurerm_linux_virtual_machine", false)
if err != nil {
return fmt.Errorf("querying resource id for %s: %v", res.Id, err)
}
azureId := res.Id.Clone().(*armid.ScopedResourceId)
azureId.AttrTypes = append(azureId.AttrTypes, "dataDisks")
azureId.AttrNames = append(azureId.AttrNames, diskName)
rset.Resources = append(rset.Resources, AzureResource{
Id: azureId,
PesudoResourceInfo: &PesudoResourceInfo{
TFType: "azurerm_virtual_machine_data_disk_attachment",
TFId: vmTFId + "/dataDisks/" + diskName,
},
})
}
}
return nil
}
// populateManagedResourcesByPath populate the managed resources in the specified paths.
func populateManagedResourcesByPath(res AzureResource, paths ...string) ([]AzureResource, error) {
b, err := json.Marshal(res.Properties)
if err != nil {
return nil, fmt.Errorf("marshaling %v: %v", res.Properties, err)
}
var resources []AzureResource
for _, path := range paths {
result := gjson.GetBytes(b, path)
if !result.Exists() {
continue
}
for _, exprResult := range result.Array() {
mid := exprResult.String()
id, err := armid.ParseResourceId(mid)
if err != nil {
return nil, fmt.Errorf("parsing managed resource id %s: %v", mid, err)
}
resources = append(resources, AzureResource{
Id: id,
})
}
}
return resources, nil
}