This guide walks you through deploying supporting infrastructure components using the kindo-peripheries module.
Table of Contents
- Quick Start
- Understanding the Peripheries Module
- Service Enable/Disable Flags
- Core Services Configuration
- Feature Flags Management (Unleash)
- Secret Management (External Secrets)
- Observability Configuration
- Advanced Configuration
- Deployment Process
- Post-Deployment Verification
- Troubleshooting
Quick Start
1. Set Up Your Deployment Directory
# Navigate to the peripheries example
cd kindo-modules/examples/kindo-peripheries-standalone
# Create your configuration
cp terraform.tfvars.example terraform.tfvars
# Edit your configuration
vi terraform.tfvars
2. Minimal Configuration
Here’s the absolute minimum you need in terraform.tfvars:
# Core configuration (REQUIRED)
project_name = "mycompany"
environment = "production"
aws_region = "us-west-2"
# Essential services (typically all enabled)
enable_alb_ingress = true # AWS Load Balancer Controller
enable_external_secrets = true # Sync secrets from AWS Secrets Manager
enable_unleash = true # Feature flags
enable_unleash_edge = true # Feature flag edge proxy
enable_external_dns = true # Automatic DNS management
# Observability (choose based on your setup)
enable_otel_collector_cr = true # If using AWS ADOT addon
# OR
# enable_otel_collector = true # If using standalone OTel
3. Deploy
# Set your AWS profile if using AWS ingress
export AWS_PROFILE=your-aws-profile
# Initialize Terraform
terraform init
# Review the plan
terraform plan
# Deploy
terraform apply
Understanding the Peripheries Module
What the Module Does
The kindo-peripheries module deploys supporting Kubernetes services that Kindo applications depend on:
- Traffic Management: ALB Ingress Controller for load balancing
- Secret Synchronization: External Secrets Operator for AWS Secrets Manager integration
- Feature Flags: Unleash and Unleash Edge for feature management
- DNS Management: External DNS for automatic Route53 updates
- Observability: OpenTelemetry Collector for metrics and traces
- PII Detection: Presidio for data anonymization (optional)
Module Architecture
Infrastructure (EKS, RDS, etc.) ─┐
├─> Peripheries Module ─> Kubernetes Services
Secrets (AWS Secrets Manager) ───┘ │
└─> Ready for Apps
How It Consumes Other Modules
The peripheries module automatically consumes outputs from:
- Infrastructure Module: IAM roles, cluster info, domain names
- Secrets Module: Generated tokens and credentials
Service Enable/Disable Flags
Understanding Each Service
Configure which services to deploy in your terraform.tfvars:
# Traffic Management
enable_alb_ingress = true
What this does:
- Deploys AWS Load Balancer Controller
- Manages Application Load Balancers for Ingress resources
- Required for external traffic routing
- When to disable: Only if using a different ingress controller (nginx, traefik)
# Secret Management
enable_external_secrets = true
What this does:
- Deploys External Secrets Operator
- Syncs secrets from AWS Secrets Manager to Kubernetes
- Creates ClusterSecretStores for app access
- When to disable: If managing secrets differently (sealed-secrets, vault-injector)
# Feature Flags
enable_unleash = true
enable_unleash_edge = true
What these do:
- Unleash: Central feature flag management server
- Unleash Edge: Edge proxy for faster flag evaluation
- Enables runtime feature toggling without deployments
- When to disable: If not using feature flags or using a different system
# DNS Management
enable_external_dns = true
What this does:
- Automatically creates Route53 records for services
- Updates DNS when services change
- Works with annotations on Ingress/Service resources
- When to disable: If manually managing DNS or using different DNS provider
# Observability
enable_otel_collector_cr = true # For AWS ADOT
# OR
enable_otel_collector = false # For standalone
What these do:
- otel_collector_cr: Creates config for AWS ADOT EKS addon
- otel_collector: Deploys standalone OpenTelemetry Collector
- Collects and exports traces, metrics, logs
- When to disable: If using different observability stack (Datadog, New Relic)
# PII Detection
enable_presidio = false
What this does:
- Deploys Microsoft Presidio for PII detection
- Provides APIs for data anonymization
- When to enable: Required
Core Services Configuration
ALB Ingress Controller
The ALB Ingress Controller manages AWS Application Load Balancers for your services:
enable_alb_ingress = true
# Optional: Pin to specific version
# alb_ingress_version = "1.7.1"
What gets configured automatically:
- IAM role with permissions (from infrastructure module)
- Service account with IRSA (IAM Roles for Service Accounts)
- Webhook certificates
- IngressClass named ‘alb’
How applications use it:
# In your app's Ingress resourcemetadata:
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
External Secrets Operator
Syncs secrets from AWS Secrets Manager to Kubernetes:
enable_external_secrets = true
# Configure secret stores
external_secrets_stores = {
"aws-secrets-manager" = {
provider = "aws"
config = {
service = "SecretsManager"
region = "us-west-2" # Your AWS region
}
}
# Optional: Add other secret backends
# "vault" = {
# provider = "vault"
# config = {
# server = "https://vault.example.com"
# path = "secret"
# }
# }
}
What gets created:
- External Secrets Operator deployment
- ClusterSecretStore for each configured backend
- IRSA for AWS Secrets Manager access
How applications use it:
The application Helm charts create ExternalSecret resources that reference these ClusterSecretStores.
Feature Flags Management (Unleash)
Basic Configuration
enable_unleash = true
enable_unleash_edge = true
# Import initial feature flags (first deployment only!)
import_feature_flags = true # Set to false after initial import
unleash_import_project = "default"
unleash_import_environment = "development"
Authentication Tokens
Default behavior
- By default, this module consumes Unleash credentials from the kindo-secrets outputs when they are available. If an expected value is missing, a secure token/password will be generated only for that specific item. When store_secrets_in_aws is true, generated values are persisted to AWS Secrets Manager so they can be read on subsequent runs.
Example references when wiring modules explicitly:
# If you expose outputs from kindo-secrets, the peripheries module will use them
# You can pass them explicitly, or rely on composition wiring in your root module
unleash_admin_password = module.kindo_secrets.unleash_admin_password # optional if wired implicitly
unleash_admin_token = module.kindo_secrets.unleash_admin_token # optional if wired implicitly
unleash_client_token = module.kindo_secrets.unleash_client_token # optional if wired implicitly
unleash_frontend_token = module.kindo_secrets.unleash_frontend_token # optional if wired implicitly
Option 1: Use kindo-secrets outputs (default)
# No manual values required when kindo-secrets outputs are present.
# The module will read them automatically and skip generation for those keys.
Option 2: Auto-generate tokens
# Leave these unset — secure random tokens will be generated for any values
# not provided by kindo-secrets outputs
# unleash_admin_password =
# unleash_admin_token =
# unleash_client_token =
# unleash_frontend_token =
Option 3: Provide your own tokens
unleash_admin_password = "your-secure-admin-password"
unleash_admin_token = "*:*.your-admin-token-here"
unleash_client_token = "default:development.your-client-token"
unleash_frontend_token = "*:development.your-frontend-token"
Token formats explained:
- Admin token:
*:*.{token}— Full API access to all projects and environments - Client token:
{project}:{environment}.{token}— Read-only for a specific project and environment - Frontend token:
*:{environment}.{token}— Frontend access for all projects in an environment
Feature Flags Import
To import existing feature flags from feature_flags.json:
# WARNING: Only enable for initial deployment
import_feature_flags = true
# After successful import, update to:
# import_feature_flags = false
Important: Disable after first import to prevent re-importing on every pod restart!
Storing Unleash Tokens
# Store generated tokens in AWS Secrets Manager
store_secrets_in_aws = true
This creates secrets:
{project}-{environment}-unleash-tokens- Contains all generated Unleash authentication tokens
Secret Management (External Secrets)
AWS Secrets Manager Integration
enable_external_secrets = true
external_secrets_stores = {
"aws-secrets-manager" = {
provider = "aws"
config = {
service = "SecretsManager"
region = "us-west-2"
}
}
}
Multiple Secret Backends
You can configure multiple secret stores:
external_secrets_stores = {
# AWS Secrets Manager
"aws-secrets-manager" = {
provider = "aws"
config = {
service = "SecretsManager"
region = "us-west-2"
}
}
# HashiCorp Vault
"vault" = {
provider = "vault"
config = {
server = "https://vault.example.com"
path = "secret"
# Auth handled via Kubernetes service account
}
}
# Doppler
"doppler" = {
provider = "doppler"
config = {
auth_secret_name = "doppler-token-secret"
# Token must be pre-created as Kubernetes secret
}
}
}
How Applications Use External Secrets
Applications don’t need to know about the secret backend. They create ExternalSecret resources:
# Created by app Helm charts
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
spec: secretStore
Ref:
name: aws-secrets-manager # References ClusterSecretStore
kind: ClusterSecretStore
target:
name: app-secrets
# Creates this Kubernetes secret
data:
- secretKey: config
remoteRef:
key: mycompany-production-api-config
Observability Configuration
AWS Distro for OpenTelemetry (ADOT)
If using the ADOT EKS addon:
# Use Custom Resource for ADOT addon
enable_otel_collector_cr = true
# The addon must be installed on your EKS cluster
# This creates an OpenTelemetryCollector CR to configure it
What this configures:
- Receivers: OTLP (gRPC and HTTP)
- Processors: Batch, memory limiter
- Exporters: AWS X-Ray (traces), CloudWatch (metrics)
Standalone OpenTelemetry Collector
If not using ADOT:
# Deploy standalone collector
enable_otel_collector = true
# Optional: Custom configuration
# otel_collector_version = "0.74.0"
What this deploys:
- OpenTelemetry Collector as Kubernetes Deployment
- Service for OTLP ingestion
- ConfigMap with collector configuration
Choosing Between ADOT and Standalone
Use ADOT CR (enable_otel_collector_cr) |
Use Standalone (enable_otel_collector) |
|---|---|
| Running on AWS EKS | Running on other Kubernetes |
| Want AWS-native integration | Need custom exporters |
| Using X-Ray and CloudWatch | Using Datadog, New Relic, etc. |
| ADOT addon already installed | Want full control over config |
Advanced Configuration
Custom Helm Values
Override default Helm values for any component:
# In your main.tf, when calling the module
module "kindo_peripheries" {
source = "../../modules/kindo-peripheries"
peripheries_config = {
unleash = {
install = true
helm_chart_version = "5.4.3"
namespace = "unleash"
# Custom Helm values
values_content = file("${path.module}/custom-unleash-values.yaml")
# Dynamic value overrides
dynamic_helm_sets = {
"postgresql.enabled" = "false" # Use external database
"dbConfig.host" = "my-rds-instance.amazonaws.com"
}
# Sensitive value overrides
sensitive_helm_sets = {
"dbConfig.password" = var.unleash_db_password
}
}
}
}
Custom Namespaces
Deploy components to specific namespaces:
# In peripheries_config
alb_ingress = {
install = true
namespace = "ingress-system" # Custom namespace
create_namespace = true
}
unleash = {
install = true
namespace = "feature-flags" # Custom namespace
create_namespace = true
}
Version Pinning
Pin components to specific versions:
# Component versions
alb_ingress_version = "1.7.1"
unleash_version = "5.4.3"
unleash_edge_version = "3.0.0"
external_secrets_version = "0.9.9"
otel_collector_version = "0.74.0"
presidio_version = "2.1.95"
Domain Configuration
# Base domain for services
base_domain = "kindo.mycompany.com"
# This creates:
# - unleash.kindo.mycompany.com
# - app.kindo.mycompany.com
# - api.kindo.mycompany.com
Resource Tagging
# Additional tags for AWS resources
additional_tags = {
Team = "Platform"
CostCenter = "Engineering"
Compliance = "SOC2"
Environment = "production"
}
Deployment Process
1. Pre-Deployment Checklist
- Infrastructure deployed (
kindo-inframodule) - Secrets configured (
kindo-secretsmodule) - kubectl access configured
- terraform.tfvars completed
- feature_flags.json present (if importing)
2. Validate Configuration
# Check cluster connectivity
kubectl get nodes
# Validate Terraform configuration
terraform fmt
terraform validate
# Preview deployment
terraform plan
3. Deploy Peripheries
# Deploy all components
terraform apply
# For CI/CD
terraform apply -auto-approve
Deployment time:
- External Secrets Operator: 1-2 minutes
- ALB Ingress Controller: 2-3 minutes
- Unleash + PostgreSQL: 3-5 minutes
- Total: 5-10 minutes
4. Save Outputs
# Save outputs for application deployment
terraform output -json > peripheries-outputs.json
# Key outputs
terraform output unleash_url
terraform output unleash_tokens # If auto-generated
Post-Deployment Verification
1. Verify Core Services
# Check deploymentskubectl get deployments -A | grep -E "unleash|external-secrets|aws-load"# Expected output:# external-secrets external-secrets-operator 1/1# external-secrets external-secrets-webhook 1/1# kube-system aws-load-balancer-controller 2/2# unleash unleash 1/1# unleash unleash-edge 1/1
2. Verify External Secrets Operator
# Check ClusterSecretStoreskubectl get clustersecretstores
# Test secret synckubectl get externalsecrets -A# Check operator logskubectl logs -n external-secrets deployment/external-secrets-operator
3. Verify ALB Ingress Controller
# Check controllerkubectl get deployment -n kube-system aws-load-balancer-controller
# Verify webhookkubectl get validatingwebhookconfiguration aws-load-balancer-webhook
# Check for IngressClasskubectl get ingressclass alb
4. Verify Unleash
# Get Unleash URLUNLEASH_URL=$(terraform output -raw unleash_url)echo "Unleash UI: https://$UNLEASH_URL"# Check Unleash podskubectl get pods -n unleash
# Get admin password (if auto-generated)kubectl get secret -n unleash unleash-admin -o jsonpath='{.data.password}' | base64 -d# Test Unleash APIcurl -H "Authorization: $(terraform output -raw unleash_admin_token)" \ https://$UNLEASH_URL/api/admin/projects
5. Verify External DNS (if enabled)
# Check External DNS deploymentkubectl get deployment -n external-dns external-dns
# Check DNS records createdaws route53 list-resource-record-sets \ --hosted-zone-id $(terraform output -raw route53_zone_id) \ --query "ResourceRecordSets[?Type=='A' || Type=='CNAME'].[Name,Type]" \ --output table
6. Verify OpenTelemetry (if enabled)
For ADOT CR:
# Check OpenTelemetryCollector resourcekubectl get opentelemetrycollectors -A# Check ADOT addonkubectl get pods -n opentelemetry-operator-system
For standalone:
# Check collector deploymentkubectl get deployment -n otel otel-collector
# Test OTLP endpointkubectl port-forward -n otel service/otel-collector 4317:4317
# In another terminal:grpcurl -plaintext localhost:4317 list
7. Create Verification Summary
cat > peripheries-summary.md << EOF# Peripheries Deployment Summary## Deployment Information- **Date**: $(date)- **Project**: $(terraform output -raw project_name)- **Environment**: $(terraform output -raw environment)- **Region**: $(terraform output -raw aws_region)## Services Deployed$(kubectl get deployments -A | grep -E "unleash|external-secrets|aws-load" | awk '{print "- " $2 " (" $1 ")"}')## Access Points- **Unleash UI**: https://$(terraform output -raw unleash_url)- **Username**: admin- **Password**: Check AWS Secrets Manager or kubectl secret## Integration Points- **ClusterSecretStores**: $(kubectl get clustersecretstores -o name | wc -l) configured- **IngressClass**: alb available- **External DNS**: $([ "$(terraform output -raw enable_external_dns)" = "true" ] && echo "Active" || echo "Not configured")## Next Steps1. Deploy applications using kindo-applications module2. Configure application ExternalSecrets3. Set up application Ingress resources4. Configure feature flags in UnleashEOFecho "✅ Summary saved to peripheries-summary.md"
Troubleshooting
Common Issues and Solutions
1. External Secrets Not Syncing
Error: “SecretSyncError: could not get secret from provider”
Solution:
# Check IRSA configurationkubectl describe sa -n external-secrets external-secrets
# Verify IAM role trust policyaws iam get-role --role-name external-secrets-role
# Check operator logskubectl logs -n external-secrets deployment/external-secrets-operator
2. ALB Not Creating
Error: “Failed to create ALB”
Solution:
# Check controller logskubectl logs -n kube-system deployment/aws-load-balancer-controller
# Verify IAM permissionskubectl describe sa -n kube-system aws-load-balancer-controller
# Check subnets have proper tagsaws ec2 describe-subnets --subnet-ids <subnet-id>
3. Unleash Connection Issues
Error: “Unable to connect to database”
Solution:
# Check PostgreSQL podkubectl get pods -n unleash | grep postgres
# Check database credentialskubectl get secret -n unleash unleash-postgresql -o yaml
# Restart Unleash podkubectl rollout restart deployment -n unleash unleash
4. Feature Flags Not Importing
Error: “Import failed”
Solution:
# Ensure import is enabled (first deployment only)
import_feature_flags = true
# Check feature_flags.json exists
ls feature_flags.json
# After successful import, disable
import_feature_flags = false
5. DNS Records Not Creating
Error: “External DNS not creating records”
Solution:
# Check External DNS logskubectl logs -n external-dns deployment/external-dns
# Verify Route53 permissionskubectl describe sa -n external-dns external-dns
# Check domain ownershipterraform output route53_zone_id
Debugging Commands
# List all peripheries resourceskubectl get all -A | grep -E "unleash|external-secrets|aws-load|otel"# Check Helm releaseshelm list -A# Verify CRDs installedkubectl get crds | grep -E "external-secrets|elbv2|opentelemetry"# Check events for issueskubectl get events -A --sort-by='.lastTimestamp' | tail -20# Enable debug loggingexport TF_LOG=DEBUG
terraform apply
Next Steps
After successful peripheries deployment:
- Deploy Applications: Follow the Application Deployment Guide to deploy Kindo applications
- Configure Ingress: Set up application routing with ALB Ingress
- Set Up Feature Flags: Log into Unleash and configure feature toggles
- Monitor Services: Check observability dashboards if configured
Appendix: Complete terraform.tfvars Reference
# ===============================================================
# Complete Peripheries Configuration Reference
# ===============================================================
# Core Configuration (REQUIRED)
project_name = "mycompany"
environment = "production"
aws_region = "us-west-2"
# Service Enable/Disable Flags
enable_alb_ingress = true
enable_external_secrets = true
enable_unleash = true
enable_unleash_edge = true
enable_external_dns = true
enable_otel_collector = false
enable_otel_collector_cr = true
enable_presidio = false
# Domain Configuration
base_domain = "kindo.mycompany.com"
# Component Versions (optional - defaults provided)
alb_ingress_version = "1.7.1"
unleash_version = "5.4.3"
unleash_edge_version = "3.0.0"
external_secrets_version = "0.9.9"
otel_collector_version = "0.74.0"
presidio_version = "2.1.95"
# Unleash Configuration
import_feature_flags = false # Only true for initial deployment
unleash_import_project = "default"
unleash_import_environment = "development"
# Optional: Provide your own tokens
# unleash_admin_password = "secure-password"
# unleash_admin_token = "*:*.admin-token"
# unleash_client_token = "default:development.client-token"
# unleash_frontend_token = "*:development.frontend-token"
# External Secrets Configuration
external_secrets_stores = {
"aws-secrets-manager" = {
provider = "aws"
config = {
service = "SecretsManager"
region = "us-west-2"
}
}
}
# AWS Integration
store_secrets_in_aws = true
# Resource Tagging
additional_tags = {
Team = "Platform"
CostCenter = "Engineering"
Compliance = "SOC2"
}
# Registry Configuration (only if using private charts)
# registry_username = "username"
# registry_password = "password"
Remember: Start with the minimal configuration and add parameters as needed. Most services have sensible defaults that work out of the box.