Skip to content

Commit 2907e7b

Browse files
committed
[Issue-1111] Add independant Realm Attributes Resource
this will help decouple the realms attributes and allow reuse in modules without relying on the entire realm configuration. use is mutually exclusive between this resource and the realm attributes Signed-off-by: Ryan H <[email protected]>
1 parent b1f323a commit 2907e7b

File tree

9 files changed

+230
-12
lines changed

9 files changed

+230
-12
lines changed

docs/resources/realm.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ resource "keycloak_realm" "realm" {
8484
- `display_name_html` - (Optional) The display name for the realm that is rendered as HTML on the screen when logging in to the admin console.
8585
- `user_managed_access` - (Optional) When `true`, users are allowed to manage their own resources. Defaults to `false`.
8686
- `organizations_enabled` - (Optional) When `true`, organization support is enabled. Defaults to `false`.
87-
- `attributes` - (Optional) A map of custom attributes to add to the realm.
87+
- `attributes` - (Optional) A map of custom attributes to add to the realm. [Also available as mutually exlusive resource](./realm_attributes.md)
8888
- `internal_id` - (Optional) When specified, this will be used as the realm's internal ID within Keycloak. When not specified, the realm's internal ID will be set to the realm's name.
8989

9090
### Login Settings

docs/resources/realm_attributes.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
page_title: "keycloak_realm_attributes Resource"
3+
---
4+
5+
# keycloak\_realm_attributes Resource
6+
7+
Allows for creating and managing Realm attributes within Keycloak. `attributes` must not be tracked by the `realm` resource if this is used.
8+
9+
## Example Usage
10+
11+
```hcl
12+
resource "keycloak_realm" "realm_example" {
13+
realm = "realm-example"
14+
enabled = true
15+
}
16+
17+
resource "keycloak_realm_attributes" "realm_attributes" {
18+
realm_id = keycloak_realm.realm_example.id
19+
attributes = {
20+
baz = "bat"
21+
qux = "quux"
22+
}
23+
}
24+
```

example/main.tf

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,6 @@ resource "keycloak_realm" "test" {
7777
ssl_required = "external"
7878
password_policy = "upperCase(1) and length(8) and forceExpiredPasswordChange(365) and notUsername"
7979

80-
attributes = {
81-
mycustomAttribute = "myCustomValue"
82-
userProfileEnabled = true
83-
}
84-
8580
web_authn_policy {
8681
relying_party_entity_name = "Example"
8782
relying_party_id = "keycloak.example.com"
@@ -101,6 +96,15 @@ resource "keycloak_realm" "test" {
10196
}
10297
}
10398

99+
resource "keycloak_realm_attributes" "test_attributes" {
100+
realm_id = keycloak_realm.test.id
101+
102+
attributes = {
103+
mycustomAttribute = "myCustomValue"
104+
userProfileEnabled = true
105+
}
106+
}
107+
104108
resource "keycloak_realm_localization" "test_translation" {
105109
realm_id = keycloak_realm.test.id
106110
locale = "en"

keycloak/realm_attributes.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package keycloak
2+
3+
import (
4+
"context"
5+
"fmt"
6+
)
7+
8+
type RealmAttributes struct {
9+
RealmId string `json:"-"`
10+
Attributes map[string]interface{} `json:"attributes"`
11+
}
12+
13+
func (keycloakClient *KeycloakClient) GetRealmAttributes(ctx context.Context, realmId string) (*RealmAttributes, error) {
14+
realm, err := keycloakClient.GetRealm(ctx, realmId)
15+
if err != nil {
16+
return nil, err
17+
}
18+
19+
return &RealmAttributes{
20+
RealmId: realmId,
21+
Attributes: realm.Attributes,
22+
}, nil
23+
}
24+
25+
func (keycloakClient *KeycloakClient) UpdateRealmAttributes(ctx context.Context, realmId string, attributes *RealmAttributes) error {
26+
return keycloakClient.put(ctx, fmt.Sprintf("/realms/%s", realmId), attributes)
27+
}

provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func KeycloakProvider(client *keycloak.KeycloakClient) *schema.Provider {
3232
},
3333
ResourcesMap: map[string]*schema.Resource{
3434
"keycloak_realm": resourceKeycloakRealm(),
35+
"keycloak_realm_attributes": resourceKeycloakRealmAttributes(),
3536
"keycloak_realm_events": resourceKeycloakRealmEvents(),
3637
"keycloak_realm_default_client_scopes": resourceKeycloakRealmDefaultClientScopes(),
3738
"keycloak_realm_optional_client_scopes": resourceKeycloakRealmOptionalClientScopes(),
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
"github.com/keycloak/terraform-provider-keycloak/keycloak"
10+
)
11+
12+
func resourceKeycloakRealmAttributes() *schema.Resource {
13+
return &schema.Resource{
14+
CreateContext: resourceKeycloakRealmAttributesCreate,
15+
ReadContext: resourceKeycloakRealmAttributesRead,
16+
UpdateContext: resourceKeycloakRealmAttributesUpdate,
17+
DeleteContext: resourceKeycloakRealmAttributesDelete,
18+
Schema: map[string]*schema.Schema{
19+
"realm_id": {
20+
Type: schema.TypeString,
21+
Required: true,
22+
},
23+
"attributes": {
24+
Type: schema.TypeMap,
25+
Required: true,
26+
Description: "A map of attributes for the realm",
27+
},
28+
},
29+
}
30+
}
31+
func resourceKeycloakRealmAttributesCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
32+
keycloakClient := meta.(*keycloak.KeycloakClient)
33+
realmId := data.Get("realm_id").(string)
34+
attributes := mapFromDataToRealmAttributes(data)
35+
36+
err := keycloakClient.UpdateRealmAttributes(ctx, realmId, attributes)
37+
if err != nil {
38+
return diag.FromErr(err)
39+
}
40+
41+
data.SetId(realmId)
42+
43+
return resourceKeycloakRealmAttributesRead(ctx, data, meta)
44+
}
45+
46+
func resourceKeycloakRealmAttributesRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
47+
keycloakClient := meta.(*keycloak.KeycloakClient)
48+
realmId := data.Get("realm_id").(string)
49+
attributes, err := keycloakClient.GetRealmAttributes(ctx, realmId)
50+
if err != nil {
51+
return diag.FromErr(err)
52+
}
53+
if attributes == nil {
54+
return nil
55+
}
56+
57+
mapFromRealmAttributesToData(attributes, data)
58+
59+
return nil
60+
}
61+
62+
func resourceKeycloakRealmAttributesUpdate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
63+
keycloakClient := meta.(*keycloak.KeycloakClient)
64+
realmId := data.Get("realm_id").(string)
65+
attributes := mapFromDataToRealmAttributes(data)
66+
67+
err := keycloakClient.UpdateRealmAttributes(ctx, realmId, attributes)
68+
if err != nil {
69+
return diag.FromErr(err)
70+
}
71+
72+
return resourceKeycloakRealmAttributesRead(ctx, data, meta)
73+
}
74+
75+
func resourceKeycloakRealmAttributesDelete(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
76+
keycloakClient := meta.(*keycloak.KeycloakClient)
77+
realmId := data.Get("realm_id").(string)
78+
attributes := &keycloak.RealmAttributes{
79+
Attributes: map[string]interface{}{},
80+
}
81+
82+
err := keycloakClient.UpdateRealmAttributes(ctx, realmId, attributes)
83+
if err != nil {
84+
return diag.FromErr(err)
85+
}
86+
87+
data.SetId("")
88+
89+
return nil
90+
}
91+
92+
func mapFromDataToRealmAttributes(data *schema.ResourceData) *keycloak.RealmAttributes {
93+
return &keycloak.RealmAttributes{
94+
RealmId: data.Get("realm_id").(string),
95+
Attributes: data.Get("attributes").(map[string]interface{}),
96+
}
97+
}
98+
99+
func mapFromRealmAttributesToData(attributes *keycloak.RealmAttributes, data *schema.ResourceData) {
100+
_attributes := map[string]interface{}{}
101+
if v, ok := data.GetOk("attributes"); ok {
102+
for key := range v.(map[string]interface{}) {
103+
_attributes[key] = attributes.Attributes[key]
104+
}
105+
}
106+
if err := data.Set("attributes", _attributes); err != nil {
107+
panic(fmt.Sprintf("Failed to set attributes: %v", err))
108+
}
109+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package provider
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
10+
)
11+
12+
func TestAccKeycloakRealmAttributes(t *testing.T) {
13+
realmName := acctest.RandomWithPrefix("tf-acc")
14+
15+
resource.Test(t, resource.TestCase{
16+
ProviderFactories: testAccProviderFactories,
17+
PreCheck: func() { testAccPreCheck(t) },
18+
Steps: []resource.TestStep{
19+
{
20+
Config: testKeycloakRealmAttributes_basic(realmName, "foo", "bar"),
21+
Check: testAccCheckKeycloakRealmAttributesExists(realmName),
22+
},
23+
},
24+
})
25+
}
26+
27+
func testKeycloakRealmAttributes_basic(realm string, name string, description string) string {
28+
return fmt.Sprintf(`
29+
resource "keycloak_realm" "realm" {
30+
realm = "%s"
31+
}
32+
33+
resource "keycloak_realm_attributes" "attributes" {
34+
realm_id = keycloak_realm.realm.id
35+
attributes = {
36+
name = "%s"
37+
description = "%s"
38+
}
39+
}`, realm, name, description)
40+
}
41+
42+
func testAccCheckKeycloakRealmAttributesExists(realm string) resource.TestCheckFunc {
43+
return func(s *terraform.State) error {
44+
_, err := keycloakClient.GetRealmAttributes(testCtx, realm)
45+
if err != nil {
46+
return fmt.Errorf("Realm attributes not found: %s", realm)
47+
}
48+
49+
return nil
50+
}
51+
}

provider/resource_keycloak_realm_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ package provider
22

33
import (
44
"fmt"
5+
"regexp"
6+
"testing"
7+
58
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
69
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
710
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
811
"github.com/keycloak/terraform-provider-keycloak/keycloak"
9-
"regexp"
10-
"testing"
1112
)
1213

1314
func TestAccKeycloakRealm_basic(t *testing.T) {

provider/resource_keycloak_user_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@ package provider
22

33
import (
44
"fmt"
5-
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
6-
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
7-
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
8-
"github.com/keycloak/terraform-provider-keycloak/keycloak"
95
"io"
106
"net/http"
117
"net/url"
128
"os"
139
"regexp"
1410
"strings"
1511
"testing"
12+
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
14+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
15+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
16+
"github.com/keycloak/terraform-provider-keycloak/keycloak"
1617
)
1718

1819
func TestAccKeycloakUser_basic_wo_attribute(t *testing.T) {

0 commit comments

Comments
 (0)