Continuing our journey with KCL, in this post we will explore how to accelerate the creation of reusable Kubernetes packages from CRDs using KIRO and the AMDF MCP tool.
Kiro + AMDF MCP elevate a process that traditionally requires long times in a conversation of minutes, where the developer simply indicates what resources are needed.
Additionally, by using KCL instead of traditional YAML, KCL provides strong typing, validation at development time, and abstraction capabilities that transform repetitive configurations into reusable and maintainable code. The result is infrastructure as data that is not only generated quickly, but is also robust, validated, and ready.
Prerequisites
- Kubernetes cluster
- CRDS in Kubernetes Cluster, for this demo ACK, KRO and External Secrets Operator
- AMDF
- KIRO
- KCL
Step 1.
Validate your CRDS.
In this case the cluster have CRDS from ESO and ACK EC2-RDS Controllers.
Step 2.
Connect MCP.
With AMDF installed is so simple like as execute
amdf mcp-server
Starting AMDF MCP Server...
Step 3.
With the MCP Server running, open KIRO for start to create reusable packages using a basic prompt.
Kiro, I need to create a ResourceGraphDefinition CRD KCL schema for generate an abstraction of multiple resources.
Step 4.
With the prompt running, the AMDF MCP tools start to working.
Step 5.
AMDF Automatically create this directorys using KCL sintax.
Step 6.
For this demo, I migrated a code from crossplane to KRO, using
helper functions and CEL expretions required for KRO.
Check the code in this repository
segoja7
/
mcp-kcl-infra-accelerator
Kubernetes packages using KRO, KCL, and MCP (KIRO + AMDF).
Keycloak Stack
A reusable Keycloak deployment abstraction built with KRO and KCL. It provisions Keycloak and its database backend — either a local PostgreSQL instance or AWS RDS — from a single Kubernetes custom resource.
Architecture
Description
This example demonstrates how to use KCL to define a KRO ResourceGraphDefinition that generates a KeycloakStack CRD. Applying a KeycloakStack instance creates all the necessary Kubernetes resources for a fully functional Keycloak deployment.
The stack supports two deployment modes controlled by the localTest flag:
-
Local mode (
localTest: true) — Deploys an in-cluster PostgreSQL Deployment, Service, and Secret. Ideal for development and testing. -
AWS RDS mode (
localTest: false) — Provisions an AWS RDS DBInstance with optional subnet group and security group, and connects Keycloak via an ExternalName Service. Supports three password management strategies.
Project Structure
├── KRO-KEYCLOAK-1.drawio.png # Architecture diagram
├── kcl_render/
│ └── KeycloakStack.yaml # Rendered YAML output…Step 7.
Check the Main.k
New to KCL? Don't worry! The syntax is designed to be developer-friendly. You can find a complete introduction in my previous blog
Is time for test that code is correctly and compile.
Step 8.
Test that new KCL package using AMDF, KIRO and RDS, EC2 ACK and External Secrets Controllers are working correctly.
kcl library/main.k | kubectl apply -f -
resourcegraphdefinition.kro.run/keycloak-stack created
Here's an RGD that creates a new KeycloakStack API. When users create an KeycloakStack, kro automatically creates multiples resources defined in the Resource Graph Definition
For now, this is a local test, but let me show you how to bring this to GitOps and integrate it with OCI registries and using the KCL plugin for deployment in the next blog.
Step 9.
check the 02-aws-rds-deploy.yaml.
apiVersion: kro.run/v1alpha1
kind: KeycloakStack
metadata:
name: keycloak-dev
namespace: default
spec:
projectName: "dev"
environment: "dev"
keycloakMode: "start-dev"
keycloakReplicas: 1
kcProxy: "" # Sin proxy para port-forward
keycloakImage: "quay.io/keycloak/keycloak:latest"
keycloakAdminUser: "admin"
keycloakHostname: "keycloakrds.local"
postgresImage: "postgres:17"
postgresPassword: ""
localTest: false
rdsInstanceClass: "db.t3.micro"
rdsAllocatedStorage: 20
rdsEngineVersion: "17"
rdsDBName: "keycloak"
rdsUsername: "keycloak"
rdsManageMasterUserPassword: true
rdsSubnetIDs:
- "subnet-0436a5657992422d2" # us-east-1a - CAMBIAR
- "subnet-03fc372cafad1feec" # us-east-1b - CAMBIAR
rdsVPCID: "vpc-0d7e4425ca4d23f89" # CAMBIAR
rdsAllowedCIDRs:
- "10.0.0.0/16" # CIDR de tu VPC
Apply the KeycloakStack yaml resource for deploy and test.
kubectl apply -f sample/02-aws-rds-deploy.yaml
keycloakstack.kro.run/keycloak-dev created
Step 10.
Validate that resources are ready.
kubectl get keycloakstack,dbsubnetgroups,securitygroups,dbinstances,secretstore,externalsecret,secrets,svc,deployments,pods,ingress -n default
NAME STATE READY AGE
keycloakstack.kro.run/keycloak-dev ACTIVE True 11m
NAME AGE
dbsubnetgroup.rds.services.k8s.aws/keycloak-subnet-group-dev 11m
NAME ID
securitygroup.ec2.services.k8s.aws/keycloak-rds-sg-dev sg-0bae8ee9ebb07ac3b
securitygroup.ec2.services.k8s.aws/my-webserver-sg sg-06db013ac4841cf58
NAME STATUS
dbinstance.rds.services.k8s.aws/keycloak-rds-dev available
NAME AGE STATUS CAPABILITIES READY
secretstore.external-secrets.io/aws-secretstore-dev 11m Valid ReadWrite True
NAME STORETYPE STORE REFRESH INTERVAL STATUS READY
externalsecret.external-secrets.io/rds-password-sync-dev SecretStore aws-secretstore-dev 1h SecretSynced True
NAME TYPE DATA AGE
secret/aws-credentials Opaque 4 2m6s
secret/rds-connection-dev Opaque 4 7m15s
secret/rds-password-synced-dev Opaque 2 11m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/keycloak-service-dev ClusterIP 10.102.50.198 <none> 8080/TCP 11m
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d3h
service/postgres-service ExternalName <none> keycloak-rds-dev.ckxssqysgxjv.us-east-1.rds.amazonaws.com 5432/TCP 7m15s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/keycloak-dev 0/1 1 0 11m
NAME READY STATUS RESTARTS AGE
pod/keycloak-dev-6fc5c4bdd9-kgkzf 1/1 Running 0 8m4s
Create a port forward and use keycloakHostname value, in this case "keycloakrds.local".

For connect con admin, the password is in the secret (rds-password-synced-dev).

CONCLUSION:
The combination of AMDF and KIRO acts as a force multiplier, allowing us to build robust, validated, and reusable OCI packages or in this case Yaml manifests from existing CRDs faster.
Using KCL instead of traditional YAML isn't just a syntax preference—it’s about building a safer, more modular, and maintainable backbone for our Cloud Native ecosystem.









Top comments (0)