Skip to content

Commit 59e69cb

Browse files
fix(secure-onboarding) Fix resource update operation (#435)
* fix(secure-onboarding) Fix resource update operation The TF provider’s knowledge of cloud account object is older and different from the actual BE cloud account. This is because BE has modified the account object with fields like organization_id during org onboarding. Hence, during intended PUTs, TF overwrites these fields resulting in unexpected state. Fix summary: ------------ 1. Add and expose the organization_id in provider's account resource schema (as a computed field only). This is to let the customers know if their onboarded account is part of any org when they fetch the real existing infrastructure objects using GET calls. 2. During resource updates (PUT API calls) restrict and reject with error when the customer tries to update any non-updatable resource fields. 3. Minor refactoring for cleaner code. Testing done: --------------- Validated the scenarios :- - During tf apply again --> it returns and updates organization_id on the account. - No unnecessary PUTs - On triggering an intended PUT :- - restricts any non-updatable fields - PUT works as expected, doesn't oevrwrite organization_id to "" * Fix lint issue * Update docs with the missing attributes
1 parent fc6bad7 commit 59e69cb

File tree

4 files changed

+86
-29
lines changed

4 files changed

+86
-29
lines changed

sysdig/common.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,5 @@ const (
5959
SchemaCloudProviderType = "provider_type"
6060
SchemaFeature = "feature"
6161
SchemaManagementAccountId = "management_account_id"
62+
SchemaOrganizationIDKey = "organization_id"
6263
)

sysdig/resource_sysdig_secure_cloud_auth_account.go

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
b64 "encoding/base64"
66
"encoding/json"
7+
"errors"
78
"fmt"
89
"reflect"
910
"strings"
@@ -153,6 +154,10 @@ func resourceSysdigSecureCloudauthAccount() *schema.Resource {
153154
Optional: true,
154155
Elem: accountComponents,
155156
},
157+
SchemaOrganizationIDKey: {
158+
Type: schema.TypeString,
159+
Computed: true,
160+
},
156161
},
157162
}
158163
}
@@ -173,6 +178,10 @@ func resourceSysdigSecureCloudauthAccountCreate(ctx context.Context, data *schem
173178
}
174179

175180
data.SetId(cloudauthAccount.Id)
181+
err = data.Set(SchemaOrganizationIDKey, cloudauthAccount.OrganizationId)
182+
if err != nil {
183+
return diag.FromErr(err)
184+
}
176185

177186
return nil
178187
}
@@ -206,8 +215,23 @@ func resourceSysdigSecureCloudauthAccountUpdate(ctx context.Context, data *schem
206215
return diag.FromErr(err)
207216
}
208217

209-
_, errStatus, err := client.UpdateCloudauthAccountSecure(ctx, data.Id(), cloudauthAccountFromResourceData(data))
218+
existingCloudAccount, errStatus, err := client.GetCloudauthAccountSecure(ctx, data.Id())
219+
if err != nil {
220+
if strings.Contains(errStatus, "404") {
221+
return nil
222+
}
223+
return diag.FromErr(err)
224+
}
225+
226+
newCloudAccount := cloudauthAccountFromResourceData(data)
210227

