Keycloak Multi-Tenancy and the Pulumi Automation API - Part 1

by Spas Poptchev

Welcome to the first article in our series exploring the integration of the Pulumi Automation API, a powerful Infrastructure as Code (IaC) platform, and Keycloak, a leading open-source Identity and Access Management (IAM) solution. In the age of rapid cloud migration, managing complex IAM processes across multiple systems and user roles has become a significant challenge. In this series, we'll show how combining Keycloak and the Pulumi Automation API can make IAM processes more efficient for businesses while also enhancing security procedures and resource allocation efficiency.

In this first part of our series, we'll concentrate on getting to know Keycloak and Pulumi and setting up the REST API framework that will support our interactions with the Pulumi Automation API in the following blog posts. Our goal is to give you a firm understanding of Keycloak and Pulumi's foundational ideas before we continue with an example of the actual integrate. We want to show how automating realm creation and management in Keycloak reduces manual effort while also improving consistency across multiple tenants.

Introduction

Over the past decade, the migration rate of applications and services to the cloud has grown exponentially — and for good reason. Cloud computing provides organizations with substantial scalability, flexibility, and cost-effectiveness.

However, embracing the cloud also brings new challenges, one of the most notable being secure access to cloud resources. Organizations today must manage complex identity and access management (IAM) processes across diverse systems and user roles, a challenging task that brings us to the topic at hand.

This article will explore the benefits of using Keycloak, a widely adopted open-source IAM solution in conjunction with the Pulumi Automation API. Said setup enables organizations of all sizes to streamline their IAM by leveraging Pulumi automating capabilities to provision Keycloak instances for multiple tenants, ensuring consistent security practices and efficient resource allocation.

What is Keycloak Multi-Tenancy

As we already mentioned, Keycloak is an open-source Identity and Access Management solution. Its main goal is to centralize user authentication and authorization, making it easier to secure applications and services. One of Keycloak's strengths is its ability to support multiple tenants. This handy feature enables organizations to effectively isolate access to resources for different groups and users. To better understand this powerful security feature, we must briefly explain the concept of realms.

In Keycloak, realms serve as isolated environments for managing users, roles, and permissions. Each realm represents a tenant or a group of users with a common set of resources and access rules. This multi-tenancy capability enables organizations to segregate their user bases and maintain separate security policies, ensuring that resources are only accessible to authorized users.

Simply put, Keycloak's multi-tenancy feature allows creating groups (tenants) according to business units, responsibilities, or whatever criteria your organization requires. This ensures a consistent security posture across the entire organization with relatively little effort.

Regarding the latter, managing an IAM system in companies with hundreds of employees can be a daunting task, even using Keycloak's fantastic UI. This is where the Pulumi Automation API comes into play.

What is Pulumi and Pulumi Automation API

Pulumi is an open-source Infrastructure as a Code platform that allows developers to define and manage cloud infrastructure using familiar programming languages like Python, TypeScript, Go, and C#, among others.

On the other hand, the Pulumi Automation API is a powerful library that enables developers to programmatically interact with Pulumi's infrastructure-as-code (IAC) engine, allowing them to create, update, and delete infrastructure resources as required.

Why Use Pulumi Automation API for Keycloak Deployments?

The Pulumi Automation API offers numerous benefits to development teams and organizations as they tackle the challenges of managing infrastructure in today's cloud-centric world.

In the context of provisioning Keycloak multi-tenancy instances, the Pulumi Automation API allows developers to automate the creation and management of realms, reducing manual effort and ensuring consistent configurations across multiple tenants. This streamlined approach saves time and minimizes the risk of misconfigurations that could lead to security vulnerabilities.

That being said, these are just some of the advantages of using the Pulumi Automation API for handling Keycloak multi-tenancy solutions.

Language Flexibility

One of the main benefits of using Pulumi Automation API is the ability to choose from various programming languages to define your infrastructure resources, including Keycloak deployments. Unlike other IaC tools like Terraform that rely on domain-specific languages or YAML/JSON configuration files, Pulumi allows developers to use popular languages like Python, Node.js, TypeScript, JavaScript, Go, Java, and the entire family of .NET languages.

