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 }