228+
// validate and reject non-updatable resource schema fields upfront
229+
err = validateCloudauthAccountUpdate(existingCloudAccount, newCloudAccount)
230+
if err != nil {
231+
return diag.FromErr(err)
232+
}
233+
234+
_, errStatus, err = client.UpdateCloudauthAccountSecure(ctx, data.Id(), newCloudAccount)
211235
if err != nil {
212236
if strings.Contains(errStatus, "404") {
213237
return nil
@@ -236,6 +260,19 @@ func resourceSysdigSecureCloudauthAccountDelete(ctx context.Context, data *schem
236260
return nil
237261
}
238262

263+
/*
264+
This function validates and restricts any fields not allowed to be updated during resource updates.
265+
*/
266+
func validateCloudauthAccountUpdate(existingCloudAccount *v2.CloudauthAccountSecure, newCloudAccount *v2.CloudauthAccountSecure) error {
267+
if existingCloudAccount.Enabled != newCloudAccount.Enabled || existingCloudAccount.Provider != newCloudAccount.Provider ||
268+
existingCloudAccount.ProviderId != newCloudAccount.ProviderId || existingCloudAccount.OrganizationId != newCloudAccount.OrganizationId {
269+
errorInvalidResourceUpdate := fmt.Sprintf("Bad Request. Updating restricted fields not allowed: %s", []string{"enabled", "provider_type", "provider_id", "organization_id"})
270+
return errors.New(errorInvalidResourceUpdate)
271+
}
272+
273+
return nil
274+
}
275+
239276
/*
240277
This function converts a schema set to map for iteration.
241278
*/
@@ -439,11 +476,12 @@ func cloudauthAccountFromResourceData(data *schema.ResourceData) *v2.CloudauthAc
439476

440477
return &v2.CloudauthAccountSecure{
441478
CloudAccount: cloudauth.CloudAccount{
442-
Enabled: data.Get(SchemaEnabled).(bool),
443-
ProviderId: data.Get(SchemaCloudProviderId).(string),
444-
Provider: cloudauth.Provider(cloudauth.Provider_value[data.Get(SchemaCloudProviderType).(string)]),
445-
Components: accountComponents,
446-
Feature: accountFeatures,
479+
Enabled: data.Get(SchemaEnabled).(bool),
480+
OrganizationId: data.Get(SchemaOrganizationIDKey).(string),
481+
ProviderId: data.Get(SchemaCloudProviderId).(string),
482+
Provider: cloudauth.Provider(cloudauth.Provider_value[data.Get(SchemaCloudProviderType).(string)]),
483+
Components: accountComponents,
484+
Feature: accountFeatures,
447485
},
448486
}
449487
}
@@ -498,6 +536,19 @@ func featureToResourceData(features *cloudauth.AccountFeatures) []interface{} {
498536
return nil
499537
}
500538

539+
func componentsToResourceData(components []*cloudauth.AccountComponent) []map[string]interface{} {
540+
componentsList := []map[string]interface{}{}
541+
542+
for _, comp := range components {
543+
componentsList = append(componentsList, map[string]interface{}{
544+
SchemaType: comp.Type.String(),
545+
SchemaInstance: comp.Instance,
546+
})
547+
}
548+
549+
return componentsList
550+
}
551+
501552
func cloudauthAccountToResourceData(data *schema.ResourceData, cloudAccount *v2.CloudauthAccountSecure) error {
502553
err := data.Set(SchemaIDKey, cloudAccount.Id)
503554
if err != nil {
@@ -524,16 +575,12 @@ func cloudauthAccountToResourceData(data *schema.ResourceData, cloudAccount *v2.
524575
return err
525576
}
526577

527-
components := []map[string]interface{}{}
528-
529-
for _, comp := range cloudAccount.Components {
530-
components = append(components, map[string]interface{}{
531-
SchemaType: comp.Type.String(),
532-
SchemaInstance: comp.Instance,
533-
})
578+
err = data.Set(SchemaComponent, componentsToResourceData(cloudAccount.Components))
579+
if err != nil {
580+
return err
534581
}
535582

536-
err = data.Set(SchemaComponent, components)
583+
err = data.Set(SchemaOrganizationIDKey, cloudAccount.OrganizationId)
537584
if err != nil {
538585
return err
539586
}

sysdig/resource_sysdig_secure_cloud_auth_account_test.go

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,6 @@ func TestAccSecureCloudAuthAccountFC(t *testing.T) {
8080
}
8181

8282
func secureCloudAuthAccountWithFC(accountID string) string {
83-
type sample_service_account_key struct {
84-
ProjectId string `json:"project_id"`
85-
PrivateKeyId string `json:"private_key_id"`
86-
PrivateKey string `json:"private_key"`
87-
}
88-
test_service_account_key := &sample_service_account_key{
89-
ProjectId: fmt.Sprintf("sample-1-%s", accountID),
90-
PrivateKeyId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
91-
PrivateKey: "-----BEGIN PRIVATE KEY-----\nxxxxxxxxxxxxxxxxxxxxxxxxxxx\n-----END PRIVATE KEY-----\n",
92-
}
93-
test_service_account_keyJSON, _ := json.Marshal(test_service_account_key)
94-
test_service_account_key_encoded := b64.StdEncoding.EncodeToString([]byte(string(test_service_account_keyJSON)))
95-
9683
return fmt.Sprintf(`
9784
resource "sysdig_secure_cloud_auth_account" "sample-1" {
9885
provider_id = "sample-1-%s"
@@ -121,5 +108,23 @@ resource "sysdig_secure_cloud_auth_account" "sample-1" {
121108
ignore_changes = [component]
122109
}
123110
}
124-
`, accountID, test_service_account_key_encoded)
111+
`, accountID, getEncodedServiceAccountKey("sample-1", accountID))
112+
}
113+
114+
func getEncodedServiceAccountKey(resourceName string, accountID string) string {
115+
type sample_service_account_key struct {
116+
Type string `json:"type"`
117+
ProjectId string `json:"project_id"`
118+
PrivateKeyId string `json:"private_key_id"`
119+
PrivateKey string `json:"private_key"`
120+
}
121+
test_service_account_key := &sample_service_account_key{
122+
Type: "service_account",
123+
ProjectId: fmt.Sprintf("%s-%s", resourceName, accountID),
124+
PrivateKeyId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
125+
PrivateKey: "-----BEGIN PRIVATE KEY-----\nxxxxxxxxxxxxxxxxxxxxxxxxxxx\n-----END PRIVATE KEY-----\n",
126+
}
127+
test_service_account_key_bytes, _ := json.Marshal(test_service_account_key)
128+
test_service_account_key_encoded := b64.StdEncoding.EncodeToString(test_service_account_key_bytes)
129+
return test_service_account_key_encoded
125130
}

website/docs/r/secure_cloud_auth_account.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,8 @@ resource "sysdig_secure_cloud_auth_account" "sample" {
3838

3939
## Attributes Reference
4040

41-
No additional attributes are exported.
41+
In addition to all arguments above, the following attributes are exported:
42+
43+
* `id` - (Computed) The ID of the cloud account.
44+
45+
* `organization_id` - (Computed) The ID of the organization, if the cloud account is part of any organization.

0 commit comments

Comments
 (0)