Moreover, regardless of the language used, Pulumi offers support for all the clouds and applications available in the Pulumi Registry. This includes Pulumi's Keycloak Resource Provider.

Comprehensive and Customizable Automation

Pulumi Automation API enables developers to create custom workflows and integrations that leverage the power of Pulumi's Infrastructure as Code capabilities. By embedding Pulumi directly into applications, developers can automate the creation, modification, and destruction of infrastructure resources with precision and control. Such flexibility is essential from a security standpoint, as your organization can tune the Pulumi automation API for whatever scenario you have in mind when deploying Keycloak.

Enhanced Developer Experience

The Pulumi automation API provides a seamless developer experience by integrating Infrastructure as Code into the same development environment used for application code. This eliminates the need for context-switching between different tools, languages, or configuration files, allowing developers to focus on building features and functionality.

Multi-Cloud Support

Pulumi Automation API supports a wide range of cloud providers, including AWS, Azure, Google Cloud, and Kubernetes. This flexibility enables developers to manage infrastructure across multiple cloud environments using a single, unified platform. In other words, with Pulumi and Keycloak, your organization can develop a vendor-agnostic solution that can be used both on-premise or the cloud, any cloud.

Setting up the REST API

In this section, we'll guide you through the setup of our REST API that we will later use to interact with the Pulumi Automation API. To create a new Keycloak realm using a REST API, we'll set up a simple HTTP server in Go and incorporate the Pulumi Automation API in the next blog article.

In Keycloak multi-tenancy can be modeled in various ways. The two most popular strategies are one realm with one client per tenant and multi-tenancy with one realm per tenant. Each strategy has its own pros and cons, which we may discuss in a future blog post. Therefore, the strategy you choose will largely depend on your specific use case. For this example, we opted for one realm per tenant, as it is the simplest option.

For the REST API we'll define two endpoints POST /tenants and DELETE /tenants/:slug.

The POST endpoint will receive a JSON body with the tenant slug and the tenant name:

{
  "slug": "zone2",
  "name": "Zone 2 technologies Ltd."
}

When this endpoint receives a request, it will trigger the Pulumi Automation API which in turn will set up a new realm named zone2 in Keycloak.

On the other hand, the DELETE endpoint will destroy the newly created realm. For this we'll pass the slug as a path parameter to the DELETE endpoint.

Project setup

Let's get started with the Go project setup.

  1. Create a new directory for your project: Choose a location outside your GOPATH, then create a new directory and navigate into it.
mkdir tenant-service
cd tenant-service
  1. Initialize a new module: Inside your project directory, initialize a new module by running:
go mod init github.com/your-username/tenant-service

Replace "your-username" with your GitHub username. This command will create a go.mod file in your directory. 3. Add dependencies: To run the project we'll need the following dependencies:

# HTTP server
go get github.com/gorilla/mux
# Pulumi Keycloak SDK
go get github.com/pulumi/pulumi-keycloak/sdk/v5
# Pulumi API
go get github.com/pulumi/pulumi/sdk/v3

In this step, we're grabbing the necessary dependencies for our project. Here's a little more information about what each of these commands do:

  • go get github.com/gorilla/mux: This line is fetching the mux package from the Gorilla web toolkit. It's a powerful URL router and dispatcher that we'll use to direct HTTP requests in our server.
  • go get github.com/pulumi/pulumi-keycloak/sdk/v5: Here we're downloading the Pulumi Keycloak SDK. This software development kit (SDK) will allow us to interact with Keycloak in our Go application.
  • go get github.com/pulumi/pulumi/sdk/v3: Lastly, this command retrieves the Pulumi SDK. This will let us use the Pulumi API.

HTTP server setup

After we are done with the basic setup of our application we can continue to integrate the HTTP server.

  1. Application setup: In this part of the project, we're setting up the main application. We begin by creating a main.go file in our root directory. This file will be the entry point for our application and will start the HTTP server.
func main() {
   router := mux.NewRouter()

   router.HandleFunc("/tenants", createTenantHandler).Methods("POST")
   router.HandleFunc("/tenants/{slug}", deleteTenantHandler).Methods("DELETE")

   s := &http.Server{Addr: ":8000", Handler: router}

   err := s.ListenAndServe()

   if err != nil {
     log.Fatal("Unable to start server", err)
   }
}

