diff --git a/azurerm/internal/services/netapp/client/client.go b/azurerm/internal/services/netapp/client/client.go index f2141ff1d023..cc0127c118bb 100644 --- a/azurerm/internal/services/netapp/client/client.go +++ b/azurerm/internal/services/netapp/client/client.go @@ -6,9 +6,10 @@ import ( ) type Client struct { - AccountClient *netapp.AccountsClient - PoolClient *netapp.PoolsClient - VolumeClient *netapp.VolumesClient + AccountClient *netapp.AccountsClient + PoolClient *netapp.PoolsClient + VolumeClient *netapp.VolumesClient + SnapshotClient *netapp.SnapshotsClient } func NewClient(o *common.ClientOptions) *Client { @@ -21,9 +22,13 @@ func NewClient(o *common.ClientOptions) *Client { volumeClient := netapp.NewVolumesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&volumeClient.Client, o.ResourceManagerAuthorizer) + snapshotClient := netapp.NewSnapshotsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&snapshotClient.Client, o.ResourceManagerAuthorizer) + return &Client{ - AccountClient: &accountClient, - PoolClient: &poolClient, - VolumeClient: &volumeClient, + AccountClient: &accountClient, + PoolClient: &poolClient, + VolumeClient: &volumeClient, + SnapshotClient: &snapshotClient, } } diff --git a/azurerm/internal/services/netapp/data_source_netapp_snapshot.go b/azurerm/internal/services/netapp/data_source_netapp_snapshot.go new file mode 100644 index 000000000000..42e8e258572f --- /dev/null +++ b/azurerm/internal/services/netapp/data_source_netapp_snapshot.go @@ -0,0 +1,89 @@ +package netapp + +import ( + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func dataSourceArmNetAppSnapshot() *schema.Resource { + return &schema.Resource{ + Read: dataSourceArmNetAppSnapshotRead, + + Timeouts: &schema.ResourceTimeout{ + Read: schema.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: ValidateNetAppSnapshotName, + }, + + "location": azure.SchemaLocationForDataSource(), + + "resource_group_name": azure.SchemaResourceGroupNameForDataSource(), + + "account_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: ValidateNetAppAccountName, + }, + + "pool_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: ValidateNetAppPoolName, + }, + + "volume_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: ValidateNetAppVolumeName, + }, + }, + } +} + +func dataSourceArmNetAppSnapshotRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).NetApp.SnapshotClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + name := d.Get("name").(string) + accountName := d.Get("account_name").(string) + poolName := d.Get("pool_name").(string) + volumeName := d.Get("volume_name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + resp, err := client.Get(ctx, resourceGroup, accountName, poolName, volumeName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error: NetApp Snapshot %q (Resource Group %q) was not found", name, resourceGroup) + } + return fmt.Errorf("Error reading NetApp Snapshot %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if resp.ID == nil || *resp.ID == "" { + return fmt.Errorf("Error retrieving NetApp Snapshot %q (Resource Group %q): ID was nil or empty", name, resourceGroup) + } + + d.SetId(*resp.ID) + + d.Set("name", name) + d.Set("resource_group_name", resourceGroup) + d.Set("account_name", accountName) + d.Set("pool_name", poolName) + d.Set("volume_name", volumeName) + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + return nil +} diff --git a/azurerm/internal/services/netapp/registration.go b/azurerm/internal/services/netapp/registration.go index e432adbd1453..c1bb265794bb 100644 --- a/azurerm/internal/services/netapp/registration.go +++ b/azurerm/internal/services/netapp/registration.go @@ -14,15 +14,17 @@ func (r Registration) Name() string { // SupportedDataSources returns the supported Data Sources supported by this Service func (r Registration) SupportedDataSources() map[string]*schema.Resource { return map[string]*schema.Resource{ - "azurerm_netapp_account": dataSourceArmNetAppAccount(), - "azurerm_netapp_pool": dataSourceArmNetAppPool(), - "azurerm_netapp_volume": dataSourceArmNetAppVolume()} + "azurerm_netapp_account": dataSourceArmNetAppAccount(), + "azurerm_netapp_pool": dataSourceArmNetAppPool(), + "azurerm_netapp_volume": dataSourceArmNetAppVolume(), + "azurerm_netapp_snapshot": dataSourceArmNetAppSnapshot()} } // SupportedResources returns the supported Resources supported by this Service func (r Registration) SupportedResources() map[string]*schema.Resource { return map[string]*schema.Resource{ - "azurerm_netapp_account": resourceArmNetAppAccount(), - "azurerm_netapp_pool": resourceArmNetAppPool(), - "azurerm_netapp_volume": resourceArmNetAppVolume()} + "azurerm_netapp_account": resourceArmNetAppAccount(), + "azurerm_netapp_pool": resourceArmNetAppPool(), + "azurerm_netapp_volume": resourceArmNetAppVolume(), + "azurerm_netapp_snapshot": resourceArmNetAppSnapshot()} } diff --git a/azurerm/internal/services/netapp/resource_arm_netapp_snapshot.go b/azurerm/internal/services/netapp/resource_arm_netapp_snapshot.go new file mode 100644 index 000000000000..a8599b9e1e9a --- /dev/null +++ b/azurerm/internal/services/netapp/resource_arm_netapp_snapshot.go @@ -0,0 +1,189 @@ +package netapp + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/netapp/mgmt/2019-06-01/netapp" + "github.com/hashicorp/go-azure-helpers/response" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmNetAppSnapshot() *schema.Resource { + return &schema.Resource{ + Create: resourceArmNetAppSnapshotCreate, + Read: resourceArmNetAppSnapshotRead, + Update: nil, + Delete: resourceArmNetAppSnapshotDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: ValidateNetAppSnapshotName, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "location": azure.SchemaLocation(), + + "account_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: ValidateNetAppAccountName, + }, + + "pool_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: ValidateNetAppPoolName, + }, + + "volume_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: ValidateNetAppVolumeName, + }, + }, + } +} + +func resourceArmNetAppSnapshotCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).NetApp.SnapshotClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + accountName := d.Get("account_name").(string) + poolName := d.Get("pool_name").(string) + volumeName := d.Get("volume_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + resp, err := client.Get(ctx, resourceGroup, accountName, poolName, volumeName, name) + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error checking for present of existing NetApp Snapshot %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + if !utils.ResponseWasNotFound(resp.Response) { + return tf.ImportAsExistsError("azurerm_netapp_snapshot", *resp.ID) + } + } + + location := azure.NormalizeLocation(d.Get("location").(string)) + + parameters := netapp.Snapshot{ + Location: utils.String(location), + } + + future, err := client.Create(ctx, parameters, resourceGroup, accountName, poolName, volumeName, name) + if err != nil { + return fmt.Errorf("Error creating NetApp Snapshot %q (Resource Group %q): %+v", name, resourceGroup, err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for creation of NetApp Snapshot %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + resp, err := client.Get(ctx, resourceGroup, accountName, poolName, volumeName, name) + if err != nil { + return fmt.Errorf("Error retrieving NetApp Snapshot %q (Resource Group %q): %+v", name, resourceGroup, err) + } + if resp.ID == nil || *resp.ID == "" { + return fmt.Errorf("Cannot read NetApp Snapshot %q (Resource Group %q) ID", name, resourceGroup) + } + d.SetId(*resp.ID) + + return resourceArmNetAppSnapshotRead(d, meta) +} + +func resourceArmNetAppSnapshotRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).NetApp.SnapshotClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + accountName := id.Path["netAppAccounts"] + poolName := id.Path["capacityPools"] + volumeName := id.Path["volumes"] + name := id.Path["snapshots"] + + resp, err := client.Get(ctx, resourceGroup, accountName, poolName, volumeName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] NetApp Snapshots %q does not exist - removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("Error reading NetApp Snapshots %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resourceGroup) + d.Set("account_name", accountName) + d.Set("pool_name", poolName) + d.Set("volume_name", volumeName) + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + return nil +} + +func resourceArmNetAppSnapshotDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).NetApp.SnapshotClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + accountName := id.Path["netAppAccounts"] + poolName := id.Path["capacityPools"] + volumeName := id.Path["volumes"] + name := id.Path["snapshots"] + + future, err := client.Delete(ctx, resourceGroup, accountName, poolName, volumeName, name) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("Error deleting NetApp Snapshot %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("Error waiting for deleting NetApp Snapshot %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + + return nil +} diff --git a/azurerm/internal/services/netapp/tests/data_source_netapp_snapshot_test.go b/azurerm/internal/services/netapp/tests/data_source_netapp_snapshot_test.go new file mode 100644 index 000000000000..272ff9cd055f --- /dev/null +++ b/azurerm/internal/services/netapp/tests/data_source_netapp_snapshot_test.go @@ -0,0 +1,41 @@ +package tests + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" +) + +func TestAccDataSourceAzureRMNetAppSnapshot_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_netapp_snapshot", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceNetAppSnapshot_basic(data), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(data.ResourceName, "name"), + ), + }, + }, + }) +} + +func testAccDataSourceNetAppSnapshot_basic(data acceptance.TestData) string { + config := testAccAzureRMNetAppSnapshot_basic(data) + return fmt.Sprintf(` +%s + +data "azurerm_netapp_snapshot" "test" { + resource_group_name = "${azurerm_netapp_snapshot.test.resource_group_name}" + account_name = "${azurerm_netapp_snapshot.test.account_name}" + pool_name = "${azurerm_netapp_snapshot.test.pool_name}" + volume_name = "${azurerm_netapp_snapshot.test.volume_name}" + name = "${azurerm_netapp_snapshot.test.name}" +} +`, config) +} diff --git a/azurerm/internal/services/netapp/tests/resource_arm_netapp_snapshot_test.go b/azurerm/internal/services/netapp/tests/resource_arm_netapp_snapshot_test.go new file mode 100644 index 000000000000..ea0ba259496a --- /dev/null +++ b/azurerm/internal/services/netapp/tests/resource_arm_netapp_snapshot_test.go @@ -0,0 +1,314 @@ +package tests + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMNetAppSnapshot_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_snapshot", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMNetAppSnapshotDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetAppSnapshot_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetAppSnapshotExists(data.ResourceName), + ), + }, + data.ImportStep(), + }, + }) +} + +func TestAccAzureRMNetAppSnapshot_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + data := acceptance.BuildTestData(t, "azurerm_netapp_snapshot", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMNetAppSnapshotDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetAppSnapshot_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetAppSnapshotExists(data.ResourceName), + ), + }, + { + Config: testAccAzureRMNetAppSnapshot_requiresImport(data), + ExpectError: acceptance.RequiresImportError("azurerm_netapp_snapshot"), + }, + }, + }) +} + +func TestAccAzureRMNetAppSnapshot_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_snapshot", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMNetAppSnapshotDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetAppSnapshot_complete(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetAppSnapshotExists(data.ResourceName), + ), + }, + data.ImportStep(), + }, + }) +} + +func TestAccAzureRMNetAppSnapshot_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_snapshot", "test") + oldVolumeName := fmt.Sprintf("acctest-NetAppVolume-%d", data.RandomInteger) + newVolumeName := fmt.Sprintf("acctest-updated-NetAppVolume-%d", data.RandomInteger) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMNetAppSnapshotDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetAppSnapshot_complete(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetAppSnapshotExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "volume_name", oldVolumeName), + ), + }, + data.ImportStep(), + { + Config: testAccAzureRMNetAppSnapshot_update(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetAppSnapshotExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "volume_name", newVolumeName), + ), + }, + data.ImportStep(), + }, + }) +} + +func testCheckAzureRMNetAppSnapshotExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("NetApp Snapshot not found: %s", resourceName) + } + + name := rs.Primary.Attributes["name"] + accountName := rs.Primary.Attributes["account_name"] + poolName := rs.Primary.Attributes["pool_name"] + volumeName := rs.Primary.Attributes["volume_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + client := acceptance.AzureProvider.Meta().(*clients.Client).NetApp.SnapshotClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + if resp, err := client.Get(ctx, resourceGroup, accountName, poolName, volumeName, name); err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: NetApp Snapshot %q (Resource Group %q) does not exist", name, resourceGroup) + } + return fmt.Errorf("Bad: Get on netapp.SnapshotClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMNetAppSnapshotDestroy(s *terraform.State) error { + client := acceptance.AzureProvider.Meta().(*clients.Client).NetApp.SnapshotClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_netapp_snapshot" { + continue + } + + name := rs.Primary.Attributes["name"] + accountName := rs.Primary.Attributes["account_name"] + poolName := rs.Primary.Attributes["pool_name"] + volumeName := rs.Primary.Attributes["volume_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + if resp, err := client.Get(ctx, resourceGroup, accountName, poolName, volumeName, name); err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Get on netapp.SnapshotClient: %+v", err) + } + } + + return nil + } + + return nil +} + +func testAccAzureRMNetAppSnapshot_basic(data acceptance.TestData) string { + template := testAccAzureRMNetAppSnapshot_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_netapp_snapshot" "test" { + name = "acctest-NetAppSnapshot-%d" + account_name = "${azurerm_netapp_account.test.name}" + pool_name = "${azurerm_netapp_pool.test.name}" + volume_name = "${azurerm_netapp_volume.test.name}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, template, data.RandomInteger) +} + +func testAccAzureRMNetAppSnapshot_requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_netapp_snapshot" "import" { + name = "${azurerm_netapp_snapshot.test.name}" + location = "${azurerm_netapp_snapshot.test.location}" + resource_group_name = "${azurerm_netapp_snapshot.test.name}" +} +`, testAccAzureRMNetAppSnapshot_basic(data)) +} + +func testAccAzureRMNetAppSnapshot_complete(data acceptance.TestData) string { + template := testAccAzureRMNetAppSnapshot_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_netapp_snapshot" "test" { + name = "acctest-NetAppSnapshot-%d" + account_name = "${azurerm_netapp_account.test.name}" + pool_name = "${azurerm_netapp_pool.test.name}" + volume_name = "${azurerm_netapp_volume.test.name}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, template, data.RandomInteger) +} + +func testAccAzureRMNetAppSnapshot_update(data acceptance.TestData) string { + template := testAccAzureRMNetAppSnapshot_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_virtual_network" "update" { + name = "acctest-updated-VirtualNetwork-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + address_space = ["10.0.0.0/16"] +} + +resource "azurerm_subnet" "update" { + name = "acctest-updated-Subnet-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.update.name}" + address_prefix = "10.0.2.0/24" + delegation { + name = "netapp" + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +resource "azurerm_netapp_volume" "update" { + name = "acctest-updated-NetAppVolume-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + account_name = "${azurerm_netapp_account.test.name}" + pool_name = "${azurerm_netapp_pool.test.name}" + volume_path = "my-updated-unique-file-path-%d" + service_level = "Premium" + subnet_id = "${azurerm_subnet.update.id}" + storage_quota_in_gb = 100 +} + +resource "azurerm_netapp_snapshot" "test" { + name = "acctest-NetAppSnapshot-%d" + account_name = "${azurerm_netapp_account.test.name}" + pool_name = "${azurerm_netapp_pool.test.name}" + volume_name = "${azurerm_netapp_volume.update.name}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, template, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) +} + +func testAccAzureRMNetAppSnapshot_template(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-netapp-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctest-VirtualNetwork-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + address_space = ["10.0.0.0/16"] +} + +resource "azurerm_subnet" "test" { + name = "acctest-Subnet-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" + + delegation { + name = "netapp" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +resource "azurerm_netapp_account" "test" { + name = "acctest-NetAppAccount-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_netapp_pool" "test" { + name = "acctest-NetAppPool-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + account_name = "${azurerm_netapp_account.test.name}" + service_level = "Premium" + size_in_tb = 4 +} + +resource "azurerm_netapp_volume" "test" { + name = "acctest-NetAppVolume-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + account_name = "${azurerm_netapp_account.test.name}" + pool_name = "${azurerm_netapp_pool.test.name}" + volume_path = "my-unique-file-path-%d" + service_level = "Premium" + subnet_id = "${azurerm_subnet.test.id}" + storage_quota_in_gb = 100 +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) +} diff --git a/azurerm/internal/services/netapp/validation.go b/azurerm/internal/services/netapp/validation.go index 24c7f3d78167..0f845c9165d0 100644 --- a/azurerm/internal/services/netapp/validation.go +++ b/azurerm/internal/services/netapp/validation.go @@ -44,3 +44,13 @@ func ValidateNetAppVolumeVolumePath(v interface{}, k string) (warnings []string, return warnings, errors } + +func ValidateNetAppSnapshotName(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + if !regexp.MustCompile(`^[\da-zA-Z][-_\da-zA-Z]{3,63}$`).MatchString(value) { + errors = append(errors, fmt.Errorf("%q must be between 4 and 64 characters in length and start with letters or numbers and contains only letters, numbers, underscore or hyphens.", k)) + } + + return warnings, errors +} diff --git a/azurerm/internal/services/netapp/validation_test.go b/azurerm/internal/services/netapp/validation_test.go index 7b72f66062f6..4359a04a159b 100644 --- a/azurerm/internal/services/netapp/validation_test.go +++ b/azurerm/internal/services/netapp/validation_test.go @@ -137,3 +137,71 @@ func TestValidateNetAppVolumeVolumePath(t *testing.T) { } } } + +func TestValidateNetAppSnapshotName(t *testing.T) { + testData := []struct { + input string + expected bool + }{ + { + // empty + input: "", + expected: false, + }, + { + // basic example + input: "hello", + expected: true, + }, + { + // can't start with an underscore + input: "_hello", + expected: false, + }, + { + // can't end with a dash + input: "hello-", + expected: true, + }, + { + // can't contain an exclamation mark + input: "hello!", + expected: false, + }, + { + // dash in the middle + input: "malcolm-in-the-middle", + expected: true, + }, + { + // can't end with a period + input: "hello.", + expected: false, + }, + { + // 63 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk", + expected: true, + }, + { + // 64 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkj", + expected: true, + }, + { + // 65 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkja", + expected: false, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + + _, errors := ValidateNetAppSnapshotName(v.input, "name") + actual := len(errors) == 0 + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} diff --git a/examples/netapp/snapshot/main.tf b/examples/netapp/snapshot/main.tf new file mode 100644 index 000000000000..ba881d4a697c --- /dev/null +++ b/examples/netapp/snapshot/main.tf @@ -0,0 +1,63 @@ +resource "azurerm_resource_group" "example" { + name = "${var.prefix}-resources" + location = "${var.location}" +} + +resource "azurerm_virtual_network" "example" { + name = "${var.prefix}-virtualnetwork" + location = "${azurerm_resource_group.example.location}" + resource_group_name = "${azurerm_resource_group.example.name}" + address_space = ["10.0.0.0/16"] +} + +resource "azurerm_subnet" "example" { + name = "${var.prefix}-subnet" + resource_group_name = "${azurerm_resource_group.example.name}" + virtual_network_name = "${azurerm_virtual_network.example.name}" + address_prefix = "10.0.2.0/24" + + delegation { + name = "netapp" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +resource "azurerm_netapp_account" "example" { + name = "${var.prefix}-netappaccount" + location = "${azurerm_resource_group.example.location}" + resource_group_name = "${azurerm_resource_group.example.name}" +} + +resource "azurerm_netapp_pool" "example" { + name = "${var.prefix}-netapppool" + location = "${azurerm_resource_group.example.location}" + resource_group_name = "${azurerm_resource_group.example.name}" + account_name = "${azurerm_netapp_account.example.name}" + service_level = "Premium" + size_in_tb = 4 +} + +resource "azurerm_netapp_volume" "example" { + name = "${var.prefix}-netappvolume" + location = "${azurerm_resource_group.example.location}" + resource_group_name = "${azurerm_resource_group.example.name}" + account_name = "${azurerm_netapp_account.example.name}" + pool_name = "${azurerm_netapp_pool.example.name}" + volume_path = "my-unique-file-path" + service_level = "Premium" + subnet_id = "${azurerm_subnet.example.id}" + storage_quota_in_gb = 100 +} + +resource "azurerm_netapp_snapshot" "example" { + name = "${var.prefix}-netappsnapshot" + account_name = "${azurerm_netapp_account.example.name}" + pool_name = "${azurerm_netapp_pool.example.name}" + volume_name = "${azurerm_netapp_volume.example.name}" + location = "${azurerm_resource_group.example.location}" + resource_group_name = "${azurerm_resource_group.example.name}" +} diff --git a/examples/netapp/snapshot/variables.tf b/examples/netapp/snapshot/variables.tf new file mode 100644 index 000000000000..f6ace0ff7d0a --- /dev/null +++ b/examples/netapp/snapshot/variables.tf @@ -0,0 +1,7 @@ +variable "location" { + description = "The Azure location where all resources in this example should be created." +} + +variable "prefix" { + description = "The prefix used for all resources used by this NetApp Snapshot" +} diff --git a/website/azurerm.erb b/website/azurerm.erb index 42c01b5ff468..ad6b83f0c2e8 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -310,6 +310,10 @@ azurerm_netapp_volume +
  • + azurerm_netapp_snapshot +
  • +
  • azurerm_platform_image
  • @@ -1804,6 +1808,10 @@
  • azurerm_netapp_volume
  • + +
  • + azurerm_netapp_snapshot +
  • diff --git a/website/docs/d/netapp_snapshot.html.markdown b/website/docs/d/netapp_snapshot.html.markdown new file mode 100644 index 000000000000..6ccb7984e9de --- /dev/null +++ b/website/docs/d/netapp_snapshot.html.markdown @@ -0,0 +1,48 @@ +--- +subcategory: "NetApp" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_netapp_snapshot" +sidebar_current: "docs-azurerm-datasource-netapp-snapshot" +description: |- +Gets information about an existing NetApp Snapshot +--- + +# Data Source: azurerm_netapp_snapshot + +Uses this data source to access information about an existing NetApp Snapshot. + +## NetApp Snapshot Usage + +```hcl +data "azurerm_netapp_snapshot" "test" { + resource_group_name = "acctestRG" + name = "acctestnetappsnapshot" + account_name = "acctestnetappaccount" + pool_name = "acctestnetapppool" + volume_name = "acctestnetappvolume" +} + +output "netapp_snapshot_id" { + value = "${data.azurerm_netapp_snapshot.example.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the NetApp Snapshot. + +* `account_name` - (Required) The name of the NetApp Account where the NetApp Pool exists. + +* `pool_name` - (Required) The name of the NetApp Pool where the NetApp Volume exists. + +* `volume_name` - (Required) The name of the NetApp Volume where the NetApp Snapshot exists. + +* `resource_group_name` - (Required) The Name of the Resource Group where the NetApp Snapshot exists. + +## Attributes Reference + +The following attributes are exported: + +* `location` - The Azure Region where the NetApp Snapshot exists. diff --git a/website/docs/r/netapp_snapshot.html.markdown b/website/docs/r/netapp_snapshot.html.markdown new file mode 100644 index 000000000000..08c3cf8c0829 --- /dev/null +++ b/website/docs/r/netapp_snapshot.html.markdown @@ -0,0 +1,112 @@ +--- +subcategory: "NetApp" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_netapp_snapshot" +sidebar_current: "docs-azurerm-resource-netapp-snapshot" +description: |- + Manages a NetApp Snapshot. +--- + +# azurerm_netapp_snapshot + +Manages a NetApp Snapshot. + +## NetApp Snapshot Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_virtual_network" "example" { + name = "example-virtualnetwork" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.example.location}" + resource_group_name = "${azurerm_resource_group.example.name}" +} + +resource "azurerm_subnet" "example" { + name = "example-subnet" + resource_group_name = "${azurerm_resource_group.example.name}" + virtual_network_name = "${azurerm_virtual_network.example.name}" + address_prefix = "10.0.2.0/24" + + delegation { + name = "netapp" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +resource "azurerm_netapp_account" "example" { + name = "example-netappaccount" + location = "${azurerm_resource_group.example.location}" + resource_group_name = "${azurerm_resource_group.example.name}" +} + +resource "azurerm_netapp_pool" "example" { + name = "example-netapppool" + account_name = "${azurerm_netapp_account.example.name}" + location = "${azurerm_resource_group.example.location}" + resource_group_name = "${azurerm_resource_group.example.name}" + service_level = "Premium" + size_in_tb = "4" +} + +resource "azurerm_netapp_volume" "example" { + name = "example-netappvolume" + location = "${azurerm_resource_group.example.location}" + resource_group_name = "${azurerm_resource_group.example.name}" + account_name = "${azurerm_netapp_account.example.name}" + pool_name = "${azurerm_netapp_pool.example.name}" + volume_path = "my-unique-file-path" + service_level = "Premium" + subnet_id = "${azurerm_subnet.test.id}" + storage_quota_in_gb = "100" +} + +resource "azurerm_netapp_snapshot" "example" { + name = "example-netappsnapshot" + account_name = "${azurerm_netapp_account.example.name}" + pool_name = "${azurerm_netapp_pool.example.name}" + volume_name = "${azurerm_netapp_volume.example.name}" + location = "${azurerm_resource_group.example.location}" + resource_group_name = "${azurerm_resource_group.example.name}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the NetApp Snapshot. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the resource group where the NetApp Snapshot should be created. Changing this forces a new resource to be created. + +* `account_name` - (Required) The name of the NetApp account in which the NetApp Pool should be created. Changing this forces a new resource to be created. + +* `pool_name` - (Required) The name of the NetApp pool in which the NetApp Volume should be created. Changing this forces a new resource to be created. + +* `volume_name` - (Required) The name of the NetApp volume in which the NetApp Snapshot should be created. 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. + +--- + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the NetApp Snapshot. + +## Import + +NetApp Snapshot can be imported using the `resource id`, e.g. + +```shell +$ terraform import azurerm_netapp_snapshot.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.NetApp/netAppAccounts/account1/capacityPools/pool1/volumes/volume1/snapshots/snapshot1 +``` \ No newline at end of file