Skip to content

Commit 9994117

Browse files
committed
fix: change user groups from List to Set type to prevent ordering issues
- Changed groups attribute from types.List to types.Set in user resource and data sources - This prevents Terraform from detecting phantom changes when group order differs - Updated example files to use correct attribute names (first_name/last_name, disabled) - Added acceptance tests for user resource with groups - Added CHANGELOG.md to track changes
1 parent 1236071 commit 9994117

6 files changed

Lines changed: 310 additions & 58 deletions

File tree

CHANGELOG.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Fixed
11+
- Fixed groups ordering issue in `pocketid_user` resource where Terraform would detect phantom changes when group IDs were in different order. Changed `groups` attribute from List to Set type to properly handle unordered collections.
12+
- Fixed attribute names in example files - changed `given_name`/`family_name` to `first_name`/`last_name` and `enabled` to `disabled` to match actual schema.
13+
14+
## [0.1.0] - 2024-01-09
15+
16+
### Added
17+
- Initial release of the Terraform Provider for PocketID
18+
- `pocketid_client` resource for managing OAuth2/OIDC clients
19+
- `pocketid_user` resource for managing users
20+
- `pocketid_group` resource for managing user groups
21+
- `pocketid_client` data source for reading client information
22+
- `pocketid_clients` data source for listing all clients
23+
- `pocketid_user` data source for reading user information
24+
- `pocketid_users` data source for listing and filtering users
25+
- `pocketid_group` data source for reading group information
26+
- `pocketid_groups` data source for listing and filtering groups
27+
- Provider configuration with base URL and API token authentication
28+
- Support for TLS certificate verification skip option
29+
- Comprehensive documentation and examples
30+
- Full test coverage for all resources and data sources
31+
- CI/CD pipeline with GitHub Actions
32+
- GoReleaser configuration for automated releases
33+
34+
[Unreleased]: https://github.com/Trozz/terraform-provider-pocketid/compare/v0.1.0...HEAD
35+
[0.1.0]: https://github.com/Trozz/terraform-provider-pocketid/releases/tag/v0.1.0

