github.com/pgray/terraform@v0.5.4-0.20170822184730-b6a464c5214d/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": &schema.Schema{ 21 Type: schema.TypeString, 22 Required: true, 23 Description: "The name of the storage account.", 24 }, 25 26 "container_name": &schema.Schema{ 27 Type: schema.TypeString, 28 Required: true, 29 Description: "The container name.", 30 }, 31 32 "key": &schema.Schema{ 33 Type: schema.TypeString, 34 Required: true, 35 Description: "The blob key.", 36 }, 37 38 "environment": &schema.Schema{ 39 Type: schema.TypeString, 40 Optional: true, 41 Description: "The Azure cloud environment.", 42 Default: "", 43 }, 44 45 "access_key": &schema.Schema{ 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": &schema.Schema{ 53 Type: schema.TypeString, 54 Optional: true, 55 Description: "The resource group name.", 56 }, 57 58 "arm_subscription_id": &schema.Schema{ 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": &schema.Schema{ 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": &schema.Schema{ 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": &schema.Schema{ 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 func (b *Backend) configure(ctx context.Context) error { 105 if b.containerName != "" { 106 return nil 107 } 108 109 // Grab the resource data 110 data := schema.FromContextBackendConfig(ctx) 111 112 b.containerName = data.Get("container_name").(string) 113 b.keyName = data.Get("key").(string) 114 115 blobClient, err := getBlobClient(data) 116 if err != nil { 117 return err 118 } 119 b.blobClient = blobClient 120 121 return nil 122 } 123 124 func getBlobClient(d *schema.ResourceData) (storage.BlobStorageClient, error) { 125 var client storage.BlobStorageClient 126 127 env, err := getAzureEnvironment(d.Get("environment").(string)) 128 if err != nil { 129 return client, err 130 } 131 132 storageAccountName := d.Get("storage_account_name").(string) 133 134 accessKey, err := getAccessKey(d, storageAccountName, env) 135 if err != nil { 136 return client, err 137 } 138 139 storageClient, err := storage.NewClient(storageAccountName, accessKey, env.StorageEndpointSuffix, 140 storage.DefaultAPIVersion, true) 141 if err != nil { 142 return client, fmt.Errorf("Error creating storage client for storage account %q: %s", storageAccountName, err) 143 } 144 145 client = storageClient.GetBlobService() 146 return client, nil 147 } 148 149 func getAccessKey(d *schema.ResourceData, storageAccountName string, env azure.Environment) (string, error) { 150 if key, ok := d.GetOk("access_key"); ok { 151 return key.(string), nil 152 } 153 154 resourceGroupName, rgOk := d.GetOk("resource_group_name") 155 subscriptionID, subOk := d.GetOk("arm_subscription_id") 156 clientID, clientIDOk := d.GetOk("arm_client_id") 157 clientSecret, clientSecretOK := d.GetOk("arm_client_secret") 158 tenantID, tenantIDOk := d.GetOk("arm_tenant_id") 159 if !rgOk || !subOk || !clientIDOk || !clientSecretOK || !tenantIDOk { 160 return "", fmt.Errorf("resource_group_name and credentials must be provided when access_key is absent") 161 } 162 163 oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID.(string)) 164 if err != nil { 165 return "", err 166 } 167 168 spt, err := adal.NewServicePrincipalToken(*oauthConfig, clientID.(string), clientSecret.(string), env.ResourceManagerEndpoint) 169 if err != nil { 170 return "", err 171 } 172 173 accountsClient := armStorage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID.(string)) 174 accountsClient.Authorizer = autorest.NewBearerAuthorizer(spt) 175 176 keys, err := accountsClient.ListKeys(resourceGroupName.(string), storageAccountName) 177 if err != nil { 178 return "", fmt.Errorf("Error retrieving keys for storage account %q: %s", storageAccountName, err) 179 } 180 181 if keys.Keys == nil { 182 return "", fmt.Errorf("Nil key returned for storage account %q", storageAccountName) 183 } 184 185 accessKeys := *keys.Keys 186 return *accessKeys[0].Value, nil 187 } 188 189 func getAzureEnvironment(environment string) (azure.Environment, error) { 190 if environment == "" { 191 return azure.PublicCloud, nil 192 } 193 194 env, err := azure.EnvironmentFromName(environment) 195 if err != nil { 196 // try again with wrapped value to support readable values like german instead of AZUREGERMANCLOUD 197 var innerErr error 198 env, innerErr = azure.EnvironmentFromName(fmt.Sprintf("AZURE%sCLOUD", environment)) 199 if innerErr != nil { 200 return env, fmt.Errorf("invalid 'environment' configuration: %s", err) 201 } 202 } 203 204 return env, nil 205 }