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  }