diff --git a/docs/resources/wem_configuration_set.md b/docs/resources/wem_configuration_set.md new file mode 100644 index 0000000..5915db9 --- /dev/null +++ b/docs/resources/wem_configuration_set.md @@ -0,0 +1,44 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "citrix_wem_configuration_set Resource - citrix" +subcategory: "WEM" +description: |- + Manages configuration sets within a WEM deployment. +--- + +# citrix_wem_configuration_set (Resource) + +Manages configuration sets within a WEM deployment. + +## Example Usage + +```terraform +resource "citrix_wem_configuration_set" "example-config-set"{ + name = "example config set" + description = "example WEM configuration set" +} +``` + + +## Schema + +### Required + +- `name` (String) Name of the configuration site. WEM Site Name should be unique. + +### Optional + +- `description` (String) Description of the configuration site. Default value is empty string. + +### Read-Only + +- `id` (String) Identifier of the configuration site. + +## Import + +Import is supported using the following syntax: + +```shell +# WEM Configuration Set can be imported by specifying the ID +terraform import citrix_wem_configuration_set.example-config-set 0000 +``` \ No newline at end of file diff --git a/docs/resources/wem_directory_object.md b/docs/resources/wem_directory_object.md new file mode 100644 index 0000000..e088ba9 --- /dev/null +++ b/docs/resources/wem_directory_object.md @@ -0,0 +1,43 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "citrix_wem_directory_object Resource - citrix" +subcategory: "WEM" +description: |- + Manages machine-level AD objects within a WEM deployment. +--- + +# citrix_wem_directory_object (Resource) + +Manages machine-level AD objects within a WEM deployment. + +## Example Usage + +```terraform +resource "citrix_wem_directory_object" "example-directory-object" { + configuration_set_id = citrix_wem_configuration_set.example-config-set.id + machine_catalog_id = citrix_machine_catalog.example-machine-catalog.id + enabled = true +} +``` + + +## Schema + +### Required + +- `configuration_set_id` (Number) Identifier of the site to which the machine-level AD object belongs. +- `enabled` (Boolean) Indicates whether the machine-level AD object is enabled. +- `machine_catalog_id` (String) GUID identifier of the machine catalog. + +### Read-Only + +- `id` (String) Identifier of the directory object. + +## Import + +Import is supported using the following syntax: + +```shell +# WEM Directory Object can be imported by specifying the ID +terraform import citrix_wem_directory_object.example-directory-object 0000 +``` \ No newline at end of file diff --git a/go.mod b/go.mod index 18d288b..e326dfd 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.0 toolchain go1.23.1 require ( - github.com/citrix/citrix-daas-rest-go v1.0.5 + github.com/citrix/citrix-daas-rest-go v1.0.6-preview github.com/google/uuid v1.6.0 github.com/hashicorp/go-azure-helpers v0.71.0 github.com/hashicorp/go-multierror v1.1.1 @@ -75,8 +75,8 @@ require ( golang.org/x/text v0.18.0 // indirect golang.org/x/tools v0.25.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 // indirect - google.golang.org/grpc v1.67.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect + google.golang.org/grpc v1.67.1 // indirect google.golang.org/protobuf v1.34.2 // indirect ) diff --git a/go.sum b/go.sum index 00556b1..904aad8 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= -github.com/citrix/citrix-daas-rest-go v1.0.5 h1:zDOoydXQq4jQ3fObZypsBG6ZJxyHIcHrS4eg6kJzlZ0= -github.com/citrix/citrix-daas-rest-go v1.0.5/go.mod h1:4Me0VHpyxMYfPwpU2XWV0jOE2Jdz8MHNpge3MLD5B2E= +github.com/citrix/citrix-daas-rest-go v1.0.6-preview h1:+wIJMiU6uz3ugHyZ61Ijh/h3mFCNSzanj4DiOLKj1Ho= +github.com/citrix/citrix-daas-rest-go v1.0.6-preview/go.mod h1:4Me0VHpyxMYfPwpU2XWV0jOE2Jdz8MHNpge3MLD5B2E= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= @@ -269,10 +269,10 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 h1:N9BgCIAUvn/M+p4NJccWPWb3BWh88+zyL0ll9HgbEeM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= -google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f h1:cUMEy+8oS78BWIH9OWazBkzbr090Od9tWBNtZHkOhf0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= diff --git a/internal/daas/admin_folder/admin_folder_data_source.go b/internal/daas/admin_folder/admin_folder_data_source.go index c7f1f8d..d9e81e0 100644 --- a/internal/daas/admin_folder/admin_folder_data_source.go +++ b/internal/daas/admin_folder/admin_folder_data_source.go @@ -63,7 +63,7 @@ func (d *AdminFolderDataSource) Read(ctx context.Context, req datasource.ReadReq adminFolderIdOrPath = data.Id.ValueString() } if data.Path.ValueString() != "" { - adminFolderIdOrPath = data.Path.ValueString() + adminFolderIdOrPath = data.Path.ValueString() + "\\" adminFolderIdOrPath = strings.ReplaceAll(adminFolderIdOrPath, "\\", "|") } diff --git a/internal/daas/admin_folder/admin_folder_data_source_model.go b/internal/daas/admin_folder/admin_folder_data_source_model.go index 9fddb91..77ff99e 100644 --- a/internal/daas/admin_folder/admin_folder_data_source_model.go +++ b/internal/daas/admin_folder/admin_folder_data_source_model.go @@ -3,6 +3,7 @@ package admin_folder import ( "context" + "regexp" "strings" citrixorchestration "github.com/citrix/citrix-daas-rest-go/citrixorchestration" @@ -43,6 +44,10 @@ func (AdminFolderDataSourceModel) GetSchema() schema.Schema { "path": schema.StringAttribute{ Description: "Path to the admin folder.", Optional: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathWithBackslashRegex), "Admin Folder Path must not start or end with a backslash"), + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathSpecialCharactersRegex), "Admin Folder Path must not contain any of the following special characters: / ; : # . * ? = < > | [ ] ( ) { } \" ' ` ~ "), + }, }, "name": schema.StringAttribute{ Description: "Name of the admin folder.", @@ -82,7 +87,7 @@ func (r AdminFolderDataSourceModel) RefreshPropertyValues(ctx context.Context, d r.Id = types.StringValue(adminFolder.GetId()) r.Name = types.StringValue(adminFolder.GetName()) - r.Path = types.StringValue(adminFolder.GetPath()) + r.Path = types.StringValue(strings.TrimSuffix(adminFolder.GetPath(), "\\")) adminFolderTypes := []string{} adminFolderMetadata := adminFolder.GetMetadata() @@ -94,6 +99,7 @@ func (r AdminFolderDataSourceModel) RefreshPropertyValues(ctx context.Context, d r.Type = adminFolderTypeSet var parentPath = strings.TrimSuffix(adminFolder.GetPath(), adminFolder.GetName()+"\\") + parentPath = strings.TrimSuffix(parentPath, "\\") if parentPath != "" { r.ParentPath = types.StringValue(parentPath) } else { diff --git a/internal/daas/admin_folder/admin_folder_resource.go b/internal/daas/admin_folder/admin_folder_resource.go index dbd964f..2e59b4a 100644 --- a/internal/daas/admin_folder/admin_folder_resource.go +++ b/internal/daas/admin_folder/admin_folder_resource.go @@ -70,7 +70,11 @@ func (r *adminFolderResource) Create(ctx context.Context, req resource.CreateReq // Generate API request body from plan var createAdminFolderRequest citrixorchestration.CreateAdminFolderRequestModel createAdminFolderRequest.SetName(plan.Name.ValueString()) - createAdminFolderRequest.SetPath(plan.ParentPath.ValueString()) + parentPath := plan.ParentPath.ValueString() + if parentPath != "" { + parentPath = parentPath + "\\" + } + createAdminFolderRequest.SetPath(parentPath) adminFolderTypesArray, err := getAdminFolderObjectIdentifierArrayFromTypeSet(ctx, &resp.Diagnostics, plan.Name.ValueString(), plan.Type) if err != nil { @@ -155,7 +159,7 @@ func (r *adminFolderResource) Update(ctx context.Context, req resource.UpdateReq editAdminFolderRequestBody.SetName(plan.Name.ValueString()) var parentPath = strings.TrimSuffix(adminFolderResource.GetPath(), adminFolderResource.GetName()+"\\") - if plan.ParentPath.ValueString() != parentPath { + if plan.ParentPath.ValueString() != strings.TrimSuffix(parentPath, "\\") { editAdminFolderRequestBody.SetParent(plan.ParentPath.ValueString()) } diff --git a/internal/daas/admin_folder/admin_folder_resource_model.go b/internal/daas/admin_folder/admin_folder_resource_model.go index 260fcc3..6852412 100644 --- a/internal/daas/admin_folder/admin_folder_resource_model.go +++ b/internal/daas/admin_folder/admin_folder_resource_model.go @@ -3,6 +3,7 @@ package admin_folder import ( "context" + "regexp" "strings" citrixorchestration "github.com/citrix/citrix-daas-rest-go/citrixorchestration" @@ -43,6 +44,10 @@ func (AdminFolderResourceModel) GetSchema() schema.Schema { "parent_path": schema.StringAttribute{ Description: "Path of the parent admin folder.", Optional: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathWithBackslashRegex), "Admin Folder Path must not start or end with a backslash"), + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathSpecialCharactersRegex), "Admin Folder Path must not contain any of the following special characters: / ; : # . * ? = < > | [ ] ( ) { } \" ' ` ~ "), + }, }, "type": schema.SetAttribute{ ElementType: types.StringType, @@ -80,7 +85,7 @@ func (r AdminFolderResourceModel) RefreshPropertyValues(ctx context.Context, dia r.Name = types.StringValue(adminFolder.GetName()) // Set optional values - r.Path = types.StringValue(adminFolder.GetPath()) + r.Path = types.StringValue(strings.TrimSuffix(adminFolder.GetPath(), "\\")) adminFolderTypes := []string{} adminFolderMetadata := adminFolder.GetMetadata() @@ -92,6 +97,7 @@ func (r AdminFolderResourceModel) RefreshPropertyValues(ctx context.Context, dia r.Type = adminFolderTypeSet var parentPath = strings.TrimSuffix(adminFolder.GetPath(), adminFolder.GetName()+"\\") + parentPath = strings.TrimSuffix(parentPath, "\\") if parentPath != "" { r.ParentPath = types.StringValue(parentPath) } else { diff --git a/internal/daas/application/application_group_resource_model.go b/internal/daas/application/application_group_resource_model.go index 4fa6538..2543fb4 100644 --- a/internal/daas/application/application_group_resource_model.go +++ b/internal/daas/application/application_group_resource_model.go @@ -5,6 +5,7 @@ package application import ( "context" "regexp" + "strings" citrixorchestration "github.com/citrix/citrix-daas-rest-go/citrixorchestration" citrixdaasclient "github.com/citrix/citrix-daas-rest-go/client" @@ -117,6 +118,10 @@ func (ApplicationGroupResourceModel) GetSchema() schema.Schema { "application_group_folder_path": schema.StringAttribute{ Description: "The path of the folder in which the application group is located.", Optional: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathWithBackslashRegex), "Admin Folder Path must not start or end with a backslash"), + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathSpecialCharactersRegex), "Admin Folder Path must not contain any of the following special characters: / ; : # . * ? = < > | [ ] ( ) { } \" ' ` ~ "), + }, }, "tenants": schema.SetAttribute{ ElementType: types.StringType, @@ -185,7 +190,7 @@ func (r ApplicationGroupResourceModel) RefreshPropertyValues(ctx context.Context r.InheritedScopes = util.StringArrayToStringSet(ctx, diagnostics, inheritedScopeIds) adminFolder := applicationGroup.GetAdminFolder() - adminFolderPath := adminFolder.GetName() + adminFolderPath := strings.TrimSuffix(adminFolder.GetName(), "\\") if adminFolderPath != "" { r.ApplicationGroupFolderPath = types.StringValue(adminFolderPath) } else { diff --git a/internal/daas/application/application_resource.go b/internal/daas/application/application_resource.go index 6d7cdd1..39d8146 100644 --- a/internal/daas/application/application_resource.go +++ b/internal/daas/application/application_resource.go @@ -138,7 +138,7 @@ func (r *applicationResource) Create(ctx context.Context, req resource.CreateReq // If the application is present in an application folder, we specify the name in this format: {application folder path plus application name}.For example, FolderName1|FolderName2|ApplicationName. if plan.ApplicationFolderPath.ValueString() != "" { - applicationName = strings.ReplaceAll(plan.ApplicationFolderPath.ValueString(), "\\", "|") + applicationName + applicationName = strings.ReplaceAll(plan.ApplicationFolderPath.ValueString(), "\\", "|") + "|" + applicationName } application, err := getApplication(ctx, r.client, &resp.Diagnostics, applicationName) @@ -399,7 +399,7 @@ func checkIfApplicationFolderPathExist(ctx context.Context, client *citrixdaascl return true } - tempFolderPath := strings.ReplaceAll(applicationFolderPath, "\\", "|") + tempFolderPath := strings.ReplaceAll(applicationFolderPath, "\\", "|") + "|" appFolderExistRequest := client.ApiClient.ApplicationFoldersAPIsDAAS.ApplicationFoldersCheckApplicationFolderPathExists(ctx, tempFolderPath) httpResp, err := citrixdaasclient.AddRequestData(appFolderExistRequest, client).Execute() if err != nil { diff --git a/internal/daas/application/application_resource_model.go b/internal/daas/application/application_resource_model.go index 5c947ae..c7961e4 100644 --- a/internal/daas/application/application_resource_model.go +++ b/internal/daas/application/application_resource_model.go @@ -6,6 +6,7 @@ import ( "context" "regexp" "sort" + "strings" citrixorchestration "github.com/citrix/citrix-daas-rest-go/citrixorchestration" "github.com/citrix/terraform-provider-citrix/internal/util" @@ -166,6 +167,10 @@ func (ApplicationResourceModel) GetSchema() schema.Schema { "application_folder_path": schema.StringAttribute{ Description: "The application folder path in which the application should be created.", Optional: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathWithBackslashRegex), "Admin Folder Path must not start or end with a backslash"), + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathSpecialCharactersRegex), "Admin Folder Path must not contain any of the following special characters: / ; : # . * ? = < > | [ ] ( ) { } \" ' ` ~ "), + }, }, "icon": schema.StringAttribute{ Description: "The Id of the icon to be associated with the application.", @@ -229,8 +234,10 @@ func (r ApplicationResourceModel) RefreshPropertyValues(ctx context.Context, dia r.ApplicationCategoryPath = types.StringValue(application.GetClientFolder()) // Set optional values - if *application.GetApplicationFolder().Name.Get() != "" { - r.ApplicationFolderPath = types.StringValue(*application.GetApplicationFolder().Name.Get()) + adminFolder := application.GetApplicationFolder() + adminFolderPath := strings.TrimSuffix(adminFolder.GetName(), "\\") + if adminFolderPath != "" { + r.ApplicationFolderPath = types.StringValue(adminFolderPath) } else { r.ApplicationFolderPath = types.StringNull() } diff --git a/internal/daas/delivery_group/delivery_group_data_source.go b/internal/daas/delivery_group/delivery_group_data_source.go index 7051ad3..4061788 100644 --- a/internal/daas/delivery_group/delivery_group_data_source.go +++ b/internal/daas/delivery_group/delivery_group_data_source.go @@ -4,6 +4,7 @@ package delivery_group import ( "context" + "strings" citrixdaasclient "github.com/citrix/citrix-daas-rest-go/client" "github.com/citrix/terraform-provider-citrix/internal/util" @@ -61,7 +62,13 @@ func (d *DeliveryGroupDataSource) Read(ctx context.Context, req datasource.ReadR // Get refreshed delivery group state from Orchestration deliveryGroupName := data.Name.ValueString() - getDeliveryGroupRequest := d.client.ApiClient.DeliveryGroupsAPIsDAAS.DeliveryGroupsGetDeliveryGroup(ctx, deliveryGroupName) + deliveryGroupPath := strings.ReplaceAll(data.DeliveryGroupFolderPath.ValueString(), "\\", "|") + if deliveryGroupPath != "" { + deliveryGroupPath = deliveryGroupPath + "|" + deliveryGroupName + } else { + deliveryGroupPath = deliveryGroupName + } + getDeliveryGroupRequest := d.client.ApiClient.DeliveryGroupsAPIsDAAS.DeliveryGroupsGetDeliveryGroup(ctx, deliveryGroupPath) deliveryGroup, httpResp, err := citrixdaasclient.AddRequestData(getDeliveryGroupRequest, d.client).Execute() if err != nil { resp.Diagnostics.AddError( diff --git a/internal/daas/delivery_group/delivery_group_data_source_model.go b/internal/daas/delivery_group/delivery_group_data_source_model.go index 4577ccd..d6b2ab4 100644 --- a/internal/daas/delivery_group/delivery_group_data_source_model.go +++ b/internal/daas/delivery_group/delivery_group_data_source_model.go @@ -4,12 +4,16 @@ package delivery_group import ( "context" + "regexp" + "strings" "github.com/citrix/citrix-daas-rest-go/citrixorchestration" "github.com/citrix/terraform-provider-citrix/internal/daas/vda" "github.com/citrix/terraform-provider-citrix/internal/util" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -38,6 +42,10 @@ func (DeliveryGroupDataSourceModel) GetSchema() schema.Schema { "delivery_group_folder_path": schema.StringAttribute{ Description: "The path to the folder in which the delivery group is located.", Optional: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathWithBackslashRegex), "Admin Folder Path must not start or end with a backslash"), + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathSpecialCharactersRegex), "Admin Folder Path must not contain any of the following special characters: / ; : # . * ? = < > | [ ] ( ) { } \" ' ` ~ "), + }, }, "vdas": schema.ListNestedAttribute{ Description: "The VDAs associated with the delivery group.", @@ -63,7 +71,7 @@ func (r DeliveryGroupDataSourceModel) RefreshPropertyValues(ctx context.Context, r.Name = types.StringValue(deliveryGroup.GetName()) adminFolder := deliveryGroup.GetAdminFolder() - adminFolderPath := adminFolder.GetName() + adminFolderPath := strings.TrimSuffix(adminFolder.GetName(), "\\") if adminFolderPath != "" { r.DeliveryGroupFolderPath = types.StringValue(adminFolderPath) } else { diff --git a/internal/daas/delivery_group/delivery_group_resource_model.go b/internal/daas/delivery_group/delivery_group_resource_model.go index 190e957..0655936 100644 --- a/internal/daas/delivery_group/delivery_group_resource_model.go +++ b/internal/daas/delivery_group/delivery_group_resource_model.go @@ -1095,6 +1095,10 @@ func (DeliveryGroupResourceModel) GetSchema() schema.Schema { "delivery_group_folder_path": schema.StringAttribute{ Description: "The path of the folder in which the delivery group is located.", Optional: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathWithBackslashRegex), "Admin Folder Path must not start or end with a backslash"), + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathSpecialCharactersRegex), "Admin Folder Path must not contain any of the following special characters: / ; : # . * ? = < > | [ ] ( ) { } \" ' ` ~ "), + }, }, "tenants": schema.SetAttribute{ ElementType: types.StringType, @@ -1186,7 +1190,7 @@ func (r DeliveryGroupResourceModel) RefreshPropertyValues(ctx context.Context, d } adminFolder := deliveryGroup.GetAdminFolder() - adminFolderPath := adminFolder.GetName() + adminFolderPath := strings.TrimSuffix(adminFolder.GetName(), "\\") if adminFolderPath != "" { r.DeliveryGroupFolderPath = types.StringValue(adminFolderPath) } else { diff --git a/internal/daas/machine_catalog/machine_catalog_common_utils.go b/internal/daas/machine_catalog/machine_catalog_common_utils.go index b48f20b..742217b 100644 --- a/internal/daas/machine_catalog/machine_catalog_common_utils.go +++ b/internal/daas/machine_catalog/machine_catalog_common_utils.go @@ -245,7 +245,7 @@ func generateBatchApiHeaders(ctx context.Context, diagnostics *diag.Diagnostics, } func readMachineCatalog(ctx context.Context, client *citrixdaasclient.CitrixDaasClient, resp *resource.ReadResponse, machineCatalogId string) (*citrixorchestration.MachineCatalogDetailResponseModel, *http.Response, error) { - getMachineCatalogRequest := client.ApiClient.MachineCatalogsAPIsDAAS.MachineCatalogsGetMachineCatalog(ctx, machineCatalogId).Fields("Id,Name,Description,ProvisioningType,Zone,AllocationType,SessionSupport,TotalCount,HypervisorConnection,ProvisioningScheme,RemotePCEnrollmentScopes,IsPowerManaged,MinimumFunctionalLevel,IsRemotePC,Metadata,Scopes") + getMachineCatalogRequest := client.ApiClient.MachineCatalogsAPIsDAAS.MachineCatalogsGetMachineCatalog(ctx, machineCatalogId).Fields("Id,Name,Description,ProvisioningType,Zone,AllocationType,SessionSupport,TotalCount,HypervisorConnection,ProvisioningScheme,RemotePCEnrollmentScopes,IsPowerManaged,MinimumFunctionalLevel,IsRemotePC,Metadata,Scopes,AdminFolder") catalog, httpResp, err := util.ReadResource[*citrixorchestration.MachineCatalogDetailResponseModel](getMachineCatalogRequest, ctx, client, resp, "Machine Catalog", machineCatalogId) return catalog, httpResp, err diff --git a/internal/daas/machine_catalog/machine_catalog_data_source.go b/internal/daas/machine_catalog/machine_catalog_data_source.go index 0121fdc..d54ed97 100644 --- a/internal/daas/machine_catalog/machine_catalog_data_source.go +++ b/internal/daas/machine_catalog/machine_catalog_data_source.go @@ -61,12 +61,18 @@ func (d *MachineCatalogDataSource) Read(ctx context.Context, req datasource.Read } // Get refreshed machine catalog state from Orchestration - machineCatalogPath := strings.ReplaceAll(data.MachineCatalogFolderPath.ValueString(), "\\", "|") + data.Name.ValueString() + machineCatalogName := data.Name.ValueString() + machineCatalogPath := strings.ReplaceAll(data.MachineCatalogFolderPath.ValueString(), "\\", "|") + if machineCatalogPath != "" { + machineCatalogPath = machineCatalogPath + "|" + machineCatalogName + } else { + machineCatalogPath += machineCatalogName + } getMachineCatalogRequest := d.client.ApiClient.MachineCatalogsAPIsDAAS.MachineCatalogsGetMachineCatalog(ctx, machineCatalogPath).Fields("Id,Name,Description,ProvisioningType,Zone,AllocationType,SessionSupport,TotalCount,HypervisorConnection,ProvisioningScheme,RemotePCEnrollmentScopes,IsPowerManaged,MinimumFunctionalLevel,IsRemotePC") machineCatalog, httpResp, err := citrixdaasclient.AddRequestData(getMachineCatalogRequest, d.client).Execute() if err != nil { resp.Diagnostics.AddError( - "Error reading Machine Catalog "+data.Name.ValueString(), + "Error reading Machine Catalog "+machineCatalogName, "TransactionId: "+citrixdaasclient.GetTransactionIdFromHttpResponse(httpResp)+ "\nError message: "+util.ReadClientError(err), ) @@ -78,7 +84,7 @@ func (d *MachineCatalogDataSource) Read(ctx context.Context, req datasource.Read if err != nil { resp.Diagnostics.AddError( - "Error listing VDAs in Machine Catalog "+data.Name.ValueString(), + "Error listing VDAs in Machine Catalog "+machineCatalogName, "TransactionId: "+citrixdaasclient.GetTransactionIdFromHttpResponse(httpResp)+ "\nError message: "+util.ReadClientError(err), ) diff --git a/internal/daas/machine_catalog/machine_catalog_data_source_model.go b/internal/daas/machine_catalog/machine_catalog_data_source_model.go index 0625565..5497a0d 100644 --- a/internal/daas/machine_catalog/machine_catalog_data_source_model.go +++ b/internal/daas/machine_catalog/machine_catalog_data_source_model.go @@ -4,12 +4,16 @@ package machine_catalog import ( "context" + "regexp" + "strings" "github.com/citrix/citrix-daas-rest-go/citrixorchestration" "github.com/citrix/terraform-provider-citrix/internal/daas/vda" "github.com/citrix/terraform-provider-citrix/internal/util" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -38,6 +42,10 @@ func (MachineCatalogDataSourceModel) GetSchema() schema.Schema { "machine_catalog_folder_path": schema.StringAttribute{ Description: "The path to the folder in which the machine catalog is located.", Optional: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathWithBackslashRegex), "Admin Folder Path must not start or end with a backslash"), + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathSpecialCharactersRegex), "Admin Folder Path must not contain any of the following special characters: / ; : # . * ? = < > | [ ] ( ) { } \" ' ` ~ "), + }, }, "vdas": schema.ListNestedAttribute{ Description: "The VDAs associated with the machine catalog.", @@ -63,7 +71,7 @@ func (r MachineCatalogDataSourceModel) RefreshPropertyValues(ctx context.Context r.Name = types.StringValue(catalog.GetName()) adminFolder := catalog.GetAdminFolder() - adminFolderPath := adminFolder.GetName() + adminFolderPath := strings.TrimSuffix(adminFolder.GetName(), "\\") if adminFolderPath != "" { r.MachineCatalogFolderPath = types.StringValue(adminFolderPath) } else { diff --git a/internal/daas/machine_catalog/machine_catalog_resource.go b/internal/daas/machine_catalog/machine_catalog_resource.go index a4569ed..a7f948a 100644 --- a/internal/daas/machine_catalog/machine_catalog_resource.go +++ b/internal/daas/machine_catalog/machine_catalog_resource.go @@ -104,7 +104,13 @@ func (r *machineCatalogResource) Create(ctx context.Context, req resource.Create if err != nil { return } - machineCatalogPath := strings.ReplaceAll(plan.MachineCatalogFolderPath.ValueString(), "\\", "|") + plan.Name.ValueString() + machineCatalogName := plan.Name.ValueString() + machineCatalogPath := strings.ReplaceAll(plan.MachineCatalogFolderPath.ValueString(), "\\", "|") + if machineCatalogPath != "" { + machineCatalogPath = machineCatalogPath + "|" + machineCatalogName + } else { + machineCatalogPath += machineCatalogName + } setMachineCatalogTags(ctx, &resp.Diagnostics, r.client, machineCatalogPath, plan.Tags) diff --git a/internal/daas/machine_catalog/machine_catalog_resource_model.go b/internal/daas/machine_catalog/machine_catalog_resource_model.go index f9e7d8b..a70c96f 100644 --- a/internal/daas/machine_catalog/machine_catalog_resource_model.go +++ b/internal/daas/machine_catalog/machine_catalog_resource_model.go @@ -589,6 +589,10 @@ func (MachineCatalogResourceModel) GetSchema() schema.Schema { "machine_catalog_folder_path": schema.StringAttribute{ Description: "The path to the folder in which the machine catalog is located.", Optional: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathWithBackslashRegex), "Admin Folder Path must not start or end with a backslash"), + stringvalidator.RegexMatches(regexp.MustCompile(util.AdminFolderPathSpecialCharactersRegex), "Admin Folder Path must not contain any of the following special characters: / ; : # . * ? = < > | [ ] ( ) { } \" ' ` ~ "), + }, }, "tenants": schema.SetAttribute{ ElementType: types.StringType, @@ -679,20 +683,8 @@ func (r MachineCatalogResourceModel) RefreshPropertyValues(ctx context.Context, r.Tenants = util.RefreshTenantSet(ctx, diagnostics, catalog.GetTenants()) - if catalog.ProvisioningScheme == nil { - if attributesMap, err := util.AttributeMapFromObject(ProvisioningSchemeModel{}); err == nil { - r.ProvisioningScheme = types.ObjectNull(attributesMap) - } else { - diagnostics.AddWarning("Error when creating null ProvisioningSchemeModel", err.Error()) - } - return r - } - - // Provisioning Scheme Properties - r = r.updateCatalogWithProvScheme(ctx, diagnostics, client, catalog, connectionType, pluginId, provScheme) - adminFolder := catalog.GetAdminFolder() - adminFolderPath := adminFolder.GetName() + adminFolderPath := strings.TrimSuffix(adminFolder.GetName(), "\\") if adminFolderPath != "" { r.MachineCatalogFolderPath = types.StringValue(adminFolderPath) } else { @@ -709,6 +701,18 @@ func (r MachineCatalogResourceModel) RefreshPropertyValues(ctx context.Context, r.Tags = util.RefreshTagSet(ctx, diagnostics, tags) + if catalog.ProvisioningScheme == nil { + if attributesMap, err := util.AttributeMapFromObject(ProvisioningSchemeModel{}); err == nil { + r.ProvisioningScheme = types.ObjectNull(attributesMap) + } else { + diagnostics.AddWarning("Error when creating null ProvisioningSchemeModel", err.Error()) + } + return r + } + + // Provisioning Scheme Properties + r = r.updateCatalogWithProvScheme(ctx, diagnostics, client, catalog, connectionType, pluginId, provScheme) + return r } diff --git a/internal/daas/machine_catalog/machine_config.go b/internal/daas/machine_catalog/machine_config.go index edd3c80..4da75b5 100644 --- a/internal/daas/machine_catalog/machine_config.go +++ b/internal/daas/machine_catalog/machine_config.go @@ -1237,6 +1237,10 @@ func (mc *AzureMachineConfigModel) RefreshProperties(ctx context.Context, diagno if !isUseEphemeralOsDiskSet { mc.StorageType = types.StringValue(stringPair.GetValue()) } + case "StorageAccountType": + if !isUseEphemeralOsDiskSet { + mc.StorageType = types.StringValue(stringPair.GetValue()) + } case "UseManagedDisks": mc.UseManagedDisks = util.StringToTypeBool(stringPair.GetValue()) case "ResourceGroups": diff --git a/internal/examples/resources/citrix_wem_configuration_set/import.sh b/internal/examples/resources/citrix_wem_configuration_set/import.sh new file mode 100644 index 0000000..8d7826a --- /dev/null +++ b/internal/examples/resources/citrix_wem_configuration_set/import.sh @@ -0,0 +1,2 @@ +# WEM Configuration Set can be imported by specifying the ID +terraform import citrix_wem_configuration_set.example-config-set 0000 \ No newline at end of file diff --git a/internal/examples/resources/citrix_wem_configuration_set/resource.tf b/internal/examples/resources/citrix_wem_configuration_set/resource.tf new file mode 100644 index 0000000..c5da5d8 --- /dev/null +++ b/internal/examples/resources/citrix_wem_configuration_set/resource.tf @@ -0,0 +1,4 @@ +resource "citrix_wem_configuration_set" "example-config-set"{ + name = "example config set" + description = "example WEM configuration set" +} diff --git a/internal/examples/resources/citrix_wem_directory_object/import.sh b/internal/examples/resources/citrix_wem_directory_object/import.sh new file mode 100644 index 0000000..377b176 --- /dev/null +++ b/internal/examples/resources/citrix_wem_directory_object/import.sh @@ -0,0 +1,2 @@ +# WEM Directory Object can be imported by specifying the ID +terraform import citrix_wem_directory_object.example-directory-object 0000 diff --git a/internal/examples/resources/citrix_wem_directory_object/resource.tf b/internal/examples/resources/citrix_wem_directory_object/resource.tf new file mode 100644 index 0000000..9a0c88e --- /dev/null +++ b/internal/examples/resources/citrix_wem_directory_object/resource.tf @@ -0,0 +1,5 @@ +resource "citrix_wem_directory_object" "example-directory-object" { + configuration_set_id = citrix_wem_configuration_set.example-config-set.id + machine_catalog_id = citrix_machine_catalog.example-machine-catalog.id + enabled = true +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1ff2a00..9643c61 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -44,6 +44,9 @@ import ( "github.com/citrix/terraform-provider-citrix/internal/storefront/stf_store" "github.com/citrix/terraform-provider-citrix/internal/storefront/stf_webreceiver" + "github.com/citrix/terraform-provider-citrix/internal/wem/wem_machine_ad_object" + "github.com/citrix/terraform-provider-citrix/internal/wem/wem_site" + "github.com/citrix/terraform-provider-citrix/internal/daas/admin_folder" "github.com/citrix/terraform-provider-citrix/internal/daas/admin_scope" "github.com/citrix/terraform-provider-citrix/internal/daas/delivery_group" @@ -596,6 +599,21 @@ func validateAndInitializeDaaSClient(ctx context.Context, resp *provider.Configu cwsHostName = "cws.ctxwsstgapi.us" } + wemHostName := "" + if environment == "Production" { + wemHostName = "api.wem.cloud.com" + } else if environment == "Staging" { + wemHostName = "api.wem.cloudburrito.com" + } else if environment == "Japan" { + wemHostName = "api.wem.citrixcloud.jp" + } else if environment == "JapanStaging" { + wemHostName = "api.wem.citrixcloudstaging.jp" + } else if environment == "Gov" { + wemHostName = "api.wem.citrixworkspacesapi.us" + } else if environment == "GovStaging" { + wemHostName = "api.wem.ctxwsstgapi.us" + } + ctx = tflog.SetField(ctx, "citrix_hostname", hostname) if !onPremises { ctx = tflog.SetField(ctx, "citrix_customer_id", customerId) @@ -709,6 +727,10 @@ func validateAndInitializeDaaSClient(ctx context.Context, resp *provider.Configu if cwsHostName != "" { client.InitializeCwsClient(ctx, cwsHostName, middleware.MiddlewareAuthFunc) } + // Set WEM Client + if wemHostName != "" { + client.InitializeWemClient(ctx, wemHostName, middleware.MiddlewareAuthFunc) + } } // DataSources defines the data sources implemented in the provider. @@ -791,6 +813,9 @@ func (p *citrixProvider) Resources(_ context.Context) []func() resource.Resource cc_identity_providers.NewGoogleIdentityProviderResource, cc_identity_providers.NewOktaIdentityProviderResource, cc_identity_providers.NewSamlIdentityProviderResource, + // Wem Resources + wem_site.NewWemSiteServiceResource, + wem_machine_ad_object.NewWemDirectoryResource, // Add resource here } } diff --git a/internal/test/wem_site_service_resource_test.go b/internal/test/wem_site_service_resource_test.go new file mode 100644 index 0000000..bd90553 --- /dev/null +++ b/internal/test/wem_site_service_resource_test.go @@ -0,0 +1,92 @@ +// Copyright © 2024. Citrix Systems, Inc. + +package test + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +// TestWemSiteResourcePreCheck validates the necessary env variable exist in the testing environment +func TestWemSiteResourcePreCheck(t *testing.T) { + if v := os.Getenv("TEST_WEM_SITE_RESOURCE_NAME"); v == "" { + t.Fatal("TEST_WEM_SITE_RESOURCE_NAME must be set for acceptance tests") + } + + if v := os.Getenv("TEST_WEM_SITE_RESOURCE_DESCRIPTION"); v == "" { + t.Fatal("TEST_WEM_SITE_RESOURCE_DESCRIPTION must be set for acceptance tests") + } +} + +func TestWemSiteResource(t *testing.T) { + wemSiteName := os.Getenv("TEST_WEM_SITE_RESOURCE_NAME") + wemSiteDescription := os.Getenv("TEST_WEM_SITE_RESOURCE_DESCRIPTION") + + wemSiteName_Updated := os.Getenv("TEST_WEM_SITE_RESOURCE_NAME") + "-updated" + wemSiteDescription_Updated := os.Getenv("TEST_WEM_SITE_RESOURCE_DESCRIPTION") + " description updated" + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + PreCheck: func() { + TestProviderPreCheck(t) + TestWemSiteResourcePreCheck(t) + }, + Steps: []resource.TestStep{ + // Create and read test + { + Config: BuildWemSiteResource(t), + Check: resource.ComposeAggregateTestCheckFunc( + // Verify the name of the wem site + resource.TestCheckResourceAttr("citrix_wem_configuration_set.test_wem_site", "name", wemSiteName), + // Verify the description of wem site + resource.TestCheckResourceAttr("citrix_wem_configuration_set.test_wem_site", "description", wemSiteDescription), + ), + }, + // Import test + { + ResourceName: "citrix_wem_configuration_set.test_wem_site", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{}, + }, + // Update and Read test + { + Config: BuildWemSiteResource_Updated(t), + Check: resource.ComposeAggregateTestCheckFunc( + // Verify the id of the wem site + resource.TestCheckResourceAttrSet("citrix_wem_configuration_set.test_wem_site", "id"), + // Verify the name of the wem site + resource.TestCheckResourceAttr("citrix_wem_configuration_set.test_wem_site", "name", wemSiteName_Updated), + // Verify the description of wem site + resource.TestCheckResourceAttr("citrix_wem_configuration_set.test_wem_site", "description", wemSiteDescription_Updated), + ), + }, + }, + }) +} + +func BuildWemSiteResource(t *testing.T) string { + wemSiteName := os.Getenv("TEST_WEM_SITE_RESOURCE_NAME") + wemSiteDescription := os.Getenv("TEST_WEM_SITE_RESOURCE_DESCRIPTION") + + return fmt.Sprintf(wem_site_test_resource, wemSiteName, wemSiteDescription) +} + +func BuildWemSiteResource_Updated(t *testing.T) string { + wemSiteName := os.Getenv("TEST_WEM_SITE_RESOURCE_NAME") + "-updated" + wemSiteDescription := os.Getenv("TEST_WEM_SITE_RESOURCE_DESCRIPTION") + " description updated" + + return fmt.Sprintf(wem_site_test_resource, wemSiteName, wemSiteDescription) +} + +var ( + wem_site_test_resource = ` + resource "citrix_wem_configuration_set" "test_wem_site" { + name = "%s" + description = "%s" + } + ` +) diff --git a/internal/util/common.go b/internal/util/common.go index 097d528..f0ed317 100644 --- a/internal/util/common.go +++ b/internal/util/common.go @@ -129,6 +129,10 @@ const AppCategoryPathRegex string = `^([a-zA-Z0-9 ]+\\)*[a-zA-Z0-9 ]+\\?$` // SAML 2.0 Identity Provider Certificate REGEX const SamlIdpCertRegex string = `\.[Pp][Ee][Mm]$|\.[Cc][Rr][Tt]$|\.[Cc][Ee][Rr]$` +// Admin Folder Path +const AdminFolderPathWithBackslashRegex string = `^[^\\].*[^\\]$` +const AdminFolderPathSpecialCharactersRegex string = `^[^/;:#.*?=<>|[\](){}"'\` + "`~]+$" + // NOT_EXIST error code const NOT_EXIST string = "NOT_EXIST" diff --git a/internal/util/resource.go b/internal/util/resource.go index 77fb531..cdbde6a 100644 --- a/internal/util/resource.go +++ b/internal/util/resource.go @@ -47,7 +47,7 @@ func GetHypervisorResourcePool(ctx context.Context, client *citrixdaasclient.Cit } func GetMachineCatalog(ctx context.Context, client *citrixdaasclient.CitrixDaasClient, diagnostics *diag.Diagnostics, machineCatalogId string, addErrorToDiagnostics bool) (*citrixorchestration.MachineCatalogDetailResponseModel, error) { - getMachineCatalogRequest := client.ApiClient.MachineCatalogsAPIsDAAS.MachineCatalogsGetMachineCatalog(ctx, machineCatalogId).Fields("Id,Name,Description,ProvisioningType,Zone,AllocationType,SessionSupport,TotalCount,HypervisorConnection,ProvisioningScheme,RemotePCEnrollmentScopes,IsPowerManaged,MinimumFunctionalLevel,IsRemotePC,Metadata,Scopes") + getMachineCatalogRequest := client.ApiClient.MachineCatalogsAPIsDAAS.MachineCatalogsGetMachineCatalog(ctx, machineCatalogId).Fields("Id,Name,Description,ProvisioningType,Zone,AllocationType,SessionSupport,TotalCount,HypervisorConnection,ProvisioningScheme,RemotePCEnrollmentScopes,IsPowerManaged,MinimumFunctionalLevel,IsRemotePC,Metadata,Scopes,AdminScope") catalog, httpResp, err := citrixdaasclient.ExecuteWithRetry[*citrixorchestration.MachineCatalogDetailResponseModel](getMachineCatalogRequest, client) if err != nil && addErrorToDiagnostics { diagnostics.AddError( diff --git a/internal/wem/wem_machine_ad_object/wem_directory_object_resource.go b/internal/wem/wem_machine_ad_object/wem_directory_object_resource.go new file mode 100644 index 0000000..81bc69f --- /dev/null +++ b/internal/wem/wem_machine_ad_object/wem_directory_object_resource.go @@ -0,0 +1,258 @@ +package wem_machine_ad_object + +import ( + "context" + "strconv" + + citrixdaasclient "github.com/citrix/citrix-daas-rest-go/client" + citrixwemservice "github.com/citrix/citrix-daas-rest-go/devicemanagement" + "github.com/citrix/terraform-provider-citrix/internal/util" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &wemDirectoryResource{} + _ resource.ResourceWithConfigure = &wemDirectoryResource{} + _ resource.ResourceWithImportState = &wemDirectoryResource{} + _ resource.ResourceWithModifyPlan = &wemDirectoryResource{} +) + +// Define the wemMachineResource struct +type wemDirectoryResource struct { + client *citrixdaasclient.CitrixDaasClient +} + +// ModifyPlan implements resource.ResourceWithModifyPlan. +func (w *wemDirectoryResource) ModifyPlan(_ context.Context, _ resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + defer util.PanicHandler(&resp.Diagnostics) + + if w.client != nil && (w.client.ApiClient == nil || w.client.WemClient == nil) { + resp.Diagnostics.AddError(util.ProviderInitializationErrorMsg, util.MissingProviderClientIdAndSecretErrorMsg) + return + } + + if w.client.AuthConfig.OnPremises { + resp.Diagnostics.AddError("Error managing WEM Directory Object", "Directory Objects are only supported for Cloud customers.") + } +} + +// ImportState implements resource.ResourceWithImportState. +func (w *wemDirectoryResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +// Configure implements resource.ResourceWithConfigure. +func (w *wemDirectoryResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + w.client = req.ProviderData.(*citrixdaasclient.CitrixDaasClient) +} + +// Schema implements resource.Resource. +func (w *wemDirectoryResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = WemDirectoryResourceModel{}.GetSchema() +} + +// Metadata implements resource.Resource. +func (w *wemDirectoryResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_wem_directory_object" +} + +// NewWemDirectoryResource creates a new instance of the WemDirectoryResource. +func NewWemDirectoryResource() resource.Resource { + return &wemDirectoryResource{} +} + +// Create implements resource.Resource. +func (w *wemDirectoryResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + defer util.PanicHandler(&resp.Diagnostics) + + // Retrieve values from plan + var plan WemDirectoryResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Supporting only catalog as machine-level AD objects in WEM + machineCatalogId := plan.CatalogId.ValueString() + catalog, err := util.GetMachineCatalog(ctx, w.client, &diags, machineCatalogId, true) + if err != nil { + resp.Diagnostics.AddError( + "Error reading machine catalog", + "Could not read machine catalog with ID "+machineCatalogId+"\nError message: "+util.ReadClientError(err), + ) + return + } + + machineCatalogName := catalog.GetName() + + // Generate API request body from plan + var body citrixwemservice.MachineModel + body.SetSiteId(plan.SiteId.ValueInt64()) + body.SetSid(machineCatalogId) + body.SetName(machineCatalogName) + body.SetType("Catalog") + body.SetEnabled(plan.Enabled.ValueBool()) + body.SetPriority(1000) // reserved priority, currently not used in WEM API + + // Generate Create MAchine AD Object API request + machineADObjectCreateRequest := w.client.WemClient.MachineADObjectDAAS.AdObjectCreate(ctx) + machineADObjectCreateRequest = machineADObjectCreateRequest.Body(body) + httpResp, err := citrixdaasclient.AddRequestData(machineADObjectCreateRequest, w.client).Execute() + + // In case of error, add it to diagnostics and return + if err != nil { + resp.Diagnostics.AddError( + "Error binding "+machineCatalogName+" to WEM configuration set ID "+strconv.FormatInt(plan.SiteId.ValueInt64(), 10), + "TransactionId: "+citrixdaasclient.GetTransactionIdFromHttpResponse(httpResp)+"\nError message: "+util.ReadClientError(err), + ) + return + } + + // Get newly created machine AD object from remote + machineADObject, err := getMachineADObjectBySid(ctx, w.client, machineCatalogId) + if err != nil { + return + } + + plan = plan.RefreshPropertyValues(ctx, &resp.Diagnostics, &machineADObject) + + // Set state to fully populated data + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Delete implements resource.Resource. +func (w *wemDirectoryResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + defer util.PanicHandler(&resp.Diagnostics) + + // Get current state + var state WemDirectoryResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Generate Delete API request + machineADObjectDeleteRequest := w.client.WemClient.MachineADObjectDAAS.AdObjectDelete(ctx, state.Id.ValueString()) + httpResp, err := citrixdaasclient.AddRequestData(machineADObjectDeleteRequest, w.client).Execute() + + // In case of error, add it to diagnostics and return + if err != nil { + resp.Diagnostics.AddError( + "Error Deleting WEM Directory Object "+state.Id.String(), + "TransactionId: "+citrixdaasclient.GetTransactionIdFromHttpResponse(httpResp)+"\nError message: "+util.ReadClientError(err), + ) + return + } +} + +// Read implements resource.Resource. +func (w *wemDirectoryResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + defer util.PanicHandler(&resp.Diagnostics) + + // Get current state + var state WemDirectoryResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Get WEM Directory object by ID + machineADObject, err := getMachineADObjectById(ctx, w.client, state.Id.ValueString()) + if err != nil { + return + } + + state = state.RefreshPropertyValues(ctx, &resp.Diagnostics, machineADObject) + + // Set refreshed state + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Update implements resource.Resource. +func (w *wemDirectoryResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + defer util.PanicHandler(&resp.Diagnostics) + + // Retrieve values from plan + var plan WemDirectoryResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Supporting only catalog as machine-level AD objects in WEM + machineCatalogId := plan.CatalogId.ValueString() + catalog, err := util.GetMachineCatalog(ctx, w.client, &diags, machineCatalogId, true) + if err != nil { + resp.Diagnostics.AddError( + "Error reading machine catalog", + "Could not read machine catalog with ID "+machineCatalogId+"\nError message: "+util.ReadClientError(err), + ) + return + } + + machineCatalogName := catalog.GetName() + + // Generate API request body from plan + var body citrixwemservice.MachineModel + idInt64, err := strconv.ParseInt(plan.Id.ValueString(), 10, 64) + if err != nil { + resp.Diagnostics.AddError( + "Error converting ID to int64", + "Could not convert ID "+plan.Id.ValueString()+" to int64: "+err.Error(), + ) + return + } + body.SetId(idInt64) + body.SetSiteId(plan.SiteId.ValueInt64()) + body.SetSid(machineCatalogId) + body.SetName(machineCatalogName) + body.SetType("Catalog") + body.SetEnabled(plan.Enabled.ValueBool()) + body.SetPriority(1000) // reserved priority, currently not used in WEM API + + // Generate Update API request + machineADObjectUpdateRequest := w.client.WemClient.MachineADObjectDAAS.AdObjectUpdate(ctx) + machineADObjectUpdateRequest = machineADObjectUpdateRequest.Body(body) + httpResp, err := citrixdaasclient.AddRequestData(machineADObjectUpdateRequest, w.client).Execute() + + // In case of error, add it to diagnostics and return + if err != nil { + resp.Diagnostics.AddError( + "Error Updating WEM Directory Object with ID "+plan.Id.ValueString(), + "TransactionId: "+citrixdaasclient.GetTransactionIdFromHttpResponse(httpResp)+"\nError message: "+util.ReadClientError(err), + ) + return + } + + // Get Newly Updated Machine AD Object from remote by Id + machineADObject, err := getMachineADObjectById(ctx, w.client, plan.Id.ValueString()) + if err != nil { + return + } + + plan = plan.RefreshPropertyValues(ctx, &resp.Diagnostics, machineADObject) + + // Set state to fully populated data + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} diff --git a/internal/wem/wem_machine_ad_object/wem_directory_object_resource_model.go b/internal/wem/wem_machine_ad_object/wem_directory_object_resource_model.go new file mode 100644 index 0000000..3c65139 --- /dev/null +++ b/internal/wem/wem_machine_ad_object/wem_directory_object_resource_model.go @@ -0,0 +1,68 @@ +package wem_machine_ad_object + +import ( + "context" + "regexp" + "strconv" + + citrixwemservice "github.com/citrix/citrix-daas-rest-go/devicemanagement" + "github.com/citrix/terraform-provider-citrix/internal/util" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type WemDirectoryResourceModel struct { + Id types.String `tfsdk:"id"` + CatalogId types.String `tfsdk:"machine_catalog_id"` + SiteId types.Int64 `tfsdk:"configuration_set_id"` + Enabled types.Bool `tfsdk:"enabled"` +} + +func (WemDirectoryResourceModel) GetSchema() schema.Schema { + return schema.Schema{ + Description: "WEM --- Manages machine-level AD objects within a WEM deployment.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: "Identifier of the directory object.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "machine_catalog_id": schema.StringAttribute{ + Description: "GUID identifier of the machine catalog.", + Required: true, + Validators: []validator.String{ + validator.String( + stringvalidator.RegexMatches(regexp.MustCompile(util.GuidRegex), "must be specified with ID in GUID format"), + ), + }, + }, + "configuration_set_id": schema.Int64Attribute{ + Description: "Identifier of the site to which the machine-level AD object belongs.", + Required: true, + }, + "enabled": schema.BoolAttribute{ + Description: "Indicates whether the machine-level AD object is enabled.", + Required: true, + }, + }, + } +} + +func (WemDirectoryResourceModel) GetAttributes() map[string]schema.Attribute { + return WemDirectoryResourceModel{}.GetSchema().Attributes +} + +func (r WemDirectoryResourceModel) RefreshPropertyValues(ctx context.Context, diagnostics *diag.Diagnostics, wemSite *citrixwemservice.MachineModel) WemDirectoryResourceModel { + r.Id = types.StringValue(strconv.FormatInt(wemSite.GetId(), 10)) + r.CatalogId = types.StringValue(wemSite.GetSid()) + r.SiteId = types.Int64Value(wemSite.GetSiteId()) + r.Enabled = types.BoolValue(wemSite.GetEnabled()) + return r +} diff --git a/internal/wem/wem_machine_ad_object/wem_directory_object_utils.go b/internal/wem/wem_machine_ad_object/wem_directory_object_utils.go new file mode 100644 index 0000000..532cdda --- /dev/null +++ b/internal/wem/wem_machine_ad_object/wem_directory_object_utils.go @@ -0,0 +1,56 @@ +package wem_machine_ad_object + +import ( + "context" + "fmt" + "strconv" + + citrixdaasclient "github.com/citrix/citrix-daas-rest-go/client" + citrixwemservice "github.com/citrix/citrix-daas-rest-go/devicemanagement" + "github.com/citrix/terraform-provider-citrix/internal/util" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func getMachineADObjectBySid(ctx context.Context, client *citrixdaasclient.CitrixDaasClient, machineCatalogId string) (citrixwemservice.MachineModel, error) { + var resp *resource.ReadResponse + machineADObjectQueryRequest := client.WemClient.MachineADObjectDAAS.AdObjectQuery(ctx) + machineADObjectQueryRequest = machineADObjectQueryRequest.Sid(machineCatalogId) + machineADObjectQueryResponse, httpResp, err := util.ReadResource[*citrixwemservice.AdObjectQuery200Response](machineADObjectQueryRequest, ctx, client, resp, "Sid", machineCatalogId) + + var machineADObject citrixwemservice.MachineModel + machineADObjectList := machineADObjectQueryResponse.GetItems() + + if err != nil { + err = fmt.Errorf("TransactionId: " + citrixdaasclient.GetTransactionIdFromHttpResponse(httpResp) + "\nError message: " + util.ReadClientError(err)) + return machineADObject, err + } + + if len(machineADObjectList) != 0 { + machineADObject = machineADObjectList[0] + } + + if (machineADObject == citrixwemservice.MachineModel{}) { + return machineADObject, fmt.Errorf("WEM Directory object with SID " + machineCatalogId + " not found") + } + return machineADObject, nil +} + +func getMachineADObjectById(ctx context.Context, client *citrixdaasclient.CitrixDaasClient, machineADObjectId string) (*citrixwemservice.MachineModel, error) { + var resp *resource.ReadResponse + idInt64, err := strconv.ParseInt(machineADObjectId, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid WEM Directory object ID: %v", err) + } + machineADObjectQueryRequest := client.WemClient.MachineADObjectDAAS.AdObjectQueryById(ctx, idInt64) + machineADObjectQueryResponse, httpResp, err := util.ReadResource[*citrixwemservice.MachineModel](machineADObjectQueryRequest, ctx, client, resp, "Id", machineADObjectId) + + if err != nil { + err = fmt.Errorf("TransactionId: " + citrixdaasclient.GetTransactionIdFromHttpResponse(httpResp) + "\nError message: " + util.ReadClientError(err)) + return machineADObjectQueryResponse, err + } + + if machineADObjectQueryResponse == nil { + return machineADObjectQueryResponse, fmt.Errorf("wem directory object with ID " + machineADObjectId + " not found") + } + return machineADObjectQueryResponse, nil +} diff --git a/internal/wem/wem_site/wem_site_service_resource.go b/internal/wem/wem_site/wem_site_service_resource.go new file mode 100644 index 0000000..d413068 --- /dev/null +++ b/internal/wem/wem_site/wem_site_service_resource.go @@ -0,0 +1,240 @@ +// Copyright © 2024. Citrix Systems, Inc. + +package wem_site + +import ( + "context" + "strconv" + + citrixdaasclient "github.com/citrix/citrix-daas-rest-go/client" + citrixwemservice "github.com/citrix/citrix-daas-rest-go/devicemanagement" + "github.com/citrix/terraform-provider-citrix/internal/util" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &wemSiteServiceResource{} + _ resource.ResourceWithConfigure = &wemSiteServiceResource{} + _ resource.ResourceWithImportState = &wemSiteServiceResource{} + _ resource.ResourceWithModifyPlan = &wemSiteServiceResource{} +) + +// wemSiteServiceResource is the resource implementation. +type wemSiteServiceResource struct { + client *citrixdaasclient.CitrixDaasClient +} + +// ImportState implements resource.ResourceWithImportState. +func (w *wemSiteServiceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +// Metadata implements resource.Resource. +func (w *wemSiteServiceResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_wem_configuration_set" +} + +// Configure implements resource.ResourceWithConfigure. +func (w *wemSiteServiceResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + w.client = req.ProviderData.(*citrixdaasclient.CitrixDaasClient) +} + +// NewWemSiteServiceResource is a helper function to simplify the provider implementation. +func NewWemSiteServiceResource() resource.Resource { + return &wemSiteServiceResource{} +} + +// Schema implements resource.Resource. +func (w *wemSiteServiceResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = WemSiteResourceModel{}.GetSchema() +} + +// ModifyPlan implements resource.ResourceWithModifyPlan. +func (w *wemSiteServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + defer util.PanicHandler(&resp.Diagnostics) + + if w.client != nil && (w.client.ApiClient == nil || w.client.WemClient == nil) { + resp.Diagnostics.AddError(util.ProviderInitializationErrorMsg, util.MissingProviderClientIdAndSecretErrorMsg) + return + } + + if w.client.AuthConfig.OnPremises { + resp.Diagnostics.AddError("Error managing WEM Configuration Sets", "Configuration Sets are only supported for Cloud customers.") + } +} + +// Create implements resource.Resource. +func (w *wemSiteServiceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + defer util.PanicHandler(&resp.Diagnostics) + + // Retrieve values from plan + var plan WemSiteResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Generate API request body from plan + var body citrixwemservice.SiteModel + body.SetName(plan.Name.ValueString()) + body.SetDescription(plan.Description.ValueString()) + + // Generate Create API request + siteCreateRequest := w.client.WemClient.SiteDAAS.SiteCreate(ctx) + siteCreateRequest = siteCreateRequest.Body(body) + httpResp, err := citrixdaasclient.AddRequestData(siteCreateRequest, w.client).Execute() + + // In case of error, add it to diagnostics and return + if err != nil { + resp.Diagnostics.AddError( + "Error creating WEM site "+plan.Name.ValueString(), + "TransactionId: "+citrixdaasclient.GetTransactionIdFromHttpResponse(httpResp)+"\nError message: "+util.ReadClientError(err), + ) + return + } + + // Get Newly created site by name from remote (ID is not available yet) + siteConfig, err := getSiteByName(ctx, w.client, plan) + if err != nil { + resp.Diagnostics.AddError( + "Error fetching WEM site", + util.ReadClientError(err), + ) + return + } + + plan = plan.RefreshPropertyValues(ctx, &resp.Diagnostics, &siteConfig) + + // Set state to fully populated data + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Delete implements resource.Resource. +func (w *wemSiteServiceResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + defer util.PanicHandler(&resp.Diagnostics) + + // Get current state + var state WemSiteResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Convert site Id to int64 + siteId, err := strconv.ParseInt(state.Id.ValueString(), 10, 64) + if err != nil { + resp.Diagnostics.AddError( + "Error converting site Id to int64", + err.Error(), + ) + return + } + + // Generate Delete API request + siteDeleteRequest := w.client.WemClient.SiteDAAS.SiteDelete(ctx, siteId) + httpResp, err := citrixdaasclient.AddRequestData(siteDeleteRequest, w.client).Execute() + + // In case of error, add it to diagnostics and return + if err != nil { + resp.Diagnostics.AddError( + "Error Deleting WEM site "+state.Name.ValueString(), + "TransactionId: "+citrixdaasclient.GetTransactionIdFromHttpResponse(httpResp)+"\nError message: "+util.ReadClientError(err), + ) + return + } +} + +// Read implements resource.Resource. +func (w *wemSiteServiceResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + defer util.PanicHandler(&resp.Diagnostics) + + // Get current state + var state WemSiteResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Get site from remote using site Id + siteConfig, err := getSiteById(ctx, w.client, state) + if err != nil { + return + } + + state = state.RefreshPropertyValues(ctx, &resp.Diagnostics, siteConfig) + + // Set refreshed state + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Update implements resource.Resource. +func (w *wemSiteServiceResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + defer util.PanicHandler(&resp.Diagnostics) + + // Get plan values + var plan WemSiteResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Generate API request body from plan + var body citrixwemservice.SiteModel + siteId, err := strconv.ParseInt(plan.Id.ValueString(), 10, 64) + if err != nil { + resp.Diagnostics.AddError( + "Error converting site Id to int64", + err.Error(), + ) + return + } + body.SetId(siteId) + body.SetName(plan.Name.ValueString()) + body.SetDescription(plan.Description.ValueString()) + + // Generate Update API request + siteUpdateRequest := w.client.WemClient.SiteDAAS.SiteUpdate(ctx) + siteUpdateRequest = siteUpdateRequest.Body(body) + httpResp, err := citrixdaasclient.AddRequestData(siteUpdateRequest, w.client).Execute() + + // In case of error, add it to diagnostics and return + if err != nil { + resp.Diagnostics.AddError( + "Error Updating WEM site "+plan.Name.ValueString(), + "TransactionId: "+citrixdaasclient.GetTransactionIdFromHttpResponse(httpResp)+"\nError message: "+util.ReadClientError(err), + ) + return + } + + // Get Newly Updated site from remote by Id + siteConfig, err := getSiteById(ctx, w.client, plan) + if err != nil { + return + } + + plan = plan.RefreshPropertyValues(ctx, &resp.Diagnostics, siteConfig) + + // Set state to fully populated data + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} diff --git a/internal/wem/wem_site/wem_site_service_resource_model.go b/internal/wem/wem_site/wem_site_service_resource_model.go new file mode 100644 index 0000000..c68272e --- /dev/null +++ b/internal/wem/wem_site/wem_site_service_resource_model.go @@ -0,0 +1,62 @@ +// Copyright © 2024. Citrix Systems, Inc. +package wem_site + +import ( + "context" + "strconv" + + citrixwemservice "github.com/citrix/citrix-daas-rest-go/devicemanagement" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type WemSiteResourceModel struct { + Id types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` +} + +func (WemSiteResourceModel) GetSchema() schema.Schema { + return schema.Schema{ + Description: "WEM --- Manages configuration sets within a WEM deployment.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: "Identifier of the configuration site.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "name": schema.StringAttribute{ + Description: "Name of the configuration site. WEM Site Name should be unique.", + Required: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 64), + }, + }, + "description": schema.StringAttribute{ + Description: "Description of the configuration site. Default value is empty string.", + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), // Default value is empty string + }, + }, + } +} + +func (WemSiteResourceModel) GetAttributes() map[string]schema.Attribute { + return WemSiteResourceModel{}.GetSchema().Attributes +} + +func (r WemSiteResourceModel) RefreshPropertyValues(ctx context.Context, diagnostics *diag.Diagnostics, wemSite *citrixwemservice.SiteModel) WemSiteResourceModel { + r.Id = types.StringValue(strconv.FormatInt(wemSite.GetId(), 10)) + r.Name = types.StringValue(wemSite.GetName()) + r.Description = types.StringValue(wemSite.GetDescription()) + return r +} diff --git a/internal/wem/wem_site/wem_site_service_utils.go b/internal/wem/wem_site/wem_site_service_utils.go new file mode 100644 index 0000000..52181a0 --- /dev/null +++ b/internal/wem/wem_site/wem_site_service_utils.go @@ -0,0 +1,59 @@ +// Copyright © 2024. Citrix Systems, Inc. + +package wem_site + +import ( + "context" + "fmt" + "strconv" + + citrixdaasclient "github.com/citrix/citrix-daas-rest-go/client" + citrixwemservice "github.com/citrix/citrix-daas-rest-go/devicemanagement" + "github.com/citrix/terraform-provider-citrix/internal/util" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func getSiteByName(ctx context.Context, client *citrixdaasclient.CitrixDaasClient, wemResource WemSiteResourceModel) (citrixwemservice.SiteModel, error) { + var resp *resource.ReadResponse + siteName := wemResource.Name.ValueString() + siteGetRequest := client.WemClient.SiteDAAS.SiteQuery(ctx) + siteGetRequest = siteGetRequest.Name(siteName) + siteGetResponse, httpResp, err := util.ReadResource[*citrixwemservice.SiteQuery200Response](siteGetRequest, ctx, client, resp, "Name", siteName) + + siteConfigList := siteGetResponse.GetItems() + var siteConfig citrixwemservice.SiteModel + + if err != nil { + err = fmt.Errorf("TransactionId: " + citrixdaasclient.GetTransactionIdFromHttpResponse(httpResp) + "\nError message: " + util.ReadClientError(err)) + return siteConfig, err + } + + if len(siteConfigList) != 0 { + siteConfig = siteConfigList[0] + } + if siteConfig.Id == nil { + return siteConfig, fmt.Errorf("site with name %s not found", wemResource.Name.ValueString()) + } + return siteConfig, nil +} + +func getSiteById(ctx context.Context, client *citrixdaasclient.CitrixDaasClient, wemResource WemSiteResourceModel) (*citrixwemservice.SiteModel, error) { + var resp *resource.ReadResponse + siteId := wemResource.Id.ValueString() + idInt64, err := strconv.ParseInt(wemResource.Id.ValueString(), 10, 64) + if err != nil { + return &citrixwemservice.SiteModel{}, fmt.Errorf("invalid id: %v", err) + } + siteGetRequest := client.WemClient.SiteDAAS.SiteQueryById(ctx, idInt64) + siteGetResponse, httpResp, err := util.ReadResource[*citrixwemservice.SiteModel](siteGetRequest, ctx, client, resp, "Id", siteId) + + if err != nil { + err = fmt.Errorf("TransactionId: " + citrixdaasclient.GetTransactionIdFromHttpResponse(httpResp) + "\nError message: " + util.ReadClientError(err)) + return siteGetResponse, err + } + + if siteGetResponse == nil { + return nil, fmt.Errorf("site with name %s not found", wemResource.Name.ValueString()) + } + return siteGetResponse, nil +} diff --git a/settings.cloud.example.json b/settings.cloud.example.json index e6111a2..a101119 100644 --- a/settings.cloud.example.json +++ b/settings.cloud.example.json @@ -474,6 +474,10 @@ // Tag Resource Go Tests Env Variables "TEST_TAG_RESOURCE_NAME": "{Name of the tag resource}", "TEST_TAG_RESOURCE_DESCRIPTION": "{Description of the tag resource}" + + // Wem Resource Go Tests Env Variables + "TEST_WEM_SITE_RESOURCE_NAME": "{Name of the WEM Site Resource}", + "TEST_WEM_SITE_RESOURCE_DESCRIPTION": "{Description of the WEM Site Resource}", }, "go.testTimeout": "120m" } \ No newline at end of file