Skip to main content
Version: 0.5 (Next)

Catalog Item Examples

Real-world catalog item examples demonstrating common patterns and best practices.

Application Catalog Item Example: Prometheus Monitoring

Deploy a complete Prometheus monitoring stack with Grafana dashboards.

Directory Structure:

catalog/prometheus-monitoring/
├── Chart.yaml
├── values.yaml
└── templates/
└── application.yaml

Chart.yaml:

apiVersion: v2
name: prometheus-monitoring
description: Prometheus monitoring stack with Grafana dashboards
type: application
version: 0.2.0
appVersion: "2.45.0"

values.yaml:

# @input.type: string
# @input.description: Target cluster name
# @input.required: true
clusterName: my-cluster

# @input.type: string
# @input.description: Namespace for monitoring stack
# @input.required: true
# @input.default: monitoring
namespace: monitoring

# @input.type: enum
# @input.description: Enable Grafana dashboards
# @input.options: true,false
# @input.required: true
# @input.default: true
grafanaEnabled: true

# @input.type: string
# @input.description: Data retention period (e.g., 15d, 30d)
# @input.required: false
# @input.default: 15d
retentionPeriod: 15d

# @input.type: enum
# @input.description: Enable alerting rules
# @input.options: true,false
# @input.required: false
# @input.default: true
alertingEnabled: true

# @input.type: secret
# @input.description: Grafana admin password
# @input.required: true
# @input.secretKey: admin-password
# @input.secretEnv: GRAFANA_ADMIN_PASSWORD
# @input.secretBackend: aws-secrets-store
# @input.secretPath: /prometheus-monitoring
grafanaAdminPassword: ""

# Standard values
# @input.type: string
# @input.description: Target cluster destination
# @input.required: true
# @input.default: in-cluster
clusterDestination: in-cluster

# @input.type: string
# @input.description: ArgoCD project
# @input.required: true
# @input.default: platform
project: platform

templates/application.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: {{ .Values.clusterName }}-prometheus
namespace: argocd
annotations:
kubefirst.konstruct.io/application-name: prometheus-monitoring
kubefirst.konstruct.io/source: catalog-templates
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
destination:
namespace: {{ .Values.namespace }}
name: {{ .Values.clusterDestination }}
project: {{ .Values.project }}
source:
chart: kube-prometheus-stack
repoURL: https://prometheus-community.github.io/helm-charts
targetRevision: "48.3.1"
helm:
releaseName: prometheus
values: |
prometheus:
prometheusSpec:
retention: {{ .Values.retentionPeriod }}
{{- if .Values.alertingEnabled }}
enableAlertmanager: true
{{- end }}
grafana:
enabled: {{ .Values.grafanaEnabled }}
{{- if .Values.grafanaEnabled }}
adminPassword:
valueFrom:
secretKeyRef:
name: grafana-secret
key: admin-password
{{- end }}
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

IaC Catalog Item Example: RDS PostgreSQL Database

Provision a production-ready PostgreSQL database with security best practices.

Directory Structure:

rds-postgres/
├── provider
├── main.tf
├── variables.tf
└── outputs.tf

provider:

terraform {
backend "s3" {
bucket = "<BUCKET_NAME>"
key = "registry/clusters/<NAME>/infrastructure/rds-postgres/terraform.tfstate"
region = "<REGION>"
encrypt = true
}
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.30.0, < 6.0.0"
}
random = {
source = "hashicorp/random"
version = ">= 3.6.0, < 4.0.0"
}
}
}

provider "aws" {
region = "<REGION>"
allowed_account_ids = ["<ACCOUNT_ID>"]
assume_role_with_web_identity {
session_name = "kubefirst-pro"
role_arn = "<ROLE_ARN>"
web_identity_token_file = "/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
}
}

main.tf:

resource "random_password" "db_password" {
length = 32
special = true
}

resource "aws_db_subnet_group" "main" {
name = "${var.database_name}-subnet-group"
subnet_ids = var.subnet_ids

tags = {
Name = "${var.database_name}-subnet-group"
}
}

resource "aws_security_group" "db" {
name = "${var.database_name}-sg"
description = "Security group for ${var.database_name} RDS instance"
vpc_id = var.vpc_id

ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = var.allowed_cidrs
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

tags = {
Name = "${var.database_name}-sg"
}
}

resource "aws_db_instance" "postgres" {
identifier = var.database_name
engine = "postgres"
engine_version = var.postgres_version
instance_class = var.instance_class
allocated_storage = var.allocated_storage
storage_encrypted = true
db_name = replace(var.database_name, "-", "_")
username = "postgres"
password = random_password.db_password.result
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.db.id]
skip_final_snapshot = var.skip_final_snapshot
backup_retention_period = var.backup_retention_days
multi_az = var.multi_az
publicly_accessible = false

tags = {
Name = var.database_name
Environment = var.environment
ManagedBy = "Terraform"
}
}

resource "aws_secretsmanager_secret" "db_credentials" {
name = "${var.database_name}-credentials"
}

resource "aws_secretsmanager_secret_version" "db_credentials" {
secret_id = aws_secretsmanager_secret.db_credentials.id
secret_string = jsonencode({
username = aws_db_instance.postgres.username
password = random_password.db_password.result
endpoint = aws_db_instance.postgres.endpoint
port = aws_db_instance.postgres.port
database = aws_db_instance.postgres.db_name
})
}

variables.tf:

variable "database_name" {
description = "Name of the database instance"
type = string
}

variable "postgres_version" {
description = "PostgreSQL version"
type = string
default = "15.3"
}

variable "instance_class" {
description = "RDS instance class"
type = string
default = "db.t3.micro"
}

variable "allocated_storage" {
description = "Allocated storage in GB"
type = number
default = 20
}

