Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat (team service account) Manage Team Based Service Account as Terraform resource #386

Merged
merged 9 commits into from
Jul 24, 2023
6 changes: 6 additions & 0 deletions sysdig/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sysdig

const (
SchemaIDKey = "id"
SchemaTeamIDKey = "team_id"
SchemaPoliciesKey = "policies"
SchemaPolicyIDsKey = "policy_ids"
SchemaNameKey = "name"
Expand All @@ -14,7 +15,9 @@ const (
SchemaAuthorKey = "author"
SchemaLastModifiedBy = "last_modified_by"
SchemaLastUpdated = "last_updated"
SchemaExpirationDateKey = "expiration_date"
SchemaPublishedDateKey = "published_date"
SchemaCreatedDateKey = "date_created"
SchemaMinKubeVersionKey = "min_kube_version"
SchemaMaxKubeVersionKey = "max_kube_version"
SchemaIsCustomKey = "is_custom"
Expand All @@ -26,5 +29,8 @@ const (
SchemaScopeKey = "scope"
SchemaScopesKey = "scopes"
SchemaTargetTypeKey = "target_type"
SchemaRoleKey = "role"
SchemaSystemRoleKey = "system_role"
SchemaRulesKey = "rules"
SchemaApiKeyKey = "api_key"
)
11 changes: 11 additions & 0 deletions sysdig/internal/client/v2/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ type UserRoles struct {
Admin bool `json:"admin,omitempty"`
}

type TeamServiceAccount struct {
ID int `json:"id,omitempty"`
Name string `json:"name"`
SystemRole string `json:"systemRole"`
TeamId int `json:"teamId"`
TeamRole string `json:"teamRole"`
DateCreated int64 `json:"dateCreated,omitempty"`
ExpirationDate int64 `json:"expirationDate"`
ApiKey string `json:"apiKey,omitempty"`
}

type EntryPoint struct {
Module string `json:"module"`
Selection string `json:"selection,omitempty"`
Expand Down
1 change: 1 addition & 0 deletions sysdig/internal/client/v2/sysdig.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type SysdigCommon interface {
Common
GroupMappingInterface
GroupMappingConfigInterface
TeamServiceAccountInterface
}

type SysdigMonitor interface {
Expand Down
123 changes: 123 additions & 0 deletions sysdig/internal/client/v2/team_service_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package v2

import (
"context"
"errors"
"fmt"
"net/http"
)

var TeamServiceAccountNotFound = errors.New("team service account not found")

const (
ServiceAccountsPath = "%s/api/serviceaccounts/team"
ServiceAccountPath = "%s/api/serviceaccounts/team/%d"
ServiceAccountDeletePath = "%s/api/serviceaccounts/team/%d/delete"
)

type TeamServiceAccountInterface interface {
Base
GetTeamServiceAccountByID(ctx context.Context, id int) (*TeamServiceAccount, error)
CreateTeamServiceAccount(ctx context.Context, account *TeamServiceAccount) (*TeamServiceAccount, error)
UpdateTeamServiceAccount(ctx context.Context, account *TeamServiceAccount, id int) (*TeamServiceAccount, error)
DeleteTeamServiceAccount(ctx context.Context, id int) error
}

func (client *Client) GetTeamServiceAccountByID(ctx context.Context, id int) (*TeamServiceAccount, error) {
response, err := client.requester.Request(ctx, http.MethodGet, client.GetTeamServiceAccountURL(id), nil)
if err != nil {
return nil, err
}
defer response.Body.Close()

if response.StatusCode != http.StatusOK {
if response.StatusCode == http.StatusNotFound {
return nil, TeamServiceAccountNotFound
}
return nil, client.ErrorFromResponse(response)
}

teamServiceAccount, err := Unmarshal[TeamServiceAccount](response.Body)
if err != nil {
return nil, err
}
return &teamServiceAccount, nil
}

func (client *Client) CreateTeamServiceAccount(ctx context.Context, account *TeamServiceAccount) (*TeamServiceAccount, error) {
payload, err := Marshal(account)
if err != nil {
return nil, err
}

response, err := client.requester.Request(ctx, http.MethodPost, client.CreateTeamServiceAccountURL(), payload)
if err != nil {
return nil, err
}
defer response.Body.Close()

if response.StatusCode != http.StatusOK {
return nil, client.ErrorFromResponse(response)
}

created, err := Unmarshal[TeamServiceAccount](response.Body)
if err != nil {
return nil, err
}

return &created, nil
}

func (client *Client) UpdateTeamServiceAccount(ctx context.Context, account *TeamServiceAccount, id int) (*TeamServiceAccount, error) {
payload, err := Marshal(account)
if err != nil {
return nil, err
}

response, err := client.requester.Request(ctx, http.MethodPut, client.UpdateTeamServiceAccountURL(id), payload)
if err != nil {
return nil, err
}
defer response.Body.Close()

if response.StatusCode != http.StatusOK {
return nil, client.ErrorFromResponse(response)
}

updated, err := Unmarshal[TeamServiceAccount](response.Body)
if err != nil {
return nil, err
}

return &updated, nil
}

func (client *Client) DeleteTeamServiceAccount(ctx context.Context, id int) error {
response, err := client.requester.Request(ctx, http.MethodDelete, client.DeleteTeamServiceAccountURL(id), nil)
if err != nil {
return err
}
defer response.Body.Close()

if response.StatusCode != http.StatusNoContent && response.StatusCode != http.StatusOK && response.StatusCode != http.StatusNotFound {
return client.ErrorFromResponse(response)
}

return nil
}

func (client *Client) GetTeamServiceAccountURL(id int) string {
return fmt.Sprintf(ServiceAccountPath, client.config.url, id)
}

func (client *Client) CreateTeamServiceAccountURL() string {
return fmt.Sprintf(ServiceAccountsPath, client.config.url)
}

func (client *Client) UpdateTeamServiceAccountURL(id int) string {
return fmt.Sprintf(ServiceAccountPath, client.config.url, id)
}

func (client *Client) DeleteTeamServiceAccountURL(id int) string {
return fmt.Sprintf(ServiceAccountDeletePath, client.config.url, id)
}
1 change: 1 addition & 0 deletions sysdig/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func Provider() *schema.Provider {
"sysdig_user": resourceSysdigUser(),
"sysdig_group_mapping": resourceSysdigGroupMapping(),
"sysdig_group_mapping_config": resourceSysdigGroupMappingConfig(),
"sysdig_team_service_account": resourceSysdigTeamServiceAccount(),

"sysdig_secure_custom_policy": resourceSysdigSecureCustomPolicy(),
"sysdig_secure_managed_policy": resourceSysdigSecureManagedPolicy(),
Expand Down
198 changes: 198 additions & 0 deletions sysdig/resource_sysdig_team_service_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package sysdig

import (
"context"
v2 "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"strconv"
"time"
)

func resourceSysdigTeamServiceAccount() *schema.Resource {
timeout := 5 * time.Minute
return &schema.Resource{
ReadContext: resourceSysdigTeamServiceAccountRead,
CreateContext: resourceSysdigTeamServiceAccountCreate,
UpdateContext: resourceSysdigTeamServiceAccountUpdate,
DeleteContext: resourceSysdigTeamServiceAccountDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(timeout),
Update: schema.DefaultTimeout(timeout),
Read: schema.DefaultTimeout(timeout),
Delete: schema.DefaultTimeout(timeout),
},
Schema: map[string]*schema.Schema{
SchemaNameKey: {
Type: schema.TypeString,
Required: true,
},
SchemaRoleKey: {
Type: schema.TypeString,
Optional: true,
Default: "ROLE_TEAM_READ",
},
SchemaExpirationDateKey: {
Type: schema.TypeInt,
Required: true,
},
SchemaTeamIDKey: {
Type: schema.TypeInt,
Required: true,
},
SchemaSystemRoleKey: {
Type: schema.TypeString,
Optional: true,
Default: "ROLE_SERVICE_ACCOUNT",
},
SchemaCreatedDateKey: {
Type: schema.TypeInt,
Computed: true,
},
SchemaApiKeyKey: {
Type: schema.TypeString,
Computed: true,
Sensitive: true,
},
},
}
}

func resourceSysdigTeamServiceAccountRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client, err := m.(SysdigClients).sysdigCommonClientV2()
if err != nil {
diag.FromErr(err)
}

id, err := strconv.Atoi(d.Id())
if err != nil {
diag.FromErr(err)
}

teamServiceAccount, err := client.GetTeamServiceAccountByID(ctx, id)
if err != nil {
if err == v2.TeamServiceAccountNotFound {
d.SetId("")
return nil
}
return diag.FromErr(err)
}

err = teamServiceAccountToResourceData(teamServiceAccount, d)
if err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceSysdigTeamServiceAccountCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
var err error

client, err := m.(SysdigClients).sysdigCommonClientV2()
if err != nil {
return diag.FromErr(err)
}

teamServiceAccount := teamServiceAccountFromResourceData(d)
teamServiceAccount, err = client.CreateTeamServiceAccount(ctx, teamServiceAccount)
if err != nil {
return diag.FromErr(err)
}

d.SetId(strconv.Itoa(teamServiceAccount.ID))

resourceSysdigTeamServiceAccountRead(ctx, d, m)

return nil
}

func resourceSysdigTeamServiceAccountUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
var err error

client, err := m.(SysdigClients).sysdigCommonClientV2()
if err != nil {
return diag.FromErr(err)
}

teamServiceAccount := teamServiceAccountFromResourceData(d)
id, err := strconv.Atoi(d.Id())
if err != nil {
return diag.FromErr(err)
}

teamServiceAccount.ID = id
_, err = client.UpdateTeamServiceAccount(ctx, teamServiceAccount, id)
if err != nil {
return diag.FromErr(err)
}

resourceSysdigTeamServiceAccountRead(ctx, d, m)

return nil
}

func resourceSysdigTeamServiceAccountDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client, err := m.(SysdigClients).sysdigCommonClientV2()
if err != nil {
return diag.FromErr(err)
}

id, err := strconv.Atoi(d.Id())
if err != nil {
return diag.FromErr(err)
}
err = client.DeleteTeamServiceAccount(ctx, id)
if err != nil {
return diag.FromErr(err)
}

return nil
}

func teamServiceAccountFromResourceData(d *schema.ResourceData) *v2.TeamServiceAccount {
return &v2.TeamServiceAccount{
Name: d.Get(SchemaNameKey).(string),
TeamRole: d.Get(SchemaRoleKey).(string),
ExpirationDate: int64(d.Get(SchemaExpirationDateKey).(int) * 1000),
TeamId: d.Get(SchemaTeamIDKey).(int),
SystemRole: d.Get(SchemaSystemRoleKey).(string),
ApiKey: d.Get(SchemaApiKeyKey).(string),
}
}

func teamServiceAccountToResourceData(teamServiceAccount *v2.TeamServiceAccount, d *schema.ResourceData) error {
err := d.Set(SchemaNameKey, teamServiceAccount.Name)
if err != nil {
return err
}
err = d.Set(SchemaRoleKey, teamServiceAccount.TeamRole)
if err != nil {
return err
}
err = d.Set(SchemaExpirationDateKey, teamServiceAccount.ExpirationDate/1000)
if err != nil {
return err
}
err = d.Set(SchemaTeamIDKey, teamServiceAccount.TeamId)
if err != nil {
return err
}
err = d.Set(SchemaSystemRoleKey, teamServiceAccount.SystemRole)
if err != nil {
return err
}
err = d.Set(SchemaCreatedDateKey, teamServiceAccount.DateCreated)
if err != nil {
return err
}
err = d.Set(SchemaApiKeyKey, teamServiceAccount.ApiKey)
if err != nil {
return err
}

return nil
}
Loading
Loading