github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/state/remote/azure.go (about)

     1  package remote
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  
     9  	"github.com/Azure/azure-sdk-for-go/arm/storage"
    10  	mainStorage "github.com/Azure/azure-sdk-for-go/storage"
    11  	"github.com/Azure/go-autorest/autorest/azure"
    12  	riviera "github.com/jen20/riviera/azure"
    13  )
    14  
    15  func azureFactory(conf map[string]string) (Client, error) {
    16  	storageAccountName, ok := conf["storage_account_name"]
    17  	if !ok {
    18  		return nil, fmt.Errorf("missing 'storage_account_name' configuration")
    19  	}
    20  	containerName, ok := conf["container_name"]
    21  	if !ok {
    22  		return nil, fmt.Errorf("missing 'container_name' configuration")
    23  	}
    24  	keyName, ok := conf["key"]
    25  	if !ok {
    26  		return nil, fmt.Errorf("missing 'key' configuration")
    27  	}
    28  
    29  	env, err := getAzureEnvironmentFromConf(conf)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	accessKey, ok := confOrEnv(conf, "access_key", "ARM_ACCESS_KEY")
    35  	if !ok {
    36  		resourceGroupName, ok := conf["resource_group_name"]
    37  		if !ok {
    38  			return nil, fmt.Errorf("missing 'resource_group_name' configuration")
    39  		}
    40  
    41  		var err error
    42  		accessKey, err = getStorageAccountAccessKey(conf, resourceGroupName, storageAccountName, env)
    43  		if err != nil {
    44  			return nil, fmt.Errorf("Couldn't read access key from storage account: %s.", err)
    45  		}
    46  	}
    47  
    48  	storageClient, err := mainStorage.NewClient(storageAccountName, accessKey, env.StorageEndpointSuffix,
    49  		mainStorage.DefaultAPIVersion, true)
    50  	if err != nil {
    51  		return nil, fmt.Errorf("Error creating storage client for storage account %q: %s", storageAccountName, err)
    52  	}
    53  
    54  	blobClient := storageClient.GetBlobService()
    55  	leaseID, _ := confOrEnv(conf, "lease_id", "ARM_LEASE_ID")
    56  
    57  	return &AzureClient{
    58  		blobClient:    &blobClient,
    59  		containerName: containerName,
    60  		keyName:       keyName,
    61  		leaseID:       leaseID,
    62  	}, nil
    63  }
    64  
    65  func getStorageAccountAccessKey(conf map[string]string, resourceGroupName, storageAccountName string, env azure.Environment) (string, error) {
    66  	creds, err := getCredentialsFromConf(conf, env)
    67  	if err != nil {
    68  		return "", err
    69  	}
    70  
    71  	oauthConfig, err := env.OAuthConfigForTenant(creds.TenantID)
    72  	if err != nil {
    73  		return "", err
    74  	}
    75  	if oauthConfig == nil {
    76  		return "", fmt.Errorf("Unable to configure OAuthConfig for tenant %s", creds.TenantID)
    77  	}
    78  
    79  	spt, err := azure.NewServicePrincipalToken(*oauthConfig, creds.ClientID, creds.ClientSecret, env.ResourceManagerEndpoint)
    80  	if err != nil {
    81  		return "", err
    82  	}
    83  
    84  	accountsClient := storage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, creds.SubscriptionID)
    85  	accountsClient.Authorizer = spt
    86  
    87  	keys, err := accountsClient.ListKeys(resourceGroupName, storageAccountName)
    88  	if err != nil {
    89  		return "", fmt.Errorf("Error retrieving keys for storage account %q: %s", storageAccountName, err)
    90  	}
    91  
    92  	if keys.Keys == nil {
    93  		return "", fmt.Errorf("Nil key returned for storage account %q", storageAccountName)
    94  	}
    95  
    96  	accessKeys := *keys.Keys
    97  	return *accessKeys[0].Value, nil
    98  }
    99  
   100  func getCredentialsFromConf(conf map[string]string, env azure.Environment) (*riviera.AzureResourceManagerCredentials, error) {
   101  	subscriptionID, ok := confOrEnv(conf, "arm_subscription_id", "ARM_SUBSCRIPTION_ID")
   102  	if !ok {
   103  		return nil, fmt.Errorf("missing 'arm_subscription_id' configuration")
   104  	}
   105  	clientID, ok := confOrEnv(conf, "arm_client_id", "ARM_CLIENT_ID")
   106  	if !ok {
   107  		return nil, fmt.Errorf("missing 'arm_client_id' configuration")
   108  	}
   109  	clientSecret, ok := confOrEnv(conf, "arm_client_secret", "ARM_CLIENT_SECRET")
   110  	if !ok {
   111  		return nil, fmt.Errorf("missing 'arm_client_secret' configuration")
   112  	}
   113  	tenantID, ok := confOrEnv(conf, "arm_tenant_id", "ARM_TENANT_ID")
   114  	if !ok {
   115  		return nil, fmt.Errorf("missing 'arm_tenant_id' configuration")
   116  	}
   117  
   118  	return &riviera.AzureResourceManagerCredentials{
   119  		SubscriptionID:          subscriptionID,
   120  		ClientID:                clientID,
   121  		ClientSecret:            clientSecret,
   122  		TenantID:                tenantID,
   123  		ActiveDirectoryEndpoint: env.ActiveDirectoryEndpoint,
   124  		ResourceManagerEndpoint: env.ResourceManagerEndpoint,
   125  	}, nil
   126  }
   127  
   128  func getAzureEnvironmentFromConf(conf map[string]string) (azure.Environment, error) {
   129  	envName, ok := confOrEnv(conf, "environment", "ARM_ENVIRONMENT")
   130  	if !ok {
   131  		return azure.PublicCloud, nil
   132  	}
   133  
   134  	env, err := azure.EnvironmentFromName(envName)
   135  	if err != nil {
   136  		// try again with wrapped value to support readable values like german instead of AZUREGERMANCLOUD
   137  		var innerErr error
   138  		env, innerErr = azure.EnvironmentFromName(fmt.Sprintf("AZURE%sCLOUD", envName))
   139  		if innerErr != nil {
   140  			return env, fmt.Errorf("invalid 'environment' configuration: %s", err)
   141  		}
   142  	}
   143  
   144  	return env, nil
   145  }
   146  
   147  func confOrEnv(conf map[string]string, confKey, envVar string) (string, bool) {
   148  	value, ok := conf[confKey]
   149  	if ok {
   150  		return value, true
   151  	}
   152  
   153  	value = os.Getenv(envVar)
   154  
   155  	return value, value != ""
   156  }
   157  
   158  type AzureClient struct {
   159  	blobClient    *mainStorage.BlobStorageClient
   160  	containerName string
   161  	keyName       string
   162  	leaseID       string
   163  }
   164  
   165  func (c *AzureClient) Get() (*Payload, error) {
   166  	blob, err := c.blobClient.GetBlob(c.containerName, c.keyName)
   167  	if err != nil {
   168  		if storErr, ok := err.(mainStorage.AzureStorageServiceError); ok {
   169  			if storErr.Code == "BlobNotFound" {
   170  				return nil, nil
   171  			}
   172  		}
   173  		return nil, err
   174  	}
   175  
   176  	defer blob.Close()
   177  
   178  	data, err := ioutil.ReadAll(blob)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  
   183  	payload := &Payload{
   184  		Data: data,
   185  	}
   186  
   187  	// If there was no data, then return nil
   188  	if len(payload.Data) == 0 {
   189  		return nil, nil
   190  	}
   191  
   192  	return payload, nil
   193  }
   194  
   195  func (c *AzureClient) Put(data []byte) error {
   196  	headers := map[string]string{
   197  		"Content-Type": "application/json",
   198  	}
   199  
   200  	if c.leaseID != "" {
   201  		headers["x-ms-lease-id"] = c.leaseID
   202  	}
   203  
   204  	return c.blobClient.CreateBlockBlobFromReader(
   205  		c.containerName,
   206  		c.keyName,
   207  		uint64(len(data)),
   208  		bytes.NewReader(data),
   209  		headers,
   210  	)
   211  }
   212  
   213  func (c *AzureClient) Delete() error {
   214  	headers := map[string]string{}
   215  	if c.leaseID != "" {
   216  		headers["x-ms-lease-id"] = c.leaseID
   217  	}
   218  
   219  	return c.blobClient.DeleteBlob(c.containerName, c.keyName, headers)
   220  }