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 }