providers/keycloak/generator.go (488 lines of code) (raw):

// Copyright 2018 The Terraformer Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package keycloak import ( "context" "errors" "fmt" "sort" "strings" "github.com/GoogleCloudPlatform/terraformer/terraformutils" "github.com/mrparkers/terraform-provider-keycloak/keycloak" ) type RealmGenerator struct { KeycloakService } func (g *RealmGenerator) InitResources() error { var realms []*keycloak.Realm var realmsGroups []*keycloak.Group ctx := context.TODO() // Connect to keycloak instance userAgent := "GoogleCloudPlatform Terraformer/0.8.22 (+https://github.com/GoogleCloudPlatform/terraformer) Terraform Plugin SDK/2.10.1" kck, err := keycloak.NewKeycloakClient(ctx, g.GetArgs()["url"].(string), g.GetArgs()["base_path"].(string), g.GetArgs()["client_id"].(string), g.GetArgs()["client_secret"].(string), g.GetArgs()["realm"].(string), "", "", true, g.GetArgs()["client_timeout"].(int), g.GetArgs()["root_ca_certificate"].(string), g.GetArgs()["tls_insecure_skip_verify"].(bool), userAgent, g.GetArgs()["red_hat_sso"].(bool), make(map[string]string)) if err != nil { return errors.New("keycloak: could not connect to Keycloak") } // Get realm resources target := g.GetArgs()["target"].(string) if target == "" { realms, err = kck.GetRealms(ctx) if err != nil { return errors.New("keycloak: could not get realms attributes in Keycloak") } } else { realm, err := kck.GetRealm(ctx, target) if err != nil { return errors.New("keycloak: could not get " + target + " realm attributes in Keycloak") } realms = append(realms, realm) } g.Resources = append(g.Resources, g.createRealmResources(realms)...) // For each realm, get resources for _, realm := range realms { // Get required actions resources requiredActions, err := kck.GetRequiredActions(ctx, realm.Realm) if err != nil { return fmt.Errorf("keycloak: could not get required actions of realm %s in Keycloak. err: %w", realm.Realm, err) } g.Resources = append(g.Resources, g.createRequiredActionResources(requiredActions)...) // Get top-level authentication flows resources authenticationFlows, err := kck.ListAuthenticationFlows(ctx, realm.Realm) if err != nil { return fmt.Errorf("keycloak: could not get authentication flows of realm %s in Keycloak. err: %w", realm.Realm, err) } g.Resources = append(g.Resources, g.createAuthenticationFlowResources(authenticationFlows)...) // For each authentication flow, get subFlow, execution and execution config resources for _, topLevelAuthenticationFlow := range authenticationFlows { authenticationSubFlowOrExecutions, err := kck.ListAuthenticationExecutions(ctx, realm.Realm, topLevelAuthenticationFlow.Alias) if err != nil { return fmt.Errorf("keycloak: could not get authentication executions of authentication flow %s of realm %s in Keycloak. err: %w", topLevelAuthenticationFlow.Alias, realm.Realm, err) } var stack []*keycloak.AuthenticationExecutionInfo parentFlowAlias := topLevelAuthenticationFlow.Alias for _, authenticationSubFlowOrExecution := range authenticationSubFlowOrExecutions { // Find the parent flow alias if len(stack) > 0 { previous := stack[len(stack)-1] if authenticationSubFlowOrExecution.Level < previous.Level { // Find the last sub flow/execution for the current level stack = stack[:authenticationSubFlowOrExecution.Level+1] previous = stack[len(stack)-1] } if authenticationSubFlowOrExecution.Level == previous.Level { // Same level sub flow/execution, it means that the sub flow/execution has same parent flow of the last sub flow/execution parentFlowAlias = previous.ParentFlowAlias } else if authenticationSubFlowOrExecution.Level > previous.Level { // Deep level sub flow/execution, it means that the parent flow is the last sub flow/execution if previous.AuthenticationFlow { parentFlowAlias = previous.Alias } else { return errors.New("keycloak: invalid parent sub flow, it should be a sub flow but it's an execution") } } } var resource terraformutils.Resource switch authenticationSubFlowOrExecution.AuthenticationFlow { case true: authenticationSubFlow, err := kck.GetAuthenticationSubFlow(ctx, realm.Realm, parentFlowAlias, authenticationSubFlowOrExecution.FlowId) if err != nil { return fmt.Errorf("keycloak: could not get authentication subflow %s of realm %s in Keycloak. err: %w", authenticationSubFlowOrExecution.FlowId, realm.Realm, err) } // Need to store the alias and parent flow alias authenticationSubFlowOrExecution.Alias = authenticationSubFlow.Alias authenticationSubFlowOrExecution.ParentFlowAlias = parentFlowAlias resource = g.createAuthenticationSubFlowResource(authenticationSubFlow) g.Resources = append(g.Resources, resource) case false: authenticationExecution, err := kck.GetAuthenticationExecution(ctx, realm.Realm, parentFlowAlias, authenticationSubFlowOrExecution.Id) if err != nil { return fmt.Errorf("keycloak: could not get authentication execution %s of realm %s in Keycloak. err: %w", authenticationSubFlowOrExecution.Id, realm.Realm, err) } // Need to store the parent flow alias authenticationSubFlowOrExecution.ParentFlowAlias = parentFlowAlias resource = g.createAuthenticationExecutionResource(authenticationExecution) g.Resources = append(g.Resources, resource) if authenticationSubFlowOrExecution.AuthenticationConfig != "" { authenticationExecutionConfig := &keycloak.AuthenticationExecutionConfig{ RealmId: realm.Realm, Id: authenticationSubFlowOrExecution.AuthenticationConfig, ExecutionId: authenticationSubFlowOrExecution.Id, } err := kck.GetAuthenticationExecutionConfig(ctx, authenticationExecutionConfig) if err != nil { return fmt.Errorf("keycloak: could not get authentication execution config %s of realm %s in Keycloak. err: %w", authenticationExecutionConfig.Id, realm.Realm, err) } g.Resources = append(g.Resources, g.createAuthenticationExecutionConfigResource(authenticationExecutionConfig)) } } if len(stack) > 0 && authenticationSubFlowOrExecution.Index > 0 { previous := stack[len(stack)-1] var resourceType string var resourceName string if previous.AuthenticationFlow { resourceType = "keycloak_authentication_subflow" resourceName = "authentication_subflow_" + normalizeResourceName(realm.Realm) + "_" + normalizeResourceName(previous.FlowId) } else { resourceType = "keycloak_authentication_execution" resourceName = "authentication_execution_" + normalizeResourceName(realm.Realm) + "_" + normalizeResourceName(previous.Id) } resource.AdditionalFields["depends_on"] = []string{resourceType + "." + terraformutils.TfSanitize(resourceName)} } // Stack the current sub flow/execution if len(stack) > 0 && stack[len(stack)-1].Level == authenticationSubFlowOrExecution.Level { // Replace it if it's same level stack[len(stack)-1] = authenticationSubFlowOrExecution } else { stack = append(stack, authenticationSubFlowOrExecution) } } } // Get custom federations resources // TODO: support kerberos user federation customUserFederations, err := kck.GetCustomUserFederations(ctx, realm.Realm, realm.Id) if err != nil { return errors.New("keycloak: could not get custom user federations of realm " + realm.Id + " in Keycloak") } g.Resources = append(g.Resources, g.createCustomUserFederationResources(customUserFederations)...) // For each custom federation, get mappers resources for _, customUserFederation := range *customUserFederations { if customUserFederation.ProviderId == "ldap" { mappers, err := kck.GetLdapUserFederationMappers(ctx, realm.Realm, customUserFederation.Id) if err != nil { return errors.New("keycloak: could not get mappers of ldap user federation " + customUserFederation.Name + " of realm " + realm.Realm + " in Keycloak") } g.Resources = append(g.Resources, g.createLdapMapperResources(realm.Realm, customUserFederation.Name, mappers)...) } } // Get groups tree and default groups resources realmGroups, err := kck.GetGroups(ctx, realm.Realm) if err != nil { return errors.New("keycloak: could not get groups of realm " + realm.Realm + " in Keycloak") } realmsGroups = append(realmsGroups, realmGroups...) g.Resources = append(g.Resources, g.createDefaultGroupResource(realm.Realm)) // Get users resources realmUsers, err := kck.GetUsers(ctx, realm.Realm) if err != nil { return errors.New("keycloak: could not get users of realm " + realm.Realm + " in Keycloak") } g.Resources = append(g.Resources, g.createUserResources(realmUsers)...) // Get realm open id client scopes resources realmScopes, err := kck.ListOpenidClientScopesWithFilter(ctx, realm.Realm, func(scope *keycloak.OpenidClientScope) bool { return true }) if err != nil { return errors.New("keycloak: could not get realm scopes of realm " + realm.Realm + " in Keycloak") } g.Resources = append(g.Resources, g.createScopeResources(realm.Realm, realmScopes)...) // Get open id clients realmClients, err := kck.GetOpenidClients(ctx, realm.Realm, true) if err != nil { return errors.New("keycloak: could not get open id clients of realm " + realm.Realm + " in Keycloak") } g.Resources = append(g.Resources, g.createOpenIDClientResources(realmClients)...) // For each open id client, get resources mapServiceAccountIds := map[string]map[string]string{} mapContainerIDs := map[string]string{} mapClientIDs := map[string]string{} for _, client := range realmClients { mapClientIDs[client.Id] = client.ClientId mapContainerIDs[client.Id] = "_" + client.ClientId // Get open id client protocol mappers resources clientMappers, err := kck.GetGenericProtocolMappers(ctx, realm.Realm, client.Id) if err != nil { return errors.New("keycloak: could not get protocol mappers of open id client " + client.ClientId + " of realm " + realm.Realm + " in Keycloak") } g.Resources = append(g.Resources, g.createOpenIDProtocolMapperResources(client.ClientId, clientMappers)...) // Get open id client default scopes resources clientScopes, err := kck.GetOpenidDefaultClientScopes(ctx, realm.Realm, client.Id) if err != nil { return errors.New("keycloak: could not get default client scopes of open id client " + client.ClientId + " of realm " + realm.Realm + " in Keycloak") } if len(*clientScopes) > 0 { g.Resources = append(g.Resources, g.createOpenidClientScopesResources(realm.Realm, client.Id, client.ClientId, "default", clientScopes)) } // Get open id client optional scopes resources clientScopes, err = kck.GetOpenidOptionalClientScopes(ctx, realm.Realm, client.Id) if err != nil { return errors.New("keycloak: could not get optional client scopes of open id client " + client.ClientId + " of realm " + realm.Realm + " in Keycloak") } if len(*clientScopes) > 0 { g.Resources = append(g.Resources, g.createOpenidClientScopesResources(realm.Realm, client.Id, client.ClientId, "optional", clientScopes)) } // Prepare a slice to be able to link roles associated to service account roles to be associated to the open id client, only if service accounts are enabled if !client.ServiceAccountsEnabled { continue } serviceAccountUser, err := kck.GetOpenidClientServiceAccountUserId(ctx, realm.Realm, client.Id) if err != nil { return errors.New("keycloak: could not get service account user associated to open id client " + client.ClientId + " of realm " + realm.Realm + " in Keycloak") } mapServiceAccountIds[serviceAccountUser.Id] = map[string]string{} mapServiceAccountIds[serviceAccountUser.Id]["Id"] = client.Id mapServiceAccountIds[serviceAccountUser.Id]["ClientId"] = client.ClientId } // Get open id client roles clientRoles, err := kck.GetClientRoles(ctx, realm.Realm, realmClients) if err != nil { return errors.New("keycloak: could not get open id clients roles of realm " + realm.Realm + " in Keycloak") } // Get roles realmRoles, err := kck.GetRealmRoles(ctx, realm.Realm) if err != nil { return errors.New("keycloak: could not get realm roles of realm " + realm.Realm + " in Keycloak") } // Set ContainerId of the roles, for realm = "", for open id clients = "_" + client.ClientId // and get roles resources mapContainerIDs[realm.Realm] = "" roles := append(clientRoles, realmRoles...) for _, role := range roles { role.ContainerId = mapContainerIDs[role.ContainerId] } g.Resources = append(g.Resources, g.createRoleResources(roles)...) // Get service account roles resources usersInRole, err := kck.GetClientRoleUsers(ctx, realm.Realm, clientRoles) if err != nil { return errors.New("keycloak: could not get users roles of realm " + realm.Realm + " in Keycloak") } g.Resources = append(g.Resources, g.createServiceAccountClientRolesResources(realm.Realm, clientRoles, *usersInRole, mapServiceAccountIds, mapClientIDs)...) } // Parse the groups trees, and get all the groups // Get groups resources groups := g.flattenGroups(realmsGroups, "") g.Resources = append(g.Resources, g.createGroupResources(groups)...) // For each group, get group memberships and roles resources for _, group := range groups { // Get group members resources members, err := kck.GetGroupMembers(ctx, group.RealmId, group.Id) if err != nil { return errors.New("keycloak: could not get group members of group " + group.Name + " in Keycloak") } if len(members) > 0 { groupMembers := make([]string, len(members)) for k, member := range members { groupMembers[k] = member.Username } g.Resources = append(g.Resources, g.createGroupMembershipsResource(group.RealmId, group.Id, group.Name, groupMembers)) } // Get group roles resources // For realm roles and open id clients roles groupDetails, err := kck.GetGroup(ctx, group.RealmId, group.Id) if err != nil { return errors.New("keycloak: could not get details about group " + group.Name + " in Keycloak") } groupRoles := []string{} if len(groupDetails.RealmRoles) > 0 { groupRoles = append(groupRoles, groupDetails.RealmRoles...) } if len(groupDetails.ClientRoles) > 0 { for _, clientRoles := range groupDetails.ClientRoles { groupRoles = append(groupRoles, clientRoles...) } } if len(groupRoles) > 0 { g.Resources = append(g.Resources, g.createGroupRolesResource(group.RealmId, group.Id, group.Name, groupRoles)) } } return nil } func (g *RealmGenerator) PostConvertHook() error { mapRealmIDs := map[string]string{} mapUserFederationIDs := map[string]string{} mapGroupIDs := map[string]string{} mapClientIDs := map[string]string{} mapClientNames := map[string]string{} mapClientClientIDs := map[string]string{} mapClientClientNames := map[string]string{} mapServiceAccountUserIDs := map[string]string{} mapRoleIDs := map[string]string{} mapClientRoleNames := map[string]string{} mapClientRoleShortNames := map[string]string{} mapScopeNames := map[string]string{} mapUserNames := map[string]string{} mapGroupNames := map[string]string{} mapAuthenticationFlowAliases := map[string]string{} mapAuthenticationExecutionIDs := map[string]string{} // Set slices to be able to map IDs with Terraform variables for _, r := range g.Resources { if r.InstanceInfo.Type != "keycloak_realm" && r.InstanceInfo.Type != "keycloak_ldap_user_federation" && r.InstanceInfo.Type != "keycloak_group" && r.InstanceInfo.Type != "keycloak_openid_client" && r.InstanceInfo.Type != "keycloak_role" && r.InstanceInfo.Type != "keycloak_openid_client_scope" && r.InstanceInfo.Type != "keycloak_user" && r.InstanceInfo.Type != "keycloak_authentication_flow" && r.InstanceInfo.Type != "keycloak_authentication_subflow" && r.InstanceInfo.Type != "keycloak_authentication_execution" { continue } if r.InstanceInfo.Type == "keycloak_realm" { mapRealmIDs[r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".id}" } if r.InstanceInfo.Type == "keycloak_ldap_user_federation" { mapUserFederationIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".id}" } if r.InstanceInfo.Type == "keycloak_group" { mapGroupIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".id}" mapGroupNames[r.Item["realm_id"].(string)+"_"+r.Item["name"].(string)] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".name}" } if r.InstanceInfo.Type == "keycloak_openid_client" { mapClientIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".id}" mapClientNames[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = r.Item["client_id"].(string) mapClientClientNames[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".client_id}" mapClientClientIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.Attributes["client_id"]] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".client_id}" if _, exist := r.InstanceState.Attributes["service_account_user_id"]; exist { mapServiceAccountUserIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.Attributes["service_account_user_id"]] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".service_account_user_id}" } } if r.InstanceInfo.Type == "keycloak_openid_client_scope" { mapScopeNames[r.Item["realm_id"].(string)+"_"+r.Item["name"].(string)] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".name}" } if r.InstanceInfo.Type == "keycloak_user" { mapUserNames[r.Item["realm_id"].(string)+"_"+r.Item["username"].(string)] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".username}" } if r.InstanceInfo.Type == "keycloak_authentication_flow" || r.InstanceInfo.Type == "keycloak_authentication_subflow" { mapAuthenticationFlowAliases[r.Item["realm_id"].(string)+"_"+r.Item["alias"].(string)] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".alias}" } if r.InstanceInfo.Type == "keycloak_authentication_execution" { mapAuthenticationExecutionIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".id}" } } // Set slices to be able to map IDs with Terraform variables // Separate loop for roles to avoid fetching with a key that is not present in the map for _, r := range g.Resources { if r.InstanceInfo.Type == "keycloak_role" { mapRoleIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".id}" if _, exist := r.Item["client_id"]; exist { mapClientRoleNames[r.Item["realm_id"].(string)+"_"+mapClientNames[r.Item["realm_id"].(string)+"_"+r.Item["client_id"].(string)]+"."+r.Item["name"].(string)] = mapClientClientNames[r.Item["realm_id"].(string)+"_"+r.Item["client_id"].(string)] + ".${" + r.InstanceInfo.Type + "." + r.ResourceName + ".name}" mapClientRoleShortNames[r.Item["realm_id"].(string)+"_"+mapClientNames[r.Item["realm_id"].(string)+"_"+r.Item["client_id"].(string)]+"."+r.Item["name"].(string)] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".name}" } else { mapClientRoleNames[r.Item["realm_id"].(string)+"_"+r.Item["name"].(string)] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".name}" } } } // For each resource, modify import if needed... for i, r := range g.Resources { // Escape keycloak text inputs not to get unpredictable results or errors when Terraform will try to interpret variables ($ vs $$) // TODO: ensure that we escape all existing fields if strings.Contains(r.InstanceState.Attributes["consent_screen_text"], "$") { g.Resources[i].Item["consent_screen_text"] = strings.ReplaceAll(r.InstanceState.Attributes["consent_screen_text"], "$", "$$") } if strings.Contains(r.InstanceState.Attributes["name"], "$") { g.Resources[i].Item["name"] = strings.ReplaceAll(r.InstanceState.Attributes["name"], "$", "$$") } if strings.Contains(r.InstanceState.Attributes["description"], "$") { g.Resources[i].Item["description"] = strings.ReplaceAll(r.InstanceState.Attributes["description"], "$", "$$") } if strings.Contains(r.InstanceState.Attributes["root_url"], "$") { g.Resources[i].Item["root_url"] = strings.ReplaceAll(r.InstanceState.Attributes["root_url"], "$", "$$") } // Sort supported_locales to get reproducible results for keycloak_realm resources if r.InstanceInfo.Type == "keycloak_realm" { if _, exist := r.Item["internationalization"]; exist { for _, v := range r.Item["internationalization"].([]interface{}) { sortedSupportedLocales := make([]string, len(v.(map[string]interface{})["supported_locales"].([]interface{}))) for k, vv := range v.(map[string]interface{})["supported_locales"].([]interface{}) { sortedSupportedLocales[k] = vv.(string) } sort.Strings(sortedSupportedLocales) v.(map[string]interface{})["supported_locales"] = sortedSupportedLocales } } } // Sort group_ids to get reproducible results for keycloak_default_groups resources // Set an empty string slice if the attribute doesn't exist as it is mandatory if r.InstanceInfo.Type == "keycloak_default_groups" { if _, exist := r.Item["group_ids"]; exist { renamedGroupIDs := make([]string, len(r.Item["group_ids"].([]interface{}))) for k, v := range r.Item["group_ids"].([]interface{}) { renamedGroupIDs[k] = mapGroupIDs[r.Item["realm_id"].(string)+"_"+v.(string)] } sort.Strings(renamedGroupIDs) g.Resources[i].Item["group_ids"] = renamedGroupIDs } else { g.Resources[i].Item["group_ids"] = []string{} } } // Sort valid_redirect_uris and web_origins to get reproducible results for keycloak_openid_client resources if r.InstanceInfo.Type == "keycloak_openid_client" { if _, exist := r.Item["valid_redirect_uris"]; exist { sortedValidRedirectUris := make([]string, len(r.Item["valid_redirect_uris"].([]interface{}))) for k, v := range r.Item["valid_redirect_uris"].([]interface{}) { sortedValidRedirectUris[k] = v.(string) } sort.Strings(sortedValidRedirectUris) g.Resources[i].Item["valid_redirect_uris"] = sortedValidRedirectUris } if _, exist := r.Item["web_origins"]; exist { sortedWebOrigins := make([]string, len(r.Item["web_origins"].([]interface{}))) for k, v := range r.Item["web_origins"].([]interface{}) { sortedWebOrigins[k] = v.(string) } sort.Strings(sortedWebOrigins) g.Resources[i].Item["web_origins"] = sortedWebOrigins } } // Sort composite_roles to get reproducible results for keycloak_role resources if _, exist := r.Item["composite_roles"]; exist && r.InstanceInfo.Type == "keycloak_role" { renamedCompositeRoles := make([]string, len(r.Item["composite_roles"].([]interface{}))) for k, v := range r.Item["composite_roles"].([]interface{}) { renamedCompositeRoles[k] = mapRoleIDs[r.Item["realm_id"].(string)+"_"+v.(string)] } sort.Strings(renamedCompositeRoles) g.Resources[i].Item["composite_roles"] = renamedCompositeRoles } // Sort default_scopes to get reproducible results for keycloak_openid_client_default_scopes resources if _, exist := r.Item["default_scopes"]; exist && r.InstanceInfo.Type == "keycloak_openid_client_default_scopes" { renamedScopes := make([]string, len(r.Item["default_scopes"].([]interface{}))) for k, v := range r.Item["default_scopes"].([]interface{}) { renamedScopes[k] = mapScopeNames[r.Item["realm_id"].(string)+"_"+v.(string)] } sort.Strings(renamedScopes) g.Resources[i].Item["default_scopes"] = renamedScopes } // Sort optional_scopes to get reproducible results for keycloak_openid_client_optional_scopes resources if _, exist := r.Item["optional_scopes"]; exist && r.InstanceInfo.Type == "keycloak_openid_client_optional_scopes" { renamedScopes := make([]string, len(r.Item["optional_scopes"].([]interface{}))) for k, v := range r.Item["optional_scopes"].([]interface{}) { renamedScopes[k] = mapScopeNames[r.Item["realm_id"].(string)+"_"+v.(string)] } sort.Strings(renamedScopes) g.Resources[i].Item["optional_scopes"] = renamedScopes } // Sort role_ids to get reproducible results for keycloak_group_roles resources if r.InstanceInfo.Type == "keycloak_group_roles" { if roleIDs, ok := r.Item["role_ids"].([]interface{}); ok { sortedRoles := make([]string, len(roleIDs)) for k, v := range roleIDs { sortedRoles[k] = mapRoleIDs[r.Item["realm_id"].(string)+"_"+v.(string)] } sort.Strings(sortedRoles) g.Resources[i].Item["role_ids"] = sortedRoles } else { g.Resources[i].Item["role_ids"] = []string{} } } // Sort members to get reproducible results for keycloak_group_memberships resources // Map members to keycloak_user.foo.username Terraform variables if r.InstanceInfo.Type == "keycloak_group_memberships" { sortedMembers := make([]string, len(r.Item["members"].([]interface{}))) for k, v := range r.Item["members"].([]interface{}) { if mapUserNames[r.Item["realm_id"].(string)+"_"+v.(string)] != "" { sortedMembers[k] = mapUserNames[r.Item["realm_id"].(string)+"_"+v.(string)] } else { sortedMembers[k] = v.(string) } } sort.Strings(sortedMembers) g.Resources[i].Item["members"] = sortedMembers } // Map ldap_user_federation_id attributes to keycloak_ldap_user_federation.foo.id Terraform variables for ldap mappers resources if r.InstanceInfo.Type == "keycloak_ldap_full_name_mapper" || r.InstanceInfo.Type == "keycloak_ldap_group_mapper" || r.InstanceInfo.Type == "keycloak_ldap_role_mapper" || r.InstanceInfo.Type == "keycloak_ldap_hardcoded_group_mapper" || r.InstanceInfo.Type == "keycloak_ldap_hardcoded_role_mapper" || r.InstanceInfo.Type == "keycloak_ldap_msad_lds_user_account_control_mapper" || r.InstanceInfo.Type == "keycloak_ldap_msad_user_account_control_mapper" || r.InstanceInfo.Type == "keycloak_ldap_user_attribute_mapper" { g.Resources[i].Item["ldap_user_federation_id"] = mapUserFederationIDs[r.Item["realm_id"].(string)+"_"+g.Resources[i].Item["ldap_user_federation_id"].(string)] } // Map group to keycloak_group.foo.name Terraform variables for ldap hardcoded group mapper resources if r.InstanceInfo.Type == "keycloak_ldap_hardcoded_group_mapper" { g.Resources[i].Item["group"] = mapGroupNames[r.Item["realm_id"].(string)+"_"+r.Item["group"].(string)] } // Map role to Terraform variables for ldap hardcoded role mapper resources if r.InstanceInfo.Type == "keycloak_ldap_hardcoded_role_mapper" { g.Resources[i].Item["role"] = mapClientRoleNames[r.Item["realm_id"].(string)+"_"+r.Item["role"].(string)] } // Map parent_id to keycloak_group.foo.id Terraform variables for keycloak_group resources if _, exist := r.Item["parent_id"]; exist && r.InstanceInfo.Type == "keycloak_group" { g.Resources[i].Item["parent_id"] = mapGroupIDs[r.Item["realm_id"].(string)+"_"+r.Item["parent_id"].(string)] } // Map group_id to keycloak_group.foo.id Terraform variables for keycloak_group_memberships and keycloak_group_roles resources if r.InstanceInfo.Type == "keycloak_group_memberships" || r.InstanceInfo.Type == "keycloak_group_roles" { g.Resources[i].Item["group_id"] = mapGroupIDs[r.Item["realm_id"].(string)+"_"+r.Item["group_id"].(string)] } // Map service_account_user_id to keycloak_openid_client.foo.service_account_user_id Terraform variables for service account role resources if r.InstanceInfo.Type == "keycloak_openid_client_service_account_role" { g.Resources[i].Item["service_account_user_id"] = mapServiceAccountUserIDs[r.Item["realm_id"].(string)+"_"+r.Item["service_account_user_id"].(string)] g.Resources[i].Item["role"] = mapClientRoleShortNames[r.Item["realm_id"].(string)+"_"+mapClientNames[r.Item["realm_id"].(string)+"_"+r.Item["client_id"].(string)]+"."+r.Item["role"].(string)] } // Map client_id attributes to keycloak_openid_client.foo.id Terraform variables for open id mappers resources if _, exist := r.Item["client_id"]; exist && (r.InstanceInfo.Type == "keycloak_openid_client_service_account_role" || r.InstanceInfo.Type == "keycloak_openid_audience_protocol_mapper" || r.InstanceInfo.Type == "keycloak_openid_audience_resolve_protocol_mapper" || r.InstanceInfo.Type == "keycloak_openid_full_name_protocol_mapper" || r.InstanceInfo.Type == "keycloak_openid_group_membership_protocol_mapper" || r.InstanceInfo.Type == "keycloak_openid_hardcoded_claim_protocol_mapper" || r.InstanceInfo.Type == "keycloak_openid_hardcoded_role_protocol_mapper" || r.InstanceInfo.Type == "keycloak_openid_script_protocol_mapper" || r.InstanceInfo.Type == "keycloak_openid_user_attribute_protocol_mapper" || r.InstanceInfo.Type == "keycloak_openid_user_client_role_protocol_mapper" || r.InstanceInfo.Type == "keycloak_openid_user_property_protocol_mapper" || r.InstanceInfo.Type == "keycloak_openid_user_realm_role_protocol_mapper" || r.InstanceInfo.Type == "keycloak_openid_user_session_note_protocol_mapper" || r.InstanceInfo.Type == "keycloak_openid_client_default_scopes" || r.InstanceInfo.Type == "keycloak_openid_client_optional_scopes" || r.InstanceInfo.Type == "keycloak_role") { g.Resources[i].Item["client_id"] = mapClientIDs[r.Item["realm_id"].(string)+"_"+r.Item["client_id"].(string)] } // Map included_client_audience to keycloak_openid_client.foo.client_id Terraform variables for open id audience mapper resources if _, exist := r.Item["included_client_audience"]; exist && r.InstanceInfo.Type == "keycloak_openid_audience_protocol_mapper" { g.Resources[i].Item["included_client_audience"] = mapClientClientIDs[r.Item["realm_id"].(string)+"_"+r.Item["included_client_audience"].(string)] } // Map parent_flow_alias attributes to keycloak_authentication_(sub)flow.foo.alias Terraform variables for authentication subflow and execution resources if r.InstanceInfo.Type == "keycloak_authentication_subflow" || r.InstanceInfo.Type == "keycloak_authentication_execution" { g.Resources[i].Item["parent_flow_alias"] = mapAuthenticationFlowAliases[r.Item["realm_id"].(string)+"_"+r.Item["parent_flow_alias"].(string)] } // Map execution_id attributes to keycloak_authentication_execution_config.foo.execution_id Terraform variables for authentication execution config resources if r.InstanceInfo.Type == "keycloak_authentication_execution_config" { g.Resources[i].Item["execution_id"] = mapAuthenticationExecutionIDs[r.Item["realm_id"].(string)+"_"+r.Item["execution_id"].(string)] } // Map realm_id attributes to keycloak_realm.foo.id Terraform variables for all the resources (almost all resources have this attribute) if _, exist := r.Item["realm_id"]; exist { g.Resources[i].Item["realm_id"] = mapRealmIDs[r.Item["realm_id"].(string)] } } return nil }