Skip to content

Commit 35b2997

Browse files
Codelaxremyleone
andauthored
feat(iam): add iam policy resource (#1344)
Co-authored-by: Rémy Léone <[email protected]>
1 parent 56f10e2 commit 35b2997

9 files changed

+3477
-1
lines changed

scaleway/helpers_iam.go

+53-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,61 @@
11
package scaleway
22

3-
import iam "github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1"
3+
import (
4+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
5+
iam "github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1"
6+
"github.com/scaleway/scaleway-sdk-go/scw"
7+
)
48

59
// instanceAPIWithZone returns a new iam API for a Create request
610
func iamAPI(m interface{}) *iam.API {
711
meta := m.(*Meta)
812
return iam.NewAPI(meta.scwClient)
913
}
14+
15+
func expandPermissionSetNames(rawPermissions interface{}) *[]string {
16+
permissions := []string{}
17+
permissionSet := rawPermissions.(*schema.Set)
18+
for _, rawPermission := range permissionSet.List() {
19+
permissions = append(permissions, rawPermission.(string))
20+
}
21+
return &permissions
22+
}
23+
24+
func expandPolicyRuleSpecs(d interface{}) []*iam.RuleSpecs {
25+
rules := []*iam.RuleSpecs(nil)
26+
rawRules := d.([]interface{})
27+
for _, rawRule := range rawRules {
28+
mapRule := rawRule.(map[string]interface{})
29+
rule := &iam.RuleSpecs{
30+
PermissionSetNames: expandPermissionSetNames(mapRule["permission_set_names"]),
31+
}
32+
if orgID, orgIDExists := mapRule["organization_id"]; orgIDExists && orgID.(string) != "" {
33+
rule.OrganizationID = scw.StringPtr(orgID.(string))
34+
}
35+
if projIDs, projIDsExists := mapRule["project_ids"]; projIDsExists {
36+
rule.ProjectIDs = expandStringsPtr(projIDs)
37+
}
38+
rules = append(rules, rule)
39+
}
40+
return rules
41+
}
42+
43+
func flattenPolicyRules(rules []*iam.Rule) interface{} {
44+
rawRules := []interface{}(nil)
45+
for _, rule := range rules {
46+
rawRule := map[string]interface{}{}
47+
if rule.OrganizationID != nil {
48+
rawRule["organization_id"] = flattenStringPtr(rule.OrganizationID)
49+
} else {
50+
rawRule["organization_id"] = nil
51+
}
52+
if rule.ProjectIDs != nil {
53+
rawRule["project_ids"] = flattenSliceString(*rule.ProjectIDs)
54+
}
55+
if rule.PermissionSetNames != nil {
56+
rawRule["permission_set_names"] = flattenSliceString(*rule.PermissionSetNames)
57+
}
58+
rawRules = append(rawRules, rawRule)
59+
}
60+
return rawRules
61+
}

scaleway/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ func addBetaResources(provider *schema.Provider) {
3535
"scaleway_iam_api_key": resourceScalewayIamAPIKey(),
3636
"scaleway_iam_application": resourceScalewayIamApplication(),
3737
"scaleway_iam_group": resourceScalewayIamGroup(),
38+
"scaleway_iam_policy": resourceScalewayIamPolicy(),
3839
"scaleway_iam_ssh_key": resourceScalewayIamSSKKey(),
3940
}
4041
betaDataSources := map[string]*schema.Resource{

scaleway/resource_iam_policy.go

+235
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package scaleway
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
iam "github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1"
10+
"github.com/scaleway/scaleway-sdk-go/scw"
11+
)
12+
13+
func resourceScalewayIamPolicy() *schema.Resource {
14+
return &schema.Resource{
15+
CreateContext: resourceScalewayIamPolicyCreate,
16+
ReadContext: resourceScalewayIamPolicyRead,
17+
UpdateContext: resourceScalewayIamPolicyUpdate,
18+
DeleteContext: resourceScalewayIamPolicyDelete,
19+
Importer: &schema.ResourceImporter{
20+
StateContext: schema.ImportStatePassthroughContext,
21+
},
22+
SchemaVersion: 0,
23+
Schema: map[string]*schema.Schema{
24+
"name": {
25+
Type: schema.TypeString,
26+
Computed: true,
27+
Optional: true,
28+
Description: "The name of the iam policy",
29+
},
30+
"description": {
31+
Type: schema.TypeString,
32+
Optional: true,
33+
Description: "The description of the iam policy",
34+
},
35+
"created_at": {
36+
Type: schema.TypeString,
37+
Computed: true,
38+
Description: "The date and time of the creation of the policy",
39+
},
40+
"updated_at": {
41+
Type: schema.TypeString,
42+
Computed: true,
43+
Description: "The date and time of the last update of the policy",
44+
},
45+
"editable": {
46+
Type: schema.TypeBool,
47+
Computed: true,
48+
Description: "Whether or not the policy is editable.",
49+
},
50+
"organization_id": organizationIDSchema(),
51+
"user_id": {
52+
Type: schema.TypeString,
53+
Optional: true,
54+
Description: "User id",
55+
ExactlyOneOf: []string{"group_id", "application_id", "no_principal"},
56+
},
57+
"group_id": {
58+
Type: schema.TypeString,
59+
Optional: true,
60+
Description: "Group id",
61+
ExactlyOneOf: []string{"user_id", "application_id", "no_principal"},
62+
},
63+
"application_id": {
64+
Type: schema.TypeString,
65+
Optional: true,
66+
Description: "Application id",
67+
ExactlyOneOf: []string{"user_id", "group_id", "no_principal"},
68+
},
69+
"no_principal": {
70+
Type: schema.TypeBool,
71+
Optional: true,
72+
Description: "Application id",
73+
ExactlyOneOf: []string{"user_id", "group_id", "application_id"},
74+
},
75+
"rule": {
76+
Type: schema.TypeList,
77+
Required: true,
78+
Description: "Rules of the policy to create",
79+
Elem: &schema.Resource{
80+
Schema: map[string]*schema.Schema{
81+
"organization_id": {
82+
Type: schema.TypeString,
83+
Optional: true,
84+
Description: "ID of organization scoped to the rule. Only one of project_ids and organization_id may be set.",
85+
},
86+
"project_ids": {
87+
Type: schema.TypeList,
88+
Optional: true,
89+
Description: "List of project IDs scoped to the rule. Only one of project_ids and organization_id may be set.",
90+
Elem: &schema.Schema{
91+
Type: schema.TypeString,
92+
},
93+
},
94+
"permission_set_names": {
95+
Type: schema.TypeSet,
96+
Required: true,
97+
Description: "Names of permission sets bound to the rule.",
98+
Elem: &schema.Schema{
99+
Type: schema.TypeString,
100+
},
101+
},
102+
},
103+
},
104+
},
105+
},
106+
}
107+
}
108+
109+
func resourceScalewayIamPolicyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
110+
api := iamAPI(meta)
111+
pol, err := api.CreatePolicy(&iam.CreatePolicyRequest{
112+
Name: expandOrGenerateString(d.Get("name"), "policy-"),
113+
Description: d.Get("description").(string),
114+
Rules: expandPolicyRuleSpecs(d.Get("rule")),
115+
UserID: expandStringPtr(d.Get("user_id")),
116+
GroupID: expandStringPtr(d.Get("group_id")),
117+
ApplicationID: expandStringPtr(d.Get("application_id")),
118+
NoPrincipal: expandBoolPtr(d.Get("no_principal")),
119+
}, scw.WithContext(ctx))
120+
if err != nil {
121+
return diag.FromErr(err)
122+
}
123+
124+
d.SetId(pol.ID)
125+
126+
return resourceScalewayIamPolicyRead(ctx, d, meta)
127+
}
128+
129+
func resourceScalewayIamPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
130+
api := iamAPI(meta)
131+
pol, err := api.GetPolicy(&iam.GetPolicyRequest{
132+
PolicyID: d.Id(),
133+
}, scw.WithContext(ctx))
134+
if err != nil {
135+
if is404Error(err) {
136+
d.SetId("")
137+
return nil
138+
}
139+
return diag.FromErr(err)
140+
}
141+
_ = d.Set("name", pol.Name)
142+
_ = d.Set("description", pol.Description)
143+
_ = d.Set("created_at", flattenTime(pol.CreatedAt))
144+
_ = d.Set("updated_at", flattenTime(pol.UpdatedAt))
145+
_ = d.Set("organization_id", pol.OrganizationID)
146+
_ = d.Set("editable", pol.Editable)
147+
148+
if pol.UserID != nil {
149+
_ = d.Set("user_id", flattenStringPtr(pol.UserID))
150+
}
151+
if pol.GroupID != nil {
152+
_ = d.Set("group_id", flattenStringPtr(pol.GroupID))
153+
}
154+
if pol.ApplicationID != nil {
155+
_ = d.Set("application_id", flattenStringPtr(pol.ApplicationID))
156+
}
157+
_ = d.Set("no_principal", flattenBoolPtr(pol.NoPrincipal))
158+
159+
listRules, err := api.ListRules(&iam.ListRulesRequest{
160+
PolicyID: &pol.ID,
161+
})
162+
if err != nil {
163+
return diag.FromErr(fmt.Errorf("failed to list policy's rules: %w", err))
164+
}
165+
166+
_ = d.Set("rule", flattenPolicyRules(listRules.Rules))
167+
168+
return nil
169+
}
170+
171+
func resourceScalewayIamPolicyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
172+
api := iamAPI(meta)
173+
174+
req := &iam.UpdatePolicyRequest{
175+
PolicyID: d.Id(),
176+
}
177+
178+
hasUpdated := false
179+
180+
if d.HasChange("name") {
181+
hasUpdated = true
182+
req.Name = expandStringPtr(d.Get("name"))
183+
}
184+
if d.HasChange("description") {
185+
hasUpdated = true
186+
req.Description = expandStringPtr(d.Get("description"))
187+
}
188+
if d.HasChange("user_id") {
189+
hasUpdated = true
190+
req.UserID = expandStringPtr(d.Get("user_id"))
191+
}
192+
if d.HasChange("group_id") {
193+
hasUpdated = true
194+
req.UserID = expandStringPtr(d.Get("group_id"))
195+
}
196+
if d.HasChange("application_id") {
197+
hasUpdated = true
198+
req.UserID = expandStringPtr(d.Get("application_id"))
199+
}
200+
if hasUpdated {
201+
_, err := api.UpdatePolicy(req, scw.WithContext(ctx))
202+
if err != nil {
203+
return diag.FromErr(err)
204+
}
205+
}
206+
207+
if d.HasChange("rule") {
208+
_, err := api.SetRules(&iam.SetRulesRequest{
209+
PolicyID: d.Id(),
210+
Rules: expandPolicyRuleSpecs(d.Get("rule")),
211+
})
212+
if err != nil {
213+
return diag.FromErr(err)
214+
}
215+
}
216+
217+
return resourceScalewayIamPolicyRead(ctx, d, meta)
218+
}
219+
220+
func resourceScalewayIamPolicyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
221+
api := iamAPI(meta)
222+
223+
err := api.DeletePolicy(&iam.DeletePolicyRequest{
224+
PolicyID: d.Id(),
225+
}, scw.WithContext(ctx))
226+
if err != nil {
227+
if is404Error(err) {
228+
d.SetId("")
229+
return nil
230+
}
231+
return diag.FromErr(err)
232+
}
233+
234+
return nil
235+
}

0 commit comments

Comments
 (0)