Skip to content

Commit a89725c

Browse files
committed
Add cloudstack_role_permission resource
1 parent f19bffc commit a89725c

5 files changed

Lines changed: 375 additions & 0 deletions

File tree

cloudstack/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ func Provider() *schema.Provider {
162162
"cloudstack_domain": resourceCloudStackDomain(),
163163
"cloudstack_network_service_provider": resourceCloudStackNetworkServiceProvider(),
164164
"cloudstack_role": resourceCloudStackRole(),
165+
"cloudstack_role_permission": resourceCloudStackRolePermission(),
165166
"cloudstack_limits": resourceCloudStackLimits(),
166167
"cloudstack_snapshot_policy": resourceCloudStackSnapshotPolicy(),
167168
"cloudstack_quota_tariff": resourceCloudStackQuotaTariff(),
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package cloudstack
21+
22+
import (
23+
"fmt"
24+
"log"
25+
26+
"github.com/apache/cloudstack-go/v2/cloudstack"
27+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
28+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
29+
)
30+
31+
func resourceCloudStackRolePermission() *schema.Resource {
32+
return &schema.Resource{
33+
Create: resourceCloudStackRolePermissionCreate,
34+
Read: resourceCloudStackRolePermissionRead,
35+
Update: resourceCloudStackRolePermissionUpdate,
36+
Delete: resourceCloudStackRolePermissionDelete,
37+
Schema: map[string]*schema.Schema{
38+
"role_id": {
39+
Type: schema.TypeString,
40+
Required: true,
41+
ForceNew: true,
42+
Description: "ID of the role the permission (rule) belongs to.",
43+
},
44+
"rule": {
45+
Type: schema.TypeString,
46+
Required: true,
47+
ForceNew: true,
48+
Description: "The API name or wildcard (e.g. 'list*') the permission applies to.",
49+
},
50+
"permission": {
51+
Type: schema.TypeString,
52+
Required: true,
53+
ValidateFunc: validation.StringInSlice([]string{"allow", "deny"}, false),
54+
Description: "Whether the rule is allowed or denied. Valid options are: allow, deny.",
55+
},
56+
"description": {
57+
Type: schema.TypeString,
58+
Optional: true,
59+
ForceNew: true,
60+
Description: "A description for the role permission.",
61+
},
62+
},
63+
}
64+
}
65+
66+
func resourceCloudStackRolePermissionCreate(d *schema.ResourceData, meta interface{}) error {
67+
cs := meta.(*cloudstack.CloudStackClient)
68+
69+
roleID := d.Get("role_id").(string)
70+
rule := d.Get("rule").(string)
71+
permission := d.Get("permission").(string)
72+
73+
// Create a new parameter struct
74+
p := cs.Role.NewCreateRolePermissionParams(permission, roleID, rule)
75+
76+
if description, ok := d.GetOk("description"); ok {
77+
p.SetDescription(description.(string))
78+
}
79+
80+
log.Printf("[DEBUG] Creating Role Permission %s (%s) for role %s", rule, permission, roleID)
81+
r, err := cs.Role.CreateRolePermission(p)
82+
83+
if err != nil {
84+
return fmt.Errorf("Error creating Role Permission: %s", err)
85+
}
86+
87+
log.Printf("[DEBUG] Role Permission %s successfully created", rule)
88+
d.SetId(r.Id)
89+
90+
return resourceCloudStackRolePermissionRead(d, meta)
91+
}
92+
93+
func resourceCloudStackRolePermissionRead(d *schema.ResourceData, meta interface{}) error {
94+
cs := meta.(*cloudstack.CloudStackClient)
95+
96+
roleID := d.Get("role_id").(string)
97+
98+
// The API only supports listing permissions by role, so fetch them all
99+
// and locate the one matching this resource's ID.
100+
p := cs.Role.NewListRolePermissionsParams()
101+
p.SetRoleid(roleID)
102+
103+
l, err := cs.Role.ListRolePermissions(p)
104+
if err != nil {
105+
return fmt.Errorf("Error listing Role Permissions: %s", err)
106+
}
107+
108+
for _, rp := range l.RolePermissions {
109+
if rp.Id == d.Id() {
110+
d.Set("role_id", rp.Roleid)
111+
d.Set("rule", rp.Rule)
112+
d.Set("permission", rp.Permission)
113+
d.Set("description", rp.Description)
114+
return nil
115+
}
116+
}
117+
118+
log.Printf("[DEBUG] Role Permission %s no longer exists", d.Id())
119+
d.SetId("")
120+
121+
return nil
122+
}
123+
124+
func resourceCloudStackRolePermissionUpdate(d *schema.ResourceData, meta interface{}) error {
125+
cs := meta.(*cloudstack.CloudStackClient)
126+
127+
// Only the permission (allow/deny) can be changed in place; the role_id,
128+
// rule and description are all ForceNew.
129+
p := cs.Role.NewUpdateRolePermissionParams(d.Get("role_id").(string))
130+
p.SetRuleid(d.Id())
131+
p.SetPermission(d.Get("permission").(string))
132+
133+
log.Printf("[DEBUG] Updating Role Permission %s", d.Id())
134+
_, err := cs.Role.UpdateRolePermission(p)
135+
136+
if err != nil {
137+
return fmt.Errorf("Error updating Role Permission: %s", err)
138+
}
139+
140+
return resourceCloudStackRolePermissionRead(d, meta)
141+
}
142+
143+
func resourceCloudStackRolePermissionDelete(d *schema.ResourceData, meta interface{}) error {
144+
cs := meta.(*cloudstack.CloudStackClient)
145+
146+
// Create a new parameter struct
147+
p := cs.Role.NewDeleteRolePermissionParams(d.Id())
148+
149+
log.Printf("[DEBUG] Deleting Role Permission %s", d.Id())
150+
_, err := cs.Role.DeleteRolePermission(p)
151+
152+
if err != nil {
153+
return fmt.Errorf("Error deleting Role Permission: %s", err)
154+
}
155+
156+
return nil
157+
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package cloudstack
21+
22+
import (
23+
"fmt"
24+
"testing"
25+
26+
"github.com/apache/cloudstack-go/v2/cloudstack"
27+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
28+
"github.com/hashicorp/terraform-plugin-testing/terraform"
29+
)
30+
31+
func TestAccCloudStackRolePermission_basic(t *testing.T) {
32+
var rolePermission cloudstack.RolePermission
33+
34+
resource.Test(t, resource.TestCase{
35+
PreCheck: func() { testAccPreCheck(t) },
36+
Providers: testAccProviders,
37+
CheckDestroy: testAccCheckCloudStackRolePermissionDestroy,
38+
Steps: []resource.TestStep{
39+
{
40+
Config: testAccCloudStackRolePermission_basic,
41+
Check: resource.ComposeTestCheckFunc(
42+
testAccCheckCloudStackRolePermissionExists("cloudstack_role_permission.foo", &rolePermission),
43+
resource.TestCheckResourceAttr(
44+
"cloudstack_role_permission.foo", "rule", "listVirtualMachines"),
45+
resource.TestCheckResourceAttr(
46+
"cloudstack_role_permission.foo", "permission", "allow"),
47+
resource.TestCheckResourceAttr(
48+
"cloudstack_role_permission.foo", "description", "terraform test role permission"),
49+
),
50+
},
51+
{
52+
Config: testAccCloudStackRolePermission_update,
53+
Check: resource.ComposeTestCheckFunc(
54+
testAccCheckCloudStackRolePermissionExists("cloudstack_role_permission.foo", &rolePermission),
55+
resource.TestCheckResourceAttr(
56+
"cloudstack_role_permission.foo", "permission", "deny"),
57+
),
58+
},
59+
},
60+
})
61+
}
62+
63+
func testAccCheckCloudStackRolePermissionExists(n string, rolePermission *cloudstack.RolePermission) resource.TestCheckFunc {
64+
return func(s *terraform.State) error {
65+
rs, ok := s.RootModule().Resources[n]
66+
if !ok {
67+
return fmt.Errorf("Not found: %s", n)
68+
}
69+
70+
if rs.Primary.ID == "" {
71+
return fmt.Errorf("No Role Permission ID is set")
72+
}
73+
74+
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
75+
76+
p := cs.Role.NewListRolePermissionsParams()
77+
p.SetRoleid(rs.Primary.Attributes["role_id"])
78+
79+
l, err := cs.Role.ListRolePermissions(p)
80+
if err != nil {
81+
return err
82+
}
83+
84+
for _, rp := range l.RolePermissions {
85+
if rp.Id == rs.Primary.ID {
86+
*rolePermission = *rp
87+
return nil
88+
}
89+
}
90+
91+
return fmt.Errorf("Role Permission not found")
92+
}
93+
}
94+
95+
func testAccCheckCloudStackRolePermissionDestroy(s *terraform.State) error {
96+
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
97+
98+
for _, rs := range s.RootModule().Resources {
99+
if rs.Type != "cloudstack_role_permission" {
100+
continue
101+
}
102+
103+
if rs.Primary.ID == "" {
104+
return fmt.Errorf("No Role Permission ID is set")
105+
}
106+
107+
p := cs.Role.NewListRolePermissionsParams()
108+
p.SetRoleid(rs.Primary.Attributes["role_id"])
109+
110+
l, err := cs.Role.ListRolePermissions(p)
111+
if err != nil {
112+
// If the parent role is already gone, the permission is too.
113+
continue
114+
}
115+
116+
for _, rp := range l.RolePermissions {
117+
if rp.Id == rs.Primary.ID {
118+
return fmt.Errorf("Role Permission %s still exists", rs.Primary.ID)
119+
}
120+
}
121+
}
122+
123+
return nil
124+
}
125+
126+
const testAccCloudStackRolePermission_basic = `
127+
resource "cloudstack_role" "foo" {
128+
name = "terraform-role"
129+
type = "User"
130+
}
131+
132+
resource "cloudstack_role_permission" "foo" {
133+
role_id = cloudstack_role.foo.id
134+
rule = "listVirtualMachines"
135+
permission = "allow"
136+
description = "terraform test role permission"
137+
}
138+
`
139+
140+
const testAccCloudStackRolePermission_update = `
141+
resource "cloudstack_role" "foo" {
142+
name = "terraform-role"
143+
type = "User"
144+
}
145+
146+
resource "cloudstack_role_permission" "foo" {
147+
role_id = cloudstack_role.foo.id
148+
rule = "listVirtualMachines"
149+
permission = "deny"
150+
description = "terraform test role permission"
151+
}
152+
`

website/cloudstack.erb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@
132132
<li<%= sidebar_current("docs-cloudstack-resource-role") %>>
133133
<a href="/docs/providers/cloudstack/r/role.html">cloudstack_role</a>
134134
</li>
135+
136+
<li<%= sidebar_current("docs-cloudstack-resource-role-permission") %>>
137+
<a href="/docs/providers/cloudstack/r/role_permission.html">cloudstack_role_permission</a>
138+
</li>
135139
</ul>
136140
</li>
137141
</ul>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
layout: "cloudstack"
3+
page_title: "CloudStack: cloudstack_role_permission"
4+
description: |-
5+
Creates a role permission (rule) for a role.
6+
---
7+
8+
# cloudstack_role_permission
9+
10+
Creates a role permission. A role permission is a single rule that allows or
11+
denies a role access to an API (or a wildcard set of APIs).
12+
13+
Rules belonging to the same role are evaluated in the order in which they are
14+
created, and the first matching rule wins. Order the corresponding
15+
`cloudstack_role_permission` resources accordingly (for example with
16+
`depends_on`) when precedence matters.
17+
18+
## Example Usage
19+
20+
```hcl
21+
resource "cloudstack_role" "custom" {
22+
name = "custom-role"
23+
type = "User"
24+
}
25+
26+
# Allow listing virtual machines
27+
resource "cloudstack_role_permission" "list_vms" {
28+
role_id = cloudstack_role.custom.id
29+
rule = "listVirtualMachines"
30+
permission = "allow"
31+
description = "Allow listing virtual machines"
32+
}
33+
34+
# Deny every other API using a wildcard
35+
resource "cloudstack_role_permission" "deny_all" {
36+
role_id = cloudstack_role.custom.id
37+
rule = "*"
38+
permission = "deny"
39+
40+
depends_on = [cloudstack_role_permission.list_vms]
41+
}
42+
```
43+
44+
## Argument Reference
45+
46+
The following arguments are supported:
47+
48+
* `role_id` - (Required) ID of the role the permission belongs to. Changing this
49+
forces a new resource to be created.
50+
* `rule` - (Required) The API name or a wildcard (e.g. `list*` or `*`) the rule
51+
applies to. Changing this forces a new resource to be created.
52+
* `permission` - (Required) Whether the rule is allowed or denied. Valid options
53+
are: `allow`, `deny`.
54+
* `description` - (Optional) A description for the role permission. Changing this
55+
forces a new resource to be created.
56+
57+
## Attributes Reference
58+
59+
The following attributes are exported:
60+
61+
* `id` - The ID of the role permission.

0 commit comments

Comments
 (0)