Skip to content

Commit 73f4c02

Browse files
chandrasekhar1996Chandu Raman
andauthored
Add self-serve group members resource (#136)
* Add self-serve group members resource Implements athenz_self_serve_group_members resource to manage specific group members without affecting externally managed ones. Follows the same pattern as self-serve role members but adapted for groups. Signed-off-by: Chandu Raman <[email protected]> * update system test result to include new field autoDeleteTenantAssumeRoleAssertions (AthenZ/athenz#3027) Signed-off-by: Chandu Raman <[email protected]> --------- Signed-off-by: Chandu Raman <[email protected]> Co-authored-by: Chandu Raman <[email protected]>
1 parent f79d8f8 commit 73f4c02

File tree

5 files changed

+655
-14
lines changed

5 files changed

+655
-14
lines changed

athenz/provider.go

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,21 @@ func Provider() *schema.Provider {
7777
},
7878

7979
ResourcesMap: map[string]*schema.Resource{
80-
"athenz_role": ResourceRole(),
81-
"athenz_role_members": ResourceRoleMembers(),
82-
"athenz_self_serve_role_members": ResourceSelfServeRoleMembers(),
83-
"athenz_role_meta": ResourceRoleMeta(),
84-
"athenz_group": ResourceGroup(),
85-
"athenz_group_members": ResourceGroupMembers(),
86-
"athenz_group_meta": ResourceGroupMeta(),
87-
"athenz_policy": ResourcePolicy(),
88-
"athenz_policy_version": ResourcePolicyVersion(),
89-
"athenz_service": ResourceService(),
90-
"athenz_sub_domain": ResourceSubDomain(),
91-
"athenz_user_domain": ResourceUserDomain(),
92-
"athenz_top_level_domain": ResourceTopLevelDomain(),
93-
"athenz_domain_meta": ResourceDomainMeta(),
80+
"athenz_role": ResourceRole(),
81+
"athenz_role_members": ResourceRoleMembers(),
82+
"athenz_self_serve_role_members": ResourceSelfServeRoleMembers(),
83+
"athenz_role_meta": ResourceRoleMeta(),
84+
"athenz_group": ResourceGroup(),
85+
"athenz_group_members": ResourceGroupMembers(),
86+
"athenz_self_serve_group_members": ResourceSelfServeGroupMembers(),
87+
"athenz_group_meta": ResourceGroupMeta(),
88+
"athenz_policy": ResourcePolicy(),
89+
"athenz_policy_version": ResourcePolicyVersion(),
90+
"athenz_service": ResourceService(),
91+
"athenz_sub_domain": ResourceSubDomain(),
92+
"athenz_user_domain": ResourceUserDomain(),
93+
"athenz_top_level_domain": ResourceTopLevelDomain(),
94+
"athenz_domain_meta": ResourceDomainMeta(),
9495
},
9596

9697
ConfigureContextFunc: configProvider,
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package athenz
2+
3+
import (
4+
"context"
5+
"log"
6+
7+
"github.com/AthenZ/athenz/clients/go/zms"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9+
10+
"github.com/AthenZ/terraform-provider-athenz/client"
11+
12+
"github.com/ardielle/ardielle-go/rdl"
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
14+
)
15+
16+
func ResourceSelfServeGroupMembers() *schema.Resource {
17+
return &schema.Resource{
18+
CreateContext: resourceSelfServeGroupMembersCreate,
19+
ReadContext: resourceSelfServeGroupMembersRead,
20+
UpdateContext: resourceSelfServeGroupMembersUpdate,
21+
DeleteContext: resourceSelfServeGroupMembersDelete,
22+
Importer: &schema.ResourceImporter{
23+
StateContext: schema.ImportStatePassthroughContext,
24+
},
25+
Schema: map[string]*schema.Schema{
26+
"domain": {
27+
Type: schema.TypeString,
28+
Description: "Name of the domain that group belongs to",
29+
Required: true,
30+
ForceNew: true,
31+
ValidateDiagFunc: validatePatternFunc(DOMAIN_NAME),
32+
},
33+
"name": {
34+
Type: schema.TypeString,
35+
Description: "Name of the self-serve group",
36+
Required: true,
37+
ForceNew: true,
38+
ValidateDiagFunc: validatePatternFunc(ENTITY_NAME),
39+
},
40+
"member": {
41+
Type: schema.TypeSet,
42+
Description: "Athenz principal to be added as members (only manages members defined in Terraform)",
43+
Optional: true,
44+
Elem: &schema.Resource{
45+
Schema: map[string]*schema.Schema{
46+
"name": {
47+
Type: schema.TypeString,
48+
Required: true,
49+
ValidateDiagFunc: validatePatternFunc(GROUP_MEMBER_NAME),
50+
},
51+
"expiration": {
52+
Type: schema.TypeString,
53+
Optional: true,
54+
Default: "",
55+
ValidateDiagFunc: validateDatePatternFunc(DATE_PATTERN, MEMBER_EXPIRATION),
56+
},
57+
},
58+
},
59+
},
60+
"audit_ref": {
61+
Type: schema.TypeString,
62+
Optional: true,
63+
Default: AUDIT_REF,
64+
},
65+
},
66+
}
67+
}
68+
69+
func resourceSelfServeGroupMembersCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
70+
zmsClient := meta.(client.ZmsClient)
71+
dn := d.Get("domain").(string)
72+
gn := d.Get("name").(string)
73+
fullResourceName := dn + GROUP_SEPARATOR + gn
74+
75+
_, err := zmsClient.GetGroup(dn, gn)
76+
if err != nil {
77+
return diag.FromErr(err)
78+
}
79+
auditRef := d.Get("audit_ref").(string)
80+
81+
if v, ok := d.GetOk("member"); ok && v.(*schema.Set).Len() > 0 {
82+
groupMembers := expandGroupMembers(v.(*schema.Set).List())
83+
for _, member := range groupMembers {
84+
membership := zms.GroupMembership{
85+
MemberName: member.MemberName,
86+
Expiration: member.Expiration,
87+
}
88+
err = zmsClient.PutGroupMembership(dn, gn, member.MemberName, auditRef, &membership)
89+
if err != nil {
90+
return diag.Errorf("error adding self-serve group member: %v", err)
91+
}
92+
}
93+
}
94+
95+
d.SetId(fullResourceName)
96+
return readAfterWrite(resourceSelfServeGroupMembersRead, ctx, d, meta)
97+
}
98+
99+
func resourceSelfServeGroupMembersRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
100+
zmsClient := meta.(client.ZmsClient)
101+
102+
dn, gn, err := splitGroupId(d.Id())
103+
if err != nil {
104+
return diag.FromErr(err)
105+
}
106+
if err = d.Set("domain", dn); err != nil {
107+
return diag.FromErr(err)
108+
}
109+
if err = d.Set("name", gn); err != nil {
110+
return diag.FromErr(err)
111+
}
112+
_, err = zmsClient.GetGroup(dn, gn)
113+
switch v := err.(type) {
114+
case rdl.ResourceError:
115+
if v.Code == 404 {
116+
if !d.IsNewResource() {
117+
log.Printf("[WARN] Athenz self-serve group %s not found, removing from state", d.Id())
118+
d.SetId("")
119+
return nil
120+
}
121+
return diag.FromErr(err)
122+
}
123+
return diag.Errorf("error retrieving Athenz self-serve group %s: %s", d.Id(), v)
124+
case rdl.Any:
125+
return diag.FromErr(err)
126+
}
127+
128+
// For self-serve group members, we only read the members that are in the Terraform state
129+
// We don't sync with the actual system members to avoid conflicts with externally managed members
130+
if v, ok := d.GetOk("member"); ok && v.(*schema.Set).Len() > 0 {
131+
// Keep the existing state as is - don't sync with system
132+
if err = d.Set("member", v); err != nil {
133+
return diag.FromErr(err)
134+
}
135+
} else {
136+
if err = d.Set("member", nil); err != nil {
137+
return diag.FromErr(err)
138+
}
139+
}
140+
141+
return nil
142+
}
143+
144+
func resourceSelfServeGroupMembersUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
145+
zmsClient := meta.(client.ZmsClient)
146+
dn, gn, err := splitGroupId(d.Id())
147+
if err != nil {
148+
return diag.FromErr(err)
149+
}
150+
auditRef := d.Get("audit_ref").(string)
151+
membersToDelete := make([]*zms.GroupMember, 0)
152+
membersToAdd := make([]*zms.GroupMember, 0)
153+
154+
_, err = zmsClient.GetGroup(dn, gn)
155+
if err != nil {
156+
return diag.FromErr(err)
157+
}
158+
159+
if d.HasChange("member") {
160+
os, ns := handleChange(d, "member")
161+
membersToDelete = append(membersToDelete, expandGroupMembers(os.Difference(ns).List())...)
162+
membersToAdd = append(membersToAdd, expandGroupMembers(ns.Difference(os).List())...)
163+
}
164+
165+
err = updateGroupMembers(dn, gn, membersToDelete, membersToAdd, zmsClient, auditRef)
166+
if err != nil {
167+
return diag.Errorf("error updating self-serve group membership: %s", err)
168+
}
169+
170+
return readAfterWrite(resourceSelfServeGroupMembersRead, ctx, d, meta)
171+
}
172+
173+
func resourceSelfServeGroupMembersDelete(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
174+
zmsClient := meta.(client.ZmsClient)
175+
dn, gn, err := splitGroupId(d.Id())
176+
if err != nil {
177+
return diag.FromErr(err)
178+
}
179+
auditRef := d.Get("audit_ref").(string)
180+
181+
// For self-serve group members, we only delete the members that are in the Terraform state
182+
// We don't delete all members from the system to avoid affecting externally managed members
183+
if v, ok := d.GetOk("member"); ok && v.(*schema.Set).Len() > 0 {
184+
groupMembers := expandGroupMembers(v.(*schema.Set).List())
185+
for _, member := range groupMembers {
186+
err = zmsClient.DeleteGroupMembership(dn, gn, member.MemberName, auditRef)
187+
if err != nil {
188+
return diag.FromErr(err)
189+
}
190+
}
191+
}
192+
return nil
193+
}

0 commit comments

Comments
 (0)