Skip to content

Commit 41345d3

Browse files
authored
feat (team service account) Manage Team Based Service Account as Terraform resource (#386)
* add support for team service account * add documentation * fix typo in test * add test for secure * add sensitive flag to api_key * fix error description * replace schema strings with constants * rename from GetTeamServiceAccountById -> GetTeamServiceAccountByID * replace strings with costants also in utility methods
1 parent ce2d591 commit 41345d3

File tree

8 files changed

+504
-0
lines changed

8 files changed

+504
-0
lines changed

sysdig/common.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package sysdig
22

33
const (
44
SchemaIDKey = "id"
5+
SchemaTeamIDKey = "team_id"
56
SchemaPoliciesKey = "policies"
67
SchemaPolicyIDsKey = "policy_ids"
78
SchemaNameKey = "name"
@@ -14,7 +15,9 @@ const (
1415
SchemaAuthorKey = "author"
1516
SchemaLastModifiedBy = "last_modified_by"
1617
SchemaLastUpdated = "last_updated"
18+
SchemaExpirationDateKey = "expiration_date"
1719
SchemaPublishedDateKey = "published_date"
20+
SchemaCreatedDateKey = "date_created"
1821
SchemaMinKubeVersionKey = "min_kube_version"
1922
SchemaMaxKubeVersionKey = "max_kube_version"
2023
SchemaIsCustomKey = "is_custom"
@@ -26,5 +29,8 @@ const (
2629
SchemaScopeKey = "scope"
2730
SchemaScopesKey = "scopes"
2831
SchemaTargetTypeKey = "target_type"
32+
SchemaRoleKey = "role"
33+
SchemaSystemRoleKey = "system_role"
2934
SchemaRulesKey = "rules"
35+
SchemaApiKeyKey = "api_key"
3036
)

sysdig/internal/client/v2/model.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ type UserRoles struct {
3838
Admin bool `json:"admin,omitempty"`
3939
}
4040

41+
type TeamServiceAccount struct {
42+
ID int `json:"id,omitempty"`
43+
Name string `json:"name"`
44+
SystemRole string `json:"systemRole"`
45+
TeamId int `json:"teamId"`
46+
TeamRole string `json:"teamRole"`
47+
DateCreated int64 `json:"dateCreated,omitempty"`
48+
ExpirationDate int64 `json:"expirationDate"`
49+
ApiKey string `json:"apiKey,omitempty"`
50+
}
51+
4152
type EntryPoint struct {
4253
Module string `json:"module"`
4354
Selection string `json:"selection,omitempty"`

sysdig/internal/client/v2/sysdig.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type SysdigCommon interface {
2020
Common
2121
GroupMappingInterface
2222
GroupMappingConfigInterface
23+
TeamServiceAccountInterface
2324
}
2425

2526
type SysdigMonitor interface {
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package v2
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"net/http"
8+
)
9+
10+
var TeamServiceAccountNotFound = errors.New("team service account not found")
11+
12+
const (
13+
ServiceAccountsPath = "%s/api/serviceaccounts/team"
14+
ServiceAccountPath = "%s/api/serviceaccounts/team/%d"
15+
ServiceAccountDeletePath = "%s/api/serviceaccounts/team/%d/delete"
16+
)
17+
18+
type TeamServiceAccountInterface interface {
19+
Base
20+
GetTeamServiceAccountByID(ctx context.Context, id int) (*TeamServiceAccount, error)
21+
CreateTeamServiceAccount(ctx context.Context, account *TeamServiceAccount) (*TeamServiceAccount, error)
22+
UpdateTeamServiceAccount(ctx context.Context, account *TeamServiceAccount, id int) (*TeamServiceAccount, error)
23+
DeleteTeamServiceAccount(ctx context.Context, id int) error
24+
}
25+
26+
func (client *Client) GetTeamServiceAccountByID(ctx context.Context, id int) (*TeamServiceAccount, error) {
27+
response, err := client.requester.Request(ctx, http.MethodGet, client.GetTeamServiceAccountURL(id), nil)
28+
if err != nil {
29+
return nil, err
30+
}
31+
defer response.Body.Close()
32+
33+
if response.StatusCode != http.StatusOK {
34+
if response.StatusCode == http.StatusNotFound {
35+
return nil, TeamServiceAccountNotFound
36+
}
37+
return nil, client.ErrorFromResponse(response)
38+
}
39+
40+
teamServiceAccount, err := Unmarshal[TeamServiceAccount](response.Body)
41+
if err != nil {
42+
return nil, err
43+
}
44+
return &teamServiceAccount, nil
45+
}
46+
47+
func (client *Client) CreateTeamServiceAccount(ctx context.Context, account *TeamServiceAccount) (*TeamServiceAccount, error) {
48+
payload, err := Marshal(account)
49+
if err != nil {
50+
return nil, err
51+
}
52+
53+
response, err := client.requester.Request(ctx, http.MethodPost, client.CreateTeamServiceAccountURL(), payload)
54+
if err != nil {
55+
return nil, err
56+
}
57+
defer response.Body.Close()
58+
59+
if response.StatusCode != http.StatusOK {
60+
return nil, client.ErrorFromResponse(response)
61+
}
62+
63+
created, err := Unmarshal[TeamServiceAccount](response.Body)
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
return &created, nil
69+
}
70+
71+
func (client *Client) UpdateTeamServiceAccount(ctx context.Context, account *TeamServiceAccount, id int) (*TeamServiceAccount, error) {
72+
payload, err := Marshal(account)
73+
if err != nil {
74+
return nil, err
75+
}
76+
77+
response, err := client.requester.Request(ctx, http.MethodPut, client.UpdateTeamServiceAccountURL(id), payload)
78+
if err != nil {
79+
return nil, err
80+
}
81+
defer response.Body.Close()
82+
83+
if response.StatusCode != http.StatusOK {
84+
return nil, client.ErrorFromResponse(response)
85+
}
86+
87+
updated, err := Unmarshal[TeamServiceAccount](response.Body)
88+
if err != nil {
89+
return nil, err
90+
}
91+
92+
return &updated, nil
93+
}
94+
95+
func (client *Client) DeleteTeamServiceAccount(ctx context.Context, id int) error {
96+
response, err := client.requester.Request(ctx, http.MethodDelete, client.DeleteTeamServiceAccountURL(id), nil)
97+
if err != nil {
98+
return err
99+
}
100+
defer response.Body.Close()
101+
102+
if response.StatusCode != http.StatusNoContent && response.StatusCode != http.StatusOK && response.StatusCode != http.StatusNotFound {
103+
return client.ErrorFromResponse(response)
104+
}
105+
106+
return nil
107+
}
108+
109+
func (client *Client) GetTeamServiceAccountURL(id int) string {
110+
return fmt.Sprintf(ServiceAccountPath, client.config.url, id)
111+
}
112+
113+
func (client *Client) CreateTeamServiceAccountURL() string {
114+
return fmt.Sprintf(ServiceAccountsPath, client.config.url)
115+
}
116+
117+
func (client *Client) UpdateTeamServiceAccountURL(id int) string {
118+
return fmt.Sprintf(ServiceAccountPath, client.config.url, id)
119+
}
120+
121+
func (client *Client) DeleteTeamServiceAccountURL(id int) string {
122+
return fmt.Sprintf(ServiceAccountDeletePath, client.config.url, id)
123+
}

sysdig/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ func Provider() *schema.Provider {
102102
"sysdig_user": resourceSysdigUser(),
103103
"sysdig_group_mapping": resourceSysdigGroupMapping(),
104104
"sysdig_group_mapping_config": resourceSysdigGroupMappingConfig(),
105+
"sysdig_team_service_account": resourceSysdigTeamServiceAccount(),
105106

106107
"sysdig_secure_custom_policy": resourceSysdigSecureCustomPolicy(),
107108
"sysdig_secure_managed_policy": resourceSysdigSecureManagedPolicy(),
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package sysdig
2+
3+
import (
4+
"context"
5+
v2 "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2"
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
"strconv"
9+
"time"
10+
)
11+
12+
func resourceSysdigTeamServiceAccount() *schema.Resource {
13+
timeout := 5 * time.Minute
14+
return &schema.Resource{
15+
ReadContext: resourceSysdigTeamServiceAccountRead,
16+
CreateContext: resourceSysdigTeamServiceAccountCreate,
17+
UpdateContext: resourceSysdigTeamServiceAccountUpdate,
18+
DeleteContext: resourceSysdigTeamServiceAccountDelete,
19+
Importer: &schema.ResourceImporter{
20+
StateContext: schema.ImportStatePassthroughContext,
21+
},
22+
Timeouts: &schema.ResourceTimeout{
23+
Create: schema.DefaultTimeout(timeout),
24+
Update: schema.DefaultTimeout(timeout),
25+
Read: schema.DefaultTimeout(timeout),
26+
Delete: schema.DefaultTimeout(timeout),
27+
},
28+
Schema: map[string]*schema.Schema{
29+
SchemaNameKey: {
30+
Type: schema.TypeString,
31+
Required: true,
32+
},
33+
SchemaRoleKey: {
34+
Type: schema.TypeString,
35+
Optional: true,
36+
Default: "ROLE_TEAM_READ",
37+
},
38+
SchemaExpirationDateKey: {
39+
Type: schema.TypeInt,
40+
Required: true,
41+
},
42+
SchemaTeamIDKey: {
43+
Type: schema.TypeInt,
44+
Required: true,
45+
},
46+
SchemaSystemRoleKey: {
47+
Type: schema.TypeString,
48+
Optional: true,
49+
Default: "ROLE_SERVICE_ACCOUNT",
50+
},
51+
SchemaCreatedDateKey: {
52+
Type: schema.TypeInt,
53+
Computed: true,
54+
},
55+
SchemaApiKeyKey: {
56+
Type: schema.TypeString,
57+
Computed: true,
58+
Sensitive: true,
59+
},
60+
},
61+
}
62+
}
63+
64+
func resourceSysdigTeamServiceAccountRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
65+
client, err := m.(SysdigClients).sysdigCommonClientV2()
66+
if err != nil {
67+
diag.FromErr(err)
68+
}
69+
70+
id, err := strconv.Atoi(d.Id())
71+
if err != nil {
72+
diag.FromErr(err)
73+
}
74+
75+
teamServiceAccount, err := client.GetTeamServiceAccountByID(ctx, id)
76+
if err != nil {
77+
if err == v2.TeamServiceAccountNotFound {
78+
d.SetId("")
79+
return nil
80+
}
81+
return diag.FromErr(err)
82+
}
83+
84+
err = teamServiceAccountToResourceData(teamServiceAccount, d)
85+
if err != nil {
86+
return diag.FromErr(err)
87+
}
88+
89+
return nil
90+
}
91+
92+
func resourceSysdigTeamServiceAccountCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
93+
var err error
94+
95+
client, err := m.(SysdigClients).sysdigCommonClientV2()
96+
if err != nil {
97+
return diag.FromErr(err)
98+
}
99+
100+
teamServiceAccount := teamServiceAccountFromResourceData(d)
101+
teamServiceAccount, err = client.CreateTeamServiceAccount(ctx, teamServiceAccount)
102+
if err != nil {
103+
return diag.FromErr(err)
104+
}
105+
106+
d.SetId(strconv.Itoa(teamServiceAccount.ID))
107+
108+
resourceSysdigTeamServiceAccountRead(ctx, d, m)
109+
110+
return nil
111+
}
112+
113+
func resourceSysdigTeamServiceAccountUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
114+
var err error
115+
116+
client, err := m.(SysdigClients).sysdigCommonClientV2()
117+
if err != nil {
118+
return diag.FromErr(err)
119+
}
120+
121+
teamServiceAccount := teamServiceAccountFromResourceData(d)
122+
id, err := strconv.Atoi(d.Id())
123+
if err != nil {
124+
return diag.FromErr(err)
125+
}
126+
127+
teamServiceAccount.ID = id
128+
_, err = client.UpdateTeamServiceAccount(ctx, teamServiceAccount, id)
129+
if err != nil {
130+
return diag.FromErr(err)
131+
}
132+
133+
resourceSysdigTeamServiceAccountRead(ctx, d, m)
134+
135+
return nil
136+
}
137+
138+
func resourceSysdigTeamServiceAccountDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
139+
client, err := m.(SysdigClients).sysdigCommonClientV2()
140+
if err != nil {
141+
return diag.FromErr(err)
142+
}
143+
144+
id, err := strconv.Atoi(d.Id())
145+
if err != nil {
146+
return diag.FromErr(err)
147+
}
148+
err = client.DeleteTeamServiceAccount(ctx, id)
149+
if err != nil {
150+
return diag.FromErr(err)
151+
}
152+
153+
return nil
154+
}
155+
156+
func teamServiceAccountFromResourceData(d *schema.ResourceData) *v2.TeamServiceAccount {
157+
return &v2.TeamServiceAccount{
158+
Name: d.Get(SchemaNameKey).(string),
159+
TeamRole: d.Get(SchemaRoleKey).(string),
160+
ExpirationDate: int64(d.Get(SchemaExpirationDateKey).(int) * 1000),
161+
TeamId: d.Get(SchemaTeamIDKey).(int),
162+
SystemRole: d.Get(SchemaSystemRoleKey).(string),
163+
ApiKey: d.Get(SchemaApiKeyKey).(string),
164+
}
165+
}
166+
167+
func teamServiceAccountToResourceData(teamServiceAccount *v2.TeamServiceAccount, d *schema.ResourceData) error {
168+
err := d.Set(SchemaNameKey, teamServiceAccount.Name)
169+
if err != nil {
170+
return err
171+
}
172+
err = d.Set(SchemaRoleKey, teamServiceAccount.TeamRole)
173+
if err != nil {
174+
return err
175+
}
176+
err = d.Set(SchemaExpirationDateKey, teamServiceAccount.ExpirationDate/1000)
177+
if err != nil {
178+
return err
179+
}
180+
err = d.Set(SchemaTeamIDKey, teamServiceAccount.TeamId)
181+
if err != nil {
182+
return err
183+
}
184+
err = d.Set(SchemaSystemRoleKey, teamServiceAccount.SystemRole)
185+
if err != nil {
186+
return err
187+
}
188+
err = d.Set(SchemaCreatedDateKey, teamServiceAccount.DateCreated)
189+
if err != nil {
190+
return err
191+
}
192+
err = d.Set(SchemaApiKeyKey, teamServiceAccount.ApiKey)
193+
if err != nil {
194+
return err
195+
}
196+
197+
return nil
198+
}

0 commit comments

Comments
 (0)