diff --git a/examples/authorization_policies_v2/main.tf b/examples/authorization_policies_v2/main.tf new file mode 100644 index 000000000..210ee0711 --- /dev/null +++ b/examples/authorization_policies_v2/main.tf @@ -0,0 +1,48 @@ +#Here we will get and list permissions +#the variable "" present in terraform.tfvars file. +#Note - Replace appropriate values of variables in terraform.tfvars file as per setup + +terraform { + required_providers { + nutanix = { + source = "nutanix/nutanix" + version = "1.3.0" + } + } +} + +#definig nutanix configuration +provider "nutanix" { + username = var.nutanix_username + password = var.nutanix_password + endpoint = var.nutanix_endpoint + port = var.nutanix_port + insecure = true +} + +# creat authorization policy +resource "nutanix_authorization_policy_v2" "auth_policy_example" { + role = "" + display_name = "" + description = "" + authorization_policy_type = "" + # identity and entity will defined as a json string + identities { + reserved = "" # ex : "{\"user\":{\"uuid\":{\"anyof\":[\"00000000-0000-0000-0000-000000000000\"]}}}" + } + entities { + reserved = "" # ex : "{\"images\":{\"*\":{\"eq\":\"*\"}}}" + } +} + +#get authorization policy by id +data "nutanix_authorization_policy_v2" "example" { + ext_id = nutanix_authorization_policy_v2.auth_policy_example.id +} + + +#list of authorization policies, with limit and filter +data "nutanix_authorization_policies_v2" "examples" { + limit = 2 + filter = "display_name eq ''" +} diff --git a/examples/authorization_policies_v2/terraform.tfvars b/examples/authorization_policies_v2/terraform.tfvars new file mode 100644 index 000000000..a91126c35 --- /dev/null +++ b/examples/authorization_policies_v2/terraform.tfvars @@ -0,0 +1,8 @@ +#replace the values as per setup configuration +nutanix_username = "admin" +nutanix_password = "Nutanix/123456" +nutanix_endpoint = "10.xx.xx.xx" +nutanix_port = 9440 + +#replace this values as per the setup +permission_ext_id = "" diff --git a/examples/authorization_policies_v2/variables.tf b/examples/authorization_policies_v2/variables.tf new file mode 100644 index 000000000..2b4dc4856 --- /dev/null +++ b/examples/authorization_policies_v2/variables.tf @@ -0,0 +1,17 @@ + +#variable definations +variable "nutanix_username" { + type = string +} +variable "nutanix_password" { + type = string +} +variable "nutanix_endpoint" { + type = string +} +variable "nutanix_port" { + type = string +} +variable "permission_ext_id" { + type = string +} diff --git a/examples/directory_services_v2/main.tf b/examples/directory_services_v2/main.tf new file mode 100644 index 000000000..cede83710 --- /dev/null +++ b/examples/directory_services_v2/main.tf @@ -0,0 +1,40 @@ +terraform { + required_providers { + nutanix = { + source = "nutanix/nutanix" + version = "1.7.0" + } + } +} + +#definig nutanix configuration +provider "nutanix" { + username = var.nutanix_username + password = var.nutanix_password + endpoint = var.nutanix_endpoint + port = 9440 + insecure = true +} + + + +# Add Directory Service . +resource "nutanix_directory_services_v2" "example" { + name = "" + url = "" + directory_type = "" + domain_name = "" + service_account { + username = "" + password = "" + } + white_listed_groups = ["example"] +} + +# List all Directory Services. +data "nutanix_directory_services_v2" "example" {} + +# Get a Directory Service. +data "nutanix_directory_service_v2" "example" { + ext_id = "" +} diff --git a/examples/directory_services_v2/terraform.tfvars b/examples/directory_services_v2/terraform.tfvars new file mode 100644 index 000000000..867888ffc --- /dev/null +++ b/examples/directory_services_v2/terraform.tfvars @@ -0,0 +1,5 @@ +#define values to the variables to be used in terraform file +nutanix_username = "admin" +nutanix_password = "password" +nutanix_endpoint = "10.xx.xx.xx" +nutanix_port = 9440 diff --git a/examples/directory_services_v2/variables.tf b/examples/directory_services_v2/variables.tf new file mode 100644 index 000000000..dcd130ec8 --- /dev/null +++ b/examples/directory_services_v2/variables.tf @@ -0,0 +1,13 @@ +#define the type of variables to be used in terraform file +variable "nutanix_username" { + type = string +} +variable "nutanix_password" { + type = string +} +variable "nutanix_endpoint" { + type = string +} +variable "nutanix_port" { + type = string +} diff --git a/examples/operations_v2/main.tf b/examples/operations_v2/main.tf new file mode 100644 index 000000000..4e71a00be --- /dev/null +++ b/examples/operations_v2/main.tf @@ -0,0 +1,34 @@ +#Here we will get and list permissions +#the variable "" present in terraform.tfvars file. +#Note - Replace appropriate values of variables in terraform.tfvars file as per setup + +terraform { + required_providers { + nutanix = { + source = "nutanix/nutanix" + version = "1.3.0" + } + } +} + +#definig nutanix configuration +provider "nutanix" { + username = var.nutanix_username + password = var.nutanix_password + endpoint = var.nutanix_endpoint + port = var.nutanix_port + insecure = true +} + +#get permission by ext-id +data "nutanix_operation_v2" "permission" { + ext_id = var.permission_ext_id +} + + +#list permissions +data "nutanix_operations_v2" "permissions" { + page = 0 + limit = 2 + filter = "display_name eq 'test-Permission-filter'" +} diff --git a/examples/operations_v2/terraform.tfvars b/examples/operations_v2/terraform.tfvars new file mode 100644 index 000000000..a91126c35 --- /dev/null +++ b/examples/operations_v2/terraform.tfvars @@ -0,0 +1,8 @@ +#replace the values as per setup configuration +nutanix_username = "admin" +nutanix_password = "Nutanix/123456" +nutanix_endpoint = "10.xx.xx.xx" +nutanix_port = 9440 + +#replace this values as per the setup +permission_ext_id = "" diff --git a/examples/operations_v2/variables.tf b/examples/operations_v2/variables.tf new file mode 100644 index 000000000..2b4dc4856 --- /dev/null +++ b/examples/operations_v2/variables.tf @@ -0,0 +1,17 @@ + +#variable definations +variable "nutanix_username" { + type = string +} +variable "nutanix_password" { + type = string +} +variable "nutanix_endpoint" { + type = string +} +variable "nutanix_port" { + type = string +} +variable "permission_ext_id" { + type = string +} diff --git a/examples/roles_v2/main.tf b/examples/roles_v2/main.tf new file mode 100644 index 000000000..c30cc148b --- /dev/null +++ b/examples/roles_v2/main.tf @@ -0,0 +1,24 @@ +provider "nutanix" { + username = var.user + password = var.password + endpoint = var.endpoint + insecure = var.insecure + port = var.port + wait_timeout = 60 +} + +# Create role +resource "nutanix_roles_v2" "test" { + display_name = "test_role" + description = "creat a test role using terraform" + operations = var.operations +} + +# list Roles +data "nutanix_roles_v2" "test"{} + +# get a specific role by id +data "nutanix_role_v2" "test" { + ext_id = resource.nutanix_roles_v2.test.id +} + diff --git a/examples/roles_v2/variables.tf b/examples/roles_v2/variables.tf new file mode 100644 index 000000000..fe180b39a --- /dev/null +++ b/examples/roles_v2/variables.tf @@ -0,0 +1,26 @@ +variable "user" { + type = string +} +variable "password" { + type = string +} +variable "endpoint" { + type = string +} +variable "insecure" { + type = bool +} +variable "port" { + type = number +} + +variable "operations" { + type = list(string) + default = [ + "operation_1_ext_id", + "operation_2_ext_id", + "operation_3_ext_id", + ] +} + + diff --git a/examples/saml_idp_v2/main.tf b/examples/saml_idp_v2/main.tf new file mode 100644 index 000000000..2de952c36 --- /dev/null +++ b/examples/saml_idp_v2/main.tf @@ -0,0 +1,45 @@ +#Here we will get and list permissions +#the variable "" present in terraform.tfvars file. +#Note - Replace appropriate values of variables in terraform.tfvars file as per setup + +terraform { + required_providers { + nutanix = { + source = "nutanix/nutanix" + version = "1.3.0" + } + } +} + +#definig nutanix configuration +provider "nutanix" { + username = var.nutanix_username + password = var.nutanix_password + endpoint = var.nutanix_endpoint + port = var.nutanix_port + insecure = true +} + +resource "nutanix_saml_identity_providers_v2" "example" { + name = "" + username_attribute = "" + email_attribute = "" + groups_attribute = "" + groups_delim = "" # such as ',' or ';' + idp_metadata_xml = "" + entity_issuer = "" + is_signed_authn_req_enabled = "" + custom_attributes = "" +} + +#get saml identity provider by external id +data "nutanix_operation_v2" "permission" { + ext_id = nutanix_saml_identity_providers_v2.example.id +} + + +#list permissions +data "nutanix_operations_v2" "permissions" { + page = 0 + limit = 2 +} diff --git a/examples/saml_idp_v2/terraform.tfvars b/examples/saml_idp_v2/terraform.tfvars new file mode 100644 index 000000000..a91126c35 --- /dev/null +++ b/examples/saml_idp_v2/terraform.tfvars @@ -0,0 +1,8 @@ +#replace the values as per setup configuration +nutanix_username = "admin" +nutanix_password = "Nutanix/123456" +nutanix_endpoint = "10.xx.xx.xx" +nutanix_port = 9440 + +#replace this values as per the setup +permission_ext_id = "" diff --git a/examples/saml_idp_v2/variables.tf b/examples/saml_idp_v2/variables.tf new file mode 100644 index 000000000..2b4dc4856 --- /dev/null +++ b/examples/saml_idp_v2/variables.tf @@ -0,0 +1,17 @@ + +#variable definations +variable "nutanix_username" { + type = string +} +variable "nutanix_password" { + type = string +} +variable "nutanix_endpoint" { + type = string +} +variable "nutanix_port" { + type = string +} +variable "permission_ext_id" { + type = string +} diff --git a/examples/user_groups_v2/main.tf b/examples/user_groups_v2/main.tf new file mode 100644 index 000000000..33f407d70 --- /dev/null +++ b/examples/user_groups_v2/main.tf @@ -0,0 +1,36 @@ +terraform { + required_providers { + nutanix = { + source = "nutanix/nutanix" + version = "1.7.0" + } + } +} + +#definig nutanix configuration +provider "nutanix" { + username = var.nutanix_username + password = var.nutanix_password + endpoint = var.nutanix_endpoint + port = 9440 + insecure = true +} + +# Add a User group to the system. + +resource "nutanix_user_groups_v2" "example" { + # Type of the User Group. LDAP, SAML + group_type = "" + idp_id = "" + name = "" + distinguished_name = "" +} + + +# List all the user groups in the system. +data "nutanix_user_groups_v2" "example"{} + +# Get the details of a user group. +data "nutanix_user_group_v2" "example" { + ext_id = nutanix_user_groups_v2.example.id +} \ No newline at end of file diff --git a/examples/user_groups_v2/terraform.tfvars b/examples/user_groups_v2/terraform.tfvars new file mode 100644 index 000000000..ff51df9af --- /dev/null +++ b/examples/user_groups_v2/terraform.tfvars @@ -0,0 +1,5 @@ +#define values to the variables to be used in terraform file +nutanix_username = "admin" +nutanix_password = "password" +nutanix_endpoint = "10.xx.xx.xx" +nutanix_port = 9440 diff --git a/examples/user_groups_v2/variables.tf b/examples/user_groups_v2/variables.tf new file mode 100644 index 000000000..dcd130ec8 --- /dev/null +++ b/examples/user_groups_v2/variables.tf @@ -0,0 +1,13 @@ +#define the type of variables to be used in terraform file +variable "nutanix_username" { + type = string +} +variable "nutanix_password" { + type = string +} +variable "nutanix_endpoint" { + type = string +} +variable "nutanix_port" { + type = string +} diff --git a/examples/users_v2/main.tf b/examples/users_v2/main.tf new file mode 100644 index 000000000..e1efd944e --- /dev/null +++ b/examples/users_v2/main.tf @@ -0,0 +1,44 @@ +terraform { + required_providers { + nutanix = { + source = "nutanix/nutanix" + version = "1.7.0" + } + } +} + +#definig nutanix configuration +provider "nutanix" { + username = var.nutanix_username + password = var.nutanix_password + endpoint = var.nutanix_endpoint + port = 9440 + insecure = true +} + +# Add a User group to the system. + +resource "nutanix_users_v2" "example" { + username = "" + first_name = "" + middle_initial = "" + last_name = "" + email_id = "" + locale = "" + region = "" + display_name = "" + password = "" + # Type of the User LOCAL, LDAP, SAML + user_type = "LOCAL" + # Status of the User ACTIVE, INACTIVE + status = "ACTIVE" +} + + +# List all the users in the system. +data "nutanix_users_v2" "test"{} + +# Get the details of a user. +data "nutanix_user_v2" "test" { + ext_id = nutanix_users_v2.example.id +} diff --git a/examples/users_v2/terraform.tfvars b/examples/users_v2/terraform.tfvars new file mode 100644 index 000000000..ff51df9af --- /dev/null +++ b/examples/users_v2/terraform.tfvars @@ -0,0 +1,5 @@ +#define values to the variables to be used in terraform file +nutanix_username = "admin" +nutanix_password = "password" +nutanix_endpoint = "10.xx.xx.xx" +nutanix_port = 9440 diff --git a/examples/users_v2/variables.tf b/examples/users_v2/variables.tf new file mode 100644 index 000000000..dcd130ec8 --- /dev/null +++ b/examples/users_v2/variables.tf @@ -0,0 +1,13 @@ +#define the type of variables to be used in terraform file +variable "nutanix_username" { + type = string +} +variable "nutanix_password" { + type = string +} +variable "nutanix_endpoint" { + type = string +} +variable "nutanix_port" { + type = string +} diff --git a/examples/virtual_machine_clone_v2/main.tf b/examples/virtual_machine_clone_v2/main.tf new file mode 100644 index 000000000..a114fa1e0 --- /dev/null +++ b/examples/virtual_machine_clone_v2/main.tf @@ -0,0 +1,71 @@ +#Here we will create a vm clone +#the variable "" present in terraform.tfvars file. +#Note - Replace appropriate values of variables in terraform.tfvars file as per setup + +terraform { + required_providers { + nutanix = { + source = "nutanix/nutanix" + version = "1.3.0" + } + } +} + +#definig nutanix configuration +provider "nutanix" { + username = var.nutanix_username + password = var.nutanix_password + endpoint = var.nutanix_endpoint + port = var.nutanix_port + insecure = true +} + +#create a virtual machine clone +resource "nutanix_vm_clone_v4" "clone-vm" { + vm_ext_id = var.vm_uuid + name = "clone-vm-123" + num_sockets = "2" + num_cores_per_socket = "2" + num_threads_per_core = "2" + memory_size_bytes = 4096 + guest_customization { + config { + sysprep { + install_type = "PREPARED" + sysprep_script { + unattend_xml { + value = "" + } + } + } + } + } + boot_config { + legacy_boot { + boot_device { + boot_device_disk { + disk_address { + bus_type = "SCSI" + index = 1 + } + } + boot_device_nic { + mac_address = "" + } + } + boot_order = ["CDROM", "DISK", "NETWORK"] + } + } + + nics { + ext_id = data.nutanix_subnet.subnet.id + backing_info { + model = "VIRTIO" + mac_address = "" + is_connected = "true" + num_queues = 1 + } + network_info {} + } + +} diff --git a/examples/virtual_machine_clone_v2/terraform.tfvars b/examples/virtual_machine_clone_v2/terraform.tfvars new file mode 100644 index 000000000..23d1e5fbc --- /dev/null +++ b/examples/virtual_machine_clone_v2/terraform.tfvars @@ -0,0 +1,11 @@ +#replace the values as per setup configuration +nutanix_username = "admin" +nutanix_password = "Nutanix/123456" +nutanix_endpoint = "10.xx.xx.xx" +nutanix_port = 9440 + +#replace this values as per the setup +vm_uuid = "" + +#this variable will be used in adding disks to vm in main.tf +disk_sizes = [1024, 1024, 2048] diff --git a/examples/virtual_machine_clone_v2/variables.tf b/examples/virtual_machine_clone_v2/variables.tf new file mode 100644 index 000000000..35e924d41 --- /dev/null +++ b/examples/virtual_machine_clone_v2/variables.tf @@ -0,0 +1,24 @@ + +#variable definations +variable "nutanix_username" { + type = string +} +variable "nutanix_password" { + type = string +} +variable "nutanix_endpoint" { + type = string +} +variable "nutanix_port" { + type = string +} +variable "vm_uuid" { + type = string +} +variable "subnet_name" { + type = string +} +variable "disk_sizes" { + type = list(string) + default = [1024, 2048] +} diff --git a/examples/virtual_machine_gc_update_v2/main.tf b/examples/virtual_machine_gc_update_v2/main.tf new file mode 100644 index 000000000..e11c153a9 --- /dev/null +++ b/examples/virtual_machine_gc_update_v2/main.tf @@ -0,0 +1,35 @@ +#Here we will create a vm clone +#the variable "" present in terraform.tfvars file. +#Note - Replace appropriate values of variables in terraform.tfvars file as per setup + +terraform { + required_providers { + nutanix = { + source = "nutanix/nutanix" + version = "1.3.0" + } + } +} + +#definig nutanix configuration +provider "nutanix" { + username = var.nutanix_username + password = var.nutanix_password + endpoint = var.nutanix_endpoint + port = var.nutanix_port + insecure = true +} + +#update a virtual machine guest customization for next boot +resource "nutanix_vm_gc_update_v4" "test" { + ext_id = var.vm_uuid + config { + cloud_init { + cloud_init_script { + user_data { + value = var.user_data + } + } + } + } +} diff --git a/examples/virtual_machine_gc_update_v2/terraform.tfvars b/examples/virtual_machine_gc_update_v2/terraform.tfvars new file mode 100644 index 000000000..28fe63873 --- /dev/null +++ b/examples/virtual_machine_gc_update_v2/terraform.tfvars @@ -0,0 +1,9 @@ +#replace the values as per setup configuration +nutanix_username = "admin" +nutanix_password = "Nutanix/123456" +nutanix_endpoint = "10.xx.xx.xx" +nutanix_port = 9440 + +#replace this values as per the setup +vm_uuid = "" +user_data = "" diff --git a/examples/virtual_machine_gc_update_v2/variables.tf b/examples/virtual_machine_gc_update_v2/variables.tf new file mode 100644 index 000000000..568fad902 --- /dev/null +++ b/examples/virtual_machine_gc_update_v2/variables.tf @@ -0,0 +1,20 @@ + +#variable definations +variable "nutanix_username" { + type = string +} +variable "nutanix_password" { + type = string +} +variable "nutanix_endpoint" { + type = string +} +variable "nutanix_port" { + type = string +} +variable "vm_uuid" { + type = string +} +variable "user_data" { + type = string +} diff --git a/go.mod b/go.mod index bf9e11e5e..cf028b447 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/nutanix-core/ntnx-api-golang-sdk-internal/networking-go-client/v16 v16.9.0-8634 github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4 v4.0.1-beta.1 // github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4 v4.0.3-alpha.2 + github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16 v16.8.0-5280 github.com/spf13/cast v1.3.1 github.com/stretchr/testify v1.7.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 2fe1958a5..e06842ac2 100644 --- a/go.sum +++ b/go.sum @@ -452,6 +452,8 @@ github.com/nutanix/ntnx-api-golang-clients/networking-go-client/v4 v4.0.2-beta.1 github.com/nutanix/ntnx-api-golang-clients/networking-go-client/v4 v4.0.2-beta.1/go.mod h1:+eZgV1+xL/r84qmuFSVt5R8OFRO70rEz92jOnVgJNco= github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4 v4.0.1-beta.1 h1:hvy3QCc2SgVidYxTq0rRPOazJOt1PP8A86kW7j6sywU= github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4 v4.0.1-beta.1/go.mod h1:Yhk+xD4mN90OKEHnk5ARf97CX5p4+MEC/B/YIVoZeZ0= +github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16 v16.8.0-5280 h1:sYX9SWnyph1+gjibK8kOQNS5WmbdakCVw2kU8/oCWn8= +github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16 v16.8.0-5280/go.mod h1:cSEUNcUEpaGpZq3CXj4wSczM3zzPQLzTDfYwhkl0aLQ= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= diff --git a/nutanix/config.go b/nutanix/config.go index 747f661fa..72aeb55db 100644 --- a/nutanix/config.go +++ b/nutanix/config.go @@ -20,6 +20,7 @@ import ( foundation_central "github.com/terraform-providers/terraform-provider-nutanix/nutanix/sdks/v3/fc" "github.com/terraform-providers/terraform-provider-nutanix/nutanix/sdks/v3/foundation" v3 "github.com/terraform-providers/terraform-provider-nutanix/nutanix/sdks/v3/prism" + "github.com/terraform-providers/terraform-provider-nutanix/nutanix/sdks/v4/iam" ) // Version represents api version @@ -82,6 +83,10 @@ func (c *Config) Client() (*Client, error) { if err != nil { return nil, err } + iamClient, err := iam.NewIamClient(configCreds) + if err != nil { + return nil, err + } networkingClient, err := networking.NewNetworkingClient(configCreds) if err != nil { return nil, err @@ -104,6 +109,7 @@ func (c *Config) Client() (*Client, error) { NetworkingAPI: networkingClient, PrismAPI: prismClient, MicroSegAPI: microsegClient, + IamAPI: iamClient, }, nil } @@ -118,4 +124,5 @@ type Client struct { NetworkingAPI *networking.Client PrismAPI *prism.Client MicroSegAPI *microseg.Client + IamAPI *iam.Client } diff --git a/nutanix/provider/provider.go b/nutanix/provider/provider.go index baa48d065..7affb01b7 100644 --- a/nutanix/provider/provider.go +++ b/nutanix/provider/provider.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" "github.com/terraform-providers/terraform-provider-nutanix/nutanix/internal" fc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/services/v1/fc" @@ -16,6 +17,7 @@ import ( "github.com/terraform-providers/terraform-provider-nutanix/nutanix/services/v1/nke" "github.com/terraform-providers/terraform-provider-nutanix/nutanix/services/v1/prism" "github.com/terraform-providers/terraform-provider-nutanix/nutanix/services/v2/networkingv2" + "github.com/terraform-providers/terraform-provider-nutanix/nutanix/services/v2/iamv2" ) var requiredProviderFields map[string][]string = map[string][]string{ @@ -235,6 +237,20 @@ func Provider() *schema.Provider { "nutanix_service_groups_v2": networkingv2.DatasourceNutanixServiceGroupsV2(), "nutanix_address_group_v2": networkingv2.DatasourceNutanixAddressGroupV2(), "nutanix_address_groups_v2": networkingv2.DatasourceNutanixAddressGroupsV2(), + "nutanix_directory_service_v2": iamv2.DatasourceNutanixDirectoryServiceV2(), + "nutanix_directory_services_v2": iamv2.DatasourceNutanixDirectoryServicesV2(), + "nutanix_saml_identity_provider_v2": iamv2.DatasourceNutanixSamlIDPV2(), + "nutanix_saml_identity_providers_v2": iamv2.DatasourceNutanixSamlIDPsV2(), + "nutanix_user_group_v2": iamv2.DatasourceNutanixUserGroupV2(), + "nutanix_user_groups_v2": iamv2.DatasourceNutanixUserGroupsV2(), + "nutanix_roles_v2": iamv2.DatasourceNutanixRolesV2(), + "nutanix_role_v2": iamv2.DatasourceNutanixRoleV2(), + "nutanix_operation_v2": iamv2.DatasourceNutanixOperationV2(), + "nutanix_operations_v2": iamv2.DatasourceNutanixOperationsV2(), + "nutanix_user_v2": iamv2.DatasourceNutanixUserV2(), + "nutanix_users_v2": iamv2.DatasourceNutanixUsersV2(), + "nutanix_authorization_policy_v2": iamv2.DatasourceNutanixAuthorizationPolicyV2(), + "nutanix_authorization_policies_v2": iamv2.DatasourceNutanixAuthorizationPoliciesV2(), }, ResourcesMap: map[string]*schema.Resource{ "nutanix_virtual_machine": prism.ResourceNutanixVirtualMachine(), @@ -294,6 +310,12 @@ func Provider() *schema.Provider { "nutanix_pbr_v2": networkingv2.ResourceNutanixPbrsV2(), "nutanix_service_groups_v2": networkingv2.ResourceNutanixServiceGroupsV2(), "nutanix_address_groups_v2": networkingv2.ResourceNutanixAddressGroupsV2(), + "nutanix_directory_services_v2": iamv2.ResourceNutanixDirectoryServicesV2(), + "nutanix_user_groups_v2": iamv2.ResourceNutanixUserGroupsV2(), + "nutanix_roles_v2": iamv2.ResourceNutanixRolesV2(), + "nutanix_users_v2": iamv2.ResourceNutanixUserV2(), + "nutanix_authorization_policy_v2": iamv2.ResourceNutanixAuthPoliciesV2(), + "nutanix_saml_identity_providers_v2": iamv2.ResourceNutanixSamlIdpV2(), }, ConfigureContextFunc: providerConfigure, } diff --git a/nutanix/sdks/v4/iam/iam.go b/nutanix/sdks/v4/iam/iam.go new file mode 100644 index 000000000..0db9c23d1 --- /dev/null +++ b/nutanix/sdks/v4/iam/iam.go @@ -0,0 +1,49 @@ +package iam + +import ( + "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/api" + iam "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/client" + + "github.com/terraform-providers/terraform-provider-nutanix/nutanix/client" +) + +type Client struct { + APIClientInstance *iam.ApiClient + DirectoryServiceAPIInstance *api.DirectoryServicesApi + SamlIdentityAPIInstance *api.SAMLIdentityProvidersApi + UsersAPIInstance *api.UsersApi + UserGroupsAPIInstance *api.UserGroupsApi + RolesAPIInstance *api.RolesApi + OperationsAPIInstance *api.OperationsApi + AuthAPIInstance *api.AuthorizationPoliciesApi +} + +func NewIamClient(credentials client.Credentials) (*Client, error) { + var baseClient *iam.ApiClient + + // check if all required fields are present. Else create an empty client + if credentials.Username != "" && credentials.Password != "" && credentials.Endpoint != "" { + pcClient := iam.NewApiClient() + + pcClient.Host = credentials.Endpoint + pcClient.Password = credentials.Password + pcClient.Username = credentials.Username + pcClient.Port = 9440 + pcClient.VerifySSL = false + + baseClient = pcClient + } + + f := &Client{ + DirectoryServiceAPIInstance: api.NewDirectoryServicesApi(baseClient), + SamlIdentityAPIInstance: api.NewSAMLIdentityProvidersApi(baseClient), + UserGroupsAPIInstance: api.NewUserGroupsApi(baseClient), + RolesAPIInstance: api.NewRolesApi(baseClient), + OperationsAPIInstance: api.NewOperationsApi(baseClient), + UsersAPIInstance: api.NewUsersApi(baseClient), + AuthAPIInstance: api.NewAuthorizationPoliciesApi(baseClient), + APIClientInstance: iam.NewApiClient(), + } + + return f, nil +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_authorization_policies_v2.go b/nutanix/services/v2/iamv2/data_source_nutanix_authorization_policies_v2.go new file mode 100644 index 000000000..113c2600e --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_authorization_policies_v2.go @@ -0,0 +1,221 @@ +package iamv2 + +import ( + "context" + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authz" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixAuthorizationPoliciesV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixAuthorizationPoliciesV2Read, + Schema: map[string]*schema.Schema{ + "page": { + Type: schema.TypeInt, + Optional: true, + }, + "limit": { + Type: schema.TypeInt, + Optional: true, + }, + "filter": { + Type: schema.TypeString, + Optional: true, + }, + "order_by": { + Type: schema.TypeString, + Optional: true, + }, + "select": { + Type: schema.TypeString, + Optional: true, + }, + "expand": { + Type: schema.TypeString, + Optional: true, + }, + "auth_policies": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "client_name": { + Type: schema.TypeString, + Computed: true, + }, + "identities": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "reserved": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "entities": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "reserved": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "role": { + Type: schema.TypeString, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + "is_system_defined": { + Type: schema.TypeBool, + Computed: true, + }, + "authorization_policy_type": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DatasourceNutanixAuthorizationPoliciesV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + // initialize query params + var filter, orderBy, selects, expand *string + var page, limit *int + + if pagef, ok := d.GetOk("page"); ok { + page = utils.IntPtr(pagef.(int)) + } else { + page = nil + } + if limitf, ok := d.GetOk("limit"); ok { + limit = utils.IntPtr(limitf.(int)) + } else { + limit = nil + } + if filterf, ok := d.GetOk("filter"); ok { + filter = utils.StringPtr(filterf.(string)) + } else { + filter = nil + } + if order, ok := d.GetOk("order_by"); ok { + orderBy = utils.StringPtr(order.(string)) + } else { + orderBy = nil + } + if selectf, ok := d.GetOk("select"); ok { + selects = utils.StringPtr(selectf.(string)) + } else { + selects = nil + } + if expandf, ok := d.GetOk("expand"); ok { + expand = utils.StringPtr(expandf.(string)) + } else { + expand = nil + } + + resp, err := conn.AuthAPIInstance.ListAuthorizationPolicies(page, limit, filter, orderBy, expand, selects) + if err != nil { + fmt.Println(err) + return diag.Errorf("error while fetching auth policies: %v", err) + } + + // Log the value of resp.Data.GetValue() under DEBUG level + log.Printf("[DEBUG] resp.Data.GetValue(): %+v\n", resp.Data.GetValue()) + + getVal := resp.ObjectType_ + + if *getVal == "iam.v4.authz.AuthorizationPolicyProjection" { + fmt.Println("policyProjection") + } + + getResp := resp.Data.GetValue().([]import1.AuthorizationPolicyProjection) + if err := d.Set("auth_policies", flattenAuthorizationPolicyEntities(getResp)); err != nil { + return diag.FromErr(err) + } + + d.SetId(resource.UniqueId()) + return nil +} + +func flattenAuthorizationPolicyEntities(pr []import1.AuthorizationPolicyProjection) []interface{} { + if len(pr) > 0 { + auths := make([]interface{}, len(pr)) + + for k, v := range pr { + log.Printf("[DEBUG] flattenAuthorizationPolicyEntities[%v].ExtId: %v", k, *v.ExtId) + auth := make(map[string]interface{}) + + auth["ext_id"] = v.ExtId + auth["display_name"] = v.DisplayName + auth["description"] = v.Description + auth["client_name"] = v.ClientName + auth["entities"] = flattenEntityFilters(v.Entities) + + auth["identities"] = flattenIdentityFilters(v.Identities) + log.Printf("[DEBUG] flattenAuthorizationPolicyEntities[%v].Identities: %v", k, v.Identities) + log.Printf("[DEBUG] flattenAuthorizationPolicyEntities[%v].Identities: %v", k, auth["identities"]) + auth["role"] = v.Role + if v.CreatedTime != nil { + t := v.CreatedTime + auth["created_time"] = t.String() + } + if v.LastUpdatedTime != nil { + t := v.LastUpdatedTime + auth["last_updated_time"] = t.String() + } + auth["created_by"] = v.CreatedBy + auth["is_system_defined"] = v.IsSystemDefined + auth["authorization_policy_type"] = flattenAuthorizationPolicyType(v.AuthorizationPolicyType) + + auths[k] = auth + } + log.Printf("[DEBUG] flattenAuthorizationPolicyEntities return: %+v", auths[0]) + return auths + } + log.Printf("[DEBUG] flattenAuthorizationPolicyEntities return nil") + return nil +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_authorization_policies_v2_test.go b/nutanix/services/v2/iamv2/data_source_nutanix_authorization_policies_v2_test.go new file mode 100644 index 000000000..18adbed3c --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_authorization_policies_v2_test.go @@ -0,0 +1,141 @@ +package iamv2_test + +import ( + "fmt" + "os" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameAuthorizationPolicies = "data.nutanix_authorization_policies_v2.test" + +const authPolicy = ` +data "nutanix_operations_v2" "test" { + limit = 3 +} + +resource "nutanix_roles_v2" "test" { + display_name = local.roles.display_name + description = local.roles.description + operations = [ + data.nutanix_operations_v2.test.permissions[0].ext_id, + data.nutanix_operations_v2.test.permissions[1].ext_id, + data.nutanix_operations_v2.test.permissions[2].ext_id, + ] + depends_on = [data.nutanix_operations_v2.test] +} +resource "nutanix_authorization_policy_v2" "auth_policy_test" { + role = nutanix_roles_v2.test.id + display_name = local.auth_policies.display_name + description = local.auth_policies.description + authorization_policy_type = local.auth_policies.authorization_policy_type + identities { + reserved = local.auth_policies.identities[0] + } + entities { + reserved = local.auth_policies.entities[0] + } + entities { + reserved = local.auth_policies.entities[1] + } + depends_on = [nutanix_roles_v2.test] +} + ` + +func TestAccNutanixAuthorizationPoliciesV2Datasource_Basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAuthorizationPoliciesDatasourceV4Config(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameAuthorizationPolicies, "auth_policies.#"), + ), + }, + }, + }) +} + +func TestAccNutanixAuthorizationPoliciesV2Datasource_WithFilter(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAuthorizationPoliciesDatasourceV4WithFilterConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameAuthorizationPolicies, "auth_policies.#"), + resource.TestCheckResourceAttr(datasourceNameAuthorizationPolicies, "auth_policies.0.display_name", testVars.Iam.AuthPolicies.DisplayName), + ), + }, + }, + }) +} + +func TestAccNutanixAuthorizationPoliciesV2Datasource_WithLimit(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAuthorizationPoliciesDatasourceV4WithLimitConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameAuthorizationPolicies, "auth_policies.#"), + resource.TestCheckResourceAttr(datasourceNameAuthorizationPolicies, "auth_policies.#", strconv.Itoa(testVars.Iam.AuthPolicies.Limit)), + ), + }, + }, + }) +} + +func testAuthorizationPoliciesDatasourceV4Config() string { + return ` + data "nutanix_authorization_policies_v2" "test"{} + ` +} + +func testAuthorizationPoliciesDatasourceV4WithFilterConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + auth_policies = local.config.iam.auth_policies + roles = local.config.iam.roles + } + + %s + + data "nutanix_authorization_policies_v2" "test" { + filter = "displayName eq '${local.auth_policies.display_name}'" + depends_on = [resource.nutanix_authorization_policy_v2.auth_policy_test] + } + + + `, filepath, authPolicy) +} + +func testAuthorizationPoliciesDatasourceV4WithLimitConfig(filepath string) string { + return fmt.Sprintf(` + locals{ + config = (jsondecode(file("%s"))) + auth_policies = local.config.iam.auth_policies + roles = local.config.iam.roles + } + + %s + + data "nutanix_authorization_policies_v2" "test" { + limit = local.auth_policies.limit + } + `, filepath, authPolicy) +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_authorization_policy_v2.go b/nutanix/services/v2/iamv2/data_source_nutanix_authorization_policy_v2.go new file mode 100644 index 000000000..b2d37c2d0 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_authorization_policy_v2.go @@ -0,0 +1,211 @@ +package iamv2 + +import ( + "context" + "encoding/json" + "log" + "reflect" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authz" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixAuthorizationPolicyV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixAuthorizationPolicyV2Read, + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Required: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "client_name": { + Type: schema.TypeString, + Computed: true, + }, + "identities": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "reserved": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "entities": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "reserved": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "role": { + Type: schema.TypeString, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + "is_system_defined": { + Type: schema.TypeBool, + Computed: true, + }, + "authorization_policy_type": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func DatasourceNutanixAuthorizationPolicyV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + log.Printf("[DEBUG] DatasourceNutanixAuthorizationPolicyV2Read \n") + conn := meta.(*conns.Client).IamAPI + + extID := d.Get("ext_id") + resp, err := conn.AuthAPIInstance.GetAuthorizationPolicyById(utils.StringPtr(extID.(string))) + if err != nil { + return diag.Errorf("error while fetching authorization polices: %v", err) + } + getResp := resp.Data.GetValue().(import1.AuthorizationPolicy) + + if err := d.Set("display_name", getResp.DisplayName); err != nil { + return diag.FromErr(err) + } + if err := d.Set("description", getResp.Description); err != nil { + return diag.FromErr(err) + } + if err := d.Set("client_name", getResp.ClientName); err != nil { + return diag.FromErr(err) + } + if err := d.Set("identities", flattenIdentityFilters(getResp.Identities)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("entities", flattenEntityFilters(getResp.Entities)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("role", getResp.Role); err != nil { + return diag.FromErr(err) + } + if getResp.CreatedTime != nil { + t := getResp.CreatedTime + if err := d.Set("created_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if getResp.LastUpdatedTime != nil { + t := getResp.LastUpdatedTime + if err := d.Set("last_updated_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if err := d.Set("created_by", getResp.CreatedBy); err != nil { + return diag.FromErr(err) + } + if err := d.Set("is_system_defined", getResp.IsSystemDefined); err != nil { + return diag.FromErr(err) + } + if err := d.Set("authorization_policy_type", flattenAuthorizationPolicyType(getResp.AuthorizationPolicyType)); err != nil { + return diag.FromErr(err) + } + + d.SetId(*getResp.ExtId) + return nil +} + +func flattenAuthorizationPolicyType(pr *import1.AuthorizationPolicyType) string { + if pr != nil { + if *pr == import1.AuthorizationPolicyType(2) { + return "USER_DEFINED" + } + if *pr == import1.AuthorizationPolicyType(3) { + return "SERVICE_DEFINED" + } + if *pr == import1.AuthorizationPolicyType(4) { + return "PREDEFINED_READ_ONLY" + } + if *pr == import1.AuthorizationPolicyType(5) { + return "PREDEFINED_UPDATE_IDENTITY_ONLY" + } + if *pr == import1.AuthorizationPolicyType(6) { + return "SERVICE_DEFINED_READ_ONLY" + } + } + return "UNKNOWN" +} + +func flattenIdentityFilters(identityFilters []import1.IdentityFilter) []interface{} { + if len(identityFilters) > 0 { + identities := make([]interface{}, len(identityFilters)) + log.Printf("[DEBUG] flattenIdentityFilters \n") + for k, v := range identityFilters { + identity := make(map[string]interface{}) + log.Printf("[DEBUG] flattenIdentityFilters %v:%v\n", k, v) + log.Printf("[DEBUG] flattenIdentityFilters val type : %v\n", reflect.TypeOf(v)) + + reservedMap, err := json.Marshal(v.Reserved_) + + if err != nil { + log.Printf("[DEBUG] flattenIdentityFiltersError [%v]:%v err : %v\n", k, v, err) + } + log.Printf("[DEBUG] flattenIdentityFilters reserved : %v\n", string(reservedMap)) + identity["reserved"] = string(reservedMap) + + identities[k] = identity + } + return identities + } + return nil +} + +func flattenEntityFilters(entityFilters []import1.EntityFilter) []interface{} { + if len(entityFilters) > 0 { + entities := make([]interface{}, len(entityFilters)) + + for k, v := range entityFilters { + entity := make(map[string]interface{}) + log.Printf("[DEBUG] flattenIdentityFilters %v:%v\n", k, v) + log.Printf("[DEBUG] flattenIdentityFilters val type : %v\n", reflect.TypeOf(v)) + reservedMap, err := json.Marshal(v.Reserved_) + + if err != nil { + log.Printf("[DEBUG] flattenIdentityFiltersError [%v]:%v err : %v\n", k, v, err) + } + log.Printf("[DEBUG] flattenIdentityFilters reserved : %v\n", string(reservedMap)) + entity["reserved"] = string(reservedMap) + entities[k] = entity + } + return entities + } + return nil +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_authorization_policy_v2_test.go b/nutanix/services/v2/iamv2/data_source_nutanix_authorization_policy_v2_test.go new file mode 100644 index 000000000..b1776af13 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_authorization_policy_v2_test.go @@ -0,0 +1,60 @@ +package iamv2_test + +import ( + "fmt" + "os" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameAuthorizationPolicy = "data.nutanix_authorization_policy_v2.test" + +func TestAccNutanixAuthorizationPolicyV2Datasource_Basic(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAuthorizationPolicyDatasourceV2Config(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameAuthorizationPolicy, "ext_id"), + resource.TestCheckResourceAttr(datasourceNameAuthorizationPolicy, "display_name", testVars.Iam.AuthPolicies.DisplayName), + resource.TestCheckResourceAttr(datasourceNameAuthorizationPolicy, "description", testVars.Iam.AuthPolicies.Description), + resource.TestCheckResourceAttr(datasourceNameAuthorizationPolicy, "authorization_policy_type", testVars.Iam.AuthPolicies.AuthPolicyType), + resource.TestCheckResourceAttr(datasourceNameAuthorizationPolicy, "identities.#", strconv.Itoa(len(testVars.Iam.AuthPolicies.Identities))), + resource.TestCheckResourceAttr(datasourceNameAuthorizationPolicy, "identities.0.reserved", testVars.Iam.AuthPolicies.Identities[0]), + resource.TestCheckResourceAttr(datasourceNameAuthorizationPolicy, "entities.#", strconv.Itoa(len(testVars.Iam.AuthPolicies.Entities))), + resource.TestCheckResourceAttr(datasourceNameAuthorizationPolicy, "entities.0.reserved", testVars.Iam.AuthPolicies.Entities[0]), + resource.TestCheckResourceAttr(datasourceNameAuthorizationPolicy, "entities.1.reserved", testVars.Iam.AuthPolicies.Entities[1]), + ), + }, + }, + }) +} + +func testAuthorizationPolicyDatasourceV2Config(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + auth_policies = local.config.iam.auth_policies + roles = local.config.iam.roles + } + + %s + + data "nutanix_authorization_policy_v2" "test" { + ext_id = nutanix_authorization_policy_v2.auth_policy_test.id + depends_on = [nutanix_authorization_policy_v2.auth_policy_test] + } + + + `, filepath, authPolicy) +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_directory_service_v2.go b/nutanix/services/v2/iamv2/data_source_nutanix_directory_service_v2.go new file mode 100644 index 000000000..0f6ebd77a --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_directory_service_v2.go @@ -0,0 +1,313 @@ +package iamv2 + +import ( + "context" + "encoding/json" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authn" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixDirectoryServiceV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixDirectoryServiceV2Read, + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "url": { + Type: schema.TypeString, + Computed: true, + }, + "secondary_urls": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "domain_name": { + Type: schema.TypeString, + Computed: true, + }, + "directory_type": { + Type: schema.TypeString, + Computed: true, + }, + "service_account": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "username": { + Type: schema.TypeString, + Computed: true, + }, + "password": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "open_ldap_configuration": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_configuration": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_object_class": { + Type: schema.TypeString, + Computed: true, + }, + "user_search_base": { + Type: schema.TypeString, + Computed: true, + }, + "username_attribute": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "user_group_configuration": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_object_class": { + Type: schema.TypeString, + Computed: true, + }, + "group_search_base": { + Type: schema.TypeString, + Computed: true, + }, + "group_member_attribute": { + Type: schema.TypeString, + Computed: true, + }, + "group_member_attribute_value": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "group_search_type": { + Type: schema.TypeString, + Computed: true, + }, + "white_listed_groups": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true}, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func DatasourceNutanixDirectoryServiceV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + extID := d.Get("ext_id") + resp, err := conn.DirectoryServiceAPIInstance.GetDirectoryServiceById(utils.StringPtr(extID.(string))) + if err != nil { + var errordata map[string]interface{} + e := json.Unmarshal([]byte(err.Error()), &errordata) + if e != nil { + return diag.FromErr(e) + } + data := errordata["data"].(map[string]interface{}) + errorList := data["error"].([]interface{}) + errorMessage := errorList[0].(map[string]interface{}) + return diag.Errorf("error while fetching address group : %v", errorMessage["message"]) + } + + getResp := resp.Data.GetValue().(import1.DirectoryService) + + if err := d.Set("name", getResp.Name); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("url", getResp.Url); err != nil { + return diag.FromErr(err) + } + if err := d.Set("secondary_urls", getResp.SecondaryUrls); err != nil { + return diag.FromErr(err) + } + if err := d.Set("domain_name", getResp.DomainName); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("directory_type", flattenDirectoryType(getResp.DirectoryType)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("service_account", flattenDsServiceAccount(getResp.ServiceAccount)); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("open_ldap_configuration", flattenOpenLdapConfig(getResp.OpenLdapConfiguration)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("group_search_type", flattenGroupSearchType(getResp.GroupSearchType)); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("white_listed_groups", getResp.WhiteListedGroups); err != nil { + return diag.FromErr(err) + } + + if getResp.CreatedTime != nil { + t := getResp.CreatedTime + if err := d.Set("created_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if getResp.LastUpdatedTime != nil { + t := getResp.LastUpdatedTime + if err := d.Set("last_updated_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + + if err := d.Set("created_by", getResp.CreatedBy); err != nil { + return diag.FromErr(err) + } + + d.SetId(*getResp.ExtId) + return nil +} + +func flattenDirectoryType(pr *import1.DirectoryType) string { + if pr != nil { + const two, three = 2, 3 + + if *pr == import1.DirectoryType(two) { + return "ACTIVE_DIRECTORY" + } + if *pr == import1.DirectoryType(three) { + return "OPEN_LDAP" + } + } + return "UNKNOWN" +} + +func flattenDsServiceAccount(pr *import1.DsServiceAccount) []map[string]interface{} { + if pr != nil { + accs := make([]map[string]interface{}, 0) + acc := make(map[string]interface{}) + + acc["username"] = pr.Username + acc["password"] = pr.Password + + accs = append(accs, acc) + return accs + } + return nil +} + +func flattenOpenLdapConfig(pr *import1.OpenLdapConfig) []map[string]interface{} { + if pr != nil { + accs := make([]map[string]interface{}, 0) + acc := make(map[string]interface{}) + + if pr.UserConfiguration != nil { + acc["user_configuration"] = flattenUserConfiguration(pr.UserConfiguration) + } + if pr.UserGroupConfiguration != nil { + acc["user_group_configuration"] = flattenUserGroupConfiguration(pr.UserGroupConfiguration) + } + accs = append(accs, acc) + return accs + } + return nil +} + +func flattenUserConfiguration(pr *import1.UserConfiguration) []map[string]interface{} { + if pr != nil { + configs := make([]map[string]interface{}, 0) + cfg := make(map[string]interface{}) + + if pr.UsernameAttribute != nil { + cfg["username_attribute"] = pr.UsernameAttribute + } + if pr.UserSearchBase != nil { + cfg["user_search_base"] = pr.UserSearchBase + } + if pr.UserObjectClass != nil { + cfg["user_object_class"] = pr.UserObjectClass + } + + configs = append(configs, cfg) + return configs + } + return nil +} + +func flattenUserGroupConfiguration(pr *import1.UserGroupConfiguration) []map[string]interface{} { + if pr != nil { + groups := make([]map[string]interface{}, 0) + grp := make(map[string]interface{}) + + if pr.GroupMemberAttribute != nil { + grp["group_object_class"] = pr.GroupMemberAttribute + } + if pr.GroupMemberAttributeValue != nil { + grp["group_search_base"] = pr.GroupMemberAttributeValue + } + if pr.GroupObjectClass != nil { + grp["group_member_attribute"] = pr.GroupObjectClass + } + if pr.GroupSearchBase != nil { + grp["group_member_attribute_value"] = pr.GroupSearchBase + } + + groups = append(groups, grp) + return groups + } + return nil +} + +func flattenGroupSearchType(pr *import1.GroupSearchType) string { + const two, three = 2, 3 + if pr != nil { + if *pr == import1.GroupSearchType(two) { + return "NON_RECURSIVE" + } + if *pr == import1.GroupSearchType(three) { + return "RECURSIVE" + } + } + return "UNKNOWN" +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_directory_service_v2_test.go b/nutanix/services/v2/iamv2/data_source_nutanix_directory_service_v2_test.go new file mode 100644 index 000000000..0008184b9 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_directory_service_v2_test.go @@ -0,0 +1,70 @@ +package iamv2_test + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameDirectoryService = "data.nutanix_directory_services_v2.test" + +func TestAccNutanixDirectoryServiceV2Datasource_Basic(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testDirectoryServiceDatasourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameDirectoryService, "ext_id"), + resource.TestCheckResourceAttr(datasourceNameDirectoryService, "name", testVars.Iam.DirectoryServices.Name), + resource.TestCheckResourceAttr(datasourceNameDirectoryService, "domain_name", testVars.Iam.DirectoryServices.DomainName), + resource.TestCheckResourceAttr(datasourceNameDirectoryService, "directory_type", "ACTIVE_DIRECTORY"), + resource.TestCheckResourceAttr(datasourceNameDirectoryService, "url", testVars.Iam.DirectoryServices.Url), + resource.TestCheckResourceAttr(datasourceNameDirectoryService, "service_account.0.username", testVars.Iam.DirectoryServices.ServiceAccount.Username), + resource.TestCheckResourceAttrSet(datasourceNameDirectoryService, "service_account.0.password"), + resource.TestCheckResourceAttr(datasourceNameDirectoryService, "white_listed_groups.0", testVars.Iam.DirectoryServices.WhiteListedGroups[0]), + ), + }, + }, + }) +} + +func testDirectoryServiceDatasourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test" { + name = local.directory_services.name + url = local.directory_services.url + directory_type = "ACTIVE_DIRECTORY" + domain_name = local.directory_services.domain_name + service_account { + username = local.directory_services.service_account.username + password = local.directory_services.service_account.password + } + white_listed_groups = [ local.directory_services.white_listed_groups[0]] + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + } + + data "nutanix_directory_service_v2" "test" { + ext_id = resource.nutanix_directory_services_v2.test.id + depends_on = [resource.nutanix_directory_services_v2.test] + } + `, filepath) +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_directory_services_v2.go b/nutanix/services/v2/iamv2/data_source_nutanix_directory_services_v2.go new file mode 100644 index 000000000..9cc3a6e71 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_directory_services_v2.go @@ -0,0 +1,274 @@ +package iamv2 + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authn" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixDirectoryServicesV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixDirectoryServicesV2Read, + Schema: map[string]*schema.Schema{ + "page": { + Type: schema.TypeInt, + Optional: true, + }, + "limit": { + Type: schema.TypeInt, + Optional: true, + }, + "filter": { + Type: schema.TypeString, + Optional: true, + }, + "order_by": { + Type: schema.TypeString, + Optional: true, + }, + "select": { + Type: schema.TypeString, + Optional: true, + }, + "directory_services": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "url": { + Type: schema.TypeString, + Computed: true, + }, + "secondary_urls": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "domain_name": { + Type: schema.TypeString, + Computed: true, + }, + "directory_type": { + Type: schema.TypeString, + Computed: true, + }, + "service_account": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "username": { + Type: schema.TypeString, + Computed: true, + }, + "password": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "open_ldap_configuration": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_configuration": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_object_class": { + Type: schema.TypeString, + Computed: true, + }, + "user_search_base": { + Type: schema.TypeString, + Computed: true, + }, + "username_attribute": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "user_group_configuration": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_object_class": { + Type: schema.TypeString, + Computed: true, + }, + "group_search_base": { + Type: schema.TypeString, + Computed: true, + }, + "group_member_attribute": { + Type: schema.TypeString, + Computed: true, + }, + "group_member_attribute_value": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "group_search_type": { + Type: schema.TypeString, + Computed: true, + }, + "white_listed_groups": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true}, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DatasourceNutanixDirectoryServicesV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + // initialize query params + var filter, orderBy, selects *string + var page, limit *int + + if pagef, ok := d.GetOk("page"); ok { + page = utils.IntPtr(pagef.(int)) + } else { + page = nil + } + if limitf, ok := d.GetOk("limit"); ok { + limit = utils.IntPtr(limitf.(int)) + } else { + limit = nil + } + if filterf, ok := d.GetOk("filter"); ok { + filter = utils.StringPtr(filterf.(string)) + } else { + filter = nil + } + if order, ok := d.GetOk("order_by"); ok { + orderBy = utils.StringPtr(order.(string)) + } else { + orderBy = nil + } + if selectf, ok := d.GetOk("select"); ok { + selects = utils.StringPtr(selectf.(string)) + } else { + selects = nil + } + + resp, err := conn.DirectoryServiceAPIInstance.ListDirectoryServices(page, limit, filter, orderBy, selects) + if err != nil { + fmt.Println(err) + var errordata map[string]interface{} + e := json.Unmarshal([]byte(err.Error()), &errordata) + if e != nil { + return diag.FromErr(e) + } + data := errordata["data"].(map[string]interface{}) + errorList := data["error"].([]interface{}) + errorMessage := errorList[0].(map[string]interface{}) + return diag.Errorf("error while fetching directory services: %v", errorMessage["message"]) + } + + getResp := resp.Data.GetValue().([]import1.DirectoryService) + if err := d.Set("directory_services", flattenDirectoryServicesEntities(getResp)); err != nil { + return diag.FromErr(err) + } + + d.SetId(resource.UniqueId()) + return nil +} + +func flattenDirectoryServicesEntities(pr []import1.DirectoryService) []interface{} { + if len(pr) > 0 { + dsList := make([]interface{}, len(pr)) + + for k, v := range pr { + ds := make(map[string]interface{}) + + ds["ext_id"] = v.ExtId + ds["name"] = v.Name + ds["domain_name"] = v.DomainName + if v.Url != nil { + ds["url"] = v.Url + } + if v.SecondaryUrls != nil { + ds["secondary_urls"] = v.SecondaryUrls + } + if v.DirectoryType != nil { + ds["directory_type"] = flattenDirectoryType(v.DirectoryType) + } + if v.ServiceAccount != nil { + ds["service_account"] = flattenDsServiceAccount(v.ServiceAccount) + } + if v.OpenLdapConfiguration != nil { + ds["open_ldap_configuration"] = flattenOpenLdapConfig(v.OpenLdapConfiguration) + } + if v.GroupSearchType != nil { + ds["group_search_type"] = flattenGroupSearchType(v.GroupSearchType) + } + if v.WhiteListedGroups != nil { + ds["white_listed_groups"] = v.WhiteListedGroups + } + if v.CreatedTime != nil { + t := v.CreatedTime + ds["created_time"] = t.String() + } + if v.LastUpdatedTime != nil { + t := v.LastUpdatedTime + ds["last_updated_time"] = t.String() + } + if v.CreatedBy != nil { + ds["created_by"] = v.CreatedBy + } + + dsList[k] = ds + } + return dsList + } + return nil +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_directory_services_v2_test.go b/nutanix/services/v2/iamv2/data_source_nutanix_directory_services_v2_test.go new file mode 100644 index 000000000..a5c589fda --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_directory_services_v2_test.go @@ -0,0 +1,171 @@ +package iamv2_test + +import ( + "fmt" + "os" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameDirectoryServices = "data.nutanix_directory_services_v2.test" + +func TestAccNutanixDirectoryServicesV2Datasource_Basic(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testDirectoryServicesDatasourceV2Config(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameDirectoryServices, "directory_services.#"), + resource.TestCheckResourceAttrSet(datasourceNameDirectoryServices, "directory_services.0.name"), + resource.TestCheckResourceAttrSet(datasourceNameDirectoryServices, "directory_services.0.url"), + resource.TestCheckResourceAttrSet(datasourceNameDirectoryServices, "directory_services.0.domain_name"), + ), + }, + }, + }) +} + +func TestAccNutanixDirectoryServicesV2Datasource_WithFilter(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testDirectoryServicesDatasourceV2WithFilterConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameDirectoryServices, "directory_services.#"), + resource.TestCheckResourceAttrSet(datasourceNameDirectoryServices, "directory_services.0.ext_id"), + resource.TestCheckResourceAttr(datasourceNameDirectoryServices, "directory_services.0.name", testVars.Iam.DirectoryServices.Name), + resource.TestCheckResourceAttr(datasourceNameDirectoryServices, "directory_services.0.domain_name", testVars.Iam.DirectoryServices.DomainName), + resource.TestCheckResourceAttr(datasourceNameDirectoryServices, "directory_services.0.directory_type", "ACTIVE_DIRECTORY"), + resource.TestCheckResourceAttr(datasourceNameDirectoryServices, "directory_services.0.url", testVars.Iam.DirectoryServices.Url), + resource.TestCheckResourceAttr(datasourceNameDirectoryServices, "directory_services.0.service_account.0.username", testVars.Iam.DirectoryServices.ServiceAccount.Username), + resource.TestCheckResourceAttrSet(datasourceNameDirectoryServices, "directory_services.0.service_account.0.password"), + resource.TestCheckResourceAttr(datasourceNameDirectoryServices, "directory_services.0.white_listed_groups.0", testVars.Iam.DirectoryServices.WhiteListedGroups[0]), + ), + }, + }, + }) +} + +func TestAccNutanixDirectoryServicesV2Datasource_WithLimit(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testDirectoryServicesDatasourceV2WithLimitConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameDirectoryServices, "directory_services.#"), + resource.TestCheckResourceAttr(datasourceNameDirectoryServices, "directory_services.#", strconv.Itoa(testVars.Iam.DirectoryServices.Limit)), + ), + }, + }, + }) +} + +func testDirectoryServicesDatasourceV2Config(filepath string) string { + return fmt.Sprintf(` + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test" { + name = local.directory_services.name + url = local.directory_services.url + directory_type = "ACTIVE_DIRECTORY" + domain_name = local.directory_services.domain_name + service_account { + username = local.directory_services.service_account.username + password = local.directory_services.service_account.password + } + white_listed_groups = [ local.directory_services.white_listed_groups[0]] + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + } + data "nutanix_directory_services_v2" "test"{ + depends_on = [resource.nutanix_directory_services_v2.test] + } + `, filepath) +} + +func testDirectoryServicesDatasourceV2WithFilterConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test" { + name = local.directory_services.name + url = local.directory_services.url + directory_type = "ACTIVE_DIRECTORY" + domain_name = local.directory_services.domain_name + service_account { + username = local.directory_services.service_account.username + password = local.directory_services.service_account.password + } + white_listed_groups = [ local.directory_services.white_listed_groups[0]] + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + } + + data "nutanix_directory_services_v2" "test" { + filter = "name eq '${resource.nutanix_directory_services_v2.test.name}'" + depends_on = [resource.nutanix_directory_services_v2.test] + } + `, filepath) +} + +func testDirectoryServicesDatasourceV2WithLimitConfig(filepath string) string { + return fmt.Sprintf(` + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test" { + name = local.directory_services.name + url = local.directory_services.url + directory_type = "ACTIVE_DIRECTORY" + domain_name = local.directory_services.domain_name + service_account { + username = local.directory_services.service_account.username + password = local.directory_services.service_account.password + } + white_listed_groups = [ local.directory_services.white_listed_groups[0]] + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + } + + data "nutanix_directory_services_v2" "test" { + limit = local.directory_services.limit + depends_on = [resource.nutanix_directory_services_v2.test] + } + `, filepath) +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_operation_v2.go b/nutanix/services/v2/iamv2/data_source_nutanix_operation_v2.go new file mode 100644 index 000000000..30e6b63ec --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_operation_v2.go @@ -0,0 +1,194 @@ +package iamv2 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authz" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixOperationV2() *schema.Resource { + + return &schema.Resource{ + ReadContext: DatasourceNutanixOperationV4Read, + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Required: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "entity_type": { + Type: schema.TypeString, + Computed: true, + }, + "operation_type": { + Type: schema.TypeString, + Computed: true, + }, + "client_name": { + Type: schema.TypeString, + Computed: true, + }, + "related_operation_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "associated_endpoint_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "endpoint_url": { + Type: schema.TypeString, + Computed: true, + }, + "http_method": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func DatasourceNutanixOperationV4Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + extID := d.Get("ext_id") + + resp, err := conn.OperationsAPIInstance.GetOperationById(utils.StringPtr(extID.(string))) + if err != nil { + return diag.Errorf("error while fetching image placement : %v", err) + } + + getResp := resp.Data.GetValue().(import1.Operation) + + if err := d.Set("display_name", getResp.DisplayName); err != nil { + return diag.FromErr(err) + } + if err := d.Set("description", getResp.Description); err != nil { + return diag.FromErr(err) + } + if err := d.Set("entity_type", getResp.EntityType); err != nil { + return diag.FromErr(err) + } + if err := d.Set("operation_type", flattenOperationType(getResp.OperationType)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("client_name", getResp.ClientName); err != nil { + return diag.FromErr(err) + } + if err := d.Set("related_operation_list", utils.StringSlice(getResp.RelatedOperationList)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("associated_endpoint_list", flattenAssociatedEndpointList(getResp.AssociatedEndpointList)); err != nil { + return diag.FromErr(err) + } + if getResp.CreatedTime != nil { + t := getResp.CreatedTime + if err := d.Set("created_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if getResp.LastUpdatedTime != nil { + t := getResp.LastUpdatedTime + if err := d.Set("last_updated_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + d.SetId(*getResp.ExtId) + return nil +} + +func flattenOperationType(pr *import1.OperationType) string { + if pr != nil { + if *pr == import1.OperationType(2) { + return "INTERNAL" + } + if *pr == import1.OperationType(3) { + return "SYSTEM_DEFINED_ONLY" + } + if *pr == import1.OperationType(4) { + return "EXTERNAL" + } + } + return "UNKNOWN" +} + +func flattenAssociatedEndpointList(pr []import1.AssociatedEndpoint) []map[string]interface{} { + if len(pr) > 0 { + endpoints := make([]map[string]interface{}, len(pr)) + for _, v := range pr { + endpoint := make(map[string]interface{}) + + endpoint["api_version"] = flattenApiVersion(v.ApiVersion) + endpoint["endpoint_url"] = v.EndpointUrl + endpoint["http_method"] = flattenHttpMethod(v.HttpMethod) + + endpoints = append(endpoints, endpoint) + } + return endpoints + } + return nil +} + +func flattenApiVersion(pr *import1.ApiVersion) string { + if pr != nil { + if *pr == import1.ApiVersion(2) { + return "V3" + } + if *pr == import1.ApiVersion(3) { + return "V4" + } + } + return "UNKNOWN" +} + +func flattenHttpMethod(pr *import1.HttpMethod) string { + if pr != nil { + if *pr == import1.HttpMethod(2) { + return "HTTPMETHOD_POST" + } + if *pr == import1.HttpMethod(3) { + return "HTTPMETHOD_GET" + } + if *pr == import1.HttpMethod(4) { + return "HTTPMETHOD_PUT" + } + if *pr == import1.HttpMethod(5) { + return "HTTPMETHOD_PATCH" + } + if *pr == import1.HttpMethod(6) { + return "HTTPMETHOD_DELETE" + } + } + return "UNKNOWN" +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_operations_v2.go b/nutanix/services/v2/iamv2/data_source_nutanix_operations_v2.go new file mode 100644 index 000000000..f15bbdf24 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_operations_v2.go @@ -0,0 +1,206 @@ +package iamv2 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authz" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixOperationsV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixOperationsV4Read, + Schema: map[string]*schema.Schema{ + "page": { + Type: schema.TypeInt, + Optional: true, + }, + "limit": { + Type: schema.TypeInt, + Optional: true, + }, + "filter": { + Type: schema.TypeString, + Optional: true, + }, + "order_by": { + Type: schema.TypeString, + Optional: true, + }, + "select": { + Type: schema.TypeString, + Optional: true, + }, + "permissions": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Required: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "entity_type": { + Type: schema.TypeString, + Computed: true, + }, + "operation_type": { + Type: schema.TypeString, + Computed: true, + }, + "client_name": { + Type: schema.TypeString, + Computed: true, + }, + "related_operation_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "associated_endpoint_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "api_version": { + Type: schema.TypeString, + Computed: true, + }, + "endpoint_url": { + Type: schema.TypeString, + Computed: true, + }, + "http_method": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DatasourceNutanixOperationsV4Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + // initialize query params + var filter, orderBy, selects *string + var page, limit *int + + if pagef, ok := d.GetOk("page"); ok { + page = utils.IntPtr(pagef.(int)) + } else { + page = nil + } + if limitf, ok := d.GetOk("limit"); ok { + limit = utils.IntPtr(limitf.(int)) + } else { + limit = nil + } + if filterf, ok := d.GetOk("filter"); ok { + filter = utils.StringPtr(filterf.(string)) + } else { + filter = nil + } + if order, ok := d.GetOk("order_by"); ok { + orderBy = utils.StringPtr(order.(string)) + } else { + orderBy = nil + } + if selectf, ok := d.GetOk("select"); ok { + selects = utils.StringPtr(selectf.(string)) + } else { + selects = nil + } + + resp, err := conn.OperationsAPIInstance.ListOperations(page, limit, filter, orderBy, selects) + if err != nil { + return diag.Errorf("error while fetching permissions : %v", err) + } + + getResp := resp.Data + + if getResp != nil { + permissions := getResp.GetValue().([]import1.Operation) + if err := d.Set("permissions", flattenPermissionEntities(permissions)); err != nil { + return diag.FromErr(err) + } + } + + d.SetId(resource.UniqueId()) + return nil +} + +func flattenPermissionEntities(pr []import1.Operation) []interface{} { + if len(pr) > 0 { + permissions := make([]interface{}, len(pr)) + + for k, v := range pr { + permission := make(map[string]interface{}) + + if v.ExtId != nil { + permission["ext_id"] = v.ExtId + } + if v.DisplayName != nil { + permission["display_name"] = v.DisplayName + } + if v.Description != nil { + permission["description"] = v.Description + } + if v.EntityType != nil { + permission["entity_type"] = v.EntityType + } + if v.OperationType != nil { + permission["operation_type"] = flattenOperationType(v.OperationType) + } + if v.ClientName != nil { + permission["client_name"] = v.ClientName + } + if v.RelatedOperationList != nil { + permission["related_operation_list"] = utils.StringSlice(v.RelatedOperationList) + } + + if v.AssociatedEndpointList != nil { + permission["associated_endpoint_list"] = flattenAssociatedEndpointList(v.AssociatedEndpointList) + } + if v.CreatedTime != nil { + t := v.CreatedTime + permission["created_time"] = t.String() + } + if v.LastUpdatedTime != nil { + t := v.LastUpdatedTime + permission["last_updated_time"] = t.String() + } + permissions[k] = permission + } + return permissions + } + return nil +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_role_v2.go b/nutanix/services/v2/iamv2/data_source_nutanix_role_v2.go new file mode 100644 index 000000000..ae53e6db0 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_role_v2.go @@ -0,0 +1,182 @@ +package iamv2 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + iamConfig "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authz" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" +) + +// List Role(s) +func DatasourceNutanixRoleV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixRoleV2Read, + Schema: map[string]*schema.Schema{ + "ext_id": { + Description: "ExtId for the Role.", + Type: schema.TypeString, + Required: true, + }, + "tenant_id": { + Type: schema.TypeString, + Computed: true, + }, + "links": { + Description: "A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Description: "The URL at which the entity described by the link can be accessed.", + Type: schema.TypeString, + Computed: true, + }, + "rel": { + Description: "A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of \"self\" identifies the URL for the object.", + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "display_name": { + Description: "The display name for the Role.", + Type: schema.TypeString, + Computed: true, + }, + "description": { + Description: "Description of the Role.", + Type: schema.TypeString, + Computed: true, + }, + "client_name": { + Description: "Client that created the entity.", + Type: schema.TypeString, + Computed: true, + }, + "operations": { + Description: "List of Operations for the Role.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Description: "List of String", + Type: schema.TypeString, + }, + }, + "accessible_clients": { + Description: "List of Accessible Clients for the Role.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Description: "List of String", + Type: schema.TypeString, + }, + }, + "accessible_entity_types": { + Description: "List of Accessible Entity Types for the Role.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Description: "List of String", + Type: schema.TypeString, + }, + }, + "assigned_users_count": { + Description: "Number of Users assigned to given Role.", + Type: schema.TypeInt, + Computed: true, + }, + "assigned_users_groups_count": { + Description: "Number of User Groups assigned to given Role.", + Type: schema.TypeInt, + Computed: true, + }, + "created_time": { + Description: "The creation time of the Role.", + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Description: "The time when the Role was last updated.", + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Description: "User or Service Name that created the Role.", + Type: schema.TypeString, + Computed: true, + }, + "is_system_defined": { + Description: "Flag identifying if the Role is system defined or not.", + Type: schema.TypeBool, + Computed: true, + }, + }, + } +} + +func DatasourceNutanixRoleV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + roleExtId := d.Get("ext_id").(string) + + resp, err := conn.RolesAPIInstance.GetRoleById(&roleExtId) + if err != nil { + return diag.Errorf("error while fetching role: %v", err) + } + + getResp := resp.Data.GetValue().(iamConfig.Role) + + if err := d.Set("links", flattenLinks(getResp.Links)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("display_name", getResp.DisplayName); err != nil { + return diag.FromErr(err) + } + if err := d.Set("description", getResp.Description); err != nil { + return diag.FromErr(err) + } + if err := d.Set("client_name", getResp.ClientName); err != nil { + return diag.FromErr(err) + } + if err := d.Set("operations", getResp.Operations); err != nil { + return diag.FromErr(err) + } + if err := d.Set("accessible_clients", getResp.AccessibleClients); err != nil { + return diag.FromErr(err) + } + if err := d.Set("accessible_entity_types", getResp.AccessibleEntityTypes); err != nil { + return diag.FromErr(err) + } + if err := d.Set("assigned_users_count", getResp.AssignedUsersCount); err != nil { + return diag.FromErr(err) + } + if err := d.Set("assigned_users_groups_count", getResp.AssignedUserGroupsCount); err != nil { + return diag.FromErr(err) + } + if getResp.CreatedTime != nil { + t := getResp.CreatedTime + if err := d.Set("created_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if getResp.LastUpdatedTime != nil { + t := getResp.LastUpdatedTime + if err := d.Set("last_updated_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if err := d.Set("created_by", getResp.CreatedBy); err != nil { + return diag.FromErr(err) + } + if err := d.Set("is_system_defined", getResp.IsSystemDefined); err != nil { + return diag.FromErr(err) + } + + d.SetId(*getResp.ExtId) + return nil +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_role_v2_test.go b/nutanix/services/v2/iamv2/data_source_nutanix_role_v2_test.go new file mode 100644 index 000000000..c3e586165 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_role_v2_test.go @@ -0,0 +1,62 @@ +package iamv2_test + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameRole = "data.nutanix_role_v2.test" + +func TestAccNutanixRolesV4Datasource_Basic_Role(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testRoleDatasourceV4Config(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameRole, "display_name"), + resource.TestCheckResourceAttr(datasourceNameRole, "display_name", testVars.Iam.Roles.DisplayName), + resource.TestCheckResourceAttr(datasourceNameRole, "description", testVars.Iam.Roles.Description), + ), + }, + }, + }) +} + +func testRoleDatasourceV4Config(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + roles = local.config.iam.roles + } + + data "nutanix_operations_v2" "test" { + limit = 3 + } + + resource "nutanix_roles_v2" "test" { + display_name = local.roles.display_name + description = local.roles.description + operations = [ + data.nutanix_operations_v2.test.permissions[0].ext_id, + data.nutanix_operations_v2.test.permissions[1].ext_id, + data.nutanix_operations_v2.test.permissions[2].ext_id, + ] + depends_on = [data.nutanix_operations_v2.test] + } + + data "nutanix_role_v2" "test" { + ext_id = resource.nutanix_roles_v2.test.id + } + `, filepath) +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_roles_v2.go b/nutanix/services/v2/iamv2/data_source_nutanix_roles_v2.go new file mode 100644 index 000000000..2861863e1 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_roles_v2.go @@ -0,0 +1,284 @@ +package iamv2 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + iamResponse "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/common/v1/response" + iamConfig "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authz" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +// List Role(s) +func DatasourceNutanixRolesV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixRolesV2Read, + Schema: map[string]*schema.Schema{ + "page": { + Description: "A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results.", + Type: schema.TypeInt, + Optional: true, + }, + "limit": { + Description: "A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set.", + Type: schema.TypeInt, + Optional: true, + }, + "filter": { + Description: "A URL query parameter that allows clients to filter a collection of resources. The expression specified with $filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the $filter must conform to the OData V4.01 URL conventions. For example, filter '$filter=name eq 'karbon-ntnx-1.0' would filter the result on cluster name 'karbon-ntnx1.0', filter '$filter=startswith(name, 'C')' would filter on cluster name starting with 'C'. The filter can be applied to the following fields: clientName, createdBy, createdTime, displayName, extId, isSystemDefined, lastUpdatedTime", + Type: schema.TypeString, + Optional: true, + }, + "order_by": { + Description: "A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default. For example, '$orderby=templateName desc' would get all templates sorted by templateName in descending order. The orderby can be applied to the following fields: clientName, displayName, extId, lastUpdatedTime", + Type: schema.TypeString, + Optional: true, + }, + "select": { + Description: "A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the $select must conform to the OData V4.01 URL conventions. If a $select expression consists of a single select item that is an asterisk (i.e., *), then all properties on the matching resource will be returned. The select can be applied to the following fields: accessibleClients, accessibleEntityTypes, assignedUserGroupsCount, assignedUsersCount, clientName, createdBy, createdTime, description, displayName, extId, isSystemDefined, lastUpdatedTime, links, operations, tenantId", + Type: schema.TypeString, + Optional: true, + }, + "roles": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "tenant_id": { + Type: schema.TypeString, + Computed: true, + }, + "ext_id": { + Type: schema.TypeString, + Computed: true, + }, + "links": { + Description: "A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Description: "The URL at which the entity described by the link can be accessed.", + Type: schema.TypeString, + Computed: true, + }, + "rel": { + Description: "A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of \"self\" identifies the URL for the object.", + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "display_name": { + Description: "The display name for the Role.", + Type: schema.TypeString, + Computed: true, + }, + "description": { + Description: "Description of the Role.", + Type: schema.TypeString, + Computed: true, + }, + "client_name": { + Description: "Client that created the entity.", + Type: schema.TypeString, + Computed: true, + }, + "operations": { + Description: "List of Operations for the Role.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Description: "List of String", + Type: schema.TypeString, + }, + }, + "accessible_clients": { + Description: "List of Accessible Clients for the Role.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Description: "List of String", + Type: schema.TypeString, + }, + }, + "accessible_entity_types": { + Description: "List of Accessible Entity Types for the Role.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Description: "List of String", + Type: schema.TypeString, + }, + }, + "assigned_users_count": { + Description: "Number of Users assigned to given Role.", + Type: schema.TypeInt, + Computed: true, + }, + "assigned_users_groups_count": { + Description: "Number of User Groups assigned to given Role.", + Type: schema.TypeInt, + Computed: true, + }, + "created_time": { + Description: "The creation time of the Role.", + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Description: "The time when the Role was last updated.", + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Description: "User or Service Name that created the Role.", + Type: schema.TypeString, + Computed: true, + }, + "is_system_defined": { + Description: "Flag identifying if the Role is system defined or not.", + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DatasourceNutanixRolesV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + // initialize query params + var filter, orderBy, selects *string + var page, limit *int + + if pagef, ok := d.GetOk("page"); ok { + page = utils.IntPtr(pagef.(int)) + } else { + page = nil + } + if limitf, ok := d.GetOk("limit"); ok { + limit = utils.IntPtr(limitf.(int)) + } else { + limit = nil + } + if filterf, ok := d.GetOk("filter"); ok { + filter = utils.StringPtr(filterf.(string)) + } else { + filter = nil + } + if order, ok := d.GetOk("order_by"); ok { + orderBy = utils.StringPtr(order.(string)) + } else { + orderBy = nil + } + if selectf, ok := d.GetOk("select"); ok { + selects = utils.StringPtr(selectf.(string)) + } else { + selects = nil + } + + resp, err := conn.RolesAPIInstance.ListRoles(page, limit, filter, orderBy, selects) + if err != nil { + return diag.Errorf("error while fetching roles: %v", err) + } + + getResp := resp.Data.GetValue().([]iamConfig.Role) + if err := d.Set("roles", flattenRolesEntities(getResp)); err != nil { + return diag.FromErr(err) + } + + d.SetId(resource.UniqueId()) + return nil +} + +func flattenRolesEntities(roles []iamConfig.Role) []interface{} { + if len(roles) > 0 { + rolesList := make([]interface{}, len(roles)) + + for k, v := range roles { + role := make(map[string]interface{}) + + if v.ExtId != nil { + role["ext_id"] = v.ExtId + } + if v.TenantId != nil { + role["tenant_id"] = v.TenantId + } + if v.Links != nil { + role["links"] = flattenLinks(v.Links) + } + if v.DisplayName != nil { + role["display_name"] = v.DisplayName + } + if v.Description != nil { + role["description"] = v.Description + } + if v.ClientName != nil { + role["client_name"] = v.ClientName + } + if v.Operations != nil { + role["operations"] = v.Operations + } + if v.AccessibleClients != nil { + role["accessible_clients"] = v.AccessibleClients + } + if v.AccessibleEntityTypes != nil { + role["accessible_entity_types"] = v.AccessibleEntityTypes + } + if v.AssignedUsersCount != nil { + role["assigned_users_count"] = v.AssignedUsersCount + } + if v.AssignedUserGroupsCount != nil { + role["assigned_users_groups_count"] = v.AssignedUserGroupsCount + } + if v.CreatedTime != nil { + t := v.CreatedTime + role["created_time"] = t.String() + } + if v.LastUpdatedTime != nil { + t := v.LastUpdatedTime + role["last_updated_time"] = t.String() + } + if v.CreatedBy != nil { + role["created_by"] = v.CreatedBy + } + if v.IsSystemDefined != nil { + role["is_system_defined"] = v.IsSystemDefined + } + + rolesList[k] = role + } + return rolesList + } + return nil +} + +func flattenLinks(apiLinks []iamResponse.ApiLink) []map[string]interface{} { + if len(apiLinks) > 0 { + apiLinkList := make([]map[string]interface{}, len(apiLinks)) + + for k, v := range apiLinks { + links := map[string]interface{}{} + if v.Href != nil { + links["href"] = v.Href + } + if v.Rel != nil { + links["rel"] = v.Rel + } + + apiLinkList[k] = links + } + return apiLinkList + } + return nil +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_roles_v2_test.go b/nutanix/services/v2/iamv2/data_source_nutanix_roles_v2_test.go new file mode 100644 index 000000000..e549a8e2d --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_roles_v2_test.go @@ -0,0 +1,119 @@ +package iamv2_test + +import ( + "fmt" + "os" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameRoles = "data.nutanix_roles_v2.test" + +func TestAccNutanixRolesV4Datasource_Basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testRolesDatasourceV4Config(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameRoles, "roles.#"), + resource.TestCheckResourceAttrSet(datasourceNameRoles, "roles.0.display_name"), + resource.TestCheckResourceAttrSet(datasourceNameRoles, "roles.0.operations.#"), + ), + }, + }, + }) +} + +func TestAccNutanixRolesV4Datasource_WithFilter(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testRolesDatasourceV4WithFilterConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameRoles, "roles.#"), + resource.TestCheckResourceAttr(datasourceNameRoles, "roles.#", "1"), + resource.TestCheckResourceAttr(datasourceNameRoles, "roles.0.display_name", testVars.Iam.Roles.DisplayName), + resource.TestCheckResourceAttr(datasourceNameRoles, "roles.0.description", testVars.Iam.Roles.Description), + ), + }, + }, + }) +} + +func TestAccNutanixRolesV4Datasource_WithLimit(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testRolesDatasourceV4WithLimitConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameRoles, "roles.#"), + resource.TestCheckResourceAttr(datasourceNameRoles, "roles.#", strconv.Itoa(testVars.Iam.Roles.Limit)), + ), + }, + }, + }) +} + +func testRolesDatasourceV4Config() string { + return ` + data "nutanix_roles_v2" "test"{} + ` +} + +func testRolesDatasourceV4WithFilterConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + roles = local.config.iam.roles + } + + data "nutanix_operations_v2" "test" { + limit = 3 + } + + resource "nutanix_roles_v2" "test" { + display_name = local.roles.display_name + description = local.roles.description + operations = [ + data.nutanix_operations_v2.test.permissions[0].ext_id, + data.nutanix_operations_v2.test.permissions[1].ext_id, + data.nutanix_operations_v2.test.permissions[2].ext_id, + ] + depends_on = [data.nutanix_operations_v2.test] + } + + data "nutanix_roles_v2" "test" { + filter = "displayName eq '${local.roles.display_name}'" + depends_on = [resource.nutanix_roles_v2.test] + } + `, filepath) +} + +func testRolesDatasourceV4WithLimitConfig(filepath string) string { + return fmt.Sprintf(` + locals{ + config = (jsondecode(file("%s"))) + roles = local.config.iam.roles + } + + data "nutanix_roles_v2" "test" { + limit = local.roles.limit + } + `, filepath) +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_saml_idp_v2.go b/nutanix/services/v2/iamv2/data_source_nutanix_saml_idp_v2.go new file mode 100644 index 000000000..f6b51fbd8 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_saml_idp_v2.go @@ -0,0 +1,226 @@ +package iamv2 + +import ( + "context" + "encoding/json" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authn" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixSamlIDPV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixSamlIDPV2Read, + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Required: true, + }, + "idp_metadata": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "entity_id": { + Type: schema.TypeString, + Computed: true, + }, + "login_url": { + Type: schema.TypeString, + Computed: true, + }, + "logout_url": { + Type: schema.TypeString, + Computed: true, + }, + "error_url": { + Type: schema.TypeString, + Computed: true, + }, + "certificate": { + Type: schema.TypeString, + Computed: true, + }, + "name_id_policy_format": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "username_attribute": { + Type: schema.TypeString, + Computed: true, + }, + "email_attribute": { + Type: schema.TypeString, + Computed: true, + }, + "groups_attribute": { + Type: schema.TypeString, + Computed: true, + }, + "groups_delim": { + Type: schema.TypeString, + Computed: true, + }, + "custom_attributes": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "entity_issuer": { + Type: schema.TypeString, + Computed: true, + }, + "is_signed_authn_req_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func DatasourceNutanixSamlIDPV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + extID := d.Get("ext_id") + + resp, err := conn.SamlIdentityAPIInstance.GetSamlIdentityProviderById(utils.StringPtr(extID.(string))) + if err != nil { + var errordata map[string]interface{} + e := json.Unmarshal([]byte(err.Error()), &errordata) + if e != nil { + return diag.FromErr(e) + } + data := errordata["data"].(map[string]interface{}) + errorList := data["error"].([]interface{}) + errorMessage := errorList[0].(map[string]interface{}) + return diag.Errorf("error while fetching saml identity providers: %v", errorMessage["message"]) + } + + getResp := resp.Data.GetValue().(import1.SamlIdentityProvider) + + if err := d.Set("name", getResp.Name); err != nil { + return diag.FromErr(err) + } + if err := d.Set("idp_metadata", flattenIdpMetadata(getResp.IdpMetadata)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("username_attribute", getResp.UsernameAttribute); err != nil { + return diag.FromErr(err) + } + if err := d.Set("email_attribute", getResp.EmailAttribute); err != nil { + return diag.FromErr(err) + } + if err := d.Set("groups_attribute", getResp.GroupsAttribute); err != nil { + return diag.FromErr(err) + } + if err := d.Set("groups_delim", getResp.GroupsDelim); err != nil { + return diag.FromErr(err) + } + if err := d.Set("custom_attributes", getResp.CustomAttributes); err != nil { + return diag.FromErr(err) + } + if err := d.Set("entity_issuer", getResp.EntityIssuer); err != nil { + return diag.FromErr(err) + } + if err := d.Set("is_signed_authn_req_enabled", getResp.IsSignedAuthnReqEnabled); err != nil { + return diag.FromErr(err) + } + if getResp.CreatedTime != nil { + t := getResp.CreatedTime + if err := d.Set("created_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if getResp.LastUpdatedTime != nil { + t := getResp.LastUpdatedTime + if err := d.Set("last_updated_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if err := d.Set("created_by", getResp.CreatedBy); err != nil { + return diag.FromErr(err) + } + + d.SetId(*getResp.ExtId) + return nil +} + +func flattenIdpMetadata(pr *import1.IdpMetadata) []map[string]interface{} { + if pr != nil { + idps := make([]map[string]interface{}, 0) + idp := make(map[string]interface{}) + + idp["entity_id"] = pr.EntityId + idp["login_url"] = pr.LoginUrl + idp["logout_url"] = pr.LogoutUrl + idp["error_url"] = pr.ErrorUrl + idp["certificate"] = pr.Certificate + if pr.NameIdPolicyFormat != nil { + idp["name_id_policy_format"] = flattenNameIdPolicyFormat(pr.NameIdPolicyFormat) + } + + idps = append(idps, idp) + return idps + } + return nil +} + +func flattenNameIdPolicyFormat(pr *import1.NameIdPolicyFormat) string { + if pr != nil { + const two, three, four, five, six, seven, eight, nine, ten = 2, 3, 4, 5, 6, 7, 8, 9, 10 + + if *pr == import1.NameIdPolicyFormat(two) { + return "emailAddress" + } + if *pr == import1.NameIdPolicyFormat(three) { + return "unspecified" + } + if *pr == import1.NameIdPolicyFormat(four) { + return "X509SubjectName" + } + if *pr == import1.NameIdPolicyFormat(five) { + return "WindowsDomainQualifiedName" + } + if *pr == import1.NameIdPolicyFormat(six) { + return "encrypted" + } + if *pr == import1.NameIdPolicyFormat(seven) { + return "entity" + } + if *pr == import1.NameIdPolicyFormat(eight) { + return "kerberos" + } + if *pr == import1.NameIdPolicyFormat(nine) { + return "persistent" + } + if *pr == import1.NameIdPolicyFormat(ten) { + return "transient" + } + } + return "UNKNOWN" +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_saml_idp_v2_test.go b/nutanix/services/v2/iamv2/data_source_nutanix_saml_idp_v2_test.go new file mode 100644 index 000000000..b277ca86e --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_saml_idp_v2_test.go @@ -0,0 +1,64 @@ +package iamv2_test + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameIdentityProvider = "data.nutanix_saml_identity_provider_v2.test" + +func TestAccNutanixIdentityProvidersV2Datasource_GetSamlIdpById(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testIdentityProviderDatasourceV4Config(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameIdentityProvider, "idp_metadata.#"), + resource.TestCheckResourceAttr(datasourceNameIdentityProvider, "name", testVars.Iam.IdentityProviders.Name), + resource.TestCheckResourceAttr(datasourceNameIdentityProvider, "username_attribute", testVars.Iam.IdentityProviders.UsernameAttribute), + resource.TestCheckResourceAttr(datasourceNameIdentityProvider, "email_attribute", testVars.Iam.IdentityProviders.EmailAttribute), + resource.TestCheckResourceAttr(datasourceNameIdentityProvider, "groups_attribute", testVars.Iam.IdentityProviders.GroupsAttribute), + resource.TestCheckResourceAttr(datasourceNameIdentityProvider, "groups_delim", testVars.Iam.IdentityProviders.GroupsDelim), + resource.TestCheckResourceAttrSet(datasourceNameIdentityProvider, "custom_attributes.#"), + resource.TestCheckResourceAttr(datasourceNameIdentityProvider, "custom_attributes.0", testVars.Iam.IdentityProviders.CustomAttributes[0]), + resource.TestCheckResourceAttr(datasourceNameIdentityProvider, "custom_attributes.1", testVars.Iam.IdentityProviders.CustomAttributes[1]), + ), + }, + }, + }) +} + +func testIdentityProviderDatasourceV4Config(filepath string) string { + return fmt.Sprintf(` + locals{ + config = (jsondecode(file("%s"))) + identity_providers = local.config.iam.identity_providers + } + + resource "nutanix_saml_identity_providers_v2" "test" { + name = local.identity_providers.name + username_attribute = local.identity_providers.username_attr + email_attribute = local.identity_providers.email_attr + groups_attribute = local.identity_providers.groups_attr + groups_delim = local.identity_providers.groups_delim + idp_metadata_xml = local.identity_providers.idp_metadata_xml + entity_issuer = local.identity_providers.entity_issuer + is_signed_authn_req_enabled = local.identity_providers.is_signed_authn_req_enabled + custom_attributes = local.identity_providers.custom_attributes + } + + data "nutanix_saml_identity_provider_v2" "test" { + ext_id = nutanix_saml_identity_providers_v2.test.id + } +`, filepath) +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_saml_idps_v2.go b/nutanix/services/v2/iamv2/data_source_nutanix_saml_idps_v2.go new file mode 100644 index 000000000..e3c35e4dd --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_saml_idps_v2.go @@ -0,0 +1,244 @@ +package iamv2 + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authn" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixSamlIDPsV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixSamlIDPsV2Read, + Schema: map[string]*schema.Schema{ + "page": { + Type: schema.TypeInt, + Optional: true, + }, + "limit": { + Type: schema.TypeInt, + Optional: true, + }, + "filter": { + Type: schema.TypeString, + Optional: true, + }, + "order_by": { + Type: schema.TypeString, + Optional: true, + }, + "select": { + Type: schema.TypeString, + Optional: true, + }, + "identity_providers": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Computed: true, + }, + "idp_metadata": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "entity_id": { + Type: schema.TypeString, + Computed: true, + }, + "login_url": { + Type: schema.TypeString, + Computed: true, + }, + "logout_url": { + Type: schema.TypeString, + Computed: true, + }, + "error_url": { + Type: schema.TypeString, + Computed: true, + }, + "certificate": { + Type: schema.TypeString, + Computed: true, + }, + "name_id_policy_format": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "username_attribute": { + Type: schema.TypeString, + Computed: true, + }, + "email_attribute": { + Type: schema.TypeString, + Computed: true, + }, + "groups_attribute": { + Type: schema.TypeString, + Computed: true, + }, + "groups_delim": { + Type: schema.TypeString, + Computed: true, + }, + "custom_attributes": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "entity_issuer": { + Type: schema.TypeString, + Computed: true, + }, + "is_signed_authn_req_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DatasourceNutanixSamlIDPsV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + // initialize query params + var filter, orderBy, selects *string + var page, limit *int + + if pagef, ok := d.GetOk("page"); ok { + page = utils.IntPtr(pagef.(int)) + } else { + page = nil + } + if limitf, ok := d.GetOk("limit"); ok { + limit = utils.IntPtr(limitf.(int)) + } else { + limit = nil + } + if filterf, ok := d.GetOk("filter"); ok { + filter = utils.StringPtr(filterf.(string)) + } else { + filter = nil + } + if order, ok := d.GetOk("order_by"); ok { + orderBy = utils.StringPtr(order.(string)) + } else { + orderBy = nil + } + if selectf, ok := d.GetOk("select"); ok { + selects = utils.StringPtr(selectf.(string)) + } else { + selects = nil + } + + resp, err := conn.SamlIdentityAPIInstance.ListSamlIdentityProviders(page, limit, filter, orderBy, selects) + if err != nil { + fmt.Println(err) + var errordata map[string]interface{} + e := json.Unmarshal([]byte(err.Error()), &errordata) + if e != nil { + return diag.FromErr(e) + } + data := errordata["data"].(map[string]interface{}) + errorList := data["error"].([]interface{}) + errorMessage := errorList[0].(map[string]interface{}) + return diag.Errorf("error while fetching identity providers: %v", errorMessage["message"]) + } + + getResp := resp.Data.GetValue().([]import1.SamlIdentityProvider) + if err := d.Set("identity_providers", flattenIdentityProvidersEntities(getResp)); err != nil { + return diag.FromErr(err) + } + + d.SetId(resource.UniqueId()) + return nil +} + +func flattenIdentityProvidersEntities(pr []import1.SamlIdentityProvider) []interface{} { + if len(pr) > 0 { + idps := make([]interface{}, len(pr)) + + for k, v := range pr { + idp := make(map[string]interface{}) + + idp["ext_id"] = v.ExtId + + if v.Name != nil { + idp["name"] = v.Name + } + if v.IdpMetadata != nil { + idp["idp_metadata"] = flattenIdpMetadata(v.IdpMetadata) + } + if v.UsernameAttribute != nil { + idp["username_attribute"] = v.UsernameAttribute + } + if v.EmailAttribute != nil { + idp["email_attribute"] = v.EmailAttribute + } + if v.GroupsAttribute != nil { + idp["groups_attribute"] = v.GroupsAttribute + } + if v.GroupsDelim != nil { + idp["groups_delim"] = v.GroupsDelim + } + if v.CustomAttributes != nil { + idp["custom_attributes"] = v.CustomAttributes + } + if v.EntityIssuer != nil { + idp["entity_issuer"] = v.EntityIssuer + } + if v.IsSignedAuthnReqEnabled != nil { + idp["is_signed_authn_req_enabled"] = v.IsSignedAuthnReqEnabled + } + if v.CreatedTime != nil { + t := v.CreatedTime + idp["created_time"] = t.String() + } + if v.LastUpdatedTime != nil { + t := v.LastUpdatedTime + idp["last_updated_time"] = t.String() + } + if v.CreatedBy != nil { + idp["created_by"] = v.CreatedBy + } + idps[k] = idp + } + return idps + } + return nil +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_saml_idps_v2_test.go b/nutanix/services/v2/iamv2/data_source_nutanix_saml_idps_v2_test.go new file mode 100644 index 000000000..a46c53b21 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_saml_idps_v2_test.go @@ -0,0 +1,160 @@ +package iamv2_test + +import ( + "fmt" + "os" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameIdentityProviders = "data.nutanix_saml_identity_providers_v2.test" + +func TestAccNutanixIdentityProvidersV2Datasource_ListAllIdps(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testIdentityProvidersDatasourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameIdentityProviders, "identity_providers.#"), + resource.TestCheckResourceAttrSet(datasourceNameIdentityProviders, "identity_providers.0.name"), + resource.TestCheckResourceAttrSet(datasourceNameIdentityProviders, "identity_providers.0.username_attribute"), + ), + }, + }, + }) +} + +func TestAccNutanixIdentityProvidersV2Datasource_WithFilter(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testIdentityProvidersDatasourceWithFilterConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(datasourceNameIdentityProviders, "identity_providers.#", "1"), + resource.TestCheckResourceAttr(datasourceNameIdentityProviders, "identity_providers.0.name", testVars.Iam.IdentityProviders.Name), + resource.TestCheckResourceAttr(datasourceNameIdentityProviders, "identity_providers.0.username_attribute", testVars.Iam.IdentityProviders.UsernameAttribute), + resource.TestCheckResourceAttr(datasourceNameIdentityProviders, "identity_providers.0.email_attribute", testVars.Iam.IdentityProviders.EmailAttribute), + resource.TestCheckResourceAttr(datasourceNameIdentityProviders, "identity_providers.0.groups_attribute", testVars.Iam.IdentityProviders.GroupsAttribute), + resource.TestCheckResourceAttr(datasourceNameIdentityProviders, "identity_providers.0.groups_delim", testVars.Iam.IdentityProviders.GroupsDelim), + resource.TestCheckResourceAttrSet(datasourceNameIdentityProviders, "identity_providers.0.custom_attributes.#"), + resource.TestCheckResourceAttr(datasourceNameIdentityProviders, "identity_providers.0.custom_attributes.0", testVars.Iam.IdentityProviders.CustomAttributes[0]), + resource.TestCheckResourceAttr(datasourceNameIdentityProviders, "identity_providers.0.custom_attributes.1", testVars.Iam.IdentityProviders.CustomAttributes[1]), + resource.TestCheckResourceAttrSet(datasourceNameIdentityProviders, "identity_providers.0.idp_metadata.0.certificate"), + resource.TestCheckResourceAttrSet(datasourceNameIdentityProviders, "identity_providers.0.idp_metadata.0.entity_id"), + resource.TestCheckResourceAttrSet(datasourceNameIdentityProviders, "identity_providers.0.idp_metadata.0.login_url"), + resource.TestCheckResourceAttrSet(datasourceNameIdentityProviders, "identity_providers.0.idp_metadata.0.name_id_policy_format"), + ), + }, + }, + }) +} + +func TestAccNutanixIdentityProvidersV2Datasource_WithLimit(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testIdentityProvidersDatasourceWithLimitConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameIdentityProviders, "identity_providers.#"), + resource.TestCheckResourceAttr(datasourceNameIdentityProviders, "identity_providers.#", strconv.Itoa(testVars.Iam.IdentityProviders.Limit)), + ), + }, + }, + }) +} + +func testIdentityProvidersDatasourceConfig(filepath string) string { + return fmt.Sprintf(` + locals{ + config = (jsondecode(file("%s"))) + identity_providers = local.config.iam.identity_providers + } + + resource "nutanix_saml_identity_providers_v2" "test" { + name = local.identity_providers.name + username_attribute = local.identity_providers.username_attr + email_attribute = local.identity_providers.email_attr + groups_attribute = local.identity_providers.groups_attr + groups_delim = local.identity_providers.groups_delim + idp_metadata_xml = local.identity_providers.idp_metadata_xml + entity_issuer = local.identity_providers.entity_issuer + is_signed_authn_req_enabled = local.identity_providers.is_signed_authn_req_enabled + custom_attributes = local.identity_providers.custom_attributes + } + data "nutanix_saml_identity_providers_v2" "test"{ + depends_on = [ resource.nutanix_saml_identity_providers_v2.test ] + } + `, filepath) +} + +func testIdentityProvidersDatasourceWithFilterConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + identity_providers = local.config.iam.identity_providers + } + + resource "nutanix_saml_identity_providers_v2" "test" { + name = local.identity_providers.name + username_attribute = local.identity_providers.username_attr + email_attribute = local.identity_providers.email_attr + groups_attribute = local.identity_providers.groups_attr + groups_delim = local.identity_providers.groups_delim + idp_metadata_xml = local.identity_providers.idp_metadata_xml + entity_issuer = local.identity_providers.entity_issuer + is_signed_authn_req_enabled = local.identity_providers.is_signed_authn_req_enabled + custom_attributes = local.identity_providers.custom_attributes + } + + data "nutanix_saml_identity_providers_v2" "test" { + filter = "extId eq '${resource.nutanix_saml_identity_providers_v2.test.id}'" + depends_on = [ resource.nutanix_saml_identity_providers_v2.test ] + } + + `, filepath) +} + +func testIdentityProvidersDatasourceWithLimitConfig(filepath string) string { + return fmt.Sprintf(` + locals{ + config = (jsondecode(file("%s"))) + identity_providers = local.config.iam.identity_providers + } + + resource "nutanix_saml_identity_providers_v2" "test" { + name = local.identity_providers.name + username_attribute = local.identity_providers.username_attr + email_attribute = local.identity_providers.email_attr + groups_attribute = local.identity_providers.groups_attr + groups_delim = local.identity_providers.groups_delim + idp_metadata_xml = local.identity_providers.idp_metadata_xml + entity_issuer = local.identity_providers.entity_issuer + is_signed_authn_req_enabled = local.identity_providers.is_signed_authn_req_enabled + custom_attributes = local.identity_providers.custom_attributes + } + + data "nutanix_saml_identity_providers_v2" "test" { + limit = local.identity_providers.limit + depends_on = [ resource.nutanix_saml_identity_providers_v2.test ] + } + `, filepath) +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_user_group_v2.go b/nutanix/services/v2/iamv2/data_source_nutanix_user_group_v2.go new file mode 100644 index 000000000..50cdaeb91 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_user_group_v2.go @@ -0,0 +1,116 @@ +package iamv2 + +import ( + "context" + "encoding/json" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authn" + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixUserGroupV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixUserGroupV4Read, + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Required: true, + }, + "group_type": { + Type: schema.TypeString, + Computed: true, + }, + "idp_id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "distinguished_name": { + Type: schema.TypeString, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func DatasourceNutanixUserGroupV4Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + extID := d.Get("ext_id") + resp, err := conn.UserGroupsAPIInstance.GetUserGroupById(utils.StringPtr(extID.(string))) + if err != nil { + var errordata map[string]interface{} + e := json.Unmarshal([]byte(err.Error()), &errordata) + if e != nil { + return diag.FromErr(e) + } + data := errordata["data"].(map[string]interface{}) + errorList := data["error"].([]interface{}) + errorMessage := errorList[0].(map[string]interface{}) + return diag.Errorf("error while fetching user groups: %v", errorMessage["message"]) + } + + getResp := resp.Data.GetValue().(import1.UserGroup) + + if err := d.Set("group_type", flattenGroupType(getResp.GroupType)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("idp_id", getResp.IdpId); err != nil { + return diag.FromErr(err) + } + if err := d.Set("name", getResp.Name); err != nil { + return diag.FromErr(err) + } + if err := d.Set("distinguished_name", getResp.DistinguishedName); err != nil { + return diag.FromErr(err) + } + if getResp.CreatedTime != nil { + t := getResp.CreatedTime + if err := d.Set("created_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if getResp.LastUpdatedTime != nil { + t := getResp.LastUpdatedTime + if err := d.Set("last_updated_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if err := d.Set("created_by", getResp.CreatedBy); err != nil { + return diag.FromErr(err) + } + + d.SetId(*getResp.ExtId) + return nil +} + +func flattenGroupType(pr *import1.GroupType) string { + if pr != nil { + if *pr == import1.GroupType(2) { + return "SAML" + } + if *pr == import1.GroupType(3) { + return "LDAP" + } + } + return "UNKNOWN" +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_user_group_v2_test.go b/nutanix/services/v2/iamv2/data_source_nutanix_user_group_v2_test.go new file mode 100644 index 000000000..bbbcd444c --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_user_group_v2_test.go @@ -0,0 +1,55 @@ +package iamv2_test + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameUserGroup = "data.nutanix_user_group_v2.test" + +func TestAccNutanixUserGroupsV2Datasource_Basic_Role(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testUserGroupDatasourceV4Config(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(datasourceNameUserGroup, "distinguished_name", testVars.Iam.UserGroups.DistinguishedName), + resource.TestCheckResourceAttr(datasourceNameUserGroup, "name", testVars.Iam.UserGroups.Name), + resource.TestCheckResourceAttr(datasourceNameUserGroup, "idp_id", testVars.Iam.UserGroups.DirectoryServiceId), + resource.TestCheckResourceAttr(datasourceNameUserGroup, "group_type", "LDAP"), + ), + }, + }, + }) +} + +func testUserGroupDatasourceV4Config(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + user_groups = local.config.iam.user_groups + } + + resource "nutanix_user_groups_v2" "test" { + group_type = "LDAP" + idp_id = local.user_groups.directory_service_id + name = local.user_groups.name + distinguished_name = local.user_groups.distinguished_name + } + + data "nutanix_user_group_v2" "test" { + ext_id = resource.nutanix_user_groups_v2.test.id + } + `, filepath) +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_user_groups_v2.go b/nutanix/services/v2/iamv2/data_source_nutanix_user_groups_v2.go new file mode 100644 index 000000000..5d9eb3604 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_user_groups_v2.go @@ -0,0 +1,173 @@ +package iamv2 + +import ( + "context" + "encoding/json" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authn" + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixUserGroupsV2() *schema.Resource { + return &schema.Resource{ + ReadContext: DatasourceNutanixUserGroupsV4Read, + Schema: map[string]*schema.Schema{ + "page": { + Type: schema.TypeInt, + Optional: true, + }, + "limit": { + Type: schema.TypeInt, + Optional: true, + }, + "filter": { + Type: schema.TypeString, + Optional: true, + }, + "order_by": { + Type: schema.TypeString, + Optional: true, + }, + "select": { + Type: schema.TypeString, + Optional: true, + }, + "user_groups": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Computed: true, + }, + "group_type": { + Type: schema.TypeString, + Computed: true, + }, + "idp_id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "distinguished_name": { + Type: schema.TypeString, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DatasourceNutanixUserGroupsV4Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + // initialize query params + var filter, orderBy, selects *string + var page, limit *int + + if pagef, ok := d.GetOk("page"); ok { + page = utils.IntPtr(pagef.(int)) + } else { + page = nil + } + if limitf, ok := d.GetOk("limit"); ok { + limit = utils.IntPtr(limitf.(int)) + } else { + limit = nil + } + if filterf, ok := d.GetOk("filter"); ok { + filter = utils.StringPtr(filterf.(string)) + } else { + filter = nil + } + if order, ok := d.GetOk("order_by"); ok { + orderBy = utils.StringPtr(order.(string)) + } else { + orderBy = nil + } + if selectf, ok := d.GetOk("select"); ok { + selects = utils.StringPtr(selectf.(string)) + } else { + selects = nil + } + + resp, err := conn.UserGroupsAPIInstance.ListUserGroups(page, limit, filter, orderBy, selects) + if err != nil { + var errordata map[string]interface{} + e := json.Unmarshal([]byte(err.Error()), &errordata) + if e != nil { + return diag.FromErr(e) + } + data := errordata["data"].(map[string]interface{}) + errorList := data["error"].([]interface{}) + errorMessage := errorList[0].(map[string]interface{}) + return diag.Errorf("error while fetching user groups: %v", errorMessage["message"]) + } + getResp := resp.Data.GetValue().([]import1.UserGroup) + if err := d.Set("user_groups", flattenUserGroupEntities(getResp)); err != nil { + return diag.FromErr(err) + } + + d.SetId(resource.UniqueId()) + return nil +} + +func flattenUserGroupEntities(pr []import1.UserGroup) []interface{} { + if len(pr) > 0 { + ugs := make([]interface{}, len(pr)) + + for k, v := range pr { + ug := make(map[string]interface{}) + + if v.Name != nil { + ug["name"] = v.Name + } + if v.DistinguishedName != nil { + ug["distinguished_name"] = v.DistinguishedName + } + if v.IdpId != nil { + ug["idp_id"] = v.IdpId + } + if v.GroupType != nil { + ug["group_type"] = flattenGroupType(v.GroupType) + } + + ug["created_by"] = v.CreatedBy + + if v.CreatedTime != nil { + t := v.CreatedTime + ug["created_time"] = t.String() + } + if v.LastUpdatedTime != nil { + t := v.LastUpdatedTime + ug["last_updated_time"] = t.String() + } + + ugs[k] = ug + } + return ugs + } + return nil +} diff --git a/nutanix/services/v2/iamv2/data_source_nutanix_user_groups_v2_test.go b/nutanix/services/v2/iamv2/data_source_nutanix_user_groups_v2_test.go new file mode 100644 index 000000000..de92843d6 --- /dev/null +++ b/nutanix/services/v2/iamv2/data_source_nutanix_user_groups_v2_test.go @@ -0,0 +1,136 @@ +package iamv2_test + +import ( + "fmt" + "os" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameUserGroups = "data.nutanix_user_groups_v2.test" + +func TestAccNutanixUserGroupsV2Datasource_Basic(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testUserGroupsDatasourceV4Config(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameUserGroups, "user_groups.#"), + resource.TestCheckResourceAttrSet(datasourceNameUserGroups, "user_groups.0.idp_id"), + resource.TestCheckResourceAttrSet(datasourceNameUserGroups, "user_groups.0.distinguished_name"), + ), + }, + }, + }) +} + +func TestAccNutanixUserGroupsV2Datasource_WithFilter(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testUserGroupsDatasourceV4WithFilterConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameUserGroups, "user_groups.#"), + resource.TestCheckResourceAttr(datasourceNameUserGroups, "user_groups.#", "1"), + resource.TestCheckResourceAttr(datasourceNameUserGroups, "user_groups.0.distinguished_name", testVars.Iam.UserGroups.DistinguishedName), + resource.TestCheckResourceAttr(datasourceNameUserGroups, "user_groups.0.name", testVars.Iam.UserGroups.Name), + resource.TestCheckResourceAttr(datasourceNameUserGroups, "user_groups.0.idp_id", testVars.Iam.UserGroups.DirectoryServiceId), + resource.TestCheckResourceAttr(datasourceNameUserGroups, "user_groups.0.group_type", "LDAP"), + ), + }, + }, + }) +} + +func TestAccNutanixUserGroupsV2Datasource_WithLimit(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testUserGroupsDatasourceV4WithLimitConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameUserGroups, "user_groups.#"), + resource.TestCheckResourceAttr(datasourceNameUserGroups, "user_groups.#", strconv.Itoa(testVars.Iam.UserGroups.Limit)), + ), + }, + }, + }) +} + +func testUserGroupsDatasourceV4Config(filepath string) string { + return fmt.Sprintf(` + locals{ + config = (jsondecode(file("%s"))) + user_groups = local.config.iam.user_groups + } + + resource "nutanix_user_groups_v2" "test" { + group_type = "LDAP" + idp_id = local.user_groups.directory_service_id + name = local.user_groups.name + distinguished_name = local.user_groups.distinguished_name + } + + data "nutanix_user_groups_v2" "test"{} + `, filepath) +} + +func testUserGroupsDatasourceV4WithFilterConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + user_groups = local.config.iam.user_groups + } + + resource "nutanix_user_groups_v2" "test" { + group_type = "LDAP" + idp_id = local.user_groups.directory_service_id + name = local.user_groups.name + distinguished_name = local.user_groups.distinguished_name + } + + data "nutanix_user_groups_v2" "test" { + filter = "name eq '${local.user_groups.name}'" + depends_on = [resource.nutanix_user_groups_v2.test] + } + `, filepath) +} + +func testUserGroupsDatasourceV4WithLimitConfig(filepath string) string { + return fmt.Sprintf(` + locals{ + config = (jsondecode(file("%s"))) + user_groups = local.config.iam.user_groups + } + + resource "nutanix_user_groups_v2" "test" { + group_type = "LDAP" + idp_id = local.user_groups.directory_service_id + name = local.user_groups.name + distinguished_name = local.user_groups.distinguished_name + } + + data "nutanix_user_groups_v2" "test" { + limit = local.user_groups.limit + depends_on = [resource.nutanix_user_groups_v2.test] + } + `, filepath) +} diff --git a/nutanix/services/v2/iamv2/datasource_nutanix_user_v2.go b/nutanix/services/v2/iamv2/datasource_nutanix_user_v2.go new file mode 100644 index 000000000..36f58500d --- /dev/null +++ b/nutanix/services/v2/iamv2/datasource_nutanix_user_v2.go @@ -0,0 +1,252 @@ +package iamv2 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + iamConfig "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authn" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixUserV2() *schema.Resource { + return &schema.Resource{ + ReadContext: datasourceNutanixUserV2Read, + Schema: map[string]*schema.Schema{ + + "ext_id": { + Type: schema.TypeString, + Required: true, + }, + "links": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + }, + "rel": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "username": { + Type: schema.TypeString, + Computed: true, + }, + "user_type": { + Type: schema.TypeString, + Computed: true, + }, + "idp_id": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "first_name": { + Type: schema.TypeString, + Computed: true, + }, + "middle_initial": { + Type: schema.TypeString, + Computed: true, + }, + "last_name": { + Type: schema.TypeString, + Computed: true, + }, + "email_id": { + Type: schema.TypeString, + Computed: true, + }, + "locale": { + Type: schema.TypeString, + Computed: true, + }, + "region": { + Type: schema.TypeString, + Computed: true, + }, + "password": { + Type: schema.TypeString, + Sensitive: true, + Computed: true, + }, + "is_force_reset_password": { + Type: schema.TypeBool, + Computed: true, + }, + "additional_attributes": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "buckets_access_keys": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Computed: true, + }, + "links": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + }, + "rel": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "access_key_name": { + Type: schema.TypeString, + Computed: true, + }, + "secret_access_key": { + Type: schema.TypeString, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "last_login_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func datasourceNutanixUserV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + extID := d.Get("ext_id") + + resp, err := conn.UsersAPIInstance.GetUserById(utils.StringPtr(extID.(string))) + + if err != nil { + return diag.Errorf("error while fetching user : %v", err) + } + + getResp := resp.Data.GetValue().(iamConfig.User) + + if err := d.Set("ext_id", getResp.ExtId); err != nil { + return diag.Errorf("error setting ext_id: %v", err) + } + if err := d.Set("links", flattenLinks(getResp.Links)); err != nil { + return diag.Errorf("error setting links: %v", err) + } + if err := d.Set("username", getResp.Username); err != nil { + return diag.Errorf("error setting username: %v", err) + } + if err := d.Set("user_type", flattenUserType(getResp.UserType)); err != nil { + return diag.Errorf("error setting user_type: %v", err) + } + if err := d.Set("idp_id", getResp.IdpId); err != nil { + return diag.Errorf("error setting idp_id: %v", err) + } + if err := d.Set("display_name", getResp.DisplayName); err != nil { + return diag.Errorf("error setting display_name: %v", err) + } + if err := d.Set("first_name", getResp.FirstName); err != nil { + return diag.Errorf("error setting first_name: %v", err) + } + if err := d.Set("middle_initial", getResp.MiddleInitial); err != nil { + return diag.Errorf("error setting middle_initial: %v", err) + } + if err := d.Set("last_name", getResp.LastName); err != nil { + return diag.Errorf("error setting last_name: %v", err) + } + if err := d.Set("email_id", getResp.EmailId); err != nil { + return diag.Errorf("error setting email_id: %v", err) + } + if err := d.Set("locale", getResp.Locale); err != nil { + return diag.Errorf("error setting locale: %v", err) + } + if err := d.Set("region", getResp.Region); err != nil { + return diag.Errorf("error setting region: %v", err) + } + if err := d.Set("password", getResp.Password); err != nil { + return diag.Errorf("error setting password: %v", err) + } + if err := d.Set("is_force_reset_password", getResp.IsForceResetPasswordEnabled); err != nil { + return diag.Errorf("error setting is_force_reset_password: %v", err) + } + if err = d.Set("additional_attributes", flattenAdditionalAttributes(getResp)); err != nil { + return diag.Errorf("error setting additional_attributes for user %s: %s", d.Id(), err) + } + if err = d.Set("status", flattenUserStatusType(getResp.Status)); err != nil { + return diag.Errorf("error setting status for user %s: %s", d.Id(), err) + } + if err = d.Set("buckets_access_keys", flattenBucketsAccessKeys(getResp)); err != nil { + return diag.Errorf("error setting buckets_access_keys for user %s: %s", d.Id(), err) + } + if err = d.Set("last_login_time", getResp.LastLoginTime.Format("2006-01-02T15:04:05Z07:00")); err != nil { + return diag.Errorf("error setting last_login_time for user %s: %s", d.Id(), err) + } + if err = d.Set("created_time", getResp.CreatedTime.Format("2006-01-02T15:04:05Z07:00")); err != nil { + return diag.Errorf("error setting created_time for user %s: %s", d.Id(), err) + } + if err = d.Set("last_updated_time", getResp.LastUpdatedTime.Format("2006-01-02T15:04:05Z07:00")); err != nil { + return diag.Errorf("error setting last_updated_time for user %s: %s", d.Id(), err) + } + if err := d.Set("created_by", getResp.CreatedBy); err != nil { + return diag.Errorf("error setting created_by: %v", err) + } + + d.SetId(resource.UniqueId()) + return nil +} diff --git a/nutanix/services/v2/iamv2/datasource_nutanix_user_v2_test.go b/nutanix/services/v2/iamv2/datasource_nutanix_user_v2_test.go new file mode 100644 index 000000000..9bc66292b --- /dev/null +++ b/nutanix/services/v2/iamv2/datasource_nutanix_user_v2_test.go @@ -0,0 +1,72 @@ +package iamv2_test + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameUser = "data.nutanix_user_v2.test" + +func TestAccNutanixUserV4Datasource_Basic(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testUserDatasourceV4Config(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(datasourceNameUser, "username", testVars.Iam.Users.Username), + resource.TestCheckResourceAttr(datasourceNameUser, "first_name", testVars.Iam.Users.FirstName), + resource.TestCheckResourceAttr(datasourceNameUser, "middle_initial", testVars.Iam.Users.MiddleInitial), + resource.TestCheckResourceAttr(datasourceNameUser, "last_name", testVars.Iam.Users.LastName), + resource.TestCheckResourceAttr(datasourceNameUser, "email_id", testVars.Iam.Users.EmailId), + resource.TestCheckResourceAttr(datasourceNameUser, "locale", testVars.Iam.Users.Locale), + resource.TestCheckResourceAttr(datasourceNameUser, "region", testVars.Iam.Users.Region), + resource.TestCheckResourceAttr(datasourceNameUser, "display_name", testVars.Iam.Users.DisplayName), + resource.TestCheckResourceAttr(datasourceNameUser, "user_type", "LOCAL"), + resource.TestCheckResourceAttr(datasourceNameUser, "status", "ACTIVE"), + ), + }, + }, + }) +} + +func testUserDatasourceV4Config(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test" { + username = local.users.username + first_name = local.users.first_name + middle_initial = local.users.middle_initial + last_name = local.users.last_name + email_id = local.users.email_id + locale = local.users.locale + region = local.users.region + display_name = local.users.display_name + password = local.users.password + user_type = "LOCAL" + status = "ACTIVE" + force_reset_password = local.users.force_reset_password + } + + data "nutanix_user_v2" "test" { + ext_id = nutanix_users_v2.test.id + depends_on = [nutanix_users_v2.test] + } + + + `, filepath) +} diff --git a/nutanix/services/v2/iamv2/datasource_nutanix_users_v2.go b/nutanix/services/v2/iamv2/datasource_nutanix_users_v2.go new file mode 100644 index 000000000..66412389c --- /dev/null +++ b/nutanix/services/v2/iamv2/datasource_nutanix_users_v2.go @@ -0,0 +1,375 @@ +package iamv2 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + iamConfig "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authn" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func DatasourceNutanixUsersV2() *schema.Resource { + return &schema.Resource{ + ReadContext: datasourceNutanixUsersV2Read, + Schema: map[string]*schema.Schema{ + "page": { + Type: schema.TypeInt, + Optional: true, + }, + "limit": { + Type: schema.TypeInt, + Optional: true, + }, + "filter": { + Type: schema.TypeString, + Optional: true, + }, + "order_by": { + Type: schema.TypeString, + Optional: true, + }, + "select": { + Type: schema.TypeString, + Optional: true, + }, + "users": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Computed: true, + }, + "links": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + }, + "rel": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "username": { + Type: schema.TypeString, + Computed: true, + }, + "user_type": { + Type: schema.TypeString, + Computed: true, + }, + "idp_id": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "first_name": { + Type: schema.TypeString, + Computed: true, + }, + "middle_initial": { + Type: schema.TypeString, + Computed: true, + }, + "last_name": { + Type: schema.TypeString, + Computed: true, + }, + "email_id": { + Type: schema.TypeString, + Computed: true, + }, + "locale": { + Type: schema.TypeString, + Computed: true, + }, + "region": { + Type: schema.TypeString, + Computed: true, + }, + "is_force_reset_password": { + Type: schema.TypeBool, + Computed: true, + }, + "additional_attributes": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "buckets_access_keys": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Computed: true, + }, + "links": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + }, + "rel": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "access_key_name": { + Type: schema.TypeString, + Computed: true, + }, + "secret_access_key": { + Type: schema.TypeString, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "last_login_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func datasourceNutanixUsersV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + // initialize query params + var filter, orderBy, selects *string + var page, limit *int + + if pagef, ok := d.GetOk("page"); ok { + page = utils.IntPtr(pagef.(int)) + } else { + page = nil + } + if limitf, ok := d.GetOk("limit"); ok { + limit = utils.IntPtr(limitf.(int)) + } else { + limit = nil + } + if filterf, ok := d.GetOk("filter"); ok { + filter = utils.StringPtr(filterf.(string)) + } else { + filter = nil + } + if order, ok := d.GetOk("order_by"); ok { + orderBy = utils.StringPtr(order.(string)) + } else { + orderBy = nil + } + if selectf, ok := d.GetOk("select"); ok { + selects = utils.StringPtr(selectf.(string)) + } else { + selects = nil + } + + resp, err := conn.UsersAPIInstance.ListUsers(page, limit, filter, orderBy, selects) + + if err != nil { + return diag.Errorf("error while fetching users : %v", err) + } + + getResp := resp.Data.GetValue().([]iamConfig.User) + + if err := d.Set("users", flattenUsersEntities(getResp)); err != nil { + return diag.FromErr(err) + } + + d.SetId(resource.UniqueId()) + return nil +} + +func flattenUsersEntities(usersResp []iamConfig.User) []interface{} { + if len(usersResp) > 0 { + users := make([]interface{}, len(usersResp)) + for k, v := range usersResp { + user := make(map[string]interface{}) + + if v.TenantId != nil { + user["tenant_id"] = v.TenantId + } + if v.ExtId != nil { + user["ext_id"] = v.ExtId + } + if v.Links != nil { + user["links"] = flattenLinks(v.Links) + } + if v.Username != nil { + user["username"] = v.Username + } + if v.UserType != nil { + user["user_type"] = flattenUserType(v.UserType) + } + if v.IdpId != nil { + user["idp_id"] = v.IdpId + } + if v.DisplayName != nil { + user["display_name"] = v.DisplayName + } + if v.FirstName != nil { + user["first_name"] = v.FirstName + } + if v.MiddleInitial != nil { + user["middle_initial"] = v.MiddleInitial + } + if v.LastName != nil { + user["last_name"] = v.LastName + } + if v.EmailId != nil { + user["email_id"] = v.EmailId + } + if v.Locale != nil { + user["locale"] = v.Locale + } + if v.Region != nil { + user["region"] = v.Region + } + if v.IsForceResetPasswordEnabled != nil { + user["is_force_reset_password"] = *v.IsForceResetPasswordEnabled + } + if v.AdditionalAttributes != nil { + user["additional_attributes"] = flattenAdditionalAttributes(v) + } + if v.Status != nil { + user["status"] = flattenUserStatusType(v.Status) + } + if v.BucketsAccessKeys != nil { + user["buckets_access_keys"] = flattenBucketsAccessKeys(v) + } + if v.LastLoginTime != nil { + user["last_login_time"] = v.LastLoginTime.Format("2006-01-02T15:04:05Z07:00") + } + if v.CreatedTime != nil { + user["created_time"] = v.CreatedTime.Format("2006-01-02T15:04:05Z07:00") + } + if v.LastUpdatedTime != nil { + user["last_updated_time"] = v.LastUpdatedTime.Format("2006-01-02T15:04:05Z07:00") + } + if v.CreatedBy != nil { + user["created_by"] = v.CreatedBy + } + if v.LastUpdatedBy != nil { + user["last_updated_by"] = v.LastUpdatedBy + } + + users[k] = user + } + return users + } + return nil +} + +func flattenBucketsAccessKeys(user iamConfig.User) []map[string]interface{} { + if len(user.BucketsAccessKeys) > 0 { + bucketsAccessKeysList := make([]map[string]interface{}, len(user.BucketsAccessKeys)) + + for k, v := range user.BucketsAccessKeys { + + bucketsAccessKeys := map[string]interface{}{} + if v.ExtId != nil { + bucketsAccessKeys["ext_id"] = *v.ExtId + } + if v.Links != nil { + bucketsAccessKeys["links"] = flattenLinks(v.Links) + } + if v.AccessKeyName != nil { + bucketsAccessKeys["access_key_name"] = *v.AccessKeyName + } + if v.SecretAccessKey != nil { + bucketsAccessKeys["secret_access_key"] = *v.SecretAccessKey + } + if v.UserId != nil { + bucketsAccessKeys["user_id"] = *v.UserId + } + if v.CreatedTime != nil { + bucketsAccessKeys["created_time"] = *v.CreatedTime + } + + bucketsAccessKeysList[k] = bucketsAccessKeys + } + return bucketsAccessKeysList + + } + return nil + +} + +func flattenAdditionalAttributes(user iamConfig.User) []interface{} { + if len(user.AdditionalAttributes) > 0 { + additionalAttributes := make([]interface{}, len(user.AdditionalAttributes)) + for i, attr := range user.AdditionalAttributes { + additionalAttributes[i] = map[string]interface{}{ + "name": *attr.Name, + "value": *attr.Value, + } + } + return additionalAttributes + } + return nil +} diff --git a/nutanix/services/v2/iamv2/datasource_nutanix_users_v2_test.go b/nutanix/services/v2/iamv2/datasource_nutanix_users_v2_test.go new file mode 100644 index 000000000..277f6198a --- /dev/null +++ b/nutanix/services/v2/iamv2/datasource_nutanix_users_v2_test.go @@ -0,0 +1,166 @@ +package iamv2_test + +import ( + "fmt" + "os" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const datasourceNameUsers = "data.nutanix_users_v2.test" + +func TestAccNutanixUsersV4Datasource_Basic(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testUsersDatasourceV4Config(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameUsers, "users.#"), + resource.TestCheckResourceAttrSet(datasourceNameUsers, "users.0.username"), + resource.TestCheckResourceAttrSet(datasourceNameUsers, "users.0.user_type"), + resource.TestCheckResourceAttrSet(datasourceNameUsers, "users.0.ext_id"), + ), + }, + }, + }) +} + +func TestAccNutanixUsersV4Datasource_WithFilter(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testUsersDatasourceV4WithFilterConfig(filepath, "userType eq Schema.Enums.UserType'LOCAL'"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameUsers, "users.0.ext_id"), + resource.TestCheckResourceAttr(datasourceNameUsers, "users.0.user_type", "LOCAL"), + ), + }, + { + Config: testUsersDatasourceV4WithFilterConfig(filepath, "username eq '"+testVars.Iam.Users.Username+"'"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceNameUsers, "users.0.ext_id"), + resource.TestCheckResourceAttr(datasourceNameUsers, "users.0.username", testVars.Iam.Users.Username), + ), + }, + }, + }) +} + +func TestAccNutanixUsersV4Datasource_WithLimit(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testUsersDatasourceV4WithLimitConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(datasourceNameUsers, "users.#", strconv.Itoa(testVars.Iam.Users.Limit)), + ), + }, + }, + }) +} + +func testUsersDatasourceV4Config(filepath string) string { + return fmt.Sprintf(` + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test" { + username = local.users.username + first_name = local.users.first_name + middle_initial = local.users.middle_initial + last_name = local.users.last_name + email_id = local.users.email_id + locale = local.users.locale + region = local.users.region + display_name = local.users.display_name + password = local.users.password + user_type = "LOCAL" + status = "ACTIVE" + force_reset_password = local.users.force_reset_password + } + + data "nutanix_users_v2" "test"{ + depends_on = [nutanix_users_v2.test] + } + `, filepath) +} + +func testUsersDatasourceV4WithFilterConfig(filepath, userQuery string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test" { + username = local.users.username + first_name = local.users.first_name + middle_initial = local.users.middle_initial + last_name = local.users.last_name + email_id = local.users.email_id + locale = local.users.locale + region = local.users.region + display_name = local.users.display_name + password = local.users.password + user_type = "LOCAL" + status = "ACTIVE" + force_reset_password = local.users.force_reset_password + } + + data "nutanix_users_v2" "test" { + filter = "%s" + depends_on = [nutanix_users_v2.test] + } + + + `, filepath, userQuery) +} + +func testUsersDatasourceV4WithLimitConfig(filepath string) string { + return fmt.Sprintf(` + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test" { + username = local.users.username + first_name = local.users.first_name + middle_initial = local.users.middle_initial + last_name = local.users.last_name + email_id = local.users.email_id + locale = local.users.locale + region = local.users.region + display_name = local.users.display_name + password = local.users.password + user_type = "LOCAL" + status = "ACTIVE" + force_reset_password = local.users.force_reset_password + } + + data "nutanix_users_v2" "test" { + limit = local.users.limit + depends_on = [nutanix_users_v2.test] + } + `, filepath) +} diff --git a/nutanix/services/v2/iamv2/main_test.go b/nutanix/services/v2/iamv2/main_test.go new file mode 100644 index 000000000..8036fa16d --- /dev/null +++ b/nutanix/services/v2/iamv2/main_test.go @@ -0,0 +1,123 @@ +package iamv2_test + +import ( + "encoding/json" + "log" + "os" + "testing" +) + +type TestConfig struct { + Iam struct { + Roles struct { + Limit int `json:"limit"` + DisplayName string `json:"display_name"` + Description string `json:"description"` + } `json:"roles"` + Users struct { + Limit int `json:"limit"` + Username string `json:"username"` + IdpId string `json:"idp_id"` + DirectoryServiceId string `json:"directory_service_id"` + DirectoryServiceUsername string `json:"directory_service_username"` + DisplayName string `json:"display_name"` + FirstName string `json:"first_name"` + MiddleInitial string `json:"middle_initial"` + LastName string `json:"last_name"` + EmailId string `json:"email_id"` + Locale string `json:"locale"` + Region string `json:"region"` + Password string `json:"password"` + IsForceResetPasswordEnabled bool `json:"is_force_reset_password"` + } `json:"users"` + // UserGroups config + UserGroups struct { + Limit int `json:"limit"` + IdpId string `json:"idp_id"` + DirectoryServiceId string `json:"directory_service_id"` + Name string `json:"name"` + SAMLName string `json:"saml_name"` + DistinguishedName string `json:"distinguished_name"` + } `json:"user_groups"` + AuthPolicies struct { + Limit int `json:"limit"` + DisplayName string `json:"display_name"` + Description string `json:"description"` + AuthPolicyType string `json:"authorization_policy_type"` + Identities []string `json:"identities"` + Entities []string `json:"entities"` + } `json:"auth_policies"` + // Directory Services config + IdentityProviders struct { + Limit int `json:"limit"` + IdpMetadataUrl string `json:"idp_metadata_url"` + IdpMetadata struct { + EntityId string `json:"entity_id"` + LoginUrl string `json:"login_url"` + LogoutUrl string `json:"logout_url"` + ErrorUrl string `json:"error_url"` + Certificate string `json:"certificate"` + NameIdPolicyFormat string `json:"name_id_policy_format"` + } `json:"idp_metadata"` + IdpMetadataXml string `json:"idp_metadata_xml"` + Name string `json:"name"` + UsernameAttribute string `json:"username_attr"` + EmailAttribute string `json:"email_attr"` + GroupsAttribute string `json:"groups_attr"` + GroupsDelim string `json:"groups_delim"` + EntityIssuer string `json:"entity_issuer"` + CustomAttributes []string `json:"custom_attributes"` + IsSignedAuthnReqEnabled bool `json:"is_signed_authn_req_enabled"` + } `json:"identity_providers"` + // Directory Services config + DirectoryServices struct { + Limit int `json:"limit"` + Name string `json:"name"` + Url string `json:"url"` + SecondaryUrls []string `json:"secondary_urls"` + DomainName string `json:"domain_name"` + ServiceAccount struct { + Username string `json:"username"` + Password string `json:"password"` + } `json:"service_account"` + OpenLdapConfiguration struct { + UserConfiguration struct { + UserObjectClass string `json:"user_object_class"` + UserSearchBase string `json:"user_search_base"` + UsernameAttribute string `json:"username_attribute"` + } `json:"user_configuration"` + UserGroupConfiguration struct { + GroupObjectClass string `json:"group_object_class"` + GroupSearchBase string `json:"group_search_base"` + GroupMemberAttribute string `json:"group_member_attribute"` + GroupMemberAttributeValue string `json:"group_member_attribute_value"` + } `json:"user_group_configuration"` + } `json:"open_ldap_configuration"` + + GroupSearchType string `json:"group_search_type"` + WhiteListedGroups []string `json:"white_listed_groups"` + } `json:"directory_services"` + } `json:"iam"` +} + +var testVars TestConfig + +func loadVars(filepath string, varStuct interface{}) { + // Read test_config_v2.json from home current path + configData, err := os.ReadFile(filepath) + if err != nil { + log.Printf("Got this error while reading config.json: %s", err.Error()) + os.Exit(1) + } + + err = json.Unmarshal(configData, varStuct) + if err != nil { + log.Printf("Got this error while unmarshalling config.json: %s", err.Error()) + os.Exit(1) + } +} +func TestMain(m *testing.M) { + log.Println("Do some crazy stuff before tests!") + loadVars("../../../../test_config_v2.json", &testVars) + os.Exit(m.Run()) +} diff --git a/nutanix/services/v2/iamv2/resource_nutanix_authorization_policies_v2.go b/nutanix/services/v2/iamv2/resource_nutanix_authorization_policies_v2.go new file mode 100644 index 000000000..ceee98992 --- /dev/null +++ b/nutanix/services/v2/iamv2/resource_nutanix_authorization_policies_v2.go @@ -0,0 +1,476 @@ +package iamv2 + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "log" + "reflect" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/common/v1/config" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authz" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func ResourceNutanixAuthPoliciesV2() *schema.Resource { + return &schema.Resource{ + CreateContext: ResourceNutanixAuthPoliciesV2Create, + ReadContext: ResourceNutanixAuthPoliciesV2Read, + UpdateContext: ResourceNutanixAuthPoliciesV2Update, + DeleteContext: ResourceNutanixAuthPoliciesV2Delete, + + Importer: &schema.ResourceImporter{ + StateContext: func(ctx context.Context, rd *schema.ResourceData, i interface{}) ([]*schema.ResourceData, error) { + log.Printf("[DEBUG] Importing Authorization Policy") + return []*schema.ResourceData{rd}, nil + }, + }, + + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "client_name": { + Type: schema.TypeString, + Computed: true, + }, + + "identities": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "reserved": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: SuppressEquivalentAuthPolicyDiffs, + StateFunc: func(v interface{}) string { + log.Printf("[DEBUG] StateFunc value: %v\n", v) + json, err := structure.NormalizeJsonString(v) + if err != nil { + log.Printf("[DEBUG] StateFunc err : %v\n", err) + } + return json + }, + }, + }, + }, + }, + + "entities": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "reserved": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: SuppressEquivalentAuthPolicyDiffs, + StateFunc: func(v interface{}) string { + log.Printf("[DEBUG] StateFunc v : %v\n", v) + json, err := structure.NormalizeJsonString(v) + if err != nil { + log.Printf("[DEBUG] StateFunc err : %v\n", err) + } + return json + }, + }, + }, + }, + }, + + "role": { + Type: schema.TypeString, + Required: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + "is_system_defined": { + Type: schema.TypeBool, + Computed: true, + }, + "authorization_policy_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"PREDEFINED_READ_ONLY", "SERVICE_DEFINED_READ_ONLY", + "PREDEFINED_UPDATE_IDENTITY_ONLY", "SERVICE_DEFINED", "USER_DEFINED"}, false), + }, + }, + } +} + +func ResourceNutanixAuthPoliciesV2Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + log.Printf("[DEBUG] Creating Authorization Policy") + conn := meta.(*conns.Client).IamAPI + input := &import1.AuthorizationPolicy{} + log.Printf("[DEBUG] Creating Authorization Policy") + + if display, ok := d.GetOk("display_name"); ok { + input.DisplayName = utils.StringPtr(display.(string)) + } + if desc, ok := d.GetOk("description"); ok { + input.Description = utils.StringPtr(desc.(string)) + } + if identities, ok := d.GetOk("identities"); ok { + identities, err := expandIdentityFilter(identities.([]interface{})) + if err != nil { + return diag.Errorf("error while creating Authorization Policy in identities err: %v", err) + } + + input.Identities = identities + + } + if entities, ok := d.GetOk("entities"); ok { + entities, err := expandEntityFilter(entities.([]interface{})) + if err != nil { + return diag.Errorf("error while creating Authorization Policy in entities err: %v", err) + } + + input.Entities = entities + } + if role, ok := d.GetOk("role"); ok { + input.Role = utils.StringPtr(role.(string)) + } + if authPolicyType, ok := d.GetOk("authorization_policy_type"); ok { + subMap := map[string]interface{}{ + "USER_DEFINED": 2, + "SERVICE_DEFINED": 3, + "PREDEFINED_READ_ONLY": 4, + "PREDEFINED_UPDATE_IDENTITY_ONLY": 5, + "SERVICE_DEFINED_READ_ONLY": 6, + } + pInt := subMap[authPolicyType.(string)] + p := import1.AuthorizationPolicyType(pInt.(int)) + input.AuthorizationPolicyType = &p + } + + resp, err := conn.AuthAPIInstance.CreateAuthorizationPolicy(input) + if err != nil { + return diag.Errorf("error while creating authorization policies : %v", err) + } + + getResp := resp.Data.GetValue().(import1.AuthorizationPolicy) + + log.Printf("[DEBUG] Creating Authorization Policy Return") + + d.Set("ext_id", *getResp.ExtId) + d.SetId(*getResp.ExtId) + return nil +} + +func ResourceNutanixAuthPoliciesV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + log.Printf("[DEBUG] Reading Authorization Policy") + conn := meta.(*conns.Client).IamAPI + + resp, err := conn.AuthAPIInstance.GetAuthorizationPolicyById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while fetching authorization polices: %v", err) + } + getResp := resp.Data.GetValue().(import1.AuthorizationPolicy) + + if err := d.Set("display_name", getResp.DisplayName); err != nil { + return diag.FromErr(err) + } + if err := d.Set("description", getResp.Description); err != nil { + return diag.FromErr(err) + } + if err := d.Set("client_name", getResp.ClientName); err != nil { + return diag.FromErr(err) + } + if err := d.Set("identities", flattenIdentityFilters(getResp.Identities)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("entities", flattenEntityFilters(getResp.Entities)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("role", getResp.Role); err != nil { + return diag.FromErr(err) + } + if getResp.CreatedTime != nil { + t := getResp.CreatedTime + if err := d.Set("created_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if getResp.LastUpdatedTime != nil { + t := getResp.LastUpdatedTime + if err := d.Set("last_updated_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if err := d.Set("created_by", getResp.CreatedBy); err != nil { + return diag.FromErr(err) + } + if err := d.Set("is_system_defined", getResp.IsSystemDefined); err != nil { + return diag.FromErr(err) + } + if err := d.Set("authorization_policy_type", flattenAuthorizationPolicyType(getResp.AuthorizationPolicyType)); err != nil { + return diag.FromErr(err) + } + return nil +} + +func ResourceNutanixAuthPoliciesV2Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + log.Printf("[DEBUG] Updating Authorization Policy") + conn := meta.(*conns.Client).IamAPI + updatedSpec := import1.AuthorizationPolicy{} + + resp, err := conn.AuthAPIInstance.GetAuthorizationPolicyById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while fetching Authorization Policy: %v", err) + } + + etagValue := conn.AuthAPIInstance.ApiClient.GetEtag(resp) + headers := make(map[string]interface{}) + headers["If-Match"] = etagValue + + updatedSpec = resp.Data.GetValue().(import1.AuthorizationPolicy) + + if d.HasChange("display_name") { + updatedSpec.DisplayName = utils.StringPtr(d.Get("display_name").(string)) + } + if d.HasChange("description") { + updatedSpec.Description = utils.StringPtr(d.Get("description").(string)) + } + if d.HasChange("identities") { + identities, err := expandIdentityFilter(d.Get("identities").([]interface{})) + if err != nil { + return diag.Errorf("error while updating Authorization Policy in identities err: %v", err) + } + updatedSpec.Identities = identities + } + if d.HasChange("entities") { + entities, err := expandEntityFilter(d.Get("entities").([]interface{})) + if err != nil { + return diag.Errorf("error while updating Authorization Policy in entities err: %v", err) + } + updatedSpec.Entities = entities + } + if d.HasChange("role") { + updatedSpec.Role = utils.StringPtr(d.Get("role").(string)) + } + if d.HasChange("authorization_policy_type") { + subMap := map[string]interface{}{ + "USER_DEFINED": 2, + "SERVICE_DEFINED": 3, + "PREDEFINED_READ_ONLY": 4, + "PREDEFINED_UPDATE_IDENTITY_ONLY": 5, + "SERVICE_DEFINED_READ_ONLY": 6, + } + pInt := subMap[d.Get("authorization_policy_type").(string)] + p := import1.AuthorizationPolicyType(pInt.(int)) + updatedSpec.AuthorizationPolicyType = &p + } + + updatedResp, err := conn.AuthAPIInstance.UpdateAuthorizationPolicyById(utils.StringPtr(d.Id()), &updatedSpec, headers) + if err != nil { + return diag.Errorf("error while updating Authorization Policy: %v", err) + } + + updatedResponse := updatedResp.Data.GetValue().(config.Message) + log.Printf("[DEBUG] updatedResponse : %v\n", updatedResponse) + + if updatedResponse.Message != nil { + fmt.Println("updated the Authorization Policy") + } + return ResourceNutanixAuthPoliciesV2Read(ctx, d, meta) +} + +func ResourceNutanixAuthPoliciesV2Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + log.Printf("[DEBUG] Deleting Authorization Policy") + conn := meta.(*conns.Client).IamAPI + + readResp, err := conn.AuthAPIInstance.GetAuthorizationPolicyById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while fetching auth policy: %v", err) + } + + etagValue := conn.AuthAPIInstance.ApiClient.GetEtag(readResp) + headers := make(map[string]interface{}) + headers["If-Match"] = etagValue + resp, err := conn.AuthAPIInstance.DeleteAuthorizationPolicyById(utils.StringPtr(d.Id()), headers) + if err != nil { + return diag.Errorf("error while deleting auth policy : %v", err) + } + + if resp == nil { + fmt.Println("auth policy deleted successfully.") + } + return nil +} + +func expandIdentityFilter(identities []interface{}) ([]import1.IdentityFilter, error) { + if len(identities) > 0 { + filters := make([]import1.IdentityFilter, len(identities)) + + log.Printf("[DEBUG] expandIdentityFilter ") + for key, value := range identities { + log.Printf("[DEBUG] expandIdentityFilter key:%v\n", key) + item, ok := value.(map[string]interface{}) + if !ok { + // Handle error or continue based on requirements + log.Printf("[DEBUG] expandIdentityFilter continue\n") + continue + } + log.Printf("[DEBUG] expandIdentityFilter item : %v\n", item) + log.Printf("[DEBUG] expandIdentityFilter item type : %v\n", reflect.TypeOf(item)) + filter := import1.IdentityFilter{} + + if val, exists := item["reserved"]; exists { + // Assuming the field is of type string, adjust the type assertion accordingly + log.Printf("[DEBUG] expandIdentityFilter val : %v\n", val.(string)) + log.Printf("[DEBUG] expandIdentityFilter val type : %v\n", reflect.TypeOf(val)) + reserved, err := deserializeJSONStringToMap(val.(string)) + if err != nil { + return nil, fmt.Errorf(err.Error()) + } + log.Printf("[DEBUG] expandIdentityFilter reserved : %v\n", reserved) + filter.Reserved_ = reserved + } + // Repeat for other fields as necessary + log.Printf("[DEBUG] expandIdentityFilter key:%v filter.Reserved_ : %v\n", key, filter.Reserved_) + filters[key] = filter + } + return filters, nil + } + return nil, nil +} +func expandEntityFilter(entities []interface{}) ([]import1.EntityFilter, error) { + if len(entities) > 0 { + filters := make([]import1.EntityFilter, len(entities)) + + log.Printf("[DEBUG] expandEntityFilter ") + for key, value := range entities { + log.Printf("[DEBUG] expandEntityFilter key:%v\n", key) + item, ok := value.(map[string]interface{}) + if !ok { + // Handle error or continue based on requirements + log.Printf("[DEBUG] expandEntityFilter continue\n") + continue + } + log.Printf("[DEBUG] expandEntityFilter item : %v\n", item) + log.Printf("[DEBUG] expandEntityFilter item type : %v\n", reflect.TypeOf(item)) + filter := import1.EntityFilter{} + + if val, exists := item["reserved"]; exists { + // Assuming the field is of type string, adjust the type assertion accordingly + log.Printf("[DEBUG] expandEntityFilter val : %v\n", val.(string)) + log.Printf("[DEBUG] expandEntityFilter val type : %v\n", reflect.TypeOf(val)) + reserved, err := deserializeJSONStringToMap(val.(string)) + if err != nil { + return nil, fmt.Errorf(err.Error()) + } + log.Printf("[DEBUG] expandEntityFilter reserved : %v\n", reserved) + filter.Reserved_ = reserved + } + // Repeat for other fields as necessary + log.Printf("[DEBUG] expandEntityFilter key:%v filter.Reserved_ : %v\n", key, filter.Reserved_) + filters[key] = filter + } + return filters, nil + } + return nil, nil +} + +func deserializeJSONStringToMap(jsonString string) (map[string]interface{}, error) { + var m map[string]interface{} + err := json.Unmarshal([]byte(jsonString), &m) + if err != nil { + log.Printf("[DEBUG] deserializeJSONStringToMap err : %v\n", err) + return nil, err + } + log.Printf("[DEBUG] deserializeJSONStringToMap map : %v\n", m) + return m, nil +} + +func SuppressEquivalentAuthPolicyDiffs(k, old, new string, d *schema.ResourceData) bool { + return AuthPolicyStringsEquivalent(old, new) +} + +func AuthPolicyStringsEquivalent(s1, s2 string) bool { + if strings.TrimSpace(s1) == "" && strings.TrimSpace(s2) == "" { + log.Printf("[DEBUG] AuthPolicyStringsEquivalent Both strings are empty") + return true + } + + if strings.TrimSpace(s1) == "{}" && strings.TrimSpace(s2) == "" { + log.Printf("[DEBUG] AuthPolicyStringsEquivalent s1 is empty and s2 is {}") + return true + } + + if strings.TrimSpace(s1) == "" && strings.TrimSpace(s2) == "{}" { + log.Printf("[DEBUG] AuthPolicyStringsEquivalent s1 is {} and s2 is empty") + return true + } + + if strings.TrimSpace(s1) == "{}" && strings.TrimSpace(s2) == "{}" { + log.Printf("[DEBUG] AuthPolicyStringsEquivalent Both strings are {}") + return true + } + log.Printf("[DEBUG] AuthPolicyStringsEquivalent s1: %s, s2: %s return false", s1, s2) + + return false + +} + +// SuppressEquivalentJSONDiffs returns a difference suppression function that compares +// two JSON strings and returns `true` if they are semantically equivalent. +func SuppressEquivalentJSONDiffs(k, old, new string, d *schema.ResourceData) bool { + return JSONStringsEqual(old, new) +} + +func JSONStringsEqual(s1, s2 string) bool { + b1 := bytes.NewBufferString("") + if err := json.Compact(b1, []byte(s1)); err != nil { + return false + } + + b2 := bytes.NewBufferString("") + if err := json.Compact(b2, []byte(s2)); err != nil { + return false + } + + return JSONBytesEqual(b1.Bytes(), b2.Bytes()) +} + +func JSONBytesEqual(b1, b2 []byte) bool { + var o1 interface{} + if err := json.Unmarshal(b1, &o1); err != nil { + return false + } + + var o2 interface{} + if err := json.Unmarshal(b2, &o2); err != nil { + return false + } + + return reflect.DeepEqual(o1, o2) +} diff --git a/nutanix/services/v2/iamv2/resource_nutanix_authorization_policies_v2_test.go b/nutanix/services/v2/iamv2/resource_nutanix_authorization_policies_v2_test.go new file mode 100644 index 000000000..9fbe47e53 --- /dev/null +++ b/nutanix/services/v2/iamv2/resource_nutanix_authorization_policies_v2_test.go @@ -0,0 +1,292 @@ +package iamv2_test + +import ( + "fmt" + "os" + "regexp" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const resourceNameAuthorizationPolicy = "nutanix_authorization_policy_v2.test" + +func TestAccNutanixAuthorizationPolicyV2Resource_CreateACP(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAuthorizationPolicyResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameAuthorizationPolicy, "ext_id"), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "display_name", testVars.Iam.AuthPolicies.DisplayName), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "description", testVars.Iam.AuthPolicies.Description), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "authorization_policy_type", testVars.Iam.AuthPolicies.AuthPolicyType), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "identities.#", strconv.Itoa(len(testVars.Iam.AuthPolicies.Identities))), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "identities.0.reserved", testVars.Iam.AuthPolicies.Identities[0]), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "entities.#", strconv.Itoa(len(testVars.Iam.AuthPolicies.Entities))), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "entities.0.reserved", testVars.Iam.AuthPolicies.Entities[0]), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "entities.1.reserved", testVars.Iam.AuthPolicies.Entities[1]), + ), + }, + // test update ac + { + Config: testAuthorizationPolicyResourceUpdateConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameAuthorizationPolicy, "ext_id"), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "description", testVars.Iam.AuthPolicies.Description+"_updated"), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "display_name", testVars.Iam.AuthPolicies.DisplayName), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "authorization_policy_type", testVars.Iam.AuthPolicies.AuthPolicyType), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "identities.#", strconv.Itoa(len(testVars.Iam.AuthPolicies.Identities))), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "identities.0.reserved", testVars.Iam.AuthPolicies.Identities[0]), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "entities.#", strconv.Itoa(len(testVars.Iam.AuthPolicies.Entities))), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "entities.0.reserved", testVars.Iam.AuthPolicies.Entities[0]), + resource.TestCheckResourceAttr(resourceNameAuthorizationPolicy, "entities.1.reserved", testVars.Iam.AuthPolicies.Entities[1]), + ), + }, + }, + }) +} + +func TestAccNutanixAuthorizationPolicyV2Resource_WithNoDisplayName(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAuthorizationPolicyResourceWithoutDisplayNameConfig(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} + +func TestAccNutanixAuthorizationPolicyV2Resource_WithNoIdentities(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAuthorizationPolicyResourceWithoutIdentitiesConfig(filepath), + ExpectError: regexp.MustCompile("Insufficient identities blocks"), + }, + }, + }) +} + +func TestAccNutanixAuthorizationPolicyV2Resource_WithNoEntities(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAuthorizationPolicyResourceWithoutEntitiesConfig(filepath), + ExpectError: regexp.MustCompile("Insufficient entities blocks"), + }, + }, + }) +} + +func TestAccNutanixAuthorizationPolicyV2Resource_WithNoRole(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAuthorizationPolicyResourceWithoutRoleConfig(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} + +func testAuthorizationPolicyResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + auth_policies = local.config.iam.auth_policies + roles = local.config.iam.roles + } + + data "nutanix_operations_v2" "test" { + limit = 3 + } + + resource "nutanix_roles_v2" "test" { + display_name = local.roles.display_name + description = local.roles.description + operations = [ + data.nutanix_operations_v2.test.permissions[0].ext_id, + data.nutanix_operations_v2.test.permissions[1].ext_id, + data.nutanix_operations_v2.test.permissions[2].ext_id, + ] + depends_on = [data.nutanix_operations_v2.test] + } + + resource "nutanix_authorization_policy_v2" "test" { + role = nutanix_roles_v2.test.id + display_name = local.auth_policies.display_name + description = local.auth_policies.description + authorization_policy_type = local.auth_policies.authorization_policy_type + identities { + reserved = local.auth_policies.identities[0] + } + entities { + reserved = local.auth_policies.entities[0] + } + entities { + reserved = local.auth_policies.entities[1] + } + depends_on = [nutanix_roles_v2.test] + + }`, filepath) +} + +func testAuthorizationPolicyResourceUpdateConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + auth_policies = local.config.iam.auth_policies + roles = local.config.iam.roles + } + + data "nutanix_operations_v2" "test" { + limit = 3 + } + + resource "nutanix_roles_v2" "test" { + display_name = local.roles.display_name + description = local.roles.description + operations = [ + data.nutanix_operations_v2.test.permissions[0].ext_id, + data.nutanix_operations_v2.test.permissions[1].ext_id, + data.nutanix_operations_v2.test.permissions[2].ext_id, + ] + depends_on = [data.nutanix_operations_v2.test] + } + + resource "nutanix_authorization_policy_v2" "test" { + role = nutanix_roles_v2.test.id + display_name = local.auth_policies.display_name + description = "${local.auth_policies.description}_updated" + authorization_policy_type = local.auth_policies.authorization_policy_type + identities { + reserved = local.auth_policies.identities[0] + } + entities { + reserved = local.auth_policies.entities[0] + } + entities { + reserved = local.auth_policies.entities[1] + } + depends_on = [nutanix_roles_v2.test] + + }`, filepath) +} + +func testAuthorizationPolicyResourceWithoutDisplayNameConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + auth_policies = local.config.iam.auth_policies + } + + resource "nutanix_authorization_policy_v2" "test" { + role = local.auth_policies.role + description = local.auth_policies.description + authorization_policy_type = local.auth_policies.authorization_policy_type + identities { + reserved = local.auth_policies.identities[0] + } + entities { + reserved = local.auth_policies.entities[0] + } + entities { + reserved = local.auth_policies.entities[1] + } + + }`, filepath) +} + +func testAuthorizationPolicyResourceWithoutIdentitiesConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + auth_policies = local.config.iam.auth_policies + } + + resource "nutanix_authorization_policy_v2" "test" { + role = local.auth_policies.role + display_name = local.auth_policies.display_name + description = local.auth_policies.description + authorization_policy_type = local.auth_policies.authorization_policy_type + + entities { + reserved = local.auth_policies.entities[0] + } + entities { + reserved = local.auth_policies.entities[1] + } + }`, filepath) +} + +func testAuthorizationPolicyResourceWithoutEntitiesConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + auth_policies = local.config.iam.auth_policies + } + + resource "nutanix_authorization_policy_v2" "test" { + role = local.auth_policies.role + display_name = local.auth_policies.display_name + description = local.auth_policies.description + authorization_policy_type = local.auth_policies.authorization_policy_type + identities { + reserved = local.auth_policies.identities[0] + } + + }`, filepath) +} + +func testAuthorizationPolicyResourceWithoutRoleConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + auth_policies = local.config.iam.auth_policies + } + + resource "nutanix_authorization_policy_v2" "test" { + display_name = local.auth_policies.display_name + description = local.auth_policies.description + authorization_policy_type = local.auth_policies.authorization_policy_type + identities { + reserved = local.auth_policies.identities[0] + } + entities { + reserved = local.auth_policies.entities[0] + } + + }`, filepath) +} diff --git a/nutanix/services/v2/iamv2/resource_nutanix_directory_services_v2.go b/nutanix/services/v2/iamv2/resource_nutanix_directory_services_v2.go new file mode 100644 index 000000000..9c576c496 --- /dev/null +++ b/nutanix/services/v2/iamv2/resource_nutanix_directory_services_v2.go @@ -0,0 +1,570 @@ +package iamv2 + +import ( + "context" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authn" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +var password string + +func ResourceNutanixDirectoryServicesV2() *schema.Resource { + return &schema.Resource{ + CreateContext: ResourceNutanixDirectoryServicesV2Create, + ReadContext: ResourceNutanixDirectoryServicesV2Read, + UpdateContext: ResourceNutanixDirectoryServicesV2Update, + DeleteContext: ResourceNutanixDirectoryServicesV2Delete, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "url": { + Type: schema.TypeString, + Required: true, + }, + "secondary_urls": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "domain_name": { + Type: schema.TypeString, + Required: true, + }, + "directory_type": { + Type: schema.TypeString, + Required: true, + }, + "service_account": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "username": { + Type: schema.TypeString, + Required: true, + }, + "password": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "open_ldap_configuration": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_configuration": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_object_class": { + Type: schema.TypeString, + Required: true, + }, + "user_search_base": { + Type: schema.TypeString, + Required: true, + }, + "username_attribute": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "user_group_configuration": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_object_class": { + Type: schema.TypeString, + Required: true, + }, + "group_search_base": { + Type: schema.TypeString, + Required: true, + }, + "group_member_attribute": { + Type: schema.TypeString, + Required: true, + }, + "group_member_attribute_value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + }, + }, + "group_search_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "white_listed_groups": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + "ext_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + } +} + +func ResourceNutanixDirectoryServicesV2Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + input := &import1.DirectoryService{} + + if name, ok := d.GetOk("name"); ok { + input.Name = utils.StringPtr(name.(string)) + } + if url, ok := d.GetOk("url"); ok { + input.Url = utils.StringPtr(url.(string)) + } + if secUrls, ok := d.GetOk("secondary_urls"); ok { + secondaryUrlsList := secUrls.([]interface{}) + secondaryUrlsListStr := make([]string, len(secondaryUrlsList)) + for i, v := range secondaryUrlsList { + secondaryUrlsListStr[i] = v.(string) + } + input.SecondaryUrls = secondaryUrlsListStr + } + if domainName, ok := d.GetOk("domain_name"); ok { + input.DomainName = utils.StringPtr(domainName.(string)) + } + if dType, ok := d.GetOk("directory_type"); ok { + subMap := map[string]interface{}{ + "ACTIVE_DIRECTORY": 2, + "OPEN_LDAP": 3, + } + pInt := subMap[dType.(string)] + p := import1.DirectoryType(pInt.(int)) + + input.DirectoryType = &p + } + if serviceAcc, ok := d.GetOk("service_account"); ok { + input.ServiceAccount = expandDsServiceAccount(serviceAcc) + } + if ldap, ok := d.GetOk("open_ldap_configuration"); ok { + input.OpenLdapConfiguration = expandOpenLdapConfig(ldap) + } + if grpSearchType, ok := d.GetOk("group_search_type"); ok { + subMap := map[string]interface{}{ + "NON_RECURSIVE": 2, + "RECURSIVE": 3, + } + pInt := subMap[grpSearchType.(string)] + p := import1.GroupSearchType(pInt.(int)) + input.GroupSearchType = &p + } + if whitelistedGrp, ok := d.GetOk("white_listed_groups"); ok { + whitelistedGrpList := whitelistedGrp.([]interface{}) + whitelistedGrpListStr := make([]string, len(whitelistedGrpList)) + for i, v := range whitelistedGrpList { + whitelistedGrpListStr[i] = v.(string) + } + input.WhiteListedGroups = whitelistedGrpListStr + } + + resp, err := conn.DirectoryServiceAPIInstance.CreateDirectoryService(input) + if err != nil { + var errordata map[string]interface{} + e := json.Unmarshal([]byte(err.Error()), &errordata) + if e != nil { + return diag.FromErr(e) + } + data := errordata["data"].(map[string]interface{}) + errorList := data["error"].([]interface{}) + errorMessage := errorList[0].(map[string]interface{}) + return diag.Errorf("error while creating directory services : %v", errorMessage["message"]) + } + + getResp := resp.Data.GetValue().(import1.DirectoryService) + + d.SetId(*getResp.ExtId) + return ResourceNutanixDirectoryServicesV2Read(ctx, d, meta) +} + +func ResourceNutanixDirectoryServicesV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + resp, err := conn.DirectoryServiceAPIInstance.GetDirectoryServiceById(utils.StringPtr(d.Id())) + if err != nil { + var errordata map[string]interface{} + e := json.Unmarshal([]byte(err.Error()), &errordata) + if e != nil { + return diag.FromErr(e) + } + data := errordata["data"].(map[string]interface{}) + errorList := data["error"].([]interface{}) + errorMessage := errorList[0].(map[string]interface{}) + return diag.Errorf("error while fetching directory services: %v", errorMessage["message"]) + } + + getResp := resp.Data.GetValue().(import1.DirectoryService) + if err := d.Set("ext_id", getResp.ExtId); err != nil { + return diag.FromErr(err) + } + if err := d.Set("name", getResp.Name); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("url", getResp.Url); err != nil { + return diag.FromErr(err) + } + if err := d.Set("secondary_urls", getResp.SecondaryUrls); err != nil { + return diag.FromErr(err) + } + if err := d.Set("domain_name", getResp.DomainName); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("directory_type", flattenDirectoryType(getResp.DirectoryType)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("service_account", flattenDsServiceAccount(getResp.ServiceAccount)); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("open_ldap_configuration", flattenOpenLdapConfig(getResp.OpenLdapConfiguration)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("group_search_type", flattenGroupSearchType(getResp.GroupSearchType)); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("white_listed_groups", getResp.WhiteListedGroups); err != nil { + return diag.FromErr(err) + } + + if getResp.CreatedTime != nil { + t := getResp.CreatedTime + if err := d.Set("created_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if getResp.LastUpdatedTime != nil { + t := getResp.LastUpdatedTime + if err := d.Set("last_updated_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + + if err := d.Set("created_by", getResp.CreatedBy); err != nil { + return diag.FromErr(err) + } + return nil +} + +func ResourceNutanixDirectoryServicesV2Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + updatedSpec := import1.DirectoryService{} + + readResp, err := conn.DirectoryServiceAPIInstance.GetDirectoryServiceById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while fetching Directory service : %v", err) + } + // get etag value from read response to pass in update request If-Match header, Required for update request + etagValue := conn.SamlIdentityAPIInstance.ApiClient.GetEtag(readResp) + headers := make(map[string]interface{}) + headers["If-Match"] = etagValue + + updatedSpec = readResp.Data.GetValue().(import1.DirectoryService) + + serviceAccount := &import1.DsServiceAccount{} + + sa, ok := d.GetOk("service_account") + if !ok { + return diag.Errorf("service_account is required") + } + if d.HasChange("service_account") { + sa = expandDsServiceAccount(d.Get("service_account")) + } + username := sa.([]interface{})[0].(map[string]interface{})["username"] + decryptedPass, err := decrypt(password, "NUTANIX!123_DS#SA") + if err != nil { + panic(err) + } + + serviceAccount.Password = utils.StringPtr(decryptedPass) + serviceAccount.Username = utils.StringPtr(username.(string)) + + updatedSpec.ServiceAccount = serviceAccount + + if d.HasChange("name") { + updatedSpec.Name = utils.StringPtr(d.Get("name").(string)) + } + if d.HasChange("url") { + updatedSpec.Url = utils.StringPtr(d.Get("url").(string)) + } + if d.HasChange("secondary_urls") { + secUrls := d.Get("secondary_urls") + secondaryUrlsList := secUrls.([]interface{}) + secondaryUrlsListStr := make([]string, len(secondaryUrlsList)) + for i, v := range secondaryUrlsList { + secondaryUrlsListStr[i] = v.(string) + } + updatedSpec.SecondaryUrls = secondaryUrlsListStr + } + if d.HasChange("domain_name") { + updatedSpec.DomainName = utils.StringPtr(d.Get("domain_name").(string)) + } + if d.HasChange("directory_type") { + subMap := map[string]interface{}{ + "ACTIVE_DIRECTORY": 2, + "OPEN_LDAP": 3, + } + pInt := subMap[d.Get("directory_type").(string)] + p := import1.DirectoryType(pInt.(int)) + + updatedSpec.DirectoryType = &p + } + if d.HasChange("open_ldap_configuration") { + updatedSpec.OpenLdapConfiguration = expandOpenLdapConfig(d.Get("open_ldap_configuration")) + } + if d.HasChange("group_search_type") { + subMap := map[string]interface{}{ + "NON_RECURSIVE": 2, + "RECURSIVE": 3, + } + pInt := subMap[d.Get("group_search_type").(string)] + p := import1.GroupSearchType(pInt.(int)) + updatedSpec.GroupSearchType = &p + } + if d.HasChange("white_listed_groups") { + whitelistedGrp := d.Get("white_listed_groups") + whitelistedGrpList := whitelistedGrp.([]interface{}) + whitelistedGrpListStr := make([]string, len(whitelistedGrpList)) + for i, v := range whitelistedGrpList { + whitelistedGrpListStr[i] = v.(string) + } + updatedSpec.WhiteListedGroups = whitelistedGrpListStr + } + + updatedResp, err := conn.DirectoryServiceAPIInstance.UpdateDirectoryServiceById(utils.StringPtr(d.Id()), &updatedSpec, headers) + if err != nil { + return diag.Errorf("error while updating directory services: %v", err) + } + + updatedResponse := updatedResp.Data.GetValue().(import1.DirectoryService) + + if updatedResponse.ExtId != nil { + fmt.Println("updated the directory services") + } + return ResourceNutanixDirectoryServicesV2Read(ctx, d, meta) +} + +func ResourceNutanixDirectoryServicesV2Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + readResp, err := conn.DirectoryServiceAPIInstance.GetDirectoryServiceById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while fetching Directory service : %v", err) + } + // get etag value from read response to pass in update request If-Match header, Required for update request + etagValue := conn.SamlIdentityAPIInstance.ApiClient.GetEtag(readResp) + headers := make(map[string]interface{}) + headers["If-Match"] = etagValue + + resp, err := conn.DirectoryServiceAPIInstance.DeleteDirectoryServiceById(utils.StringPtr(d.Id()), headers) + if err != nil { + return diag.Errorf("error while deleting directory services : %v", err) + } + + if resp == nil { + fmt.Println("Directory Services deleted successfully.") + } + return nil +} + +func expandDsServiceAccount(pr interface{}) *import1.DsServiceAccount { + if pr != nil { + prI := pr.([]interface{}) + val := prI[0].(map[string]interface{}) + + ds := &import1.DsServiceAccount{} + + if pass, ok := val["password"]; ok { + ds.Password = utils.StringPtr(pass.(string)) + } + if user, ok := val["username"]; ok { + ds.Username = utils.StringPtr(user.(string)) + } + encryptedPass, err := encrypt(utils.StringValue(ds.Password), "NUTANIX!123_DS#SA") + if err != nil { + panic(err) + } + password = encryptedPass + return ds + } + return nil +} + +func generateKey(passphrase string) []byte { + key := sha256.Sum256([]byte(passphrase)) + return key[:] +} + +func encrypt(plainText, passphrase string) (string, error) { + key := generateKey(passphrase) + + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + + aesGCM, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + nonce := make([]byte, aesGCM.NonceSize()) + if _, err = io.ReadFull(rand.Reader, nonce); err != nil { + return "", err + } + + cipherText := aesGCM.Seal(nonce, nonce, []byte(plainText), nil) + return base64.StdEncoding.EncodeToString(cipherText), nil +} + +func decrypt(cipherText, passphrase string) (string, error) { + key := generateKey(passphrase) + + encData, err := base64.StdEncoding.DecodeString(cipherText) + if err != nil { + return "", err + } + + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + + aesGCM, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + nonceSize := aesGCM.NonceSize() + if len(encData) < nonceSize { + return "", errors.New("ciphertext too short") + } + + nonce, cipherTextBytes := encData[:nonceSize], encData[nonceSize:] + plainText, err := aesGCM.Open(nil, nonce, cipherTextBytes, nil) + if err != nil { + return "", err + } + + return string(plainText), nil +} + +func expandOpenLdapConfig(pr interface{}) *import1.OpenLdapConfig { + if pr != nil { + prI := pr.([]interface{}) + val := prI[0].(map[string]interface{}) + + ldap := &import1.OpenLdapConfig{} + + if userCfg, ok := val["user_configuration"]; ok { + ldap.UserConfiguration = expandUserConfiguration(userCfg) + } + if userGroupCfg, ok := val["user_group_configuration"]; ok { + ldap.UserGroupConfiguration = expandUserGroupConfiguration(userGroupCfg) + } + return ldap + } + return nil +} + +func expandUserConfiguration(pr interface{}) *import1.UserConfiguration { + if pr != nil { + prI := pr.([]interface{}) + val := prI[0].(map[string]interface{}) + + usrcfg := &import1.UserConfiguration{} + + if usrObjClass, ok := val["user_object_class"]; ok { + usrcfg.UserObjectClass = utils.StringPtr(usrObjClass.(string)) + } + if usrSearchbase, ok := val["user_search_base"]; ok { + usrcfg.UserSearchBase = utils.StringPtr(usrSearchbase.(string)) + } + if usernameAttr, ok := val["username_attribute"]; ok { + usrcfg.UsernameAttribute = utils.StringPtr(usernameAttr.(string)) + } + + return usrcfg + } + return nil +} + +func expandUserGroupConfiguration(pr interface{}) *import1.UserGroupConfiguration { + if pr != nil { + prI := pr.([]interface{}) + val := prI[0].(map[string]interface{}) + + usrGrp := &import1.UserGroupConfiguration{} + + if grpObjClass, ok := val["group_object_class"]; ok { + usrGrp.GroupObjectClass = utils.StringPtr(grpObjClass.(string)) + } + if grpSearchbase, ok := val["group_search_base"]; ok { + usrGrp.GroupSearchBase = utils.StringPtr(grpSearchbase.(string)) + } + if grpMemberAttr, ok := val["group_member_attribute"]; ok { + usrGrp.GroupMemberAttribute = utils.StringPtr(grpMemberAttr.(string)) + } + if grpAttrVal, ok := val["group_member_attribute_value"]; ok { + usrGrp.GroupMemberAttributeValue = utils.StringPtr(grpAttrVal.(string)) + } + return usrGrp + } + return nil +} diff --git a/nutanix/services/v2/iamv2/resource_nutanix_directory_services_v2_test.go b/nutanix/services/v2/iamv2/resource_nutanix_directory_services_v2_test.go new file mode 100644 index 000000000..2245a0584 --- /dev/null +++ b/nutanix/services/v2/iamv2/resource_nutanix_directory_services_v2_test.go @@ -0,0 +1,410 @@ +package iamv2_test + +import ( + "fmt" + "os" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const resourceNameDirectoryServices = "nutanix_directory_services_v2.test" + +func TestAccNutanixDirectoryServicesV2Resource_CreateACTIVE_DIRECTORYService(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testDirectoryServicesResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameDirectoryServices, "ext_id"), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "name", testVars.Iam.DirectoryServices.Name), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "domain_name", testVars.Iam.DirectoryServices.DomainName), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "directory_type", "ACTIVE_DIRECTORY"), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "url", testVars.Iam.DirectoryServices.Url), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "service_account.0.username", testVars.Iam.DirectoryServices.ServiceAccount.Username), + resource.TestCheckResourceAttrSet(resourceNameDirectoryServices, "service_account.0.password"), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "white_listed_groups.0", testVars.Iam.DirectoryServices.WhiteListedGroups[0]), + ), + }, + { + Config: testDirectoryServicesUpdateResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameDirectoryServices, "ext_id"), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "name", testVars.Iam.DirectoryServices.Name), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "domain_name", testVars.Iam.DirectoryServices.DomainName), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "directory_type", "ACTIVE_DIRECTORY"), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "url", testVars.Iam.DirectoryServices.Url), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "service_account.0.username", testVars.Iam.DirectoryServices.ServiceAccount.Username), + resource.TestCheckResourceAttrSet(resourceNameDirectoryServices, "service_account.0.password"), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "white_listed_groups.0", testVars.Iam.DirectoryServices.WhiteListedGroups[1]), + ), + }}, + }) +} + +func TestAccNutanixDirectoryServicesV2Resource_CreateOpenLDAPService(t *testing.T) { + t.Skip("Skipping test as OpenLDAP waiting for LDAP configuration") + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testDirectoryOpenLDAPServicesResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameDirectoryServices, "ext_id"), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "name", testVars.Iam.DirectoryServices.Name), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "domain_name", testVars.Iam.DirectoryServices.DomainName), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "directory_type", "ACTIVE_DIRECTORY"), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "url", testVars.Iam.DirectoryServices.Url), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "service_account.0.username", testVars.Iam.DirectoryServices.ServiceAccount.Username), + resource.TestCheckResourceAttrSet(resourceNameDirectoryServices, "service_account.0.password"), + ), + }}, + }) +} + +func TestAccNutanixDirectoryServicesV2Resource_CreateACTIVE_DIRECTORYAlreadyExists(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testDirectoryServicesResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameDirectoryServices, "ext_id"), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "name", testVars.Iam.DirectoryServices.Name), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "domain_name", testVars.Iam.DirectoryServices.DomainName), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "directory_type", "ACTIVE_DIRECTORY"), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "url", testVars.Iam.DirectoryServices.Url), + resource.TestCheckResourceAttr(resourceNameDirectoryServices, "service_account.0.username", testVars.Iam.DirectoryServices.ServiceAccount.Username), + resource.TestCheckResourceAttrSet(resourceNameDirectoryServices, "service_account.0.password"), + ), + }, + { + Config: testDirectoryServicesDuplicatedResourceConfig(filepath), + ExpectError: regexp.MustCompile("Failed to create directory service as directory service with name " + testVars.Iam.DirectoryServices.Name + " already exists"), + }}, + }) +} + +func TestAccNutanixDirectoryServicesV2Resource_WithNoName(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testDirectoryServicesResourceWithoutNameConfig(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} +func TestAccNutanixDirectoryServicesV2Resource_WithNoUrl(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testDirectoryServicesResourceWithoutUrlConfig(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} + +func TestAccNutanixDirectoryServicesV2Resource_WithNoDomainName(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testDirectoryServicesResourceWithoutDomainNameConfig(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} + +func TestAccNutanixDirectoryServicesV2Resource_WithNoDirectoryType(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testDirectoryServicesResourceWithoutDirectoryTypeConfig(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} + +func TestAccNutanixDirectoryServicesV2Resource_WithNoServiceAccount(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testDirectoryServicesResourceWithoutServiceAccountConfig(filepath), + ExpectError: regexp.MustCompile("Insufficient service_account blocks"), + }, + }, + }) +} + +func testDirectoryServicesResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test" { + name = local.directory_services.name + url = local.directory_services.url + directory_type = "ACTIVE_DIRECTORY" + domain_name = local.directory_services.domain_name + service_account { + username = local.directory_services.service_account.username + password = local.directory_services.service_account.password + } + white_listed_groups = [ local.directory_services.white_listed_groups[0]] + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + }`, filepath) +} + +func testDirectoryServicesUpdateResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test" { + name = local.directory_services.name + url = local.directory_services.url + directory_type = "ACTIVE_DIRECTORY" + domain_name = local.directory_services.domain_name + service_account { + username = local.directory_services.service_account.username + password = local.directory_services.service_account.password + } + white_listed_groups = [ local.directory_services.white_listed_groups[1]] + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + }`, filepath) +} + +func testDirectoryOpenLDAPServicesResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test" { + name = local.directory_services.name + url = local.directory_services.url + directory_type = "OPEN_LDAP" + domain_name = local.directory_services.domain_name + service_account { + username = local.directory_services.service_account.username + password = local.directory_services.service_account.password + } + open_ldap_configuration { + user_configuration { + user_search_base = local.directory_services.open_ldap_configuration.user_configuration.user_search_base + username_attribute = local.directory_services.open_ldap_configuration.user_configuration.username_attribute + user_object_class = local.directory_services.open_ldap_configuration.user_configuration.user_object_class + } + user_group_configuration { + group_object_class = local.directory_services.open_ldap_configuration.user_group_configuration.group_object_class + group_search_base = local.directory_services.open_ldap_configuration.user_group_configuration.group_search_base + group_member_attribute = local.directory_services.open_ldap_configuration.user_group_configuration.group_member_attribute + group_member_attribute_value = local.directory_services.open_ldap_configuration.user_group_configuration.group_member_attribute_value + } + } + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + }`, filepath) +} + +func testDirectoryServicesDuplicatedResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test_1" { + name = local.directory_services.name + url = local.directory_services.url + directory_type = "ACTIVE_DIRECTORY" + domain_name = local.directory_services.domain_name + service_account { + username = local.directory_services.service_account.username + password = local.directory_services.service_account.password + } + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + }`, filepath) +} + +func testDirectoryServicesResourceWithoutNameConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test" { + service_account { + username = local.directory_services.service_account.username + password = local.directory_services.service_account.password + } + directory_type = local.directory_services.directory_type + domain_name = local.directory_services.domain_name + url = local.directory_services.url + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + }`, filepath) +} + +func testDirectoryServicesResourceWithoutUrlConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test" { + name = local.directory_services.name + service_account { + username = local.directory_services.service_account.username + password = local.directory_services.service_account.password + } + directory_type = local.directory_services.directory_type + domain_name = local.directory_services.domain_name + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + }`, filepath) +} + +func testDirectoryServicesResourceWithoutDomainNameConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test" { + name = local.directory_services.name + service_account { + username = local.directory_services.service_account.username + password = local.directory_services.service_account.password + } + directory_type = local.directory_services.directory_type + url = local.directory_services.url + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + }`, filepath) +} + +func testDirectoryServicesResourceWithoutDirectoryTypeConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test" { + name = local.directory_services.name + service_account { + username = local.directory_services.service_account.username + password = local.directory_services.service_account.password + } + domain_name = local.directory_services.domain_name + url = local.directory_services.url + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + }`, filepath) +} + +func testDirectoryServicesResourceWithoutServiceAccountConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + directory_services = local.config.iam.directory_services + } + + resource "nutanix_directory_services_v2" "test" { + name = local.directory_services.name + + directory_type = local.directory_services.directory_type + domain_name = local.directory_services.domain_name + url = local.directory_services.url + lifecycle { + ignore_changes = [ + service_account.0.password, + ] + } + + }`, filepath) +} diff --git a/nutanix/services/v2/iamv2/resource_nutanix_roles_v2.go b/nutanix/services/v2/iamv2/resource_nutanix_roles_v2.go new file mode 100644 index 000000000..4a8559f3f --- /dev/null +++ b/nutanix/services/v2/iamv2/resource_nutanix_roles_v2.go @@ -0,0 +1,341 @@ +package iamv2 + +import ( + "context" + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/common/v1/config" + iamConfig "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authz" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func ResourceNutanixRolesV2() *schema.Resource { + return &schema.Resource{ + CreateContext: ResourceNutanixRolesV4Create, + ReadContext: ResourceNutanixRolesV4Read, + UpdateContext: ResourceNutanixRolesV4Update, + DeleteContext: ResourceNutanixRolesV4Delete, + Schema: map[string]*schema.Schema{ + "ext_id": { + Description: "ExtId for the Role.", + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "display_name": { + Description: "The display name for the Role.", + Type: schema.TypeString, + Required: true, + }, + "description": { + Description: "Description of the Role.", + Type: schema.TypeString, + Optional: true, + }, + "client_name": { + Description: "Client that created the entity.", + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "operations": { + Description: "List of Operations for the Role.", + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Description: "List of String", + Type: schema.TypeString, + }, + }, + "tenant_id": { + Type: schema.TypeString, + Computed: true, + }, + "links": { + Description: "A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Description: "The URL at which the entity described by the link can be accessed.", + Type: schema.TypeString, + Computed: true, + }, + "rel": { + Description: "A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of \"self\" identifies the URL for the object.", + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "accessible_clients": { + Description: "List of Accessible Clients for the Role.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Description: "List of String", + Type: schema.TypeString, + }, + }, + "accessible_entity_types": { + Description: "List of Accessible Entity Types for the Role.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Description: "List of String", + Type: schema.TypeString, + }, + }, + "assigned_users_count": { + Description: "Number of Users assigned to given Role.", + Type: schema.TypeInt, + Computed: true, + }, + "assigned_users_groups_count": { + Description: "Number of User Groups assigned to given Role.", + Type: schema.TypeInt, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + "is_system_defined": { + Description: "Flag identifying if the Role is system defined or not.", + Type: schema.TypeBool, + Computed: true, + }, + }, + } +} + +func ResourceNutanixRolesV4Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + body := &iamConfig.Role{} + + if extId, ok := d.GetOk("ext_id"); ok { + body.ExtId = utils.StringPtr(extId.(string)) + } + if displayName, ok := d.GetOk("display_name"); ok { + body.DisplayName = utils.StringPtr(displayName.(string)) + } + if description, ok := d.GetOk("description"); ok { + body.Description = utils.StringPtr(description.(string)) + } + if clientName, ok := d.GetOk("client_name"); ok { + body.ClientName = utils.StringPtr(clientName.(string)) + } + if operations, ok := d.GetOk("operations"); ok { + operationsList := operations.([]interface{}) + operationsListStr := make([]string, len(operationsList)) + for i, v := range operationsList { + operationsListStr[i] = v.(string) + } + body.Operations = operationsListStr + } + + resp, err := conn.RolesAPIInstance.CreateRole(body) + if err != nil { + return diag.Errorf("error while creating role: %v", err) + } + + getResp := resp.Data.GetValue().(iamConfig.Role) + d.SetId(*getResp.ExtId) + return ResourceNutanixRolesV4Read(ctx, d, meta) +} + +func ResourceNutanixRolesV4Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + resp, err := conn.RolesAPIInstance.GetRoleById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while Reading role: %v", err) + } + + getResp := resp.Data.GetValue().(iamConfig.Role) + + // after creating role, operations saved in remote in different order than local + if len(getResp.Operations) > 0 { + // read the remote operations and local operations list + remoteOperations := getResp.Operations + localOperations := d.Get("operations").([]interface{}) + + // final result for checking if operations are different + diff := false + + // convert local operations to string slice + localOperationsStr := make([]string, len(localOperations)) + for i, v := range localOperations { + localOperationsStr[i] = (v.(string)) + } + + log.Printf("[DEBUG] localOperationsStr: %v", localOperationsStr) + + // check if remote operations are different from local operations + for _, operation := range remoteOperations { + offset := indexOf(localOperationsStr, operation) + + if offset == -1 { + log.Printf("[DEBUG] Operation %v not found in local operations", operation) + diff = true + break + } + } + + // if operations are different, update local operations + if diff { + log.Printf("[DEBUG] Operations are different. Updating local operations") + if err := d.Set("operations", getResp.Operations); err != nil { + return diag.FromErr(err) + } + } else { + // if operations are same, do not update local operations + log.Printf("[DEBUG] Operations are same. Not updating local operations") + } + } + + if err := d.Set("links", flattenLinks(getResp.Links)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("display_name", getResp.DisplayName); err != nil { + return diag.FromErr(err) + } + if err := d.Set("description", getResp.Description); err != nil { + return diag.FromErr(err) + } + if err := d.Set("client_name", getResp.ClientName); err != nil { + return diag.FromErr(err) + } + if err := d.Set("accessible_clients", getResp.AccessibleClients); err != nil { + return diag.FromErr(err) + } + if err := d.Set("accessible_entity_types", getResp.AccessibleEntityTypes); err != nil { + return diag.FromErr(err) + } + if err := d.Set("assigned_users_count", getResp.AssignedUsersCount); err != nil { + return diag.FromErr(err) + } + if err := d.Set("assigned_users_groups_count", getResp.AssignedUserGroupsCount); err != nil { + return diag.FromErr(err) + } + if getResp.CreatedTime != nil { + t := getResp.CreatedTime + if err := d.Set("created_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if getResp.LastUpdatedTime != nil { + t := getResp.LastUpdatedTime + if err := d.Set("last_updated_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if err := d.Set("created_by", getResp.CreatedBy); err != nil { + return diag.FromErr(err) + } + if err := d.Set("is_system_defined", getResp.IsSystemDefined); err != nil { + return diag.FromErr(err) + } + + return nil +} + +func ResourceNutanixRolesV4Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + extId := utils.StringPtr(d.Id()) + + updatedSpec := iamConfig.Role{} + + readResp, err := conn.RolesAPIInstance.GetRoleById(extId) + if err != nil { + return diag.Errorf("error while fetching role: %v", err) + } + + // get etag value from read response to pass in update request If-Match header, Required for update request + etagValue := conn.RolesAPIInstance.ApiClient.GetEtag(readResp) + headers := make(map[string]interface{}) + headers["If-Match"] = etagValue + + updatedSpec = readResp.Data.GetValue().(iamConfig.Role) + + if d.HasChange("display_name") { + updatedSpec.DisplayName = utils.StringPtr(d.Get("display_name").(string)) + } + if d.HasChange("description") { + updatedSpec.Description = utils.StringPtr(d.Get("description").(string)) + } + if d.HasChange("client_name") { + updatedSpec.ClientName = utils.StringPtr(d.Get("client_name").(string)) + } + if d.HasChange("operations") { + operations := d.Get("operations").([]interface{}) + operationsListStr := make([]string, len(operations)) + for i, v := range operations { + operationsListStr[i] = v.(string) + } + updatedSpec.Operations = operationsListStr + } + + updateResp, err := conn.RolesAPIInstance.UpdateRoleById(extId, &updatedSpec, headers) + + if err != nil { + return diag.Errorf("error while updating role: %v", err) + } + log.Printf("[DEBUG] Role updated. Response: %v", *updateResp) + + updateTaskResp := updateResp.Data.GetValue().(config.Message) + + if updateTaskResp.Message != nil { + fmt.Println(*updateTaskResp.Message) + } + return ResourceNutanixRolesV4Read(ctx, d, meta) +} + +func ResourceNutanixRolesV4Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + readResp, err := conn.RolesAPIInstance.GetRoleById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while fetching role: %v", err) + } + + etagValue := conn.RolesAPIInstance.ApiClient.GetEtag(readResp) + headers := make(map[string]interface{}) + headers["If-Match"] = etagValue + + resp, err := conn.RolesAPIInstance.DeleteRoleById(utils.StringPtr(d.Id()), headers) + + if err != nil { + return diag.Errorf("error while Deleting role: %v", err) + } + + if resp == nil { + fmt.Println("Role deleted successfully.") + } + return nil +} + +func indexOf(slice []string, target string) int { + for i, v := range slice { + if v == target { + return i + } + } + return -1 +} diff --git a/nutanix/services/v2/iamv2/resource_nutanix_roles_v2_test.go b/nutanix/services/v2/iamv2/resource_nutanix_roles_v2_test.go new file mode 100644 index 000000000..8aba4c885 --- /dev/null +++ b/nutanix/services/v2/iamv2/resource_nutanix_roles_v2_test.go @@ -0,0 +1,209 @@ +package iamv2_test + +import ( + "fmt" + "os" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const resourceNameRoles = "nutanix_roles_v2.test" + +func TestAccNutanixRolesV4Resource_Basic(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testRoleResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameRoles, "client_name"), + resource.TestCheckResourceAttr(resourceNameRoles, "display_name", testVars.Iam.Roles.DisplayName), + resource.TestCheckResourceAttr(resourceNameRoles, "description", testVars.Iam.Roles.Description), + ), + }, + // update role + { + Config: testRoleResourceUpdateConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameRoles, "client_name"), + resource.TestCheckResourceAttr(resourceNameRoles, "display_name", fmt.Sprintf("%s_updated", testVars.Iam.Roles.DisplayName)), + resource.TestCheckResourceAttr(resourceNameRoles, "description", testVars.Iam.Roles.Description), + ), + }, + }, + }) +} + +func TestAccNutanixRolesV4Resource_DuplicateRole(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testRoleResourceDuplicateRoleConfig(filepath), + ExpectError: regexp.MustCompile("Failed to create a role due to already exists"), + }, + }, + }) +} + +func TestAccNutanixRolesV4Resource_WithNoDisplayName(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testRoleResourceWithoutDisplayNameConfig(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} +func TestAccNutanixRolesV4Resource_WithNoOperations(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testRoleResourceWithoutOperationsConfig(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} + +func testRoleResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + roles = local.config.iam.roles + } + + data "nutanix_operations_v2" "test" { + limit = 3 + } + + resource "nutanix_roles_v2" "test" { + display_name = local.roles.display_name + description = local.roles.description + operations = [ + data.nutanix_operations_v2.test.permissions[0].ext_id, + data.nutanix_operations_v2.test.permissions[1].ext_id, + data.nutanix_operations_v2.test.permissions[2].ext_id, + ] + depends_on = [data.nutanix_operations_v2.test] + }`, filepath) +} + +func testRoleResourceUpdateConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + roles = local.config.iam.roles + } + + data "nutanix_operations_v2" "test" { + limit = 3 + } + + resource "nutanix_roles_v2" "test" { + display_name = "${local.roles.display_name}_updated" + description = local.roles.description + operations = [ + data.nutanix_operations_v2.test.permissions[0].ext_id, + data.nutanix_operations_v2.test.permissions[1].ext_id, + data.nutanix_operations_v2.test.permissions[2].ext_id, + ] + depends_on = [data.nutanix_operations_v2.test] + }`, filepath) +} + +func testRoleResourceDuplicateRoleConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + roles = local.config.iam.roles + } + + data "nutanix_operations_v2" "test" { + limit = 3 + } + + resource "nutanix_roles_v2" "test_1" { + display_name = local.roles.display_name + description = local.roles.description + operations = [ + data.nutanix_operations_v2.test.permissions[0].ext_id, + data.nutanix_operations_v2.test.permissions[1].ext_id, + data.nutanix_operations_v2.test.permissions[2].ext_id, + ] + depends_on = [data.nutanix_operations_v2.test] + } + + resource "nutanix_roles_v2" "test_2" { + display_name = local.roles.display_name + description = local.roles.description + operations = [ + data.nutanix_operations_v2.test.permissions[0].ext_id, + data.nutanix_operations_v2.test.permissions[1].ext_id, + data.nutanix_operations_v2.test.permissions[2].ext_id, + ] + depends_on = [data.nutanix_operations_v2.test, resource.nutanix_roles_v2.test_1] + } + + `, filepath) +} + +func testRoleResourceWithoutDisplayNameConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + roles = local.config.iam.roles + } + + data "nutanix_operations_v2" "test" { + limit = 3 + } + + resource "nutanix_roles_v2" "test" { + description = local.roles.description + operations = [ + data.nutanix_operations_v2.test.permissions[0].ext_id, + data.nutanix_operations_v2.test.permissions[1].ext_id, + data.nutanix_operations_v2.test.permissions[2].ext_id, + ] + depends_on = [data.nutanix_operations_v2.test] + }`, filepath) +} + +func testRoleResourceWithoutOperationsConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + roles = local.config.iam.roles + } + + resource "nutanix_roles_v2" "test" { + display_name = local.roles.display_name + description = local.roles.description + }`, filepath) +} diff --git a/nutanix/services/v2/iamv2/resource_nutanix_saml_idp_v2.go b/nutanix/services/v2/iamv2/resource_nutanix_saml_idp_v2.go new file mode 100644 index 000000000..df2fd863e --- /dev/null +++ b/nutanix/services/v2/iamv2/resource_nutanix_saml_idp_v2.go @@ -0,0 +1,382 @@ +package iamv2 + +import ( + "context" + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authn" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func ResourceNutanixSamlIdpV2() *schema.Resource { + return &schema.Resource{ + CreateContext: ResourceNutanixSamlIdpV2Create, + ReadContext: ResourceNutanixSamlIdpV2Read, + UpdateContext: ResourceNutanixSamlIdpV2Update, + DeleteContext: ResourceNutanixSamlIdpV2Delete, + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "idp_metadata_url": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "idp_metadata": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "entity_id": { + Type: schema.TypeString, + Required: true, + }, + "login_url": { + Type: schema.TypeString, + Required: true, + }, + "logout_url": { + Type: schema.TypeString, + Optional: true, + }, + "error_url": { + Type: schema.TypeString, + Optional: true, + }, + "certificate": { + Type: schema.TypeString, + Required: true, + }, + "name_id_policy_format": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"emailAddress", "encrypted", "unspecified", "transient", + "WindowsDomainQualifiedName", "X509SubjectName", "kerberos", "persistent", "entity"}, false), + }, + }, + }, + }, + "idp_metadata_xml": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "username_attribute": { + Type: schema.TypeString, + Optional: true, + }, + "email_attribute": { + Type: schema.TypeString, + Optional: true, + }, + "groups_attribute": { + Type: schema.TypeString, + Optional: true, + }, + "groups_delim": { + Type: schema.TypeString, + Optional: true, + }, + "custom_attributes": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "entity_issuer": { + Type: schema.TypeString, + Optional: true, + }, + "is_signed_authn_req_enabled": { + Type: schema.TypeBool, + Optional: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func ResourceNutanixSamlIdpV2Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + input := &import1.SamlIdentityProvider{} + if idpMetadataurl, ok := d.GetOk("idp_metadata_url"); ok { + input.IdpMetadataUrl = utils.StringPtr(idpMetadataurl.(string)) + } + if idpMetadata, ok := d.GetOk("idp_metadata"); ok { + log.Printf("idp metadata: %v", idpMetadata) + input.IdpMetadata = expandIdpMetadata(idpMetadata) + } + if idpMetaXml, ok := d.GetOk("idp_metadata_xml"); ok { + input.IdpMetadataXml = utils.StringPtr(idpMetaXml.(string)) + } + if name, ok := d.GetOk("name"); ok { + input.Name = utils.StringPtr(name.(string)) + } + if uName, ok := d.GetOk("username_attribute"); ok { + input.UsernameAttribute = utils.StringPtr(uName.(string)) + } + if emailAttr, ok := d.GetOk("email_attribute"); ok { + input.EmailAttribute = utils.StringPtr(emailAttr.(string)) + } + if grpAttr, ok := d.GetOk("groups_attribute"); ok { + input.GroupsAttribute = utils.StringPtr(grpAttr.(string)) + } + if grpDelim, ok := d.GetOk("groups_delim"); ok { + input.GroupsDelim = utils.StringPtr(grpDelim.(string)) + } + if customAttributes, ok := d.GetOk("custom_attributes"); ok { + customAttributesList := customAttributes.([]interface{}) + customAttributesListStr := make([]string, len(customAttributesList)) + for i, v := range customAttributesList { + customAttributesListStr[i] = v.(string) + } + input.CustomAttributes = customAttributesListStr + } + if entity, ok := d.GetOk("entity_issuer"); ok { + input.EntityIssuer = utils.StringPtr(entity.(string)) + } + if isSigned, ok := d.GetOk("is_signed_authn_req_enabled"); ok { + input.IsSignedAuthnReqEnabled = utils.BoolPtr(isSigned.(bool)) + } + + resp, err := conn.SamlIdentityAPIInstance.CreateSamlIdentityProvider(input) + + if err != nil { + return diag.Errorf("error while creating saml identity providers: %v", err) + } + + getResp := resp.Data.GetValue().(import1.SamlIdentityProvider) + + d.SetId(*getResp.ExtId) + return ResourceNutanixSamlIdpV2Read(ctx, d, meta) +} + +func ResourceNutanixSamlIdpV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + resp, err := conn.SamlIdentityAPIInstance.GetSamlIdentityProviderById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while fetching saml identity providers: %v", err) + } + + getResp := resp.Data.GetValue().(import1.SamlIdentityProvider) + + if err := d.Set("name", getResp.Name); err != nil { + return diag.FromErr(err) + } + if err := d.Set("idp_metadata_url", getResp.IdpMetadataUrl); err != nil { + return diag.FromErr(err) + } + //if err := d.Set("idp_metadata_xml", getResp.IdpMetadataXml); err != nil { + // return diag.FromErr(err) + //} + if err := d.Set("idp_metadata", flattenIdpMetadata(getResp.IdpMetadata)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("username_attribute", getResp.UsernameAttribute); err != nil { + return diag.FromErr(err) + } + if err := d.Set("email_attribute", getResp.EmailAttribute); err != nil { + return diag.FromErr(err) + } + if err := d.Set("groups_attribute", getResp.GroupsAttribute); err != nil { + return diag.FromErr(err) + } + if err := d.Set("groups_delim", getResp.GroupsDelim); err != nil { + return diag.FromErr(err) + } + if err := d.Set("custom_attributes", getResp.CustomAttributes); err != nil { + return diag.FromErr(err) + } + if err := d.Set("entity_issuer", getResp.EntityIssuer); err != nil { + return diag.FromErr(err) + } + if err := d.Set("is_signed_authn_req_enabled", getResp.IsSignedAuthnReqEnabled); err != nil { + return diag.FromErr(err) + } + if getResp.CreatedTime != nil { + t := getResp.CreatedTime + if err := d.Set("created_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if getResp.LastUpdatedTime != nil { + t := getResp.LastUpdatedTime + if err := d.Set("last_updated_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if err := d.Set("created_by", getResp.CreatedBy); err != nil { + return diag.FromErr(err) + } + return nil +} + +func ResourceNutanixSamlIdpV2Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + updatedInput := import1.SamlIdentityProvider{} + resp, err := conn.SamlIdentityAPIInstance.GetSamlIdentityProviderById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while fetching saml identity providers: %v", err) + } + + // get etag value from read response to pass in update request If-Match header, Required for update request + etagValue := conn.SamlIdentityAPIInstance.ApiClient.GetEtag(resp) + headers := make(map[string]interface{}) + headers["If-Match"] = etagValue + + updatedInput = resp.Data.GetValue().(import1.SamlIdentityProvider) + + if d.HasChange("name") { + updatedInput.Name = utils.StringPtr(d.Get("name").(string)) + } + if d.HasChange("idp_metadata_url") { + updatedInput.IdpMetadataUrl = utils.StringPtr(d.Get("idp_metadata_url").(string)) + } + if d.HasChange("idp_metadata_xml") { + updatedInput.IdpMetadataXml = utils.StringPtr(d.Get("idp_metadata_xml").(string)) + } + if d.HasChange("idp_metadata") { + updatedInput.IdpMetadata = expandIdpMetadata(d.Get("idp_metadata")) + } + if d.HasChange("username_attribute") { + updatedInput.UsernameAttribute = utils.StringPtr(d.Get("username_attribute").(string)) + } + if d.HasChange("email_attribute") { + updatedInput.EmailAttribute = utils.StringPtr(d.Get("email_attribute").(string)) + } + if d.HasChange("groups_attribute") { + updatedInput.GroupsAttribute = utils.StringPtr(d.Get("groups_attribute").(string)) + } + if d.HasChange("groups_delim") { + updatedInput.GroupsDelim = utils.StringPtr(d.Get("groups_delim").(string)) + } + if d.HasChange("custom_attributes") { + customAttributes := d.Get("custom_attributes") + customAttributesList := customAttributes.([]interface{}) + customAttributesListStr := make([]string, len(customAttributesList)) + for i, v := range customAttributesList { + customAttributesListStr[i] = v.(string) + } + updatedInput.CustomAttributes = customAttributesListStr + } + if d.HasChange("entity_issuer") { + updatedInput.EntityIssuer = utils.StringPtr(d.Get("entity_issuer").(string)) + } + if d.HasChange("is_signed_authn_req_enabled") { + updatedInput.IsSignedAuthnReqEnabled = utils.BoolPtr(d.Get("is_signed_authn_req_enabled").(bool)) + } + + updateResp, err := conn.SamlIdentityAPIInstance.UpdateSamlIdentityProviderById(utils.StringPtr(d.Id()), &updatedInput, headers) + if err != nil { + return diag.Errorf("error while updating saml identity providers: %v", err) + } + + updateTaskResp := updateResp.Data.GetValue().(import1.SamlIdentityProvider) + + if updateTaskResp.ExtId != nil { + fmt.Println("Saml Identity provider updated successfully") + } + return nil +} + +func ResourceNutanixSamlIdpV2Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + readResp, err := conn.SamlIdentityAPIInstance.GetSamlIdentityProviderById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while fetching saml identity providers: %v", err) + } + // get etag value from read response to pass in update request If-Match header, Required for update request + etagValue := conn.SamlIdentityAPIInstance.ApiClient.GetEtag(readResp) + headers := make(map[string]interface{}) + headers["If-Match"] = etagValue + + resp, err := conn.SamlIdentityAPIInstance.DeleteSamlIdentityProviderById(utils.StringPtr(d.Id()), headers) + if err != nil { + return diag.Errorf("error while deleting saml idp : %v", err) + } + + if resp == nil { + fmt.Println("Saml IDP deleted successfully.") + } + return nil +} + +func expandIdpMetadata(pr interface{}) *import1.IdpMetadata { + if pr != nil { + prI := pr.([]interface{}) + val := prI[0].(map[string]interface{}) + idp := &import1.IdpMetadata{} + + if entityID, ok := val["entity_id"]; ok { + idp.EntityId = utils.StringPtr(entityID.(string)) + } + if loginUrl, ok := val["login_url"]; ok { + idp.LoginUrl = utils.StringPtr(loginUrl.(string)) + } + if logoutUrl, ok := val["logout_url"]; ok { + idp.LogoutUrl = utils.StringPtr(logoutUrl.(string)) + } + if errorUrl, ok := val["error_url"]; ok { + log.Printf("error url: %v", errorUrl) + if errorUrl != "" { + log.Printf("idp error url: %v", idp.ErrorUrl) + idp.ErrorUrl = utils.StringPtr(errorUrl.(string)) + } else { + idp.ErrorUrl = nil + } + + } + if certi, ok := val["certificate"]; ok { + idp.Certificate = utils.StringPtr(certi.(string)) + } + if policyFormat, ok := val["name_id_policy_format"]; ok { + subMap := map[string]interface{}{ + "emailAddress": 2, + "unspecified": 3, + "X509SubjectName": 4, + "WindowsDomainQualifiedName": 5, + "encrypted": 6, + "entity": 7, + "kerberos": 8, + "persistent": 9, + "transient": 10, + } + pInt := subMap[policyFormat.(string)] + p := import1.NameIdPolicyFormat(pInt.(int)) + idp.NameIdPolicyFormat = &p + } + log.Printf("idp: %v", idp) + return idp + } + return nil +} diff --git a/nutanix/services/v2/iamv2/resource_nutanix_saml_idp_v2_test.go b/nutanix/services/v2/iamv2/resource_nutanix_saml_idp_v2_test.go new file mode 100644 index 000000000..1313b79b5 --- /dev/null +++ b/nutanix/services/v2/iamv2/resource_nutanix_saml_idp_v2_test.go @@ -0,0 +1,137 @@ +package iamv2_test + +import ( + "fmt" + "os" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const resourceNameIdentityProviders = "nutanix_saml_identity_providers_v2.test" + +func TestAccNutanixIdentityProvidersV2Resource_CreateSamlIdp(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testIdentityProvidersResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + // resource.TestCheckResourceAttrSet(resourceNameIdentityProviders, "ext_id"), + resource.TestCheckResourceAttrSet(resourceNameIdentityProviders, "idp_metadata.#"), + resource.TestCheckResourceAttr(resourceNameIdentityProviders, "name", testVars.Iam.IdentityProviders.Name), + resource.TestCheckResourceAttr(resourceNameIdentityProviders, "username_attribute", testVars.Iam.IdentityProviders.UsernameAttribute), + resource.TestCheckResourceAttr(resourceNameIdentityProviders, "email_attribute", testVars.Iam.IdentityProviders.EmailAttribute), + resource.TestCheckResourceAttr(resourceNameIdentityProviders, "groups_attribute", testVars.Iam.IdentityProviders.GroupsAttribute), + resource.TestCheckResourceAttr(resourceNameIdentityProviders, "groups_delim", testVars.Iam.IdentityProviders.GroupsDelim), + resource.TestCheckResourceAttrSet(resourceNameIdentityProviders, "custom_attributes.#"), + resource.TestCheckResourceAttr(resourceNameIdentityProviders, "custom_attributes.0", testVars.Iam.IdentityProviders.CustomAttributes[0]), + resource.TestCheckResourceAttr(resourceNameIdentityProviders, "custom_attributes.1", testVars.Iam.IdentityProviders.CustomAttributes[1]), + ), + }, + }, + }) +} + +func TestAccNutanixIdentityProvidersV2ResourceWithNoName(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testIdentityProvidersResourceWithoutName(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} +func TestAccNutanixIdentityProvidersV2ResourceWithNoEntityId(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testIdentityProvidersResourceWithoutEntityId(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} + +func testIdentityProvidersResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + identity_providers = local.config.iam.identity_providers + } + + resource "nutanix_saml_identity_providers_v2" "test" { + name = local.identity_providers.name + username_attribute = local.identity_providers.username_attr + email_attribute = local.identity_providers.email_attr + groups_attribute = local.identity_providers.groups_attr + groups_delim = local.identity_providers.groups_delim + idp_metadata_xml = local.identity_providers.idp_metadata_xml + entity_issuer = local.identity_providers.entity_issuer + is_signed_authn_req_enabled = local.identity_providers.is_signed_authn_req_enabled + custom_attributes = local.identity_providers.custom_attributes + }`, filepath) +} + +func testIdentityProvidersResourceWithoutName(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + identity_providers = local.config.iam.identity_providers + } + + resource "nutanix_saml_identity_providers_v2" "test" { + idp_metadata { + entity_id = local.identity_providers.idp_metadata.entity_id + login_url = local.identity_providers.idp_metadata.login_url + logout_url = local.identity_providers.idp_metadata.logout_url + certificate = local.identity_providers.idp_metadata.certificate + name_id_policy_format = local.identity_providers.idp_metadata.name_id_policy_format + } + username_attribute = local.identity_providers.username_attr + email_attribute = local.identity_providers.email_attr + entity_issuer = local.identity_providers.entity_issuer + is_signed_authn_req_enabled = local.identity_providers.is_signed_authn_req_enabled + }`, filepath) +} + +func testIdentityProvidersResourceWithoutEntityId(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + identity_providers = local.config.iam.identity_providers + } + + resource "nutanix_saml_identity_providers_v2" "test" { + idp_metadata { + login_url = local.identity_providers.idp_metadata.login_url + logout_url = local.identity_providers.idp_metadata.logout_url + certificate = local.identity_providers.idp_metadata.certificate + name_id_policy_format = local.identity_providers.idp_metadata.name_id_policy_format + } + name = local.identity_providers.name + username_attribute = local.identity_providers.username_attr + email_attribute = local.identity_providers.email_attr + entity_issuer = local.identity_providers.entity_issuer + is_signed_authn_req_enabled = local.identity_providers.is_signed_authn_req_enabled + }`, filepath) +} diff --git a/nutanix/services/v2/iamv2/resource_nutanix_user_groups_v2.go b/nutanix/services/v2/iamv2/resource_nutanix_user_groups_v2.go new file mode 100644 index 000000000..b962f96dd --- /dev/null +++ b/nutanix/services/v2/iamv2/resource_nutanix_user_groups_v2.go @@ -0,0 +1,177 @@ +package iamv2 + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authn" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func ResourceNutanixUserGroupsV2() *schema.Resource { + return &schema.Resource{ + CreateContext: ResourceNutanixUserGroupsV4Create, + ReadContext: ResourceNutanixUserGroupsV4Read, + UpdateContext: ResourceNutanixUserGroupsV4Update, + DeleteContext: ResourceNutanixUserGroupsV4Delete, + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "group_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"SAML", "LDAP"}, false), + }, + "idp_id": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + "distinguished_name": { + Type: schema.TypeString, + Optional: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func ResourceNutanixUserGroupsV4Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + input := &import1.UserGroup{} + + if gType, ok := d.GetOk("group_type"); ok { + subMap := map[string]interface{}{ + "SAML": 2, + "LDAP": 3, + } + pInt := subMap[gType.(string)] + p := import1.GroupType(pInt.(int)) + input.GroupType = &p + } + + if idp, ok := d.GetOk("idp_id"); ok { + input.IdpId = utils.StringPtr(idp.(string)) + } + if name, ok := d.GetOk("name"); ok { + input.Name = utils.StringPtr(name.(string)) + } + if dName, ok := d.GetOk("distinguished_name"); ok { + input.DistinguishedName = utils.StringPtr(dName.(string)) + } + + resp, err := conn.UserGroupsAPIInstance.CreateUserGroup(input) + if err != nil { + var errordata map[string]interface{} + e := json.Unmarshal([]byte(err.Error()), &errordata) + if e != nil { + return diag.FromErr(e) + } + data := errordata["data"].(map[string]interface{}) + errorList := data["error"].([]interface{}) + errorMessage := errorList[0].(map[string]interface{}) + return diag.Errorf("error while creating user groups: %v", errorMessage["message"]) + } + + getResp := resp.Data.GetValue().(import1.UserGroup) + d.SetId(*getResp.ExtId) + return ResourceNutanixUserGroupsV4Read(ctx, d, meta) +} + +func ResourceNutanixUserGroupsV4Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + resp, err := conn.UserGroupsAPIInstance.GetUserGroupById(utils.StringPtr(d.Id())) + if err != nil { + var errordata map[string]interface{} + e := json.Unmarshal([]byte(err.Error()), &errordata) + if e != nil { + return diag.FromErr(e) + } + data := errordata["data"].(map[string]interface{}) + errorList := data["error"].([]interface{}) + errorMessage := errorList[0].(map[string]interface{}) + return diag.Errorf("error while fetching user groups: %v", errorMessage["message"]) + } + + getResp := resp.Data.GetValue().(import1.UserGroup) + + if err := d.Set("group_type", flattenGroupType(getResp.GroupType)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("idp_id", getResp.IdpId); err != nil { + return diag.FromErr(err) + } + if err := d.Set("name", getResp.Name); err != nil { + return diag.FromErr(err) + } + if err := d.Set("distinguished_name", getResp.DistinguishedName); err != nil { + return diag.FromErr(err) + } + if getResp.CreatedTime != nil { + t := getResp.CreatedTime + if err := d.Set("created_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if getResp.LastUpdatedTime != nil { + t := getResp.LastUpdatedTime + if err := d.Set("last_updated_time", t.String()); err != nil { + return diag.FromErr(err) + } + } + if err := d.Set("created_by", getResp.CreatedBy); err != nil { + return diag.FromErr(err) + } + return nil +} + +func ResourceNutanixUserGroupsV4Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return nil +} + +func ResourceNutanixUserGroupsV4Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + readResp, err := conn.UserGroupsAPIInstance.GetUserGroupById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while fetching role: %v", err) + } + + etagValue := conn.UserGroupsAPIInstance.ApiClient.GetEtag(readResp) + headers := make(map[string]interface{}) + headers["If-Match"] = etagValue + + resp, err := conn.UserGroupsAPIInstance.DeleteUserGroupById(utils.StringPtr(d.Id()), headers) + if err != nil { + return diag.Errorf("error while deleting user group : %v", err) + } + + if resp == nil { + fmt.Println("User group deleted successfully.") + } + return nil +} diff --git a/nutanix/services/v2/iamv2/resource_nutanix_user_groups_v2_test.go b/nutanix/services/v2/iamv2/resource_nutanix_user_groups_v2_test.go new file mode 100644 index 000000000..df97624ea --- /dev/null +++ b/nutanix/services/v2/iamv2/resource_nutanix_user_groups_v2_test.go @@ -0,0 +1,185 @@ +package iamv2_test + +import ( + "fmt" + "os" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const resourceNameUserGroups = "nutanix_user_groups_v2.test" + +func TestAccNutanixUserGroupsV2Resource_LDAPUserGroup(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testLDAPUserGroupsResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceNameUserGroups, "name", testVars.Iam.UserGroups.Name), + resource.TestCheckResourceAttr(resourceNameUserGroups, "idp_id", testVars.Iam.UserGroups.DirectoryServiceId), + resource.TestCheckResourceAttr(resourceNameUserGroups, "group_type", "LDAP"), + resource.TestCheckResourceAttr(resourceNameUserGroups, "distinguished_name", testVars.Iam.UserGroups.DistinguishedName), + ), + }, + { + Config: testLDAPUserGroupsResourceAlreadyExistsConfig(filepath), + ExpectError: regexp.MustCompile("Failed to create the user group due to an already existing user group."), + }, + }, + }) +} + +func TestAccNutanixUserGroupsV2Resource_SAMLUserGroup(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testSAMLUserGroupsResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceNameUserGroups, "name", testVars.Iam.UserGroups.SAMLName), + resource.TestCheckResourceAttr(resourceNameUserGroups, "idp_id", testVars.Iam.UserGroups.IdpId), + resource.TestCheckResourceAttr(resourceNameUserGroups, "group_type", "SAML"), + ), + }, + { + Config: testSAMLAlreadyExistsUserGroupsResourceConfig(filepath), + ExpectError: regexp.MustCompile("Failed to create the user group due to an already existing user group."), + }, + }, + }) +} + +func TestAccNutanixUserGroupsV2Resource_WithNoGroupType(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testUserGroupsResourceWithoutGroupTypeConfig(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} + +func TestAccNutanixUserGroupsV2Resource_WithNoIdpId(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testUserGroupsResourceWithoutIdpIdConfig(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} + +func testLDAPUserGroupsResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + user_groups = local.config.iam.user_groups + } + + resource "nutanix_user_groups_v2" "test" { + group_type = "LDAP" + idp_id = local.user_groups.directory_service_id + name = local.user_groups.name + distinguished_name = local.user_groups.distinguished_name + }`, filepath) +} + +func testLDAPUserGroupsResourceAlreadyExistsConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + user_groups = local.config.iam.user_groups + } + + resource "nutanix_user_groups_v2" "test_2" { + group_type = "LDAP" + idp_id = local.user_groups.directory_service_id + name = local.user_groups.name + distinguished_name = local.user_groups.distinguished_name + }`, filepath) +} + +func testSAMLUserGroupsResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + user_groups = local.config.iam.user_groups + } + + resource "nutanix_user_groups_v2" "test" { + group_type = "SAML" + idp_id = local.user_groups.idp_id + name = local.user_groups.saml_name + }`, filepath) +} + +func testSAMLAlreadyExistsUserGroupsResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + user_groups = local.config.iam.user_groups + } + + resource "nutanix_user_groups_v2" "test_1" { + group_type = "SAML" + idp_id = local.user_groups.idp_id + name = local.user_groups.saml_name + }`, filepath) +} + +func testUserGroupsResourceWithoutGroupTypeConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + user_groups = local.config.iam.user_groups + } + + resource "nutanix_user_groups_v2" "test" { + idp_id = local.user_groups.idp_id + name = local.user_groups.name + distinguished_name = local.user_groups.distinguished_name + }`, filepath) +} + +func testUserGroupsResourceWithoutIdpIdConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + user_groups = local.config.iam.user_groups + } + + resource "nutanix_user_groups_v2" "test" { + group_type = "LDAP" + name = local.user_groups.name + distinguished_name = local.user_groups.distinguished_name + }`, filepath) +} diff --git a/nutanix/services/v2/iamv2/resource_nutanix_users_v2.go b/nutanix/services/v2/iamv2/resource_nutanix_users_v2.go new file mode 100644 index 000000000..4cafb6e0a --- /dev/null +++ b/nutanix/services/v2/iamv2/resource_nutanix_users_v2.go @@ -0,0 +1,524 @@ +package iamv2 + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/common/v1/config" + import1 "github.com/nutanix-core/ntnx-api-golang-sdk-internal/iam-go-client/v16/models/iam/v4/authn" + + conns "github.com/terraform-providers/terraform-provider-nutanix/nutanix" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func ResourceNutanixUserV2() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceNutanixUserV2Create, + ReadContext: resourceNutanixUserV2Read, + UpdateContext: resourceNutanixUserV2Update, + DeleteContext: resourceNutanixUserV2Delete, + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Computed: true, + }, + "links": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + }, + "rel": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "username": { + Type: schema.TypeString, + Required: true, + }, + "user_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"LOCAL", "SAML", "LDAP", "EXTERNAL"}, false), + }, + "idp_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "first_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "middle_initial": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "last_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "email_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "locale": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "password": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "force_reset_password": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + "additional_attributes": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "value": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + }, + }, + }, + "status": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "buckets_access_keys": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ext_id": { + Type: schema.TypeString, + Computed: true, + }, + "links": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + }, + "rel": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "access_key_name": { + Type: schema.TypeString, + Computed: true, + }, + "secret_access_key": { + Type: schema.TypeString, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "last_login_time": { + Type: schema.TypeString, + // Optional: true, + Computed: true, + }, + "created_time": { + Type: schema.TypeString, + // Optional: true, + Computed: true, + }, + "last_updated_time": { + Type: schema.TypeString, + // Optional: true, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + // Optional: true, + Computed: true, + }, + }, + } +} + +func resourceNutanixUserV2Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + spec := &import1.User{} + + if un, ok := d.GetOk("username"); ok { + spec.Username = utils.StringPtr(un.(string)) + } + if ut, ok := d.GetOk("user_type"); ok { + usertypeMap := map[string]interface{}{ + "LOCAL": 2, + "SAML": 3, + "LDAP": 4, + "EXTERNAL": 5, + } + pInt := usertypeMap[ut.(string)] + p := import1.UserType(pInt.(int)) + spec.UserType = &p + } + + if idp, ok := d.GetOk("idp_id"); ok { + spec.IdpId = utils.StringPtr(idp.(string)) + } + if displayName, ok := d.GetOk("display_name"); ok { + spec.DisplayName = utils.StringPtr(displayName.(string)) + } + if fName, ok := d.GetOk("first_name"); ok { + spec.FirstName = utils.StringPtr(fName.(string)) + } + if middle, ok := d.GetOk("middle_initial"); ok { + spec.MiddleInitial = utils.StringPtr(middle.(string)) + } + + if lName, ok := d.GetOk("last_name"); ok { + spec.LastName = utils.StringPtr(lName.(string)) + } + if email, ok := d.GetOk("email_id"); ok { + spec.EmailId = utils.StringPtr(email.(string)) + } + if locale, ok := d.GetOk("locale"); ok { + spec.Locale = utils.StringPtr(locale.(string)) + } + if region, ok := d.GetOk("region"); ok { + spec.Region = utils.StringPtr(region.(string)) + } + if pass, ok := d.GetOk("password"); ok { + spec.Password = utils.StringPtr(pass.(string)) + } + if frp, ok := d.GetOk("force_reset_password"); ok { + spec.IsForceResetPasswordEnabled = utils.BoolPtr(frp.(bool)) + } + if status, ok := d.GetOk("status"); ok { + statusMap := map[string]interface{}{ + "ACTIVE": 2, + "INACTIVE": 3, + } + pInt := statusMap[status.(string)] + p := import1.UserStatusType(pInt.(int)) + spec.Status = &p + } + if lastLogin, ok := d.GetOk("last_login_time"); ok { + t := time.Time(lastLogin.(time.Time)) + spec.LastLoginTime = &t + } + if cTime, ok := d.GetOk("created_time"); ok { + t := time.Time(cTime.(time.Time)) + spec.CreatedTime = &t + } + if lastUpdate, ok := d.GetOk("last_updated_time"); ok { + t := time.Time(lastUpdate.(time.Time)) + spec.LastUpdatedTime = &t + } + if cBy, ok := d.GetOk("created_by"); ok { + spec.CreatedBy = utils.StringPtr(cBy.(string)) + } + if addAttr, ok := d.GetOk("additional_attributes"); ok { + spec.AdditionalAttributes = expandKVPair(addAttr.([]interface{})) + } + + resp, err := conn.UsersAPIInstance.CreateUser(spec) + if err != nil { + return diag.Errorf("error while creating User : %v", err) + } + + getResp := resp.Data.GetValue().(import1.User) + + d.SetId(*getResp.ExtId) + return resourceNutanixUserV2Read(ctx, d, meta) +} + +func resourceNutanixUserV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + resp, err := conn.UsersAPIInstance.GetUserById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while fetching user : %v", err) + } + + getResp := resp.Data.GetValue().(import1.User) + + if err = d.Set("ext_id", getResp.ExtId); err != nil { + return diag.Errorf("error setting ext_id for user %s: %s", d.Id(), err) + } + if err := d.Set("links", flattenLinks(getResp.Links)); err != nil { + return diag.FromErr(err) + } + if err = d.Set("username", getResp.Username); err != nil { + return diag.Errorf("error setting username for user %s: %s", d.Id(), err) + } + if err = d.Set("user_type", flattenUserType(getResp.UserType)); err != nil { + return diag.Errorf("error setting user_type for user %s: %s", d.Id(), err) + } + if err = d.Set("idp_id", getResp.IdpId); err != nil { + return diag.Errorf("error setting idp_id for user %s: %s", d.Id(), err) + } + if err = d.Set("display_name", getResp.DisplayName); err != nil { + return diag.Errorf("error setting display_name for user %s: %s", d.Id(), err) + } + if err = d.Set("first_name", getResp.FirstName); err != nil { + return diag.Errorf("error setting first_name for user %s: %s", d.Id(), err) + } + if err = d.Set("middle_initial", getResp.MiddleInitial); err != nil { + return diag.Errorf("error setting middle_initial for user %s: %s", d.Id(), err) + } + if err = d.Set("last_name", getResp.LastName); err != nil { + return diag.Errorf("error setting last_name for user %s: %s", d.Id(), err) + } + if err = d.Set("email_id", getResp.EmailId); err != nil { + return diag.Errorf("error setting email_id for user %s: %s", d.Id(), err) + } + if err = d.Set("locale", getResp.Locale); err != nil { + return diag.Errorf("error setting username for user %s: %s", d.Id(), err) + } + if err = d.Set("region", getResp.Region); err != nil { + return diag.Errorf("error setting region for user %s: %s", d.Id(), err) + } + if err = d.Set("force_reset_password", getResp.IsForceResetPasswordEnabled); err != nil { + return diag.Errorf("error setting force_reset_password for user %s: %s", d.Id(), err) + } + if err = d.Set("additional_attributes", flattenAdditionalAttributes(getResp)); err != nil { + return diag.Errorf("error setting additional_attributes for user %s: %s", d.Id(), err) + } + if err = d.Set("status", flattenUserStatusType(getResp.Status)); err != nil { + return diag.Errorf("error setting status for user %s: %s", d.Id(), err) + } + if err = d.Set("buckets_access_keys", flattenBucketsAccessKeys(getResp)); err != nil { + return diag.Errorf("error setting buckets_access_keys for user %s: %s", d.Id(), err) + } + + if err = d.Set("last_login_time", getResp.LastLoginTime.Format("2006-01-02T15:04:05Z07:00")); err != nil { + return diag.Errorf("error setting last_login_time for user %s: %s", d.Id(), err) + } + if err = d.Set("created_time", getResp.CreatedTime.Format("2006-01-02T15:04:05Z07:00")); err != nil { + return diag.Errorf("error setting created_time for user %s: %s", d.Id(), err) + } + if err = d.Set("last_updated_time", getResp.LastUpdatedTime.Format("2006-01-02T15:04:05Z07:00")); err != nil { + return diag.Errorf("error setting last_updated_time for user %s: %s", d.Id(), err) + } + if err = d.Set("created_by", getResp.CreatedBy); err != nil { + return diag.Errorf("error setting created_by for user %s: %s", d.Id(), err) + } + return nil +} + +func resourceNutanixUserV2Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + updateSpec := &import1.User{} + + // get Resp + getResp, er := conn.UsersAPIInstance.GetUserById(utils.StringPtr(d.Id())) + if er != nil { + return diag.FromErr(er) + } + + getUserResp := getResp.Data.GetValue().(import1.User) + + updateSpec = &getUserResp + + // checking if attribute is updated or not + + if d.HasChange("user_type") { + usertypeMap := map[string]interface{}{ + "LOCAL": 2, + "SAML": 3, + "LDAP": 4, + "EXTERNAL": 5, + } + pInt := usertypeMap[d.Get("user_type").(string)] + p := import1.UserType(pInt.(int)) + updateSpec.UserType = &p + } + if d.HasChange("idp_id") { + updateSpec.IdpId = utils.StringPtr(d.Get("idp_id").(string)) + } + if d.HasChange("display_name") { + updateSpec.DisplayName = utils.StringPtr(d.Get("display_name").(string)) + } + if d.HasChange("first_name") { + updateSpec.FirstName = utils.StringPtr(d.Get("first_name").(string)) + } + if d.HasChange("middle_initial") { + updateSpec.MiddleInitial = utils.StringPtr(d.Get("middle_initial").(string)) + } + if d.HasChange("last_name") { + updateSpec.LastName = utils.StringPtr(d.Get("last_name").(string)) + } + if d.HasChange("email_id") { + updateSpec.EmailId = utils.StringPtr(d.Get("email_id").(string)) + } + if d.HasChange("locale") { + updateSpec.Locale = utils.StringPtr(d.Get("locale").(string)) + } + if d.HasChange("region") { + updateSpec.Region = utils.StringPtr(d.Get("region").(string)) + } + if d.HasChange("password") { + updateSpec.Password = utils.StringPtr(d.Get("password").(string)) + } + if d.HasChange("force_reset_password") { + updateSpec.IsForceResetPasswordEnabled = utils.BoolPtr(d.Get("force_reset_password").(bool)) + } + if d.HasChange("status") { + statusMap := map[string]interface{}{ + "ACTIVE": 2, + "INACTIVE": 3, + } + pInt := statusMap[d.Get("status").(string)] + p := import1.UserStatusType(pInt.(int)) + updateSpec.Status = &p + } + if d.HasChange("additional_attributes") { + updateSpec.AdditionalAttributes = expandKVPair(d.Get("additional_attributes").([]interface{})) + } + + // Extract E-Tag Header + etagValue := conn.APIClientInstance.GetEtag(getResp) + + args := make(map[string]interface{}) + args["If-Match"] = etagValue + + updateresp, err := conn.UsersAPIInstance.UpdateUserById(utils.StringPtr(d.Id()), updateSpec, args) + if err != nil { + return diag.FromErr(err) + } + updateResp := updateresp.Data.GetValue().(import1.User) + + if d.Id() != *updateResp.ExtId { + return diag.Errorf("ext_id is different in update user") + } + return resourceNutanixUserV2Read(ctx, d, meta) +} + +func resourceNutanixUserV2Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.Client).IamAPI + + readResp, err := conn.UsersAPIInstance.GetUserById(utils.StringPtr(d.Id())) + if err != nil { + return diag.Errorf("error while fetching user: %v", err) + } + + etagValue := conn.UserGroupsAPIInstance.ApiClient.GetEtag(readResp) + headers := make(map[string]interface{}) + headers["If-Match"] = etagValue + + resp, err := conn.UsersAPIInstance.DeleteUserById(utils.StringPtr(d.Id()), headers) + if err != nil { + return diag.Errorf("error while deleting user : %v", err) + } + + if resp == nil { + fmt.Println("User deleted successfully.") + } + return nil +} + +func expandKVPair(pr []interface{}) []config.KVPair { + if len(pr) > 0 { + kvPairs := make([]config.KVPair, len(pr)) + + for k, v := range pr { + val := v.(map[string]interface{}) + pair := &config.KVPair{} + + if name, ok := val["name"]; ok { + pair.Name = utils.StringPtr(name.(string)) + } + if value, ok := val["value"]; ok { + pair.Value = value.(*config.OneOfKVPairValue) + } + kvPairs[k] = *pair + } + return kvPairs + } + return nil +} + +func flattenUserType(pr *import1.UserType) string { + if pr != nil { + const two, three, four, five = 2, 3, 4, 5 + if *pr == import1.UserType(two) { + return "LOCAL" + } + if *pr == import1.UserType(three) { + return "SAML" + } + if *pr == import1.UserType(four) { + return "LDAP" + } + if *pr == import1.UserType(five) { + return "EXTERNAL" + } + } + return "UNKNOWN" +} + +func flattenUserStatusType(pr *import1.UserStatusType) string { + if pr != nil { + const two, three = 2, 3 + + if *pr == import1.UserStatusType(two) { + return "ACTIVE" + } + if *pr == import1.UserStatusType(three) { + return "INACTIVE" + } + } + return "UNKNOWN" +} diff --git a/nutanix/services/v2/iamv2/resource_nutanix_users_v2_test.go b/nutanix/services/v2/iamv2/resource_nutanix_users_v2_test.go new file mode 100644 index 000000000..fe4933cdd --- /dev/null +++ b/nutanix/services/v2/iamv2/resource_nutanix_users_v2_test.go @@ -0,0 +1,423 @@ +package iamv2_test + +import ( + "fmt" + "os" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + acc "github.com/terraform-providers/terraform-provider-nutanix/nutanix/acctest" +) + +const resourceNameUsers = "nutanix_users_v2.test" + +// create local Active user, and test update the username and display name +func TestAccNutanixUsersV4Resource_LocalActiveUser(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testLocalActiveUserResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameUsers, "ext_id"), + resource.TestCheckResourceAttr(resourceNameUsers, "username", testVars.Iam.Users.Username), + resource.TestCheckResourceAttr(resourceNameUsers, "display_name", testVars.Iam.Users.DisplayName), + resource.TestCheckResourceAttr(resourceNameUsers, "user_type", "LOCAL"), + resource.TestCheckResourceAttr(resourceNameUsers, "first_name", testVars.Iam.Users.FirstName), + resource.TestCheckResourceAttr(resourceNameUsers, "middle_initial", testVars.Iam.Users.MiddleInitial), + resource.TestCheckResourceAttr(resourceNameUsers, "last_name", testVars.Iam.Users.LastName), + resource.TestCheckResourceAttr(resourceNameUsers, "email_id", testVars.Iam.Users.EmailId), + resource.TestCheckResourceAttr(resourceNameUsers, "status", "ACTIVE"), + ), + }, + // test update + { + Config: testLocalActiveUserResourceUpdateConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameUsers, "ext_id"), + resource.TestCheckResourceAttr(resourceNameUsers, "username", testVars.Iam.Users.Username), + resource.TestCheckResourceAttr(resourceNameUsers, "display_name", fmt.Sprintf("%s_updated", testVars.Iam.Users.DisplayName)), + resource.TestCheckResourceAttr(resourceNameUsers, "user_type", "LOCAL"), + resource.TestCheckResourceAttr(resourceNameUsers, "first_name", fmt.Sprintf("%s_updated", testVars.Iam.Users.FirstName)), + resource.TestCheckResourceAttr(resourceNameUsers, "middle_initial", fmt.Sprintf("%s_updated", testVars.Iam.Users.MiddleInitial)), + resource.TestCheckResourceAttr(resourceNameUsers, "last_name", fmt.Sprintf("%s_updated", testVars.Iam.Users.LastName)), + resource.TestCheckResourceAttr(resourceNameUsers, "email_id", fmt.Sprintf("updated_%s", testVars.Iam.Users.EmailId)), + ), + }, + }, + }) +} + +// test duplicate user creation +func TestAccNutanixUsersV4Resource_AlreadyExistsUser(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testLocalActiveUserResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameUsers, "ext_id"), + resource.TestCheckResourceAttr(resourceNameUsers, "username", testVars.Iam.Users.Username), + resource.TestCheckResourceAttr(resourceNameUsers, "display_name", testVars.Iam.Users.DisplayName), + resource.TestCheckResourceAttr(resourceNameUsers, "user_type", "LOCAL"), + resource.TestCheckResourceAttr(resourceNameUsers, "first_name", testVars.Iam.Users.FirstName), + resource.TestCheckResourceAttr(resourceNameUsers, "last_name", testVars.Iam.Users.LastName), + ), + }, + { + Config: testLocalUserAlreadyExistsResourceConfig(filepath), + ExpectError: regexp.MustCompile("already existing User with given username"), + }, + }, + }) +} + +// create local Inactive user +func TestAccNutanixUsersV4Resource_LocalInactiveUser(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testLocalInactiveUserResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameUsers, "ext_id"), + resource.TestCheckResourceAttr(resourceNameUsers, "username", testVars.Iam.Users.Username), + resource.TestCheckResourceAttr(resourceNameUsers, "display_name", testVars.Iam.Users.DisplayName), + resource.TestCheckResourceAttr(resourceNameUsers, "user_type", "LOCAL"), + resource.TestCheckResourceAttr(resourceNameUsers, "first_name", testVars.Iam.Users.FirstName), + resource.TestCheckResourceAttr(resourceNameUsers, "last_name", testVars.Iam.Users.LastName), + resource.TestCheckResourceAttr(resourceNameUsers, "status", "INACTIVE"), + ), + }, + }, + }) +} + +// create SAML user +func TestAccNutanixUsersV4Resource_SAMLUser(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testSAMLUserResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameUsers, "ext_id"), + resource.TestCheckResourceAttr(resourceNameUsers, "username", testVars.Iam.Users.Username), + resource.TestCheckResourceAttr(resourceNameUsers, "user_type", "SAML"), + resource.TestCheckResourceAttr(resourceNameUsers, "idp_id", testVars.Iam.Users.IdpId), + ), + }, + }, + }) +} + +// create LDAP user +func TestAccNutanixUsersV4Resource_LDAPUser(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testLDAPUserWithMinimalConfigResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameUsers, "ext_id"), + resource.TestCheckResourceAttr(resourceNameUsers, "username", testVars.Iam.Users.DirectoryServiceUsername), + resource.TestCheckResourceAttr(resourceNameUsers, "user_type", "LDAP"), + resource.TestCheckResourceAttr(resourceNameUsers, "idp_id", testVars.Iam.Users.DirectoryServiceId), + ), + }, + }, + }) +} + +// create local Active user, and test update the username and display name +func TestAccNutanixUsersV4Resource_DeactivateLocalUser(t *testing.T) { + t.Skip("these test were commented since they are using different APIs") + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccFoundationPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testLocalActiveUserResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameUsers, "ext_id"), + resource.TestCheckResourceAttr(resourceNameUsers, "username", testVars.Iam.Users.Username), + resource.TestCheckResourceAttr(resourceNameUsers, "display_name", testVars.Iam.Users.DisplayName), + resource.TestCheckResourceAttr(resourceNameUsers, "user_type", "LOCAL"), + resource.TestCheckResourceAttr(resourceNameUsers, "first_name", testVars.Iam.Users.FirstName), + resource.TestCheckResourceAttr(resourceNameUsers, "last_name", testVars.Iam.Users.LastName), + resource.TestCheckResourceAttr(resourceNameUsers, "status", "ACTIVE"), + ), + }, + // test Deactivate User + { + Config: testDeactivateLocalUserResourceConfig(filepath), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceNameUsers, "ext_id"), + resource.TestCheckResourceAttr(resourceNameUsers, "username", testVars.Iam.Users.Username), + resource.TestCheckResourceAttr(resourceNameUsers, "display_name", testVars.Iam.Users.DisplayName), + resource.TestCheckResourceAttr(resourceNameUsers, "user_type", "LOCAL"), + resource.TestCheckResourceAttr(resourceNameUsers, "first_name", testVars.Iam.Users.FirstName), + resource.TestCheckResourceAttr(resourceNameUsers, "last_name", testVars.Iam.Users.LastName), + resource.TestCheckResourceAttr(resourceNameUsers, "status", "INACTIVE"), + ), + }, + }, + }) +} + +// Test missing username +func TestAccNutanixUsersV4Resource_WithNoUserName(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testUsersResourceWithoutUserNameConfig(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} + +// Test missing user type +func TestAccNutanixUsersV4Resource_WithNoUserType(t *testing.T) { + path, _ := os.Getwd() + filepath := path + "/../../../../test_config_v2.json" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testUsersResourceWithoutUserTypeConfig(filepath), + ExpectError: regexp.MustCompile("Missing required argument"), + }, + }, + }) +} + +func testLocalActiveUserResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test" { + username = local.users.username + first_name = local.users.first_name + middle_initial = local.users.middle_initial + last_name = local.users.last_name + email_id = local.users.email_id + locale = local.users.locale + region = local.users.region + display_name = local.users.display_name + password = local.users.password + user_type = "LOCAL" + status = "ACTIVE" + force_reset_password = local.users.force_reset_password + }`, filepath) +} + +func testLocalActiveUserResourceUpdateConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test" { + username = local.users.username + first_name = "${local.users.first_name}_updated" + middle_initial = "${local.users.middle_initial}_updated" + last_name = "${local.users.last_name}_updated" + email_id = "updated_${local.users.email_id}" + locale = local.users.locale + region = local.users.region + display_name = "${local.users.display_name}_updated" + password = "${local.users.password}_updated" + user_type = "LOCAL" + status = "ACTIVE" + force_reset_password = local.users.force_reset_password + + }`, filepath) +} + +func testLocalUserAlreadyExistsResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test2" { + username = local.users.username + first_name = local.users.first_name + middle_initial = local.users.middle_initial + last_name = local.users.last_name + email_id = local.users.email_id + locale = local.users.locale + region = local.users.region + display_name = local.users.display_name + password = local.users.password + user_type = "LOCAL" + status = "ACTIVE" + force_reset_password = local.users.force_reset_password + } + + `, filepath) +} + +func testLocalInactiveUserResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test" { + username = local.users.username + first_name = local.users.first_name + middle_initial = local.users.middle_initial + last_name = local.users.last_name + email_id = local.users.email_id + locale = local.users.locale + region = local.users.region + display_name = local.users.display_name + password = local.users.password + user_type = "LOCAL" + status = "INACTIVE" + force_reset_password = local.users.force_reset_password + + }`, filepath) +} + +func testSAMLUserResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test" { + username = local.users.username + user_type = "SAML" + idp_id = local.users.idp_id + }`, filepath) +} + +func testLDAPUserWithMinimalConfigResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test" { + username = local.users.directory_service_username + user_type = "LDAP" + idp_id = local.users.directory_service_id + + }`, filepath) +} + +func testDeactivateLocalUserResourceConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test" { + username = local.users.username + user_type = "LOCAL" + idp_id = local.users.idp_id + display_name = local.users.display_name + locale = local.users.locale + region = local.users.region + password = local.users.password + force_reset_password = local.users.force_reset_password + status = INACTIVE + }`, filepath) +} + +func testUsersResourceWithoutUserNameConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test" { + first_name = local.users.first_name + middle_initial = local.users.middle_initial + last_name = local.users.last_name + email_id = local.users.email_id + locale = local.users.locale + region = local.users.region + display_name = local.users.display_name + password = local.users.password + user_type = "LOCAL" + status = "ACTIVE" + force_reset_password = local.users.force_reset_password + + }`, filepath) +} + +func testUsersResourceWithoutUserTypeConfig(filepath string) string { + return fmt.Sprintf(` + + locals{ + config = (jsondecode(file("%s"))) + users = local.config.iam.users + } + + resource "nutanix_users_v2" "test" { + username = local.users.username + first_name = local.users.first_name + middle_initial = local.users.middle_initial + last_name = local.users.last_name + email_id = local.users.email_id + locale = local.users.locale + region = local.users.region + display_name = local.users.display_name + password = local.users.password + status = "ACTIVE" + force_reset_password = local.users.force_reset_password + + }`, filepath) +} diff --git a/test_config_v2.json b/test_config_v2.json index c1d667764..c42f11622 100644 --- a/test_config_v2.json +++ b/test_config_v2.json @@ -3,5 +3,90 @@ "floating_ip": { "vm_nic_reference": "ef53e6e8-2eaa-44db-b44a-2e934964d792" } + }, + "iam":{ + "roles":{ + "limit":3, + "display_name":"test_role", + "description":"Test Role Description" + }, + "users":{ + "limit":1, + "username":"terraform_test_username", + "idp_id":"", + "directory_service_id":"", + "directory_service_username":"", + "display_name":"terraform_test_displayname", + "first_name":"terraform_test_firstname", + "middle_initial":"terraform_test_middle_initial", + "last_name":"terraform_test_middleinitial", + "email_id":"terraform_test_2@email.com", + "locale":"en-US", + "region":"en-US", + "password":"test.Password.123", + "force_reset_password":true + }, + "user_groups":{ + "limit":1, + "name":"test_custom_{_group_}", + "saml_name":"adfs19admingroup", + "idp_id":"", + "directory_service_id":"", + "distinguished_name":"" + }, + "auth_policies":{ + "limit":1, + "display_name":"auth_policies_test", + "description":"auth policies description ", + "authorization_policy_type":"USER_DEFINED", + "identities": [ "{\"user\":{\"uuid\":{\"anyof\":[\"00000000-0000-0000-0000-000000000000\"]}}}"], + "entities": [ "{\"images\":{\"*\":{\"eq\":\"*\"}}}", "{\"marketplace_item\":{\"owner_uuid\":{\"eq\":\"SELF_OWNED\"}}}"] + }, + "identity_providers":{ + "limit":1, + "idp_metadata_url":"", + "idp_metadata_xml":"", + "idp_metadata":{ + "entity_id":"", + "login_url":"", + "logout_url":"", + "error_url":"", + "certificate":"", + "name_id_policy_format":"emailAddress" + }, + "name":"terraform_test_idp_name", + "username_attr":"terrafrorm_test_idp_username", + "email_attr":"email", + "groups_attr":"groups", + "groups_delim":",", + "entity_issuer":"entity_issuer_test", + "is_signed_authn_req_enabled": true, + "custom_attributes" : ["custom1","custom2"] + }, + "directory_services":{ + "limit":1, + "name":"terraform_test_active_directory", + "url":"", + "domain_name":"", + "service_account":{ + "username":"", + "password":"" + }, + "open_ldap_configuration":{ + "user_configuration":{ + "user_object_class":"inetOrgPerson", + "user_search_base":"", + "username_attribute":"uid" + }, + "user_group_configuration":{ + "group_object_class":"groupOfNames", + "group_search_base":"", + "group_member_attribute":"member", + "group_member_attribute_value":"uid" + } + }, + "group_search_type":"NON_RECURSIVE", + "white_listed_groups":["test","test_updated"] + } } -} +} \ No newline at end of file diff --git a/website/docs/d/auth_policies_v2.html.markdown b/website/docs/d/auth_policies_v2.html.markdown new file mode 100644 index 000000000..448c3963e --- /dev/null +++ b/website/docs/d/auth_policies_v2.html.markdown @@ -0,0 +1,64 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_authorization_policies_v2" +sidebar_current: "docs-nutanix-datasource-authorization-policies-v4" +description: |- + This operation retrieves the list of existing Authorization Policies. +--- + +# nutanix_authorization_policies_v2 + +Provides a datasource to retrieve the list of existing Authorization Policies. + +## Example Usage + +```hcl + data "nutanix_authorization_policies_v2" "test"{ } + +``` + +## Argument Reference + +The following arguments are supported: + +* `page`: (Optional) A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results. +* `limit`: (Optional) A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set. +* `filter`: (Optional) A URL query parameter that allows clients to filter a collection of resources. +* `order_by`: (Optional) A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default. +* `select`: A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the $select must conform to the OData V4.01 URL conventions. + +* `auth_policies`: List of all existing Authorization Policies. + +## Attribute Reference + +The following attributes are exported: + +* `ext_id`: ext_id of Authorization policy. +* `links`: A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `display_name`: Name of the Authorization Policy. +* `description`: Description of the Authorization Policy. +* `client_name`: Client that created the entity. +* `identities`: The identities for which the Authorization Policy is created. +* `entities`: The entities being qualified by the Authorization Policy. +* `role`: The Role associated with the Authorization Policy. +* `created_time`: The creation time of the Authorization Policy. +* `last_updated_time`: The time when the Authorization Policy was last updated. +* `created_by`: User or Service Name that created the Authorization Policy. +* `is_system_defined`: Flag identifying if the Authorization Policy is system defined or not. +* `authorization_policy_type`: Type of Authorization Policy. + * `PREDEFINED_READ_ONLY` : System-defined read-only ACP, i.e. no modifications allowed. + * `SERVICE_DEFINED_READ_ONLY` : Read-only ACP defined by a service. + * `PREDEFINED_UPDATE_IDENTITY_ONLY` : System-defined ACP prohibiting any modifications from customer. + * `SERVICE_DEFINED` : ACP defined by a service. + * `USER_DEFINED` : ACP defined by an User. + + +### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object + + +See detailed information in [Nutanix Authorization Policies](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/d/auth_policy_v2.html.markdown b/website/docs/d/auth_policy_v2.html.markdown new file mode 100644 index 000000000..7737a32cf --- /dev/null +++ b/website/docs/d/auth_policy_v2.html.markdown @@ -0,0 +1,58 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_authorization_policy_v2" +sidebar_current: "docs-nutanix-datasource-authorization-policy-v4" +description: |- + Provides a datasource to retrieve authorization policy with authorization policy uuid . +--- +# nutanix_authorization_policy_v2 + +Provides a datasource to retrieve authorization policy with authorization policy uuid . + +## Example Usage + +```hcl + data "nutanix_authorization_policy_v2" "test"{ + ext_id = {{ acp uuid }} + } + +``` + +## Argument Reference + +The following arguments are supported: + +* `ext_id`: (Required) Authorization Policy UUID + +## Attribute Reference + +The following attributes are exported: +* `ext_id`: ext_id of Authorization policy. +* `links`: A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `display_name`: Name of the Authorization Policy. +* `description`: Description of the Authorization Policy. +* `client_name`: Client that created the entity. +* `identities`: The identities for which the Authorization Policy is created. +* `entities`: The entities being qualified by the Authorization Policy. +* `role`: The Role associated with the Authorization Policy. +* `created_time`: The creation time of the Authorization Policy. +* `last_updated_time`: The time when the Authorization Policy was last updated. +* `created_by`: User or Service Name that created the Authorization Policy. +* `is_system_defined`: Flag identifying if the Authorization Policy is system defined or not. +* `authorization_policy_type`: Type of Authorization Policy. + * `PREDEFINED_READ_ONLY` : System-defined read-only ACP, i.e. no modifications allowed. + * `SERVICE_DEFINED_READ_ONLY` : Read-only ACP defined by a service. + * `PREDEFINED_UPDATE_IDENTITY_ONLY` : System-defined ACP prohibiting any modifications from customer. + * `SERVICE_DEFINED` : ACP defined by a service. + * `USER_DEFINED` : ACP defined by an User. + + +### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object + + +See detailed information in [Nutanix Authorization Policies](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/d/directory_service_v2.html.markdown b/website/docs/d/directory_service_v2.html.markdown new file mode 100644 index 000000000..a768d9e06 --- /dev/null +++ b/website/docs/d/directory_service_v2.html.markdown @@ -0,0 +1,77 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_directory_service_v2" +sidebar_current: "docs-nutanix-datasource-nutanix-directory-service-v4" +description: |- + This operation retrieves a Directory Service +--- + +# nutanix_pbr + +Provides a datasource to retrieve all Directory Service(s). + +## Example Usage + +``` hcl +data "nutanix_directory_service_v2" "example"{ + ext_id = "directory service ext id" +} +``` + +## Argument Reference +The following arguments are supported: +* `ext_id`: -(Required) External identifier of the Directory Service. + +## Attributes Reference +The following attributes are exported: + +* `tenant_id`: - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id`: - A globally unique identifier of an instance that is suitable for external consumption. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `name`: - Name for the Directory Service. +* `url`: - URL for the Directory Service. +* `secondary_urls`: - Secondary URL for the Directory Service. +* `domain_name`: - Domain name for the Directory Service. +* `directory_type`: - Type of Directory Service, Supported values are: "ACTIVE_DIRECTORY" (Directory Service type is Active Directory.) and "OPEN_LDAP" (Directory Service type is Open LDAP.) +* `service_account`: - Information of Service account to connect to the Directory Service. +* `open_ldap_configuration`: - Configuration for OpenLDAP Directory Service. +* `group_search_type`: - Group membership search type for the Directory Service. Supported values are: "NON_RECURSIVE" (Doesn't search recursively within groups.) and "RECURSIVE" (Searches recursively within groups.) +* `white_listed_groups`: - List of allowed User Groups for the Directory Service. +* `created_time`: - Creation time of the Directory Service. +* `last_updated_time`: - Last updated time of the Directory Service. +* `created_by`: - User or Service who created the Directory Service. + +### Service Account + +The service_account attribute supports the following: + +* `username`: - Username to connect to the Directory Service. +* `password`: - Password to connect to the Directory Service. + + +### Open Ldap Configuration + +The open_ldap_configuration attribute supports the following: + +* `user_configuration`: - this field will avoid down migration of data from the hot tier unless the overrides field is specified for the virtual disks. +* `user_group_configuration`: - this field will avoid down migration of data from the hot tier unless the overrides field is specified for the virtual disks. + +#### User Configuration + +The user_configuration attribute supports the following: + +* `user_object_class`: - Object class in the OpenLDAP system that corresponds to Users. +* `user_search_base`: - Base DN for User search. +* `username_attribute`: - Unique Identifier for each User which can be used in Authentication. + +#### User Group Configuration + +The user_group_configuration attribute supports the following: + +* `group_object_class`: - Object class in the OpenLDAP system that corresponds to groups. +* `group_search_base`: - Base DN for group search. +* `group_member_attribute`: - Attribute in a group that associates Users to the group. +* `group_member_attribute_value`: - User attribute value that will be used in group entity to associate User to the group. + + +See detailed information in [Nutanix Directory Services](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/d/directory_services_v2.html.markdown b/website/docs/d/directory_services_v2.html.markdown new file mode 100644 index 000000000..0922e28ff --- /dev/null +++ b/website/docs/d/directory_services_v2.html.markdown @@ -0,0 +1,91 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_directory_services_v2" +sidebar_current: "docs-nutanix-datasource-nutanix-directory-services-v4" +description: |- + This operation retrieves a list of all Directory Service(s). +--- + +# nutanix_pbr + +Provides a datasource to retrieve all Directory Service(s). + +## Example Usage + +``` hcl +data "nutanix_directory_services_v2" "example"{} +``` + +## Argument Reference +The following arguments are supported: + + +* `page`: -(Optional) A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results. +* `limit`: -(Optional) A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set. +* `filter`: -(Optional) A URL query parameter that allows clients to filter a collection of resources. The expression specified with $filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the $filter must conform to the OData V4.01 URL conventions. For example, filter '$filter=name eq 'karbon-ntnx-1.0' would filter the result on cluster name 'karbon-ntnx1.0', filter '$filter=startswith(name, 'C')' would filter on cluster name starting with 'C'. The filter can be applied to the following fields: createdBy, domainName, extId, name +* `order_by`: -(Optional) A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default. For example, '$orderby=templateName desc' would get all templates sorted by templateName in descending order. The orderby can be applied to the following fields: createdTime, domainName, lastUpdatedTime, name +* `select`: -(Optional) A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the $select must conform to the OData V4.01 URL conventions. If a $select expression consists of a single select item that is an asterisk (i.e., *), then all properties on the matching resource will be returned. it can be applied to the following fields: createdBy, createdTime, directoryType, domainName, extId, groupSearchType, lastUpdatedTime, links, name, openLdapConfiguration/userConfiguration, openLdapConfiguration/userGroupConfiguration, secondaryUrls, serviceAccount/password, serviceAccount/username, tenantId, url, whiteListedGroups + + + + +## Attributes Reference +The following attributes are exported: + +* `directory_services`: - list of all Directory Service(s). + + +### Directory Services +The directory_services attribute supports the following: + + +* `tenant_id`: - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id`: - A globally unique identifier of an instance that is suitable for external consumption. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `name`: - Name for the Directory Service. +* `url`: - URL for the Directory Service. +* `secondary_urls`: - Secondary URL for the Directory Service. +* `domain_name`: - Domain name for the Directory Service. +* `directory_type`: - Type of Directory Service, Supported values are: "ACTIVE_DIRECTORY" (Directory Service type is Active Directory.) and "OPEN_LDAP" (Directory Service type is Open LDAP.) +* `service_account`: - Information of Service account to connect to the Directory Service. +* `open_ldap_configuration`: - Configuration for OpenLDAP Directory Service. +* `group_search_type`: - Group membership search type for the Directory Service. Supported values are: "NON_RECURSIVE" (Doesn't search recursively within groups.) and "RECURSIVE" (Searches recursively within groups.) +* `white_listed_groups`: - List of allowed User Groups for the Directory Service. +* `created_time`: - Creation time of the Directory Service. +* `last_updated_time`: - Last updated time of the Directory Service. +* `created_by`: - User or Service who created the Directory Service. + +#### Service Account + +The service_account attribute supports the following: + +* `username`: - Username to connect to the Directory Service. +* `password`: - Password to connect to the Directory Service. + + +#### Open Ldap Configuration + +The open_ldap_configuration attribute supports the following: + +* `user_configuration`: - this field will avoid down migration of data from the hot tier unless the overrides field is specified for the virtual disks. +* `user_group_configuration`: - this field will avoid down migration of data from the hot tier unless the overrides field is specified for the virtual disks. + +##### User Configuration + +The user_configuration attribute supports the following: + +* `user_object_class`: - Object class in the OpenLDAP system that corresponds to Users. +* `user_search_base`: - Base DN for User search. +* `username_attribute`: - Unique Identifier for each User which can be used in Authentication. + +##### User Group Configuration + +The user_group_configuration attribute supports the following: + +* `group_object_class`: - Object class in the OpenLDAP system that corresponds to groups. +* `group_search_base`: - Base DN for group search. +* `group_member_attribute`: - Attribute in a group that associates Users to the group. +* `group_member_attribute_value`: - User attribute value that will be used in group entity to associate User to the group. + + +See detailed information in [Nutanix Directory Services](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/d/nutanix_role_v4.html.markdown b/website/docs/d/nutanix_role_v4.html.markdown new file mode 100644 index 000000000..44cb3d430 --- /dev/null +++ b/website/docs/d/nutanix_role_v4.html.markdown @@ -0,0 +1,55 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_roles_v2" +sidebar_current: "docs-nutanix-datasource-roles-v4" +description: |- + Describes a List Role(s). +--- + +# nutanix_volume_groups_v4 + +Describes a List all the Role(s). + +## Example Usage + +```hcl +data "nutanix_roles_v2" "roles"{ + ext_id = var.role_ext_id +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `ext_id`: - (Required) ExtId for the Role. + +## Attributes Reference +The following attributes are exported: + + +* `tenant_id`: - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id`: - A globally unique identifier of an instance that is suitable for external consumption. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `display_name`: - The display name for the Role. +* `description`: - Description of the Role. +* `client_name`: - Client that created the entity. +* `operations`: - Indicates whether to enable Volume Group load balancing for VM attachments. This cannot be enabled if there are iSCSI client attachments already associated with the Volume Group, and vice-versa. This is an optional field. +* `accessible_clients`: - List of Accessible Clients for the Role. +* `accessible_entity_types`: - List of Accessible Entity Types for the Role. +* `assigned_users_count`: - Number of Users assigned to given Role. +* `assigned_users_groups_count`: - Number of User Groups assigned to given Role. +* `created_time`: - The creation time of the Role. +* `last_updated_time`: - The time when the Role was last updated. +* `created_by`: - User or Service Name that created the Role. +* `is_system_defined`: - Flag identifying if the Role is system defined or not. + +#### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + +See detailed information in [Nutanix Roles](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/d/nutanix_roles_v4.html.markdown b/website/docs/d/nutanix_roles_v4.html.markdown new file mode 100644 index 000000000..61f944b2e --- /dev/null +++ b/website/docs/d/nutanix_roles_v4.html.markdown @@ -0,0 +1,59 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_roles_v2" +sidebar_current: "docs-nutanix-datasource-roles-v4" +description: |- + Describes a List Role(s). +--- + +# nutanix_volume_groups_v4 + +Describes a List all the Role(s). + +## Example Usage + +```hcl +data "nutanix_roles_v2" "roles"{} + +data "nutanix_roles_v2" "test"{ + filter = "displayName eq 'example_role'" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `page`: - A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results. +* `limit` : A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set. +* `filter` :A URL query parameter that allows clients to filter a collection of resources. The expression specified with \$filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the \$filter must conform to the OData V4.01 URL conventions. For example, filter '\$filter=name eq 'karbon-ntnx-1.0' would filter the result on cluster name 'karbon-ntnx1.0', filter '\$filter=startswith(name, 'C')' would filter on cluster name starting with 'C'. The filter can be applied to the following fields: clientName, createdBy, extId, createdTime, displayName, extId, isSystemDefined, lastUpdatedTime. +* `orderby` : A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default. For example, '\$orderby=templateName desc' would get all templates sorted by templateName in descending order. The orderby can be applied to the following fields: createdTime, distinguishedName, displayName, extId, lastUpdatedTime. +* `select` : A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the \$select must conform to the OData V4.01 URL conventions. If a \$select expression consists of a single select item that is an asterisk (i.e., *), then all properties on the matching resource will be returned. following fields: accessibleClients, accessibleEntityTypes, assignedUserGroupsCount, assignedUsersCount, clientName, createdBy, createdTime, description, displayName, extId, isSystemDefined, lastUpdatedTime, links, operations, tenantId. + +## Attributes Reference +The following attributes are exported: + +* `tenant_id`: - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id`: - A globally unique identifier of an instance that is suitable for external consumption. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `display_name`: - The display name for the Role. +* `description`: - Description of the Role. +* `client_name`: - Client that created the entity. +* `operations`: - Indicates whether to enable Volume Group load balancing for VM attachments. This cannot be enabled if there are iSCSI client attachments already associated with the Volume Group, and vice-versa. This is an optional field. +* `accessible_clients`: - List of Accessible Clients for the Role. +* `accessible_entity_types`: - List of Accessible Entity Types for the Role. +* `assigned_users_count`: - Number of Users assigned to given Role. +* `assigned_users_groups_count`: - Number of User Groups assigned to given Role. +* `created_time`: - The creation time of the Role. +* `last_updated_time`: - The time when the Role was last updated. +* `created_by`: - User or Service Name that created the Role. +* `is_system_defined`: - Flag identifying if the Role is system defined or not. + +### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + +See detailed information in [Nutanix Roles](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/d/nutanix_saml_idp_v2.html.markdown b/website/docs/d/nutanix_saml_idp_v2.html.markdown new file mode 100644 index 000000000..1d3c77f4f --- /dev/null +++ b/website/docs/d/nutanix_saml_idp_v2.html.markdown @@ -0,0 +1,75 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_saml_identity_provider_v2" +sidebar_current: "docs-nutanix-datasource-saml-identity-provider-v4" +description: |- + Provides a datasource to View a SAML Identity Provider. +--- + +# nutanix_saml_identity_provider_v2 + +Provides a datasource to View a SAML Identity Provider. + +## Example Usage + +``` hcl +data "nutanix_saml_identity_provider_v2" "idp"{ + ext_id = <"SAML extID"> +} +``` + +## Argument Reference + +The following arguments are supported: + +* `ext_id`: - External identifier of the SAML Identity Provider. + +## Attributes Reference +The following attributes are exported: + +* `tenant_id` - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id` - The External Identifier of the User Group. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `idp_metadata`: - Type of the User Group. LDAP (User Group belonging to a Directory Service (Open LDAP/AD)), SAML (User Group belonging to a SAML IDP.) +* `name`: - Unique name of the IDP. +* `username_attr`: - SAML assertion Username attribute element. +* `email_attr`: - SAML assertion email attribute element. +* `groups_attr`: - SAML assertion groups attribute element. +* `groups_delim`: - Delimiter is used to split the value of attribute into multiple groups. +* `custom_attr`: - SAML assertions for list of custom attribute elements. +* `entity_issuer`: - It will be used as Issuer in SAML authnRequest. +* `is_signed_authn_req_enabled`: - Flag indicating signing of SAML authnRequests. +* `created_time`: - Creation time of the SAML Identity Provider. +* `last_updated_time`: - Last updated time of the SAML Identity Provider. +* `created_by`: - User or Service who created the SAML Identity Provider. + + +### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + +### Idp Metadata + +The idp_metadata attribute supports the following: + +* `entity_id`: - Entity Identifier of Identity provider. +* `login_url`: - Login URL of the Identity provider. +* `logout_url`: - Logout URL of the Identity provider. +* `error_url`: - Error URL of the Identity provider. +* `certificate`: - Certificate for verification. +* `name_id_policy_format`: - Name ID Policy format. + * supported values: + * `emailAddress`: - Uses email address as NameID format + * `encrypted`: - Uses encrypted as NameID format. + * `unspecified`: - NameID format is left to individual implementations. + * `transient`: - Uses identifier with transient semantics as NameID format. + * `WindowsDomainQualifiedName`: - Uses Windows domain qualified name as NameID format. + * `X509SubjectName`: - Uses X509SubjectName as NameID format. + * `kerberos`: - Uses kerberos principal name as NameID format. + * `persistent`: - Uses persistent name identifier as NameID format. + * `entity`: - Uses identifier of an entity as NameID format. + +See detailed information in [Nutanix SAML Identity Providers](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/d/nutanix_saml_idps_v2.html.markdown b/website/docs/d/nutanix_saml_idps_v2.html.markdown new file mode 100644 index 000000000..e06908dc2 --- /dev/null +++ b/website/docs/d/nutanix_saml_idps_v2.html.markdown @@ -0,0 +1,83 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_saml_identity_providers_v2" +sidebar_current: "docs-nutanix-datasource-saml-identity-providers-v4" +description: |- + Provides a datasource to retrieve all the all SAML Identity Provider(s). +--- + +# nutanix_saml_identity_providers_v2 + +Provides a datasource to retrieve all the SAML Identity Provider(s). + +## Example Usage + +``` hcl +data "nutanix_saml_identity_providers_v2" "idps"{} +``` + +## Argument Reference + +The following arguments are supported: + +* `page`: - A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results. +* `limit` : A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set. +* `filter` :A URL query parameter that allows clients to filter a collection of resources. The expression specified with \$filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the \$filter must conform to the OData V4.01 URL conventions. For example, filter '\$filter=name eq 'karbon-ntnx-1.0' would filter the result on cluster name 'karbon-ntnx1.0', filter '\$filter=startswith(name, 'C')' would filter on cluster name starting with 'C'. The filter can be applied to the following fields: createdBy, extId, name +* `orderby` : A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default. For example, '\$orderby=templateName desc' would get all templates sorted by templateName in descending order. The orderby can be applied to the following fields: createdTime, lastUpdatedTime, name +* `select` : A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the \$select must conform to the OData V4.01 URL conventions. If a \$select expression consists of a single select item that is an asterisk (i.e., *), then all properties on the matching resource will be returned. +createdBy, createdTime, customAttributes, emailAttribute, entityIssuer, extId, groupsAttribute, groupsDelim, idpMetadata/certificate, idpMetadata/entityId, idpMetadata/errorUrl, idpMetadata/loginUrl, idpMetadata/logoutUrl, idpMetadata/nameIdPolicyFormat, isSignedAuthnReqEnabled, lastUpdatedTime, links, name, tenantId, usernameAttribute. + +## Attributes Reference +The following attributes are exported: + +* `identity_providers` : List all SAML Identity Provider(s). + +### Identity Providers + +The identity_providers attribute element contains the following attributes: + +* `tenant_id` - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id` - The External Identifier of the User Group. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `idp_metadata`: - Type of the User Group. LDAP (User Group belonging to a Directory Service (Open LDAP/AD)), SAML (User Group belonging to a SAML IDP.) +* `name`: - Unique name of the IDP. +* `username_attr`: - SAML assertion Username attribute element. +* `email_attr`: - SAML assertion email attribute element. +* `groups_attr`: - SAML assertion groups attribute element. +* `groups_delim`: - Delimiter is used to split the value of attribute into multiple groups. +* `custom_attr`: - SAML assertions for list of custom attribute elements. +* `entity_issuer`: - It will be used as Issuer in SAML authnRequest. +* `is_signed_authn_req_enabled`: - Flag indicating signing of SAML authnRequests. +* `created_time`: - Creation time of the SAML Identity Provider. +* `last_updated_time`: - Last updated time of the SAML Identity Provider. +* `created_by`: - User or Service who created the SAML Identity Provider. + + +### Links +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + +### Idp Metadata + +The idp_metadata attribute supports the following: + +* `entity_id`: - Entity Identifier of Identity provider. +* `login_url`: - Login URL of the Identity provider. +* `logout_url`: - Logout URL of the Identity provider. +* `error_url`: - Error URL of the Identity provider. +* `certificate`: - Certificate for verification. +* `name_id_policy_format`: - Name ID Policy format. + * supported values: + * `emailAddress`: - Uses email address as NameID format + * `encrypted`: - Uses encrypted as NameID format. + * `unspecified`: - NameID format is left to individual implementations. + * `transient`: - Uses identifier with transient semantics as NameID format. + * `WindowsDomainQualifiedName`: - Uses Windows domain qualified name as NameID format. + * `X509SubjectName`: - Uses X509SubjectName as NameID format. + * `kerberos`: - Uses kerberos principal name as NameID format. + * `persistent`: - Uses persistent name identifier as NameID format. + * `entity`: - Uses identifier of an entity as NameID format. + +See detailed information in [Nutanix SAML Identity Providers](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/d/operation_v2.html.markdown b/website/docs/d/operation_v2.html.markdown new file mode 100644 index 000000000..8bef55886 --- /dev/null +++ b/website/docs/d/operation_v2.html.markdown @@ -0,0 +1,47 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_permission_v2" +sidebar_current: "docs-nutanix-datasource-permission-v2" +description: |- + This operation retrieves a list of all the permission. +--- + +# nutanix_permission_v2 +Lists the Permission defined on the system. List of permission can be further filtered out using various filtering options. + +## Example + +```hcl + + data "nutanix_operation_v2" "test" { + ext_id = "" + } + +``` + +## Argument Reference + +The following arguments are supported: + +* `ext_id`:(Required) ExtId of the Operation. + +## Attributes Reference + +* `ext_id`: A globally unique identifier of an instance that is suitable for external consumption. +* `display_name`: Permission name. +* `dedescription`: Permission description +* `create_time`: Permission creation time +* `last_updated_time`: Permission last updated time. + +* `entity_type`: Type of entity associated with this Operation. +* `operation_type`: The Operation type. Currently we support INTERNAL, EXTERNAL and SYSTEM_DEFINED_ONLY. +* `client_name`: Client that created the entity. +* `related_operation_list`: List of related Operations. These are the Operations which might need to be given access to, along with the current Operation, for certain workflows to succeed. +* `associated_endpoint_list`: List of associated endpoint objects for the Operation. + +### associated_endpoint_list +* `api_version`: Version of the API for the provided associated endpoint. +* `endpoint_url`: Endpoint URL. +* `http_method`: HTTP method for the provided associated endpoint. + +See detailed information in [Nutanix Operations](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). \ No newline at end of file diff --git a/website/docs/d/operations_v2.html.markdown b/website/docs/d/operations_v2.html.markdown new file mode 100644 index 000000000..180a6c755 --- /dev/null +++ b/website/docs/d/operations_v2.html.markdown @@ -0,0 +1,59 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_permissions_v2" +sidebar_current: "docs-nutanix-datasource-permissions-v2" +description: |- + This operation retrieves a list of all the permissions. +--- + +# nutanix_permissions_v2 +Lists the Permissions defined on the system. List of permissions can be further filtered out using various filtering options. + +## Example + +```hcl + + data "nutanix_operations_v2" "test" { } + + data "nutanix_operations_v2" "test" { + page=0 + limit=2 + } + + data "nutanix_operations_v2" "test" { + filter = "display_name eq 'test-Permission-filter'" + } + +``` + +## Attribute Reference + +The following attributes are exported: + +* `page`: A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results. +* `limit`: A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set. +* `filter`: A URL query parameter that allows clients to filter a collection of resources. The expression specified with $filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the $filter must conform to the OData V4.01 URL conventions +* `order_by`: A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default +* `select`: A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the $select must conform to the OData V4.01 URL conventions. +* `permissions`: List of all permissions + +### permissions + +* `ext_id`: A globally unique identifier of an instance that is suitable for external consumption. +* `display_name`: Permission name. +* `dedescription`: Permission description +* `create_time`: Permission creation time +* `last_updated_time`: Permission last updated time. + +* `entity_type`: Type of entity associated with this Operation. +* `operation_type`: The Operation type. Currently we support INTERNAL, EXTERNAL and SYSTEM_DEFINED_ONLY. +* `client_name`: Client that created the entity. +* `related_operation_list`: List of related Operations. These are the Operations which might need to be given access to, along with the current Operation, for certain workflows to succeed. +* `associated_endpoint_list`: List of associated endpoint objects for the Operation. + +### associated_endpoint_list +* `api_version`: Version of the API for the provided associated endpoint. +* `endpoint_url`: Endpoint URL. +* `http_method`: HTTP method for the provided associated endpoint. + +See detailed information in [Nutanix Operations](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). \ No newline at end of file diff --git a/website/docs/d/user_group_v2.html.markdown b/website/docs/d/user_group_v2.html.markdown new file mode 100644 index 000000000..40e27f84a --- /dev/null +++ b/website/docs/d/user_group_v2.html.markdown @@ -0,0 +1,56 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_user_group_v2" +sidebar_current: "docs-nutanix-datasource-user-group-v4" +description: |- + This operation retrieves a User Group based on the External Identifier of the User Group. +--- + +# nutanix_user_group_v2 + +Provides a datasource to retrieve a user group based on the External Identifier of the User Group. + +## Example Usage + +``` hcl + +//Retrieve by ext_id +data "nutanix_user_group_v2" "usergroup"{ + ext_id = "" +} + +``` + + + + +## Argument Reference + +The following arguments are supported: + +* `ext_id`: - (Required) The External Identifier of the User Group. + +## Attributes Reference + +The following attributes are exported: + +* `tenant_id` - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id` - The External Identifier of the User Group. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `group_type`: - Type of the User Group. LDAP (User Group belonging to a Directory Service (Open LDAP/AD)), SAML (User Group belonging to a SAML IDP.) +* `idp_id`: - Identifier of the IDP for the User Group. +* `name`: - Common Name of the User Group. +* `distinguished_name`: - Identifier for the User Group in the form of a distinguished name. +* `created_time`: - Creation time of the User Group. +* `last_updated_time`: - Last updated time of the User Group. +* `created_by`: - User or Service who created the User Group. + +### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + + +See detailed information in [Nutanix User Groups](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). \ No newline at end of file diff --git a/website/docs/d/user_groups_v2.html.markdown b/website/docs/d/user_groups_v2.html.markdown new file mode 100644 index 000000000..9bc26e832 --- /dev/null +++ b/website/docs/d/user_groups_v2.html.markdown @@ -0,0 +1,61 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_user_groups_v2" +sidebar_current: "docs-nutanix-datasource-user-groups-v4" +description: |- + Provides a datasource to retrieve all the user groups. +--- + +# nutanix_user_groups_v2 + +Provides a datasource to retrieve all the user groups. + +## Example Usage + +``` hcl +data "nutanix_user_groups_v2" "usergroups"{} +``` + +## Argument Reference + +The following arguments are supported: + +* `page`: - A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results. +* `limit` : A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set. +* `filter` :A URL query parameter that allows clients to filter a collection of resources. The expression specified with \$filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the \$filter must conform to the OData V4.01 URL conventions. For example, filter '\$filter=name eq 'karbon-ntnx-1.0' would filter the result on cluster name 'karbon-ntnx1.0', filter '\$filter=startswith(name, 'C')' would filter on cluster name starting with 'C'. The filter can be applied to the following fields: createdBy, distinguishedName, extId, groupType, idpId, name. +* `orderby` : A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default. For example, '\$orderby=templateName desc' would get all templates sorted by templateName in descending order. The orderby can be applied to the following fields: createdTime, distinguishedName, groupType, lastUpdatedTime, name. +* `select` : A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the \$select must conform to the OData V4.01 URL conventions. If a \$select expression consists of a single select item that is an asterisk (i.e., *), then all properties on the matching resource will be returned. following fields: createdBy, createdTime, distinguishedName, extId, groupType, idpId, lastUpdatedTime, links, name, tenantId. + + +## Attributes Reference +The following attributes are exported: + +* `user_groups` : List all User Group(s). + +### User Groups + +The user_groups attribute element contains the following attributes: + +The following attributes are exported: + +* `tenant_id` - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id` - The External Identifier of the User Group. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `group_type`: - Type of the User Group. LDAP (User Group belonging to a Directory Service (Open LDAP/AD)), SAML (User Group belonging to a SAML IDP.) +* `idp_id`: - Identifier of the IDP for the User Group. +* `name`: - Common Name of the User Group. +* `distinguished_name`: - Identifier for the User Group in the form of a distinguished name. +* `created_time`: - Creation time of the User Group. +* `last_updated_time`: - Last updated time of the User Group. +* `created_by`: - User or Service who created the User Group. + + +#### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + + +See detailed information in [Nutanix User Groups](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/d/user_v2.html.markdown b/website/docs/d/user_v2.html.markdown new file mode 100644 index 000000000..df761351a --- /dev/null +++ b/website/docs/d/user_v2.html.markdown @@ -0,0 +1,84 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_user_v2" +sidebar_current: "docs-nutanix-datasource-user-v4" +description: |- + Provides a datasource to View a User. +--- + +# nutanix_user_v2 + +Provides a datasource to View a User. + +## Example Usage + +``` hcl +data "nutanix_user_v2" "users"{ + ext_id = "" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `ext_id`: -(Required) External Identifier of the User. + + +## Attributes Reference +The following attributes are exported: + +* `tenant_id` - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id` - The External Identifier of the User Group. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `username`: - Identifier for the User in the form an email address. +* `user_type`: - Enum: `$UNKNOWN` `$REDACTED` `LOCAL` `SAML` `LDAP` `EXTERNAL` +Type of the User. +* `idp_id`: - Identifier of the IDP for the User. +* `display_name`: - Display name for the User. +* `first_name`: - First name for the User. +* `middle_initial`: - Middle name for the User. +* `last_name`: - Last name for the User. +* `email_id`: - Email Id for the User. +* `locale`: - Default locale for the User. +* `region`: - Default Region for the User. +* `is_force_reset_password`: - Flag to force the User to reset password. +* `additional_attributes`: - Any additional attribute for the User. +* `status`: - Status of the User. `ACTIVE`: Denotes that the local User is active. `INACTIVE`: Denotes that the local User is inactive and needs to be reactivated. +* `buckets_access_keys`: - Bucket Access Keys for the User. +* `last_login_time`: - Last successful logged in time for the User. +* `created_time`: - Creation time of the User. +* `last_updated_time`: - Last updated time of the User. +* `created_by`: - User or Service who created the User. +* `last_updated_by`: - Last updated by this User ID. + + +### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + + +### Additional Attributes + +The additional_attributes attribute supports the following: + +* `name`: - The URL at which the entity described by the link can be accessed. +* `value`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + +### Buckets Access Keys + +The buckets_access_keys attribute supports the following: + +* `tenant_id`: - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id`: - A globally unique identifier of an instance that is suitable for external consumption. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `access_key_name`: - Name of the Bucket Access Key. +* `secret_access_key`: - Secret Access Key, it will be returned only during Bucket Access Key creation. +* `user_id`: - User Identifier who owns the Bucket Access Key. +* `created_time`: - Creation time for the Bucket Access Key. + + +See detailed information in [Nutanix Users](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/d/users_v2.html.markdown b/website/docs/d/users_v2.html.markdown new file mode 100644 index 000000000..bf2706045 --- /dev/null +++ b/website/docs/d/users_v2.html.markdown @@ -0,0 +1,134 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_users_v2" +sidebar_current: "docs-nutanix-datasource-users-v4" +description: |- + Provides a datasource to retrieve all User(s). +--- + +# nutanix_users_v2 + +Provides a datasource to retrieve all User(s). + +## Example Usage + +``` hcl +data "nutanix_users_v2" "users"{} +``` + +## Argument Reference + +The following arguments are supported: + +* `page`: - A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results. +* `limit` : A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set. +* `filter` :A URL query parameter that allows clients to filter a collection of resources. The expression specified with \$filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the \$filter must conform to the OData V4.01 URL conventions. For example, filter '\$filter=name eq 'karbon-ntnx-1.0' would filter the result on cluster name 'karbon-ntnx1.0', filter '\$filter=startswith(name, 'C')' would filter on cluster name starting with 'C'. The filter can be applied to the following fields: + * createdBy + * displayName + * emailId + * extId + * firstName + * idpId + * lastName + * lastUpdatedBy + * status + * userType + * username +* `orderby` : A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default. For example, '\$orderby=templateName desc' would get all templates sorted by templateName in descending order. The orderby can be applied to the following fields: * createdBy + * createdTime + * displayName + * emailId + * extId + * firstName + * lastLoginTime + * lastName + * lastUpdatedTime + * userType + * username +* `select` : A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the \$select must conform to the OData V4.01 URL conventions. If a \$select expression consists of a single select item that is an asterisk (i.e., *), then all properties on the matching resource will be returned. following fields: + * additionalAttributes + * bucketsAccessKeys + * createdBy + * createdTime + * displayName + * emailId + * extId + * firstName + * idpId + * isForceResetPasswordEnabled + * lastLoginTime + * lastName + * lastUpdatedBy + * lastUpdatedTime + * links + * locale + * middleInitial + * region + * status + * tenantId + * userType + * username + +## Attributes Reference +The following attributes are exported: + +* `users` : List all User(s). + +### User Groups + +The users attribute element contains the following attributes: + +* `tenant_id` - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id` - The External Identifier of the User Group. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `username`: - Identifier for the User in the form an email address. +* `user_type`: - Enum: `$UNKNOWN` `$REDACTED` `LOCAL` `SAML` `LDAP` `EXTERNAL` +Type of the User. +* `idp_id`: - Identifier of the IDP for the User. +* `display_name`: - Display name for the User. +* `first_name`: - First name for the User. +* `middle_initial`: - Middle name for the User. +* `last_name`: - Last name for the User. +* `email_id`: - Email Id for the User. +* `locale`: - Default locale for the User. +* `region`: - Default Region for the User. +* `is_force_reset_password`: - Flag to force the User to reset password. +* `additional_attributes`: - Any additional attribute for the User. +* `status`: - Status of the User. `ACTIVE`: Denotes that the local User is active. `INACTIVE`: Denotes that the local User is inactive and needs to be reactivated. +* `buckets_access_keys`: - Bucket Access Keys for the User. +* `last_login_time`: - Last successful logged in time for the User. +* `created_time`: - Creation time of the User. +* `last_updated_time`: - Last updated time of the User. +* `created_by`: - User or Service who created the User. +* `last_updated_by`: - Last updated by this User ID. + + +### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + + +### Additional Attributes + +The additional_attributes attribute supports the following: + +* `name`: - The URL at which the entity described by the link can be accessed. +* `value`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + +### Buckets Access Keys + +The buckets_access_keys attribute supports the following: + +* `tenant_id`: - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id`: - A globally unique identifier of an instance that is suitable for external consumption. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `access_key_name`: - Name of the Bucket Access Key. +* `secret_access_key`: - Secret Access Key, it will be returned only during Bucket Access Key creation. +* `user_id`: - User Identifier who owns the Bucket Access Key. +* `created_time`: - Creation time for the Bucket Access Key. + + +See detailed information in [Nutanix Users](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/r/authorization_policy_v2.html.markdown b/website/docs/r/authorization_policy_v2.html.markdown new file mode 100644 index 000000000..6c9495231 --- /dev/null +++ b/website/docs/r/authorization_policy_v2.html.markdown @@ -0,0 +1,78 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: authorization_policy_v4" +sidebar_current: "docs-nutanix-resource-authorization-policy-v4" +description: |- + Create Virtual Private Cloud . +--- + +# nutanix_vpc_v4 + +Provides Nutanix resource to create authorization policy. + + +## Example + +```hcl + + resource "authorization_policy_v4" "test"{ + role = + display_name = + description = "auth policies description" + authorization_policy_type = "USER_DEFINED" + identities { + # must be a json string + reserved = ""{\"user\":{\"uuid\":{\"anyof\":[\"\"]}}}"" + } + + entities { + # must be a json string + reserved = "{\"*\":{\"*\":{\"eq\":\"*\"}}}" + } + } +``` + +## Argument Reference + +The following arguments are supported: + +* `display_name`: Name of the Authorization Policy. +* `description`: Description of the Authorization Policy. +* `client_name`: Client that created the entity. +* `identities`: The identities for which the Authorization Policy is created. +* `entities`: The entities being qualified by the Authorization Policy. +* `role`: The Role associated with the Authorization Policy. +* `authorization_policy_type`: Type of Authorization Policy. + * `PREDEFINED_READ_ONLY` : System-defined read-only ACP, i.e. no modifications allowed. + * `SERVICE_DEFINED_READ_ONLY` : Read-only ACP defined by a service. + * `PREDEFINED_UPDATE_IDENTITY_ONLY` : System-defined ACP prohibiting any modifications from customer. + * `SERVICE_DEFINED` : ACP defined by a service. + * `USER_DEFINED` : ACP defined by an User. + +## Attribute Reference + +The following attributes are exported: +* `ext_id`: ext_id of Authorization policy. +* `links`: A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `display_name`: Name of the Authorization Policy. +* `description`: Description of the Authorization Policy. +* `client_name`: Client that created the entity. +* `identities`: The identities for which the Authorization Policy is created. +* `entities`: The entities being qualified by the Authorization Policy. +* `role`: The Role associated with the Authorization Policy. +* `created_time`: The creation time of the Authorization Policy. +* `last_updated_time`: The time when the Authorization Policy was last updated. +* `created_by`: User or Service Name that created the Authorization Policy. +* `is_system_defined`: Flag identifying if the Authorization Policy is system defined or not. +* `authorization_policy_type`: Type of Authorization Policy. + + +### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object + + +See detailed information in [Nutanix Authorization Policies](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/r/nutanix_directory_services_v2.html.markdown b/website/docs/r/nutanix_directory_services_v2.html.markdown new file mode 100644 index 000000000..62c136056 --- /dev/null +++ b/website/docs/r/nutanix_directory_services_v2.html.markdown @@ -0,0 +1,130 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_directory_services_v2" +sidebar_current: "docs-nutanix-resource-directory-services-v4" +description: |- + This operation submits a request to Create a Directory Service. +--- + +# nutanix_directory_services_v2 + +Provides a resource to Create a Directory Service. + +## Example Usage + +``` hcl +resource "nutanix_directory_services_v2" "example"{ + name = "directory services name" + service_account { + username = "service account username" + password = "service account password" + } + directory_type = "directory type" + domain_name = "domain name" + url = "URL for the Directory Service" +} +``` + +## Argument Reference +The following arguments are supported: + + +* `ext_id`: -(Optional) A globally unique identifier of an instance that is suitable for external consumption. +* `name`: -(Required) Name for the Directory Service. +* `url`: -(Required) URL for the Directory Service. +* `secondary_urls`: -(Optional) Secondary URL for the Directory Service. +* `domain_name`: -(Required) Domain name for the Directory Service. +* `directory_type`: -(Required) Type of Directory Service, Supported values are: "ACTIVE_DIRECTORY" (Directory Service type is Active Directory.) and "OPEN_LDAP" (Directory Service type is Open LDAP.) +* `service_account`: -(Required) Information of Service account to connect to the Directory Service. +* `open_ldap_configuration`: -(Optional) Configuration for OpenLDAP Directory Service. +* `group_search_type`: -(Optional) Group membership search type for the Directory Service. Supported values are: "NON_RECURSIVE" (Doesn't search recursively within groups.) and "RECURSIVE" (Searches recursively within groups.) +* `white_listed_groups`: -(Optional) List of allowed User Groups for the Directory Service. + + +### Service Account + +The service_account attribute supports the following: + +* `username`: -(Required) Username to connect to the Directory Service. +* `password`: -(Required) Password to connect to the Directory Service. + + +### Open Ldap Configuration + +The open_ldap_configuration attribute supports the following: + +* `user_configuration`: -(Required) this field will avoid down migration of data from the hot tier unless the overrides field is specified for the virtual disks. +* `user_group_configuration`: -(Required) this field will avoid down migration of data from the hot tier unless the overrides field is specified for the virtual disks. + +#### User Configuration + +The user_configuration attribute supports the following: + +* `user_object_class`: -(Required) Object class in the OpenLDAP system that corresponds to Users. +* `user_search_base`: -(Required) Base DN for User search. +* `username_attribute`: -(Required) Unique Identifier for each User which can be used in Authentication. + +#### User Group Configuration + +The user_group_configuration attribute supports the following: + +* `group_object_class`: -(Required) Object class in the OpenLDAP system that corresponds to groups. +* `group_search_base`: -(Required) Base DN for group search. +* `group_member_attribute`: -(Required) Attribute in a group that associates Users to the group. +* `group_member_attribute_value`: -(Required) User attribute value that will be used in group entity to associate User to the group. + + +## Attributes Reference +The following attributes are exported: + + +* `tenant_id`: - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id`: - A globally unique identifier of an instance that is suitable for external consumption. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `name`: - Name for the Directory Service. +* `url`: - URL for the Directory Service. +* `secondary_urls`: - Secondary URL for the Directory Service. +* `domain_name`: - Domain name for the Directory Service. +* `directory_type`: - Type of Directory Service, Supported values are: "ACTIVE_DIRECTORY" (Directory Service type is Active Directory.) and "OPEN_LDAP" (Directory Service type is Open LDAP.) +* `service_account`: - Information of Service account to connect to the Directory Service. +* `open_ldap_configuration`: - Configuration for OpenLDAP Directory Service. +* `group_search_type`: - Group membership search type for the Directory Service. Supported values are: "NON_RECURSIVE" (Doesn't search recursively within groups.) and "RECURSIVE" (Searches recursively within groups.) +* `white_listed_groups`: - List of allowed User Groups for the Directory Service. +* `created_time`: - Creation time of the Directory Service. +* `last_updated_time`: - Last updated time of the Directory Service. +* `created_by`: - User or Service who created the Directory Service. + +### Service Account + +The service_account attribute supports the following: + +* `username`: - Username to connect to the Directory Service. +* `password`: - Password to connect to the Directory Service. + + +### Open Ldap Configuration + +The open_ldap_configuration attribute supports the following: + +* `user_configuration`: - this field will avoid down migration of data from the hot tier unless the overrides field is specified for the virtual disks. +* `user_group_configuration`: - this field will avoid down migration of data from the hot tier unless the overrides field is specified for the virtual disks. + +#### User Configuration + +The user_configuration attribute supports the following: + +* `user_object_class`: - Object class in the OpenLDAP system that corresponds to Users. +* `user_search_base`: - Base DN for User search. +* `username_attribute`: - Unique Identifier for each User which can be used in Authentication. + +#### User Group Configuration + +The user_group_configuration attribute supports the following: + +* `group_object_class`: - Object class in the OpenLDAP system that corresponds to groups. +* `group_search_base`: - Base DN for group search. +* `group_member_attribute`: - Attribute in a group that associates Users to the group. +* `group_member_attribute_value`: - User attribute value that will be used in group entity to associate User to the group. + + +See detailed information in [Nutanix Directory Services](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/r/nutanix_saml_idps_v2.html.markdown b/website/docs/r/nutanix_saml_idps_v2.html.markdown new file mode 100644 index 000000000..c381dbdbb --- /dev/null +++ b/website/docs/r/nutanix_saml_idps_v2.html.markdown @@ -0,0 +1,119 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_saml_identity_providers_v2" +sidebar_current: "docs-nutanix-resource-saml-identity-providers-v4" +description: |- + Create a SAML Identity Provider. +--- + +# nutanix_saml_identity_providers_v2 + +Provides a resource to Create a SAML Identity Provider. + +## Example Usage + +```hcl + resource "nutanix_saml_identity_providers_v2" "idp"{ + idp_metadata { + entity_id = "" + login_url = "" + logout_url = "" + error_url = "" + certificate = "" + name_id_policy_format = " " + } + name = "" + username_attribute = "" + email_attribute = "" + entity_issuer = "" + is_signed_authn_req_enabled = "" + } +``` + +## Argument Reference + +The following arguments are supported: + +* `ext_id`: -(Optional) External identifier of the SAML Identity Provider. +* `idp_metadata_url`: -(Optional) Metadata url that provides IDP details. +* `idp_metadata_xml`: -(Optional) Base64 encoded metadata in XML format with IDP details. +* `idp_metadata`: -(Optional) Type of the User Group. LDAP (User Group belonging to a Directory Service (Open LDAP/AD)), SAML (User Group belonging to a SAML IDP.) +* `name`: -(Required) Unique name of the IDP. +* `username_attr`: -(Optional) SAML assertion Username attribute element. +* `email_attr`: -(Optional) SAML assertion email attribute element. +* `groups_attr`: -(Optional) SAML assertion groups attribute element. +* `groups_delim`: -(Optional) Delimiter is used to split the value of attribute into multiple groups. +* `custom_attr`: -(Optional) SAML assertions for list of custom attribute elements. +* `entity_issuer`: -(Optional) It will be used as Issuer in SAML authnRequest. +* `is_signed_authn_req_enabled`: -(Optional) Flag indicating signing of SAML authnRequests. + +### Idp Metadata + +The idp_metadata attribute supports the following: + +* `entity_id`: -(Required) Entity Identifier of Identity provider. +* `login_url`: -(Required) Login URL of the Identity provider. +* `logout_url`: -(Optional) Logout URL of the Identity provider. +* `error_url`: - (Optional) Error URL of the Identity provider. +* `certificate`: -(Required) Certificate for verification. +* `name_id_policy_format`: -(Optional) Name ID Policy format. + * supported values: + * `emailAddress`: - Uses email address as NameID format + * `encrypted`: - Uses encrypted as NameID format. + * `unspecified`: - NameID format is left to individual implementations. + * `transient`: - Uses identifier with transient semantics as NameID format. + * `WindowsDomainQualifiedName`: - Uses Windows domain qualified name as NameID format. + * `X509SubjectName`: - Uses X509SubjectName as NameID format. + * `kerberos`: - Uses kerberos principal name as NameID format. + * `persistent`: - Uses persistent name identifier as NameID format. + * `entity`: - Uses identifier of an entity as NameID format. + +## Attributes Reference +The following attributes are exported: + +* `tenant_id` - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id` - The External Identifier of the User Group. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `idp_metadata`: - Type of the User Group. LDAP (User Group belonging to a Directory Service (Open LDAP/AD)), SAML (User Group belonging to a SAML IDP.) +* `name`: - Unique name of the IDP. +* `username_attr`: - SAML assertion Username attribute element. +* `email_attr`: - SAML assertion email attribute element. +* `groups_attr`: - SAML assertion groups attribute element. +* `groups_delim`: - Delimiter is used to split the value of attribute into multiple groups. +* `custom_attr`: - SAML assertions for list of custom attribute elements. +* `entity_issuer`: - It will be used as Issuer in SAML authnRequest. +* `is_signed_authn_req_enabled`: - Flag indicating signing of SAML authnRequests. +* `created_time`: - Creation time of the SAML Identity Provider. +* `last_updated_time`: - Last updated time of the SAML Identity Provider. +* `created_by`: - User or Service who created the SAML Identity Provider. + + +### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + +### Idp Metadata + +The idp_metadata attribute supports the following: + +* `entity_id`: - Entity Identifier of Identity provider. +* `login_url`: - Login URL of the Identity provider. +* `logout_url`: - Logout URL of the Identity provider. +* `error_url`: - Error URL of the Identity provider. +* `certificate`: - Certificate for verification. +* `name_id_policy_format`: - Name ID Policy format. + * supported values: + * `emailAddress`: - Uses email address as NameID format + * `encrypted`: - Uses encrypted as NameID format. + * `unspecified`: - NameID format is left to individual implementations. + * `transient`: - Uses identifier with transient semantics as NameID format. + * `WindowsDomainQualifiedName`: - Uses Windows domain qualified name as NameID format. + * `X509SubjectName`: - Uses X509SubjectName as NameID format. + * `kerberos`: - Uses kerberos principal name as NameID format. + * `persistent`: - Uses persistent name identifier as NameID format. + * `entity`: - Uses identifier of an entity as NameID format. + +See detailed information in [Nutanix SAML Identity Providers](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/r/roles_v2.html.markdown b/website/docs/r/roles_v2.html.markdown new file mode 100644 index 000000000..2bfbf8033 --- /dev/null +++ b/website/docs/r/roles_v2.html.markdown @@ -0,0 +1,61 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_roles_v2" +sidebar_current: "docs-nutanix-resource-roles-v4" +description: |- + This operation submits a request to add a Role.. +--- + +# nutanix_roles_v2 + +Provides a resource to add a Role. + +## Example Usage + +```hcl + resource "nutanix_roles_v2" "example" { + display_name= "{{ display-name }}" + description = "test description" + operations = "{{ operations }}" + } +``` + +## Argument Reference +The following arguments are supported: + + +* `display_name`: -(Required) The display name for the Role. +* `description`: - Description of the Role. +* `client_name`: - Client that created the entity. +* `operations`: -(Required) Indicates whether to enable Volume Group load balancing for VM attachments. This cannot be enabled if there are iSCSI client attachments already associated with the Volume Group, and vice-versa. This is an optional field. + + + + +## Attributes Reference +The following attributes are exported: +* `tenant_id`: - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id`: - A globally unique identifier of an instance that is suitable for external consumption. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `display_name`: - The display name for the Role. +* `description`: - Description of the Role. +* `client_name`: - Client that created the entity. +* `operations`: - Indicates whether to enable Volume Group load balancing for VM attachments. This cannot be enabled if there are iSCSI client attachments already associated with the Volume Group, and vice-versa. This is an optional field. +* `accessible_clients`: - List of Accessible Clients for the Role. +* `accessible_entity_types`: - List of Accessible Entity Types for the Role. +* `assigned_users_count`: - Number of Users assigned to given Role. +* `assigned_users_groups_count`: - Number of User Groups assigned to given Role. +* `created_time`: - The creation time of the Role. +* `last_updated_time`: - The time when the Role was last updated. +* `created_by`: - User or Service Name that created the Role. +* `is_system_defined`: - Flag identifying if the Role is system defined or not. + +### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + +See detailed information in [Nutanix Roles](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). + diff --git a/website/docs/r/user_groups_v2.html.markdown b/website/docs/r/user_groups_v2.html.markdown new file mode 100644 index 000000000..8955992dc --- /dev/null +++ b/website/docs/r/user_groups_v2.html.markdown @@ -0,0 +1,60 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_user_groups_v2" +sidebar_current: "docs-nutanix-resource-user-groups-v4" +description: |- + This operation add a User group to the system. +--- + +# nutanix_user_groups_v2 + +Provides a resource to add a User group to the system.. + +## Example Usage + +``` hcl +resource "nutanix_user_groups_v2" "usr_group"{ + group_type = "" + idp_id = "" + name = "" + distinguished_name = "" +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `ext_id` -(Optional) The External Identifier of the User Group. +* `group_type`: -(Required) Type of the User Group. LDAP (User Group belonging to a Directory Service (Open LDAP/AD)), SAML (User Group belonging to a SAML IDP.) +* `idp_id`: -(Required) Identifier of the IDP for the User Group. +* `name`: -(Optional) Common Name of the User Group. +* `distinguished_name`: -(Optional) Identifier for the User Group in the form of a distinguished name. + + +## Attributes Reference +The following attributes are exported: + +* `tenant_id` - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id` - The External Identifier of the User Group. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `group_type`: - Type of the User Group. LDAP (User Group belonging to a Directory Service (Open LDAP/AD)), SAML (User Group belonging to a SAML IDP.) +* `idp_id`: - Identifier of the IDP for the User Group. +* `name`: - Common Name of the User Group. +* `distinguished_name`: - Identifier for the User Group in the form of a distinguished name. +* `created_time`: - Creation time of the User Group. +* `last_updated_time`: - Last updated time of the User Group. +* `created_by`: - User or Service who created the User Group. + + + +### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + + +See detailed information in [Nutanix User Groups](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/r/users_v2.html.markdown b/website/docs/r/users_v2.html.markdown new file mode 100644 index 000000000..1fdb5d90b --- /dev/null +++ b/website/docs/r/users_v2.html.markdown @@ -0,0 +1,112 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_users_v2" +sidebar_current: "docs-nutanix-resource-users-v4" +description: |- + Create a User. +--- + +# nutanix_users_v2 + +Provides Nutanix resource to Create a User. + +## Example Usage +``` hcl +resource "nutanix_users_v2" "user"{ + username = "" + user_type = "" + idp_id = "" + display_name = "" + locale = "" + region = "" + force_reset_password = + status = "" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `ext_id`: -(Optional) External Identifier of the User. +* `username`: -(Required) Identifier for the User in the form an email address. +* `user_type`: -(Required) Enum: `$UNKNOWN` `$REDACTED` `LOCAL` `SAML` `LDAP` `EXTERNAL` +Type of the User. +* `idp_id`: -(Optional) Identifier of the IDP for the User. +* `display_name`: -(Optional) Display name for the User. +* `first_name`: -(Optional) First name for the User. +* `middle_initial`: -(Optional) Middle name for the User. +* `last_name`: -(Optional) Last name for the User. +* `email_id`: -(Optional) Email Id for the User. +* `locale`: -(Optional) Default locale for the User. +* `region`: -(Optional) Default Region for the User. +* `password`: -(Optional) Password for the User. +* `is_force_reset_password`: -(Optional) Flag to force the User to reset password. +* `additional_attributes`: -(Optional) Any additional attribute for the User. +* `status`: -(Optional) Status of the User. `ACTIVE`: Denotes that the local User is active. `INACTIVE`: Denotes that the local User is inactive and needs to be reactivated. + +### Additional Attributes + +The additional_attributes attribute supports the following: + +* `name`: -(Optional) The URL at which the entity described by the link can be accessed. +* `value`: -(Optional) A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + + +## Attributes Reference +The following attributes are exported: + +* `tenant_id` - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id` - The External Identifier of the User Group. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `username`: - Identifier for the User in the form an email address. +* `user_type`: - Enum: `$UNKNOWN` `$REDACTED` `LOCAL` `SAML` `LDAP` `EXTERNAL` +Type of the User. +* `idp_id`: - Identifier of the IDP for the User. +* `display_name`: - Display name for the User. +* `first_name`: - First name for the User. +* `middle_initial`: - Middle name for the User. +* `last_name`: - Last name for the User. +* `email_id`: - Email Id for the User. +* `locale`: - Default locale for the User. +* `region`: - Default Region for the User. +* `is_force_reset_password`: - Flag to force the User to reset password. +* `additional_attributes`: - Any additional attribute for the User. +* `status`: - Status of the User. `ACTIVE`: Denotes that the local User is active. `INACTIVE`: Denotes that the local User is inactive and needs to be reactivated. +* `buckets_access_keys`: - Bucket Access Keys for the User. +* `last_login_time`: - Last successful logged in time for the User. +* `created_time`: - Creation time of the User. +* `last_updated_time`: - Last updated time of the User. +* `created_by`: - User or Service who created the User. +* `last_updated_by`: - Last updated by this User ID. + + +### Links + +The links attribute supports the following: + +* `href`: - The URL at which the entity described by the link can be accessed. +* `rel`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + + +### Additional Attributes + +The additional_attributes attribute supports the following: + +* `name`: - The URL at which the entity described by the link can be accessed. +* `value`: - A name that identifies the relationship of the link to the object that is returned by the URL. The unique value of "self" identifies the URL for the object. + +### Buckets Access Keys + +The buckets_access_keys attribute supports the following: + +* `tenant_id`: - A globally unique identifier that represents the tenant that owns this entity. The system automatically assigns it, and it and is immutable from an API consumer perspective (some use cases may cause this Id to change - For instance, a use case may require the transfer of ownership of the entity, but these cases are handled automatically on the server). +* `ext_id`: - A globally unique identifier of an instance that is suitable for external consumption. +* `links`: - A HATEOAS style link for the response. Each link contains a user-friendly name identifying the link and an address for retrieving the particular resource. +* `access_key_name`: - Name of the Bucket Access Key. +* `secret_access_key`: - Secret Access Key, it will be returned only during Bucket Access Key creation. +* `user_id`: - User Identifier who owns the Bucket Access Key. +* `created_time`: - Creation time for the Bucket Access Key. + + +See detailed information in [Nutanix Users](https://developers.nutanix.com/api-reference?namespace=iam&version=v4.0.b1). diff --git a/website/docs/r/virtual_machine_clone_v2.html.markdown b/website/docs/r/virtual_machine_clone_v2.html.markdown new file mode 100644 index 000000000..33fce615e --- /dev/null +++ b/website/docs/r/virtual_machine_clone_v2.html.markdown @@ -0,0 +1,304 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_vm_clone" +sidebar_current: "docs-nutanix-resource-vm-clone" +description: |- + Provides a Nutanix Virtual Machine resource to Create a virtual machine clone. +--- + +# nutanix_vm_clone + +Provides a Nutanix Virtual Machine resource to Create a virtual machine clone. + +## Example Usage + +```hcl +data "nutanix_virtual_machines_v4" "vms" {} + +resource "nutanix_vm_clone_v4" "vm1" { + vm_ext_id = data.nutanix_virtual_machines_v4.vms.0.data.ext_id + name = "test-dou" + num_cores_per_socket = 1 + num_sockets = 1 + memory_size_mib = 2048 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `vm_ext_id`: - (Required) The globally unique identifier of a VM. It should be of type UUID. +* `name`: - (Optional) The name for the vm. +* `num_sockets`: - (Optional) Number of vCPU sockets. +* `num_cores_per_socket`: - (Optional) Number of cores per socket. +* `memory_size_mib`: - (Optional) Memory size in MiB. +* `num_threads_per_core`: - (Optional) Number of threads per core. +* `guest_customization`: - (Optional) Stage a Sysprep or cloud-init configuration file to be used by the guest for the next boot. Note that the Sysprep command must be used to generalize the Windows VMs before triggering this API call. +* `boot_config`: - (Optional) Indicates the order of device types in which the VM should try to boot from. If the boot device order is not provided the system will decide an appropriate boot device order. +* `nics`: - (Optional) NICs attached to the VM. + +### Nics + +The nics attribute supports the following: + +* `ext_id`: - (Optional) A globally unique identifier of an instance that is suitable for external consumption. +* `backing_info`: - (Optional) Defines a NIC emulated by the hypervisor +* `network_info`: - (Optional) Network information for a NIC. + +### Backing Info + +The backing_info attribute supports the following: + +* `model`: - (Optional) Options for the NIC emulation. + Valid values are: + - `VIRTIO` The NIC emulation model is Virtio. + - `E1000` The NIC emulation model is E1000. +* `mac_address`: - (Optional) MAC address of the emulated NIC. +* `is_connected`: - (Optional) Indicates whether the NIC is connected or not. Default is True. +* `num_queues`: - (Optional) The number of Tx/Rx queue pairs for this NIC. + +### Network Info + +The network_info attribute supports the following: + +* `nic_type`: - (Optional) NIC type. +Defaults to NORMAL_NIC. + Valid values are: + - `SPAN_DESTINATION_NIC` The type of NIC is Span-Destination. + - `NORMAL_NIC` The type of NIC is Normal. + - `DIRECT_NIC` The type of NIC is Direct. + - `NETWORK_FUNCTION_NIC` The type of NIC is Network-Function. +* `network_function_chain`: - (Optional)The network function chain associates with the NIC. Only valid if nic_type is NORMAL_NIC. +* `network_function_nic_type`: - (Optional) The type of this Network function NIC. +Defaults to INGRESS. + Valid values are: + - `TAP` The type of Network-Function NIC is Tap. + - `EGRESS` The type of Network-Function NIC is Egress. + - `INGRESS` The type of Network-Function NIC is Ingress. +* `subnet`: - (Optional) Network identifier for this adapter. Only valid if nic_type is NORMAL_NIC or DIRECT_NIC. +* `vlan_mode`: - (Optional) By default, all the virtual NICs are created in ACCESS mode, which permits only one VLAN per virtual network. TRUNKED mode allows multiple VLANs on a single VM NIC for network-aware user VMs. + Valid values are: + - `TRUNK` The virtual NIC is created in TRUNKED mode. + - `ACCESS` The virtual NIC is created in ACCESS mode. +* `trunked_vlans`: - (Optional) List of networks to trunk if VLAN mode is marked as TRUNKED. If empty and VLAN mode is set to TRUNKED, all the VLANs are trunked. +* `should_allow_unknown_macs`: - (Optional) Indicates whether an unknown unicast traffic is forwarded to this NIC or not. This is applicable only for the NICs on the overlay subnets. +* `ipv4_config`: - (Optional) The IP address configurations. + +### Network Function Chain + +The network_function_chain attribute supports the following: + +* `ext_id`: - (Optional) The globally unique identifier of a network function chain. It should be of type UUID. + +### Subnet + +The subnet attribute supports the following: + +* `ext_id`: - (Optional) The globally unique identifier of a subnet. It should be of type UUID. + +### IPV4 Config + +The ipv4_config attribute supports the following: + +* `should_assign_ip`: - (Optional) If set to true (default value), an IP address must be assigned to the VM NIC - either the one explicitly specified by the user or allocated automatically by the IPAM service by not specifying the IP address. If false, then no IP assignment is required for this VM NIC. +`ip_address`: - (Optional) Ip config settings. +`secondary_ip_address_list`: - (Optional) Secondary IP addresses for the NIC. + +### IP Address + +The ip_address attribute supports the following: + +* `prefix_length`: - (Optional) The prefix length of the network to which this host IPv4 address belongs. +* `value`: - Ip address. + +### Secondary IP Address List + +The secondary_ip_address_list attribute supports the following: + +* `prefix_length`: - (Optional) The prefix length of the network to which this host IPv4 address belongs. +* `value`: - Ip address. + +### Boot Config + +The boot_config attribute supports the following: + +* `legacy_config`: - (Optional) The Nutanix Legacy Boot Config settings. +* `uefi_config`: - (Optional) The Nutanix Uefi Boot Config settings. + +### Legacy Boot Config + +The legacy_boot attribute supports the following: + +* `boot_device`: - (Optional) The Boot Device settings. +* `boot_order`: - (Optional) Indicates the order of device types in which the VM should try to boot from. If the boot device order is not provided the system will decide an appropriate boot device order. + +### Boot Device + +The boot_device attribute supports the following: + +* `boot_device_disk`: - (Optional) The Boot Device Disk settings. +* `boot_device_nic`: - (Optional) The Boot Device Nic settings. + +### Boot Device Disk + +The boot_device_disk attribute supports the following: + +* `disk_address`: - (Optional) Address of disk to boot from. + + +### Disk Address + + The disk_address attribute supports the following: + +* `index`: - (Optional) Device index on the bus. This field is ignored unless the bus details are specified. +* `bus_type`: - (Optional) Bus type for the device. The acceptable values are: SCSI, IDE, PCI, SATA, SPAPR (only PPC). + Valid values are: + - `SCSI` The type of disk bus is SCSI. + - `SPAPR` The type of disk bus is SPAPR. + - `PCI` The type of disk bus is PCI. + - `PCI` The type of disk bus is PCI. + - `SATA` The type of disk bus is SATA. + +### Boot Device Nic + +The boot_device_nic attribute supports the following: + +* `mac_address`: - (Optional) MAC address of nic to boot from. + +### Uefi Boot Config + +The uefi_boot attribute supports the following: + +* `is_secure_boot_enabled`: - (Optional) Indicate whether to enable secure boot or not. +* `nvram_device`: - (Optional) Configuration for NVRAM to be presented to the VM. + +### Nvram Device + +The nvram_device attribute supports the following: + +* `backing_storage_info`: - (Optional) Storage provided by Nutanix ADSF. + +### Backing Storage Info + +The backing_storage_info attribute supports the following: + +* `disk_size_bytes`: - (Optional) Size of the disk in Bytes. +* `storage_container`: - (Optional) This reference is for disk level storage container preference. This preference specifies the storage container to which this disk belongs. +* `storage_config`: - (Optional) Storage configuration for VM disks. +* `data_source`: - (Optional) A reference to a disk or image that contains the contents of a disk. + +### Storage Container + +The storage_container attribute supports the following: + +* `ext_id`: - (Optional) The globally unique identifier of a VM disk container. It should be of type UUID. + +### Storage Config + +The storage_config attribute supports the following: + +* `is_flash_mode_enabled`: - (Optional) Indicates whether the virtual disk is pinned to the hot tier or not. + +### Data Source + +The data_source attribute supports the following: + +* `reference`: - (Optional) Data Source Reference settings. + +### Data Source Reference + +The reference attribute supports the following: + +* `image_reference`: - (Optional) Data Source Image Reference settings. +* `vm_disk_reference`: - (Optional) Data Source VM Disk Reference settings. + +### Image Reference + +The image_reference attribute supports the following: + +* `image_ext_id`: - (Optional) The globally unique identifier of an image. It should be of type UUID. + +### VM Disk Reference + +The vm_disk_reference attribute supports the following: + +* `disk_ext_id`: - (Optional) The globally unique identifier of a VM disk. It should be of type UUID. +* `disk_address`: - (Optional) Address of disk. +* `vm_reference`: - (Optional) Reference to a VM. + +### VM Reference + +The vm_reference attribute supports the following: + +* `ext_id`: - (Optional) The globally unique identifier of a VM. It should be of type UUID. + +### Guest Customization + +The guest_customization attribute supports the following: + +* `config`: - (Optional) The Nutanix Guest Tools customization settings. + +### Config + +The config attribute supports the following: + +* `sysprep`: - (Optional) VM guests may be customized at boot time using one of several different methods. Currently, cloud-init w/ ConfigDriveV2 (for Linux VMs) and Sysprep (for Windows VMs) are supported. Only ONE OF sysprep or cloud_init should be provided. Note that guest customization can currently only be set during VM creation. Attempting to change it after creation will result in an error. Additional properties can be specified. For example - in the context of VM template creation if \"override_script\" is set to \"True\" then the deployer can upload their own custom script. + +* `cloud_init`: - (Optional) VM guests may be customized at boot time using one of several different methods. Currently, cloud-init w/ ConfigDriveV2 (for Linux VMs) and Sysprep (for Windows VMs) are supported. Only ONE OF sysprep or cloud_init should be provided. Note that guest customization can currently only be set during VM creation. Attempting to change it after creation will result in an error. Additional properties can be specified. For example - in the context of VM template creation if \"override_script\" is set to \"True\" then the deployer can upload their own custom script. + +### Sysprep + +The sysprep attribute supports the following: + +* `install_type`: - (Optional) Whether the guest will be freshly installed using this unattend configuration, or whether this unattend configuration will be applied to a pre-prepared image. Default is `PREPARED`. + Valid values are: + - `PREPARED` is done when sysprep is used to finalize Windows installation from an installed Windows and file name it is searching `unattend.xml` for `unattend_xml` parameter + - `FRESH` is done when sysprep is used to install Windows from ISO and file name it is searching `autounattend.xml` for `unattend_xml` parameter +* `unattend_xml`: - (Optional) Generic key value pair used for custom attributes. + +### Cloud Init + +The cloud_init attribute supports the following: + +* `datasource_type`: - (Optional) Type of datasource. +Default: CONFIG_DRIVE_V2Default is `CONFIG_DRIVE_V2`. + Valid values are: + - `CONFIG_DRIVE_V2` The type of datasource for cloud-init is Config Drive V2. +* `metadata` - (Optional) The contents of the meta_data configuration for cloud-init. This can be formatted as YAML or JSON. The value must be base64 encoded. +* `cloud_init_script`: - (Optional) The script to use for cloud-init. + +### Cloud Init Script + +The cloud_init_script attribute supports the following: + +* `user_data`: - (Optional) The contents of the user_data configuration for cloud-init. This can be formatted as YAML, JSON, or could be a shell script. The value must be base64 encoded. +* `custom_key_values`: - (Optional) Generic key value pair used for custom attributes in cloud init. + +### User Data + +The user_data attribute supports the following: + +* `value`: - (Optional) The value for the cloud-init user_data. + +### Custom Key Values + +The custom_key_values attribute supports the following: + +* `key_value_pairs`: - (Optional) The list of the individual KeyValuePair elements. + +### Key Value Pairs + +The key_value_pairs attribute supports the following: + +* `name`: - (Optional) The key of this key-value pair +* `value`: - (Optional) The value associated with the key for this key-value pair. + +See detailed information in [Nutanix Virtual Machine](https://developers.nutanix.com/api-reference?namespace=vmm&version=v4.0.b1). + +## Import +Nutanix Virtual machines can be imported using the `UUID` eg, + +` +terraform import nutanix_virtual_machine_v4.vm01 0F75E6A7-55FB-44D9-A50D-14AD72E2CF7C +` diff --git a/website/docs/r/virtual_machine_gc_update_v2.html.markdown b/website/docs/r/virtual_machine_gc_update_v2.html.markdown new file mode 100644 index 000000000..fba95165e --- /dev/null +++ b/website/docs/r/virtual_machine_gc_update_v2.html.markdown @@ -0,0 +1,101 @@ +--- +layout: "nutanix" +page_title: "NUTANIX: nutanix_vm_gc_update" +sidebar_current: "docs-nutanix-resource-vm-gc-update" +description: |- + Provides a Nutanix Virtual Machine resource to Create a virtual machine guest customization update. +--- + +# nutanix_vm_gc_update + +Provides a Nutanix Virtual Machine resource to Create a virtual machine guest customization update. + +## Example Usage + +```hcl +data "nutanix_virtual_machines_v4" "vms" {} + +resource "nutanix_vm_gc_update_v4" "vm-gc-update" { + ext_id = data.nutanix_virtual_machines_v4.vms.0.data.ext_id + config{ + cloud_init{ + cloud_init_script{ + user_data{ + value="${local.gs}" + } + } + } + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `ext_id`: - (Required) The globally unique identifier of a VM. It should be of type UUID. +* `config`: - (Optional) The Nutanix Guest Tools customization settings. + +### Config + +The config attribute supports the following: + +* `sysprep`: - (Optional) VM guests may be customized at boot time using one of several different methods. Currently, cloud-init w/ ConfigDriveV2 (for Linux VMs) and Sysprep (for Windows VMs) are supported. Only ONE OF sysprep or cloud_init should be provided. Note that guest customization can currently only be set during VM creation. Attempting to change it after creation will result in an error. Additional properties can be specified. For example - in the context of VM template creation if \"override_script\" is set to \"True\" then the deployer can upload their own custom script. + +* `cloud_init`: - (Optional) VM guests may be customized at boot time using one of several different methods. Currently, cloud-init w/ ConfigDriveV2 (for Linux VMs) and Sysprep (for Windows VMs) are supported. Only ONE OF sysprep or cloud_init should be provided. Note that guest customization can currently only be set during VM creation. Attempting to change it after creation will result in an error. Additional properties can be specified. For example - in the context of VM template creation if \"override_script\" is set to \"True\" then the deployer can upload their own custom script. + +### Sysprep + +The sysprep attribute supports the following: + +* `install_type`: - (Optional) Whether the guest will be freshly installed using this unattend configuration, or whether this unattend configuration will be applied to a pre-prepared image. Default is `PREPARED`. + Valid values are: + - `PREPARED` is done when sysprep is used to finalize Windows installation from an installed Windows and file name it is searching `unattend.xml` for `unattend_xml` parameter + - `FRESH` is done when sysprep is used to install Windows from ISO and file name it is searching `autounattend.xml` for `unattend_xml` parameter +* `unattend_xml`: - (Optional) Generic key value pair used for custom attributes. + +### Cloud Init + +The cloud_init attribute supports the following: + +* `datasource_type`: - (Optional) Type of datasource. +Default: CONFIG_DRIVE_V2Default is `CONFIG_DRIVE_V2`. + Valid values are: + - `CONFIG_DRIVE_V2` The type of datasource for cloud-init is Config Drive V2. +* `metadata` - (Optional) The contents of the meta_data configuration for cloud-init. This can be formatted as YAML or JSON. The value must be base64 encoded. +* `cloud_init_script`: - (Optional) The script to use for cloud-init. + +### Cloud Init Script + +The cloud_init_script attribute supports the following: + +* `user_data`: - (Optional) The contents of the user_data configuration for cloud-init. This can be formatted as YAML, JSON, or could be a shell script. The value must be base64 encoded. +* `custom_key_values`: - (Optional) Generic key value pair used for custom attributes in cloud init. + +### User Data + +The user_data attribute supports the following: + +* `value`: - (Optional) The value for the cloud-init user_data. + +### Custom Key Values + +The custom_key_values attribute supports the following: + +* `key_value_pairs`: - (Optional) The list of the individual KeyValuePair elements. + +### Key Value Pairs + +The key_value_pairs attribute supports the following: + +* `name`: - (Optional) The key of this key-value pair +* `value`: - (Optional) The value associated with the key for this key-value pair. + +See detailed information in [Nutanix Virtual Machine](https://developers.nutanix.com/api-reference?namespace=vmm&version=v4.0.b1). + +## Import +Nutanix Virtual machines can be imported using the `UUID` eg, + +` +terraform import nutanix_virtual_machine_v4.vm01 0F75E6A7-55FB-44D9-A50D-14AD72E2CF7C +`