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