Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New resource azurerm_managed_disk_sas_token to manage disk exports #15558

Merged
merged 12 commits into from
May 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions internal/services/compute/disk_sas_token_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package compute

import (
"fmt"
"log"
"time"

"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
"github.com/hashicorp/terraform-provider-azurerm/internal/timeouts"
)

func resourceManagedDiskSasToken() *pluginsdk.Resource {

return &pluginsdk.Resource{
Create: resourceManagedDiskSasTokenCreate,
Read: resourceManagedDiskSasTokenRead,
Delete: resourceManagedDiskSasTokenDelete,

Timeouts: &pluginsdk.ResourceTimeout{
Create: pluginsdk.DefaultTimeout(30 * time.Minute),
Read: pluginsdk.DefaultTimeout(5 * time.Minute),
Delete: pluginsdk.DefaultTimeout(30 * time.Minute),
},

Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error {
_, err := parse.ManagedDiskID(id)
return err
}),

Schema: map[string]*pluginsdk.Schema{
"managed_disk_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.ManagedDiskID,
},

// unable to provide upper value of 4294967295 as it's not comptabile with 32-bit (overflow errors)
"duration_in_seconds": {
Type: pluginsdk.TypeInt,
Required: true,
ForceNew: true,
ValidateFunc: validation.IntAtLeast(30),
},

"access_level": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
string(compute.AccessLevelRead),
string(compute.AccessLevelWrite),
}, false),
},

"sas_url": {
Type: pluginsdk.TypeString,
Computed: true,
Sensitive: true,
},
},
}

}

func resourceManagedDiskSasTokenCreate(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Compute.DisksClient
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
defer cancel()

log.Printf("[INFO] preparing arguments for AzureRM Disk Export.")
durationInSeconds := int32(d.Get("duration_in_seconds").(int))
access := compute.AccessLevel(d.Get("access_level").(string))

diskId, err := parse.ManagedDiskID(d.Get("managed_disk_id").(string))
if err != nil {
return err
}

grantAccessData := compute.GrantAccessData{
Access: access,
DurationInSeconds: &durationInSeconds,
}

resp, err := client.Get(ctx, diskId.ResourceGroup, diskId.DiskName)
if err != nil {
return fmt.Errorf("error retrieving Disk %s: %+v", *diskId, err)
}

// checking whether disk export SAS URL is active already before creating. If yes, we raise an error
if resp.DiskState == "ActiveSAS" {
return fmt.Errorf("active SAS Token for Disk Export already exists, cannot create another one %s: %+v", *diskId, err)
}

future, err := client.GrantAccess(ctx, diskId.ResourceGroup, diskId.DiskName, grantAccessData)
if err != nil {
return fmt.Errorf("granting access to %s: %+v", *diskId, err)
}
if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("waiting for access to be granted to %s: %+v", *diskId, err)
}
read, err := future.Result(*client)
if err != nil {
return fmt.Errorf("retrieving SAS Token for Disk Access %s: %+v", *diskId, err)
}
if read.AccessSAS == nil {
return fmt.Errorf("retrieving SAS Token for Disk Access %s: SAS was nil", *diskId)
}

d.SetId(diskId.ID())
sasToken := *read.AccessSAS
d.Set("sas_url", sasToken)

return resourceManagedDiskSasTokenRead(d, meta)

}

func resourceManagedDiskSasTokenRead(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Compute.DisksClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

diskId, err := parse.ManagedDiskID(d.Id())
if err != nil {
return err
}

resp, err := client.Get(ctx, diskId.ResourceGroup, diskId.DiskName)
if err != nil {
// checking whether disk export SAS URL is active post creation. If no, we raise an error
if resp.DiskState != "ActiveSAS" {
log.Printf("[INFO] Disk SAS token %q does not exist - removing from state", d.Id())
d.SetId("")
return nil
}
return fmt.Errorf("making Read request on Azure Disk Export for SAS Token %s (resource group %s): %s", diskId.DiskName, diskId.ResourceGroup, err)
}

d.SetId(diskId.ID())

return nil
}

func resourceManagedDiskSasTokenDelete(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Compute.DisksClient
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.ManagedDiskID(d.Id())
if err != nil {
return err
}

future, err := client.RevokeAccess(ctx, id.ResourceGroup, id.DiskName)
if err != nil {
return fmt.Errorf("revoking access to %s: %+v", *id, err)
}

if err := future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("waiting for revocation of access to %s: %+v", *id, err)
}