examples/resources/users_groups.tf

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -24,56 +24,56 @@ resource "pocketid_group" "api_consumers" {
2424
# This resource creates the user account, but authentication setup is done separately
2525

2626
resource "pocketid_user" "admin_user" {
27-
username = "admin"
28-
email = "admin@example.com"
29-
given_name = "Admin"
30-
family_name = "User"
27+
username = "admin"
28+
email = "admin@example.com"
29+
first_name = "Admin"
30+
last_name = "User"
3131

3232
# Assign to administrator group
3333
groups = [
3434
pocketid_group.administrators.id
3535
]
3636

37-
enabled = true
37+
3838
}
3939

4040
resource "pocketid_user" "john_doe" {
41-
username = "john.doe"
42-
email = "john.doe@example.com"
43-
given_name = "John"
44-
family_name = "Doe"
41+
username = "john.doe"
42+
email = "john.doe@example.com"
43+
first_name = "John"
44+
last_name = "Doe"
4545

4646
# Assign to multiple groups
4747
groups = [
4848
pocketid_group.developers.id,
4949
pocketid_group.users.id
5050
]
5151

52-
enabled = true
52+
5353
}
5454

5555
resource "pocketid_user" "jane_smith" {
56-
username = "jane.smith"
57-
email = "jane.smith@example.com"
58-
given_name = "Jane"
59-
family_name = "Smith"
56+
username = "jane.smith"
57+
email = "jane.smith@example.com"
58+
first_name = "Jane"
59+
last_name = "Smith"
6060

6161
groups = [
6262
pocketid_group.users.id
6363
]
6464

65-
enabled = true
65+
6666
}
6767

6868
# Example: Disabled User Account
6969
resource "pocketid_user" "inactive_user" {
70-
username = "old.employee"
71-
email = "old.employee@example.com"
72-
given_name = "Former"
73-
family_name = "Employee"
70+
username = "old.employee"
71+
email = "old.employee@example.com"
72+
first_name = "Former"
73+
last_name = "Employee"
7474

7575
# User is disabled - cannot authenticate
76-
enabled = false
76+
disabled = true
7777
}
7878

7979
# Example: Service Account User
@@ -82,14 +82,14 @@ resource "pocketid_user" "service_account" {
8282
email = "ci-cd@example.com"
8383

8484
# Service accounts might not need human names
85-
given_name = "CI/CD"
86-
family_name = "Service"
85+
first_name = "CI/CD"
86+
last_name = "Service"
8787

8888
groups = [
8989
pocketid_group.api_consumers.id
9090
]
9191

92-
enabled = true
92+
9393
}
9494

9595
# Example: Using Dynamic Groups
@@ -110,44 +110,44 @@ resource "pocketid_group" "departments" {
110110
variable "team_members" {
111111
description = "List of team members to create"
112112
type = list(object({
113-
username = string
114-
email = string
115-
given_name = string
116-
family_name = string
117-
department = string
113+
username = string
114+
email = string
115+
first_name = string
116+
last_name = string
117+
department = string
118118
}))
119119
default = [
120120
{
121-
username = "alice.johnson"
122-
email = "alice.johnson@example.com"
123-
given_name = "Alice"
124-
family_name = "Johnson"
125-
department = "engineering"
121+
username = "alice.johnson"
122+
email = "alice.johnson@example.com"
123+
first_name = "Alice"
124+
last_name = "Johnson"
125+
department = "engineering"
126126
},
127127
{
128-
username = "bob.wilson"
129-
email = "bob.wilson@example.com"
130-
given_name = "Bob"
131-
family_name = "Wilson"
132-
department = "marketing"
128+
username = "bob.wilson"
129+
email = "bob.wilson@example.com"
130+
first_name = "Bob"
131+
last_name = "Wilson"
132+
department = "marketing"
133133
}
134134
]
135135
}
136136

137137
resource "pocketid_user" "team_members" {
138138
for_each = { for member in var.team_members : member.username => member }
139139

140-
username = each.value.username
141-
email = each.value.email
142-
given_name = each.value.given_name
143-
family_name = each.value.family_name
140+
username = each.value.username
141+
email = each.value.email
142+
first_name = each.value.first_name
143+
last_name = each.value.last_name
144144

145145
groups = [
146146
pocketid_group.users.id,
147147
pocketid_group.departments[each.value.department].id
148148
]
149149

150-
enabled = true
150+
151151
}
152152

153153
# Example: Outputs for Integration
@@ -182,13 +182,13 @@ output "developer_users" {
182182
# resource "pocketid_user" "new_admin" {
183183
# username = "admin2"
184184
# email = "admin2@example.com"
185-
# given_name = "Second"
186-
# family_name = "Admin"
185+
# first_name = "Second"
186+
# last_name = "Admin"
187187
#
188188
# # Reference the existing group
189189
# groups = [
190190
# data.pocketid_group.existing_admins.id
191191
# ]
192192
#
193-
# enabled = true
193+
# }
194194
# }

internal/datasources/user_data_source.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type userDataSourceModel struct {
3838
IsAdmin types.Bool `tfsdk:"is_admin"`
3939
Locale types.String `tfsdk:"locale"`
4040
Disabled types.Bool `tfsdk:"disabled"`
41-
Groups types.List `tfsdk:"groups"`
41+
Groups types.Set `tfsdk:"groups"`
4242
}
4343

4444
// Metadata returns the data source type name.
@@ -85,7 +85,7 @@ func (d *userDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, r
8585
Description: "Whether the user account is disabled.",
8686
Computed: true,
8787
},
88-
"groups": schema.ListAttribute{
88+
"groups": schema.SetAttribute{
8989
Description: "List of group IDs the user belongs to.",
9090
Computed: true,
9191
ElementType: types.StringType,
@@ -160,11 +160,11 @@ func (d *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, r
160160
for _, group := range userResp.UserGroups {
161161
groupIDs = append(groupIDs, group.ID)
162162
}
163-
groups, diags := types.ListValueFrom(ctx, types.StringType, groupIDs)
163+
groups, diags := types.SetValueFrom(ctx, types.StringType, groupIDs)
164164
resp.Diagnostics.Append(diags...)
165165
state.Groups = groups
166166
} else {
167-
state.Groups = types.ListNull(types.StringType)
167+
state.Groups = types.SetNull(types.StringType)
168168
}
169169

170170
// Set state

internal/datasources/users_data_source.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type userModel struct {
4343
IsAdmin types.Bool `tfsdk:"is_admin"`
4444
Locale types.String `tfsdk:"locale"`
4545
Disabled types.Bool `tfsdk:"disabled"`
46-
Groups types.List `tfsdk:"groups"`
46+
Groups types.Set `tfsdk:"groups"`
4747
}
4848

4949
// Metadata returns the data source type name.
@@ -94,7 +94,7 @@ func (d *usersDataSource) Schema(_ context.Context, _ datasource.SchemaRequest,
9494
Description: "Whether the user account is disabled.",
9595
Computed: true,
9696
},
97-
"groups": schema.ListAttribute{
97+
"groups": schema.SetAttribute{
9898
Description: "List of group IDs the user belongs to.",
9999
Computed: true,
100100
ElementType: types.StringType,
@@ -168,11 +168,11 @@ func (d *usersDataSource) Read(ctx context.Context, req datasource.ReadRequest,
168168
for _, group := range userResp.UserGroups {
169169
groupIDs = append(groupIDs, group.ID)
170170
}
171-
groups, diags := types.ListValueFrom(ctx, types.StringType, groupIDs)
171+
groups, diags := types.SetValueFrom(ctx, types.StringType, groupIDs)
172172
resp.Diagnostics.Append(diags...)
173173
userState.Groups = groups
174174
} else {
175-
userState.Groups = types.ListNull(types.StringType)
175+
userState.Groups = types.SetNull(types.StringType)
176176
}
177177

178178
state.Users = append(state.Users, userState)

0 commit comments

Comments
 (0)