diff --git a/builtin/providers/azurerm/resource_arm_storage_blob.go b/builtin/providers/azurerm/resource_arm_storage_blob.go index 67a3900b0..5fcf0f1c9 100644 --- a/builtin/providers/azurerm/resource_arm_storage_blob.go +++ b/builtin/providers/azurerm/resource_arm_storage_blob.go @@ -46,7 +46,7 @@ func resourceArmStorageBlob() *schema.Resource { }, "type": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, ValidateFunc: validateArmStorageBlobType, }, @@ -58,9 +58,16 @@ func resourceArmStorageBlob() *schema.Resource { ValidateFunc: validateArmStorageBlobSize, }, "source": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"source_uri"}, + }, + "source_uri": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"source"}, }, "url": { Type: schema.TypeString, @@ -144,34 +151,41 @@ func resourceArmStorageBlobCreate(d *schema.ResourceData, meta interface{}) erro name := d.Get("name").(string) blobType := d.Get("type").(string) cont := d.Get("storage_container_name").(string) + sourceUri := d.Get("source_uri").(string) log.Printf("[INFO] Creating blob %q in storage account %q", name, storageAccountName) - switch strings.ToLower(blobType) { - case "block": - if err := blobClient.CreateBlockBlob(cont, name); err != nil { + if sourceUri != "" { + if err := blobClient.CopyBlob(cont, name, sourceUri); err != nil { return fmt.Errorf("Error creating storage blob on Azure: %s", err) } + } else { + switch strings.ToLower(blobType) { + case "block": + if err := blobClient.CreateBlockBlob(cont, name); err != nil { + return fmt.Errorf("Error creating storage blob on Azure: %s", err) + } - source := d.Get("source").(string) - if source != "" { - parallelism := d.Get("parallelism").(int) - attempts := d.Get("attempts").(int) - if err := resourceArmStorageBlobBlockUploadFromSource(cont, name, source, blobClient, parallelism, attempts); err != nil { - return fmt.Errorf("Error creating storage blob on Azure: %s", err) + source := d.Get("source").(string) + if source != "" { + parallelism := d.Get("parallelism").(int) + attempts := d.Get("attempts").(int) + if err := resourceArmStorageBlobBlockUploadFromSource(cont, name, source, blobClient, parallelism, attempts); err != nil { + return fmt.Errorf("Error creating storage blob on Azure: %s", err) + } } - } - case "page": - source := d.Get("source").(string) - if source != "" { - parallelism := d.Get("parallelism").(int) - attempts := d.Get("attempts").(int) - if err := resourceArmStorageBlobPageUploadFromSource(cont, name, source, blobClient, parallelism, attempts); err != nil { - return fmt.Errorf("Error creating storage blob on Azure: %s", err) - } - } else { - size := int64(d.Get("size").(int)) - if err := blobClient.PutPageBlob(cont, name, size, map[string]string{}); err != nil { - return fmt.Errorf("Error creating storage blob on Azure: %s", err) + case "page": + source := d.Get("source").(string) + if source != "" { + parallelism := d.Get("parallelism").(int) + attempts := d.Get("attempts").(int) + if err := resourceArmStorageBlobPageUploadFromSource(cont, name, source, blobClient, parallelism, attempts); err != nil { + return fmt.Errorf("Error creating storage blob on Azure: %s", err) + } + } else { + size := int64(d.Get("size").(int)) + if err := blobClient.PutPageBlob(cont, name, size, map[string]string{}); err != nil { + return fmt.Errorf("Error creating storage blob on Azure: %s", err) + } } } } diff --git a/builtin/providers/azurerm/resource_arm_storage_blob_test.go b/builtin/providers/azurerm/resource_arm_storage_blob_test.go index ecf768fe7..4a5794fb0 100644 --- a/builtin/providers/azurerm/resource_arm_storage_blob_test.go +++ b/builtin/providers/azurerm/resource_arm_storage_blob_test.go @@ -257,6 +257,41 @@ func TestAccAzureRMStorageBlobPage_source(t *testing.T) { }) } +func TestAccAzureRMStorageBlob_source_uri(t *testing.T) { + ri := acctest.RandInt() + rs1 := strings.ToLower(acctest.RandString(11)) + sourceBlob, err := ioutil.TempFile("", "") + if err != nil { + t.Fatalf("Failed to create local source blob file") + } + + _, err = io.CopyN(sourceBlob, rand.Reader, 25*1024*1024) + if err != nil { + t.Fatalf("Failed to write random test to source blob") + } + + err = sourceBlob.Close() + if err != nil { + t.Fatalf("Failed to close source blob") + } + + config := fmt.Sprintf(testAccAzureRMStorageBlob_source_uri, ri, rs1, sourceBlob.Name()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobMatchesFile("azurerm_storage_blob.destination", storage.BlobTypeBlock, sourceBlob.Name()), + ), + }, + }, + }) +} + func testCheckAzureRMStorageBlobExists(name string) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -500,3 +535,51 @@ resource "azurerm_storage_blob" "source" { attempts = 3 } ` + +var testAccAzureRMStorageBlob_source_uri = ` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "westus" +} + +resource "azurerm_storage_account" "source" { + name = "acctestacc%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "westus" + account_type = "Standard_LRS" + + tags { + environment = "staging" + } +} + +resource "azurerm_storage_container" "source" { + name = "source" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.source.name}" + container_access_type = "blob" +} + +resource "azurerm_storage_blob" "source" { + name = "source.vhd" + + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.source.name}" + storage_container_name = "${azurerm_storage_container.source.name}" + + type = "block" + source = "%s" + parallelism = 4 + attempts = 2 +} + +resource "azurerm_storage_blob" "destination" { + name = "destination.vhd" + + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.source.name}" + storage_container_name = "${azurerm_storage_container.source.name}" + + source_uri = "${azurerm_storage_blob.source.url}" +} +` diff --git a/website/source/docs/providers/azurerm/r/storage_blob.html.markdown b/website/source/docs/providers/azurerm/r/storage_blob.html.markdown index 9c8a3e7ea..4cac70e26 100644 --- a/website/source/docs/providers/azurerm/r/storage_blob.html.markdown +++ b/website/source/docs/providers/azurerm/r/storage_blob.html.markdown @@ -58,11 +58,15 @@ The following arguments are supported: * `storage_container_name` - (Required) The name of the storage container in which this blob should be created. -* `type` - (Required) The type of the storage blob to be created. One of either `block` or `page`. +* `type` - (Optional) The type of the storage blob to be created. One of either `block` or `page`. When not copying from an existing blob, + this becomes required. * `size` - (Optional) Used only for `page` blobs to specify the size in bytes of the blob to be created. Must be a multiple of 512. Defaults to 0. - -* `source` - (Optional) An absolute path to a file on the local system + +* `source` - (Optional) An absolute path to a file on the local system. Cannot be defined if `source_uri` is defined. + +* `source_uri` - (Optional) The URI of an existing blob, or a file in the Azure File service, to use as the source contents + for the blob to be created. Changing this forces a new resource to be created. Cannot be defined if `source` is defined. * `parallelism` - (Optional) The number of workers per CPU core to run for concurrent uploads. Defaults to `8`.