github.com/GoogleCloudPlatform/terraformer@v0.8.18/providers/azure/helper.go (about) 1 // Copyright 2019 The Terraformer Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package azure 16 17 import ( 18 "fmt" 19 "net/url" 20 "strings" 21 ) 22 23 // FROM https://github.com/terraform-providers/terraform-provider-azurerm/blob/6e006ff4e5d1fb200a6b37eb2743ff0ec8b11e0d/azurerm/helpers/azure/resourceid.go#L24 24 25 // ResourceID represents a parsed long-form Azure Resource Manager ID 26 // with the Subscription ID, Resource Group and the Provider as top- 27 // level fields, and other key-value pairs available via a map in the 28 // Path field. 29 type ResourceID struct { 30 SubscriptionID string 31 ResourceGroup string 32 Provider string 33 Path map[string]string 34 } 35 36 // ParseAzureResourceID converts a long-form Azure Resource Manager ID 37 // into a ResourceID. We make assumptions about the structure of URLs, 38 // which is obviously not good, but the best thing available given the 39 // SDK. 40 func ParseAzureResourceID(id string) (*ResourceID, error) { 41 idURL, err := url.ParseRequestURI(id) 42 if err != nil { 43 return nil, fmt.Errorf("Cannot parse Azure ID: %s", err) 44 } 45 46 path := idURL.Path 47 48 path = strings.TrimPrefix(path, "/") 49 path = strings.TrimSuffix(path, "/") 50 51 components := strings.Split(path, "/") 52 53 // We should have an even number of key-value pairs. 54 if len(components)%2 != 0 { 55 return nil, fmt.Errorf("The number of path segments is not divisible by 2 in %q", path) 56 } 57 58 var subscriptionID string 59 60 // Put the constituent key-value pairs into a map 61 componentMap := make(map[string]string, len(components)/2) 62 for current := 0; current < len(components); current += 2 { 63 key := components[current] 64 value := components[current+1] 65 66 // Check key/value for empty strings. 67 if key == "" || value == "" { 68 return nil, fmt.Errorf("Key/Value cannot be empty strings. Key: '%s', Value: '%s'", key, value) 69 } 70 71 // Catch the subscriptionID before it can be overwritten by another "subscriptions" 72 // value in the ID which is the case for the Service Bus subscription resource 73 if key == "subscriptions" && subscriptionID == "" { 74 subscriptionID = value 75 } else { 76 componentMap[key] = value 77 } 78 } 79 80 // Build up a TargetResourceID from the map 81 idObj := &ResourceID{} 82 idObj.Path = componentMap 83 84 if subscriptionID != "" { 85 idObj.SubscriptionID = subscriptionID 86 } else { 87 return nil, fmt.Errorf("No subscription ID found in: %q", path) 88 } 89 90 if resourceGroup, ok := componentMap["resourceGroups"]; ok { 91 idObj.ResourceGroup = resourceGroup 92 delete(componentMap, "resourceGroups") 93 } else if resourceGroup, ok := componentMap["resourcegroups"]; ok { 94 // Some Azure APIs are weird and provide things in lower case... 95 // However it's not clear whether the casing of other elements in the URI 96 // matter, so we explicitly look for that case here. 97 idObj.ResourceGroup = resourceGroup 98 delete(componentMap, "resourcegroups") 99 } 100 101 // It is OK not to have a provider in the case of a resource group 102 if provider, ok := componentMap["providers"]; ok { 103 idObj.Provider = provider 104 delete(componentMap, "providers") 105 } 106 107 return idObj, nil 108 } 109 110 func asHereDoc(json string) string { 111 return fmt.Sprintf(`<<JSON 112 %s 113 JSON`, json) 114 }