github.com/GoogleCloudPlatform/terraformer@v0.8.18/providers/keycloak/generator.go (about) 1 // Copyright 2018 The Terraformer Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package keycloak 16 17 import ( 18 "errors" 19 "fmt" 20 "sort" 21 "strings" 22 23 "github.com/GoogleCloudPlatform/terraformer/terraformutils" 24 "github.com/mrparkers/terraform-provider-keycloak/keycloak" 25 ) 26 27 type RealmGenerator struct { 28 KeycloakService 29 } 30 31 func (g *RealmGenerator) InitResources() error { 32 var realms []*keycloak.Realm 33 var realmsGroups []*keycloak.Group 34 35 // Connect to keycloak instance 36 kck, err := keycloak.NewKeycloakClient(g.GetArgs()["url"].(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)) 37 if err != nil { 38 return errors.New("keycloak: could not connect to Keycloak") 39 } 40 41 // Get realm resources 42 target := g.GetArgs()["target"].(string) 43 if target == "" { 44 realms, err = kck.GetRealms() 45 if err != nil { 46 return errors.New("keycloak: could not get realms attributes in Keycloak") 47 } 48 } else { 49 realm, err := kck.GetRealm(target) 50 if err != nil { 51 return errors.New("keycloak: could not get " + target + " realm attributes in Keycloak") 52 } 53 realms = append(realms, realm) 54 } 55 g.Resources = append(g.Resources, g.createRealmResources(realms)...) 56 57 // For each realm, get resources 58 for _, realm := range realms { 59 // Get required actions resources 60 requiredActions, err := kck.GetRequiredActions(realm.Id) 61 if err != nil { 62 return fmt.Errorf("keycloak: could not get required actions of realm %s in Keycloak. err: %w", realm.Id, err) 63 } 64 g.Resources = append(g.Resources, g.createRequiredActionResources(requiredActions)...) 65 66 // Get top-level authentication flows resources 67 authenticationFlows, err := kck.ListAuthenticationFlows(realm.Id) 68 if err != nil { 69 return fmt.Errorf("keycloak: could not get authentication flows of realm %s in Keycloak. err: %w", realm.Id, err) 70 } 71 g.Resources = append(g.Resources, g.createAuthenticationFlowResources(authenticationFlows)...) 72 73 // For each authentication flow, get subFlow, execution and execution config resources 74 for _, topLevelAuthenticationFlow := range authenticationFlows { 75 authenticationSubFlowOrExecutions, err := kck.ListAuthenticationExecutions(realm.Id, topLevelAuthenticationFlow.Alias) 76 if err != nil { 77 return fmt.Errorf("keycloak: could not get authentication executions of authentication flow %s of realm %s in Keycloak. err: %w", 78 topLevelAuthenticationFlow.Alias, realm.Id, err) 79 } 80 81 var stack []*keycloak.AuthenticationExecutionInfo 82 parentFlowAlias := topLevelAuthenticationFlow.Alias 83 84 for _, authenticationSubFlowOrExecution := range authenticationSubFlowOrExecutions { 85 86 // Find the parent flow alias 87 if len(stack) > 0 { 88 previous := stack[len(stack)-1] 89 if authenticationSubFlowOrExecution.Level < previous.Level { 90 // Find the last sub flow/execution for the current level 91 stack = stack[:authenticationSubFlowOrExecution.Level+1] 92 previous = stack[len(stack)-1] 93 } 94 if authenticationSubFlowOrExecution.Level == previous.Level { 95 // Same level sub flow/execution, it means that the sub flow/execution has same parent flow of the last sub flow/execution 96 parentFlowAlias = previous.ParentFlowAlias 97 98 } else if authenticationSubFlowOrExecution.Level > previous.Level { 99 // Deep level sub flow/execution, it means that the parent flow is the last sub flow/execution 100 if previous.AuthenticationFlow { 101 parentFlowAlias = previous.Alias 102 } else { 103 return errors.New("keycloak: invalid parent sub flow, it should be a sub flow but it's an execution") 104 } 105 } 106 } 107 108 var resource terraformutils.Resource 109 110 switch authenticationSubFlowOrExecution.AuthenticationFlow { 111 case true: 112 authenticationSubFlow, err := kck.GetAuthenticationSubFlow(realm.Id, parentFlowAlias, authenticationSubFlowOrExecution.FlowId) 113 if err != nil { 114 return fmt.Errorf("keycloak: could not get authentication subflow %s of realm %s in Keycloak. err: %w", 115 authenticationSubFlowOrExecution.FlowId, realm.Id, err) 116 } 117 118 // Need to store the alias and parent flow alias 119 authenticationSubFlowOrExecution.Alias = authenticationSubFlow.Alias 120 authenticationSubFlowOrExecution.ParentFlowAlias = parentFlowAlias 121 122 resource = g.createAuthenticationSubFlowResource(authenticationSubFlow) 123 g.Resources = append(g.Resources, resource) 124 125 case false: 126 authenticationExecution, err := kck.GetAuthenticationExecution(realm.Id, parentFlowAlias, authenticationSubFlowOrExecution.Id) 127 if err != nil { 128 return fmt.Errorf("keycloak: could not get authentication execution %s of realm %s in Keycloak. err: %w", 129 authenticationSubFlowOrExecution.Id, realm.Id, err) 130 } 131 132 // Need to store the parent flow alias 133 authenticationSubFlowOrExecution.ParentFlowAlias = parentFlowAlias 134 135 resource = g.createAuthenticationExecutionResource(authenticationExecution) 136 g.Resources = append(g.Resources, resource) 137 138 if authenticationSubFlowOrExecution.AuthenticationConfig != "" { 139 authenticationExecutionConfig := &keycloak.AuthenticationExecutionConfig{ 140 RealmId: realm.Id, 141 Id: authenticationSubFlowOrExecution.AuthenticationConfig, 142 ExecutionId: authenticationSubFlowOrExecution.Id, 143 } 144 err := kck.GetAuthenticationExecutionConfig(authenticationExecutionConfig) 145 if err != nil { 146 return fmt.Errorf("keycloak: could not get authentication execution config %s of realm %s in Keycloak. err: %w", 147 authenticationExecutionConfig.Id, realm.Id, err) 148 } 149 150 g.Resources = append(g.Resources, g.createAuthenticationExecutionConfigResource(authenticationExecutionConfig)) 151 } 152 } 153 154 if len(stack) > 0 && authenticationSubFlowOrExecution.Index > 0 { 155 previous := stack[len(stack)-1] 156 var resouceType string 157 var resouceName string 158 if previous.AuthenticationFlow { 159 resouceType = "keycloak_authentication_subflow" 160 resouceName = "authentication_subflow_" + 161 normalizeResourceName(realm.Id) + "_" + normalizeResourceName(previous.FlowId) 162 } else { 163 resouceType = "keycloak_authentication_execution" 164 resouceName = "authentication_execution_" + 165 normalizeResourceName(realm.Id) + "_" + normalizeResourceName(previous.Id) 166 } 167 resource.AdditionalFields["depends_on"] = []string{resouceType + "." + terraformutils.TfSanitize(resouceName)} 168 } 169 170 // Stack the current sub flow/execution 171 if len(stack) > 0 && stack[len(stack)-1].Level == authenticationSubFlowOrExecution.Level { 172 // Replace it if it's same level 173 stack[len(stack)-1] = authenticationSubFlowOrExecution 174 } else { 175 stack = append(stack, authenticationSubFlowOrExecution) 176 } 177 } 178 } 179 180 // Get custom federations resources 181 // TODO: support kerberos user federation 182 customUserFederations, err := kck.GetCustomUserFederations(realm.Id) 183 if err != nil { 184 return errors.New("keycloak: could not get custom user federations of realm " + realm.Id + " in Keycloak") 185 } 186 g.Resources = append(g.Resources, g.createCustomUserFederationResources(customUserFederations)...) 187 188 // For each custom federation, get mappers resources 189 for _, customUserFederation := range *customUserFederations { 190 if customUserFederation.ProviderId == "ldap" { 191 mappers, err := kck.GetLdapUserFederationMappers(realm.Id, customUserFederation.Id) 192 if err != nil { 193 return errors.New("keycloak: could not get mappers of ldap user federation " + customUserFederation.Name + " of realm " + realm.Id + " in Keycloak") 194 } 195 g.Resources = append(g.Resources, g.createLdapMapperResources(realm.Id, customUserFederation.Name, mappers)...) 196 } 197 } 198 199 // Get groups tree and default groups resources 200 realmGroups, err := kck.GetGroups(realm.Id) 201 if err != nil { 202 return errors.New("keycloak: could not get groups of realm " + realm.Id + " in Keycloak") 203 } 204 realmsGroups = append(realmsGroups, realmGroups...) 205 g.Resources = append(g.Resources, g.createDefaultGroupResource(realm.Id)) 206 207 // Get users resources 208 realmUsers, err := kck.GetUsers(realm.Id) 209 if err != nil { 210 return errors.New("keycloak: could not get users of realm " + realm.Id + " in Keycloak") 211 } 212 g.Resources = append(g.Resources, g.createUserResources(realmUsers)...) 213 214 // Get realm open id client scopes resources 215 realmScopes, err := kck.ListOpenidClientScopesWithFilter(realm.Id, func(scope *keycloak.OpenidClientScope) bool { return true }) 216 if err != nil { 217 return errors.New("keycloak: could not get realm scopes of realm " + realm.Id + " in Keycloak") 218 } 219 g.Resources = append(g.Resources, g.createScopeResources(realm.Id, realmScopes)...) 220 221 // Get open id clients 222 realmClients, err := kck.GetOpenidClients(realm.Id, true) 223 if err != nil { 224 return errors.New("keycloak: could not get open id clients of realm " + realm.Id + " in Keycloak") 225 } 226 g.Resources = append(g.Resources, g.createOpenIDClientResources(realmClients)...) 227 228 // For earch open id client, get resources 229 mapServiceAccountIds := map[string]map[string]string{} 230 mapContainerIDs := map[string]string{} 231 mapClientIDs := map[string]string{} 232 for _, client := range realmClients { 233 mapClientIDs[client.Id] = client.ClientId 234 mapContainerIDs[client.Id] = "_" + client.ClientId 235 236 // Get open id client protocol mappers resources 237 clientMappers, err := kck.GetGenericClientProtocolMappers(realm.Id, client.Id) 238 if err != nil { 239 return errors.New("keycloak: could not get protocol mappers of open id client " + client.ClientId + " of realm " + realm.Id + " in Keycloak") 240 } 241 g.Resources = append(g.Resources, g.createOpenIDProtocolMapperResources(client.ClientId, clientMappers)...) 242 243 // Get open id client default scopes resources 244 clientScopes, err := kck.GetOpenidDefaultClientScopes(realm.Id, client.Id) 245 if err != nil { 246 return errors.New("keycloak: could not get default client scopes of open id client " + client.ClientId + " of realm " + realm.Id + " in Keycloak") 247 } 248 if len(*clientScopes) > 0 { 249 g.Resources = append(g.Resources, g.createOpenidClientScopesResources(realm.Id, client.Id, client.ClientId, "default", clientScopes)) 250 } 251 252 // Get open id client optional scopes resources 253 clientScopes, err = kck.GetOpenidOptionalClientScopes(realm.Id, client.Id) 254 if err != nil { 255 return errors.New("keycloak: could not get optional client scopes of open id client " + client.ClientId + " of realm " + realm.Id + " in Keycloak") 256 } 257 if len(*clientScopes) > 0 { 258 g.Resources = append(g.Resources, g.createOpenidClientScopesResources(realm.Id, client.Id, client.ClientId, "optional", clientScopes)) 259 } 260 261 // 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 262 if !client.ServiceAccountsEnabled { 263 continue 264 } 265 serviceAccountUser, err := kck.GetOpenidClientServiceAccountUserId(realm.Id, client.Id) 266 if err != nil { 267 return errors.New("keycloak: could not get service account user associated to open id client " + client.ClientId + " of realm " + realm.Id + " in Keycloak") 268 } 269 mapServiceAccountIds[serviceAccountUser.Id] = map[string]string{} 270 mapServiceAccountIds[serviceAccountUser.Id]["Id"] = client.Id 271 mapServiceAccountIds[serviceAccountUser.Id]["ClientId"] = client.ClientId 272 } 273 274 // Get open id client roles 275 clientRoles, err := kck.GetClientRoles(realm.Id, realmClients) 276 if err != nil { 277 return errors.New("keycloak: could not get open id clients roles of realm " + realm.Id + " in Keycloak") 278 } 279 280 // Get roles 281 realmRoles, err := kck.GetRealmRoles(realm.Id) 282 if err != nil { 283 return errors.New("keycloak: could not get realm roles of realm " + realm.Id + " in Keycloak") 284 } 285 286 // Set ContainerId of the roles, for realm = "", for open id clients = "_" + client.ClientId 287 // and get roles resources 288 mapContainerIDs[realm.Id] = "" 289 roles := append(clientRoles, realmRoles...) 290 for _, role := range roles { 291 role.ContainerId = mapContainerIDs[role.ContainerId] 292 } 293 g.Resources = append(g.Resources, g.createRoleResources(roles)...) 294 295 // Get service account roles resources 296 usersInRole, err := kck.GetClientRoleUsers(realm.Id, clientRoles) 297 if err != nil { 298 return errors.New("keycloak: could not get users roles of realm " + realm.Id + " in Keycloak") 299 } 300 g.Resources = append(g.Resources, g.createServiceAccountClientRolesResources(realm.Id, clientRoles, *usersInRole, mapServiceAccountIds, mapClientIDs)...) 301 } 302 303 // Parse the groups trees, and get all the groups 304 // Get groups resources 305 groups := g.flattenGroups(realmsGroups, "") 306 g.Resources = append(g.Resources, g.createGroupResources(groups)...) 307 308 // For each group, get group memberships and roles resources 309 for _, group := range groups { 310 // Get group members resources 311 members, err := kck.GetGroupMembers(group.RealmId, group.Id) 312 if err != nil { 313 return errors.New("keycloak: could not get group members of group " + group.Name + " in Keycloak") 314 } 315 if len(members) > 0 { 316 groupMembers := make([]string, len(members)) 317 for k, member := range members { 318 groupMembers[k] = member.Username 319 } 320 g.Resources = append(g.Resources, g.createGroupMembershipsResource(group.RealmId, group.Id, group.Name, groupMembers)) 321 } 322 323 // Get group roles resources 324 // For realm roles and open id clients roles 325 groupDetails, err := kck.GetGroup(group.RealmId, group.Id) 326 if err != nil { 327 return errors.New("keycloak: could not get details about group " + group.Name + " in Keycloak") 328 } 329 groupRoles := []string{} 330 if len(groupDetails.RealmRoles) > 0 { 331 groupRoles = append(groupRoles, groupDetails.RealmRoles...) 332 } 333 if len(groupDetails.ClientRoles) > 0 { 334 for _, clientRoles := range groupDetails.ClientRoles { 335 groupRoles = append(groupRoles, clientRoles...) 336 } 337 } 338 if len(groupRoles) > 0 { 339 g.Resources = append(g.Resources, g.createGroupRolesResource(group.RealmId, group.Id, group.Name, groupRoles)) 340 } 341 } 342 343 return nil 344 } 345 346 func (g *RealmGenerator) PostConvertHook() error { 347 mapRealmIDs := map[string]string{} 348 mapUserFederationIDs := map[string]string{} 349 mapGroupIDs := map[string]string{} 350 mapClientIDs := map[string]string{} 351 mapClientNames := map[string]string{} 352 mapClientClientIDs := map[string]string{} 353 mapClientClientNames := map[string]string{} 354 mapServiceAccountUserIDs := map[string]string{} 355 mapRoleIDs := map[string]string{} 356 mapClientRoleNames := map[string]string{} 357 mapClientRoleShortNames := map[string]string{} 358 mapScopeNames := map[string]string{} 359 mapUserNames := map[string]string{} 360 mapGroupNames := map[string]string{} 361 mapAuthenticationFlowAliases := map[string]string{} 362 mapAuthenticationExecutionIDs := map[string]string{} 363 364 // Set slices to be able to map IDs with Terraform variables 365 for _, r := range g.Resources { 366 if r.InstanceInfo.Type != "keycloak_realm" && 367 r.InstanceInfo.Type != "keycloak_ldap_user_federation" && 368 r.InstanceInfo.Type != "keycloak_group" && 369 r.InstanceInfo.Type != "keycloak_openid_client" && 370 r.InstanceInfo.Type != "keycloak_role" && 371 r.InstanceInfo.Type != "keycloak_openid_client_scope" && 372 r.InstanceInfo.Type != "keycloak_user" && 373 r.InstanceInfo.Type != "keycloak_authentication_flow" && 374 r.InstanceInfo.Type != "keycloak_authentication_subflow" && 375 r.InstanceInfo.Type != "keycloak_authentication_execution" { 376 continue 377 } 378 if r.InstanceInfo.Type == "keycloak_realm" { 379 mapRealmIDs[r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".id}" 380 } 381 if r.InstanceInfo.Type == "keycloak_ldap_user_federation" { 382 mapUserFederationIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".id}" 383 } 384 if r.InstanceInfo.Type == "keycloak_group" { 385 mapGroupIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".id}" 386 mapGroupNames[r.Item["realm_id"].(string)+"_"+r.Item["name"].(string)] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".name}" 387 } 388 if r.InstanceInfo.Type == "keycloak_openid_client" { 389 mapClientIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".id}" 390 mapClientNames[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = r.Item["client_id"].(string) 391 mapClientClientNames[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".client_id}" 392 mapClientClientIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.Attributes["client_id"]] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".client_id}" 393 if _, exist := r.InstanceState.Attributes["service_account_user_id"]; exist { 394 mapServiceAccountUserIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.Attributes["service_account_user_id"]] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".service_account_user_id}" 395 } 396 } 397 if r.InstanceInfo.Type == "keycloak_role" { 398 mapRoleIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".id}" 399 if _, exist := r.Item["client_id"]; exist { 400 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}" 401 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}" 402 } else { 403 mapClientRoleNames[r.Item["realm_id"].(string)+"_"+r.Item["name"].(string)] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".name}" 404 } 405 } 406 if r.InstanceInfo.Type == "keycloak_openid_client_scope" { 407 mapScopeNames[r.Item["realm_id"].(string)+"_"+r.Item["name"].(string)] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".name}" 408 } 409 if r.InstanceInfo.Type == "keycloak_user" { 410 mapUserNames[r.Item["realm_id"].(string)+"_"+r.Item["username"].(string)] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".username}" 411 } 412 if r.InstanceInfo.Type == "keycloak_authentication_flow" || r.InstanceInfo.Type == "keycloak_authentication_subflow" { 413 mapAuthenticationFlowAliases[r.Item["realm_id"].(string)+"_"+r.Item["alias"].(string)] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".alias}" 414 } 415 if r.InstanceInfo.Type == "keycloak_authentication_execution" { 416 mapAuthenticationExecutionIDs[r.Item["realm_id"].(string)+"_"+r.InstanceState.ID] = "${" + r.InstanceInfo.Type + "." + r.ResourceName + ".id}" 417 } 418 } 419 420 // For each resources, modify import if needed... 421 for i, r := range g.Resources { 422 // Escape keycloak text inputs not to get unpredictable results or errors when Terraform will try to interpret variables ($ vs $$) 423 // TODO: ensure that we escape all existing fields 424 if strings.Contains(r.InstanceState.Attributes["consent_screen_text"], "$") { 425 g.Resources[i].Item["consent_screen_text"] = strings.ReplaceAll(r.InstanceState.Attributes["consent_screen_text"], "$", "$$") 426 } 427 if strings.Contains(r.InstanceState.Attributes["name"], "$") { 428 g.Resources[i].Item["name"] = strings.ReplaceAll(r.InstanceState.Attributes["name"], "$", "$$") 429 } 430 if strings.Contains(r.InstanceState.Attributes["description"], "$") { 431 g.Resources[i].Item["description"] = strings.ReplaceAll(r.InstanceState.Attributes["description"], "$", "$$") 432 } 433 if strings.Contains(r.InstanceState.Attributes["root_url"], "$") { 434 g.Resources[i].Item["root_url"] = strings.ReplaceAll(r.InstanceState.Attributes["root_url"], "$", "$$") 435 } 436 437 // Sort supported_locales to get reproducible results for keycloak_realm resources 438 if r.InstanceInfo.Type == "keycloak_realm" { 439 if _, exist := r.Item["internationalization"]; exist { 440 for _, v := range r.Item["internationalization"].([]interface{}) { 441 sortedSupportedLocales := make([]string, len(v.(map[string]interface{})["supported_locales"].([]interface{}))) 442 for k, vv := range v.(map[string]interface{})["supported_locales"].([]interface{}) { 443 sortedSupportedLocales[k] = vv.(string) 444 } 445 sort.Strings(sortedSupportedLocales) 446 v.(map[string]interface{})["supported_locales"] = sortedSupportedLocales 447 } 448 } 449 } 450 451 // Sort group_ids to get reproducible results for keycloak_default_groups resources 452 // Set an empty string slice if the attribute doesn't exist as it is mandatory 453 if r.InstanceInfo.Type == "keycloak_default_groups" { 454 if _, exist := r.Item["group_ids"]; exist { 455 renamedGroupIDs := make([]string, len(r.Item["group_ids"].([]interface{}))) 456 for k, v := range r.Item["group_ids"].([]interface{}) { 457 renamedGroupIDs[k] = mapGroupIDs[r.Item["realm_id"].(string)+"_"+v.(string)] 458 } 459 sort.Strings(renamedGroupIDs) 460 g.Resources[i].Item["group_ids"] = renamedGroupIDs 461 } else { 462 g.Resources[i].Item["group_ids"] = []string{} 463 } 464 } 465 466 // Sort valid_redirect_uris and web_origins to get reproducible results for keycloak_openid_client resources 467 if r.InstanceInfo.Type == "keycloak_openid_client" { 468 if _, exist := r.Item["valid_redirect_uris"]; exist { 469 sortedValidRedirectUris := make([]string, len(r.Item["valid_redirect_uris"].([]interface{}))) 470 for k, v := range r.Item["valid_redirect_uris"].([]interface{}) { 471 sortedValidRedirectUris[k] = v.(string) 472 } 473 sort.Strings(sortedValidRedirectUris) 474 g.Resources[i].Item["valid_redirect_uris"] = sortedValidRedirectUris 475 } 476 477 if _, exist := r.Item["web_origins"]; exist { 478 sortedWebOrigins := make([]string, len(r.Item["web_origins"].([]interface{}))) 479 for k, v := range r.Item["web_origins"].([]interface{}) { 480 sortedWebOrigins[k] = v.(string) 481 } 482 sort.Strings(sortedWebOrigins) 483 g.Resources[i].Item["web_origins"] = sortedWebOrigins 484 } 485 } 486 487 // Sort composite_roles to get reproducible results for keycloak_role resources 488 if _, exist := r.Item["composite_roles"]; exist && r.InstanceInfo.Type == "keycloak_role" { 489 renamedCompositeRoles := make([]string, len(r.Item["composite_roles"].([]interface{}))) 490 for k, v := range r.Item["composite_roles"].([]interface{}) { 491 renamedCompositeRoles[k] = mapRoleIDs[r.Item["realm_id"].(string)+"_"+v.(string)] 492 } 493 sort.Strings(renamedCompositeRoles) 494 g.Resources[i].Item["composite_roles"] = renamedCompositeRoles 495 } 496 497 // Sort default_scopes to get reproducible results for keycloak_openid_client_default_scopes resources 498 if _, exist := r.Item["default_scopes"]; exist && r.InstanceInfo.Type == "keycloak_openid_client_default_scopes" { 499 renamedScopes := make([]string, len(r.Item["default_scopes"].([]interface{}))) 500 for k, v := range r.Item["default_scopes"].([]interface{}) { 501 renamedScopes[k] = mapScopeNames[r.Item["realm_id"].(string)+"_"+v.(string)] 502 } 503 sort.Strings(renamedScopes) 504 g.Resources[i].Item["default_scopes"] = renamedScopes 505 } 506 507 // Sort optional_scopes to get reproducible results for keycloak_openid_client_optional_scopes resources 508 if _, exist := r.Item["optional_scopes"]; exist && r.InstanceInfo.Type == "keycloak_openid_client_optional_scopes" { 509 renamedScopes := make([]string, len(r.Item["optional_scopes"].([]interface{}))) 510 for k, v := range r.Item["optional_scopes"].([]interface{}) { 511 renamedScopes[k] = mapScopeNames[r.Item["realm_id"].(string)+"_"+v.(string)] 512 } 513 sort.Strings(renamedScopes) 514 g.Resources[i].Item["optional_scopes"] = renamedScopes 515 } 516 517 // Sort role_ids to get reproducible results for keycloak_group_roles resources 518 if r.InstanceInfo.Type == "keycloak_group_roles" { 519 sortedRoles := make([]string, len(r.Item["role_ids"].([]interface{}))) 520 for k, v := range r.Item["role_ids"].([]interface{}) { 521 sortedRoles[k] = mapRoleIDs[r.Item["realm_id"].(string)+"_"+v.(string)] 522 } 523 sort.Strings(sortedRoles) 524 g.Resources[i].Item["role_ids"] = sortedRoles 525 } 526 527 // Sort members to get reproducible results for keycloak_group_memberships resources 528 // Map members to keycloak_user.foo.username Terraform variables 529 if r.InstanceInfo.Type == "keycloak_group_memberships" { 530 sortedMembers := make([]string, len(r.Item["members"].([]interface{}))) 531 for k, v := range r.Item["members"].([]interface{}) { 532 if mapUserNames[r.Item["realm_id"].(string)+"_"+v.(string)] != "" { 533 sortedMembers[k] = mapUserNames[r.Item["realm_id"].(string)+"_"+v.(string)] 534 } else { 535 sortedMembers[k] = v.(string) 536 } 537 } 538 sort.Strings(sortedMembers) 539 g.Resources[i].Item["members"] = sortedMembers 540 } 541 542 // Map ldap_user_federation_id attributes to keycloak_ldap_user_federation.foo.id Terraform variables for ldap mappers resources 543 if r.InstanceInfo.Type == "keycloak_ldap_full_name_mapper" || 544 r.InstanceInfo.Type == "keycloak_ldap_group_mapper" || 545 r.InstanceInfo.Type == "keycloak_ldap_role_mapper" || 546 r.InstanceInfo.Type == "keycloak_ldap_hardcoded_group_mapper" || 547 r.InstanceInfo.Type == "keycloak_ldap_hardcoded_role_mapper" || 548 r.InstanceInfo.Type == "keycloak_ldap_msad_lds_user_account_control_mapper" || 549 r.InstanceInfo.Type == "keycloak_ldap_msad_user_account_control_mapper" || 550 r.InstanceInfo.Type == "keycloak_ldap_user_attribute_mapper" { 551 g.Resources[i].Item["ldap_user_federation_id"] = mapUserFederationIDs[r.Item["realm_id"].(string)+"_"+g.Resources[i].Item["ldap_user_federation_id"].(string)] 552 } 553 554 // Map group to keycloak_group.foo.name Terraform variables for ldap hardcoded group mapper resources 555 if r.InstanceInfo.Type == "keycloak_ldap_hardcoded_group_mapper" { 556 g.Resources[i].Item["group"] = mapGroupNames[r.Item["realm_id"].(string)+"_"+r.Item["group"].(string)] 557 } 558 559 // Map role to Terraform variables for ldap hardcoded role mapper resources 560 if r.InstanceInfo.Type == "keycloak_ldap_hardcoded_role_mapper" { 561 g.Resources[i].Item["role"] = mapClientRoleNames[r.Item["realm_id"].(string)+"_"+r.Item["role"].(string)] 562 } 563 564 // Map parent_id to keycloak_group.foo.id Terraform variables for keycloak_group resources 565 if _, exist := r.Item["parent_id"]; exist && r.InstanceInfo.Type == "keycloak_group" { 566 g.Resources[i].Item["parent_id"] = mapGroupIDs[r.Item["realm_id"].(string)+"_"+r.Item["parent_id"].(string)] 567 } 568 569 // Map group_id to keycloak_group.foo.id Terraform variables for keycloak_group_memberships and keycloak_group_roles resources 570 if r.InstanceInfo.Type == "keycloak_group_memberships" || r.InstanceInfo.Type == "keycloak_group_roles" { 571 g.Resources[i].Item["group_id"] = mapGroupIDs[r.Item["realm_id"].(string)+"_"+r.Item["group_id"].(string)] 572 } 573 574 // Map service_account_user_id to keycloak_openid_client.foo.service_account_user_id Terraform variables for service account role resources 575 if r.InstanceInfo.Type == "keycloak_openid_client_service_account_role" { 576 g.Resources[i].Item["service_account_user_id"] = mapServiceAccountUserIDs[r.Item["realm_id"].(string)+"_"+r.Item["service_account_user_id"].(string)] 577 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)] 578 } 579 580 // Map client_id attributes to keycloak_openid_client.foo.id Terraform variables for open id mappers resources 581 if _, exist := r.Item["client_id"]; exist && (r.InstanceInfo.Type == "keycloak_openid_client_service_account_role" || 582 r.InstanceInfo.Type == "keycloak_openid_audience_protocol_mapper" || 583 r.InstanceInfo.Type == "keycloak_openid_full_name_protocol_mapper" || 584 r.InstanceInfo.Type == "keycloak_openid_group_membership_protocol_mapper" || 585 r.InstanceInfo.Type == "keycloak_openid_hardcoded_claim_protocol_mapper" || 586 r.InstanceInfo.Type == "keycloak_openid_hardcoded_group_protocol_mapper" || 587 r.InstanceInfo.Type == "keycloak_openid_hardcoded_role_protocol_mapper" || 588 r.InstanceInfo.Type == "keycloak_openid_user_attribute_protocol_mapper" || 589 r.InstanceInfo.Type == "keycloak_openid_user_property_protocol_mapper" || 590 r.InstanceInfo.Type == "keycloak_openid_user_realm_role_protocol_mapper" || 591 r.InstanceInfo.Type == "keycloak_openid_client_default_scopes" || 592 r.InstanceInfo.Type == "keycloak_openid_client_optional_scopes" || 593 r.InstanceInfo.Type == "keycloak_role") { 594 g.Resources[i].Item["client_id"] = mapClientIDs[r.Item["realm_id"].(string)+"_"+r.Item["client_id"].(string)] 595 } 596 597 // Map included_client_audience to keycloak_openid_client.foo.client_id Terraform variables for open id audience mapper resources 598 if _, exist := r.Item["included_client_audience"]; exist && r.InstanceInfo.Type == "keycloak_openid_audience_protocol_mapper" { 599 g.Resources[i].Item["included_client_audience"] = mapClientClientIDs[r.Item["realm_id"].(string)+"_"+r.Item["included_client_audience"].(string)] 600 } 601 602 // Map parent_flow_alias attributes to keycloak_authentication_(sub)flow.foo.alias Terraform variables for authentication subflow and execution resources 603 if r.InstanceInfo.Type == "keycloak_authentication_subflow" || r.InstanceInfo.Type == "keycloak_authentication_execution" { 604 g.Resources[i].Item["parent_flow_alias"] = mapAuthenticationFlowAliases[r.Item["realm_id"].(string)+"_"+r.Item["parent_flow_alias"].(string)] 605 } 606 607 // Map execution_id attributes to keycloak_authentication_execution_config.foo.execution_id Terraform variables for authentication execution config resources 608 if r.InstanceInfo.Type == "keycloak_authentication_execution_config" { 609 g.Resources[i].Item["execution_id"] = mapAuthenticationExecutionIDs[r.Item["realm_id"].(string)+"_"+r.Item["execution_id"].(string)] 610 } 611 612 // Map realm_id attributes to keycloak_realm.foo.id Terraform variables for all the resources (almost all resources have this attribute) 613 if _, exist := r.Item["realm_id"]; exist { 614 g.Resources[i].Item["realm_id"] = mapRealmIDs[r.Item["realm_id"].(string)] 615 } 616 } 617 return nil 618 }