github.com/kwoods/terraform@v0.6.11-0.20160809170336-13497db7138e/builtin/providers/azurerm/resource_arm_storage_account.go (about) 1 package azurerm 2 3 import ( 4 "fmt" 5 "log" 6 "net/http" 7 "regexp" 8 "strings" 9 "time" 10 11 "github.com/Azure/azure-sdk-for-go/arm/storage" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func resourceArmStorageAccount() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceArmStorageAccountCreate, 19 Read: resourceArmStorageAccountRead, 20 Update: resourceArmStorageAccountUpdate, 21 Delete: resourceArmStorageAccountDelete, 22 Importer: &schema.ResourceImporter{ 23 State: schema.ImportStatePassthrough, 24 }, 25 26 Schema: map[string]*schema.Schema{ 27 "name": { 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 ValidateFunc: validateArmStorageAccountName, 32 }, 33 34 "resource_group_name": { 35 Type: schema.TypeString, 36 Required: true, 37 ForceNew: true, 38 }, 39 40 "location": { 41 Type: schema.TypeString, 42 Required: true, 43 ForceNew: true, 44 StateFunc: azureRMNormalizeLocation, 45 }, 46 47 "account_type": { 48 Type: schema.TypeString, 49 Required: true, 50 ValidateFunc: validateArmStorageAccountType, 51 }, 52 53 "primary_location": { 54 Type: schema.TypeString, 55 Computed: true, 56 }, 57 58 "secondary_location": { 59 Type: schema.TypeString, 60 Computed: true, 61 }, 62 63 "primary_blob_endpoint": { 64 Type: schema.TypeString, 65 Computed: true, 66 }, 67 68 "secondary_blob_endpoint": { 69 Type: schema.TypeString, 70 Computed: true, 71 }, 72 73 "primary_queue_endpoint": { 74 Type: schema.TypeString, 75 Computed: true, 76 }, 77 78 "secondary_queue_endpoint": { 79 Type: schema.TypeString, 80 Computed: true, 81 }, 82 83 "primary_table_endpoint": { 84 Type: schema.TypeString, 85 Computed: true, 86 }, 87 88 "secondary_table_endpoint": { 89 Type: schema.TypeString, 90 Computed: true, 91 }, 92 93 // NOTE: The API does not appear to expose a secondary file endpoint 94 "primary_file_endpoint": { 95 Type: schema.TypeString, 96 Computed: true, 97 }, 98 99 "primary_access_key": { 100 Type: schema.TypeString, 101 Computed: true, 102 }, 103 104 "secondary_access_key": { 105 Type: schema.TypeString, 106 Computed: true, 107 }, 108 109 "tags": tagsSchema(), 110 }, 111 } 112 } 113 114 func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) error { 115 client := meta.(*ArmClient) 116 storageClient := client.storageServiceClient 117 118 resourceGroupName := d.Get("resource_group_name").(string) 119 storageAccountName := d.Get("name").(string) 120 accountType := d.Get("account_type").(string) 121 location := d.Get("location").(string) 122 tags := d.Get("tags").(map[string]interface{}) 123 124 sku := storage.Sku{ 125 Name: storage.SkuName(accountType), 126 } 127 128 opts := storage.AccountCreateParameters{ 129 Location: &location, 130 Sku: &sku, 131 Tags: expandTags(tags), 132 } 133 134 _, err := storageClient.Create(resourceGroupName, storageAccountName, opts, make(chan struct{})) 135 if err != nil { 136 return fmt.Errorf("Error creating Azure Storage Account '%s': %s", storageAccountName, err) 137 } 138 139 // The only way to get the ID back apparently is to read the resource again 140 read, err := storageClient.GetProperties(resourceGroupName, storageAccountName) 141 if err != nil { 142 return err 143 } 144 if read.ID == nil { 145 return fmt.Errorf("Cannot read Storage Account %s (resource group %s) ID", 146 storageAccountName, resourceGroupName) 147 } 148 149 log.Printf("[DEBUG] Waiting for Storage Account (%s) to become available", storageAccountName) 150 stateConf := &resource.StateChangeConf{ 151 Pending: []string{"Updating", "Creating"}, 152 Target: []string{"Succeeded"}, 153 Refresh: storageAccountStateRefreshFunc(client, resourceGroupName, storageAccountName), 154 Timeout: 30 * time.Minute, 155 MinTimeout: 15 * time.Second, 156 } 157 if _, err := stateConf.WaitForState(); err != nil { 158 return fmt.Errorf("Error waiting for Storage Account (%s) to become available: %s", storageAccountName, err) 159 } 160 161 d.SetId(*read.ID) 162 163 return resourceArmStorageAccountRead(d, meta) 164 } 165 166 // resourceArmStorageAccountUpdate is unusual in the ARM API where most resources have a combined 167 // and idempotent operation for CreateOrUpdate. In particular updating all of the parameters 168 // available requires a call to Update per parameter... 169 func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) error { 170 client := meta.(*ArmClient).storageServiceClient 171 id, err := parseAzureResourceID(d.Id()) 172 if err != nil { 173 return err 174 } 175 storageAccountName := id.Path["storageAccounts"] 176 resourceGroupName := id.ResourceGroup 177 178 d.Partial(true) 179 180 if d.HasChange("account_type") { 181 accountType := d.Get("account_type").(string) 182 183 sku := storage.Sku{ 184 Name: storage.SkuName(accountType), 185 } 186 187 opts := storage.AccountUpdateParameters{ 188 Sku: &sku, 189 } 190 _, err := client.Update(resourceGroupName, storageAccountName, opts) 191 if err != nil { 192 return fmt.Errorf("Error updating Azure Storage Account type %q: %s", storageAccountName, err) 193 } 194 195 d.SetPartial("account_type") 196 } 197 198 if d.HasChange("tags") { 199 tags := d.Get("tags").(map[string]interface{}) 200 201 opts := storage.AccountUpdateParameters{ 202 Tags: expandTags(tags), 203 } 204 _, err := client.Update(resourceGroupName, storageAccountName, opts) 205 if err != nil { 206 return fmt.Errorf("Error updating Azure Storage Account tags %q: %s", storageAccountName, err) 207 } 208 209 d.SetPartial("tags") 210 } 211 212 d.Partial(false) 213 return nil 214 } 215 216 func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) error { 217 client := meta.(*ArmClient).storageServiceClient 218 219 id, err := parseAzureResourceID(d.Id()) 220 if err != nil { 221 return err 222 } 223 name := id.Path["storageAccounts"] 224 resGroup := id.ResourceGroup 225 226 resp, err := client.GetProperties(resGroup, name) 227 if err != nil { 228 if resp.StatusCode == http.StatusNotFound { 229 d.SetId("") 230 return nil 231 } 232 233 return fmt.Errorf("Error reading the state of AzureRM Storage Account %q: %s", name, err) 234 } 235 236 keys, err := client.ListKeys(resGroup, name) 237 if err != nil { 238 return err 239 } 240 241 accessKeys := *keys.Keys 242 d.Set("primary_access_key", accessKeys[0].Value) 243 d.Set("secondary_access_key", accessKeys[1].Value) 244 d.Set("location", resp.Location) 245 d.Set("account_type", resp.Sku.Name) 246 d.Set("primary_location", resp.Properties.PrimaryLocation) 247 d.Set("secondary_location", resp.Properties.SecondaryLocation) 248 249 if resp.Properties.PrimaryEndpoints != nil { 250 d.Set("primary_blob_endpoint", resp.Properties.PrimaryEndpoints.Blob) 251 d.Set("primary_queue_endpoint", resp.Properties.PrimaryEndpoints.Queue) 252 d.Set("primary_table_endpoint", resp.Properties.PrimaryEndpoints.Table) 253 d.Set("primary_file_endpoint", resp.Properties.PrimaryEndpoints.File) 254 } 255 256 if resp.Properties.SecondaryEndpoints != nil { 257 if resp.Properties.SecondaryEndpoints.Blob != nil { 258 d.Set("secondary_blob_endpoint", resp.Properties.SecondaryEndpoints.Blob) 259 } else { 260 d.Set("secondary_blob_endpoint", "") 261 } 262 if resp.Properties.SecondaryEndpoints.Queue != nil { 263 d.Set("secondary_queue_endpoint", resp.Properties.SecondaryEndpoints.Queue) 264 } else { 265 d.Set("secondary_queue_endpoint", "") 266 } 267 if resp.Properties.SecondaryEndpoints.Table != nil { 268 d.Set("secondary_table_endpoint", resp.Properties.SecondaryEndpoints.Table) 269 } else { 270 d.Set("secondary_table_endpoint", "") 271 } 272 } 273 274 d.Set("name", resp.Name) 275 276 flattenAndSetTags(d, resp.Tags) 277 278 return nil 279 } 280 281 func resourceArmStorageAccountDelete(d *schema.ResourceData, meta interface{}) error { 282 client := meta.(*ArmClient).storageServiceClient 283 284 id, err := parseAzureResourceID(d.Id()) 285 if err != nil { 286 return err 287 } 288 name := id.Path["storageAccounts"] 289 resGroup := id.ResourceGroup 290 291 _, err = client.Delete(resGroup, name) 292 if err != nil { 293 return fmt.Errorf("Error issuing AzureRM delete request for storage account %q: %s", name, err) 294 } 295 296 return nil 297 } 298 299 func validateArmStorageAccountName(v interface{}, k string) (ws []string, es []error) { 300 input := v.(string) 301 302 if !regexp.MustCompile(`\A([a-z0-9]{3,24})\z`).MatchString(input) { 303 es = append(es, fmt.Errorf("name can only consist of lowercase letters and numbers, and must be between 3 and 24 characters long")) 304 } 305 306 return 307 } 308 309 func validateArmStorageAccountType(v interface{}, k string) (ws []string, es []error) { 310 validAccountTypes := []string{"standard_lrs", "standard_zrs", 311 "standard_grs", "standard_ragrs", "premium_lrs"} 312 313 input := strings.ToLower(v.(string)) 314 315 for _, valid := range validAccountTypes { 316 if valid == input { 317 return 318 } 319 } 320 321 es = append(es, fmt.Errorf("Invalid storage account type %q", input)) 322 return 323 } 324 325 func storageAccountStateRefreshFunc(client *ArmClient, resourceGroupName string, storageAccountName string) resource.StateRefreshFunc { 326 return func() (interface{}, string, error) { 327 res, err := client.storageServiceClient.GetProperties(resourceGroupName, storageAccountName) 328 if err != nil { 329 return nil, "", fmt.Errorf("Error issuing read request in storageAccountStateRefreshFunc to Azure ARM for Storage Account '%s' (RG: '%s'): %s", storageAccountName, resourceGroupName, err) 330 } 331 332 return res, string(res.Properties.ProvisioningState), nil 333 } 334 }