github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/azurerm/resourceid.go (about) 1 package azurerm 2 3 import ( 4 "fmt" 5 "net/url" 6 "strings" 7 ) 8 9 // ResourceID represents a parsed long-form Azure Resource Manager ID 10 // with the Subscription ID, Resource Group and the Provider as top- 11 // level fields, and other key-value pairs available via a map in the 12 // Path field. 13 type ResourceID struct { 14 SubscriptionID string 15 ResourceGroup string 16 Provider string 17 Path map[string]string 18 } 19 20 // parseAzureResourceID converts a long-form Azure Resource Manager ID 21 // into a ResourceID. We make assumptions about the structure of URLs, 22 // which is obviously not good, but the best thing available given the 23 // SDK. 24 func parseAzureResourceID(id string) (*ResourceID, error) { 25 idURL, err := url.ParseRequestURI(id) 26 if err != nil { 27 return nil, fmt.Errorf("Cannot parse Azure Id: %s", err) 28 } 29 30 path := idURL.Path 31 32 path = strings.TrimSpace(path) 33 if strings.HasPrefix(path, "/") { 34 path = path[1:] 35 } 36 37 if strings.HasSuffix(path, "/") { 38 path = path[:len(path)-1] 39 } 40 41 components := strings.Split(path, "/") 42 43 // We should have an even number of key-value pairs. 44 if len(components)%2 != 0 { 45 return nil, fmt.Errorf("The number of path segments is not divisible by 2 in %q", path) 46 } 47 48 var subscriptionID string 49 50 // Put the constituent key-value pairs into a map 51 componentMap := make(map[string]string, len(components)/2) 52 for current := 0; current < len(components); current += 2 { 53 key := components[current] 54 value := components[current+1] 55 56 // Check key/value for empty strings. 57 if key == "" || value == "" { 58 return nil, fmt.Errorf("Key/Value cannot be empty strings. Key: '%s', Value: '%s'", key, value) 59 } 60 61 // Catch the subscriptionID before it can be overwritten by another "subscriptions" 62 // value in the ID which is the case for the Service Bus subscription resource 63 if key == "subscriptions" && subscriptionID == "" { 64 subscriptionID = value 65 } else { 66 componentMap[key] = value 67 } 68 } 69 70 // Build up a ResourceID from the map 71 idObj := &ResourceID{} 72 idObj.Path = componentMap 73 74 if subscriptionID != "" { 75 idObj.SubscriptionID = subscriptionID 76 } else { 77 return nil, fmt.Errorf("No subscription ID found in: %q", path) 78 } 79 80 if resourceGroup, ok := componentMap["resourceGroups"]; ok { 81 idObj.ResourceGroup = resourceGroup 82 delete(componentMap, "resourceGroups") 83 } else { 84 // Some Azure APIs are weird and provide things in lower case... 85 // However it's not clear whether the casing of other elements in the URI 86 // matter, so we explicitly look for that case here. 87 if resourceGroup, ok := componentMap["resourcegroups"]; ok { 88 idObj.ResourceGroup = resourceGroup 89 delete(componentMap, "resourcegroups") 90 } else { 91 return nil, fmt.Errorf("No resource group name found in: %q", path) 92 } 93 } 94 95 // It is OK not to have a provider in the case of a resource group 96 if provider, ok := componentMap["providers"]; ok { 97 idObj.Provider = provider 98 delete(componentMap, "providers") 99 } 100 101 return idObj, nil 102 } 103 104 func parseNetworkSecurityGroupName(networkSecurityGroupId string) (string, error) { 105 id, err := parseAzureResourceID(networkSecurityGroupId) 106 if err != nil { 107 return "", fmt.Errorf("[ERROR] Unable to Parse Network Security Group ID '%s': %+v", networkSecurityGroupId, err) 108 } 109 110 return id.Path["networkSecurityGroups"], nil 111 } 112 113 func parseRouteTableName(routeTableId string) (string, error) { 114 id, err := parseAzureResourceID(routeTableId) 115 if err != nil { 116 return "", fmt.Errorf("[ERROR] Unable to parse Route Table ID '%s': %+v", routeTableId, err) 117 } 118 119 return id.Path["routeTables"], nil 120 }