variable "vpc_id" {
description = "VPC ID for security group"
type = string
}

variable "subnet_ids" {
description = "List of subnet IDs for DB subnet group"
type = list(string)
}

variable "allowed_cidrs" {
description = "List of CIDR blocks allowed to connect"
type = list(string)
}

variable "skip_final_snapshot" {
description = "Skip final snapshot on deletion"
type = bool
default = false
}

variable "backup_retention_days" {
description = "Number of days to retain backups"
type = number
default = 7
}

variable "multi_az" {
description = "Enable multi-AZ deployment"
type = bool
default = false
}

variable "environment" {
description = "Environment name"
type = string
}

outputs.tf:

output "db_instance_endpoint" {
description = "Database instance endpoint"
value = aws_db_instance.postgres.endpoint
}

output "db_instance_arn" {
description = "Database instance ARN"
value = aws_db_instance.postgres.arn
}

output "db_instance_port" {
description = "Database port"
value = aws_db_instance.postgres.port
}

output "db_name" {
description = "Database name"
value = aws_db_instance.postgres.db_name
}

output "secret_arn" {
description = "ARN of secrets manager secret with credentials"
value = aws_secretsmanager_secret.db_credentials.arn
}

output "security_group_id" {
description = "Security group ID for database"
value = aws_security_group.db.id
}

Hybrid Catalog Item Example: Redis with Management UI

Provision Redis ElastiCache and deploy RedisInsight management UI.

This example combines:

  • IAC: AWS ElastiCache Redis cluster
  • YAML: RedisInsight management application

Key Files (Condensed):

values.yaml (Combined):

# Application parameters
# @input.type: string
# @input.description: Application name
# @input.required: true
appName: redis-insight

# @input.type: string
# @input.description: Namespace
# @input.required: true
# @input.default: redis
namespace: redis

# Infrastructure parameters
# @input.type: string
# @input.description: Redis cluster name
# @input.required: true
redisClusterName: my-redis

# @input.type: enum
# @input.description: Redis node type
# @input.options: cache.t3.micro,cache.t3.small,cache.m5.large
# @input.required: true
# @input.default: cache.t3.micro
redisNodeType: cache.t3.micro

# @input.type: string
# @input.description: Number of cache nodes
# @input.required: true
# @input.default: 1
numCacheNodes: 1

# @input.type: string
# @input.description: Redis engine version
# @input.required: true
# @input.default: 7.0
redisVersion: 7.0

# Standard values
# @input.type: string
# @input.description: VPC ID
# @input.required: true
vpcId: vpc-xxxxx

# @input.type: string
# @input.description: Subnet IDs (comma-separated)
# @input.required: true
subnetIds: subnet-xxxxx,subnet-yyyyy

main.tf (Redis ElastiCache):

resource "aws_elasticache_subnet_group" "redis" {
name = "${var.redis_cluster_name}-subnet-group"
subnet_ids = split(",", var.subnet_ids)
}

resource "aws_security_group" "redis" {
name = "${var.redis_cluster_name}-sg"
description = "Security group for Redis cluster"
vpc_id = var.vpc_id

ingress {
from_port = 6379
to_port = 6379
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
}
}

resource "aws_elasticache_cluster" "redis" {
cluster_id = var.redis_cluster_name
engine = "redis"
engine_version = var.redis_version
node_type = var.redis_node_type
num_cache_nodes = var.num_cache_nodes
parameter_group_name = "default.redis7"
subnet_group_name = aws_elasticache_subnet_group.redis.name
security_group_ids = [aws_security_group.redis.id]

tags = {
Name = var.redis_cluster_name
}
}

templates/application.yaml (RedisInsight UI):

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: {{ .Values.clusterName }}-{{ .Values.appName }}
namespace: argocd
spec:
destination:
namespace: {{ .Values.namespace }}
name: {{ .Values.clusterDestination }}
project: {{ .Values.project }}
source:
chart: redisinsight
repoURL: https://charts.redis.com
targetRevision: "1.0.0"
helm:
values: |
redis:
host: {{ .Values.redisEndpoint }}
port: 6379
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

Common Patterns

Pattern 1: External Secrets Integration

Use External Secrets Operator (ESO) to pull secrets from your cloud provider's secret store into Kubernetes Secrets on the workload cluster.

ExternalSecret template (included in your catalog template):

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: {{ .Values.clusterName }}-{{ .Values.appName }}-secrets
namespace: {{ .Values.namespace }}
spec:
secretStoreRef:
name: aws-secrets-store
kind: ClusterSecretStore
target:
name: app-secrets
dataFrom:
- extract:
key: {{ .Values.secretPath }}

The secretStoreRef.name references the workload cluster's ClusterSecretStore, and dataFrom.extract.key matches the @input.secretPath annotation from your values.yaml.

Annotate secret parameters in values.yaml:

# @input.type: secret
# @input.description: Database password
# @input.required: true
# @input.secretKey: db-password
# @input.secretEnv: DATABASE_PASSWORD
# @input.secretBackend: aws-secrets-store
# @input.secretPath: /my-app
databasePassword: ""

See the Catalog Item Annotations Reference for complete secret annotation documentation.

Pattern 2: Multi-Environment Configuration

Use environment-specific values:

# @input.type: enum
# @input.description: Deployment environment
# @input.options: dev,staging,prod
# @input.required: true
environment: dev

# @input.type: string
# @input.description: Instance size (varies by environment)
# @input.required: true
# @input.default: small
instanceSize: {{ if eq .Values.environment "prod" }}large{{ else }}small{{ end }}

Pattern 3: Conditional Resource Deployment

Deploy resources based on flags:

{{- if .Values.monitoringEnabled }}
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: {{ .Values.clusterName }}-monitoring
# ... monitoring application
{{- end }}

What's Next?