return nil
}
76 changes: 76 additions & 0 deletions internal/services/compute/disk_sas_token_resource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package compute_test

import (
"context"
"fmt"
"testing"

"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/utils"
)

type ManagedDiskSASTokenResource struct{}

func TestAccManagedDiskSASToken_basic(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_managed_disk_sas_token", "test")
r := ManagedDiskSASTokenResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
})
}

func (t ManagedDiskSASTokenResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := parse.ManagedDiskID(state.ID)
if err != nil {
return nil, err
}

resp, err := clients.Compute.DisksClient.Get(ctx, id.ResourceGroup, id.DiskName)
if err != nil {
return nil, fmt.Errorf("retrieving Compute Disk Export status %q", id.String())
}

if resp.DiskState != "ActiveSAS" {
return nil, fmt.Errorf("Disk SAS token %s (resource group %s): %s", id.DiskName, id.ResourceGroup, err)
}

return utils.Bool(resp.ID != nil), nil
}

func (r ManagedDiskSASTokenResource) basic(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}

resource "azurerm_resource_group" "test" {
name = "acctestRG-revokedisk-%d"
location = "%s"
}

resource "azurerm_managed_disk" "test" {
name = "acctestsads%s"
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 = "1"
}

resource "azurerm_managed_disk_sas_token" "test" {
managed_disk_id = azurerm_managed_disk.test.id
duration_in_seconds = 300
access_level = "Read"
}
`, data.RandomInteger, data.Locations.Primary, data.RandomString)
}
1 change: 1 addition & 0 deletions internal/services/compute/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource {
"azurerm_windows_virtual_machine": resourceWindowsVirtualMachine(),
"azurerm_windows_virtual_machine_scale_set": resourceWindowsVirtualMachineScaleSet(),
"azurerm_ssh_public_key": resourceSshPublicKey(),
"azurerm_managed_disk_sas_token": resourceManagedDiskSasToken(),
}

return resources
Expand Down
78 changes: 78 additions & 0 deletions website/docs/r/disk_sas_token.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
subcategory: "Compute"
layout: "azurerm"
page_title: "Azure Resource Manager: azurerm_managed_disk_sas_token"
description: |-
Manages a Disk SAS Token.
---

# azurerm_managed_disk_sas_token

Manages a Disk SAS Token.

Use this resource to obtain a Shared Access Signature (SAS Token) for an existing Managed Disk.

Shared access signatures allow fine-grained, ephemeral access control to various aspects of Managed Disk similar to blob/storage account container.

With the help of this resource, data from the disk can be copied from managed disk to a storage blob or to some other system without the need of azcopy.

## Example Usage

```hcl
resource "azurerm_resource_group" "test" {
name = "testrg"
location = "West Europe"
}

resource "azurerm_managed_disk" "test" {
name = "tst-disk-export"
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 = "1"
}

resource "azurerm_managed_disk_sas_token" "test" {
managed_disk_id = azurerm_managed_disk.test.id
duration_in_seconds = 300
access_level = "Read"
}
```

## Arguments Reference

The following arguments are supported:

* `managed_disk_id` - (Required) The ID of an existing Managed Disk which should be exported. Changing this forces a new resource to be created.

* `duration_in_seconds` - (Required) The duration for which the export should be allowed. Should be between 30 & 4294967295 seconds.

* `access_level` - (Required) The level of access required on the disk. Supported are Read, Write.

Refer to the [SAS creation reference from Azure](https://docs.microsoft.com/en-us/rest/api/compute/disks/grant-access)
for additional details on the fields above.

## Attributes Reference

In addition to the Arguments listed above - the following Attributes are exported:

* `id` - The ID of the Disk Export resource.

* `sas_url` - The computed Shared Access Signature (SAS) of the Managed Disk.

## Timeouts

The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions:

* `create` - (Defaults to 30 minutes) Used when creating the Disk.
* `read` - (Defaults to 5 minutes) Used when retrieving the Disk.
* `delete` - (Defaults to 30 minutes) Used when deleting the Disk.

## Import

Disk SAS Token can be imported using the `resource id`, e.g.

```shell
terraform import azurerm_managed_disk_sas_token.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/microsoft.compute/disks/manageddisk1
```