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 }