From deb0a4b69c3c54ea8e6f085dbf847937fdd6d265 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Thu, 12 Oct 2017 13:03:33 +0100 Subject: [PATCH] New Resource: `azurerm_snapshot` --- azurerm/config.go | 22 +- azurerm/import_arm_snapshot_test.go | 53 +++ azurerm/provider.go | 1 + azurerm/resource_arm_snapshot.go | 239 +++++++++++ azurerm/resource_arm_snapshot_test.go | 546 ++++++++++++++++++++++++++ website/azurerm.erb | 4 + website/docs/r/snapshot.html.markdown | 55 +++ 7 files changed, 914 insertions(+), 6 deletions(-) create mode 100644 azurerm/import_arm_snapshot_test.go create mode 100644 azurerm/resource_arm_snapshot.go create mode 100644 azurerm/resource_arm_snapshot_test.go create mode 100644 website/docs/r/snapshot.html.markdown diff --git a/azurerm/config.go b/azurerm/config.go index 819fdc6bf54f..a9a5096d0708 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -65,6 +65,7 @@ type ArmClient struct { imageClient compute.ImagesClient diskClient disk.DisksClient + snapshotsClient disk.SnapshotsClient cosmosDBClient cosmosdb.DatabaseAccountsClient automationAccountClient automation.AccountClient automationRunbookClient automation.RunbookClient @@ -354,12 +355,6 @@ func (c *Config) getArmClient() (*ArmClient, error) { cdb.Sender = sender client.cosmosDBClient = cdb - dkc := disk.NewDisksClientWithBaseURI(endpoint, c.SubscriptionID) - setUserAgent(&dkc.Client) - dkc.Authorizer = auth - dkc.Sender = sender - client.diskClient = dkc - img := compute.NewImagesClientWithBaseURI(endpoint, c.SubscriptionID) setUserAgent(&img.Client) img.Authorizer = auth @@ -656,6 +651,7 @@ func (c *Config) getArmClient() (*ArmClient, error) { client.registerAuthentication(endpoint, graphEndpoint, c.SubscriptionID, c.TenantID, auth, graphAuth, sender) client.registerDatabases(endpoint, c.SubscriptionID, auth, sender) + client.registerDisks(endpoint, c.SubscriptionID, auth, sender) client.registerKeyVaultClients(endpoint, c.SubscriptionID, auth, keyVaultAuth, sender) return &client, nil @@ -758,6 +754,20 @@ func (c *ArmClient) registerDatabases(endpoint, subscriptionId string, auth auto c.sqlServersClient = sqlSrvClient } +func (c *ArmClient) registerDisks(endpoint, subscriptionId string, auth autorest.Authorizer, sender autorest.Sender) { + diskClient := disk.NewDisksClientWithBaseURI(endpoint, subscriptionId) + setUserAgent(&diskClient.Client) + diskClient.Authorizer = auth + diskClient.Sender = sender + c.diskClient = diskClient + + snapshotsClient := disk.NewSnapshotsClientWithBaseURI(endpoint, subscriptionId) + setUserAgent(&snapshotsClient.Client) + snapshotsClient.Authorizer = auth + snapshotsClient.Sender = sender + c.snapshotsClient = snapshotsClient +} + func (c *ArmClient) registerKeyVaultClients(endpoint, subscriptionId string, auth autorest.Authorizer, keyVaultAuth autorest.Authorizer, sender autorest.Sender) { keyVaultClient := keyvault.NewVaultsClientWithBaseURI(endpoint, subscriptionId) setUserAgent(&keyVaultClient.Client) diff --git a/azurerm/import_arm_snapshot_test.go b/azurerm/import_arm_snapshot_test.go new file mode 100644 index 000000000000..bec19ce8ec7e --- /dev/null +++ b/azurerm/import_arm_snapshot_test.go @@ -0,0 +1,53 @@ +package azurerm + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAzureRMSnapshot_import(t *testing.T) { + resourceName := "azurerm_snapshot.test" + ri := acctest.RandInt() + config := testAccAzureRMSnapshot_fromManagedDisk(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSnapshotDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMSnapshot_importEncryption(t *testing.T) { + resourceName := "azurerm_snapshot.test" + ri := acctest.RandInt() + rs := acctest.RandString(4) + config := testAccAzureRMSnapshot_encryption(ri, rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSnapshotDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/azurerm/provider.go b/azurerm/provider.go index 1e3ea8f7d252..9829b6ab5f50 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -147,6 +147,7 @@ func Provider() terraform.ResourceProvider { "azurerm_servicebus_queue": resourceArmServiceBusQueue(), "azurerm_servicebus_subscription": resourceArmServiceBusSubscription(), "azurerm_servicebus_topic": resourceArmServiceBusTopic(), + "azurerm_snapshot": resourceArmSnapshot(), "azurerm_sql_database": resourceArmSqlDatabase(), "azurerm_sql_elasticpool": resourceArmSqlElasticPool(), "azurerm_sql_firewall_rule": resourceArmSqlFirewallRule(), diff --git a/azurerm/resource_arm_snapshot.go b/azurerm/resource_arm_snapshot.go new file mode 100644 index 000000000000..66244c0092fe --- /dev/null +++ b/azurerm/resource_arm_snapshot.go @@ -0,0 +1,239 @@ +package azurerm + +import ( + "fmt" + "log" + "regexp" + + "github.com/Azure/azure-sdk-for-go/arm/disk" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmSnapshot() *schema.Resource { + return &schema.Resource{ + Create: resourceArmSnapshotCreateUpdate, + Read: resourceArmSnapshotRead, + Update: resourceArmSnapshotCreateUpdate, + Delete: resourceArmSnapshotDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + // TODO: this wants an associated Data Source + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateSnapshotName, + }, + + "location": locationSchema(), + + "resource_group_name": resourceGroupNameSchema(), + + "create_option": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(disk.Copy), + string(disk.Import), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + + "source_uri": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "source_resource_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "storage_account_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "disk_size_gb": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + + "encryption_settings": encryptionSettingsSchema(), + + "tags": tagsSchema(), + }, + } +} + +func resourceArmSnapshotCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).snapshotsClient + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + location := azureRMNormalizeLocation(d.Get("location").(string)) + createOption := d.Get("create_option").(string) + tags := d.Get("tags").(map[string]interface{}) + + properties := disk.Snapshot{ + Location: utils.String(location), + Properties: &disk.Properties{ + CreationData: &disk.CreationData{ + CreateOption: disk.CreateOption(createOption), + }, + }, + Tags: expandTags(tags), + } + + if v, ok := d.GetOk("source_uri"); ok { + properties.Properties.CreationData.SourceURI = utils.String(v.(string)) + } + + if v, ok := d.GetOk("source_resource_id"); ok { + properties.Properties.CreationData.SourceResourceID = utils.String(v.(string)) + } + + if v, ok := d.GetOk("storage_account_id"); ok { + properties.Properties.CreationData.StorageAccountID = utils.String(v.(string)) + } + + diskSizeGB := d.Get("disk_size_gb").(int) + if diskSizeGB > 0 { + properties.Properties.DiskSizeGB = utils.Int32(int32(diskSizeGB)) + } + + if v, ok := d.GetOk("encryption_settings"); ok { + encryptionSettings := v.([]interface{}) + settings := encryptionSettings[0].(map[string]interface{}) + properties.EncryptionSettings = expandManagedDiskEncryptionSettings(settings) + } + + _, createErr := client.CreateOrUpdate(resourceGroup, name, properties, make(chan struct{})) + err := <-createErr + if err != nil { + return err + } + + resp, err := client.Get(resourceGroup, name) + if err != nil { + return err + } + + d.SetId(*resp.ID) + + return resourceArmSnapshotRead(d, meta) +} + +func resourceArmSnapshotRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).snapshotsClient + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + name := id.Path["snapshots"] + + resp, err := client.Get(resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Error reading Snapshot %q - removing from state", d.Id()) + d.SetId("") + return nil + } + + return fmt.Errorf("Error making Read request on Snapshot %q: %+v", name, err) + } + + d.Set("name", resp.Name) + d.Set("location", azureRMNormalizeLocation(*resp.Location)) + d.Set("resource_group_name", resourceGroup) + + if props := resp.Properties; props != nil { + + if data := props.CreationData; data != nil { + d.Set("create_option", string(data.CreateOption)) + + if data.SourceURI != nil { + d.Set("source_uri", data.SourceURI) + } + + if data.SourceResourceID != nil { + d.Set("source_resource_id", data.SourceResourceID) + } + + if data.StorageAccountID != nil { + d.Set("storage_account_id", *data.StorageAccountID) + } + } + + if props.DiskSizeGB != nil { + d.Set("disk_size_gb", int(*props.DiskSizeGB)) + } + + if props.EncryptionSettings != nil { + d.Set("encryption_settings", flattenManagedDiskEncryptionSettings(props.EncryptionSettings)) + } + } + + flattenAndSetTags(d, resp.Tags) + + return nil +} + +func resourceArmSnapshotDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).snapshotsClient + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + name := id.Path["snapshots"] + + deleteResp, deleteErr := client.Delete(resourceGroup, name, make(chan struct{})) + resp := <-deleteResp + err = <-deleteErr + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return fmt.Errorf("Error making Read request on Snapshot %q: %+v", name, err) + } + + if err != nil { + return fmt.Errorf("Error deleting Snapshot: %+v", err) + } + + return nil +} + +func validateSnapshotName(v interface{}, k string) (ws []string, errors []error) { + // a-z, A-Z, 0-9 and _. The max name length is 80 + value := v.(string) + + r, _ := regexp.Compile("^[A-Za-z0-9_]+$") + if !r.MatchString(value) { + errors = append(errors, fmt.Errorf("Snapshot Names can only contain alphanumeric characters and underscores.")) + } + + length := len(value) + if length > 80 { + errors = append(errors, fmt.Errorf("Snapshot Name can be up to 80 characters, currently %d.", length)) + } + + return +} diff --git a/azurerm/resource_arm_snapshot_test.go b/azurerm/resource_arm_snapshot_test.go new file mode 100644 index 000000000000..c3fdd6ef6dc7 --- /dev/null +++ b/azurerm/resource_arm_snapshot_test.go @@ -0,0 +1,546 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestSnapshotName_validation(t *testing.T) { + str := acctest.RandString(80) + cases := []struct { + Value string + ErrCount int + }{ + { + Value: "ab", + ErrCount: 0, + }, + { + Value: "abc", + ErrCount: 0, + }, + { + Value: "cosmosDBAccount1", + ErrCount: 0, + }, + { + Value: "hello-world", + ErrCount: 1, + }, + { + Value: "hello_world", + ErrCount: 0, + }, + { + Value: str, + ErrCount: 0, + }, + { + Value: str + "a", + ErrCount: 1, + }, + } + + for _, tc := range cases { + _, errors := validateSnapshotName(tc.Value, "azurerm_snapshot") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the Snapshot Name to trigger a validation error for '%s'", tc.Value) + } + } +} + +func TestAccAzureRMSnapshot_fromManagedDisk(t *testing.T) { + resourceName := "azurerm_snapshot.test" + ri := acctest.RandInt() + config := testAccAzureRMSnapshot_fromManagedDisk(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSnapshotDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSnapshotExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMSnapshot_encryption(t *testing.T) { + resourceName := "azurerm_snapshot.test" + ri := acctest.RandInt() + rs := acctest.RandString(4) + config := testAccAzureRMSnapshot_encryption(ri, rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSnapshotDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSnapshotExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMSnapshot_update(t *testing.T) { + resourceName := "azurerm_snapshot.test" + ri := acctest.RandInt() + config := testAccAzureRMSnapshot_fromManagedDisk(ri, testLocation()) + updatedConfig := testAccAzureRMSnapshot_fromManagedDiskUpdated(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSnapshotDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSnapshotExists(resourceName), + ), + }, + { + Config: updatedConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSnapshotExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMSnapshot_extendingManagedDisk(t *testing.T) { + resourceName := "azurerm_snapshot.test" + ri := acctest.RandInt() + config := testAccAzureRMSnapshot_extendingManagedDisk(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSnapshotDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSnapshotExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMSnapshot_fromExistingSnapshot(t *testing.T) { + resourceName := "azurerm_snapshot.second" + ri := acctest.RandInt() + config := testAccAzureRMSnapshot_fromExistingSnapshot(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSnapshotDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSnapshotExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMSnapshot_fromUnmanagedDisk(t *testing.T) { + resourceName := "azurerm_snapshot.test" + ri := acctest.RandInt() + rs := acctest.RandString(4) + config := testAccAzureRMSnapshot_fromUnmanagedDisk(ri, rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSnapshotDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSnapshotExists(resourceName), + ), + }, + }, + }) +} + +func testCheckAzureRMSnapshotDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_snapshot" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + client := testAccProvider.Meta().(*ArmClient).snapshotsClient + resp, err := client.Get(resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + return err + } + + return fmt.Errorf("Snapshot still exists:\n%+v", resp) + } + + return nil +} + +func testCheckAzureRMSnapshotExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + name := rs.Primary.Attributes["name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Snapshot: %q", name) + } + + client := testAccProvider.Meta().(*ArmClient).snapshotsClient + + resp, err := client.Get(resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Topic %q (resource group: %q) does not exist", name, resourceGroup) + } + + return fmt.Errorf("Bad: Get on snapshotsClient: %+v", err) + } + + return nil + } +} + +func testAccAzureRMSnapshot_fromManagedDisk(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_managed_disk" "test" { + name = "acctestmd-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "10" +} + +resource "azurerm_snapshot" "test" { + name = "acctestss_%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + create_option = "Copy" + source_uri = "${azurerm_managed_disk.test.id}" +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMSnapshot_fromManagedDiskUpdated(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_managed_disk" "test" { + name = "acctestmd-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "10" +} + +resource "azurerm_snapshot" "test" { + name = "acctestss_%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + create_option = "Copy" + source_uri = "${azurerm_managed_disk.test.id}" + + tags { + "Hello" = "World" + } +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMSnapshot_encryption(rInt int, rString string, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_managed_disk" "test" { + name = "acctestmd-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "10" +} + +resource "azurerm_key_vault" "test" { + name = "acctestkv%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + + sku { + name = "premium" + } + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.service_principal_object_id}" + + key_permissions = [ + "all", + ] + + secret_permissions = [ + "all", + ] + } + + enabled_for_disk_encryption = true +} + +resource "azurerm_key_vault_key" "test" { + name = "generated-certificate" + vault_uri = "${azurerm_key_vault.test.vault_uri}" + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_key_vault_secret" "test" { + name = "secret-sauce" + value = "szechuan" + vault_uri = "${azurerm_key_vault.test.vault_uri}" +} + +resource "azurerm_snapshot" "test" { + name = "acctestss_%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + create_option = "Copy" + source_uri = "${azurerm_managed_disk.test.id}" + disk_size_gb = "20" + + encryption_settings { + enabled = true + + disk_encryption_key { + secret_url = "${azurerm_key_vault_secret.test.id}" + source_vault_id = "${azurerm_key_vault.test.id}" + } + + key_encryption_key { + key_url = "${azurerm_key_vault_key.test.id}" + source_vault_id = "${azurerm_key_vault.test.id}" + } + } +} +`, rInt, location, rInt, rString, rInt) +} + +func testAccAzureRMSnapshot_extendingManagedDisk(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_managed_disk" "test" { + name = "acctestmd-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "10" +} + +resource "azurerm_snapshot" "test" { + name = "acctestss_%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + create_option = "Copy" + source_uri = "${azurerm_managed_disk.test.id}" + disk_size_gb = "20" +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMSnapshot_fromExistingSnapshot(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_managed_disk" "original" { + name = "acctestmd-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "10" +} + +resource "azurerm_snapshot" "first" { + name = "acctestss1_%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + create_option = "Copy" + source_uri = "${azurerm_managed_disk.original.id}" +} + +resource "azurerm_snapshot" "second" { + name = "acctestss2_%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + create_option = "Copy" + source_resource_id = "${azurerm_snapshot.first.id}" +} +`, rInt, location, rInt, rInt, rInt) +} + +func testAccAzureRMSnapshot_fromUnmanagedDisk(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvn-%d" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "acctestnic-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags { + environment = "staging" + } +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_virtual_machine" "test" { + name = "acctestvm-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.test.id}"] + vm_size = "Standard_A0" + delete_os_disk_on_termination = true + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "myosdisk1" + vhd_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" + caching = "ReadWrite" + create_option = "FromImage" + } + + os_profile { + computer_name = "acctestvm-%d" + admin_username = "testadmin" + admin_password = "Password1234!" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "staging" + } +} + +resource "azurerm_snapshot" "test" { + name = "acctestss_%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + create_option = "Import" + source_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" + depends_on = ["azurerm_virtual_machine.test"] +} +`, rInt, location, rInt, rInt, rString, rInt, rInt, rInt) +} diff --git a/website/azurerm.erb b/website/azurerm.erb index e15e4794cc4a..e5c4c90b2909 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -162,6 +162,10 @@ azurerm_managed_disk + > + azurerm_snapshot + + > azurerm_image diff --git a/website/docs/r/snapshot.html.markdown b/website/docs/r/snapshot.html.markdown new file mode 100644 index 000000000000..b1b2a1614cec --- /dev/null +++ b/website/docs/r/snapshot.html.markdown @@ -0,0 +1,55 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_snapshot" +sidebar_current: "docs-azurerm-resource-compute-snapshot" +description: |- + Manages a Disk Snapshot. + +--- + +# azurerm_snapshot + +Manages a Disk Snapshot. + +## Example Usage + +```hcl + +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the Snapshot resource. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the resource group in which to create the Snapshot. Changing this forces a new resource to be created. + +* `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. + +* `create_option` - (Required) Indicates how the snapshot is to be created. Possible values are `Copy` or `Import`. Changing this forces a new resource to be created. + +~> **Note:** One of `source_uri`, `source_resource_id` or `storage_account_id` must be specified. + +* `source_uri` - (Optional) Specifies the URI to a Managed or Unmanaged Disk. Changing this forces a new resource to be created. + +* `source_resource_id` - (Optional) Specifies a reference to an existing snapshot, when `create_option` is `Copy`. Changing this forces a new resource to be created. + +* `storage_account_id` - (Optional) Specifies the ID of an storage account. Used with `source_uri` to allow authorization during import of unmanaged blobs from a different subscription. Changing this forces a new resource to be created. + +* `disk_size_gb` - (Optional) + +## Attributes Reference + +The following attributes are exported: + +* `id` - The Snapshot ID. +* `disk_size_gb` - The Size of the Snapshotted Disk in GB + +## Import + +Snapshots can be imported using the `resource id`, e.g. + +``` +terraform import azurerm_snapshot.test /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Compute/snapshots/snapshot1 +```