This is an automated email from the ASF dual-hosted git repository.

sureshanaparti pushed a commit to branch main
in repository 
https://gitbox.apache.org/repos/asf/cloudstack-terraform-provider.git


The following commit(s) were added to refs/heads/main by this push:
     new add8c3a  Add cloudstack_role_permission resource (#300)
add8c3a is described below

commit add8c3a4ab26f2aad2b037719e6bc752f9c81007
Author: bddvlpr <[email protected]>
AuthorDate: Mon Aug 17 08:39:51 2026 +0200

    Add cloudstack_role_permission resource (#300)
    
    * Add cloudstack_role_permission resource
    
    * Fix ordering of permissions by using a list with (optionally) 
authoritative management
---
 cloudstack/provider.go                             |   1 +
 cloudstack/resource_cloudstack_role_permission.go  | 455 +++++++++++++++++++++
 .../resource_cloudstack_role_permission_test.go    | 378 +++++++++++++++++
 website/cloudstack.erb                             |   4 +
 website/docs/r/role_permission.html.markdown       |  67 +++
 5 files changed, 905 insertions(+)

diff --git a/cloudstack/provider.go b/cloudstack/provider.go
index aea132a..bad42dc 100644
--- a/cloudstack/provider.go
+++ b/cloudstack/provider.go
@@ -164,6 +164,7 @@ func Provider() *schema.Provider {
                        "cloudstack_domain":                         
resourceCloudStackDomain(),
                        "cloudstack_network_service_provider":       
resourceCloudStackNetworkServiceProvider(),
                        "cloudstack_role":                           
resourceCloudStackRole(),
+                       "cloudstack_role_permission":                
resourceCloudStackRolePermission(),
                        "cloudstack_limits":                         
resourceCloudStackLimits(),
                        "cloudstack_snapshot_policy":                
resourceCloudStackSnapshotPolicy(),
                        "cloudstack_quota_tariff":                   
resourceCloudStackQuotaTariff(),
diff --git a/cloudstack/resource_cloudstack_role_permission.go 
b/cloudstack/resource_cloudstack_role_permission.go
new file mode 100644
index 0000000..1769f8d
--- /dev/null
+++ b/cloudstack/resource_cloudstack_role_permission.go
@@ -0,0 +1,455 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package cloudstack
+
+import (
+       "fmt"
+       "log"
+       "sync"
+
+       "github.com/apache/cloudstack-go/v2/cloudstack"
+       "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+       "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+)
+
+var rolePermissionLocks sync.Map
+
+type rolePermissionSpec struct {
+       ID          string
+       Rule        string
+       Permission  string
+       Description string
+}
+
+func resourceCloudStackRolePermission() *schema.Resource {
+       return &schema.Resource{
+               Create: resourceCloudStackRolePermissionCreate,
+               Read:   resourceCloudStackRolePermissionRead,
+               Update: resourceCloudStackRolePermissionUpdate,
+               Delete: resourceCloudStackRolePermissionDelete,
+               Schema: map[string]*schema.Schema{
+                       "role_id": {
+                               Type:        schema.TypeString,
+                               Required:    true,
+                               ForceNew:    true,
+                               Description: "ID of the role the permissions 
belong to.",
+                       },
+                       "authoritative": {
+                               Type:        schema.TypeBool,
+                               Optional:    true,
+                               Default:     false,
+                               Description: "Whether permissions not declared 
in this resource should be deleted.",
+                       },
+                       "permission": {
+                               Type:        schema.TypeList,
+                               Optional:    true,
+                               Description: "Ordered list of role permission 
rules. Rules are evaluated from top to bottom.",
+                               Elem: &schema.Resource{
+                                       Schema: map[string]*schema.Schema{
+                                               "id": {
+                                                       Type:        
schema.TypeString,
+                                                       Computed:    true,
+                                                       Description: "ID of the 
role permission.",
+                                               },
+                                               "rule": {
+                                                       Type:        
schema.TypeString,
+                                                       Required:    true,
+                                                       Description: "The API 
name or wildcard (e.g. 'list*') the permission applies to.",
+                                               },
+                                               "permission": {
+                                                       Type:         
schema.TypeString,
+                                                       Required:     true,
+                                                       ValidateFunc: 
validation.StringInSlice([]string{"allow", "deny"}, false),
+                                                       Description:  "Whether 
the rule is allowed or denied. Valid options are: allow, deny.",
+                                               },
+                                               "description": {
+                                                       Type:        
schema.TypeString,
+                                                       Optional:    true,
+                                                       Description: "A 
description for the role permission.",
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+}
+
+func resourceCloudStackRolePermissionCreate(d *schema.ResourceData, meta 
interface{}) error {
+       roleID := d.Get("role_id").(string)
+       d.SetId(roleID)
+
+       roleLock := rolePermissionLock(roleID)
+       roleLock.Lock()
+       defer roleLock.Unlock()
+
+       if err := reconcileCloudStackRolePermissions(d, meta, nil); err != nil {
+               return err
+       }
+
+       return resourceCloudStackRolePermissionRead(d, meta)
+}
+
+func resourceCloudStackRolePermissionRead(d *schema.ResourceData, meta 
interface{}) error {
+       cs := meta.(*cloudstack.CloudStackClient)
+       roleID := d.Get("role_id").(string)
+       if roleID == "" {
+               roleID = d.Id()
+       }
+
+       rolePermissions, err := listCloudStackRolePermissions(cs, roleID)
+       if err != nil {
+               return fmt.Errorf("Error listing Role Permissions: %s", err)
+       }
+
+       permissionsByID := make(map[string]*cloudstack.RolePermission)
+       for _, rp := range rolePermissions {
+               permissionsByID[rp.Id] = rp
+       }
+
+       var missing bool
+       var readPermissions []interface{}
+       used := make(map[string]bool)
+
+       for _, desired := range 
rolePermissionSpecs(d.Get("permission").([]interface{})) {
+               var rp *cloudstack.RolePermission
+               if desired.ID != "" {
+                       rp = permissionsByID[desired.ID]
+               }
+               if rp == nil {
+                       rp = findMatchingRolePermission(rolePermissions, 
desired, used)
+               }
+               if rp == nil {
+                       missing = true
+                       readPermissions = append(readPermissions, 
rolePermissionState(desired))
+                       continue
+               }
+
+               used[rp.Id] = true
+               readPermissions = append(readPermissions, 
rolePermissionState(rolePermissionSpec{
+                       ID:          rp.Id,
+                       Rule:        rp.Rule,
+                       Permission:  rp.Permission,
+                       Description: rp.Description,
+               }))
+       }
+
+       if err := d.Set("permission", readPermissions); err != nil {
+               return fmt.Errorf("Error setting Role Permissions: %s", err)
+       }
+
+       if missing {
+               log.Printf("[DEBUG] One or more Role Permissions for role %s no 
longer exist", roleID)
+               d.SetId("")
+       }
+
+       return nil
+}
+
+func resourceCloudStackRolePermissionUpdate(d *schema.ResourceData, meta 
interface{}) error {
+       roleID := d.Get("role_id").(string)
+       roleLock := rolePermissionLock(roleID)
+       roleLock.Lock()
+       defer roleLock.Unlock()
+
+       var oldPermissions []rolePermissionSpec
+       if d.HasChange("permission") {
+               oldRaw, _ := d.GetChange("permission")
+               oldPermissions = rolePermissionSpecs(oldRaw.([]interface{}))
+       }
+
+       if err := reconcileCloudStackRolePermissions(d, meta, oldPermissions); 
err != nil {
+               return err
+       }
+
+       return resourceCloudStackRolePermissionRead(d, meta)
+}
+
+func resourceCloudStackRolePermissionDelete(d *schema.ResourceData, meta 
interface{}) error {
+       cs := meta.(*cloudstack.CloudStackClient)
+       roleID := d.Get("role_id").(string)
+
+       roleLock := rolePermissionLock(roleID)
+       roleLock.Lock()
+       defer roleLock.Unlock()
+
+       rolePermissions, err := listCloudStackRolePermissions(cs, roleID)
+       if err != nil {
+               return fmt.Errorf("Error listing Role Permissions: %s", err)
+       }
+
+       if d.Get("authoritative").(bool) {
+               for _, rp := range rolePermissions {
+                       if err := deleteCloudStackRolePermission(cs, rp.Id); 
err != nil {
+                               return err
+                       }
+               }
+               return nil
+       }
+
+       rolePermissionsByID := make(map[string]*cloudstack.RolePermission)
+       for _, rp := range rolePermissions {
+               rolePermissionsByID[rp.Id] = rp
+       }
+
+       used := make(map[string]bool)
+       for _, permission := range 
rolePermissionSpecs(d.Get("permission").([]interface{})) {
+               ruleID := permission.ID
+               if ruleID == "" {
+                       if rp := findMatchingRolePermission(rolePermissions, 
permission, used); rp != nil {
+                               ruleID = rp.Id
+                       }
+               }
+               if ruleID == "" || rolePermissionsByID[ruleID] == nil {
+                       continue
+               }
+               used[ruleID] = true
+               if err := deleteCloudStackRolePermission(cs, ruleID); err != 
nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
+func reconcileCloudStackRolePermissions(d *schema.ResourceData, meta 
interface{}, oldPermissions []rolePermissionSpec) error {
+       cs := meta.(*cloudstack.CloudStackClient)
+       roleID := d.Get("role_id").(string)
+
+       rolePermissions, err := listCloudStackRolePermissions(cs, roleID)
+       if err != nil {
+               return fmt.Errorf("Error listing Role Permissions: %s", err)
+       }
+
+       rolePermissionsByID := make(map[string]*cloudstack.RolePermission)
+       for _, rp := range rolePermissions {
+               rolePermissionsByID[rp.Id] = rp
+       }
+
+       used := make(map[string]bool)
+       deleted := make(map[string]bool)
+       managedIDs := make([]string, 0)
+       managedIDSet := make(map[string]bool)
+
+       for _, desired := range 
rolePermissionSpecs(d.Get("permission").([]interface{})) {
+               rp := rolePermissionsByID[desired.ID]
+               if rp != nil && (rp.Rule != desired.Rule || rp.Description != 
desired.Description) {
+                       if exactMatch := 
findMatchingRolePermission(rolePermissions, desired, used); exactMatch != nil {
+                               rp = exactMatch
+                       } else {
+                               if err := deleteCloudStackRolePermission(cs, 
rp.Id); err != nil {
+                                       return err
+                               }
+                               deleted[rp.Id] = true
+                               used[rp.Id] = true
+                               rp = nil
+                       }
+               } else if rp == nil {
+                       rp = findMatchingRolePermission(rolePermissions, 
desired, used)
+               }
+
+               if rp == nil {
+                       rp, err = createCloudStackRolePermission(cs, roleID, 
desired)
+                       if err != nil {
+                               return err
+                       }
+               } else if rp.Permission != desired.Permission {
+                       if err := updateCloudStackRolePermission(cs, roleID, 
rp.Id, desired.Permission); err != nil {
+                               return err
+                       }
+               }
+
+               used[rp.Id] = true
+               managedIDs = append(managedIDs, rp.Id)
+               managedIDSet[rp.Id] = true
+       }
+
+       oldManagedIDs := make(map[string]bool)
+       for _, oldPermission := range oldPermissions {
+               if oldPermission.ID != "" {
+                       oldManagedIDs[oldPermission.ID] = true
+               }
+       }
+
+       if d.Get("authoritative").(bool) {
+               for _, rp := range rolePermissions {
+                       if managedIDSet[rp.Id] || deleted[rp.Id] {
+                               continue
+                       }
+                       if err := deleteCloudStackRolePermission(cs, rp.Id); 
err != nil {
+                               return err
+                       }
+                       deleted[rp.Id] = true
+               }
+       } else {
+               for oldID := range oldManagedIDs {
+                       if managedIDSet[oldID] || deleted[oldID] {
+                               continue
+                       }
+                       if rolePermissionsByID[oldID] == nil {
+                               continue
+                       }
+                       if err := deleteCloudStackRolePermission(cs, oldID); 
err != nil {
+                               return err
+                       }
+                       deleted[oldID] = true
+               }
+       }
+
+       rolePermissions, err = listCloudStackRolePermissions(cs, roleID)
+       if err != nil {
+               return fmt.Errorf("Error listing Role Permissions: %s", err)
+       }
+
+       ruleOrder := append([]string{}, managedIDs...)
+       for _, rp := range rolePermissions {
+               if !managedIDSet[rp.Id] {
+                       ruleOrder = append(ruleOrder, rp.Id)
+               }
+       }
+
+       if err := orderCloudStackRolePermissions(cs, roleID, ruleOrder); err != 
nil {
+               return err
+       }
+
+       return nil
+}
+
+func listCloudStackRolePermissions(cs *cloudstack.CloudStackClient, roleID 
string) ([]*cloudstack.RolePermission, error) {
+       p := cs.Role.NewListRolePermissionsParams()
+       p.SetRoleid(roleID)
+
+       l, err := cs.Role.ListRolePermissions(p)
+       if err != nil {
+               return nil, err
+       }
+
+       return l.RolePermissions, nil
+}
+
+func createCloudStackRolePermission(cs *cloudstack.CloudStackClient, roleID 
string, permission rolePermissionSpec) (*cloudstack.RolePermission, error) {
+       p := cs.Role.NewCreateRolePermissionParams(permission.Permission, 
roleID, permission.Rule)
+       if permission.Description != "" {
+               p.SetDescription(permission.Description)
+       }
+
+       log.Printf("[DEBUG] Creating Role Permission %s (%s) for role %s", 
permission.Rule, permission.Permission, roleID)
+       r, err := cs.Role.CreateRolePermission(p)
+       if err != nil {
+               return nil, fmt.Errorf("Error creating Role Permission: %s", 
err)
+       }
+
+       return &cloudstack.RolePermission{
+               Id:          r.Id,
+               Roleid:      roleID,
+               Rule:        permission.Rule,
+               Permission:  permission.Permission,
+               Description: permission.Description,
+       }, nil
+}
+
+func updateCloudStackRolePermission(cs *cloudstack.CloudStackClient, roleID, 
ruleID, permission string) error {
+       p := cs.Role.NewUpdateRolePermissionParams(roleID)
+       p.SetRuleid(ruleID)
+       p.SetPermission(permission)
+
+       log.Printf("[DEBUG] Updating Role Permission %s", ruleID)
+       if _, err := cs.Role.UpdateRolePermission(p); err != nil {
+               return fmt.Errorf("Error updating Role Permission: %s", err)
+       }
+
+       return nil
+}
+
+func deleteCloudStackRolePermission(cs *cloudstack.CloudStackClient, ruleID 
string) error {
+       p := cs.Role.NewDeleteRolePermissionParams(ruleID)
+
+       log.Printf("[DEBUG] Deleting Role Permission %s", ruleID)
+       if _, err := cs.Role.DeleteRolePermission(p); err != nil {
+               return fmt.Errorf("Error deleting Role Permission: %s", err)
+       }
+
+       return nil
+}
+
+func orderCloudStackRolePermissions(cs *cloudstack.CloudStackClient, roleID 
string, ruleIDs []string) error {
+       if len(ruleIDs) == 0 {
+               return nil
+       }
+
+       p := cs.Role.NewUpdateRolePermissionParams(roleID)
+       p.SetRuleorder(ruleIDs)
+
+       log.Printf("[DEBUG] Ordering Role Permissions for role %s: %v", roleID, 
ruleIDs)
+       if _, err := cs.Role.UpdateRolePermission(p); err != nil {
+               return fmt.Errorf("Error ordering Role Permissions: %s", err)
+       }
+
+       return nil
+}
+
+func rolePermissionSpecs(raw []interface{}) []rolePermissionSpec {
+       permissions := make([]rolePermissionSpec, 0, len(raw))
+       for _, item := range raw {
+               permissionMap := item.(map[string]interface{})
+               permissions = append(permissions, rolePermissionSpec{
+                       ID:          rolePermissionString(permissionMap, "id"),
+                       Rule:        rolePermissionString(permissionMap, 
"rule"),
+                       Permission:  rolePermissionString(permissionMap, 
"permission"),
+                       Description: rolePermissionString(permissionMap, 
"description"),
+               })
+       }
+
+       return permissions
+}
+
+func rolePermissionState(permission rolePermissionSpec) map[string]interface{} 
{
+       return map[string]interface{}{
+               "id":          permission.ID,
+               "rule":        permission.Rule,
+               "permission":  permission.Permission,
+               "description": permission.Description,
+       }
+}
+
+func findMatchingRolePermission(rolePermissions []*cloudstack.RolePermission, 
desired rolePermissionSpec, used map[string]bool) *cloudstack.RolePermission {
+       for _, rp := range rolePermissions {
+               if used[rp.Id] {
+                       continue
+               }
+               if rp.Rule == desired.Rule && rp.Description == 
desired.Description {
+                       return rp
+               }
+       }
+
+       return nil
+}
+
+func rolePermissionLock(roleID string) *sync.Mutex {
+       lock, _ := rolePermissionLocks.LoadOrStore(roleID, &sync.Mutex{})
+       return lock.(*sync.Mutex)
+}
+
+func rolePermissionString(permission map[string]interface{}, key string) 
string {
+       if value, ok := permission[key].(string); ok {
+               return value
+       }
+
+       return ""
+}
diff --git a/cloudstack/resource_cloudstack_role_permission_test.go 
b/cloudstack/resource_cloudstack_role_permission_test.go
new file mode 100644
index 0000000..18eeee6
--- /dev/null
+++ b/cloudstack/resource_cloudstack_role_permission_test.go
@@ -0,0 +1,378 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package cloudstack
+
+import (
+       "fmt"
+       "testing"
+
+       "github.com/apache/cloudstack-go/v2/cloudstack"
+       "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+       "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+func TestAccCloudStackRolePermission_basic(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackRolePermissionDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackRolePermission_basic,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackRolePermissionExists("cloudstack_role_permission.foo"),
+                                       
testAccCheckCloudStackRolePermissionOrder("cloudstack_role_permission.foo", 
[]string{"listVirtualMachines"}),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_role_permission.foo", "permission.0.rule", "listVirtualMachines"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_role_permission.foo", "permission.0.permission", "allow"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_role_permission.foo", "permission.0.description", "terraform test 
role permission"),
+                               ),
+                       },
+                       {
+                               Config: testAccCloudStackRolePermission_update,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackRolePermissionExists("cloudstack_role_permission.foo"),
+                                       
testAccCheckCloudStackRolePermissionOrder("cloudstack_role_permission.foo", 
[]string{"listVirtualMachines"}),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_role_permission.foo", "permission.0.permission", "deny"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackRolePermission_orderAfterRecreate(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackRolePermissionDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: 
testAccCloudStackRolePermission_orderWithSpecificRule,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackRolePermissionExists("cloudstack_role_permission.foo"),
+                                       
testAccCheckCloudStackRolePermissionOrder("cloudstack_role_permission.foo", 
[]string{"listZones", "*"}),
+                               ),
+                       },
+                       {
+                               Config: 
testAccCloudStackRolePermission_orderWithoutSpecificRule,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackRolePermissionExists("cloudstack_role_permission.foo"),
+                                       
testAccCheckCloudStackRolePermissionOrder("cloudstack_role_permission.foo", 
[]string{"*"}),
+                               ),
+                       },
+                       {
+                               Config: 
testAccCloudStackRolePermission_orderWithSpecificRule,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackRolePermissionExists("cloudstack_role_permission.foo"),
+                                       
testAccCheckCloudStackRolePermissionOrder("cloudstack_role_permission.foo", 
[]string{"listZones", "*"}),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackRolePermission_authoritative(t *testing.T) {
+       var externalRuleID string
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackRolePermissionDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackRolePermission_subset,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackRolePermissionExists("cloudstack_role_permission.foo"),
+                                       
testAccCreateCloudStackRolePermission("cloudstack_role_permission.foo", 
"listVirtualMachines", "allow", "external role permission", &externalRuleID),
+                               ),
+                       },
+                       {
+                               Config: testAccCloudStackRolePermission_subset,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackRolePermissionExists("cloudstack_role_permission.foo"),
+                                       
testAccCheckCloudStackRolePermissionRuleExists("cloudstack_role_permission.foo",
 &externalRuleID),
+                               ),
+                       },
+                       {
+                               Config: 
testAccCloudStackRolePermission_authoritative,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackRolePermissionExists("cloudstack_role_permission.foo"),
+                                       
testAccCheckCloudStackRolePermissionRuleMissing("cloudstack_role_permission.foo",
 &externalRuleID),
+                               ),
+                       },
+               },
+       })
+}
+
+func testAccCheckCloudStackRolePermissionExists(n string) 
resource.TestCheckFunc {
+       return func(s *terraform.State) error {
+               rs, ok := s.RootModule().Resources[n]
+               if !ok {
+                       return fmt.Errorf("Not found: %s", n)
+               }
+
+               if rs.Primary.ID == "" {
+                       return fmt.Errorf("No Role Permission ID is set")
+               }
+
+               cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
+
+               p := cs.Role.NewListRolePermissionsParams()
+               p.SetRoleid(rs.Primary.Attributes["role_id"])
+
+               if _, err := cs.Role.ListRolePermissions(p); err != nil {
+                       return err
+               }
+
+               return nil
+       }
+}
+
+func testAccCheckCloudStackRolePermissionOrder(n string, rules []string) 
resource.TestCheckFunc {
+       return func(s *terraform.State) error {
+               rolePermissions, err := testAccListCloudStackRolePermissions(s, 
n)
+               if err != nil {
+                       return err
+               }
+               if len(rolePermissions) < len(rules) {
+                       return fmt.Errorf("Expected at least %d Role 
Permissions, got %d", len(rules), len(rolePermissions))
+               }
+
+               for i, rule := range rules {
+                       if rolePermissions[i].Rule != rule {
+                               return fmt.Errorf("Expected Role Permission 
rule %d to be %q, got %q", i, rule, rolePermissions[i].Rule)
+                       }
+               }
+
+               return nil
+       }
+}
+
+func testAccCreateCloudStackRolePermission(n, rule, permission, description 
string, ruleID *string) resource.TestCheckFunc {
+       return func(s *terraform.State) error {
+               rs, ok := s.RootModule().Resources[n]
+               if !ok {
+                       return fmt.Errorf("Not found: %s", n)
+               }
+
+               cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
+               p := cs.Role.NewCreateRolePermissionParams(permission, 
rs.Primary.Attributes["role_id"], rule)
+               p.SetDescription(description)
+
+               r, err := cs.Role.CreateRolePermission(p)
+               if err != nil {
+                       return err
+               }
+
+               *ruleID = r.Id
+               return nil
+       }
+}
+
+func testAccCheckCloudStackRolePermissionRuleExists(n string, ruleID *string) 
resource.TestCheckFunc {
+       return func(s *terraform.State) error {
+               if *ruleID == "" {
+                       return fmt.Errorf("No external Role Permission ID is 
set")
+               }
+
+               rolePermissions, err := testAccListCloudStackRolePermissions(s, 
n)
+               if err != nil {
+                       return err
+               }
+
+               for _, rp := range rolePermissions {
+                       if rp.Id == *ruleID {
+                               return nil
+                       }
+               }
+
+               return fmt.Errorf("Role Permission %s not found", *ruleID)
+       }
+}
+
+func testAccCheckCloudStackRolePermissionRuleMissing(n string, ruleID *string) 
resource.TestCheckFunc {
+       return func(s *terraform.State) error {
+               if *ruleID == "" {
+                       return fmt.Errorf("No external Role Permission ID is 
set")
+               }
+
+               rolePermissions, err := testAccListCloudStackRolePermissions(s, 
n)
+               if err != nil {
+                       return err
+               }
+
+               for _, rp := range rolePermissions {
+                       if rp.Id == *ruleID {
+                               return fmt.Errorf("Role Permission %s still 
exists", *ruleID)
+                       }
+               }
+
+               return nil
+       }
+}
+
+func testAccListCloudStackRolePermissions(s *terraform.State, n string) 
([]*cloudstack.RolePermission, error) {
+       rs, ok := s.RootModule().Resources[n]
+       if !ok {
+               return nil, fmt.Errorf("Not found: %s", n)
+       }
+
+       cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
+       p := cs.Role.NewListRolePermissionsParams()
+       p.SetRoleid(rs.Primary.Attributes["role_id"])
+
+       l, err := cs.Role.ListRolePermissions(p)
+       if err != nil {
+               return nil, err
+       }
+
+       return l.RolePermissions, nil
+}
+
+func testAccCheckCloudStackRolePermissionDestroy(s *terraform.State) error {
+       cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
+
+       for _, rs := range s.RootModule().Resources {
+               if rs.Type != "cloudstack_role_permission" {
+                       continue
+               }
+
+               if rs.Primary.ID == "" {
+                       return fmt.Errorf("No Role Permission ID is set")
+               }
+
+               p := cs.Role.NewListRolePermissionsParams()
+               p.SetRoleid(rs.Primary.Attributes["role_id"])
+
+               if _, err := cs.Role.ListRolePermissions(p); err != nil {
+                       // If the parent role is already gone, the permissions 
are too.
+                       continue
+               }
+       }
+
+       return nil
+}
+
+const testAccCloudStackRolePermission_basic = `
+resource "cloudstack_role" "foo" {
+  name = "terraform-role"
+  type = "User"
+}
+
+resource "cloudstack_role_permission" "foo" {
+  role_id = cloudstack_role.foo.id
+
+  permission {
+    rule        = "listVirtualMachines"
+    permission  = "allow"
+    description = "terraform test role permission"
+  }
+}
+`
+
+const testAccCloudStackRolePermission_update = `
+resource "cloudstack_role" "foo" {
+  name = "terraform-role"
+  type = "User"
+}
+
+resource "cloudstack_role_permission" "foo" {
+  role_id = cloudstack_role.foo.id
+
+  permission {
+    rule        = "listVirtualMachines"
+    permission  = "deny"
+    description = "terraform test role permission"
+  }
+}
+`
+
+const testAccCloudStackRolePermission_orderWithSpecificRule = `
+resource "cloudstack_role" "foo" {
+  name = "terraform-role"
+  type = "User"
+}
+
+resource "cloudstack_role_permission" "foo" {
+  role_id = cloudstack_role.foo.id
+
+  permission {
+    rule       = "listZones"
+    permission = "allow"
+  }
+
+  permission {
+    rule       = "*"
+    permission = "deny"
+  }
+}
+`
+
+const testAccCloudStackRolePermission_orderWithoutSpecificRule = `
+resource "cloudstack_role" "foo" {
+  name = "terraform-role"
+  type = "User"
+}
+
+resource "cloudstack_role_permission" "foo" {
+  role_id = cloudstack_role.foo.id
+
+  permission {
+    rule       = "*"
+    permission = "deny"
+  }
+}
+`
+
+const testAccCloudStackRolePermission_subset = `
+resource "cloudstack_role" "foo" {
+  name = "terraform-role"
+  type = "User"
+}
+
+resource "cloudstack_role_permission" "foo" {
+  role_id = cloudstack_role.foo.id
+
+  permission {
+    rule       = "listZones"
+    permission = "allow"
+  }
+}
+`
+
+const testAccCloudStackRolePermission_authoritative = `
+resource "cloudstack_role" "foo" {
+  name = "terraform-role"
+  type = "User"
+}
+
+resource "cloudstack_role_permission" "foo" {
+  role_id       = cloudstack_role.foo.id
+  authoritative = true
+
+  permission {
+    rule       = "listZones"
+    permission = "allow"
+  }
+}
+`
diff --git a/website/cloudstack.erb b/website/cloudstack.erb
index 1ea7f7e..06b6a7c 100644
--- a/website/cloudstack.erb
+++ b/website/cloudstack.erb
@@ -132,6 +132,10 @@
                         <li<%= 
sidebar_current("docs-cloudstack-resource-role") %>>
                             <a 
href="/docs/providers/cloudstack/r/role.html">cloudstack_role</a>
                         </li>
+
+                        <li<%= 
sidebar_current("docs-cloudstack-resource-role-permission") %>>
+                            <a 
href="/docs/providers/cloudstack/r/role_permission.html">cloudstack_role_permission</a>
+                        </li>
                     </ul>
                 </li>
             </ul>
diff --git a/website/docs/r/role_permission.html.markdown 
b/website/docs/r/role_permission.html.markdown
new file mode 100644
index 0000000..090d927
--- /dev/null
+++ b/website/docs/r/role_permission.html.markdown
@@ -0,0 +1,67 @@
+---
+layout: "cloudstack"
+page_title: "CloudStack: cloudstack_role_permission"
+description: |-
+  Manages ordered role permissions for a role.
+---
+
+# cloudstack_role_permission
+
+Manages an ordered list of role permissions for a role. A role permission is a
+single rule that allows or denies access to an API or wildcard set of APIs.
+
+Rules belonging to the same role are evaluated in order, and the first matching
+rule wins. This resource stores that order explicitly and reapplies it after
+rules are added, removed, or recreated.
+
+By default, only the permissions declared in this resource are managed.
+Undeclared permissions on the role are preserved and ordered after the declared
+permissions. Set `authoritative = true` to delete undeclared permissions and
+make the CloudStack role permission list exactly match this resource.
+
+## Example Usage
+
+```hcl
+resource "cloudstack_role" "custom" {
+  name = "custom-role"
+  type = "User"
+}
+
+resource "cloudstack_role_permission" "custom" {
+  role_id = cloudstack_role.custom.id
+
+  permission {
+    rule        = "listVirtualMachines"
+    permission  = "allow"
+    description = "Allow listing virtual machines"
+  }
+
+  permission {
+    rule       = "*"
+    permission = "deny"
+  }
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `role_id` - (Required) ID of the role the permissions belong to. Changing 
this
+  forces a new resource to be created.
+* `authoritative` - (Optional) Whether permissions not declared in this 
resource
+  should be deleted. Defaults to `false`.
+* `permission` - (Optional) Ordered list of role permission rules. Each block
+  supports the following:
+  * `rule` - (Required) The API name or a wildcard (e.g. `list*` or `*`) the
+    rule applies to.
+  * `permission` - (Required) Whether the rule is allowed or denied. Valid
+    options are: `allow`, `deny`.
+  * `description` - (Optional) A description for the role permission.
+
+## Attributes Reference
+
+The following attributes are exported:
+
+* `id` - The role ID.
+* `permission.*.id` - The ID of each role permission rule.

Reply via email to