From c17e3b92ae296a87595b03be0bd74fb4adb8466f Mon Sep 17 00:00:00 2001 From: Zhuolun Liu Date: Thu, 9 Nov 2023 15:35:00 -0500 Subject: [PATCH] Support managed disk and snapshot for machine catalog. Allow specifying VDA resource group. Gracefully handle client init error. --- docs/resources/daas_machine_catalog.md | 5 +- go.mod | 18 ++--- go.sum | 38 +++++----- internal/daas/machine_catalog_resource.go | 75 +++++++++++++++---- .../models/machine_catalog_resource_model.go | 18 ++++- internal/provider/provider.go | 60 ++++++++++++--- 6 files changed, 156 insertions(+), 58 deletions(-) diff --git a/docs/resources/daas_machine_catalog.md b/docs/resources/daas_machine_catalog.md index 5c00e4a..eed751e 100644 --- a/docs/resources/daas_machine_catalog.md +++ b/docs/resources/daas_machine_catalog.md @@ -93,6 +93,7 @@ Optional: - `availability_zones` (String) The Azure Availability Zones containing provisioned virtual machines. Use a comma as a delimiter for multiple availability_zones. - `network_mapping` (Attributes) Specifies how the attached NICs are mapped to networks. If this parameter is omitted, provisioned VMs are created with a single NIC, which is mapped to the default network in the hypervisor resource pool. If this parameter is supplied, machines are created with the number of NICs specified in the map, and each NIC is attached to the specified network. (see [below for nested schema](#nestedatt--provisioning_scheme--network_mapping)) +- `vda_resource_group` (String) Designated resource group where the VDA VMs will be located on Azure. - `writeback_cache` (Attributes) Write-back Cache config. Leave this empty to disable Write-back Cache. (see [below for nested schema](#nestedatt--provisioning_scheme--writeback_cache)) @@ -119,13 +120,13 @@ Required: Optional: -- `container` (String) The Azure Storage Account Container where the image VHD for creating machines is located. Only applicable to Azure Hypervisor. +- `container` (String) The Azure Storage Account Container where the image VHD for creating machines is located. Only applicable to Azure VHD image blob. - `image_ami` (String) AMI of the AWS image to be used as the template image for the machine catalog. Only applicable to AWS Hypervisor. - `machine_profile` (String) The name of the virtual machine template that will be used to identify the default value for the tags, virtual machine size, boot diagnostics, host cache property of OS disk, accelerated networking and availability zone. Only applicable to GCP Hypervisor. - `master_image` (String) The name of the virtual machine snapshot or VM template that will be used. This identifies the hard disk to be used and the default values for the memory and processors. - `resource_group` (String) The Azure Resource Group where the image VHD for creating machines is located. Only applicable to Azure Hypervisor. - `service_offering` (String) The VM Sku of a Cloud service offering to use when creating machines. -- `storage_account` (String) The Azure Storage Account where the image VHD for creating machines is located. Only applicable to Azure Hypervisor. +- `storage_account` (String) The Azure Storage Account where the image VHD for creating machines is located. Only applicable to Azure VHD image blob. diff --git a/go.mod b/go.mod index 6766e11..2d94d81 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/citrix/terraform-provider-citrix go 1.20 require ( - github.com/citrix/citrix-daas-rest-go v0.2.3 + github.com/citrix/citrix-daas-rest-go v0.2.4 github.com/google/uuid v1.4.0 github.com/hashicorp/terraform-plugin-docs v0.14.1 github.com/hashicorp/terraform-plugin-framework v1.4.2 @@ -11,7 +11,7 @@ require ( github.com/hashicorp/terraform-plugin-go v0.19.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-testing v1.2.0 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d + golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 ) require ( @@ -24,7 +24,7 @@ require ( github.com/armon/go-radix v1.0.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/cloudflare/circl v1.3.3 // indirect - github.com/fatih/color v1.15.0 // indirect + github.com/fatih/color v1.16.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -64,13 +64,13 @@ require ( github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.14.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/go.sum b/go.sum index 6078712..2b8f5b6 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,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/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/citrix/citrix-daas-rest-go v0.2.3 h1:0Qui4TXvCWuPFU8/C+m8eW4zPc9cDNjoS0S+eds6H4A= -github.com/citrix/citrix-daas-rest-go v0.2.3/go.mod h1:wObnH2H4QP/nwoKR589SzQZ5dGTu3AoVi927NN/e77s= +github.com/citrix/citrix-daas-rest-go v0.2.4 h1:IR7Aq3OzyvBiNHLxt/Zsl9accI6NGXO211jIJ5x3PT0= +github.com/citrix/citrix-daas-rest-go v0.2.4/go.mod h1:wObnH2H4QP/nwoKR589SzQZ5dGTu3AoVi927NN/e77s= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -32,8 +32,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= @@ -188,14 +188,14 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -204,8 +204,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -226,8 +226,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -240,20 +240,20 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= diff --git a/internal/daas/machine_catalog_resource.go b/internal/daas/machine_catalog_resource.go index 57b71dc..5fb0704 100644 --- a/internal/daas/machine_catalog_resource.go +++ b/internal/daas/machine_catalog_resource.go @@ -141,12 +141,28 @@ func (r *machineCatalogResource) Schema(_ context.Context, _ resource.SchemaRequ Optional: true, }, "storage_account": schema.StringAttribute{ - Description: "The Azure Storage Account where the image VHD for creating machines is located. Only applicable to Azure Hypervisor.", + Description: "The Azure Storage Account where the image VHD for creating machines is located. Only applicable to Azure VHD image blob.", Optional: true, + Validators: []validator.String{ + stringvalidator.AlsoRequires(path.Expressions{ + path.MatchRelative().AtParent().AtName("container"), + }...), + stringvalidator.AlsoRequires(path.Expressions{ + path.MatchRelative().AtParent().AtName("resource_group"), + }...), + }, }, "container": schema.StringAttribute{ - Description: "The Azure Storage Account Container where the image VHD for creating machines is located. Only applicable to Azure Hypervisor.", + Description: "The Azure Storage Account Container where the image VHD for creating machines is located. Only applicable to Azure VHD image blob.", Optional: true, + Validators: []validator.String{ + stringvalidator.AlsoRequires(path.Expressions{ + path.MatchRelative().AtParent().AtName("storage_account"), + }...), + stringvalidator.AlsoRequires(path.Expressions{ + path.MatchRelative().AtParent().AtName("resource_group"), + }...), + }, }, "image_ami": schema.StringAttribute{ Description: "AMI of the AWS image to be used as the template image for the machine catalog. Only applicable to AWS Hypervisor.", @@ -230,6 +246,13 @@ func (r *machineCatalogResource) Schema(_ context.Context, _ resource.SchemaRequ ), }, }, + "vda_resource_group": schema.StringAttribute{ + Description: "Designated resource group where the VDA VMs will be located on Azure.", + Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, "use_managed_disks": schema.BoolAttribute{ Description: "Indicate whether to use Azure managed disks for the provisioned virtual machine.", Required: true, @@ -369,12 +392,24 @@ func (r *machineCatalogResource) Create(ctx context.Context, req resource.Create serviceOfferingPath := util.GetSingleResourcePathFromHypervisor(ctx, r.client, hypervisor.GetName(), hypervisorResourcePool.GetName(), queryPath, serviceOffering, "serviceoffering", "") provisioningScheme.SetServiceOfferingPath(serviceOfferingPath) - queryPath = fmt.Sprintf( - "image.folder\\%s.resourcegroup\\%s.storageaccount\\%s.container", - plan.ProvisioningScheme.MachineConfig.ResourceGroup.ValueString(), - plan.ProvisioningScheme.MachineConfig.StorageAccount.ValueString(), - plan.ProvisioningScheme.MachineConfig.Container.ValueString()) - imagePath := util.GetSingleResourcePathFromHypervisor(ctx, r.client, hypervisor.GetName(), hypervisorResourcePool.GetName(), queryPath, masterImage, "", "") + resourceGroup := plan.ProvisioningScheme.MachineConfig.ResourceGroup.ValueString() + storageAccount := plan.ProvisioningScheme.MachineConfig.StorageAccount.ValueString() + container := plan.ProvisioningScheme.MachineConfig.Container.ValueString() + imagePath := "" + if storageAccount != "" && container != "" { + queryPath = fmt.Sprintf( + "image.folder\\%s.resourcegroup\\%s.storageaccount\\%s.container", + resourceGroup, + storageAccount, + container) + imagePath = util.GetSingleResourcePathFromHypervisor(ctx, r.client, hypervisor.GetName(), hypervisorResourcePool.GetName(), queryPath, masterImage, "", "") + } else { + queryPath = fmt.Sprintf( + "image.folder\\%s.resourcegroup", + resourceGroup) + imagePath = util.GetSingleResourcePathFromHypervisor(ctx, r.client, hypervisor.GetName(), hypervisorResourcePool.GetName(), queryPath, masterImage, "", "") + } + provisioningScheme.SetMasterImagePath(imagePath) case citrixorchestration.HYPERVISORCONNECTIONTYPE_AWS: serviceOfferingPath := util.GetSingleResourcePathFromHypervisor(ctx, r.client, hypervisor.GetName(), hypervisorResourcePool.GetName(), "", serviceOffering, "serviceoffering", "") @@ -607,7 +642,6 @@ func (r *machineCatalogResource) Update(ctx context.Context, req resource.Update queryPath := "serviceoffering.folder" serviceOfferingPath := util.GetSingleResourcePathFromHypervisor(ctx, r.client, hypervisor.GetName(), hypervisorResourcePool.GetName(), queryPath, serviceOffering, "folder", "") body.SetServiceOfferingPath(serviceOfferingPath) - case citrixorchestration.HYPERVISORCONNECTIONTYPE_AWS: serviceOfferingPath := util.GetSingleResourcePathFromHypervisor(ctx, r.client, hypervisor.GetName(), hypervisorResourcePool.GetName(), "", serviceOffering, "serviceoffering", "") body.SetServiceOfferingPath(serviceOfferingPath) @@ -654,12 +688,23 @@ func (r *machineCatalogResource) Update(ctx context.Context, req resource.Update switch hypervisor.GetConnectionType() { case citrixorchestration.HYPERVISORCONNECTIONTYPE_AZURE_RM: - queryPath := fmt.Sprintf( - "image.folder\\%s.resourcegroup\\%s.storageaccount\\%s.container", - plan.ProvisioningScheme.MachineConfig.ResourceGroup.ValueString(), - plan.ProvisioningScheme.MachineConfig.StorageAccount.ValueString(), - plan.ProvisioningScheme.MachineConfig.Container.ValueString()) - imagePath = util.GetSingleResourcePathFromHypervisor(ctx, r.client, hypervisor.GetName(), hypervisorResourcePool.GetName(), queryPath, plan.ProvisioningScheme.MachineConfig.MasterImage.ValueString(), "", "") + newImage := plan.ProvisioningScheme.MachineConfig.MasterImage.ValueString() + resourceGroup := plan.ProvisioningScheme.MachineConfig.ResourceGroup.ValueString() + storageAccount := plan.ProvisioningScheme.MachineConfig.StorageAccount.ValueString() + container := plan.ProvisioningScheme.MachineConfig.Container.ValueString() + if storageAccount != "" && container != "" { + queryPath := fmt.Sprintf( + "image.folder\\%s.resourcegroup\\%s.storageaccount\\%s.container", + resourceGroup, + storageAccount, + container) + imagePath = util.GetSingleResourcePathFromHypervisor(ctx, r.client, hypervisor.GetName(), hypervisorResourcePool.GetName(), queryPath, newImage, "", "") + } else { + queryPath := fmt.Sprintf( + "image.folder\\%s.resourcegroup", + resourceGroup) + imagePath = util.GetSingleResourcePathFromHypervisor(ctx, r.client, hypervisor.GetName(), hypervisorResourcePool.GetName(), queryPath, newImage, "", "") + } case citrixorchestration.HYPERVISORCONNECTIONTYPE_AWS: imageId := fmt.Sprintf("%s (%s)", plan.ProvisioningScheme.MachineConfig.MasterImage.ValueString(), plan.ProvisioningScheme.MachineConfig.ImageAmi.ValueString()) imagePath = util.GetSingleResourcePathFromHypervisor(ctx, r.client, hypervisor.GetName(), hypervisorResourcePool.GetName(), "", imageId, "template", "") diff --git a/internal/daas/models/machine_catalog_resource_model.go b/internal/daas/models/machine_catalog_resource_model.go index fb558ac..c9380ce 100644 --- a/internal/daas/models/machine_catalog_resource_model.go +++ b/internal/daas/models/machine_catalog_resource_model.go @@ -37,6 +37,7 @@ type ProvisioningSchemeModel struct { MachineAccountCreationRules *MachineAccountCreationRulesModel `tfsdk:"machine_account_creation_rules"` AvailabilityZones types.String `tfsdk:"availability_zones"` StorageType types.String `tfsdk:"storage_type"` + VdaResourceGroup types.String `tfsdk:"vda_resource_group"` UseManagedDisks types.Bool `tfsdk:"use_managed_disks"` WritebackCache *WritebackCacheModel `tfsdk:"writeback_cache"` } @@ -138,9 +139,15 @@ func (r MachineCatalogResourceModel) RefreshPropertyValues(ctx context.Context, if masterImageXdPath != "" { segments := strings.Split(masterImage.GetXDPath(), "\\") lastIndex := len(segments) - r.ProvisioningScheme.MachineConfig.Container = types.StringValue(strings.Split(segments[lastIndex-2], ".")[0]) - r.ProvisioningScheme.MachineConfig.StorageAccount = types.StringValue(strings.Split(segments[lastIndex-3], ".")[0]) - r.ProvisioningScheme.MachineConfig.ResourceGroup = types.StringValue(strings.Split(segments[lastIndex-4], ".")[0]) + if lastIndex == 8 { + // VHD image + r.ProvisioningScheme.MachineConfig.Container = types.StringValue(strings.Split(segments[lastIndex-2], ".")[0]) + r.ProvisioningScheme.MachineConfig.StorageAccount = types.StringValue(strings.Split(segments[lastIndex-3], ".")[0]) + r.ProvisioningScheme.MachineConfig.ResourceGroup = types.StringValue(strings.Split(segments[lastIndex-4], ".")[0]) + } else { + // Snapshot or Managed Disk + r.ProvisioningScheme.MachineConfig.ResourceGroup = types.StringValue(strings.Split(segments[lastIndex-2], ".")[0]) + } } } @@ -215,6 +222,8 @@ func (res *ProvisioningSchemeModel) RefreshProperties(stringPairs []citrixorches res.UseManagedDisks = util.StringToTypeBool(stringPair.GetValue()) case "Zones": res.AvailabilityZones = types.StringValue(stringPair.GetValue()) + case "ResourceGroups": + res.VdaResourceGroup = types.StringValue(stringPair.GetValue()) case "WBCDiskStorageType": if res.WritebackCache == nil { res.WritebackCache = &WritebackCacheModel{WBCDiskStorageType: types.StringValue(stringPair.GetValue())} @@ -255,6 +264,9 @@ func ParseCustomPropertiesToClientModel(provisioningScheme ProvisioningSchemeMod if !provisioningScheme.StorageType.IsNull() { util.AppendNameValueStringPair(res, "StorageType", provisioningScheme.StorageType.ValueString()) } + if !provisioningScheme.VdaResourceGroup.IsNull() { + util.AppendNameValueStringPair(res, "ResourceGroups", provisioningScheme.VdaResourceGroup.ValueString()) + } if provisioningScheme.UseManagedDisks.ValueBool() { util.AppendNameValueStringPair(res, "UseManagedDisks", "true") } else { diff --git a/internal/provider/provider.go b/internal/provider/provider.go index c9de2c0..cca65dc 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -246,6 +246,10 @@ func (p *citrixProvider) Configure(ctx context.Context, req provider.ConfigureRe disableSslVerification = config.DisableSslVerification.ValueBool() } + if environment == "" { + environment = "Production" // default to production + } + if customerId == "" { customerId = "CitrixOnPremises" } @@ -346,9 +350,28 @@ func (p *citrixProvider) Configure(ctx context.Context, req provider.ConfigureRe if err != nil { if httpResp != nil { if httpResp.StatusCode == 401 { + resp.Diagnostics.AddError( + "Invalid credential in provider config", + "Make sure client_id and client_secret is correct in provider config. ", + ) + } else if httpResp.StatusCode == 503 { + if onPremise { + resp.Diagnostics.AddError( + "Citrix DaaS service unavailable", + "Please check if you can access Web Studio. \n\n"+ + "Please ensure that Citrix Orchestration Service on the target DDC(s) are running reachable from this Machine.", + ) + } else { + resp.Diagnostics.AddError( + "Citrix DaaS service unavailable", + "The DDC(s) for the customer cannot be reached. Please check if you can access DaaS UI.", + ) + } + } else { resp.Diagnostics.AddError( "Unable to Create Citrix API Client", - "The provider credential is invalid. Make sure client_id and client_secret is correct in provider config. ", + "An unexpected error occurred when creating the Citrix API client. \n\n"+ + "Error: "+err.Error(), ) } } else { @@ -358,24 +381,41 @@ func (p *citrixProvider) Configure(ctx context.Context, req provider.ConfigureRe syscallErr := new(os.SyscallError) if errors.As(err, &urlErr) && errors.As(urlErr.Err, &opErr) && errors.As(opErr.Err, &syscallErr) && syscallErr.Err == syscall.Errno(10060) { resp.Diagnostics.AddError( - "Unable to Create Citrix API Client", - "An unexpected error occurred when creating the Citrix API client. \n\n"+ - "Error: DDC(s) cannot be reached. \n\n"+ - "Ensure that the DDC(s) are running. Make sure this machine has proper network routing to reach the DDC(s) and is not blocked by any firewall rules.", + "DDC(s) cannot be reached", + "Ensure that the DDC(s) are running. Make sure this machine has proper network routing to reach the DDC(s) and is not blocked by any firewall rules.", ) + + return } - // Case 2: Invalid Certificate + // Case 2: Invalid certificate cryptoErr := new(tls.CertificateVerificationError) errors.As(urlErr.Err, &cryptoErr) if len(cryptoErr.UnverifiedCertificates) > 0 { resp.Diagnostics.AddError( - "Unable to Create Citrix API Client", - "An unexpected error occurred when creating the Citrix API client. \n\n"+ - "Error: DDC(s) does not have a valid SSL certificate issued by a trusted Certificate Authority. \n\n"+ - "If you are running against on-premise DDC(s) that does not have an SSL certificate issue by a trusted CA, consider setting \"disable_ssl_verification\" to \"true\" in provider config.", + "DDC(s) does not have a valid SSL certificate issued by a trusted Certificate Authority", + "If you are running against on-premise DDC(s) that does not have an SSL certificate issue by a trusted CA, consider setting \"disable_ssl_verification\" to \"true\" in provider config.", + ) + + return + } + + // Case 3: Malformed hostname + if urlErr != nil && opErr.Err == nil { + resp.Diagnostics.AddError( + "Invalid DDC(s) hostname", + "Please revise the hostname in provider config and make sure it is a valid hostname or IP address.", ) + + return } + + // Case 4: Catch all other errors + resp.Diagnostics.AddError( + "Unable to Create Citrix API Client", + "An unexpected error occurred when creating the Citrix API client. \n\n"+ + "Error: "+err.Error(), + ) } return