Skip to content

Commit

Permalink
[datadog_service_account] Role-assignment improvements for service ac…
Browse files Browse the repository at this point in the history
…counts (#2434)

* init

* fix test

* make docs

* hmm

* more

* make docs again

* try fix

* try unknown?

* oops

* [datadog_service_account] Implement exact match filtering (#2447)

* add exact match

* make docs
  • Loading branch information
retsguj authored Jul 5, 2024
1 parent 8508ce8 commit 19c9189
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 20 deletions.
40 changes: 35 additions & 5 deletions datadog/fwprovider/data_source_datadog_service_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand All @@ -33,7 +34,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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -102,7 +107,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,
Expand Down Expand Up @@ -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())
}
Expand All @@ -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
}
Expand All @@ -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)...)
Expand Down Expand Up @@ -202,5 +232,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)
}
26 changes: 15 additions & 11 deletions datadog/fwprovider/resource_datadog_service_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -298,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.IsUnknown() {
diags.Append(state.Roles.ElementsAs(ctx, &roles, false)...)
}
rolesData := make([]datadogV2.RelationshipToRoleData, len(roles))
for i, role := range roles {
roleData := datadogV2.NewRelationshipToRoleData()
Expand Down Expand Up @@ -347,6 +350,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()
Expand All @@ -358,15 +371,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
}
2 changes: 1 addition & 1 deletion datadog/fwprovider/resource_datadog_user_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
3 changes: 2 additions & 1 deletion docs/data-sources/service_account.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -28,7 +29,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.
4 changes: 2 additions & 2 deletions docs/resources/user_role.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 19c9189

Please sign in to comment.