github.com/sathish1597/hashicorp-terraform@v0.11.12-beta1/backend/remote-state/azure/backend.go (about)

     1  package azure
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	armStorage "github.com/Azure/azure-sdk-for-go/arm/storage"
     8  	"github.com/Azure/azure-sdk-for-go/storage"
     9  	"github.com/Azure/go-autorest/autorest"
    10  	"github.com/Azure/go-autorest/autorest/adal"
    11  	"github.com/Azure/go-autorest/autorest/azure"
    12  	"github.com/hashicorp/terraform/backend"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  // New creates a new backend for S3 remote state.
    17  func New() backend.Backend {
    18  	s := &schema.Backend{
    19  		Schema: map[string]*schema.Schema{
    20  			"storage_account_name": {
    21  				Type:        schema.TypeString,
    22  				Required:    true,
    23  				Description: "The name of the storage account.",
    24  			},
    25  
    26  			"container_name": {
    27  				Type:        schema.TypeString,
    28  				Required:    true,
    29  				Description: "The container name.",
    30  			},
    31  
    32  			"key": {
    33  				Type:        schema.TypeString,
    34  				Required:    true,
    35  				Description: "The blob key.",
    36  			},
    37  
    38  			"environment": {
    39  				Type:        schema.TypeString,
    40  				Optional:    true,
    41  				Description: "The Azure cloud environment.",
    42  				DefaultFunc: schema.EnvDefaultFunc("ARM_ENVIRONMENT", ""),
    43  			},
    44  
    45  			"access_key": {
    46  				Type:        schema.TypeString,
    47  				Optional:    true,
    48  				Description: "The access key.",
    49  				DefaultFunc: schema.EnvDefaultFunc("ARM_ACCESS_KEY", ""),
    50  			},
    51  
    52  			"resource_group_name": {
    53  				Type:        schema.TypeString,
    54  				Optional:    true,
    55  				Description: "The resource group name.",
    56  			},
    57  
    58  			"arm_subscription_id": {
    59  				Type:        schema.TypeString,
    60  				Optional:    true,
    61  				Description: "The Subscription ID.",
    62  				DefaultFunc: schema.EnvDefaultFunc("ARM_SUBSCRIPTION_ID", ""),
    63  			},
    64  
    65  			"arm_client_id": {
    66  				Type:        schema.TypeString,
    67  				Optional:    true,
    68  				Description: "The Client ID.",
    69  				DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_ID", ""),
    70  			},
    71  
    72  			"arm_client_secret": {
    73  				Type:        schema.TypeString,
    74  				Optional:    true,
    75  				Description: "The Client Secret.",
    76  				DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_SECRET", ""),
    77  			},
    78  
    79  			"arm_tenant_id": {
    80  				Type:        schema.TypeString,
    81  				Optional:    true,
    82  				Description: "The Tenant ID.",
    83  				DefaultFunc: schema.EnvDefaultFunc("ARM_TENANT_ID", ""),
    84  			},
    85  		},
    86  	}
    87  
    88  	result := &Backend{Backend: s}
    89  	result.Backend.ConfigureFunc = result.configure
    90  	return result
    91  }
    92  
    93  type Backend struct {
    94  	*schema.Backend
    95  
    96  	// The fields below are set from configure
    97  	blobClient storage.BlobStorageClient
    98  
    99  	containerName string
   100  	keyName       string
   101  	leaseID       string
   102  }
   103  
   104  type BackendConfig struct {
   105  	AccessKey          string
   106  	Environment        string
   107  	ClientID           string
   108  	ClientSecret       string
   109  	ResourceGroupName  string
   110  	StorageAccountName string
   111  	SubscriptionID     string
   112  	TenantID           string
   113  }
   114  
   115  func (b *Backend) configure(ctx context.Context) error {
   116  	if b.containerName != "" {
   117  		return nil
   118  	}
   119  
   120  	// Grab the resource data
   121  	data := schema.FromContextBackendConfig(ctx)
   122  
   123  	b.containerName = data.Get("container_name").(string)
   124  	b.keyName = data.Get("key").(string)
   125  
   126  	config := BackendConfig{
   127  		AccessKey:          data.Get("access_key").(string),
   128  		ClientID:           data.Get("arm_client_id").(string),
   129  		ClientSecret:       data.Get("arm_client_secret").(string),
   130  		Environment:        data.Get("environment").(string),
   131  		ResourceGroupName:  data.Get("resource_group_name").(string),
   132  		StorageAccountName: data.Get("storage_account_name").(string),
   133  		SubscriptionID:     data.Get("arm_subscription_id").(string),
   134  		TenantID:           data.Get("arm_tenant_id").(string),
   135  	}
   136  
   137  	blobClient, err := getBlobClient(config)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	b.blobClient = blobClient
   142  
   143  	return nil
   144  }
   145  
   146  func getBlobClient(config BackendConfig) (storage.BlobStorageClient, error) {
   147  	var client storage.BlobStorageClient
   148  
   149  	env, err := getAzureEnvironment(config.Environment)
   150  	if err != nil {
   151  		return client, err
   152  	}
   153  
   154  	accessKey, err := getAccessKey(config, env)
   155  	if err != nil {
   156  		return client, err
   157  	}
   158  
   159  	storageClient, err := storage.NewClient(config.StorageAccountName, accessKey, env.StorageEndpointSuffix,
   160  		storage.DefaultAPIVersion, true)
   161  	if err != nil {
   162  		return client, fmt.Errorf("Error creating storage client for storage account %q: %s", config.StorageAccountName, err)
   163  	}
   164  
   165  	client = storageClient.GetBlobService()
   166  	return client, nil
   167  }
   168  
   169  func getAccessKey(config BackendConfig, env azure.Environment) (string, error) {
   170  	if config.AccessKey != "" {
   171  		return config.AccessKey, nil
   172  	}
   173  
   174  	rgOk := config.ResourceGroupName != ""
   175  	subOk := config.SubscriptionID != ""
   176  	clientIDOk := config.ClientID != ""
   177  	clientSecretOK := config.ClientSecret != ""
   178  	tenantIDOk := config.TenantID != ""
   179  	if !rgOk || !subOk || !clientIDOk || !clientSecretOK || !tenantIDOk {
   180  		return "", fmt.Errorf("resource_group_name and credentials must be provided when access_key is absent")
   181  	}
   182  
   183  	oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, config.TenantID)
   184  	if err != nil {
   185  		return "", err
   186  	}
   187  
   188  	spt, err := adal.NewServicePrincipalToken(*oauthConfig, config.ClientID, config.ClientSecret, env.ResourceManagerEndpoint)
   189  	if err != nil {
   190  		return "", err
   191  	}
   192  
   193  	accountsClient := armStorage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, config.SubscriptionID)
   194  	accountsClient.Authorizer = autorest.NewBearerAuthorizer(spt)
   195  
   196  	keys, err := accountsClient.ListKeys(config.ResourceGroupName, config.StorageAccountName)
   197  	if err != nil {
   198  		return "", fmt.Errorf("Error retrieving keys for storage account %q: %s", config.StorageAccountName, err)
   199  	}
   200  
   201  	if keys.Keys == nil {
   202  		return "", fmt.Errorf("Nil key returned for storage account %q", config.StorageAccountName)
   203  	}
   204  
   205  	accessKeys := *keys.Keys
   206  	return *accessKeys[0].Value, nil
   207  }
   208  
   209  func getAzureEnvironment(environment string) (azure.Environment, error) {
   210  	if environment == "" {
   211  		return azure.PublicCloud, nil
   212  	}
   213  
   214  	env, err := azure.EnvironmentFromName(environment)
   215  	if err != nil {
   216  		// try again with wrapped value to support readable values like german instead of AZUREGERMANCLOUD
   217  		var innerErr error
   218  		env, innerErr = azure.EnvironmentFromName(fmt.Sprintf("AZURE%sCLOUD", environment))
   219  		if innerErr != nil {
   220  			return env, fmt.Errorf("invalid 'environment' configuration: %s", err)
   221  		}
   222  	}
   223  
   224  	return env, nil
   225  }