github.com/ojiry/terraform@v0.8.2-0.20161218223921-e50cec712c4a/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  	accessKey, ok := confOrEnv(conf, "access_key", "ARM_ACCESS_KEY")
    30  	if !ok {
    31  		resourceGroupName, ok := conf["resource_group_name"]
    32  		if !ok {
    33  			return nil, fmt.Errorf("missing 'resource_group' configuration")
    34  		}
    35  
    36  		var err error
    37  		accessKey, err = getStorageAccountAccessKey(conf, resourceGroupName, storageAccountName)
    38  		if err != nil {
    39  			return nil, fmt.Errorf("Couldn't read access key from storage account: %s.", err)
    40  		}
    41  	}
    42  
    43  	storageClient, err := mainStorage.NewBasicClient(storageAccountName, accessKey)
    44  	if err != nil {
    45  		return nil, fmt.Errorf("Error creating storage client for storage account %q: %s", storageAccountName, err)
    46  	}
    47  
    48  	blobClient := storageClient.GetBlobService()
    49  	leaseID, _ := confOrEnv(conf, "lease_id", "ARM_LEASE_ID")
    50  
    51  	return &AzureClient{
    52  		blobClient:    &blobClient,
    53  		containerName: containerName,
    54  		keyName:       keyName,
    55  		leaseID:       leaseID,
    56  	}, nil
    57  }
    58  
    59  func getStorageAccountAccessKey(conf map[string]string, resourceGroupName, storageAccountName string) (string, error) {
    60  	creds, err := getCredentialsFromConf(conf)
    61  	if err != nil {
    62  		return "", err
    63  	}
    64  
    65  	oauthConfig, err := azure.PublicCloud.OAuthConfigForTenant(creds.TenantID)
    66  	if err != nil {
    67  		return "", err
    68  	}
    69  	if oauthConfig == nil {
    70  		return "", fmt.Errorf("Unable to configure OAuthConfig for tenant %s", creds.TenantID)
    71  	}
    72  
    73  	spt, err := azure.NewServicePrincipalToken(*oauthConfig, creds.ClientID, creds.ClientSecret, azure.PublicCloud.ResourceManagerEndpoint)
    74  	if err != nil {
    75  		return "", err
    76  	}
    77  
    78  	accountsClient := storage.NewAccountsClient(creds.SubscriptionID)
    79  	accountsClient.Authorizer = spt
    80  
    81  	keys, err := accountsClient.ListKeys(resourceGroupName, storageAccountName)
    82  	if err != nil {
    83  		return "", fmt.Errorf("Error retrieving keys for storage account %q: %s", storageAccountName, err)
    84  	}
    85  
    86  	if keys.Keys == nil {
    87  		return "", fmt.Errorf("Nil key returned for storage account %q", storageAccountName)
    88  	}
    89  
    90  	accessKeys := *keys.Keys
    91  	return *accessKeys[0].Value, nil
    92  }
    93  
    94  func getCredentialsFromConf(conf map[string]string) (*riviera.AzureResourceManagerCredentials, error) {
    95  	subscriptionID, ok := confOrEnv(conf, "arm_subscription_id", "ARM_SUBSCRIPTION_ID")
    96  	if !ok {
    97  		return nil, fmt.Errorf("missing 'arm_subscription_id' configuration")
    98  	}
    99  	clientID, ok := confOrEnv(conf, "arm_client_id", "ARM_CLIENT_ID")
   100  	if !ok {
   101  		return nil, fmt.Errorf("missing 'arm_client_id' configuration")
   102  	}
   103  	clientSecret, ok := confOrEnv(conf, "arm_client_secret", "ARM_CLIENT_SECRET")
   104  	if !ok {
   105  		return nil, fmt.Errorf("missing 'arm_client_secret' configuration")
   106  	}
   107  	tenantID, ok := confOrEnv(conf, "arm_tenant_id", "ARM_TENANT_ID")
   108  	if !ok {
   109  		return nil, fmt.Errorf("missing 'arm_tenant_id' configuration")
   110  	}
   111  
   112  	return &riviera.AzureResourceManagerCredentials{
   113  		SubscriptionID: subscriptionID,
   114  		ClientID:       clientID,
   115  		ClientSecret:   clientSecret,
   116  		TenantID:       tenantID,
   117  	}, nil
   118  }
   119  
   120  func confOrEnv(conf map[string]string, confKey, envVar string) (string, bool) {
   121  	value, ok := conf[confKey]
   122  	if ok {
   123  		return value, true
   124  	}
   125  
   126  	value = os.Getenv(envVar)
   127  
   128  	return value, value != ""
   129  }
   130  
   131  type AzureClient struct {
   132  	blobClient    *mainStorage.BlobStorageClient
   133  	containerName string
   134  	keyName       string
   135  	leaseID       string
   136  }
   137  
   138  func (c *AzureClient) Get() (*Payload, error) {
   139  	blob, err := c.blobClient.GetBlob(c.containerName, c.keyName)
   140  	if err != nil {
   141  		if storErr, ok := err.(mainStorage.AzureStorageServiceError); ok {
   142  			if storErr.Code == "BlobNotFound" {
   143  				return nil, nil
   144  			}
   145  		}
   146  		return nil, err
   147  	}
   148  
   149  	defer blob.Close()
   150  
   151  	data, err := ioutil.ReadAll(blob)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	payload := &Payload{
   157  		Data: data,
   158  	}
   159  
   160  	// If there was no data, then return nil
   161  	if len(payload.Data) == 0 {
   162  		return nil, nil
   163  	}
   164  
   165  	return payload, nil
   166  }
   167  
   168  func (c *AzureClient) Put(data []byte) error {
   169  	headers := map[string]string{
   170  		"Content-Type": "application/json",
   171  	}
   172  
   173  	if c.leaseID != "" {
   174  		headers["x-ms-lease-id"] = c.leaseID
   175  	}
   176  
   177  	return c.blobClient.CreateBlockBlobFromReader(
   178  		c.containerName,
   179  		c.keyName,
   180  		uint64(len(data)),
   181  		bytes.NewReader(data),
   182  		headers,
   183  	)
   184  }
   185  
   186  func (c *AzureClient) Delete() error {
   187  	headers := map[string]string{}
   188  	if c.leaseID != "" {
   189  		headers["x-ms-lease-id"] = c.leaseID
   190  	}
   191  
   192  	return c.blobClient.DeleteBlob(c.containerName, c.keyName, headers)
   193  }