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  }