From 1cdcb8d5a0e3631bdfa5760ad362b345cf822f92 Mon Sep 17 00:00:00 2001 From: Julia Gu Date: Tue, 11 Jun 2024 14:45:00 -0700 Subject: [PATCH 01/10] init --- .../resource_datadog_service_account.go | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/datadog/fwprovider/resource_datadog_service_account.go b/datadog/fwprovider/resource_datadog_service_account.go index 2df13434b1..24b013570f 100644 --- a/datadog/fwprovider/resource_datadog_service_account.go +++ b/datadog/fwprovider/resource_datadog_service_account.go @@ -72,6 +72,7 @@ func (r *serviceAccountResource) Schema(_ context.Context, _ resource.SchemaRequ "roles": schema.SetAttribute{ Description: "A list a role IDs to assign to the service account.", Optional: true, + Computed: true, ElementType: types.StringType, }, "id": utils.ResourceIDAttribute(), @@ -347,6 +348,16 @@ func (r *serviceAccountResource) updateRolesFw(ctx context.Context, userID strin rolesToRemove := utils.StringSliceDifference(oldRolesSlice, newRolesSlice) rolesToAdd := utils.StringSliceDifference(newRolesSlice, oldRolesSlice) + for _, role := range rolesToAdd { + roleRelation := datadogV2.NewRelationshipToUserWithDefaults() + roleRelationData := datadogV2.NewRelationshipToUserDataWithDefaults() + roleRelationData.SetId(userID) + roleRelation.SetData(*roleRelationData) + _, _, err := r.RolesApiV2.AddUserToRole(r.Auth, role, *roleRelation) + if err != nil { + return diag.NewErrorDiagnostic("error adding user to role: ", err.Error()) + } + } for _, role := range rolesToRemove { userRelation := datadogV2.NewRelationshipToUserWithDefaults() userRelationData := datadogV2.NewRelationshipToUserDataWithDefaults() @@ -358,15 +369,6 @@ func (r *serviceAccountResource) updateRolesFw(ctx context.Context, userID strin return diag.NewErrorDiagnostic("error removing user from role: ", err.Error()) } } - for _, role := range rolesToAdd { - roleRelation := datadogV2.NewRelationshipToUserWithDefaults() - roleRelationData := datadogV2.NewRelationshipToUserDataWithDefaults() - roleRelationData.SetId(userID) - roleRelation.SetData(*roleRelationData) - _, _, err := r.RolesApiV2.AddUserToRole(r.Auth, role, *roleRelation) - if err != nil { - return diag.NewErrorDiagnostic("error adding user to role: ", err.Error()) - } - } + return nil } From 8b334611189708ef6dc9e9c6dab69bd3641e68e3 Mon Sep 17 00:00:00 2001 From: Julia Gu Date: Fri, 14 Jun 2024 10:35:46 -0700 Subject: [PATCH 02/10] fix test --- datadog/fwprovider/resource_datadog_service_account.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datadog/fwprovider/resource_datadog_service_account.go b/datadog/fwprovider/resource_datadog_service_account.go index 24b013570f..613b650e2e 100644 --- a/datadog/fwprovider/resource_datadog_service_account.go +++ b/datadog/fwprovider/resource_datadog_service_account.go @@ -69,7 +69,7 @@ func (r *serviceAccountResource) Schema(_ context.Context, _ resource.SchemaRequ Description: "Email of the associated user.", Required: true, }, - "roles": schema.SetAttribute{ + "roles": schema.ListAttribute{ Description: "A list a role IDs to assign to the service account.", Optional: true, Computed: true, From c846ac35d7bdb40d8e549333630bb6f5273cb411 Mon Sep 17 00:00:00 2001 From: Julia Gu Date: Fri, 14 Jun 2024 10:47:09 -0700 Subject: [PATCH 03/10] make docs --- datadog/fwprovider/resource_datadog_user_role.go | 2 +- docs/resources/service_account.md | 2 +- docs/resources/user_role.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/datadog/fwprovider/resource_datadog_user_role.go b/datadog/fwprovider/resource_datadog_user_role.go index 7ecf6855be..e233db3127 100644 --- a/datadog/fwprovider/resource_datadog_user_role.go +++ b/datadog/fwprovider/resource_datadog_user_role.go @@ -48,7 +48,7 @@ func (r *userRoleResource) Metadata(_ context.Context, request resource.Metadata func (r *userRoleResource) Schema(_ context.Context, _ resource.SchemaRequest, response *resource.SchemaResponse) { response.Schema = schema.Schema{ - Description: "Provides a Datadog UserRole resource. This can be used to create and manage Datadog User Roles. Conflicts may occur if used together with the `datadog_user` resource's `roles` attribute. This resource is in beta and is subject to change.", + Description: "Provides a Datadog UserRole resource. This can be used to create and manage Datadog User Roles. Conflicts may occur if used together with the `datadog_user` resource's `roles` attribute or the `datadog_service_account` resource's `roles` attribute. This resource is in beta and is subject to change.", Attributes: map[string]schema.Attribute{ "id": utils.ResourceIDAttribute(), "role_id": schema.StringAttribute{ diff --git a/docs/resources/service_account.md b/docs/resources/service_account.md index b9e08d1722..8e5d7c2f1f 100644 --- a/docs/resources/service_account.md +++ b/docs/resources/service_account.md @@ -36,7 +36,7 @@ resource "datadog_service_account" "bar" { - `disabled` (Boolean) Whether the service account is disabled. Defaults to `false`. - `name` (String) Name for the service account. -- `roles` (Set of String) A list a role IDs to assign to the service account. +- `roles` (List of String) A list a role IDs to assign to the service account. ### Read-Only diff --git a/docs/resources/user_role.md b/docs/resources/user_role.md index c6a7407867..95a3943ad1 100644 --- a/docs/resources/user_role.md +++ b/docs/resources/user_role.md @@ -3,12 +3,12 @@ page_title: "datadog_user_role Resource - terraform-provider-datadog" subcategory: "" description: |- - Provides a Datadog UserRole resource. This can be used to create and manage Datadog User Roles. Conflicts may occur if used together with the datadog_user resource's roles attribute. This resource is in beta and is subject to change. + Provides a Datadog UserRole resource. This can be used to create and manage Datadog User Roles. Conflicts may occur if used together with the datadog_user resource's roles attribute or the datadog_service_account resource's roles attribute. This resource is in beta and is subject to change. --- # datadog_user_role (Resource) -Provides a Datadog UserRole resource. This can be used to create and manage Datadog User Roles. Conflicts may occur if used together with the `datadog_user` resource's `roles` attribute. This resource is in beta and is subject to change. +Provides a Datadog UserRole resource. This can be used to create and manage Datadog User Roles. Conflicts may occur if used together with the `datadog_user` resource's `roles` attribute or the `datadog_service_account` resource's `roles` attribute. This resource is in beta and is subject to change. ## Example Usage From 1b3d6c89f8777ecdda7e35e12b2960e67c3db89d Mon Sep 17 00:00:00 2001 From: Julia Gu Date: Fri, 14 Jun 2024 10:52:22 -0700 Subject: [PATCH 04/10] hmm --- datadog/fwprovider/data_source_datadog_service_account.go | 2 +- datadog/fwprovider/resource_datadog_service_account.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datadog/fwprovider/data_source_datadog_service_account.go b/datadog/fwprovider/data_source_datadog_service_account.go index 48bf8c3106..f9c8fe7788 100644 --- a/datadog/fwprovider/data_source_datadog_service_account.go +++ b/datadog/fwprovider/data_source_datadog_service_account.go @@ -102,7 +102,7 @@ func (d *datadogServiceAccountDatasource) Schema(_ context.Context, _ datasource Computed: true, Description: "Whether the user is verified.", }, - "roles": schema.ListAttribute{ + "roles": schema.SetAttribute{ Computed: true, Description: "Roles assigned to this service account.", ElementType: types.StringType, diff --git a/datadog/fwprovider/resource_datadog_service_account.go b/datadog/fwprovider/resource_datadog_service_account.go index 613b650e2e..24b013570f 100644 --- a/datadog/fwprovider/resource_datadog_service_account.go +++ b/datadog/fwprovider/resource_datadog_service_account.go @@ -69,7 +69,7 @@ func (r *serviceAccountResource) Schema(_ context.Context, _ resource.SchemaRequ Description: "Email of the associated user.", Required: true, }, - "roles": schema.ListAttribute{ + "roles": schema.SetAttribute{ Description: "A list a role IDs to assign to the service account.", Optional: true, Computed: true, From f11e61ce290e32c2c3b659574d5c1f3c29a028e4 Mon Sep 17 00:00:00 2001 From: Julia Gu Date: Fri, 14 Jun 2024 10:57:10 -0700 Subject: [PATCH 05/10] more --- datadog/fwprovider/data_source_datadog_service_account.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datadog/fwprovider/data_source_datadog_service_account.go b/datadog/fwprovider/data_source_datadog_service_account.go index f9c8fe7788..2b69fb9c8e 100644 --- a/datadog/fwprovider/data_source_datadog_service_account.go +++ b/datadog/fwprovider/data_source_datadog_service_account.go @@ -33,7 +33,7 @@ type datadogServiceAccountDatasourceModel struct { Status types.String `tfsdk:"status"` Title types.String `tfsdk:"title"` Verified types.Bool `tfsdk:"verified"` - Roles types.List `tfsdk:"roles"` + Roles types.Set `tfsdk:"roles"` } type datadogServiceAccountDatasource struct { @@ -202,5 +202,5 @@ func (r *datadogServiceAccountDatasource) updateState(ctx context.Context, state } } } - state.Roles, _ = types.ListValueFrom(ctx, types.StringType, roles) + state.Roles, _ = types.SetValueFrom(ctx, types.StringType, roles) } From 12b976d6bcc9322ea6c9c01ed81bf4e2c48aa79b Mon Sep 17 00:00:00 2001 From: Julia Gu Date: Fri, 14 Jun 2024 11:02:25 -0700 Subject: [PATCH 06/10] make docs again --- docs/data-sources/service_account.md | 2 +- docs/resources/service_account.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/data-sources/service_account.md b/docs/data-sources/service_account.md index ecf93576aa..04dbf36bf0 100644 --- a/docs/data-sources/service_account.md +++ b/docs/data-sources/service_account.md @@ -28,7 +28,7 @@ Use this data source to retrieve information about an existing Datadog service a - `handle` (String) Handle of the user. - `icon` (String) URL of the user's icon. - `name` (String) Name of the user. -- `roles` (List of String) Roles assigned to this service account. +- `roles` (Set of String) Roles assigned to this service account. - `status` (String) Status of the user. - `title` (String) Title of the user. - `verified` (Boolean) Whether the user is verified. diff --git a/docs/resources/service_account.md b/docs/resources/service_account.md index 8e5d7c2f1f..b9e08d1722 100644 --- a/docs/resources/service_account.md +++ b/docs/resources/service_account.md @@ -36,7 +36,7 @@ resource "datadog_service_account" "bar" { - `disabled` (Boolean) Whether the service account is disabled. Defaults to `false`. - `name` (String) Name for the service account. -- `roles` (List of String) A list a role IDs to assign to the service account. +- `roles` (Set of String) A list a role IDs to assign to the service account. ### Read-Only From 42b9b276a2b87435ff3fc70603f70e4c3c735408 Mon Sep 17 00:00:00 2001 From: Julia Gu Date: Mon, 17 Jun 2024 16:25:21 -0700 Subject: [PATCH 07/10] try fix --- datadog/fwprovider/resource_datadog_service_account.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/datadog/fwprovider/resource_datadog_service_account.go b/datadog/fwprovider/resource_datadog_service_account.go index 24b013570f..df54d061d1 100644 --- a/datadog/fwprovider/resource_datadog_service_account.go +++ b/datadog/fwprovider/resource_datadog_service_account.go @@ -299,7 +299,9 @@ func buildDatadogServiceAccountV2Request(ctx context.Context, state *serviceAcco serviceAccountCreate.SetAttributes(*serviceAccountAttributes) var roles []string - diags.Append(state.Roles.ElementsAs(ctx, &roles, false)...) + if !state.Roles.IsNull() { + diags.Append(state.Roles.ElementsAs(ctx, &roles, false)...) + } rolesData := make([]datadogV2.RelationshipToRoleData, len(roles)) for i, role := range roles { roleData := datadogV2.NewRelationshipToRoleData() From 1e47ea0e5318e1cdf7d77257a15af75f867cbb33 Mon Sep 17 00:00:00 2001 From: Julia Gu Date: Mon, 17 Jun 2024 16:34:07 -0700 Subject: [PATCH 08/10] try unknown? --- datadog/fwprovider/resource_datadog_service_account.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datadog/fwprovider/resource_datadog_service_account.go b/datadog/fwprovider/resource_datadog_service_account.go index df54d061d1..d596d65886 100644 --- a/datadog/fwprovider/resource_datadog_service_account.go +++ b/datadog/fwprovider/resource_datadog_service_account.go @@ -299,7 +299,7 @@ func buildDatadogServiceAccountV2Request(ctx context.Context, state *serviceAcco serviceAccountCreate.SetAttributes(*serviceAccountAttributes) var roles []string - if !state.Roles.IsNull() { + if state.Roles.IsUnknown() { diags.Append(state.Roles.ElementsAs(ctx, &roles, false)...) } rolesData := make([]datadogV2.RelationshipToRoleData, len(roles)) From 9928ec0c1e333d9a4c1f98156094bc6d4464ded7 Mon Sep 17 00:00:00 2001 From: Julia Gu Date: Mon, 17 Jun 2024 16:37:32 -0700 Subject: [PATCH 09/10] oops --- datadog/fwprovider/resource_datadog_service_account.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datadog/fwprovider/resource_datadog_service_account.go b/datadog/fwprovider/resource_datadog_service_account.go index d596d65886..fd412f3408 100644 --- a/datadog/fwprovider/resource_datadog_service_account.go +++ b/datadog/fwprovider/resource_datadog_service_account.go @@ -299,7 +299,7 @@ func buildDatadogServiceAccountV2Request(ctx context.Context, state *serviceAcco serviceAccountCreate.SetAttributes(*serviceAccountAttributes) var roles []string - if state.Roles.IsUnknown() { + if !state.Roles.IsUnknown() { diags.Append(state.Roles.ElementsAs(ctx, &roles, false)...) } rolesData := make([]datadogV2.RelationshipToRoleData, len(roles)) From 15ec09ee954fa13d4e6ac8ba4ab0c520076763f0 Mon Sep 17 00:00:00 2001 From: Julia Gu <45079895+retsguj@users.noreply.github.com> Date: Thu, 20 Jun 2024 08:54:49 -0700 Subject: [PATCH 10/10] [datadog_service_account] Implement exact match filtering (#2447) * add exact match * make docs --- .../data_source_datadog_service_account.go | 34 +++++++++++++++++-- docs/data-sources/service_account.md | 1 + 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/datadog/fwprovider/data_source_datadog_service_account.go b/datadog/fwprovider/data_source_datadog_service_account.go index 2b69fb9c8e..e6c6ee1303 100644 --- a/datadog/fwprovider/data_source_datadog_service_account.go +++ b/datadog/fwprovider/data_source_datadog_service_account.go @@ -24,6 +24,7 @@ type datadogServiceAccountDatasourceModel struct { ID types.String `tfsdk:"id"` Filter types.String `tfsdk:"filter"` FilterStatus types.String `tfsdk:"filter_status"` + ExactMatch types.Bool `tfsdk:"exact_match"` // Results Disabled types.Bool `tfsdk:"disabled"` Email types.String `tfsdk:"email"` @@ -69,6 +70,10 @@ func (d *datadogServiceAccountDatasource) Schema(_ context.Context, _ datasource Description: "Filter on status attribute. Comma separated list, with possible values `Active`, `Pending`, and `Disabled`.", Optional: true, }, + "exact_match": schema.BoolAttribute{ + Description: "When true, `filter` string is exact matched against the user's `email`, followed by `name` attribute.", + Optional: true, + }, // Computed values "disabled": schema.BoolAttribute{ Computed: true, @@ -133,7 +138,8 @@ func (d *datadogServiceAccountDatasource) Read(ctx context.Context, req datasour userData = ddResp.Data } else { optionalParams := datadogV2.ListUsersOptionalParameters{} - optionalParams.WithFilter(state.Filter.ValueString()) + filter := state.Filter.ValueString() + optionalParams.WithFilter(filter) if !state.FilterStatus.IsNull() { optionalParams.WithFilterStatus(state.FilterStatus.ValueString()) } @@ -151,7 +157,8 @@ func (d *datadogServiceAccountDatasource) Read(ctx context.Context, req datasour serviceAccounts = append(serviceAccounts, user) } } - if len(serviceAccounts) > 1 { + isExactMatch := state.ExactMatch.ValueBool() + if len(serviceAccounts) > 1 && !isExactMatch { resp.Diagnostics.AddError("filter keyword returned more than one result, use more specific search criteria", "") return } @@ -160,6 +167,29 @@ func (d *datadogServiceAccountDatasource) Read(ctx context.Context, req datasour return } userData = &serviceAccounts[0] + if isExactMatch { + matchCount := 0 + for _, serviceAccount := range serviceAccounts { + if *serviceAccount.GetAttributes().Email == filter { + userData = &serviceAccount + matchCount++ + continue + } + if *serviceAccount.GetAttributes().Name.Get() == filter { + userData = &serviceAccount + matchCount++ + continue + } + } + if matchCount > 1 { + resp.Diagnostics.AddError("your query returned more than one result for filter with exact match, please try a more specific search criteria", "") + return + } + if matchCount == 0 { + resp.Diagnostics.AddError("didn't find any service account matching filter string with exact match", "") + return + } + } } d.updateState(ctx, &state, userData) resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) diff --git a/docs/data-sources/service_account.md b/docs/data-sources/service_account.md index 04dbf36bf0..9e0c82139d 100644 --- a/docs/data-sources/service_account.md +++ b/docs/data-sources/service_account.md @@ -17,6 +17,7 @@ Use this data source to retrieve information about an existing Datadog service a ### Optional +- `exact_match` (Boolean) When true, `filter` string is exact matched against the user's `email`, followed by `name` attribute. - `filter` (String) Filter all users and service accounts by name, email, or role. - `filter_status` (String) Filter on status attribute. Comma separated list, with possible values `Active`, `Pending`, and `Disabled`. - `id` (String) The service account's ID.