diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a306377d..b4d7522e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## 1.7.1 (August 31, 2022) +[Full Changelog](https://github.com/nutanix/terraform-provider-nutanix/compare/v1.7.0...v1.7.1) + +**Implemented enhancements:** + +- Support for /projects_internal API in nutanix projects [\#487](https://github.com/nutanix/terraform-provider-nutanix/pull/487) + +**Closed Issues:** +- project internal changes [\#488] (https://github.com/nutanix/terraform-provider-nutanix/pull/488) + + ## 1.7.0 (August 12, 2022) [Full Changelog](https://github.com/nutanix/terraform-provider-nutanix/compare/v1.6.1...v1.7.0) diff --git a/README.md b/README.md index 73d301d1c..fe7a8983b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Terraform provider plugin to integrate with Nutanix Enterprise Cloud -NOTE: The latest version of the Nutanix provider is [v1.7.0](https://github.com/nutanix/terraform-provider-nutanix/releases/tag/v1.7.0) +NOTE: The latest version of the Nutanix provider is [v1.7.1](https://github.com/nutanix/terraform-provider-nutanix/releases/tag/v1.7.1) Modules based on Terraform Nutanix Provider can be found here : [Modules](https://github.com/nutanix/terraform-provider-nutanix/tree/master/modules) ## Build, Quality Status @@ -44,12 +44,16 @@ The Terraform Nutanix provider is designed to work with Nutanix Prism Central an > For the 1.7.0 release of the provider it will have N-2 compatibility with the Prism Central APIs. This release was tested against Prism Central versions pc2022.6, pc2022.4 and pc2022.1.0.2. +> For the 1.7.1 release of the provider it will have N-2 compatibility with the Prism Central APIs. This release was tested against Prism Central versions pc2022.6, pc2022.4.0.1 and pc2022.1.0.2. + ### note With v1.6.1 release of flow networking feature in provider, IAMv2 setups would be mandate. Also, there is known issue for access_control_policies resource where update would be failing. We are continuously tracking the issue internally. with v1.7.0 release of user groups feature in provider, pc version should be minimum 2022.1 to support organisational and saml user group. +With v1.7.1 release of project internal in provider is supported. Note to use this, set "use_project_internal" to true. It also enables the ACP mapping with projects. + ## Foundation > For the 1.5.0-beta release of the provider it will have N-1 compatibility with the Foundation. This release was tested against Foundation versions v5.2 and v5.1.1 diff --git a/client/v3/v3_service.go b/client/v3/v3_service.go index 558c3b5bc..229da6620 100644 --- a/client/v3/v3_service.go +++ b/client/v3/v3_service.go @@ -142,6 +142,9 @@ type Service interface { ListAllFloatingIPs(ctx context.Context, filter string) (*FloatingIPsListIntentResponse, error) GetStaticRoute(ctx context.Context, vpcUUID string) (*StaticRouteIntentResponse, error) UpdateStaticRoute(ctx context.Context, uuid string, body *StaticRouteIntentInput) (*StaticRouteIntentResponse, error) + CreateProjectInternal(ctx context.Context, request *ProjectInternalIntentInput) (*ProjectInternalIntentResponse, error) + GetProjectInternal(ctx context.Context, uuid string) (*ProjectInternalIntentResponse, error) + UpdateProjectInternal(ctx context.Context, uuid string, body *ProjectInternalIntentInput) (*ProjectInternalIntentResponse, error) } /*CreateVM Creates a VM @@ -2822,3 +2825,39 @@ func (op Operations) ListAllFloatingIPs(ctx context.Context, filter string) (*Fl return resp, nil } + +func (op Operations) CreateProjectInternal(ctx context.Context, request *ProjectInternalIntentInput) (*ProjectInternalIntentResponse, error) { + req, err := op.client.NewRequest(ctx, http.MethodPost, "/projects_internal", request) + if err != nil { + return nil, err + } + + projectResponse := new(ProjectInternalIntentResponse) + + return projectResponse, op.client.Do(ctx, req, projectResponse) +} + +func (op Operations) GetProjectInternal(ctx context.Context, projectUUID string) (*ProjectInternalIntentResponse, error) { + path := fmt.Sprintf("/projects_internal/%s", projectUUID) + project := new(ProjectInternalIntentResponse) + + req, err := op.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, err + } + + return project, op.client.Do(ctx, req, project) +} + +func (op Operations) UpdateProjectInternal(ctx context.Context, uuid string, body *ProjectInternalIntentInput) (*ProjectInternalIntentResponse, error) { + path := fmt.Sprintf("/projects_internal/%s", uuid) + + projectInput := new(ProjectInternalIntentResponse) + + req, err := op.client.NewRequest(ctx, http.MethodPut, path, body) + if err != nil { + return nil, err + } + + return projectInput, op.client.Do(ctx, req, projectInput) +} diff --git a/client/v3/v3_structs.go b/client/v3/v3_structs.go index 611fed3da..e8a4622ef 100644 --- a/client/v3/v3_structs.go +++ b/client/v3/v3_structs.go @@ -2861,3 +2861,97 @@ type StaticRouteIntentResponse struct { Status *StaticRouteDefStatus `json:"status"` } + +// ProjectResources ... +type ProjectInternalResources struct { + ResourceDomain *ResourceDomain `json:"resource_domain,omitempty"` + AccountReferenceList []*ReferenceValues `json:"account_reference_list,omitempty"` + EnvironmentReferenceList []*ReferenceValues `json:"environment_reference_list,omitempty"` + DefaultSubnetReference *ReferenceValues `json:"default_subnet_reference,omitempty"` + UserReferenceList []*ReferenceValues `json:"user_reference_list,omitempty"` + TunnelReferenceList []*ReferenceValues `json:"tunnel_reference_list,omitempty"` + ExternalUserGroupReferenceList []*ReferenceValues `json:"external_user_group_reference_list,omitempty"` + ClusterReferenceList []*ReferenceValues `json:"cluster_reference_list,omitempty"` + SubnetReferenceList []*ReferenceValues `json:"subnet_reference_list,omitempty"` + VPCReferenceList []*ReferenceValues `json:"vpc_reference_list,omitempty"` + ExternalNetworkList []*ReferenceValues `json:"external_network_list,omitempty"` + DefaultEnvironmentReference *Reference `json:"default_environment_reference,omitempty"` + IsDefault bool `json:"is_default,omitempty"` +} + +type ProjectStatusResources struct { + Name *string `json:"name,omitempty"` + State *string `json:"state,omitempty"` + Reason *string `json:"reason,omitempty"` + Message *string `json:"message,omitempty"` + Resources *ProjectInternalResources `json:"resources,omitempty"` + Description *string `json:"description,omitempty"` +} + +type ProjectAccessControlPolicyStatus struct { + Name *string `json:"name,omitempty"` + IsSystemDefined bool `json:"is_system_defined,omitempty"` + State *string `json:"state,omitempty"` + MessageList []*MessageResource `json:"message_list,omitempty"` + Resources *AccessControlPolicyResources `json:"resources,omitempty"` + Description *string `json:"description,omitempty"` +} + +type ProjectAccessControlPolicyListStatus struct { + Metadata *Metadata `json:"metadata,omitempty"` + ProjectAccessControlPolicyStatus *ProjectAccessControlPolicyStatus `json:"access_control_policy_status,omitempty"` +} + +// ProjectStatus ... +type ProjectInternalStatus struct { + AccessControlPolicyListStatus []*ProjectAccessControlPolicyListStatus `json:"access_control_policy_list_status,omitempty"` + ProjectStatus *ProjectStatusResources `json:"project_status,omitempty"` + ExecutionContext *ExecutionContext `json:"execution_context,omitempty"` + State *string `json:"state,omitempty"` +} + +type ProjectDetails struct { + Name *string `json:"name,omitempty"` + Resources *ProjectInternalResources `json:"resources,omitempty"` + Description *string `json:"description,omitempty"` +} + +type AccessControlPolicyList struct { + Operation *string `json:"operation,omitempty"` + ACP *AccessControlPolicySpec `json:"acp,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` +} + +type UserList struct { + Operation *string `json:"operation,omitempty"` + User *UserSpec `json:"user,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` +} + +type UserGroupList struct { + Operation *string `json:"operation,omitempty"` + UserGroup *UserGroupSpec `json:"user_group,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` +} + +// ProjectSpec ... +type ProjectInternalSpec struct { + ProjectDetail *ProjectDetails `json:"project_detail,omitempty"` + AccessControlPolicyList []*AccessControlPolicyList `json:"access_control_policy_list,omitempty"` + UserList []*UserList `json:"user_list"` + UserGroupList []*UserGroupList `json:"user_group_list"` +} + +type ProjectInternalIntentInput struct { + Spec *ProjectInternalSpec `json:"spec,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` + APIVersion string `json:"api_version,omitempty"` +} + +// Project Response object for intentful operations on a Host +type ProjectInternalIntentResponse struct { + Status *ProjectInternalStatus `json:"status,omitempty"` + Spec *ProjectInternalSpec `json:"spec,omitempty"` + APIVersion string `json:"api_version,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` +} diff --git a/examples/projects/main.tf b/examples/projects/main.tf new file mode 100644 index 000000000..9b6e73144 --- /dev/null +++ b/examples/projects/main.tf @@ -0,0 +1,105 @@ +terraform{ + required_providers { + nutanix = { + source = "nutanix/nutanix" + version = "1.7.1" + } + } +} + +#defining nutanix configuration +provider "nutanix"{ + username = var.nutanix_username + password = var.nutanix_password + endpoint = var.nutanix_endpoint + port = 9440 + insecure = true +} + +# set use_project_internal flag to use user-role mapping + +data "nutanix_clusters" "clusters" {} + +locals { + cluster1 = [ + for cluster in data.nutanix_clusters.clusters.entities : + cluster.metadata.uuid if cluster.service_list[0] != "PRISM_CENTRAL" + ][0] +} + +# ### Define Terraform Managed Subnets +resource "nutanix_subnet" "infra-managed-network-140" { + # What cluster will this VLAN live on? + cluster_uuid = local.cluster1 + + # General Information + name = "infra-managed-network-140" + vlan_id = 140 + subnet_type = "VLAN" + + # Provision a Managed L3 Network + # This bit is only needed if you intend to turn on AHV's IPAM + subnet_ip = "10.xx.xx.xx" + + default_gateway_ip = "10.xx.xx.xx" + prefix_length = 24 + + dhcp_options = { + boot_file_name = "bootfile" + domain_name = "lab" + tftp_server_name = "10.xx.xx.xx" + } + + dhcp_server_address = { + ip = "10.xx.xx.xx" + } + + dhcp_domain_name_server_list = ["10.xx.xx.xx"] + dhcp_domain_search_list = ["ntnxlab.local"] + #ip_config_pool_list_ranges = ["10.xx.xx.xx 10.xx.xx.xx"] +} + +# Note: user reference and acp->user_reference should be same for mapping the role. Also whenever acp is given +# it's mandate to provide cluster_uuid to get the filter context list and scope of each defined user. + +resource "nutanix_project" "testp1" { + name = "testProj" + description = "test project description" + + # cluster uuid is required to map acp in projects + cluster_uuid = "${local.cluster1}" + + # set this use_project_internal flag for using projects_internal API + use_project_internal=true + + # set project collaboration, default it is true + enable_collab = true + default_subnet_reference{ + kind="subnet" + uuid=resource.nutanix_subnet.sub.id + } + user_reference_list{ + name= "{{user_name}}" + kind= "user" + uuid= "{{user_uuid}}" + } + subnet_reference_list{ + uuid=resource.nutanix_subnet.sub.id + } + acp{ + # acp name consists name_uuid string, it should be different for each acp. + name="{{acp_name}}" + role_reference{ + kind= "role" + uuid= "{{role_uuid}}" + name="Developer" + } + user_reference_list{ + name= "{{user_name}}" + kind= "user" + uuid= "{{user_uuid}}" + } + description= "descripton" + } + api_version = "3.1" +} diff --git a/examples/projects/terraform.tfvars b/examples/projects/terraform.tfvars new file mode 100644 index 000000000..867888ffc --- /dev/null +++ b/examples/projects/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/projects/variables.tf b/examples/projects/variables.tf new file mode 100644 index 000000000..dcd130ec8 --- /dev/null +++ b/examples/projects/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/nutanix/data_source_nutanix_project.go b/nutanix/data_source_nutanix_project.go index a4903d8ab..0df34f165 100644 --- a/nutanix/data_source_nutanix_project.go +++ b/nutanix/data_source_nutanix_project.go @@ -225,6 +225,278 @@ func dataSourceNutanixProject() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "tunnel_reference_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "cluster_reference_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "vpc_reference_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "default_environment_reference": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "acp": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "user_reference_list": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "user_group_reference_list": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "role_reference": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "context_filter_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "scope_filter_expression_list": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "left_hand_side": { + Type: schema.TypeString, + Computed: true, + }, + "operator": { + Type: schema.TypeString, + Computed: true, + }, + "right_hand_side": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "collection": { + Type: schema.TypeString, + Computed: true, + }, + "categories": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + "uuid_list": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + }, + }, + }, + "entity_filter_expression_list": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "left_hand_side_entity_type": { + Type: schema.TypeString, + Computed: true, + }, + "operator": { + Type: schema.TypeString, + Computed: true, + }, + "right_hand_side": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "collection": { + Type: schema.TypeString, + Computed: true, + }, + "categories": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + "uuid_list": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, }, } } @@ -240,13 +512,18 @@ func dataSourceNutanixProjectRead(ctx context.Context, d *schema.ResourceData, m } var err error - var project *v3.Project - + var er error + var project *v3.ProjectInternalIntentResponse + var projectID string if iok { - project, err = conn.V3.GetProject(id.(string)) + project, err = conn.V3.GetProjectInternal(ctx, id.(string)) } if nOk { - project, err = findProjectByName(conn, name.(string)) + projectID, er = findProjectByName(conn, name.(string)) + if er != nil { + return diag.FromErr(er) + } + project, err = conn.V3.GetProjectInternal(ctx, projectID) } if err != nil { @@ -255,41 +532,41 @@ func dataSourceNutanixProjectRead(ctx context.Context, d *schema.ResourceData, m m, c := setRSEntityMetadata(project.Metadata) - if err := d.Set("name", project.Status.Name); err != nil { + if err := d.Set("name", project.Status.ProjectStatus.Name); err != nil { return diag.Errorf("error setting `name` for Project(%s): %s", d.Id(), err) } - if err := d.Set("description", project.Status.Descripion); err != nil { + if err := d.Set("description", project.Status.ProjectStatus.Description); err != nil { return diag.Errorf("error setting `description` for Project(%s): %s", d.Id(), err) } if err := d.Set("state", project.Status.State); err != nil { return diag.Errorf("error setting `state` for Project(%s): %s", d.Id(), err) } - if err := d.Set("is_default", project.Status.Resources.IsDefault); err != nil { + if err := d.Set("is_default", project.Status.ProjectStatus.Resources.IsDefault); err != nil { return diag.Errorf("error setting `is_default` for Project(%s): %s", d.Id(), err) } - if err := d.Set("resource_domain", flattenResourceDomain(project.Spec.Resources.ResourceDomain)); err != nil { + if err := d.Set("resource_domain", flattenResourceDomain(project.Spec.ProjectDetail.Resources.ResourceDomain)); err != nil { return diag.Errorf("error setting `resource_domain` for Project(%s): %s", d.Id(), err) } - if err := d.Set("account_reference_list", flattenReferenceList(project.Spec.Resources.AccountReferenceList)); err != nil { + if err := d.Set("account_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.AccountReferenceList)); err != nil { return diag.Errorf("error setting `account_reference_list` for Project(%s): %s", d.Id(), err) } - if err := d.Set("environment_reference_list", flattenReferenceList(project.Spec.Resources.EnvironmentReferenceList)); err != nil { + if err := d.Set("environment_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.EnvironmentReferenceList)); err != nil { return diag.Errorf("error setting `environment_reference_list` for Project(%s): %s", d.Id(), err) } - if err := d.Set("default_subnet_reference", flattenReference(project.Spec.Resources.DefaultSubnetReference)); err != nil { + if err := d.Set("default_subnet_reference", flattenReference(project.Spec.ProjectDetail.Resources.DefaultSubnetReference)); err != nil { return diag.Errorf("error setting `default_subnet_reference` for Project(%s): %s", d.Id(), err) } - if err := d.Set("user_reference_list", flattenReferenceList(project.Spec.Resources.UserReferenceList)); err != nil { + if err := d.Set("user_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.UserReferenceList)); err != nil { return diag.Errorf("error setting `user_reference_list` for Project(%s): %s", d.Id(), err) } if err := d.Set("external_user_group_reference_list", - flattenReferenceList(project.Spec.Resources.ExternalUserGroupReferenceList)); err != nil { + flattenReferenceList(project.Spec.ProjectDetail.Resources.ExternalUserGroupReferenceList)); err != nil { return diag.Errorf("error setting `external_user_group_reference_list` for Project(%s): %s", d.Id(), err) } - if err := d.Set("subnet_reference_list", flattenReferenceList(project.Spec.Resources.SubnetReferenceList)); err != nil { + if err := d.Set("subnet_reference_list", flattenReferenceList(project.Status.ProjectStatus.Resources.SubnetReferenceList)); err != nil { return diag.Errorf("error setting `subnet_reference_list` for Project(%s): %s", d.Id(), err) } - if err := d.Set("external_network_list", flattenExternalNetworkListReferenceList(project.Spec.Resources.ExternalNetworkList)); err != nil { + if err := d.Set("external_network_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.ExternalNetworkList)); err != nil { return diag.Errorf("error setting `external_network_list` for Project(%s): %s", d.Id(), err) } if err := d.Set("metadata", m); err != nil { @@ -308,16 +585,32 @@ func dataSourceNutanixProjectRead(ctx context.Context, d *schema.ResourceData, m return diag.Errorf("error setting `api_version` for Project(%s): %s", d.Id(), err) } + if err := d.Set("tunnel_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.TunnelReferenceList)); err != nil { + return diag.Errorf("error setting `tunnel_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("vpc_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.VPCReferenceList)); err != nil { + return diag.Errorf("error setting `vpc_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("cluster_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.ClusterReferenceList)); err != nil { + return diag.Errorf("error setting `cluster_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("default_environment_reference", flattenReferenceValuesList(project.Spec.ProjectDetail.Resources.DefaultEnvironmentReference)); err != nil { + return diag.Errorf("error setting `default_environment_reference` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("acp", flattenProjectAcp(project.Status.AccessControlPolicyListStatus)); err != nil { + return diag.Errorf("error setting `acp` for Project(%s): %s", d.Id(), err) + } + d.SetId(utils.StringValue(project.Metadata.UUID)) return nil } -func findProjectByName(conn *v3.Client, name string) (*v3.Project, error) { +func findProjectByName(conn *v3.Client, name string) (string, error) { filter := fmt.Sprintf("name==%s", name) resp, err := conn.V3.ListAllProject(filter) if err != nil { - return nil, err + return "nil", err } entities := resp.Entities @@ -330,12 +623,12 @@ func findProjectByName(conn *v3.Client, name string) (*v3.Project, error) { } if len(found) > 1 { - return nil, fmt.Errorf("your query returned more than one result. Please use project_id argument instead") + return "nil", fmt.Errorf("your query returned more than one result. Please use project_id argument instead") } if len(found) == 0 { - return nil, fmt.Errorf("project with the given name, not found") + return "nil", fmt.Errorf("project with the given name, not found") } - return found[0], nil + return utils.StringValue(found[0].Metadata.UUID), nil } diff --git a/nutanix/resource_nutanix_project.go b/nutanix/resource_nutanix_project.go index 66f20ae91..426171c72 100644 --- a/nutanix/resource_nutanix_project.go +++ b/nutanix/resource_nutanix_project.go @@ -10,6 +10,7 @@ import ( "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" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/spf13/cast" v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" "github.com/terraform-providers/terraform-provider-nutanix/utils" @@ -46,6 +47,10 @@ func resourceNutanixProject() *schema.Resource { Type: schema.TypeBool, Computed: true, }, + "enable_collab": { + Type: schema.TypeBool, + Optional: true, + }, "resource_domain": { Type: schema.TypeList, Optional: true, @@ -235,6 +240,103 @@ func resourceNutanixProject() *schema.Resource { }, }, }, + "tunnel_reference_list": { + Type: schema.TypeList, + Optional: true, + Computed: true, + RequiredWith: []string{"use_project_internal"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + Default: "tunnel", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "cluster_reference_list": { + Type: schema.TypeList, + Optional: true, + Computed: true, + RequiredWith: []string{"use_project_internal"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + Default: "cluster", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "vpc_reference_list": { + Type: schema.TypeList, + Optional: true, + Computed: true, + RequiredWith: []string{"use_project_internal"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + Default: "vpc", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "default_environment_reference": { + Type: schema.TypeList, + Optional: true, + Computed: true, + RequiredWith: []string{"use_project_internal"}, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + Default: "environment", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, "metadata": { Type: schema.TypeMap, Computed: true, @@ -264,174 +366,859 @@ func resourceNutanixProject() *schema.Resource { Optional: true, Computed: true, }, + "use_project_internal": { + Type: schema.TypeBool, + Optional: true, + }, + "acp": { + Type: schema.TypeList, + Optional: true, + RequiredWith: []string{"use_project_internal"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "user_reference_list": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "user_group_reference_list": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "role_reference": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"role"}, false), + }, + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "context_filter_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "scope_filter_expression_list": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "left_hand_side": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"CATEGORY", "PROJECT"}, false), + }, + "operator": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"IN", "IN_ALL", "NOT_IN"}, false), + }, + "right_hand_side": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "collection": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"ALL"}, false), + }, + "categories": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "value": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + "uuid_list": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + }, + }, + }, + "entity_filter_expression_list": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "left_hand_side_entity_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: utils.StringLowerCaseValidateFunc, + }, + "operator": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"IN", "NOT_IN"}, false), + }, + "right_hand_side": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "collection": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"ALL", "SELF_OWNED"}, false), + }, + "categories": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "value": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + "uuid_list": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "user_list": { + Type: schema.TypeList, + Optional: true, + RequiredWith: []string{"use_project_internal"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "directory_service_user": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_principal_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "directory_service_reference": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + Default: "directory_service", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "default_user_principal_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "identity_provider_user": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "username": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "identity_provider_reference": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Optional: true, + Default: "identity_provider", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "metadata": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "user_group_list": { + Type: schema.TypeList, + Optional: true, + RequiredWith: []string{"use_project_internal"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "directory_service_user_group": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "distinguished_name": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "saml_user_group": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "idp_uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "directory_service_ou": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "distinguished_name": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "metadata": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "cluster_uuid": { + Type: schema.TypeString, + Optional: true, + }, }, } } func resourceNutanixProjectCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*Client).API + uuid := "" + taskUUID := "" + // if use project internal flag is set , we will use projects_internal API + if _, ok := d.GetOkExists("use_project_internal"); ok { + req := &v3.ProjectInternalIntentInput{ + Spec: expandProjectInternalSpec(d, meta), + Metadata: expandMetadata(d, "project"), + APIVersion: d.Get("api_version").(string), + } - req := &v3.Project{ - Spec: expandProjectSpec(d), - Metadata: expandMetadata(d, "project"), - APIVersion: d.Get("api_version").(string), - } + resp, err := conn.V3.CreateProjectInternal(ctx, req) + if err != nil { + return diag.FromErr(err) + } - resp, err := conn.V3.CreateProject(req) - if err != nil { - return diag.FromErr(err) - } + uuid = *resp.Metadata.UUID + taskUUID = resp.Status.ExecutionContext.TaskUUID.(string) - uuid := *resp.Metadata.UUID - taskUUID := resp.Status.ExecutionContext.TaskUUID.(string) + // Wait for the Project to be available + stateConf := &resource.StateChangeConf{ + Pending: []string{"QUEUED", "RUNNING"}, + Target: []string{"SUCCEEDED"}, + Refresh: taskStateRefreshFunc(conn, taskUUID), + Timeout: d.Timeout(schema.TimeoutCreate), + Delay: vmDelay, + MinTimeout: vmMinTimeout, + } - // Wait for the Project to be available - stateConf := &resource.StateChangeConf{ - Pending: []string{"QUEUED", "RUNNING"}, - Target: []string{"SUCCEEDED"}, - Refresh: taskStateRefreshFunc(conn, taskUUID), - Timeout: d.Timeout(schema.TimeoutCreate), - Delay: vmDelay, - MinTimeout: vmMinTimeout, - } + if _, errWaitTask := stateConf.WaitForStateContext(ctx); errWaitTask != nil { + return diag.Errorf("error waiting for project(%s) to create: %s", uuid, errWaitTask) + } - if _, errWaitTask := stateConf.WaitForStateContext(ctx); errWaitTask != nil { - return diag.Errorf("error waiting for project(%s) to create: %s", uuid, errWaitTask) - } + d.SetId(uuid) - d.SetId(uuid) - return resourceNutanixProjectRead(ctx, d, meta) -} + // once project is created , create acp . + // check if acp is given in resource + if _, ok1 := d.GetOk("acp"); ok1 { + request := &v3.ProjectInternalIntentInput{} + spec := &v3.ProjectInternalSpec{} + metadata := &v3.Metadata{} + projDetails := &v3.ProjectDetails{} + accessControlPolicy := make([]*v3.AccessControlPolicyList, 0) -func resourceNutanixProjectRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*Client).API + var clusterUUID string + if clusterID, ok := d.GetOk("cluster_uuid"); ok { + clusterUUID = clusterID.(string) + } + response, err := conn.V3.GetProjectInternal(ctx, d.Id()) + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + return nil + } + return diag.Errorf("error reading Project %s: %s", d.Id(), err) + } + if response.Metadata != nil { + metadata = response.Metadata + } - project, err := conn.V3.GetProject(d.Id()) - if err != nil { - if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { - d.SetId("") - return nil - } - return diag.FromErr(err) - } + if response.Spec != nil { + spec = response.Spec - m, c := setRSEntityMetadata(project.Metadata) + if response.Spec.ProjectDetail != nil || response.Spec.AccessControlPolicyList != nil { + projDetails = response.Spec.ProjectDetail + accessControlPolicy = response.Spec.AccessControlPolicyList + } - if err := d.Set("name", project.Status.Name); err != nil { - return diag.Errorf("error setting `name` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("description", project.Status.Descripion); err != nil { - return diag.Errorf("error setting `description` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("state", project.Status.State); err != nil { - return diag.Errorf("error setting `state` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("is_default", project.Status.Resources.IsDefault); err != nil { - return diag.Errorf("error setting `is_default` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("resource_domain", flattenResourceDomain(project.Spec.Resources.ResourceDomain)); err != nil { - return diag.Errorf("error setting `resource_domain` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("account_reference_list", flattenReferenceList(project.Spec.Resources.AccountReferenceList)); err != nil { - return diag.Errorf("error setting `account_reference_list` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("environment_reference_list", flattenReferenceList(project.Spec.Resources.EnvironmentReferenceList)); err != nil { - return diag.Errorf("error setting `environment_reference_list` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("default_subnet_reference", []interface{}{flattenReference(project.Spec.Resources.DefaultSubnetReference)}); err != nil { - return diag.Errorf("error setting `default_subnet_reference` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("user_reference_list", flattenReferenceList(project.Spec.Resources.UserReferenceList)); err != nil { - return diag.Errorf("error setting `user_reference_list` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("external_user_group_reference_list", - flattenReferenceList(project.Spec.Resources.ExternalUserGroupReferenceList)); err != nil { - return diag.Errorf("error setting `external_user_group_reference_list` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("subnet_reference_list", flattenReferenceList(project.Spec.Resources.SubnetReferenceList)); err != nil { - return diag.Errorf("error setting `subnet_reference_list` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("external_network_list", flattenReferenceList(project.Spec.Resources.ExternalNetworkList)); err != nil { - return diag.Errorf("error setting `external_network_list` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("metadata", m); err != nil { - return diag.Errorf("error setting `metadata` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("project_reference", flattenReferenceValues(project.Metadata.ProjectReference)); err != nil { - return diag.Errorf("error setting `project_reference` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("owner_reference", flattenReferenceValues(project.Metadata.OwnerReference)); err != nil { - return diag.Errorf("error setting `owner_reference` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("categories", c); err != nil { - return diag.Errorf("error setting `categories` for Project(%s): %s", d.Id(), err) - } - if err := d.Set("api_version", project.APIVersion); err != nil { - return diag.Errorf("error setting `api_version` for Project(%s): %s", d.Id(), err) + if len(response.Spec.ProjectDetail.Resources.ResourceDomain.Resources) > 0 { + projDetails.Resources.ResourceDomain = response.Spec.ProjectDetail.Resources.ResourceDomain + } else { + projDetails.Resources.ResourceDomain = nil + } + } + + if acp, ok := d.GetOk("acp"); ok { + accessControlPolicy = expandCreateAcp(acp.([]interface{}), d, d.Id(), clusterUUID, meta) + } + spec.AccessControlPolicyList = accessControlPolicy + spec.ProjectDetail = projDetails + + request.Spec = spec + request.Metadata = metadata + request.APIVersion = response.APIVersion + + UpdateResp, err := conn.V3.UpdateProjectInternal(ctx, d.Id(), request) + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + } + return diag.FromErr(err) + } + + uuid = *UpdateResp.Metadata.UUID + taskUUID = UpdateResp.Status.ExecutionContext.TaskUUID.(string) + // Wait for the Project to be available + UpstateConf := &resource.StateChangeConf{ + Pending: []string{"QUEUED", "RUNNING"}, + Target: []string{"SUCCEEDED"}, + Refresh: taskStateRefreshFunc(conn, taskUUID), + Timeout: d.Timeout(schema.TimeoutUpdate), + Delay: vmDelay, + MinTimeout: vmMinTimeout, + } + + if _, errWaitTask := UpstateConf.WaitForStateContext(ctx); errWaitTask != nil { + return diag.Errorf("error waiting for project(%s) to update: %s", uuid, errWaitTask) + } + } + } else { + req := &v3.Project{ + Spec: expandProjectSpec(d), + Metadata: expandMetadata(d, "project"), + APIVersion: d.Get("api_version").(string), + } + + resp, err := conn.V3.CreateProject(req) + if err != nil { + return diag.FromErr(err) + } + + uuid = *resp.Metadata.UUID + taskUUID = resp.Status.ExecutionContext.TaskUUID.(string) + + // Wait for the Project to be available + stateConf := &resource.StateChangeConf{ + Pending: []string{"QUEUED", "RUNNING"}, + Target: []string{"SUCCEEDED"}, + Refresh: taskStateRefreshFunc(conn, taskUUID), + Timeout: d.Timeout(schema.TimeoutCreate), + Delay: vmDelay, + MinTimeout: vmMinTimeout, + } + + if _, errWaitTask := stateConf.WaitForStateContext(ctx); errWaitTask != nil { + return diag.Errorf("error waiting for project(%s) to create: %s", uuid, errWaitTask) + } + + d.SetId(uuid) } + return resourceNutanixProjectRead(ctx, d, meta) +} + +func resourceNutanixProjectRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*Client).API + + if _, ok := d.GetOkExists("use_project_internal"); ok { + project, err := conn.V3.GetProjectInternal(ctx, d.Id()) + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + return nil + } + return diag.FromErr(err) + } + m, c := setRSEntityMetadata(project.Metadata) + + if err := d.Set("name", project.Status.ProjectStatus.Name); err != nil { + return diag.Errorf("error setting `name` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("description", project.Status.ProjectStatus.Description); err != nil { + return diag.Errorf("error setting `description` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("state", project.Status.State); err != nil { + return diag.Errorf("error setting `state` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("is_default", project.Status.ProjectStatus.Resources.IsDefault); err != nil { + return diag.Errorf("error setting `is_default` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("resource_domain", flattenResourceDomain(project.Spec.ProjectDetail.Resources.ResourceDomain)); err != nil { + return diag.Errorf("error setting `resource_domain` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("account_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.AccountReferenceList)); err != nil { + return diag.Errorf("error setting `account_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("environment_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.EnvironmentReferenceList)); err != nil { + return diag.Errorf("error setting `environment_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("default_subnet_reference", []interface{}{flattenReference(project.Spec.ProjectDetail.Resources.DefaultSubnetReference)}); err != nil { + return diag.Errorf("error setting `default_subnet_reference` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("user_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.UserReferenceList)); err != nil { + return diag.Errorf("error setting `user_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("external_user_group_reference_list", + flattenReferenceList(project.Spec.ProjectDetail.Resources.ExternalUserGroupReferenceList)); err != nil { + return diag.Errorf("error setting `external_user_group_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("subnet_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.SubnetReferenceList)); err != nil { + return diag.Errorf("error setting `subnet_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("external_network_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.ExternalNetworkList)); err != nil { + return diag.Errorf("error setting `external_network_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("metadata", m); err != nil { + return diag.Errorf("error setting `metadata` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("project_reference", flattenReferenceValues(project.Metadata.ProjectReference)); err != nil { + return diag.Errorf("error setting `project_reference` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("owner_reference", flattenReferenceValues(project.Metadata.OwnerReference)); err != nil { + return diag.Errorf("error setting `owner_reference` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("categories", c); err != nil { + return diag.Errorf("error setting `categories` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("api_version", project.APIVersion); err != nil { + return diag.Errorf("error setting `api_version` for Project(%s): %s", d.Id(), err) + } + + if err := d.Set("tunnel_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.TunnelReferenceList)); err != nil { + return diag.Errorf("error setting `tunnel_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("vpc_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.VPCReferenceList)); err != nil { + return diag.Errorf("error setting `vpc_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("cluster_reference_list", flattenReferenceList(project.Spec.ProjectDetail.Resources.ClusterReferenceList)); err != nil { + return diag.Errorf("error setting `cluster_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("default_environment_reference", flattenReferenceValuesList(project.Spec.ProjectDetail.Resources.DefaultEnvironmentReference)); err != nil { + return diag.Errorf("error setting `default_environment_reference` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("acp", flattenProjectAcp(project.Status.AccessControlPolicyListStatus)); err != nil { + return diag.Errorf("error setting `acp` for Project(%s): %s", d.Id(), err) + } + } else { + project, err := conn.V3.GetProject(d.Id()) + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + return nil + } + return diag.FromErr(err) + } + + m, c := setRSEntityMetadata(project.Metadata) + + if err := d.Set("name", project.Status.Name); err != nil { + return diag.Errorf("error setting `name` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("description", project.Status.Descripion); err != nil { + return diag.Errorf("error setting `description` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("state", project.Status.State); err != nil { + return diag.Errorf("error setting `state` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("is_default", project.Status.Resources.IsDefault); err != nil { + return diag.Errorf("error setting `is_default` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("resource_domain", flattenResourceDomain(project.Spec.Resources.ResourceDomain)); err != nil { + return diag.Errorf("error setting `resource_domain` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("account_reference_list", flattenReferenceList(project.Spec.Resources.AccountReferenceList)); err != nil { + return diag.Errorf("error setting `account_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("environment_reference_list", flattenReferenceList(project.Spec.Resources.EnvironmentReferenceList)); err != nil { + return diag.Errorf("error setting `environment_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("default_subnet_reference", []interface{}{flattenReference(project.Spec.Resources.DefaultSubnetReference)}); err != nil { + return diag.Errorf("error setting `default_subnet_reference` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("user_reference_list", flattenReferenceList(project.Spec.Resources.UserReferenceList)); err != nil { + return diag.Errorf("error setting `user_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("external_user_group_reference_list", + flattenReferenceList(project.Spec.Resources.ExternalUserGroupReferenceList)); err != nil { + return diag.Errorf("error setting `external_user_group_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("subnet_reference_list", flattenReferenceList(project.Spec.Resources.SubnetReferenceList)); err != nil { + return diag.Errorf("error setting `subnet_reference_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("external_network_list", flattenReferenceList(project.Spec.Resources.ExternalNetworkList)); err != nil { + return diag.Errorf("error setting `external_network_list` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("metadata", m); err != nil { + return diag.Errorf("error setting `metadata` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("project_reference", flattenReferenceValues(project.Metadata.ProjectReference)); err != nil { + return diag.Errorf("error setting `project_reference` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("owner_reference", flattenReferenceValues(project.Metadata.OwnerReference)); err != nil { + return diag.Errorf("error setting `owner_reference` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("categories", c); err != nil { + return diag.Errorf("error setting `categories` for Project(%s): %s", d.Id(), err) + } + if err := d.Set("api_version", project.APIVersion); err != nil { + return diag.Errorf("error setting `api_version` for Project(%s): %s", d.Id(), err) + } + } return nil } func resourceNutanixProjectUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*Client).API + uuid := "" + taskUUID := "" - project, err := conn.V3.GetProject(d.Id()) - if err != nil { - return diag.FromErr(err) - } - project.Status = nil + if _, ok := d.GetOkExists("use_project_internal"); ok { + request := &v3.ProjectInternalIntentInput{} + spec := &v3.ProjectInternalSpec{} + metadata := &v3.Metadata{} + projDetails := &v3.ProjectDetails{} + var accessControlPolicy []*v3.AccessControlPolicyList - if d.HasChange("name") { - project.Spec.Name = d.Get("name").(string) - } - if d.HasChange("description") { - project.Spec.Descripion = d.Get("description").(string) - } - if d.HasChange("resource_domain") { - project.Spec.Resources.ResourceDomain = expandResourceDomain(d) - } - if d.HasChange("account_reference_list") { - project.Spec.Resources.AccountReferenceList = expandReferenceList(d, "account_reference_list") - } - if d.HasChange("environment_reference_list") { - project.Spec.Resources.EnvironmentReferenceList = expandReferenceList(d, "environment_reference_list") - } - if d.HasChange("default_subnet_reference") { - project.Spec.Resources.DefaultSubnetReference = expandReferenceList(d, "default_subnet_reference")[0] - } - if d.HasChange("user_reference_list") { - project.Spec.Resources.UserReferenceList = expandReferenceSet(d, "user_reference_list") - } - if d.HasChange("external_user_group_reference_list") { - project.Spec.Resources.ExternalUserGroupReferenceList = expandReferenceSet(d, "external_user_group_reference_list") - } - if d.HasChange("subnet_reference_list") { - project.Spec.Resources.SubnetReferenceList = expandReferenceList(d, "subnet_reference_list") - } - if d.HasChange("external_network_list") { - project.Spec.Resources.ExternalNetworkList = expandReferenceList(d, "external_network_list") - } - if d.HasChange("metadata") || d.HasChange("project_reference") || - d.HasChange("owner_reference") || d.HasChange("categories") { - if err = getMetadataAttributes(d, project.Metadata, "project"); err != nil { - return diag.Errorf("error expanding metadata: %+v", err) + response, err := conn.V3.GetProjectInternal(ctx, d.Id()) + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + return nil + } + return diag.Errorf("error reading Project %s: %s", d.Id(), err) + } + if response.Metadata != nil { + metadata = response.Metadata } - } - if d.HasChange("api_version") { - project.APIVersion = d.Get("api_version").(string) - } - resp, err := conn.V3.UpdateProject(d.Id(), project) - if err != nil { - if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { - d.SetId("") + if response.Spec != nil { + spec = response.Spec + + if response.Spec.ProjectDetail != nil || response.Spec.AccessControlPolicyList != nil { + projDetails = response.Spec.ProjectDetail + } + + if len(response.Spec.ProjectDetail.Resources.ResourceDomain.Resources) > 0 { + projDetails.Resources.ResourceDomain = response.Spec.ProjectDetail.Resources.ResourceDomain + } else { + projDetails.Resources.ResourceDomain = nil + } + } + var clusterUUID string + if clusterID, ok := d.GetOk("cluster_uuid"); ok { + clusterUUID = clusterID.(string) + } + + if d.HasChange("name") { + projDetails.Name = utils.StringPtr(d.Get("name").(string)) + } + if d.HasChange("description") { + projDetails.Description = utils.StringPtr(d.Get("description").(string)) + } + if d.HasChange("resource_domain") { + projDetails.Resources.ResourceDomain = expandProjectInternalResourceDomain(d.Get("resource_domain")) + } + if d.HasChange("account_reference_list") { + projDetails.Resources.AccountReferenceList = expandReferenceList(d, "account_reference_list") + } + if d.HasChange("environment_reference_list") { + projDetails.Resources.EnvironmentReferenceList = expandReferenceList(d, "environment_reference_list") + } + if d.HasChange("default_subnet_reference") { + projDetails.Resources.DefaultSubnetReference = expandReferenceList(d, "default_subnet_reference")[0] + } + if d.HasChange("user_reference_list") { + projDetails.Resources.UserReferenceList = expandReferenceSet(d, "user_reference_list") + } + if d.HasChange("external_user_group_reference_list") { + projDetails.Resources.ExternalUserGroupReferenceList = expandReferenceSet(d, "external_user_group_reference_list") + } + if d.HasChange("subnet_reference_list") { + projDetails.Resources.SubnetReferenceList = expandReferenceList(d, "subnet_reference_list") + } + if d.HasChange("external_network_list") { + projDetails.Resources.ExternalNetworkList = expandReferenceList(d, "external_network_list") + } + if d.HasChange("tunnel_reference_list") { + projDetails.Resources.TunnelReferenceList = expandReferenceList(d, "tunnel_reference_list") + } + if d.HasChange("vpc_reference_list") { + projDetails.Resources.VPCReferenceList = expandReferenceList(d, "vpc_reference_list") + } + if d.HasChange("cluster_reference_list") { + projDetails.Resources.ClusterReferenceList = expandReferenceList(d, "cluster_reference_list") + } + if d.HasChange("default_environment_reference") { + projDetails.Resources.DefaultEnvironmentReference = expandOptionalReference(d, "default_environment_reference", "environment") + } + + if d.HasChange("metadata") || d.HasChange("project_reference") || + d.HasChange("owner_reference") || d.HasChange("categories") { + if err = getMetadataAttributes(d, response.Metadata, "project"); err != nil { + return diag.Errorf("error expanding metadata: %+v", err) + } + } + if d.HasChange("api_version") { + response.APIVersion = d.Get("api_version").(string) + } + + if d.HasChange("acp") { + acp := d.Get("acp") + accessControlPolicy = UpdateExpandAcpRM(acp.([]interface{}), response, d, meta, d.Id(), clusterUUID) + } else { + accessControlPolicy = UpdateACPNoChange(response) } - return diag.FromErr(err) - } - uuid := *resp.Metadata.UUID - taskUUID := resp.Status.ExecutionContext.TaskUUID.(string) + spec.AccessControlPolicyList = accessControlPolicy + spec.ProjectDetail = projDetails + + request.Spec = spec + request.Metadata = metadata + request.APIVersion = response.APIVersion + + resp, err := conn.V3.UpdateProjectInternal(ctx, d.Id(), request) + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + } + return diag.FromErr(err) + } + + uuid = *resp.Metadata.UUID + taskUUID = resp.Status.ExecutionContext.TaskUUID.(string) + } else { + project, err := conn.V3.GetProject(d.Id()) + if err != nil { + return diag.FromErr(err) + } + project.Status = nil + + if d.HasChange("name") { + project.Spec.Name = d.Get("name").(string) + } + if d.HasChange("description") { + project.Spec.Descripion = d.Get("description").(string) + } + if d.HasChange("resource_domain") { + project.Spec.Resources.ResourceDomain = expandResourceDomain(d) + } + if d.HasChange("account_reference_list") { + project.Spec.Resources.AccountReferenceList = expandReferenceList(d, "account_reference_list") + } + if d.HasChange("environment_reference_list") { + project.Spec.Resources.EnvironmentReferenceList = expandReferenceList(d, "environment_reference_list") + } + if d.HasChange("default_subnet_reference") { + project.Spec.Resources.DefaultSubnetReference = expandReferenceList(d, "default_subnet_reference")[0] + } + if d.HasChange("user_reference_list") { + project.Spec.Resources.UserReferenceList = expandReferenceSet(d, "user_reference_list") + } + if d.HasChange("external_user_group_reference_list") { + project.Spec.Resources.ExternalUserGroupReferenceList = expandReferenceSet(d, "external_user_group_reference_list") + } + if d.HasChange("subnet_reference_list") { + project.Spec.Resources.SubnetReferenceList = expandReferenceList(d, "subnet_reference_list") + } + if d.HasChange("external_network_list") { + project.Spec.Resources.ExternalNetworkList = expandReferenceList(d, "external_network_list") + } + if d.HasChange("metadata") || d.HasChange("project_reference") || + d.HasChange("owner_reference") || d.HasChange("categories") { + if err = getMetadataAttributes(d, project.Metadata, "project"); err != nil { + return diag.Errorf("error expanding metadata: %+v", err) + } + } + if d.HasChange("api_version") { + project.APIVersion = d.Get("api_version").(string) + } + + resp, err := conn.V3.UpdateProject(d.Id(), project) + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + } + return diag.FromErr(err) + } + + uuid = *resp.Metadata.UUID + taskUUID = resp.Status.ExecutionContext.TaskUUID.(string) + } // Wait for the Project to be available stateConf := &resource.StateChangeConf{ @@ -537,10 +1324,6 @@ func expandReferenceByMap(reference map[string]interface{}) *v3.ReferenceValues } } -// func expandReference(d *schema.ResourceData, key string) *v3.ReferenceValues { -// return expandReferenceByMap(cast.ToStringMap(d.Get(key))) -// } - func expandReferenceList(d *schema.ResourceData, key string) []*v3.ReferenceValues { references := d.Get(key).([]interface{}) list := make([]*v3.ReferenceValues, len(references)) @@ -569,3 +1352,509 @@ func expandMetadata(d *schema.ResourceData, kind string) *v3.Metadata { } return metadata } + +func expandProjectDetails(d *schema.ResourceData) *v3.ProjectDetails { + return &v3.ProjectDetails{ + Name: utils.StringPtr(d.Get("name").(string)), + Description: utils.StringPtr(d.Get("description").(string)), + Resources: &v3.ProjectInternalResources{ + ResourceDomain: expandResourceDomain(d), + AccountReferenceList: expandReferenceList(d, "account_reference_list"), + EnvironmentReferenceList: expandReferenceList(d, "environment_reference_list"), + DefaultSubnetReference: expandReferenceList(d, "default_subnet_reference")[0], + UserReferenceList: expandReferenceSet(d, "user_reference_list"), + ExternalUserGroupReferenceList: expandReferenceSet(d, "external_user_group_reference_list"), + SubnetReferenceList: expandReferenceList(d, "subnet_reference_list"), + ExternalNetworkList: expandReferenceList(d, "external_network_list"), + TunnelReferenceList: expandReferenceList(d, "tunnel_reference_list"), + ClusterReferenceList: expandReferenceList(d, "cluster_reference_list"), + VPCReferenceList: expandReferenceList(d, "vpc_reference_list"), + DefaultEnvironmentReference: expandOptionalReference(d, "default_environment_reference", "environment"), + }, + } +} + +func expandProjectInternalSpec(d *schema.ResourceData, meta interface{}) *v3.ProjectInternalSpec { + proSpec := &v3.ProjectInternalSpec{} + accessControlPolicyList := []*v3.AccessControlPolicyList{} + userList := make([]*v3.UserList, 0) + userGroupList := make([]*v3.UserGroupList, 0) + + projDetail := expandProjectDetails(d) + + if user, ok := d.GetOk("user_list"); ok { + userList = expandUser(user.([]interface{}), d, projDetail.Resources.UserReferenceList, meta) + } + + if usergroup, ok := d.GetOk("user_group_list"); ok { + userGroupList = expandUserGroup(usergroup.([]interface{}), d, projDetail.Resources.ExternalUserGroupReferenceList, meta) + } + + proSpec.ProjectDetail = projDetail + proSpec.AccessControlPolicyList = accessControlPolicyList + proSpec.UserList = userList + proSpec.UserGroupList = userGroupList + + return proSpec +} + +func expandAcp(pr []interface{}, d *schema.ResourceData) []*v3.AccessControlPolicyList { + if len(pr) > 0 { + acpList := make([]*v3.AccessControlPolicyList, len(pr)) + + for k, val := range pr { + acps := &v3.AccessControlPolicyList{} + acpSpec := &v3.AccessControlPolicySpec{} + acpRes := &v3.AccessControlPolicyResources{} + + v := val.(map[string]interface{}) + + if v1, ok1 := v["operation"]; ok1 { + acps.Operation = utils.StringPtr(v1.(string)) + } + if v1, ok1 := v["name"]; ok1 { + acpSpec.Name = utils.StringPtr(v1.(string)) + } + if v1, ok1 := v["description"]; ok1 { + acpSpec.Description = utils.StringPtr(v1.(string)) + } + if v, ok := v["user_reference_list"]; ok { + acpRes.UserReferenceList = validateArrayRef(v.(*schema.Set), utils.StringPtr("user")) + } + + if v, ok := v["user_group_reference_list"]; ok { + acpRes.UserGroupReferenceList = validateArrayRef(v.(*schema.Set), utils.StringPtr("user_group")) + } + + if v, ok := v["role_reference"]; ok { + acpRes.RoleReference = validateRefList(v.([]interface{}), nil) + } + + if cfl, ok := v["context_filter_list"]; ok && len(cfl.([]interface{})) > 0 { + filterList := &v3.FilterList{} + filterList.ContextList = expandProjectContextFilterList(cfl) + if filterList.ContextList != nil { + acpRes.FilterList = filterList + } + } + + metadata := &v3.Metadata{} + if err := getMetadataAttributes(d, metadata, "access_control_policy"); err != nil { + return nil + } + + acps.Metadata = metadata + acpSpec.Resources = acpRes + acps.ACP = acpSpec + acpList[k] = acps + } + + return acpList + } + return nil +} + +func expandUser(pr []interface{}, d *schema.ResourceData, userListRef []*v3.ReferenceValues, meta interface{}) []*v3.UserList { + if len(pr) > 0 { + userList := make([]*v3.UserList, len(pr)) + + for k, val := range pr { + user := &v3.UserList{} + userSpec := &v3.UserSpec{} + userRes := &v3.UserResources{} + metaData := &v3.Metadata{} + + v := val.(map[string]interface{}) + + if v2, ok1 := v["directory_service_user"]; ok1 { + userRes.DirectoryServiceUser = expandDirectoryServiceUserPI(v2.([]interface{})) + } + if _, ok1 := v["identity_provider_user"]; ok1 { + userRes.IdentityProviderUser = expandIdentityProviderUser(d) + } + + userSpec.Resources = userRes + user.User = userSpec + metaData.Kind = utils.StringPtr("user") + metaData.UUID = &userListRef[k].UUID + user.Operation = utils.StringPtr("ADD") + user.Metadata = metaData + userList[k] = user + } + return userList + } + return nil +} + +func expandUserGroup(pr []interface{}, d *schema.ResourceData, userListRef []*v3.ReferenceValues, meta interface{}) []*v3.UserGroupList { + if len(pr) > 0 { + userList := make([]*v3.UserGroupList, len(pr)) + + for k, val := range pr { + user := &v3.UserGroupList{} + userSpec := &v3.UserGroupSpec{} + userRes := &v3.UserGroupResources{} + metaData := &v3.Metadata{} + + v := val.(map[string]interface{}) + + if ds, ok := v["directory_service_user_group"]; ok { + userRes.DirectoryServiceUserGroup = expandDirectoryUserGroup(ds.([]interface{})) + } + + if ds, ok := v["directory_service_ou"]; ok { + userRes.DirectoryServiceOU = expandDirectoryUserGroup(ds.([]interface{})) + } + + if su, ok := v["saml_user_group"]; ok { + userRes.SamlUserGroup = expandSamlUserGroup(su.([]interface{})) + } + + userSpec.Resources = userRes + user.UserGroup = userSpec + metaData.Kind = utils.StringPtr("user_group") + metaData.UUID = &userListRef[k].UUID + user.Operation = utils.StringPtr("ADD") + user.Metadata = metaData + userList[k] = user + } + return userList + } + return nil +} + +func expandDirectoryServiceUserPI(pr []interface{}) *v3.DirectoryServiceUser { + if pr != nil { + res := &v3.DirectoryServiceUser{} + entry := pr[0].(map[string]interface{}) + if v1, ok1 := entry["directory_service_reference"]; ok1 { + res.DirectoryServiceReference = expandReference(v1.([]interface{})[0].(map[string]interface{})) + } + + if v1, ok1 := entry["user_principal_name"]; ok1 { + res.UserPrincipalName = utils.StringPtr(v1.(string)) + } + return res + } + return nil +} + +func flattenProjectAcp(acp []*v3.ProjectAccessControlPolicyListStatus) []map[string]interface{} { + if len(acp) > 0 { + extSub := make([]map[string]interface{}, len(acp)) + + for k, v := range acp { + exts := make(map[string]interface{}) + + if v.ProjectAccessControlPolicyStatus != nil { + if v.ProjectAccessControlPolicyStatus.Name != nil { + exts["name"] = v.ProjectAccessControlPolicyStatus.Name + } + + if v.ProjectAccessControlPolicyStatus.Description != nil { + exts["description"] = v.ProjectAccessControlPolicyStatus.Description + } + + if v.ProjectAccessControlPolicyStatus.Resources != nil { + exts["user_reference_list"] = flattenArrayReferenceValues(v.ProjectAccessControlPolicyStatus.Resources.UserReferenceList) + exts["user_group_reference_list"] = flattenArrayReferenceValues(v.ProjectAccessControlPolicyStatus.Resources.UserGroupReferenceList) + exts["role_reference"] = flattenReferenceValuesList(v.ProjectAccessControlPolicyStatus.Resources.RoleReference) + exts["context_filter_list"] = flattenContextList(v.ProjectAccessControlPolicyStatus.Resources.FilterList.ContextList) + } + } + + if v.Metadata != nil { + m, _ := setRSEntityMetadata(v.Metadata) + exts["metadata"] = m + } + extSub[k] = exts + } + return extSub + } + return nil +} + +func UpdateExpandAcp(pr []interface{}, res *v3.ProjectInternalIntentResponse, kind string) []*v3.AccessControlPolicyList { + if len(pr) > 0 { + acpList := make([]*v3.AccessControlPolicyList, len(pr)) + + for k, val := range pr { + acps := &v3.AccessControlPolicyList{} + acpSpec := &v3.AccessControlPolicySpec{} + acpRes := &v3.AccessControlPolicyResources{} + var filterList v3.FilterList + + v := val.(map[string]interface{}) + + if v1, ok1 := v["operation"]; ok1 { + if kind == "old" { + acps.Operation = utils.StringPtr("UPDATE") + } else { + acps.Operation = utils.StringPtr(v1.(string)) + } + } + if v1, ok1 := v["name"]; ok1 { + acpSpec.Name = utils.StringPtr(v1.(string)) + } + if v1, ok1 := v["description"]; ok1 { + acpSpec.Description = utils.StringPtr(v1.(string)) + } + if v, ok := v["user_reference_list"]; ok { + acpRes.UserReferenceList = validateArrayRef(v.(*schema.Set), utils.StringPtr("user")) + } + + if v, ok := v["user_group_reference_list"]; ok { + acpRes.UserGroupReferenceList = validateArrayRef(v.(*schema.Set), utils.StringPtr("user_group")) + } + + if v, ok := v["role_reference"]; ok { + acpRes.RoleReference = validateRefList(v.([]interface{}), nil) + } + + if cfl, ok := v["context_filter_list"]; ok && len(cfl.([]interface{})) > 0 { + filterList.ContextList = expandProjectContextFilterList(cfl) + + if filterList.ContextList != nil { + acpRes.FilterList = &filterList + } + } + + metadata := &v3.Metadata{} + metadata.Kind = res.Spec.AccessControlPolicyList[k].Metadata.Kind + metadata.UUID = res.Spec.AccessControlPolicyList[k].Metadata.UUID + metadata.Categories = nil + metadata.ProjectReference = nil + + acps.Metadata = metadata + acpSpec.Resources = acpRes + acps.ACP = acpSpec + acpList[k] = acps + } + + return acpList + } + return nil +} + +func expandProjectContextFilterList(pr interface{}) []*v3.ContextList { + if pr != nil { + contextList := make([]*v3.ContextList, 0) + for _, a1 := range pr.([]interface{}) { + var context v3.ContextList + con := a1.(map[string]interface{}) + + context.ScopeFilterExpressionList = expandScopeExpressionList(con) + context.EntityFilterExpressionList = expandEntityExpressionList(con) + + contextList = append(contextList, &context) + } + return contextList + } + return nil +} + +func expandProjectInternalResourceDomain(pr interface{}) *v3.ResourceDomain { + if pr != nil { + resources := cast.ToStringMap(pr.([]interface{})[0])["resources"].([]interface{}) + + rs := make([]*v3.Resources, len(resources)) + for i, resource := range resources { + r := cast.ToStringMap(resource) + rs[i] = &v3.Resources{ + Limit: utils.Int64Ptr(cast.ToInt64(r["limit"])), + ResourceType: cast.ToString(r["resource_type"]), + } + } + return &v3.ResourceDomain{Resources: rs} + } + return nil +} + +func expandOptionalReference(d *schema.ResourceData, key string, kind string) *v3.Reference { + if v, ok := d.GetOk(key); ok { + val := v.([]interface{})[0].(map[string]interface{}) + return validateRef(val) + } + return nil +} + +func UpdateACPNoChange(resp *v3.ProjectInternalIntentResponse) []*v3.AccessControlPolicyList { + acpList := resp.Spec.AccessControlPolicyList + acpRes := make([]*v3.AccessControlPolicyList, len(acpList)) + for k, v := range acpList { + acps := &v3.AccessControlPolicyList{} + + acps.ACP = v.ACP + acps.Metadata = v.Metadata + acps.Metadata = v.Metadata + acps.Operation = utils.StringPtr("UPDATE") + + acpRes[k] = acps + } + return acpRes +} + +func expandCreateAcp(pr []interface{}, d *schema.ResourceData, projectUUID string, clusterUUID string, meta interface{}) []*v3.AccessControlPolicyList { + if len(pr) > 0 { + acpList := make([]*v3.AccessControlPolicyList, len(pr)) + + for k, val := range pr { + acps := &v3.AccessControlPolicyList{} + acpSpec := &v3.AccessControlPolicySpec{} + acpRes := &v3.AccessControlPolicyResources{} + + v := val.(map[string]interface{}) + + if v1, ok1 := v["name"]; ok1 { + acpSpec.Name = utils.StringPtr(v1.(string)) + } + if v1, ok1 := v["description"]; ok1 { + acpSpec.Description = utils.StringPtr(v1.(string)) + } + if v1, ok := v["user_reference_list"]; ok { + acpRes.UserReferenceList = validateArrayRef(v1.(*schema.Set), utils.StringPtr("user")) + } + + if v1, ok := v["user_group_reference_list"]; ok { + acpRes.UserGroupReferenceList = validateArrayRef(v1.(*schema.Set), utils.StringPtr("user_group")) + } + + if v, ok := v["role_reference"]; ok { + acpRes.RoleReference = validateRefList(v.([]interface{}), nil) + + // role uuid + roleID := acpRes.RoleReference.UUID + + // check for project collaboration. default is set to true + pcCollab := true + if pc, ok1 := d.GetOkExists("enable_collab"); ok1 { + pcCollab = pc.(bool) + } + // get permissions based on role + + conList := getRolesPermission(*roleID, meta, projectUUID, clusterUUID, pcCollab) + + // generate the filter list based on role + filterList := &v3.FilterList{} + filterList.ContextList = conList + + if filterList.ContextList != nil { + acpRes.FilterList = filterList + } + } + + metadata := &v3.Metadata{} + metadata.Kind = utils.StringPtr("access_control_policy") + + acps.Operation = utils.StringPtr("ADD") + acps.Metadata = metadata + acpSpec.Resources = acpRes + acps.ACP = acpSpec + acpList[k] = acps + } + + return acpList + } + return nil +} + +func UpdateExpandAcpRM(pr []interface{}, res *v3.ProjectInternalIntentResponse, d *schema.ResourceData, meta interface{}, projectUUID string, clusterUUID string) []*v3.AccessControlPolicyList { + if len(pr) > 0 { + acpList := make([]*v3.AccessControlPolicyList, len(pr)) + oldACPExists := len(res.Spec.AccessControlPolicyList) + for k, val := range pr { + acps := &v3.AccessControlPolicyList{} + acpSpec := &v3.AccessControlPolicySpec{} + acpRes := &v3.AccessControlPolicyResources{} + + v := val.(map[string]interface{}) + + if v1, ok1 := v["name"]; ok1 { + acpSpec.Name = utils.StringPtr(v1.(string)) + } + if v1, ok1 := v["description"]; ok1 { + acpSpec.Description = utils.StringPtr(v1.(string)) + } + if v, ok := v["user_reference_list"]; ok { + acpRes.UserReferenceList = validateArrayRef(v.(*schema.Set), utils.StringPtr("user")) + } + + if v, ok := v["user_group_reference_list"]; ok { + acpRes.UserGroupReferenceList = validateArrayRef(v.(*schema.Set), utils.StringPtr("user_group")) + } + + if v, ok := v["role_reference"]; ok { + acpRes.RoleReference = validateRefList(v.([]interface{}), nil) + + // get permissions based on role + roleID := acpRes.RoleReference.UUID + + // check for project collaboration. default is set to true + pcCollab := true + if pc, ok1 := d.GetOkExists("enable_collab"); ok1 { + pcCollab = pc.(bool) + } + + conList := getRolesPermission(*roleID, meta, projectUUID, clusterUUID, pcCollab) + + // get the filter list based on role + filterList := &v3.FilterList{} + filterList.ContextList = conList + + if filterList.ContextList != nil { + acpRes.FilterList = filterList + } + } + + metadata := &v3.Metadata{} + if k < oldACPExists { + acps.Operation = utils.StringPtr("UPDATE") + + metadata.Kind = res.Spec.AccessControlPolicyList[k].Metadata.Kind + metadata.UUID = res.Spec.AccessControlPolicyList[k].Metadata.UUID + metadata.Categories = nil + metadata.ProjectReference = nil + } else { + acps.Operation = utils.StringPtr("ADD") + metadata.Kind = utils.StringPtr("access_control_policy") + } + + acps.Metadata = metadata + acpSpec.Resources = acpRes + acps.ACP = acpSpec + acpList[k] = acps + } + + // check for delete ACP + + ck, delacp := checkACPdelete(res, acpList) + if ck { + acpList = append(acpList, delacp) + } + return acpList + } + return nil +} + +func checkACPdelete(resp *v3.ProjectInternalIntentResponse, acpList []*v3.AccessControlPolicyList) (bool, *v3.AccessControlPolicyList) { + oldACP := len(resp.Status.AccessControlPolicyListStatus) + newACP := len(acpList) + + if newACP < oldACP { + for _, v := range resp.Spec.AccessControlPolicyList { + oldmeta := v.Metadata.UUID + checkk := false + for _, val := range acpList { + if oldmeta == val.Metadata.UUID { + checkk = true + } + } + if !checkk { + v.Operation = utils.StringPtr("DELETE") + return true, v + } + } + return false, nil + } + return false, nil +} diff --git a/nutanix/resource_nutanix_project_test.go b/nutanix/resource_nutanix_project_test.go index 777d812da..96b0596cc 100644 --- a/nutanix/resource_nutanix_project_test.go +++ b/nutanix/resource_nutanix_project_test.go @@ -97,6 +97,145 @@ func TestAccNutanixProject_importBasic(t *testing.T) { }) } +func TestAccNutanixProject_withInternal(t *testing.T) { + resourceName := "nutanix_project.project_test" + + subnetName := acctest.RandomWithPrefix("test-subnateName") + name := acctest.RandomWithPrefix("test-project-name-dou") + description := acctest.RandomWithPrefix("test-project-desc-dou") + categoryName := "Environment" + categoryVal := "Staging" + limit := cast.ToString(acctest.RandIntRange(2, 4)) + rsType := "STORAGE" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccNutanixProjectInternalConfig(subnetName, name, description, categoryName, categoryVal, limit, rsType), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "description", description), + resource.TestCheckResourceAttr(resourceName, "resource_domain.#", "1"), + resource.TestCheckResourceAttr(resourceName, "resource_domain.0.resources.#", "1"), + resource.TestCheckResourceAttr(resourceName, "resource_domain.0.resources.0.limit", limit), + resource.TestCheckResourceAttr(resourceName, "resource_domain.0.resources.0.resource_type", rsType), + resource.TestCheckResourceAttr(resourceName, "categories.#", "1"), + resource.TestCheckResourceAttr(resourceName, "api_version", "3.1"), + resource.TestCheckResourceAttr(resourceName, "subnet_reference_list.#", "2"), + resource.TestCheckResourceAttr(resourceName, "cluster_reference_list.#", "1"), + resource.TestCheckResourceAttr(resourceName, "vpc_reference_list.#", "1"), + ), + }, + }, + }) +} + +func TestAccNutanixProject_withInternalUpdate(t *testing.T) { + resourceName := "nutanix_project.project_test" + subnetName := acctest.RandomWithPrefix("test-subnateName") + name := acctest.RandomWithPrefix("test-project-name-dou") + description := acctest.RandomWithPrefix("test-project-desc-dou") + + updatedName := acctest.RandomWithPrefix("test-project-updated") + updateDes := acctest.RandomWithPrefix("test-desc-got-updated") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccNutanixProjectInternalConfigUpdate(subnetName, name, description), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "description", description), + resource.TestCheckResourceAttr(resourceName, "api_version", "3.1"), + resource.TestCheckResourceAttr(resourceName, "subnet_reference_list.#", "1"), + ), + }, + { + Config: testAccNutanixProjectInternalConfigUpdate(subnetName, updatedName, updateDes), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", updatedName), + resource.TestCheckResourceAttr(resourceName, "description", updateDes), + resource.TestCheckResourceAttr(resourceName, "api_version", "3.1"), + resource.TestCheckResourceAttr(resourceName, "subnet_reference_list.#", "1"), + ), + }, + }, + }) +} + +func TestAccNutanixProject_withInternalWithACP(t *testing.T) { + resourceName := "nutanix_project.project_test" + + subnetName := acctest.RandomWithPrefix("test-subnateName") + name := acctest.RandomWithPrefix("test-project-name-dou") + description := acctest.RandomWithPrefix("test-project-desc-dou") + categoryName := "Environment" + categoryVal := "Staging" + limit := cast.ToString(acctest.RandIntRange(2, 4)) + rsType := "STORAGE" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccNutanixProjectInternalConfigWithACP(subnetName, name, description, categoryName, categoryVal, limit, rsType), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "description", description), + resource.TestCheckResourceAttr(resourceName, "resource_domain.#", "1"), + resource.TestCheckResourceAttr(resourceName, "resource_domain.0.resources.#", "1"), + resource.TestCheckResourceAttr(resourceName, "resource_domain.0.resources.0.limit", limit), + resource.TestCheckResourceAttr(resourceName, "resource_domain.0.resources.0.resource_type", rsType), + resource.TestCheckResourceAttr(resourceName, "categories.#", "1"), + resource.TestCheckResourceAttr(resourceName, "api_version", "3.1"), + resource.TestCheckResourceAttr(resourceName, "subnet_reference_list.#", "1"), + resource.TestCheckResourceAttr(resourceName, "acp.#", "1"), + ), + }, + }, + }) +} + +func TestAccNutanixProject_withInternalWithACPUserGroup(t *testing.T) { + resourceName := "nutanix_project.project_test" + + subnetName := acctest.RandomWithPrefix("test-subnateName") + name := acctest.RandomWithPrefix("test-project-name-dou") + description := acctest.RandomWithPrefix("test-project-desc-dou") + categoryName := "Environment" + categoryVal := "Staging" + limit := cast.ToString(acctest.RandIntRange(2, 4)) + rsType := "STORAGE" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccNutanixProjectInternalConfigWithACPUserGroup(subnetName, name, description, categoryName, categoryVal, limit, rsType), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "description", description), + resource.TestCheckResourceAttr(resourceName, "resource_domain.#", "1"), + resource.TestCheckResourceAttr(resourceName, "resource_domain.0.resources.#", "1"), + resource.TestCheckResourceAttr(resourceName, "resource_domain.0.resources.0.limit", limit), + resource.TestCheckResourceAttr(resourceName, "resource_domain.0.resources.0.resource_type", rsType), + resource.TestCheckResourceAttr(resourceName, "categories.#", "1"), + resource.TestCheckResourceAttr(resourceName, "api_version", "3.1"), + resource.TestCheckResourceAttr(resourceName, "subnet_reference_list.#", "1"), + resource.TestCheckResourceAttr(resourceName, "acp.#", "1"), + resource.TestCheckResourceAttr(resourceName, "external_user_group_reference_list.#", "1"), + ), + }, + }, + }) +} + func testAccCheckNutanixProjectImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { return func(s *terraform.State) (string, error) { rs, ok := s.RootModule().Resources[resourceName] @@ -192,8 +331,387 @@ func testAccNutanixProjectConfig(subnetName, name, description, categoryName, ca default_subnet_reference { uuid = nutanix_subnet.subnet.metadata.uuid } + subnet_reference_list{ + uuid = nutanix_subnet.subnet.metadata.uuid + } api_version = "3.1" } `, subnetName, name, description, categoryName, categoryVal, limit, rsType) } + +func testAccNutanixProjectInternalConfig(subnetName, name, description, categoryName, categoryVal, limit, rsType string) string { + return fmt.Sprintf(` + data "nutanix_clusters" "clusters" {} + + locals { + cluster1 = [ + for cluster in data.nutanix_clusters.clusters.entities : + cluster.metadata.uuid if cluster.service_list[0] != "PRISM_CENTRAL" + ][0] + } + + resource "nutanix_subnet" "subnet" { + cluster_uuid = local.cluster1 + name = "%s" + description = "Description of my unit test VLAN" + vlan_id = 31 + subnet_type = "VLAN" + subnet_ip = "10.250.140.0" + default_gateway_ip = "10.250.140.1" + prefix_length = 24 + + dhcp_options = { + boot_file_name = "bootfile" + domain_name = "nutanix" + tftp_server_name = "10.250.140.200" + } + + dhcp_domain_name_server_list = ["8.8.8.8", "4.2.2.2"] + dhcp_domain_search_list = ["terraform.nutanix.com", "terraform.unit.test.com"] + } + + + resource "nutanix_subnet" "overlay-subnet" { + cluster_uuid = local.cluster1 + name = "acctest-subnet-updated" + description = "Description of my unit test VLAN" + vlan_id = 876 + subnet_type = "VLAN" + subnet_ip = "10.250.144.0" + default_gateway_ip = "10.250.144.1" + prefix_length = 24 + is_external = true + ip_config_pool_list_ranges = ["10.250.144.10 10.250.144.20"] + } + + + resource "nutanix_vpc" "acctest-managed" { + depends_on = [ + resource.nutanix_subnet.overlay-subnet + ] + name = "acctest-managed-vpc" + + external_subnet_reference_name = [ + "acctest-subnet-updated" + ] + + common_domain_name_server_ip_list{ + ip = "8.8.8.9" + } + + externally_routable_prefix_list{ + ip= "172.30.0.0" + prefix_length= 16 + } + externally_routable_prefix_list{ + ip= "172.34.0.0" + prefix_length= 16 + } + } + + resource "nutanix_project" "project_test" { + name = "%s" + description = "%s" + + categories { + name = "%s" + value = "%s" + } + + resource_domain { + resources { + limit = %s + resource_type = "%s" + } + } + + default_subnet_reference { + uuid = nutanix_subnet.subnet.metadata.uuid + } + + use_project_internal = true + + api_version = "3.1" + + subnet_reference_list{ + kind="subnet" + name="nutanix_subnet.subnet.name" + uuid=nutanix_subnet.subnet.metadata.uuid + } + subnet_reference_list{ + kind="subnet" + name="nutanix_subnet.overlay-subnet.name" + uuid=nutanix_subnet.overlay-subnet.id + } + cluster_reference_list{ + kind="cluster" + uuid=local.cluster1 + } + vpc_reference_list{ + kind="vpc" + uuid= nutanix_vpc.acctest-managed.id + } + } + `, subnetName, name, description, categoryName, categoryVal, limit, rsType) +} + +func testAccNutanixProjectInternalConfigUpdate(subnetName, name, description string) string { + return fmt.Sprintf(` + data "nutanix_clusters" "clusters" {} + + locals { + cluster1 = [ + for cluster in data.nutanix_clusters.clusters.entities : + cluster.metadata.uuid if cluster.service_list[0] != "PRISM_CENTRAL" + ][0] + } + + resource "nutanix_subnet" "subnet" { + cluster_uuid = local.cluster1 + name = "%s" + description = "Description of my unit test VLAN" + vlan_id = 31 + subnet_type = "VLAN" + subnet_ip = "10.250.140.0" + default_gateway_ip = "10.250.140.1" + prefix_length = 24 + + dhcp_options = { + boot_file_name = "bootfile" + domain_name = "nutanix" + tftp_server_name = "10.250.140.200" + } + + dhcp_domain_name_server_list = ["8.8.8.8", "4.2.2.2"] + dhcp_domain_search_list = ["terraform.nutanix.com", "terraform.unit.test.com"] + } + + + resource "nutanix_subnet" "overlay-subnet" { + cluster_uuid = local.cluster1 + name = "acctest-subnet-updated" + description = "Description of my unit test VLAN" + vlan_id = 876 + subnet_type = "VLAN" + subnet_ip = "10.250.144.0" + default_gateway_ip = "10.250.144.1" + prefix_length = 24 + is_external = true + ip_config_pool_list_ranges = ["10.250.144.10 10.250.144.20"] + } + + resource "nutanix_project" "project_test" { + name = "%s" + description = "%s" + + default_subnet_reference { + uuid = nutanix_subnet.subnet.metadata.uuid + } + + use_project_internal = true + + api_version = "3.1" + + subnet_reference_list{ + kind="subnet" + uuid=nutanix_subnet.subnet.metadata.uuid + } + } + `, subnetName, name, description) +} + +func testAccNutanixProjectInternalConfigWithACP(subnetName, name, description, categoryName, categoryVal, limit, rsType string) string { + return fmt.Sprintf(` + data "nutanix_clusters" "clusters" {} + + locals { + cluster1 = [ + for cluster in data.nutanix_clusters.clusters.entities : + cluster.metadata.uuid if cluster.service_list[0] != "PRISM_CENTRAL" + ][0] + } + + resource "nutanix_subnet" "subnet" { + cluster_uuid = local.cluster1 + name = "%[1]s" + description = "Description of my unit test VLAN" + vlan_id = 31 + subnet_type = "VLAN" + subnet_ip = "10.250.140.0" + default_gateway_ip = "10.250.140.1" + prefix_length = 24 + + dhcp_options = { + boot_file_name = "bootfile" + domain_name = "nutanix" + tftp_server_name = "10.250.140.200" + } + + dhcp_domain_name_server_list = ["8.8.8.8", "4.2.2.2"] + dhcp_domain_search_list = ["terraform.nutanix.com", "terraform.unit.test.com"] + } + + resource "nutanix_role" "test" { + name = "project-role-acctest" + description = "description role" + permission_reference_list { + kind = "permission" + uuid = "%[8]s" + } + } + + resource "nutanix_project" "project_test" { + name = "%[2]s" + description = "%[3]s" + cluster_uuid = local.cluster1 + categories { + name = "%[4]s" + value = "%[5]s" + } + + resource_domain { + resources { + limit = %[6]s + resource_type = "%[7]s" + } + } + + default_subnet_reference { + uuid = nutanix_subnet.subnet.metadata.uuid + } + + use_project_internal = true + + api_version = "3.1" + + subnet_reference_list{ + kind="subnet" + uuid=nutanix_subnet.subnet.metadata.uuid + } + + user_reference_list{ + uuid = "00000000-0000-0000-0000-000000000000" + name = "admin" + } + + acp{ + name="nuCalmAcp-97c623" + + role_reference { + kind = "role" + uuid = nutanix_role.test.id + } + + user_reference_list{ + uuid = "00000000-0000-0000-0000-000000000000" + name = "admin" + kind = "user" + } + + description= "untitledAcp-54acc50f-ab94-640a-5f06-5c855cc09539" + } + } + `, subnetName, name, description, categoryName, categoryVal, limit, rsType, testVars.Permissions[0].UUID) +} + +func testAccNutanixProjectInternalConfigWithACPUserGroup(subnetName, name, description, categoryName, categoryVal, limit, rsType string) string { + return fmt.Sprintf(` + data "nutanix_clusters" "clusters" {} + + locals { + cluster1 = [ + for cluster in data.nutanix_clusters.clusters.entities : + cluster.metadata.uuid if cluster.service_list[0] != "PRISM_CENTRAL" + ][0] + } + + resource "nutanix_subnet" "subnet" { + cluster_uuid = local.cluster1 + name = "%[1]s" + description = "Description of my unit test VLAN" + vlan_id = 31 + subnet_type = "VLAN" + subnet_ip = "10.250.140.0" + default_gateway_ip = "10.250.140.1" + prefix_length = 24 + + dhcp_options = { + boot_file_name = "bootfile" + domain_name = "nutanix" + tftp_server_name = "10.250.140.200" + } + + dhcp_domain_name_server_list = ["8.8.8.8", "4.2.2.2"] + dhcp_domain_search_list = ["terraform.nutanix.com", "terraform.unit.test.com"] + } + + resource "nutanix_role" "test" { + name = "project-role-acctest" + description = "description role" + permission_reference_list { + kind = "permission" + uuid = "%[8]s" + } + } + + resource "nutanix_user_groups" "acctest-managed" { + directory_service_user_group { + distinguished_name = "%[9]s" + } + } + + resource "nutanix_project" "project_test" { + name = "%[2]s" + description = "%[3]s" + cluster_uuid = local.cluster1 + categories { + name = "%[4]s" + value = "%[5]s" + } + + resource_domain { + resources { + limit = %[6]s + resource_type = "%[7]s" + } + } + + default_subnet_reference { + uuid = nutanix_subnet.subnet.metadata.uuid + } + + use_project_internal = true + + api_version = "3.1" + + subnet_reference_list{ + kind="subnet" + uuid=nutanix_subnet.subnet.metadata.uuid + } + + external_user_group_reference_list { + name= "%[9]s" + kind= "user_group" + uuid= nutanix_user_groups.acctest-managed.id + } + + acp{ + name="nuCalmAcp-97c623" + + role_reference { + kind = "role" + uuid = nutanix_role.test.id + } + + user_group_reference_list { + name= "%[9]s" + kind= "user_group" + uuid= nutanix_user_groups.acctest-managed.id + } + + description= "untitledAcp-54acc50f-ab94-640a-5f06-5c855cc09539" + } + } + `, subnetName, name, description, categoryName, categoryVal, limit, rsType, testVars.Permissions[0].UUID, testVars.UserGroupWithDistinguishedName[3].DistinguishedName) +} diff --git a/nutanix/role_mapping.go b/nutanix/role_mapping.go new file mode 100644 index 000000000..83a9ccb78 --- /dev/null +++ b/nutanix/role_mapping.go @@ -0,0 +1,701 @@ +package nutanix + +import ( + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func getSystemRoles(kind string, projectUUID string, clusterUUID string) []*v3.ContextList { + if kind == "Project Admin" { + return []*v3.ContextList{ + { + EntityFilterExpressionList: []v3.EntityFilterExpressionList{ + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("image"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("marketplace_item"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("directory_service"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("role"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("project"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + UUIDList: []string{projectUUID}, + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("user"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("user_group"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("environment"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_icon"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("category"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_task"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("cluster"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + UUIDList: []string{clusterUUID}, + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_variable"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("identity_provider"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("vm_recovery_point"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("virtual_network"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + }, + }, + } + } + if kind == "Developer" { + return []*v3.ContextList{ + { + EntityFilterExpressionList: []v3.EntityFilterExpressionList{ + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_icon"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_task"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_variable"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("category"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("cluster"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + UUIDList: []string{clusterUUID}, + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("image"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("marketplace_item"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("vm_recovery_point"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("virtual_network"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + }, + }, + } + } + if kind == "Consumer" { + return []*v3.ContextList{ + { + EntityFilterExpressionList: []v3.EntityFilterExpressionList{ + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("image"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("marketplace_item"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_icon"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("category"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("cluster"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + UUIDList: []string{clusterUUID}, + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_task"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_variable"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("vm_recovery_point"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("virtual_network"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + }, + }, + } + } + if kind == "Operator" { + return []*v3.ContextList{ + { + EntityFilterExpressionList: []v3.EntityFilterExpressionList{ + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_icon"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("category"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("vm_recovery_point"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("cluster"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + UUIDList: []string{clusterUUID}, + }, + }, + { + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("virtual_network"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + }, + }, + } + } + return nil +} + +func getDefaultContext(projectUUID string) []*v3.ContextList { + return []*v3.ContextList{ + { + EntityFilterExpressionList: []v3.EntityFilterExpressionList{ + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("blueprint"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("environment"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("marketplace_item"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + }, + }, + ScopeFilterExpressionList: []*v3.ScopeFilterExpressionList{ + { + LeftHandSide: "PROJECT", + Operator: "IN", + RightHandSide: v3.RightHandSide{ + UUIDList: []string{projectUUID}, + }, + }, + }, + }, + } +} + +func getFilterCollaboration(collab bool, projectUUID string) []*v3.ContextList { + var FilterScope string + if collab { + FilterScope = "ALL" + } else { + FilterScope = "SELF_OWNED" + } + return []*v3.ContextList{ + { + ScopeFilterExpressionList: []*v3.ScopeFilterExpressionList{ + { + LeftHandSide: "PROJECT", + Operator: "IN", + RightHandSide: v3.RightHandSide{ + UUIDList: []string{projectUUID}, + }, + }, + }, + EntityFilterExpressionList: []v3.EntityFilterExpressionList{ + { + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("ALL"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr(FilterScope), + }, + }, + }, + }, + } +} + +// Custom Roles + +func generateCustomFilters(permissionList []string) []interface{} { + customMap := make(map[string]v3.EntityFilterExpressionList) + viewImage := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("image"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + } + customMap["view_image"] = viewImage + + viewAppIcon := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_icon"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + } + customMap["View_App_Icon"] = viewAppIcon + + viewNameCategory := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("category"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + } + customMap["View_Name_Category"] = viewNameCategory + + createOrUpdateNameCategory := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("category"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + } + customMap["Create_Or_Update_Name_Category"] = createOrUpdateNameCategory + + viewEnvironment := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("environment"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + } + customMap["View_Environment"] = viewEnvironment + + viewMarketplaceItem := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("marketplace_item"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + } + customMap["View_Marketplace_Item"] = viewMarketplaceItem + + viewUser := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("user_group"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + } + customMap["View_User"] = viewUser + + viewUserGroup := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("image"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + } + customMap["View_User_Group"] = viewUserGroup + + viewRole := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("role"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + } + customMap["View_Role"] = viewRole + + viewDirectoryService := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("directory_service"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + } + customMap["View_Directory_Service"] = viewDirectoryService + + searchDirectoryService := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("directory_service"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + } + customMap["Search_Directory_Service"] = searchDirectoryService + + viewIdentityProvider := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("identity_provider"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("ALL"), + }, + } + customMap["View_Identity_Provider"] = viewIdentityProvider + + viewAppTask := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_task"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + } + customMap["View_App_Task"] = viewAppTask + + viewAppVariable := v3.EntityFilterExpressionList{ + Operator: "IN", + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("app_variable"), + }, + RightHandSide: v3.RightHandSide{ + Collection: utils.StringPtr("SELF_OWNED"), + }, + } + customMap["View_App_Variable"] = viewAppVariable + + filterLIST := make([]interface{}, 0) + + for _, v := range permissionList { + val := v + if vals, ok := customMap[val]; ok { + filterLIST = append(filterLIST, vals) + } + } + + return filterLIST +} + +func getRolesPermission(roleID string, meta interface{}, projectUUID string, clusterUUID string, pcCollab bool) []*v3.ContextList { + conn := meta.(*Client).API + + resp, _ := conn.V3.GetRole(roleID) + + roleName := utils.StringValue(resp.Status.Name) + + if roleName == "Developer" || roleName == "Consumer" || roleName == ("Operator") || roleName == ("Project Admin") { + contextOut := make([]*v3.ContextList, 0) + + contextOut = append(contextOut, getFilterCollaboration(pcCollab, projectUUID)...) + + contextOut = append(contextOut, getSystemRoles(roleName, projectUUID, clusterUUID)...) + + contextOut = append(contextOut, getDefaultContext(projectUUID)...) + + return contextOut + } + permList := resp.Status.Resources.PermissionReferenceList + + permS := []string{} + + for _, v := range permList { + permS = append(permS, utils.StringValue(v.Name)) + } + + customPerms := generateCustomFilters(permS) + + ans := make([]v3.EntityFilterExpressionList, 0) + for _, v := range customPerms { + val := v.(v3.EntityFilterExpressionList) + ans = append(ans, val) + } + + if clusterUUID != "" { + ans = append(ans, v3.EntityFilterExpressionList{ + LeftHandSide: v3.LeftHandSide{ + EntityType: utils.StringPtr("cluster"), + }, + Operator: "IN", + RightHandSide: v3.RightHandSide{ + UUIDList: []string{clusterUUID}, + }, + }) + } + + out := &v3.ContextList{} + out.EntityFilterExpressionList = ans + + contextOut := make([]*v3.ContextList, 0) + + contextOut = append(contextOut, getFilterCollaboration(pcCollab, projectUUID)...) + + contextOut = append(contextOut, out) + + contextOut = append(contextOut, getDefaultContext(projectUUID)...) + + return contextOut +} diff --git a/test_config.json b/test_config.json index 2b7fcc883..8e432fe72 100644 --- a/test_config.json +++ b/test_config.json @@ -10,6 +10,9 @@ { "distinguished_name": "" }, + { + "distinguished_name": "" + }, { "distinguished_name": "" } diff --git a/website/docs/d/project.html.markdown b/website/docs/d/project.html.markdown index 99cc87aa8..ba5ef6268 100644 --- a/website/docs/d/project.html.markdown +++ b/website/docs/d/project.html.markdown @@ -127,6 +127,69 @@ The following attributes are exported: * `resource_domain.resources.#.units` - The units of the resource type * `resource_domain.resources.#.value` - The amount of resource consumed +### Tunnel Reference List +* `tunnel_reference_list` - (Optional/Computed) List of tunnels associated with the project. +* `tunnel_reference_list.#.kind` - (Optional) The kind name. Default value is `tunnel` +* `tunnel_reference_list.#.uuid` - (Required) The UUID of a tunnel +* `tunnel_reference_list.#.name` - (Optional/Computed) The name of a tunnel. + +### Cluster Reference List +* `cluster_reference_list` - (Optional/Computed) List of clusters associated with the project.. +* `cluster_reference_list.#.kind` - (Optional) The kind name. Default value is `cluster` +* `cluster_reference_list.#.uuid` - (Required) The UUID of a cluster +* `cluster_reference_list.#.name` - (Optional/Computed) The name of a cluster. + +### VPC Reference List +* `vpc_reference_list` - (Optional/Computed) List of VPCs associated with the project.. +* `vpc_reference_list.#.kind` - (Optional) The kind name. Default value is `vpc` +* `vpc_reference_list.#.uuid` - (Required) The UUID of a vpc +* `vpc_reference_list.#.name` - (Optional/Computed) The name of a vpc. + +### Default Environment Reference Map +* `default_environment_reference` - (Optional/Computed) Reference to a environment. +* `default_environment_reference.kind` - (Optional) The kind name. Default value is `environment` +* `default_environment_reference.uuid` - (Required) The UUID of a environment +* `default_environment_reference.name` - (Optional/Computed) The name of a environment. + +### ACP +ACPs will be exported if use_project_internal flag is set. +* `name` - Name of ACP +* `description` - Description of ACP +* `user_reference_list` - List of Reference of users. +* `user_group_reference_list` - List of Reference of users groups. +* `role_reference` - Reference to role. +* `context_filter_list` - The list of context filters. These are OR filters. The scope-expression-list defines the context, and the filter works in conjunction with the entity-expression-list. + +The context_list attribute supports the following: + +* `scope_filter_expression_list`: - (Optional) Filter the scope of an Access Control Policy. +* `entity_filter_expression_list` - (Required) A list of Entity filter expressions. + +### Scope Filter Expression List + +The scope_filter_expression_list attribute supports the following. + +* `left_hand_side`: - (Optional) The LHS of the filter expression - the scope type. +* `operator`: - (Required) The operator of the filter expression. +* `right_hand_side`: - (Required) The right hand side (RHS) of an scope expression. + + +### Entity Filter Expression List + +The scope_filter_expression_list attribute supports the following. + +* `left_hand_side_entity_type`: - (Optional) The LHS of the filter expression - the entity type. +* `operator`: - (Required) The operator in the filter expression. +* `right_hand_side`: - (Required) The right hand side (RHS) of an scope expression. + +### Right Hand Side + +The right_hand_side attribute supports the following. + +* `collection`: - (Optional) A representative term for supported groupings of entities. ALL = All the entities of a given kind. +* `categories`: - (Optional) The category values represented as a dictionary of key -> list of values. +* `uuid_list`: - (Optional) The explicit list of UUIDs for the given kind. + ### Metadata The metadata attribute exports the following: diff --git a/website/docs/r/address_group.html.markdown b/website/docs/r/address_group.html.markdown index cac156543..cd3ea5f72 100644 --- a/website/docs/r/address_group.html.markdown +++ b/website/docs/r/address_group.html.markdown @@ -6,7 +6,7 @@ description: |- This operation submits a request to create a address group based on the input parameters. --- -# nutanix_service_group +# nutanix_address_group Provides a resource to create a address group based on the input parameters. diff --git a/website/docs/r/project.markdown b/website/docs/r/project.markdown index e3252b6f1..f7d71dd7b 100644 --- a/website/docs/r/project.markdown +++ b/website/docs/r/project.markdown @@ -55,6 +55,100 @@ resource "nutanix_project" "project_test" { api_version = "3.1" } + +# set use_project_internal flag to create project with acps + +resource "nutanix_project" "project_test" { + name = "my-project" + description = "This is my project" + + cluster_uuid = "" + + use_project_internal = true + + default_subnet_reference { + uuid = nutanix_subnet.subnet.metadata.uuid + } + + user_reference_list{ + name= "{{user_name}}" + kind= "user" + uuid= "{{user_uuid}}" + } + subnet_reference_list{ + uuid=resource.nutanix_subnet.sub.id + } + acp{ + # acp name consists name_uuid string, it should be different for each acp. + name="{{acp_name}}" + role_reference{ + kind= "role" + uuid= "{{role_uuid}}" + name="Developer" + } + user_reference_list{ + name= "{{user_name}}" + kind= "user" + uuid= "{{user_uuid}}" + } + description= "{{description}}" + } + api_version = "3.1" +} + +## Create a project with user which not added in the PC + +resource "nutanix_project" "project_test" { + name = "my-project" + description = "This is my project" + + cluster_uuid = "" + + use_project_internal = true + + default_subnet_reference { + uuid = nutanix_subnet.subnet.metadata.uuid + } + + user_reference_list{ + name= "{{user_name}}" + kind= "user" + uuid= "{{user_uuid}}" + } + subnet_reference_list{ + uuid=resource.nutanix_subnet.sub.id + } + acp{ + # acp name consists name_uuid string, it should be different for each acp. + name="{{acp_name}}" + role_reference{ + kind= "role" + uuid= "{{role_uuid}}" + name="Developer" + } + user_reference_list{ + name= "{{user_name}}" + kind= "user" + uuid= "{{user_uuid}}" + } + description= "{{description}}" + } + user_list{ + metadata={ + kind="user" + uuid= "{{ UUID of the USER }}" + } + directory_service_user{ + user_principal_name= "{{ Name of user }}" + directory_service_reference{ + uuid="{{ DIRECTORY SERVICE UUID }}" + kind="directory_service" + } + } + } + api_version = "3.1" +} + ``` ## Argument Reference @@ -64,6 +158,10 @@ The following arguments are supported: * `name` - (Required) The name for the project. * `description` - (Required) A description for project. +* `use_project_internal` - (Optional) flag to use project internal for user role mapping +* `cluster_uuid` - (Optional) The UUID of cluster. (Required when using project_internal flag). +* `enable_collab` - (Optional) flag to allow collaboration of projects. (Use with project_internal flag) + ### Resource Domain * `resource_domain` - (Required) The status for a resource domain (limits and values) * `resource_domain.resources` - (Required) Array of the utilization/limit for resource types @@ -111,6 +209,85 @@ The following arguments are supported: * `external_network_list.#.uuid` - (Required) The UUID of a network. * `external_network_list.#.name` - (Optional/Computed) The name of a network. +### Tunnel Reference List +* `tunnel_reference_list` - (Optional/Computed) List of tunnels associated with the project. +* `tunnel_reference_list.#.kind` - (Optional) The kind name. Default value is `tunnel` +* `tunnel_reference_list.#.uuid` - (Required) The UUID of a tunnel +* `tunnel_reference_list.#.name` - (Optional/Computed) The name of a tunnel. + +### Cluster Reference List +* `cluster_reference_list` - (Optional/Computed) List of clusters associated with the project.. +* `cluster_reference_list.#.kind` - (Optional) The kind name. Default value is `cluster` +* `cluster_reference_list.#.uuid` - (Required) The UUID of a cluster +* `cluster_reference_list.#.name` - (Optional/Computed) The name of a cluster. + +### VPC Reference List +* `vpc_reference_list` - (Optional/Computed) List of VPCs associated with the project.. +* `vpc_reference_list.#.kind` - (Optional) The kind name. Default value is `vpc` +* `vpc_reference_list.#.uuid` - (Required) The UUID of a vpc +* `vpc_reference_list.#.name` - (Optional/Computed) The name of a vpc. + +### Default Environment Reference Map +* `default_environment_reference` - (Optional/Computed) Reference to a environment. +* `default_environment_reference.kind` - (Optional) The kind name. Default value is `environment` +* `default_environment_reference.uuid` - (Required) The UUID of a environment +* `default_environment_reference.name` - (Optional/Computed) The name of a environment. + + +### ACP +* `acp` - (Optional) The list of ACPs to be attached to the users belonging to a project. It is mandate to provide cluster_uuid while using ACP. It helps to get the context list based on user role. +* `acp.#.name` - (Required) Name of the Access Control Policy. +* `acp.#.description` - The description of the association of a role to a user in a given context. + +* `acp.#.user_reference_list` - The User(s) being assigned a given role. +* `acp.#.user_reference_list.#.kind` - The kind name. Default value is `user` +* `acp.#.user_reference_list.#.uuid` - (Required) The UUID of a user +* `acp.#.user_reference_list.#.name` - (Optional/Computed) The name of a user. + +* `acp.#.user_group_reference_list` - The User group(s) being assigned a given role +* `acp.#.user_group_reference_list.#.kind` - The kind name. Default value is `user_group` +* `acp.#.user_group_reference_list.#.uuid` - (Required) The UUID of a user group +* `acp.#.user_group_reference_list.#.name` - (Optional/Computed) The name of a user group. + +* `acp.#.role_reference` - Reference to a role. +* `acp.#.role_reference.kind` - The kind name. Default value is `role` +* `acp.#.role_reference.uuid` - (Required) The UUID of a role +* `acp.#.role_reference.name` - (Optional/Computed) The name of a role. + +### User List +* `user_list` - (Optional) The list of user specification to be associated with the project. It is only required when user is not added in the PC. +* `user_list.#.directory_service_user` - (Optional) A Directory Service user. +* `user_list.#.directory_service_user.user_principal_name` - (Required) The UserPrincipalName of the user from the directory service. +* `user_list.#.directory_service_user.directory_service_reference` - (Required) Reference to a directory_service . +* `user_list.#.directory_service_user.directory_service_reference.uuid` - (Required) The uuid to a directory_service. +* `user_list.#.directory_service_user.directory_service_reference.kind` - (Optional) The kind to a directory_service. + +* `user_list.#.identity_provider_user` - (Optional) An Identity Provider user. +* `user_list.#.identity_provider_user.username` - (Required) The username from the identity provider. Name Id for SAML Identity Provider. +* `user_list.#.identity_provider_user.identity_provider_reference` - (Required) The reference to a identity_provider. +* `user_list.#.identity_provider_user.identity_provider_reference.uuid` - (Required) The uuid to a identity_provider. +* `user_list.#.identity_provider_user.identity_provider_reference.kind` - (Optional) The kind to a identity_provider. +* `user_list.#.metadata` - (Required) Metadata Reference for user +* `user_list.#.metadata.uuid` - (Required) UUID of the USER +* `user_list.#.metadata.Kind` - Kind of the USER. + + +### User Group + +* `user_group` - (Optional) The list of user group specification to be associated with the project. It is only Required when user group is not added in the PC. +* `user_group.#.directory_service_user_group` - (Optional) A Directory Service user group. +* `user_group.#.directory_service_user_group.distinguished_name` - (Required) The Distinguished name for the user group. + +* `user_group.#.saml_user_group` - (Optional) A SAML Service user group. +* `user_group.#.saml_user_group.idp_uuid` - (Required) The UUID of the Identity Provider that the group belongs to. +* `user_group.#.saml_user_group.name` - (Required) The name of the SAML group which the IDP provides as attribute in SAML response. + +* `user_group.#.directory_service_ou` - (Optional) A Directory Service user group. +* `user_group.#.directory_service_ou.distinguished_name` - (Required) The Distinguished name for the user group. +* `user_group.#.metadata` - (Required) Metadata Reference for user group +* `user_group.#.metadata.uuid` - (Required) UUID of the USER Group +* `user_group.#.metadata.Kind` - Kind of the USER Group. + ## Attributes Reference The following attributes are exported: @@ -119,6 +296,46 @@ The following attributes are exported: * `resource_domain.resources.#.units` - The units of the resource type * `resource_domain.resources.#.value` - The amount of resource consumed +### ACP +ACPs will be exported if use_project_internal flag is set. +* `name` - Name of ACP +* `description` - Description of ACP +* `user_reference_list` - List of Reference of users. +* `user_group_reference_list` - List of Reference of users groups. +* `role_reference` - Reference to role. +* `context_filter_list` - The list of context filters. These are OR filters. The scope-expression-list defines the context, and the filter works in conjunction with the entity-expression-list. + +The context_list attribute supports the following: + +* `scope_filter_expression_list`: - (Optional) Filter the scope of an Access Control Policy. +* `entity_filter_expression_list` - (Required) A list of Entity filter expressions. + +### Scope Filter Expression List + +The scope_filter_expression_list attribute supports the following. + +* `left_hand_side`: - (Optional) The LHS of the filter expression - the scope type. +* `operator`: - (Required) The operator of the filter expression. +* `right_hand_side`: - (Required) The right hand side (RHS) of an scope expression. + + +### Entity Filter Expression List + +The scope_filter_expression_list attribute supports the following. + +* `left_hand_side_entity_type`: - (Optional) The LHS of the filter expression - the entity type. +* `operator`: - (Required) The operator in the filter expression. +* `right_hand_side`: - (Required) The right hand side (RHS) of an scope expression. + +### Right Hand Side + +The right_hand_side attribute supports the following. + +* `collection`: - (Optional) A representative term for supported groupings of entities. ALL = All the entities of a given kind. +* `categories`: - (Optional) The category values represented as a dictionary of key -> list of values. +* `uuid_list`: - (Optional) The explicit list of UUIDs for the given kind. + + ### Metadata The metadata attribute exports the following: @@ -143,4 +360,6 @@ The `project_reference`, `owner_reference` attributes supports the following: * `name` - (Optional) the name. * `uuid` - (Required) the UUID. +Note: Few attributes which are added to support ACPs for Project are dependent on PC version. Features such as VPC, Cluster Reference requires pc2022.4 while Tunnel Reference requires pc2022.6 . + See detailed information in [Nutanix Project](https://www.nutanix.dev/reference/prism_central/v3/api/projects/postprojects/).