Skip to content

Commit

Permalink
Add Principle Identity resource (#1021)
Browse files Browse the repository at this point in the history
This PR adds support of Principle Identity resource. Due to limitations
in API, PI in terraform is treated as immutable resource. Any changes
to the definition will force a resource recreation.

This change also refactos role schema. Now for both PI and role
bindings, roles for a path can be defined as a list, rather than sub
resources for convenience.

Signed-off-by: Shawn Wang <[email protected]>
  • Loading branch information
wsquan171 authored Nov 15, 2023
1 parent 18e6e5a commit dcb04f8
Show file tree
Hide file tree
Showing 8 changed files with 520 additions and 95 deletions.
1 change: 1 addition & 0 deletions nsxt/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ func Provider() *schema.Provider {
"nsxt_manager_cluster": resourceNsxtManagerCluster(),
"nsxt_policy_uplink_host_switch_profile": resourceNsxtUplinkHostSwitchProfile(),
"nsxt_node_user": resourceNsxtUsers(),
"nsxt_principle_identity": resourceNsxtPrincipleIdentity(),
"nsxt_transport_node": resourceNsxtTransportNode(),
"nsxt_failure_domain": resourceNsxtFailureDomain(),
"nsxt_cluster_virtual_ip": resourceNsxtClusterVirualIP(),
Expand Down
105 changes: 46 additions & 59 deletions nsxt/resource_nsxt_policy_role_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,17 @@ package nsxt
import (
"fmt"
"log"
"regexp"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/aaa"
nsxModel "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model"
)

// Only support local user at the moment
var roleBindingUserTypes = [](string){
nsxModel.RoleBinding_TYPE_LOCAL_USER,
nsxModel.RoleBinding_TYPE_REMOTE_USER,
nsxModel.RoleBinding_TYPE_REMOTE_GROUP,
nsxModel.RoleBinding_TYPE_PRINCIPAL_IDENTITY,
}

var roleBindingIdentitySourceTypes = [](string){
Expand Down Expand Up @@ -71,40 +68,31 @@ func resourceNsxtPolicyUserManagementRoleBinding() *schema.Resource {
Optional: true,
ValidateFunc: validation.StringInSlice(roleBindingIdentitySourceTypes, false),
},
"roles_for_path": {
Type: schema.TypeList,
Description: "List of roles that are associated with the user, limiting them to a path",
Required: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"path": {
Type: schema.TypeString,
Description: "Path of the entity in parent hierarchy.",
Required: true,
},
"role": {
Type: schema.TypeList,
Description: "Applicable roles",
Required: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"role": {
Type: schema.TypeString,
Description: "Short identifier for the role",
Required: true,
ValidateFunc: validation.StringMatch(
regexp.MustCompile(
`^[_a-z0-9-]+$`),
"Must be a valid role identifier matching: ^[_a-z0-9-]+$"),
},
"role_display_name": {
Type: schema.TypeString,
Description: "Display name for role",
Computed: true,
},
},
},
},
"roles_for_path": getRolesForPathSchema(false),
},
}
}