This is our basic HTTP server setup in Go that implements the before mentioned endpoints. We have not yet implemented the createTenantHandler and deleteTenantHandler functions - but stay tuned, we'll be tackling those in the next steps. 2. Implement createTenantHandler: In this step, we're implementing the createTenantHandler function in a new file named handlers.go. This function will be responsible for handling incoming HTTP POST requests to create a new tenant.

// POST request payload
type CreateTenantRequest struct {
   Slug string `json:"slug"`
   Name string `json:"name"`
}

// POST request response
type CreateTenantResponse struct {
   RealmId string `json:"realmId"`
}


func createTenantHandler(w http.ResponseWriter, req *http.Request) {
   var payload CreateTenantRequest
   err := json.NewDecoder(req.Body).Decode(&payload)

    if err != nil {
        http.Error(w, "Invalid request body", http.StatusBadRequest)

        return
    }

    // TODO: implement creation of the Keycloak realm

    response := &CreateTenantResponse{
        RealmId: "TODO",
    }

    err = json.NewEncoder(w).Encode(&response)

    if err != nil {
        http.Error(w, "Could not write response json", http.StatusInternalServerError)
    }
}

In this function, we're processing a POST request to create a new tenant (or "realm" in Keycloak terms). After successfully decoding the incoming request, we reach a placeholder section:

// TODO: implement creation of the Keycloak realm

Here is where we'll add our code to interact with Keycloak, using the information provided in the CreateTenantRequest to create a new realm.

After the realm is created in Keycloak, we'll need to replace the "TODO" placeholder in the CreateTenantResponse with information relevant to the newly created realm:

response := &CreateTenantResponse{
    RealmId: "TODO",
}

Lastly, we encode and send our response. If there's an issue during encoding, we handle it accordingly and respond with an appropriate error status.

So, this function is all about making Keycloak create a new realm based on our request, then sending back some meaningful information about the created realm in the response. It's a key part of our multi-tenancy setup with Keycloak, allowing us to programmatically expand our application with new tenants as required. We'll explore how to actually communicate with Keycloak and create realms in the following parts of the blog. 3. Implement deleteTenantHandler: We're now moving on to implementing the deleteTenantHandler, which is responsible for deleting a tenant. You can add the following code to the handlers.go file:

func deleteTenantHandler(w http.ResponseWriter, req *http.Request) {
    params := mux.Vars(req)

    if len(params["slug"]) == 0 {
        http.Error(w, "slug is required", http.StatusBadRequest)

        return
    }

    // TODO: implement deletion of the Keycloak realm
}

This function will handle incoming HTTP DELETE requests, allowing us to delete an existing tenant. The slug of the tenant we want to delete is expected to be included in the request URL. Like in the createTenantHandler we have a TODO placeholder where we'll add our code to communicate with Keycloak and delete the realm corresponding to the given slug. 4. Start the application: At this stage, we've set up our HTTP server and implemented the handler functions for creating and deleting tenants. Our next step is to actually start the application and see it in action. Here's how we can do that:

# build
go build -o tenant-service

# run
./tenant-service

Once the server is up and running, we'll be able to send HTTP requests to theoretically create and delete tenants. In the next steps, we'll be adding more functionality and starting to interact with Pulumi to manage our tenants.

Conclusion

We've started our journey exploring the integration of Keycloak and the Pulumi Automation API for more efficient Identity and Access Management. Additionally, we've laid out the basics of Keycloak and Pulumi, with a focus on the potential of Pulumi's Automation API for improving a multi-tenancy setup in organizations. Moreover, we walked through setting up a simple REST API framework in Go, demonstrating how it will support our interactions moving forward.

In the following articles, we will build on this foundation, focusing on implementing the API, automating realm creation, and managing Keycloak using Pulumi's Infrastructure as Code capabilities.

I hope that you enjoyed the blog post and will join me for next one, too. I look forward to hearing your thoughts and feedback!

The code for each part of the project will be available in this Git repository.

How can we help?

We're passionate about solving challenges and turning exciting ideas into reality, together with you. If you have any questions or need assistance with your projects, we're here to help. Don't hesitate to get in touch!

Book a Call
or send a message