diff --git a/internal/services/desktopvirtualization/parse/host_pool_registration_info.go b/internal/services/desktopvirtualization/parse/host_pool_registration_info.go new file mode 100644 index 000000000000..475457495079 --- /dev/null +++ b/internal/services/desktopvirtualization/parse/host_pool_registration_info.go @@ -0,0 +1,131 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" +) + +type HostPoolRegistrationInfoId struct { + SubscriptionId string + ResourceGroup string + HostPoolName string + RegistrationInfoName string +} + +func NewHostPoolRegistrationInfoID(subscriptionId, resourceGroup, hostPoolName, registrationInfoName string) HostPoolRegistrationInfoId { + return HostPoolRegistrationInfoId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + HostPoolName: hostPoolName, + RegistrationInfoName: registrationInfoName, + } +} + +func (id HostPoolRegistrationInfoId) String() string { + segments := []string{ + fmt.Sprintf("Registration Info Name %q", id.RegistrationInfoName), + fmt.Sprintf("Host Pool Name %q", id.HostPoolName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Host Pool Registration Info", segmentsStr) +} + +func (id HostPoolRegistrationInfoId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DesktopVirtualization/hostPools/%s/registrationInfo/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.HostPoolName, id.RegistrationInfoName) +} + +// HostPoolRegistrationInfoID parses a HostPoolRegistrationInfo ID into an HostPoolRegistrationInfoId struct +func HostPoolRegistrationInfoID(input string) (*HostPoolRegistrationInfoId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := HostPoolRegistrationInfoId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.HostPoolName, err = id.PopSegment("hostPools"); err != nil { + return nil, err + } + if resourceId.RegistrationInfoName, err = id.PopSegment("registrationInfo"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} + +// HostPoolRegistrationInfoIDInsensitively parses an HostPoolRegistrationInfo ID into an HostPoolRegistrationInfoId struct, insensitively +// This should only be used to parse an ID for rewriting, the HostPoolRegistrationInfoID +// method should be used instead for validation etc. +// +// Whilst this may seem strange, this enables Terraform have consistent casing +// which works around issues in Core, whilst handling broken API responses. +func HostPoolRegistrationInfoIDInsensitively(input string) (*HostPoolRegistrationInfoId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := HostPoolRegistrationInfoId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + // find the correct casing for the 'hostPools' segment + hostPoolsKey := "hostPools" + for key := range id.Path { + if strings.EqualFold(key, hostPoolsKey) { + hostPoolsKey = key + break + } + } + if resourceId.HostPoolName, err = id.PopSegment(hostPoolsKey); err != nil { + return nil, err + } + + // find the correct casing for the 'registrationInfo' segment + registrationInfoKey := "registrationInfo" + for key := range id.Path { + if strings.EqualFold(key, registrationInfoKey) { + registrationInfoKey = key + break + } + } + if resourceId.RegistrationInfoName, err = id.PopSegment(registrationInfoKey); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/desktopvirtualization/parse/host_pool_registration_info_test.go b/internal/services/desktopvirtualization/parse/host_pool_registration_info_test.go new file mode 100644 index 000000000000..b1814ce61a52 --- /dev/null +++ b/internal/services/desktopvirtualization/parse/host_pool_registration_info_test.go @@ -0,0 +1,264 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/resourceid" +) + +var _ resourceid.Formatter = HostPoolRegistrationInfoId{} + +func TestHostPoolRegistrationInfoIDFormatter(t *testing.T) { + actual := NewHostPoolRegistrationInfoID("12345678-1234-9876-4563-123456789012", "resGroup1", "pool1", "default").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/pool1/registrationInfo/default" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestHostPoolRegistrationInfoID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *HostPoolRegistrationInfoId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing HostPoolName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/", + Error: true, + }, + + { + // missing value for HostPoolName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/", + Error: true, + }, + + { + // missing RegistrationInfoName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/pool1/", + Error: true, + }, + + { + // missing value for RegistrationInfoName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/pool1/registrationInfo/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/pool1/registrationInfo/default", + Expected: &HostPoolRegistrationInfoId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + HostPoolName: "pool1", + RegistrationInfoName: "default", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DESKTOPVIRTUALIZATION/HOSTPOOLS/POOL1/REGISTRATIONINFO/DEFAULT", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := HostPoolRegistrationInfoID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.HostPoolName != v.Expected.HostPoolName { + t.Fatalf("Expected %q but got %q for HostPoolName", v.Expected.HostPoolName, actual.HostPoolName) + } + if actual.RegistrationInfoName != v.Expected.RegistrationInfoName { + t.Fatalf("Expected %q but got %q for RegistrationInfoName", v.Expected.RegistrationInfoName, actual.RegistrationInfoName) + } + } +} + +func TestHostPoolRegistrationInfoIDInsensitively(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *HostPoolRegistrationInfoId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing HostPoolName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/", + Error: true, + }, + + { + // missing value for HostPoolName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/", + Error: true, + }, + + { + // missing RegistrationInfoName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/pool1/", + Error: true, + }, + + { + // missing value for RegistrationInfoName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/pool1/registrationInfo/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/pool1/registrationInfo/default", + Expected: &HostPoolRegistrationInfoId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + HostPoolName: "pool1", + RegistrationInfoName: "default", + }, + }, + + { + // lower-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostpools/pool1/registrationinfo/default", + Expected: &HostPoolRegistrationInfoId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + HostPoolName: "pool1", + RegistrationInfoName: "default", + }, + }, + + { + // upper-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/HOSTPOOLS/pool1/REGISTRATIONINFO/default", + Expected: &HostPoolRegistrationInfoId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + HostPoolName: "pool1", + RegistrationInfoName: "default", + }, + }, + + { + // mixed-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/HoStPoOlS/pool1/ReGiStRaTiOnInFo/default", + Expected: &HostPoolRegistrationInfoId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + HostPoolName: "pool1", + RegistrationInfoName: "default", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := HostPoolRegistrationInfoIDInsensitively(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.HostPoolName != v.Expected.HostPoolName { + t.Fatalf("Expected %q but got %q for HostPoolName", v.Expected.HostPoolName, actual.HostPoolName) + } + if actual.RegistrationInfoName != v.Expected.RegistrationInfoName { + t.Fatalf("Expected %q but got %q for RegistrationInfoName", v.Expected.RegistrationInfoName, actual.RegistrationInfoName) + } + } +} diff --git a/internal/services/desktopvirtualization/registration.go b/internal/services/desktopvirtualization/registration.go index 545a9eb7ace2..4d02bdf7a17b 100644 --- a/internal/services/desktopvirtualization/registration.go +++ b/internal/services/desktopvirtualization/registration.go @@ -31,5 +31,6 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { "azurerm_virtual_desktop_application_group": resourceVirtualDesktopApplicationGroup(), "azurerm_virtual_desktop_application": resourceVirtualDesktopApplication(), "azurerm_virtual_desktop_workspace_application_group_association": resourceVirtualDesktopWorkspaceApplicationGroupAssociation(), + "azurerm_virtual_desktop_host_pool_registration_info": resourceVirtualDesktopHostPoolRegistrationInfo(), } } diff --git a/internal/services/desktopvirtualization/resourcesid.go b/internal/services/desktopvirtualization/resourcesid.go index 3d49df141138..d73f647e0940 100644 --- a/internal/services/desktopvirtualization/resourcesid.go +++ b/internal/services/desktopvirtualization/resourcesid.go @@ -3,4 +3,5 @@ package desktopvirtualization //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ApplicationGroup -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/applicationGroups/applicationGroup1 -rewrite=true //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Application -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/applicationGroups/applicationGroup1/applications/application1 -rewrite=true //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=HostPool -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/pool1 -rewrite=true +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=HostPoolRegistrationInfo -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/pool1/registrationInfo/default -rewrite=true //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Workspace -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/workspaces/workspace1 diff --git a/internal/services/desktopvirtualization/validate/host_pool_registration_info_id.go b/internal/services/desktopvirtualization/validate/host_pool_registration_info_id.go new file mode 100644 index 000000000000..aa4ddd74f368 --- /dev/null +++ b/internal/services/desktopvirtualization/validate/host_pool_registration_info_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/desktopvirtualization/parse" +) + +func HostPoolRegistrationInfoID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.HostPoolRegistrationInfoID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/desktopvirtualization/validate/host_pool_registration_info_id_test.go b/internal/services/desktopvirtualization/validate/host_pool_registration_info_id_test.go new file mode 100644 index 000000000000..771f31ddfa90 --- /dev/null +++ b/internal/services/desktopvirtualization/validate/host_pool_registration_info_id_test.go @@ -0,0 +1,88 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestHostPoolRegistrationInfoID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing HostPoolName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/", + Valid: false, + }, + + { + // missing value for HostPoolName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/", + Valid: false, + }, + + { + // missing RegistrationInfoName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/pool1/", + Valid: false, + }, + + { + // missing value for RegistrationInfoName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/pool1/registrationInfo/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DesktopVirtualization/hostPools/pool1/registrationInfo/default", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DESKTOPVIRTUALIZATION/HOSTPOOLS/POOL1/REGISTRATIONINFO/DEFAULT", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := HostPoolRegistrationInfoID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/desktopvirtualization/virtual_desktop_host_pool_registration_info_resource.go b/internal/services/desktopvirtualization/virtual_desktop_host_pool_registration_info_resource.go new file mode 100644 index 000000000000..b86adca195a8 --- /dev/null +++ b/internal/services/desktopvirtualization/virtual_desktop_host_pool_registration_info_resource.go @@ -0,0 +1,187 @@ +package desktopvirtualization + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/preview/desktopvirtualization/mgmt/2020-11-02-preview/desktopvirtualization" + "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/desktopvirtualization/parse" + "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" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceVirtualDesktopHostPoolRegistrationInfo() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceVirtualDesktopHostPoolRegistrationInfoCreateUpdate, + Read: resourceVirtualDesktopHostPoolRegistrationInfoRead, + Update: resourceVirtualDesktopHostPoolRegistrationInfoCreateUpdate, + Delete: resourceVirtualDesktopHostPoolRegistrationInfoDelete, + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(60 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(60 * time.Minute), + Delete: pluginsdk.DefaultTimeout(60 * time.Minute), + }, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.HostPoolRegistrationInfoID(id) + return err + }), + + SchemaVersion: 1, + + CustomizeDiff: pluginsdk.CustomizeDiffShim(hostpoolRegistrationInfoCustomDiff), + + Schema: map[string]*pluginsdk.Schema{ + "hostpool_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "expiration_date": { + Type: pluginsdk.TypeString, + ValidateFunc: validation.IsRFC3339Time, + Required: true, + }, + + "token": { + Type: pluginsdk.TypeString, + Sensitive: true, + Computed: true, + }, + }, + } +} + +func hostpoolRegistrationInfoCustomDiff(ctx context.Context, d *pluginsdk.ResourceDiff, _ interface{}) error { + if d.HasChange("expiration_date") { + if err := d.SetNewComputed("token"); err != nil { + return err + } + return nil + } + return nil +} + +func resourceVirtualDesktopHostPoolRegistrationInfoCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).DesktopVirtualization.HostPoolsClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + hostpoolId, err := parse.HostPoolID(d.Get("hostpool_id").(string)) + if err != nil { + return err + } + + // This is a virtual resource so the last segment is hardcoded + id := parse.NewHostPoolRegistrationInfoID(hostpoolId.SubscriptionId, hostpoolId.ResourceGroup, hostpoolId.Name, "default") + + hostpool, err := client.Get(ctx, id.ResourceGroup, id.HostPoolName) + if err != nil { + if utils.ResponseWasNotFound(hostpool.Response) { + return fmt.Errorf("%s could not be found: %s", hostpoolId, err) + } + return fmt.Errorf("reading %s: %s", hostpoolId, err) + } + + if hostpool.HostPoolProperties.RegistrationInfo == nil { + hostpool.HostPoolProperties.RegistrationInfo = &desktopvirtualization.RegistrationInfo{} + } + + expdt, _ := date.ParseTime(time.RFC3339, d.Get("expiration_date").(string)) + hostpool.HostPoolProperties.RegistrationInfo.ExpirationTime = &date.Time{ + Time: expdt, + } + hostpool.HostPoolProperties.RegistrationInfo.RegistrationTokenOperation = desktopvirtualization.Update + + if _, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.HostPoolName, hostpool); err != nil { + return fmt.Errorf("Creating Virtual Desktop Host Pool Registration Info %q (Resource Group %q): %+v", id.HostPoolName, id.ResourceGroup, err) + } + + d.SetId(id.ID()) + + return resourceVirtualDesktopHostPoolRegistrationInfoRead(d, meta) +} + +func resourceVirtualDesktopHostPoolRegistrationInfoRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).DesktopVirtualization.HostPoolsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.HostPoolRegistrationInfoID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.HostPoolName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Virtual Desktop Host Pool %q was not found in Resource Group %q - removing from state!", id.HostPoolName, id.ResourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Making Read request on Virtual Desktop Host Pool %q (Resource Group %q): %+v", id.HostPoolName, id.ResourceGroup, err) + } + + if regInfo := resp.HostPoolProperties.RegistrationInfo; regInfo != nil { + hostpoolId := parse.NewHostPoolID(id.SubscriptionId, id.ResourceGroup, id.HostPoolName) + d.Set("hostpool_id", hostpoolId.ID()) + d.Set("expiration_date", regInfo.ExpirationTime.Format(time.RFC3339)) + d.Set("token", regInfo.Token) + } + + return nil +} + +func resourceVirtualDesktopHostPoolRegistrationInfoDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).DesktopVirtualization.HostPoolsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.HostPoolRegistrationInfoID(d.Id()) + if err != nil { + return err + } + + hostpoolId := parse.NewHostPoolID(id.SubscriptionId, id.ResourceGroup, id.HostPoolName) + + resp, err := client.Get(ctx, id.ResourceGroup, id.HostPoolName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Virtual Desktop Host Pool %q was not found in Resource Group %q - removing from state!", id.HostPoolName, id.ResourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Making Read request on Virtual Desktop Host Pool %q (Resource Group %q): %+v", id.HostPoolName, id.ResourceGroup, err) + } + + if resp.HostPoolProperties == nil { + return fmt.Errorf("%s was returned without any properties", hostpoolId) + } + if resp.HostPoolProperties.RegistrationInfo == nil { + log.Printf("[INFO] RegistrationInfo for %s was nil, registrationInfo already deleted - removing %s from state", hostpoolId.ID(), id) + return nil + } + + if regInfo := resp.HostPoolProperties.RegistrationInfo; regInfo != nil { + regInfo.RegistrationTokenOperation = desktopvirtualization.Delete + } + + if _, err = client.CreateOrUpdate(ctx, id.ResourceGroup, id.HostPoolName, resp); err != nil { + return fmt.Errorf("deleting Virtual Desktop Host Pool Registration Info %q (Resource Group %q): %+v", id.HostPoolName, id.ResourceGroup, err) + } + + return nil + +} diff --git a/internal/services/desktopvirtualization/virtual_desktop_host_pool_registration_info_resource_test.go b/internal/services/desktopvirtualization/virtual_desktop_host_pool_registration_info_resource_test.go new file mode 100644 index 000000000000..acaeb6abd92a --- /dev/null +++ b/internal/services/desktopvirtualization/virtual_desktop_host_pool_registration_info_resource_test.go @@ -0,0 +1,140 @@ +package desktopvirtualization_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/desktopvirtualization/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type VirtualDesktopHostPoolRegistrationInfoResource struct { +} + +func TestAccVirtualDesktopHostPoolRegInfo_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_virtual_desktop_host_pool_registration_info", "test") + r := VirtualDesktopHostPoolRegistrationInfoResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + }) +} + +func TestAccVirtualDesktopHostPoolRegInfo_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_virtual_desktop_host_pool", "test") + r := VirtualDesktopHostPoolResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (VirtualDesktopHostPoolRegistrationInfoResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + + //resp, err := clients.Network.VnetClient.Get(ctx, id.ResourceGroup, id.VirtualNetworkName, "") + //if err != nil { + // return nil, fmt.Errorf("reading %s: %+v", *id, err) + //} + + //exists := resp.ID != nil && resp.VirtualNetworkPropertiesFormat != nil && resp.VirtualNetworkPropertiesFormat.DhcpOptions != nil && + // resp.VirtualNetworkPropertiesFormat.DhcpOptions.DNSServers != nil && len(*resp.VirtualNetworkPropertiesFormat.DhcpOptions.DNSServers) > 0 + + //return utils.Bool(exists), nil + + id, err := parse.HostPoolRegistrationInfoID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.DesktopVirtualization.HostPoolsClient.Get(ctx, id.ResourceGroup, id.HostPoolName) + exists := resp.ID != nil && resp.HostPoolProperties != nil && resp.HostPoolProperties.RegistrationInfo != nil && len(*resp.HostPoolProperties.RegistrationInfo.Token) > 0 + + return utils.Bool(exists), nil +} + +func (VirtualDesktopHostPoolRegistrationInfoResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-vdesktop-%d" + location = "%s" +} + +resource "azurerm_virtual_desktop_host_pool" "test" { + name = "acctestHP%s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + type = "Pooled" + validate_environment = true + load_balancer_type = "BreadthFirst" + +} + +resource "azurerm_virtual_desktop_host_pool_registration_info" "test" { + hostpool_id = azurerm_virtual_desktop_host_pool.test.id + expiration_date = timeadd(timestamp(), "48h") +} + + +`, data.RandomInteger, data.Locations.Secondary, data.RandomString) +} + +func (VirtualDesktopHostPoolRegistrationInfoResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` + provider "azurerm" { + features {} + } + + resource "azurerm_resource_group" "test" { + name = "acctestRG-vdesktop-%d" + location = "%s" + } + + resource "azurerm_virtual_desktop_host_pool" "test" { + name = "acctestHP%s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + type = "Pooled" + validate_environment = true + load_balancer_type = "BreadthFirst" + + } + + resource "azurerm_virtual_desktop_host_pool_registration_info" "test" { + hostpool_id = azurerm_virtual_desktop_host_pool.test.id + expiration_date = timeadd(timestamp(), "72h") + } + +`, data.RandomInteger, data.Locations.Secondary, data.RandomString) +} diff --git a/internal/services/desktopvirtualization/virtual_desktop_host_pool_resource.go b/internal/services/desktopvirtualization/virtual_desktop_host_pool_resource.go index 4da46b34fbb1..ae4fe2d08563 100644 --- a/internal/services/desktopvirtualization/virtual_desktop_host_pool_resource.go +++ b/internal/services/desktopvirtualization/virtual_desktop_host_pool_resource.go @@ -1,7 +1,6 @@ package desktopvirtualization import ( - "context" "fmt" "log" "time" @@ -44,8 +43,6 @@ func resourceVirtualDesktopHostPool() *pluginsdk.Resource { 0: migration.HostPoolV0ToV1{}, }), - CustomizeDiff: pluginsdk.CustomizeDiffShim(hostpoolRegistrationCustomDiff), - Schema: map[string]*pluginsdk.Schema{ "name": { Type: pluginsdk.TypeString, @@ -163,28 +160,12 @@ func resourceVirtualDesktopHostPool() *pluginsdk.Resource { }, }, }, - // Added new to enable current token value to be stored in state using CustomizeDiff - "registration_token": { - Type: pluginsdk.TypeString, - Sensitive: true, - Computed: true, - }, "tags": tags.Schema(), }, } } -func hostpoolRegistrationCustomDiff(ctx context.Context, d *pluginsdk.ResourceDiff, _ interface{}) error { - if d.HasChange("registration_info") { - if err := d.SetNewComputed("registration_token"); err != nil { - return err - } - return nil - } - return nil -} - func resourceVirtualDesktopHostPoolCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).DesktopVirtualization.HostPoolsClient subscriptionId := meta.(*clients.Client).Account.SubscriptionId diff --git a/internal/services/desktopvirtualization/virtual_desktop_host_pool_resource_test.go b/internal/services/desktopvirtualization/virtual_desktop_host_pool_resource_test.go index 77eea823acab..22912f13de3a 100644 --- a/internal/services/desktopvirtualization/virtual_desktop_host_pool_resource_test.go +++ b/internal/services/desktopvirtualization/virtual_desktop_host_pool_resource_test.go @@ -126,10 +126,6 @@ resource "azurerm_virtual_desktop_host_pool" "test" { validate_environment = true load_balancer_type = "BreadthFirst" - # Do not use timestamp() outside of testing due to https://github.com/hashicorp/terraform/issues/22461 - registration_info { - expiration_date = timeadd(timestamp(), "48h") - } lifecycle { ignore_changes = [ registration_info[0].expiration_date,