// getRolesForPathSchema return schema for RolesForPath, which is shared between role bindings and PI
func getRolesForPathSchema(forceNew bool) *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Description: "List of roles that are associated with the user, limiting them to a path",
Required: true,
ForceNew: forceNew,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"path": {
Type: schema.TypeString,
Description: "Path of the entity in parent hierarchy.",
Required: true,
},
"roles": {
Type: schema.TypeSet,
Description: "Applicable roles",
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
Expand All @@ -121,12 +109,10 @@ func getRolesForPathFromSchema(d *schema.ResourceData) rolesForPath {
for _, rolesPerPathInput := range rolesForPathInput {
data := rolesPerPathInput.(map[string]interface{})
path := data["path"].(string)
roles := data["role"].([]interface{})
roles := interface2StringList(data["roles"].(*schema.Set).List())
rolesPerPathMap := make(rolesPerPath)
for _, role := range roles {
roleData := role.(map[string]interface{})
roleInput := roleData["role"].(string)
rolesPerPathMap[roleInput] = true
rolesPerPathMap[role] = true
}
rolesForPathMap[path] = rolesPerPathMap
}
Expand All @@ -139,14 +125,11 @@ func setRolesForPathInSchema(d *schema.ResourceData, nsxRolesForPathList []nsxMo
for _, nsxRolesForPath := range nsxRolesForPathList {
elem := make(map[string]interface{})
elem["path"] = nsxRolesForPath.Path
var roles []map[string]interface{}
roles := make([]string, 0, len(nsxRolesForPath.Roles))
for _, nsxRole := range nsxRolesForPath.Roles {
rElem := make(map[string]interface{})
rElem["role"] = nsxRole.Role
rElem["role_display_name"] = nsxRole.RoleDisplayName
roles = append(roles, rElem)
roles = append(roles, *nsxRole.Role)
}
elem["role"] = roles
elem["roles"] = roles
rolesForPathList = append(rolesForPathList, elem)
}
err := d.Set("roles_for_path", rolesForPathList)
Expand All @@ -155,15 +138,8 @@ func setRolesForPathInSchema(d *schema.ResourceData, nsxRolesForPathList []nsxMo
}
}

func getRoleBindingObject(d *schema.ResourceData) *nsxModel.RoleBinding {
func getRolesForPathList(d *schema.ResourceData) []nsxModel.RolesForPath {
boolTrue := true
displayName := d.Get("display_name").(string)
description := d.Get("description").(string)
tags := getPolicyTagsFromSchema(d)
name := d.Get("name").(string)
identitySrcID := d.Get("identity_source_id").(string)
identitySrcType := d.Get("identity_source_type").(string)
roleBindingType := d.Get("type").(string)
rolesPerPathMap := getRolesForPathFromSchema(d)
nsxRolesForPaths := make([]nsxModel.RolesForPath, 0)

Expand Down Expand Up @@ -193,19 +169,30 @@ func getRoleBindingObject(d *schema.ResourceData) *nsxModel.RoleBinding {
continue
}
// Add one role in the list to make NSX happy
roles := data["role"].([]interface{})
roles := interface2StringList(data["roles"].(*schema.Set).List())
if len(roles) == 0 {
continue
}
roleData := roles[0].(map[string]interface{})
roleID := roleData["role"].(string)
nsxRolesForPaths = append(nsxRolesForPaths, nsxModel.RolesForPath{
Path: &path,
DeletePath: &boolTrue,
Roles: []nsxModel.Role{{Role: &roleID}},
Roles: []nsxModel.Role{{Role: &roles[0]}},
})
}
}
return nsxRolesForPaths
}

func getRoleBindingObject(d *schema.ResourceData) *nsxModel.RoleBinding {
boolTrue := true
displayName := d.Get("display_name").(string)
description := d.Get("description").(string)
tags := getPolicyTagsFromSchema(d)
name := d.Get("name").(string)
identitySrcID := d.Get("identity_source_id").(string)
identitySrcType := d.Get("identity_source_type").(string)
roleBindingType := d.Get("type").(string)
nsxRolesForPaths := getRolesForPathList(d)

obj := nsxModel.RoleBinding{
DisplayName: &displayName,
Expand Down
35 changes: 12 additions & 23 deletions nsxt/resource_nsxt_policy_role_binding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,12 @@ func TestAccResourceNsxtPolicyRoleBinding_basic(t *testing.T) {
resource.TestCheckResourceAttr(testResourceName, "identity_source_type", identType),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.#", "2"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.0.path", "/"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.0.role.#", "1"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.0.role.0.role", "auditor"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.0.roles.#", "1"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.0.roles.0", "auditor"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.1.path", "/orgs/default"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.1.role.#", "1"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.1.role.0.role", "org_admin"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.1.roles.#", "1"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.1.roles.0", "org_admin"),

resource.TestCheckResourceAttrSet(testResourceName, "nsx_id"),
resource.TestCheckResourceAttrSet(testResourceName, "revision"),
),
},
Expand All @@ -71,10 +70,9 @@ func TestAccResourceNsxtPolicyRoleBinding_basic(t *testing.T) {
resource.TestCheckResourceAttr(testResourceName, "identity_source_type", identType),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.#", "1"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.0.path", "/"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.0.role.#", "1"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.0.role.0.role", "auditor"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.0.roles.#", "1"),
resource.TestCheckResourceAttr(testResourceName, "roles_for_path.0.roles.0", "auditor"),

resource.TestCheckResourceAttrSet(testResourceName, "nsx_id"),
resource.TestCheckResourceAttrSet(testResourceName, "revision"),
),
},
Expand Down Expand Up @@ -172,19 +170,13 @@ resource "nsxt_policy_user_management_role_binding" "test" {
identity_source_type = "%s"
roles_for_path {
path = "/"
role {
role = "auditor"
}
path = "/"
roles = ["auditor"]
}
roles_for_path {
path = "/orgs/default"
role {
role = "org_admin"
}
path = "/orgs/default"
roles = ["org_admin"]
}
}`, attrMap["display_name"], attrMap["description"], user, userType, identType)
}
Expand All @@ -200,11 +192,8 @@ resource "nsxt_policy_user_management_role_binding" "test" {
identity_source_type = "%s"
roles_for_path {
path = "/"
role {
role = "auditor"
}
path = "/"
roles = ["auditor"]
}
}`, attrMap["display_name"], attrMap["description"], user, userType, identType)
}
Loading

0 comments on commit dcb04f8

Please sign in to comment.