liveBook · Manning

Keycloak container

As explained in Building Distributed Applications that Work

A Keycloak container is a containerized Keycloak instance that runs the identity and access management service.

A Keycloak container runs the identity service alongside an Aspire-hosted application. For an online shop, the Aspire host registers Keycloak as a resource, then gives the API and web frontend references to it. section 2.4

Wire it into the host in three steps:

  • Define a KeycloakResource derived from ContainerResource and implementing IResourceWithServiceDiscovery. Store the client secret on the resource so it can be passed to Keycloak and its client. section 2.4
  • Register it with AddResource(), configure the Keycloak image, HTTP endpoint and environment variables, then bind-mount the realm files for import. section 2.4
  • Reference it from the API and frontend, and use WaitFor(idp) so those services wait for the identity provider. Pass the frontend's client secret through Identity__ClientSecret. section 2.4
Listing 2.1 Adding a Keycloak container
public static IResourceBuilder<KeycloakResource>
    AddKeycloakContainer(
    this IDistributedApplicationBuilder builder,
    string name,
    int? port = null,
    string? tag = null)
{
    var keycloakContainer = new KeycloakResource(name)   #1
    {
        ClientSecret = "some_secret"
    };

    var keycloak = builder
        .AddResource(keycloakContainer)    #2
        .WithAnnotation(new ContainerImageAnnotation    #3
        {
            Registry = "quay.io",
            Image = "keycloak/keycloak",
            Tag = tag ?? "latest"
         })
        .WithHttpEndpoint(port: port,    #4
            targetPort: 8080)
        .WithEnvironment("KEYCLOAK_ADMIN", "admin")   #5
        .WithEnvironment("KEYCLOAK_ADMIN_PASSWORD",
            "admin")
        .WithEnvironment("WEBAPP_CLIENT_SECRET",
            keycloakContainer.ClientSecret);

    if (builder.ExecutionContext.IsRunMode)    #6
    {
        keycloak.WithArgs("start-dev");
    }
    else
    {
        keycloak.WithArgs("start");
    }

    return keycloak;
}

The container registration sets the image, target port and environment variables; AddResource() allows the custom resource to carry the client secret. section 2.4

Listing 2.2 Importing the Keycloak realms
public static IResourceBuilder<KeycloakResource>
    ImportRealms(this IResourceBuilder<KeycloakResource>
        builder,
    string source)
{
    builder
        .WithBindMount(source,
            "/opt/keycloak/data/import")
        .WithAnnotation(
            new CommandLineArgsCallbackAnnotation(
                args =>
        {
            args.Clear();
            if (builder.ApplicationBuilder
                .ExecutionContext.IsRunMode)
            {
                args.Add("start-dev");
            }
            else
            {
                args.Add("start");
            }
            args.Add("--import-realm");
        }));

    return builder;
}

Realm import mounts the source directory and selects start-dev in run mode or start otherwise, adding --import-realm in either case. section 2.4

How Building Distributed Applications that Work covers it

How deep it goes
Works through resource registration, image and endpoint configuration, realm import, service references, and startup ordering.
Examples
Shows a KeycloakResource class, AddKeycloakContainer and ImportRealms extensions, and API and frontend host registrations.
What it assumes
Assumes familiarity with C# projects and extension methods while defining the custom container resource used for Aspire integration.
What you can do after
Operate/configure it in an Aspire host with realm import and references from dependent services.
How it connects
Links container resources to service discovery, environment variables, realm import, HTTP endpoints, and hosted-service dependencies.

This explainer was generated by Manning's knowledge graph from the text of Building Distributed Applications that Work. The section links point to the parts of the book it draws on.