From 59598cf544072a58d6de7fb62c861ee0825094b7 Mon Sep 17 00:00:00 2001 From: Dan Petersen Date: Mon, 9 Dec 2019 22:38:28 +0100 Subject: [PATCH] Initial work on user resource --- CHANGELOG.md | 1 + Makefile | 3 + README.md | 67 ++-- data_source_virtual_environment_user.go | 4 +- data_source_virtual_environment_users.go | 4 +- .../data_source_virtual_environment_user.tf | 2 +- example/resource_virtual_environment_group.tf | 2 +- example/resource_virtual_environment_user.tf | 46 +++ provider.go | 1 + proxmox/virtual_environment.go | 2 +- proxmox/virtual_environment_users.go | 75 +++-- resource_virtual_environment_group.go | 1 + resource_virtual_environment_role.go | 1 + resource_virtual_environment_user.go | 317 ++++++++++++++++++ resource_virtual_environment_user_test.go | 35 ++ 15 files changed, 510 insertions(+), 51 deletions(-) create mode 100644 example/resource_virtual_environment_user.tf create mode 100644 resource_virtual_environment_user.go create mode 100644 resource_virtual_environment_user_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 316336aa..fa29836f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,3 +11,4 @@ FEATURES: * **New Data Source:** `proxmox_virtual_environment_version` * **New Resource:** `proxmox_virtual_environment_group` * **New Resource:** `proxmox_virtual_environment_role` +* **New Resource:** `proxmox_virtual_environment_user` diff --git a/Makefile b/Makefile index 4fcea4ac..25fbbe5d 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,9 @@ example-init: go build -o "example/$(NAME)_v$(VERSION)-custom_x4" cd ./example && terraform init +example-plan: + cd ./example && terraform plan + fmt: gofmt -w $(GOFMT_FILES) diff --git a/README.md b/README.md index 625e2181..8441d115 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,11 @@ If you're building the provider, follow the instructions to [install it as a plu ### Configuration #### Arguments -* `virtual_environment` - (Optional) This is the configuration block for the Proxmox Virtual Environment. - * `endpoint` - (Required) The endpoint for the Proxmox Virtual Environment API. - * `insecure` - (Optional) Whether to skip the TLS verification step. Defaults to `false`. - * `password` - (Required) The password for the Proxmox Virtual Environment API. - * `username` - (Required) The username for the Proxmox Virtual Environment API. +* `virtual_environment` - (Optional) This is the configuration block for the Proxmox Virtual Environment + * `endpoint` - (Required) The endpoint for the Proxmox Virtual Environment API + * `insecure` - (Optional) Whether to skip the TLS verification step (defaults to `false`) + * `password` - (Required) The password for the Proxmox Virtual Environment API + * `username` - (Required) The username for the Proxmox Virtual Environment API ### Data Sources @@ -40,11 +40,11 @@ If you're building the provider, follow the instructions to [install it as a plu ##### Group (proxmox_virtual_environment_group) ###### Arguments -* `group_id` - (Required) The group id. +* `group_id` - (Required) The group id ###### Attributes -* `comment` - The group comment. -* `members` - The group members as a list with `username@realm` entries. +* `comment` - The group comment +* `members` - The group members as a list with `username@realm` entries ##### Groups (proxmox_virtual_environment_groups) @@ -52,16 +52,16 @@ If you're building the provider, follow the instructions to [install it as a plu This data source doesn't accept arguments. ###### Attributes -* `comments` - The group comments. -* `group_ids` - The group ids. +* `comments` - The group comments +* `group_ids` - The group ids ##### Role (proxmox_virtual_environment_role) ###### Arguments -* `role_id` - (Required) The role id. +* `role_id` - (Required) The role id ###### Attributes -* `privileges` - The role privileges. +* `privileges` - The role privileges ##### Roles (proxmox_virtual_environment_roles) @@ -69,9 +69,9 @@ This data source doesn't accept arguments. This data source doesn't accept arguments. ###### Attributes -* `privileges` - The role privileges. -* `role_ids` - The role ids. -* `special` - Whether the role is special (built-in). +* `privileges` - The role privileges +* `role_ids` - The role ids +* `special` - Whether the role is special (built-in) ##### User (proxmox_virtual_environment_user) @@ -102,7 +102,7 @@ This data source doesn't accept arguments. * `groups` - The users' groups * `keys` - The users' keys * `last_names` - The users' last names -* `user_ids` - The user ids. +* `user_ids` - The user ids ##### Version (proxmox_virtual_environment_version) @@ -110,10 +110,10 @@ This data source doesn't accept arguments. This data source doesn't accept arguments. ###### Attributes -* `keyboard` - The keyboard layout. -* `release` - The release number. -* `repository_id` - The repository id. -* `version` - The version string. +* `keyboard` - The keyboard layout +* `release` - The release number +* `repository_id` - The repository id +* `version` - The version string ### Resources @@ -122,17 +122,34 @@ This data source doesn't accept arguments. ##### Group (proxmox_virtual_environment_group) ###### Arguments -* `comment` - (Optional) The group comment. -* `group_id` - (Required) The group id. +* `comment` - (Optional) The group comment +* `group_id` - (Required) The group id ###### Attributes -* `members` - The group members as a list with `username@realm` entries. +* `members` - The group members as a list with `username@realm` entries ##### Role (proxmox_virtual_environment_role) ###### Arguments -* `privileges` - (Required) The role privileges. -* `role_id` - (Required) The role id. +* `privileges` - (Required) The role privileges +* `role_id` - (Required) The role id + +###### Attributes +This resource doesn't expose any additional attributes. + +##### User (proxmox_virtual_environment_user) + +###### Arguments +* `comment` - (Optional) The user comment +* `email` - (Optional) The user's email address +* `enabled` - (Optional) Whether the user account is enabled +* `expiration_date` - (Optional) The user account's expiration date +* `first_name` - (Optional) The user's first name +* `groups` - (Optional) The user's groups +* `keys` - (Optional) The user's keys +* `last_name` - (Optional) The user's last name +* `password` - (Required) The user's password +* `user_id` - (Required) The user id ###### Attributes This resource doesn't expose any additional attributes. diff --git a/data_source_virtual_environment_user.go b/data_source_virtual_environment_user.go index 916c34ab..67a048cc 100644 --- a/data_source_virtual_environment_user.go +++ b/data_source_virtual_environment_user.go @@ -117,10 +117,10 @@ func dataSourceVirtualEnvironmentUserRead(d *schema.ResourceData, m interface{}) if t.Unix() > 0 { d.Set(mkDataSourceVirtualEnvironmentUserExpirationDate, t.UTC().Format(time.RFC3339)) } else { - d.Set(mkDataSourceVirtualEnvironmentUserExpirationDate, time.Unix(2147483647, 0).UTC().Format(time.RFC3339)) + d.Set(mkDataSourceVirtualEnvironmentUserExpirationDate, time.Unix(0, 0).UTC().Format(time.RFC3339)) } } else { - d.Set(mkDataSourceVirtualEnvironmentUserExpirationDate, time.Unix(2147483647, 0).UTC().Format(time.RFC3339)) + d.Set(mkDataSourceVirtualEnvironmentUserExpirationDate, time.Unix(0, 0).UTC().Format(time.RFC3339)) } if v.FirstName != nil { diff --git a/data_source_virtual_environment_users.go b/data_source_virtual_environment_users.go index e156576a..cec1dde4 100644 --- a/data_source_virtual_environment_users.go +++ b/data_source_virtual_environment_users.go @@ -136,10 +136,10 @@ func dataSourceVirtualEnvironmentUsersRead(d *schema.ResourceData, m interface{} if t.Unix() > 0 { expirationDates[i] = t.UTC().Format(time.RFC3339) } else { - expirationDates[i] = time.Unix(2147483647, 0).UTC().Format(time.RFC3339) + expirationDates[i] = time.Unix(0, 0).UTC().Format(time.RFC3339) } } else { - expirationDates[i] = time.Unix(2147483647, 0).UTC().Format(time.RFC3339) + expirationDates[i] = time.Unix(0, 0).UTC().Format(time.RFC3339) } if v.FirstName != nil { diff --git a/example/data_source_virtual_environment_user.tf b/example/data_source_virtual_environment_user.tf index 9df34935..0c3e6849 100644 --- a/example/data_source_virtual_environment_user.tf +++ b/example/data_source_virtual_environment_user.tf @@ -1,5 +1,5 @@ data "proxmox_virtual_environment_user" "example" { - user_id = "root@pam" + user_id = "${proxmox_virtual_environment_user.example.id}" } output "data_proxmox_virtual_environment_user_example_comment" { diff --git a/example/resource_virtual_environment_group.tf b/example/resource_virtual_environment_group.tf index 3f86c3ee..5084c2f3 100644 --- a/example/resource_virtual_environment_group.tf +++ b/example/resource_virtual_environment_group.tf @@ -1,5 +1,5 @@ resource "proxmox_virtual_environment_group" "example" { - comment = "Created by Terraform" + comment = "Managed by Terraform" group_id = "terraform-provider-proxmox-example" } diff --git a/example/resource_virtual_environment_user.tf b/example/resource_virtual_environment_user.tf new file mode 100644 index 00000000..6129580a --- /dev/null +++ b/example/resource_virtual_environment_user.tf @@ -0,0 +1,46 @@ +resource "proxmox_virtual_environment_user" "example" { + comment = "Managed by Terraform" + password = "Test1234!" + user_id = "terraform-provider-proxmox-example@pve" +} + +output "resource_proxmox_virtual_environment_user_example_comment" { + value = "${proxmox_virtual_environment_user.example.comment}" +} + +output "resource_proxmox_virtual_environment_user_example_email" { + value = "${proxmox_virtual_environment_user.example.email}" +} + +output "resource_proxmox_virtual_environment_user_example_enabled" { + value = "${proxmox_virtual_environment_user.example.enabled}" +} + +output "resource_proxmox_virtual_environment_user_example_expiration_date" { + value = "${proxmox_virtual_environment_user.example.expiration_date}" +} + +output "resource_proxmox_virtual_environment_user_example_first_name" { + value = "${proxmox_virtual_environment_user.example.first_name}" +} + +output "resource_proxmox_virtual_environment_user_example_groups" { + value = "${proxmox_virtual_environment_user.example.groups}" +} + +output "resource_proxmox_virtual_environment_user_example_keys" { + value = "${proxmox_virtual_environment_user.example.keys}" +} + +output "resource_proxmox_virtual_environment_user_example_last_name" { + value = "${proxmox_virtual_environment_user.example.last_name}" +} + +output "resource_proxmox_virtual_environment_user_example_password" { + value = "${proxmox_virtual_environment_user.example.password}" + sensitive = true +} + +output "resource_proxmox_virtual_environment_user_example_user_id" { + value = "${proxmox_virtual_environment_user.example.id}" +} diff --git a/provider.go b/provider.go index 70489b22..3d5318a6 100644 --- a/provider.go +++ b/provider.go @@ -39,6 +39,7 @@ func Provider() *schema.Provider { ResourcesMap: map[string]*schema.Resource{ "proxmox_virtual_environment_group": resourceVirtualEnvironmentGroup(), "proxmox_virtual_environment_role": resourceVirtualEnvironmentRole(), + "proxmox_virtual_environment_user": resourceVirtualEnvironmentUser(), }, Schema: map[string]*schema.Schema{ mkProviderVirtualEnvironment: &schema.Schema{ diff --git a/proxmox/virtual_environment.go b/proxmox/virtual_environment.go index 14a98fed..b26bb085 100644 --- a/proxmox/virtual_environment.go +++ b/proxmox/virtual_environment.go @@ -152,7 +152,7 @@ func (c *VirtualEnvironmentClient) ValidateResponseCode(res *http.Response) erro case 404: return fmt.Errorf("Received a HTTP %d response - Please verify that the endpoint refers to a supported version of the Proxmox Virtual Environment API", res.StatusCode) case 500: - return fmt.Errorf("Received a HTTP %d response - Please verify that the Proxmox Virtual Environment API is healthy", res.StatusCode) + return fmt.Errorf("Received a HTTP %d response - Please verify that the Proxmox Virtual Environment API is healthy (status: %s)", res.StatusCode, res.Status) default: return fmt.Errorf("Received a HTTP %d response", res.StatusCode) } diff --git a/proxmox/virtual_environment_users.go b/proxmox/virtual_environment_users.go index bea1dbe0..05651519 100644 --- a/proxmox/virtual_environment_users.go +++ b/proxmox/virtual_environment_users.go @@ -9,20 +9,27 @@ import ( "fmt" "net/url" "sort" + "time" ) +// VirtualEnvironmentUserChangePasswordRequestBody contains the data for a user password change request. +type VirtualEnvironmentUserChangePasswordRequestBody struct { + ID string `json:"userid" url:"userid"` + Password string `json:"password" url:"password"` +} + // VirtualEnvironmentUserCreateRequestBody contains the data for an user create request. type VirtualEnvironmentUserCreateRequestBody struct { - Comment *string `json:"comment,omitempty"` - Email *string `json:"email,omitempty"` - Enabled *CustomBool `json:"enable,omitempty"` - ExpirationDate *CustomTimestamp `json:"expire,omitempty"` - FirstName *string `json:"firstname,omitempty"` - Groups *[]string `json:"groups,omitempty"` - ID string `json:"userid"` - Keys *string `json:"keys,omitempty"` - LastName *string `json:"lastname,omitempty"` - Password string `json:"password"` + Comment *string `json:"comment,omitempty" url:"comment,omitempty"` + Email *string `json:"email,omitempty" url:"email,omitempty"` + Enabled *CustomBool `json:"enable,omitempty" url:"enable,omitempty,int"` + ExpirationDate *CustomTimestamp `json:"expire,omitempty" url:"expire,omitempty,unix"` + FirstName *string `json:"firstname,omitempty" url:"firstname,omitempty"` + Groups []string `json:"groups,omitempty" url:"groups,omitempty,comma"` + ID string `json:"userid" url:"userid"` + Keys *string `json:"keys,omitempty" url:"keys,omitempty"` + LastName *string `json:"lastname,omitempty" url:"lastname,omitempty"` + Password string `json:"password" url:"password"` } // VirtualEnvironmentUserGetResponseBody contains the body from an user get response. @@ -62,15 +69,25 @@ type VirtualEnvironmentUserListResponseData struct { // VirtualEnvironmentUserUpdateRequestBody contains the data for an user update request. type VirtualEnvironmentUserUpdateRequestBody struct { - Append *CustomBool `json:"append,omitempty"` - Comment *string `json:"comment,omitempty"` - Email *string `json:"email,omitempty"` - Enabled *CustomBool `json:"enable,omitempty"` - ExpirationDate *CustomTimestamp `json:"expire,omitempty"` - FirstName *string `json:"firstname,omitempty"` - Groups *[]string `json:"groups,omitempty"` - Keys *string `json:"keys,omitempty"` - LastName *string `json:"lastname,omitempty"` + Append *CustomBool `json:"append,omitempty" url:"append,omitempty"` + Comment *string `json:"comment,omitempty" url:"comment,omitempty"` + Email *string `json:"email,omitempty" url:"email,omitempty"` + Enabled *CustomBool `json:"enable,omitempty" url:"enable,omitempty,int"` + ExpirationDate *CustomTimestamp `json:"expire,omitempty" url:"expire,omitempty,unix"` + FirstName *string `json:"firstname,omitempty" url:"firstname,omitempty"` + Groups []string `json:"groups,omitempty" url:"groups,omitempty,comma"` + Keys *string `json:"keys,omitempty" url:"keys,omitempty"` + LastName *string `json:"lastname,omitempty" url:"lastname,omitempty"` +} + +// ChangeUserPassword changes a user's password. +func (c *VirtualEnvironmentClient) ChangeUserPassword(id, password string) error { + d := VirtualEnvironmentUserChangePasswordRequestBody{ + ID: id, + Password: password, + } + + return c.DoRequest(hmPUT, "access/password", d, nil) } // CreateUser creates an user. @@ -96,6 +113,15 @@ func (c *VirtualEnvironmentClient) GetUser(id string) (*VirtualEnvironmentUserGe return nil, errors.New("The server did not include a data object in the response") } + if resBody.Data.ExpirationDate != nil { + expirationDate := CustomTimestamp(time.Time(*resBody.Data.ExpirationDate).UTC()) + resBody.Data.ExpirationDate = &expirationDate + } + + if resBody.Data.Groups != nil { + sort.Strings(*resBody.Data.Groups) + } + return resBody.Data, nil } @@ -116,6 +142,17 @@ func (c *VirtualEnvironmentClient) ListUsers() ([]*VirtualEnvironmentUserListRes return resBody.Data[i].ID < resBody.Data[j].ID }) + for i := range resBody.Data { + if resBody.Data[i].ExpirationDate != nil { + expirationDate := CustomTimestamp(time.Time(*resBody.Data[i].ExpirationDate).UTC()) + resBody.Data[i].ExpirationDate = &expirationDate + } + + if resBody.Data[i].Groups != nil { + sort.Strings(*resBody.Data[i].Groups) + } + } + return resBody.Data, nil } diff --git a/resource_virtual_environment_group.go b/resource_virtual_environment_group.go index 24e816c3..d5630e00 100644 --- a/resource_virtual_environment_group.go +++ b/resource_virtual_environment_group.go @@ -30,6 +30,7 @@ func resourceVirtualEnvironmentGroup() *schema.Resource { Type: schema.TypeString, Description: "The group id", Required: true, + ForceNew: true, }, mkResourceVirtualEnvironmentGroupMembers: &schema.Schema{ Type: schema.TypeSet, diff --git a/resource_virtual_environment_role.go b/resource_virtual_environment_role.go index 577ba7dd..5054486b 100644 --- a/resource_virtual_environment_role.go +++ b/resource_virtual_environment_role.go @@ -29,6 +29,7 @@ func resourceVirtualEnvironmentRole() *schema.Resource { Type: schema.TypeString, Description: "The role id", Required: true, + ForceNew: true, }, }, Create: resourceVirtualEnvironmentRoleCreate, diff --git a/resource_virtual_environment_user.go b/resource_virtual_environment_user.go new file mode 100644 index 00000000..c0b3b510 --- /dev/null +++ b/resource_virtual_environment_user.go @@ -0,0 +1,317 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package main + +import ( + "strings" + "time" + + "github.com/danitso/terraform-provider-proxmox/proxmox" + "github.com/hashicorp/terraform/helper/schema" +) + +const ( + mkResourceVirtualEnvironmentUserComment = "comment" + mkResourceVirtualEnvironmentUserEmail = "email" + mkResourceVirtualEnvironmentUserEnabled = "enabled" + mkResourceVirtualEnvironmentUserExpirationDate = "expiration_date" + mkResourceVirtualEnvironmentUserFirstName = "first_name" + mkResourceVirtualEnvironmentUserGroups = "groups" + mkResourceVirtualEnvironmentUserKeys = "keys" + mkResourceVirtualEnvironmentUserLastName = "last_name" + mkResourceVirtualEnvironmentUserPassword = "password" + mkResourceVirtualEnvironmentUserUserID = "user_id" +) + +func resourceVirtualEnvironmentUser() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + mkResourceVirtualEnvironmentUserComment: &schema.Schema{ + Type: schema.TypeString, + Description: "The user comment", + Optional: true, + Default: "", + }, + mkResourceVirtualEnvironmentUserEmail: &schema.Schema{ + Type: schema.TypeString, + Description: "The user's email address", + Optional: true, + Default: "", + }, + mkResourceVirtualEnvironmentUserEnabled: &schema.Schema{ + Type: schema.TypeBool, + Description: "Whether the user account is enabled", + Optional: true, + Default: true, + }, + mkResourceVirtualEnvironmentUserExpirationDate: &schema.Schema{ + Type: schema.TypeString, + Description: "The user account's expiration date", + Optional: true, + Default: time.Unix(0, 0).UTC().Format(time.RFC3339), + }, + mkResourceVirtualEnvironmentUserFirstName: &schema.Schema{ + Type: schema.TypeString, + Description: "The user's first name", + Optional: true, + Default: "", + }, + mkResourceVirtualEnvironmentUserGroups: &schema.Schema{ + Type: schema.TypeSet, + Description: "The user's groups", + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + DefaultFunc: func() (interface{}, error) { + return []string{}, nil + }, + }, + mkResourceVirtualEnvironmentUserKeys: &schema.Schema{ + Type: schema.TypeString, + Description: "The user's keys", + Optional: true, + Default: "", + }, + mkResourceVirtualEnvironmentUserLastName: &schema.Schema{ + Type: schema.TypeString, + Description: "The user's last name", + Optional: true, + Default: "", + }, + mkResourceVirtualEnvironmentUserPassword: &schema.Schema{ + Type: schema.TypeString, + Description: "The user's password", + Required: true, + }, + mkResourceVirtualEnvironmentUserUserID: &schema.Schema{ + Type: schema.TypeString, + Description: "The user id", + Required: true, + ForceNew: true, + }, + }, + Create: resourceVirtualEnvironmentUserCreate, + Read: resourceVirtualEnvironmentUserRead, + Update: resourceVirtualEnvironmentUserUpdate, + Delete: resourceVirtualEnvironmentUserDelete, + } +} + +func resourceVirtualEnvironmentUserCreate(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + comment := d.Get(mkResourceVirtualEnvironmentUserComment).(string) + email := d.Get(mkResourceVirtualEnvironmentUserEmail).(string) + enabled := proxmox.CustomBool(d.Get(mkResourceVirtualEnvironmentUserEnabled).(bool)) + expirationDate, err := time.Parse(time.RFC3339, d.Get(mkResourceVirtualEnvironmentUserExpirationDate).(string)) + + if err != nil { + return err + } + + expirationDateCustom := proxmox.CustomTimestamp(expirationDate) + firstName := d.Get(mkResourceVirtualEnvironmentUserFirstName).(string) + groups := d.Get(mkResourceVirtualEnvironmentUserGroups).(*schema.Set).List() + groupsCustom := make([]string, len(groups)) + + for i, v := range groups { + groupsCustom[i] = v.(string) + } + + keys := d.Get(mkResourceVirtualEnvironmentUserKeys).(string) + lastName := d.Get(mkResourceVirtualEnvironmentUserLastName).(string) + password := d.Get(mkResourceVirtualEnvironmentUserPassword).(string) + userID := d.Get(mkResourceVirtualEnvironmentUserUserID).(string) + + body := &proxmox.VirtualEnvironmentUserCreateRequestBody{ + Comment: &comment, + Email: &email, + Enabled: &enabled, + ExpirationDate: &expirationDateCustom, + FirstName: &firstName, + Groups: groupsCustom, + ID: userID, + Keys: &keys, + LastName: &lastName, + Password: password, + } + + err = veClient.CreateUser(body) + + if err != nil { + return err + } + + d.SetId(userID) + + return resourceVirtualEnvironmentUserRead(d, m) +} + +func resourceVirtualEnvironmentUserRead(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + userID := d.Id() + user, err := veClient.GetUser(userID) + + if err != nil { + if strings.Contains(err.Error(), "HTTP 404") { + d.SetId("") + + return nil + } + + return err + } + + d.SetId(userID) + + if user.Comment != nil { + d.Set(mkResourceVirtualEnvironmentUserComment, user.Comment) + } else { + d.Set(mkResourceVirtualEnvironmentUserComment, "") + } + + if user.Email != nil { + d.Set(mkResourceVirtualEnvironmentUserEmail, user.Email) + } else { + d.Set(mkResourceVirtualEnvironmentUserEmail, "") + } + + if user.Enabled != nil { + d.Set(mkResourceVirtualEnvironmentUserEnabled, user.Enabled) + } else { + d.Set(mkResourceVirtualEnvironmentUserEnabled, true) + } + + if user.ExpirationDate != nil { + d.Set(mkResourceVirtualEnvironmentUserExpirationDate, time.Time(*user.ExpirationDate).Format(time.RFC3339)) + } else { + d.Set(mkResourceVirtualEnvironmentUserExpirationDate, time.Unix(0, 0).UTC().Format(time.RFC3339)) + } + + if user.FirstName != nil { + d.Set(mkResourceVirtualEnvironmentUserFirstName, user.FirstName) + } else { + d.Set(mkResourceVirtualEnvironmentUserFirstName, "") + } + + groups := schema.NewSet(schema.HashString, make([]interface{}, 0)) + + if user.Groups != nil { + for _, v := range *user.Groups { + groups.Add(v) + } + } + + d.Set(mkResourceVirtualEnvironmentUserGroups, groups) + + if user.Keys != nil { + d.Set(mkResourceVirtualEnvironmentUserKeys, user.Keys) + } else { + d.Set(mkResourceVirtualEnvironmentUserKeys, "") + } + + if user.LastName != nil { + d.Set(mkResourceVirtualEnvironmentUserLastName, user.LastName) + } else { + d.Set(mkResourceVirtualEnvironmentUserLastName, "") + } + + return nil +} + +func resourceVirtualEnvironmentUserUpdate(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + comment := d.Get(mkResourceVirtualEnvironmentUserComment).(string) + email := d.Get(mkResourceVirtualEnvironmentUserEmail).(string) + enabled := proxmox.CustomBool(d.Get(mkResourceVirtualEnvironmentUserEnabled).(bool)) + expirationDate, err := time.Parse(time.RFC3339, d.Get(mkResourceVirtualEnvironmentUserExpirationDate).(string)) + + if err != nil { + return err + } + + expirationDateCustom := proxmox.CustomTimestamp(expirationDate) + firstName := d.Get(mkResourceVirtualEnvironmentUserFirstName).(string) + groups := d.Get(mkResourceVirtualEnvironmentUserGroups).(*schema.Set).List() + groupsCustom := make([]string, len(groups)) + + for i, v := range groups { + groupsCustom[i] = v.(string) + } + + keys := d.Get(mkResourceVirtualEnvironmentUserKeys).(string) + lastName := d.Get(mkResourceVirtualEnvironmentUserLastName).(string) + + body := &proxmox.VirtualEnvironmentUserUpdateRequestBody{ + Comment: &comment, + Email: &email, + Enabled: &enabled, + ExpirationDate: &expirationDateCustom, + FirstName: &firstName, + Groups: groupsCustom, + Keys: &keys, + LastName: &lastName, + } + + userID := d.Id() + err = veClient.UpdateUser(userID, body) + + if err != nil { + return err + } + + if d.HasChange(mkResourceVirtualEnvironmentUserPassword) { + password := d.Get(mkResourceVirtualEnvironmentUserPassword).(string) + err = veClient.ChangeUserPassword(userID, password) + + if err != nil { + return err + } + } + + return resourceVirtualEnvironmentUserRead(d, m) +} + +func resourceVirtualEnvironmentUserDelete(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + userID := d.Id() + err = veClient.DeleteUser(userID) + + if err != nil { + if strings.Contains(err.Error(), "HTTP 404") { + d.SetId("") + + return nil + } + + return err + } + + d.SetId("") + + return nil +} diff --git a/resource_virtual_environment_user_test.go b/resource_virtual_environment_user_test.go new file mode 100644 index 00000000..180bf446 --- /dev/null +++ b/resource_virtual_environment_user_test.go @@ -0,0 +1,35 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package main + +import ( + "testing" +) + +// TestResourceVirtualEnvironmentUserInstantiation tests whether the ResourceVirtualEnvironmentUser instance can be instantiated. +func TestResourceVirtualEnvironmentUserInstantiation(t *testing.T) { + s := resourceVirtualEnvironmentUser() + + if s == nil { + t.Fatalf("Cannot instantiate resourceVirtualEnvironmentUser") + } +} + +// TestResourceVirtualEnvironmentUserSchema tests the resourceVirtualEnvironmentUser schema. +func TestResourceVirtualEnvironmentUserSchema(t *testing.T) { + s := resourceVirtualEnvironmentUser() + + attributeKeys := []string{} + + for _, v := range attributeKeys { + if s.Schema[v] == nil { + t.Fatalf("Error in resourceVirtualEnvironmentUser.Schema: Missing attribute \"%s\"", v) + } + + if s.Schema[v].Computed != true { + t.Fatalf("Error in resourceVirtualEnvironmentUser.Schema: Attribute \"%s\" is not computed", v) + } + } +}