diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/DEPLOYMENT_CHECKLIST.md b/beispiel-terraform/beispiel-k8s-cluster-afc/DEPLOYMENT_CHECKLIST.md new file mode 100644 index 0000000..4c8aa8f --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/DEPLOYMENT_CHECKLIST.md @@ -0,0 +1,474 @@ +# ✅ Deployment Checklist + +Diese Checkliste führt dich Schritt-für-Schritt durch das erste Deployment. + +## 📋 Pre-Deployment + +### 1. Lokale Umgebung + +- [ ] Terraform >= 1.9.0 installiert +- [ ] Azure CLI >= 2.0 installiert +- [ ] kubectl >= 1.28 installiert +- [ ] Docker >= 20.10 installiert +- [ ] PHP >= 8.2 installiert (für Laravel-Befehle) + +**Prüfen:** +```bash +terraform version +az version +kubectl version +docker version +php -v +``` + +### 2. Azure Zugriff + +- [ ] Azure Account vorhanden +- [ ] Subscription ID bekannt: `77677a80-2dea-493d-9867-f1c961b80fb3` +- [ ] Zugriff auf Resource Group: `trusted_ai_demo_rg` +- [ ] Zugriff auf AKS Cluster: `trai_k8s_cluster` + +**Prüfen:** +```bash +az login +az account set --subscription "77677a80-2dea-493d-9867-f1c961b80fb3" +az account show +az aks show --resource-group trusted_ai_demo_rg --name trai_k8s_cluster +``` + +## 🔧 Setup + +### 3. Azure Container Registry + +- [ ] ACR erstellt oder vorhanden +- [ ] ACR Name: `mylaravelregistry` (oder eigener Name) +- [ ] AKS hat Pull-Berechtigung für ACR + +**Erstellen:** +```bash +# ACR erstellen (falls noch nicht vorhanden) +az acr create \ + --resource-group trusted_ai_demo_rg \ + --name mylaravelregistry \ + --sku Basic \ + --location germanywestcentral + +# AKS Zugriff geben +az aks update \ + --resource-group trusted_ai_demo_rg \ + --name trai_k8s_cluster \ + --attach-acr mylaravelregistry + +# Login +az acr login --name mylaravelregistry +``` + +### 4. Docker Image + +- [ ] Docker Image gebaut +- [ ] Docker Image zu ACR gepusht +- [ ] Image Tag notiert + +**Bauen & Pushen:** +```bash +cd /Users/sebastianfrohlich/Herd/frontend + +# Bauen +docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.0 . + +# Pushen +docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.0 + +# Image Tag notieren: +IMAGE_TAG="mylaravelregistry.azurecr.io/laravel-app:v1.0.0" +``` + +### 5. Laravel Konfiguration + +- [ ] APP_KEY generiert +- [ ] APP_KEY notiert + +**Generieren:** +```bash +cd /Users/sebastianfrohlich/Herd/frontend +php artisan key:generate --show + +# Output: base64:xyz... +# Notieren für terraform.tfvars +``` + +### 6. Backup-Dateien + +- [ ] Database Backup vorhanden +- [ ] Backup-Pfad notiert + +**Prüfen:** +```bash +ls -lh /Users/sebastianfrohlich/Herd/frontend/backups/ + +# Wähle ein Backup, z.B.: +BACKUP_FILE="../backups/backup_backend_20251203_101741.dump" +``` + +## ⚙️ Terraform Konfiguration + +### 7. terraform.tfvars erstellen + +- [ ] `terraform.tfvars` aus Example kopiert +- [ ] Alle erforderlichen Werte eingetragen + +**Erstellen:** +```bash +cd /Users/sebastianfrohlich/Herd/frontend/terraform +cp terraform.tfvars.example terraform.tfvars +nano terraform.tfvars +``` + +**Erforderliche Werte:** + +```hcl +# ✅ Docker Image +docker_image = "mylaravelregistry.azurecr.io/laravel-app:v1.0.0" + +# ✅ Laravel APP_KEY +app_key = "base64:YOUR_GENERATED_KEY_HERE" + +# ✅ PostgreSQL Admin Password (sicher wählen!) +postgresql_admin_password = "YourVerySecurePassword123!" + +# ✅ Alert Email +alert_email_address = "your-email@example.com" + +# ✅ Database Restore (beim ersten Deployment) +db_restore_enabled = true +db_backup_file_path = "../backups/backup_backend_20251203_101741.dump" +``` + +**Optionale Werte:** + +```hcl +# Für Custom Domain +ingress_host = "app.yourdomain.com" + +# Für SSL/TLS (benötigt Domain) +ssl_enabled = true +ssl_issuer_email = "admin@yourdomain.com" + +# Ressourcen anpassen +app_replicas = 2 +postgresql_sku_name = "B_Standard_B1ms" +``` + +### 8. Konfiguration validieren + +- [ ] terraform.tfvars Syntax korrekt +- [ ] Alle Secrets/Passwörter sicher +- [ ] Backup-Pfad korrekt + +**Prüfen:** +```bash +cd terraform + +# Terraform init +terraform init + +# Validate +terraform validate + +# Sollte ausgeben: Success! The configuration is valid. +``` + +## 🚀 Deployment + +### 9. Deployment ausführen + +**Option A: Automatisches Script (Empfohlen)** + +- [ ] Deploy-Script ausgeführt + +```bash +cd /Users/sebastianfrohlich/Herd/frontend/terraform +./scripts/deploy.sh +``` + +Das Script führt automatisch aus: +1. ✅ Prerequisite Check +2. ✅ Azure Login Status +3. ✅ terraform.tfvars Check +4. ✅ Docker Image Check +5. ✅ APP_KEY Check +6. ✅ terraform init +7. ✅ terraform validate +8. ✅ terraform plan +9. ✅ terraform apply (nach Bestätigung) +10. ✅ kubectl konfigurieren +11. ✅ Pod Status prüfen + +**Option B: Manuell** + +- [ ] Terraform Plan erstellt +- [ ] Plan überprüft +- [ ] Terraform Apply ausgeführt + +```bash +cd terraform + +# Plan +terraform plan -out=tfplan + +# Plan überprüfen +# Sollte Resources anzeigen: +XX to add, ~0 to change, -0 to destroy + +# Apply +terraform apply tfplan + +# Outputs anzeigen +terraform output +terraform output deployment_instructions +``` + +### 10. kubectl konfigurieren + +- [ ] kubectl Credentials abgerufen +- [ ] Cluster-Zugriff getestet + +```bash +az aks get-credentials \ + --resource-group trusted_ai_demo_rg \ + --name trai_k8s_cluster \ + --overwrite-existing + +# Testen +kubectl get nodes +kubectl get namespaces +``` + +## 🔄 Database Restore + +### 11. Datenbank wiederherstellen + +**Option A: Automatisches Script (Empfohlen)** + +- [ ] Restore-Script ausgeführt +- [ ] Job Status geprüft + +```bash +cd /Users/sebastianfrohlich/Herd/frontend/terraform +./scripts/restore-db.sh + +# Script fragt nach: +# - Welches Backup-File? +# - Bestätigung: yes +# Zeigt dann Logs und Status +``` + +**Option B: Via Terraform** + +- [ ] `db_restore_enabled = true` in terraform.tfvars +- [ ] `terraform apply` ausgeführt + +```bash +# In terraform.tfvars: +db_restore_enabled = true +db_backup_file_path = "../backups/backup_backend_20251203_101741.dump" + +# Apply +terraform apply + +# Job Status prüfen +kubectl get jobs -n laravel-app +kubectl logs -n laravel-app -l job-type=database-restore -f +``` + +## ✅ Post-Deployment Checks + +### 12. Infrastruktur Status + +- [ ] Pods sind running +- [ ] Service ist erreichbar +- [ ] Ingress hat External IP +- [ ] Database ist connected + +**Prüfen:** +```bash +# Pods +kubectl get pods -n laravel-app +# Sollte: 2/2 Running anzeigen + +# Service +kubectl get svc -n laravel-app + +# Ingress +kubectl get svc ingress-nginx-controller -n ingress-nginx +# Sollte: EXTERNAL-IP anzeigen (dauert 5-10 Min) + +# Database +kubectl get secret laravel-app-db-credentials -n laravel-app +``` + +### 13. Application Health + +- [ ] Application ist erreichbar +- [ ] HTTP Status 200 oder 302 +- [ ] Keine Fehler in Logs + +**Prüfen:** +```bash +# URL abrufen +terraform output app_url + +# Beispiel Output: http://20.79.123.456 + +# Im Browser öffnen oder: +curl -I http://20.79.123.456 + +# Sollte: HTTP/1.1 200 OK oder 302 Found + +# Logs prüfen +kubectl logs -n laravel-app -l app=laravel-app -f + +# Sollte: Keine Errors zeigen +``` + +### 14. Database Connectivity + +- [ ] Database Restore Job completed +- [ ] Laravel kann auf DB zugreifen + +**Prüfen:** +```bash +# Restore Job Status +kubectl get jobs -n laravel-app +# Sollte: db-restore-* mit COMPLETIONS 1/1 + +# Job Logs +kubectl logs -n laravel-app -l job-type=database-restore + +# Sollte: "Database restore completed successfully!" enthalten + +# Laravel Migrations Status (in Pod) +POD_NAME=$(kubectl get pods -n laravel-app -l app=laravel-app -o jsonpath='{.items[0].metadata.name}') +kubectl exec -it -n laravel-app $POD_NAME -- php artisan migrate:status + +# Sollte: Migration table anzeigen +``` + +## 🎯 Finalisierung + +### 15. Monitoring Setup + +- [ ] Azure Monitor funktioniert +- [ ] Alert Rules aktiv +- [ ] Email Alerts konfiguriert + +**Prüfen:** +```bash +# Azure Portal öffnen +open https://portal.azure.com + +# Gehe zu: +# 1. Resource Group: trusted_ai_demo_rg +# 2. AKS Cluster: trai_k8s_cluster +# 3. Monitoring > Insights + +# Alert Rules prüfen: +az monitor metrics alert list \ + --resource-group trusted_ai_demo_rg +``` + +### 16. Dokumentation + +- [ ] LoadBalancer IP notiert +- [ ] Database Connection String notiert +- [ ] Terraform Outputs gespeichert + +**Notieren:** +```bash +# Alle Outputs anzeigen +terraform output + +# Spezifische Outputs +terraform output app_url +terraform output postgresql_server_fqdn +terraform output postgresql_connection_string + +# In Passwort-Manager oder sicheren Ort speichern! +``` + +### 17. Cleanup & Security + +- [ ] Sensitive Dateien nicht committed (terraform.tfvars) +- [ ] Passwörter sicher gespeichert +- [ ] .gitignore überprüft + +**Prüfen:** +```bash +# Git Status +git status + +# Sollte NICHT enthalten: +# - terraform.tfvars +# - *.tfstate +# - kubeconfig + +# Falls vorhanden: +git rm --cached terraform/terraform.tfvars +git rm --cached terraform/*.tfstate +``` + +## 🚦 Success Criteria + +Deployment ist erfolgreich wenn: + +- ✅ `kubectl get pods -n laravel-app` zeigt 2/2 Running +- ✅ `terraform output app_url` zeigt eine URL +- ✅ URL im Browser ist erreichbar +- ✅ Database Restore Job ist completed +- ✅ Keine Fehler in Pod Logs +- ✅ Ingress hat External IP + +## 🎉 Fertig! + +Deine Laravel-Anwendung läuft jetzt auf Azure Kubernetes Service! + +### Nächste Schritte: + +1. **Testen**: Funktionalität der Anwendung testen +2. **Monitoring**: Azure Monitor regelmäßig prüfen +3. **Backups**: PostgreSQL Backup-Strategy überprüfen +4. **Domain**: Custom Domain konfigurieren (optional) +5. **SSL**: Let's Encrypt aktivieren (optional) +6. **CI/CD**: GitHub Actions einrichten (optional) + +### Wichtige Commands: + +```bash +# Status prüfen +kubectl get all -n laravel-app + +# Logs anzeigen +kubectl logs -n laravel-app -l app=laravel-app -f + +# Shell in Pod +kubectl exec -it -n laravel-app -- /bin/sh + +# Port-forward für lokalen Zugriff +kubectl port-forward -n laravel-app svc/laravel-app 8080:80 + +# Terraform Outputs +terraform output deployment_instructions +``` + +## 📞 Support + +Bei Problemen: + +1. **Troubleshooting Guide**: [README.md#troubleshooting](README.md#troubleshooting) +2. **Kubernetes Events**: `kubectl get events -n laravel-app --sort-by='.lastTimestamp'` +3. **Pod Logs**: `kubectl logs -n laravel-app ` +4. **Terraform State**: `terraform show` + +--- + +**Deployment Time**: ~30 Minuten +**Status**: ✅ Ready for Production diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/GITHUB_ACTIONS_SETUP.md b/beispiel-terraform/beispiel-k8s-cluster-afc/GITHUB_ACTIONS_SETUP.md new file mode 100644 index 0000000..2df896b --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/GITHUB_ACTIONS_SETUP.md @@ -0,0 +1,410 @@ +# GitHub Actions CI/CD Setup + +Diese Anleitung zeigt, wie du die automatische Deployment-Pipeline mit GitHub Actions einrichtest. + +## 📋 Übersicht + +Die Pipeline führt automatisch folgende Schritte aus: + +1. **Build & Push**: Docker Image bauen und zu Azure Container Registry pushen +2. **Terraform Deploy**: Infrastruktur mit Terraform deployen +3. **Smoke Tests**: Basis-Tests nach Deployment ausführen +4. **Notifications**: Status-Benachrichtigungen + +## 🔐 Erforderliche GitHub Secrets + +Gehe zu deinem GitHub Repository → Settings → Secrets and variables → Actions → New repository secret + +### Azure Credentials + +```bash +# Azure Service Principal erstellen +az ad sp create-for-rbac \ + --name "github-actions-laravel-app" \ + --role contributor \ + --scopes /subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/trusted_ai_demo_rg \ + --sdk-auth + +# Output sieht so aus: +{ + "clientId": "xxx", + "clientSecret": "xxx", + "subscriptionId": "77677a80-2dea-493d-9867-f1c961b80fb3", + "tenantId": "xxx", + "activeDirectoryEndpointUrl": "https://login.microsoftonline.com", + "resourceManagerEndpointUrl": "https://management.azure.com/", + "activeDirectoryGraphResourceId": "https://graph.windows.net/", + "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/", + "galleryEndpointUrl": "https://gallery.azure.com/", + "managementEndpointUrl": "https://management.core.windows.net/" +} +``` + +**Secret Name**: `AZURE_CREDENTIALS` +**Secret Value**: Der gesamte JSON Output von oben + +### Azure Container Registry + +```bash +# ACR Credentials abrufen +az acr credential show --name mylaravelregistry + +# Output: +{ + "passwords": [ + { + "name": "password", + "value": "xxx" + }, + { + "name": "password2", + "value": "yyy" + } + ], + "username": "mylaravelregistry" +} +``` + +**Secret Name**: `ACR_USERNAME` +**Secret Value**: `mylaravelregistry` + +**Secret Name**: `ACR_PASSWORD` +**Secret Value**: Der Wert von `password` (oder `password2`) + +### Azure Subscription + +**Secret Name**: `AZURE_SUBSCRIPTION_ID` +**Secret Value**: `77677a80-2dea-493d-9867-f1c961b80fb3` + +### Laravel Application + +**Secret Name**: `LARAVEL_APP_KEY` +**Secret Value**: Generiere mit `php artisan key:generate --show` +**Beispiel**: `base64:abcdefgh12345...` + +### PostgreSQL Database + +**Secret Name**: `POSTGRESQL_ADMIN_USERNAME` +**Secret Value**: `pgadmin` + +**Secret Name**: `POSTGRESQL_ADMIN_PASSWORD` +**Secret Value**: Dein sicheres PostgreSQL Passwort + +### Ingress & SSL + +**Secret Name**: `INGRESS_HOST` +**Secret Value**: Deine Domain (z.B. `app.yourdomain.com`) oder leer lassen für IP-Zugriff + +**Secret Name**: `SSL_ISSUER_EMAIL` +**Secret Value**: Email für Let's Encrypt (z.B. `admin@yourdomain.com`) + +### Monitoring + +**Secret Name**: `ALERT_EMAIL` +**Secret Value**: Email für Azure Alerts + +## 📝 Secrets Checkliste + +- [ ] `AZURE_CREDENTIALS` (JSON von Service Principal) +- [ ] `ACR_USERNAME` (mylaravelregistry) +- [ ] `ACR_PASSWORD` (ACR password) +- [ ] `AZURE_SUBSCRIPTION_ID` (77677a80-2dea-493d-9867-f1c961b80fb3) +- [ ] `LARAVEL_APP_KEY` (base64:...) +- [ ] `POSTGRESQL_ADMIN_USERNAME` (pgadmin) +- [ ] `POSTGRESQL_ADMIN_PASSWORD` (sicheres Passwort) +- [ ] `INGRESS_HOST` (optional: deine Domain) +- [ ] `SSL_ISSUER_EMAIL` (optional: für SSL) +- [ ] `ALERT_EMAIL` (deine Email) + +## 🚀 Workflow Trigger + +### Automatisch bei Push + +Die Pipeline wird automatisch ausgeführt bei Push auf: +- `main` Branch (Staging Deployment) +- `production` Branch (Production Deployment) + +```bash +# Code ändern und committen +git add . +git commit -m "Update feature" + +# Push zu main für Staging +git push origin main + +# Push zu production für Production +git push origin production +``` + +### Manuell über GitHub UI + +1. Gehe zu deinem Repository auf GitHub +2. Klicke auf "Actions" Tab +3. Wähle "Deploy to Azure AKS" Workflow +4. Klicke auf "Run workflow" +5. Wähle Environment (staging/production) +6. Klicke auf "Run workflow" + +### Manuell über GitHub CLI + +```bash +# Installiere gh CLI (falls noch nicht vorhanden) +brew install gh # macOS +# oder: https://cli.github.com/ + +# Login +gh auth login + +# Workflow manuell triggern +gh workflow run deploy-azure.yml \ + --ref main \ + --field environment=staging + +# Workflow Status prüfen +gh run list --workflow=deploy-azure.yml + +# Logs anzeigen +gh run view --log +``` + +## 🔧 Workflow Konfiguration anpassen + +Die Workflow-Datei liegt in [.github/workflows/deploy-azure.yml](../.github/workflows/deploy-azure.yml). + +### Environment-spezifische Settings + +Im Workflow werden verschiedene Settings basierend auf dem Environment gesetzt: + +**Staging:** +- `app_debug = true` +- `app_replicas = 2` +- `postgresql_sku_name = "B_Standard_B1ms"` (Basic) +- `postgresql_storage_mb = 32768` (32 GB) +- `ssl_enabled = false` + +**Production:** +- `app_debug = false` +- `app_replicas = 3` +- `postgresql_sku_name = "GP_Standard_D2s_v3"` (General Purpose) +- `postgresql_storage_mb = 131072` (128 GB) +- `ssl_enabled = true` + +### Weitere Trigger hinzufügen + +```yaml +on: + push: + branches: + - main + - production + pull_request: + branches: + - main + schedule: + - cron: '0 2 * * 0' # Jeden Sonntag um 2 Uhr + workflow_dispatch: + # ... existing inputs +``` + +## 📊 Workflow Monitoring + +### In GitHub UI + +1. Gehe zu "Actions" Tab in deinem Repository +2. Siehst alle Workflow-Runs +3. Klicke auf einen Run für Details +4. Siehst Logs für jeden Job/Step + +### Via GitHub CLI + +```bash +# Aktuelle Runs anzeigen +gh run list --workflow=deploy-azure.yml + +# Spezifischen Run anzeigen +gh run view + +# Logs anzeigen +gh run view --log + +# Run erneut starten +gh run rerun + +# Run abbrechen +gh run cancel +``` + +## 🐛 Troubleshooting + +### Pipeline schlägt bei "Build and Push" fehl + +**Problem**: ACR Authentication fehlgeschlagen + +**Lösung**: +```bash +# Prüfe ACR Credentials +az acr credential show --name mylaravelregistry + +# Update GitHub Secrets mit neuen Credentials +``` + +### Pipeline schlägt bei "Terraform Deploy" fehl + +**Problem**: Azure Credentials ungültig + +**Lösung**: +```bash +# Service Principal neu erstellen +az ad sp create-for-rbac \ + --name "github-actions-laravel-app" \ + --role contributor \ + --scopes /subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/trusted_ai_demo_rg \ + --sdk-auth + +# AZURE_CREDENTIALS Secret updaten +``` + +**Problem**: Terraform State Lock + +**Lösung**: +```bash +# State Lock manuell entfernen +cd terraform +terraform force-unlock +``` + +### Pipeline schlägt bei "Smoke Tests" fehl + +**Problem**: Pods nicht ready + +**Lösung**: +```bash +# kubectl credentials abrufen +az aks get-credentials \ + --resource-group trusted_ai_demo_rg \ + --name trai_k8s_cluster + +# Pod Status prüfen +kubectl get pods -n laravel-app +kubectl describe pod -n laravel-app +kubectl logs -n laravel-app +``` + +## 🔒 Sicherheit + +### Service Principal Permissions + +Der Service Principal benötigt folgende Berechtigungen: +- Contributor auf Resource Group +- AcrPush auf Container Registry (optional, wenn über ACR credentials) + +```bash +# Berechtigungen prüfen +az role assignment list \ + --assignee \ + --resource-group trusted_ai_demo_rg +``` + +### Secrets Rotation + +Rotiere Secrets regelmäßig: + +```bash +# Neues ACR Password generieren +az acr credential renew \ + --name mylaravelregistry \ + --password-name password + +# Service Principal Secret erneuern +az ad sp credential reset \ + --id +``` + +## 🚦 Branch Protection Rules + +Empfohlene Branch Protection Rules für `main` und `production`: + +1. Gehe zu Repository → Settings → Branches +2. Füge Branch Protection Rule hinzu für `main` und `production` +3. Aktiviere: + - ✅ Require status checks to pass before merging + - ✅ Require branches to be up to date before merging + - ✅ Require deployments to succeed before merging + - ✅ Require conversation resolution before merging + - ✅ Include administrators + +## 📈 Deployment Environments + +GitHub Environments für bessere Kontrolle: + +1. Gehe zu Repository → Settings → Environments +2. Erstelle zwei Environments: `staging` und `production` +3. Für `production`: + - ✅ Required reviewers: Füge Reviewer hinzu + - ✅ Wait timer: 5 minutes + - ✅ Deployment branches: Only `production` branch + +## 🔄 CI/CD Best Practices + +### 1. Feature Branch Workflow + +```bash +# Feature branch erstellen +git checkout -b feature/new-feature + +# Änderungen committen +git add . +git commit -m "Add new feature" + +# Push und Pull Request erstellen +git push origin feature/new-feature + +# Nach Review: Merge in main (automatisches Staging Deployment) +# Dann: Merge in production (automatisches Production Deployment) +``` + +### 2. Semantic Versioning + +Verwende Git Tags für Releases: + +```bash +# Tag erstellen +git tag -a v1.0.0 -m "Release version 1.0.0" +git push origin v1.0.0 + +# Workflow wird Docker Image mit diesem Tag bauen +``` + +### 3. Rollback Strategy + +```bash +# Bei Problemen: Zu vorheriger Version zurück +gh workflow run deploy-azure.yml \ + --ref \ + --field environment=production + +# Oder: Kubernetes Rollback +kubectl rollout undo deployment/laravel-app -n laravel-app +``` + +## 📚 Weitere Ressourcen + +- [GitHub Actions Documentation](https://docs.github.com/en/actions) +- [Azure DevOps Documentation](https://docs.microsoft.com/en-us/azure/devops/) +- [Terraform Cloud](https://www.terraform.io/cloud) - Alternative für Terraform State Management + +## 🎯 Nächste Schritte + +Nach Setup der CI/CD Pipeline: + +1. [ ] Teste Pipeline mit Dummy-Commit +2. [ ] Erstelle Feature Branch und PR +3. [ ] Richte Branch Protection Rules ein +4. [ ] Konfiguriere GitHub Environments +5. [ ] Dokumentiere Team-Workflow +6. [ ] Teste Rollback-Prozess +7. [ ] Richte Slack/Teams Notifications ein (optional) + +--- + +**Support**: Bei Problemen prüfe GitHub Actions Logs und Terraform State diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/QUICKSTART.md b/beispiel-terraform/beispiel-k8s-cluster-afc/QUICKSTART.md new file mode 100644 index 0000000..c4e6329 --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/QUICKSTART.md @@ -0,0 +1,398 @@ +# 🚀 Quickstart Guide - Laravel auf Azure AKS + +Diese Anleitung führt dich in ~30 Minuten durch das komplette Deployment deiner Laravel-Anwendung auf Azure Kubernetes Service. + +## ✅ Voraussetzungen Check + +```bash +# Prüfe ob alle Tools installiert sind +terraform version # >= 1.9.0 +az version # >= 2.0 +kubectl version # >= 1.28 +docker version # >= 20.10 +php -v # >= 8.2 +``` + +## 📝 Schritt-für-Schritt Anleitung + +### 1️⃣ Azure Login (2 Min) + +```bash +# Login +az login + +# Subscription setzen +az account set --subscription "77677a80-2dea-493d-9867-f1c961b80fb3" + +# Verify +az account show +``` + +### 2️⃣ Container Registry Setup (5 Min) + +```bash +# ACR erstellen (wenn nicht vorhanden) +az acr create \ + --resource-group trusted_ai_demo_rg \ + --name mylaravelregistry \ + --sku Basic \ + --location germanywestcentral + +# AKS Zugriff auf ACR geben +az aks update \ + --resource-group trusted_ai_demo_rg \ + --name trai_k8s_cluster \ + --attach-acr mylaravelregistry + +# Login +az acr login --name mylaravelregistry +``` + +### 3️⃣ Docker Image Build & Push (5 Min) + +```bash +# Zum Projekt-Root wechseln +cd /Users/sebastianfrohlich/Herd/frontend + +# Image bauen +docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.0 . + +# Image pushen +docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.0 +``` + +### 4️⃣ Laravel APP_KEY generieren (1 Min) + +```bash +# Im Projekt-Root +php artisan key:generate --show + +# Beispiel Output: base64:abcdefgh12345... +# Kopiere diesen Wert für den nächsten Schritt +``` + +### 5️⃣ Terraform Konfiguration (5 Min) + +```bash +# Zu Terraform wechseln +cd terraform + +# Konfiguration kopieren +cp terraform.tfvars.example terraform.tfvars + +# Bearbeiten +nano terraform.tfvars +``` + +**Wichtige Werte in `terraform.tfvars` ändern:** + +```hcl +# Docker Image (von Schritt 3) +docker_image = "mylaravelregistry.azurecr.io/laravel-app:v1.0.0" + +# Laravel Key (von Schritt 4) +app_key = "base64:abcdefgh12345..." + +# PostgreSQL Password (sicheres Passwort wählen!) +postgresql_admin_password = "YourSecurePassword123!" + +# Email für Alerts +alert_email_address = "your-email@example.com" +``` + +### 6️⃣ Deployment ausführen (10 Min) + +```bash +# Automatisches Deployment mit Script +./scripts/deploy.sh +``` + +Das Script führt automatisch aus: +- ✅ Prerequisite-Check +- ✅ Terraform init +- ✅ Terraform validate +- ✅ Terraform plan +- ✅ Terraform apply (nach Bestätigung) +- ✅ kubectl konfigurieren +- ✅ Pod-Status prüfen + +**Oder manuell:** + +```bash +# Init +terraform init + +# Plan +terraform plan -out=tfplan + +# Apply +terraform apply tfplan + +# kubectl konfigurieren +az aks get-credentials \ + --resource-group trusted_ai_demo_rg \ + --name trai_k8s_cluster \ + --overwrite-existing +``` + +### 7️⃣ Datenbank wiederherstellen (5 Min) + +```bash +# Automatisches Restore-Script +./scripts/restore-db.sh + +# Script fragt: +# - Welches Backup-File? (z.B. backup_backend_20251203_101741.dump) +# - Bestätigung: yes +# - Zeigt Fortschritt und Logs +``` + +**Oder in terraform.tfvars setzen und nochmal apply:** + +```hcl +db_restore_enabled = true +db_backup_file_path = "../backups/backup_backend_20251203_101741.dump" +``` + +```bash +terraform apply +``` + +### 8️⃣ Anwendung testen (2 Min) + +```bash +# LoadBalancer IP abrufen +kubectl get svc ingress-nginx-controller -n ingress-nginx + +# Oder über Terraform Output +terraform output app_url + +# Example Output: +# app_url = "http://20.79.123.456" +``` + +🎉 **Öffne die URL im Browser!** + +### 9️⃣ Status prüfen + +```bash +# Pods prüfen +kubectl get pods -n laravel-app + +# Services prüfen +kubectl get svc -n laravel-app + +# Ingress prüfen +kubectl get ingress -n laravel-app + +# Logs anzeigen +kubectl logs -n laravel-app -l app=laravel-app -f + +# Database restore job prüfen (wenn aktiviert) +kubectl get jobs -n laravel-app +kubectl logs -n laravel-app -l job-type=database-restore +``` + +## 🔧 Häufige Probleme + +### Problem: Pods starten nicht (ImagePullBackOff) + +```bash +# Prüfe ob ACR-Integration funktioniert +az aks check-acr \ + --resource-group trusted_ai_demo_rg \ + --name trai_k8s_cluster \ + --acr mylaravelregistry.azurecr.io + +# Fix: ACR Integration neu setzen +az aks update \ + --resource-group trusted_ai_demo_rg \ + --name trai_k8s_cluster \ + --attach-acr mylaravelregistry +``` + +### Problem: LoadBalancer IP bleibt "Pending" + +```bash +# Prüfe Service Events +kubectl describe svc ingress-nginx-controller -n ingress-nginx + +# Warte 5-10 Minuten - Azure braucht Zeit für LoadBalancer Setup +kubectl get svc -n ingress-nginx -w +``` + +### Problem: Database Connection Failed + +```bash +# Prüfe PostgreSQL Firewall Rules +az postgres flexible-server firewall-rule list \ + --resource-group trusted_ai_demo_rg \ + --name + +# Prüfe Secrets +kubectl get secret laravel-app-db-credentials -n laravel-app -o yaml | grep DB_ +``` + +### Problem: 502 Bad Gateway + +```bash +# Prüfe ob Pods ready sind +kubectl get pods -n laravel-app + +# Prüfe Pod Logs +kubectl logs -n laravel-app + +# Teste direkt zum Service +kubectl port-forward -n laravel-app svc/laravel-app 8080:80 +# Dann: http://localhost:8080 +``` + +## 📊 Nützliche Befehle + +### Status-Übersicht + +```bash +# Alles auf einen Blick +kubectl get all -n laravel-app +kubectl get all -n ingress-nginx + +# Terraform Outputs +terraform output +``` + +### Logs anzeigen + +```bash +# Application Logs (alle Pods) +kubectl logs -n laravel-app -l app=laravel-app -f + +# Bestimmter Pod +kubectl logs -n laravel-app -f + +# Ingress Controller Logs +kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller -f +``` + +### Scale Up/Down + +```bash +# Mehr Replicas +kubectl scale deployment/laravel-app --replicas=3 -n laravel-app + +# Prüfen +kubectl get pods -n laravel-app +``` + +### Shell in Pod + +```bash +# Shell öffnen +kubectl exec -it -n laravel-app -- /bin/sh + +# Laravel Artisan in Pod ausführen +kubectl exec -it -n laravel-app -- php artisan migrate:status +kubectl exec -it -n laravel-app -- php artisan route:list +``` + +### Port-Forward für lokalen Zugriff + +```bash +# App direkt aufrufen (ohne LoadBalancer) +kubectl port-forward -n laravel-app svc/laravel-app 8080:80 + +# Dann: http://localhost:8080 +``` + +## 🔄 Updates deployen + +### Neue Version deployen + +```bash +# 1. Code ändern +# 2. Neues Image bauen +docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.1 . +docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.1 + +# 3. Deployment updaten (schnell) +kubectl set image deployment/laravel-app \ + laravel-app=mylaravelregistry.azurecr.io/laravel-app:v1.0.1 \ + -n laravel-app + +# Rollout Status prüfen +kubectl rollout status deployment/laravel-app -n laravel-app +``` + +### Rollback + +```bash +# Zur vorherigen Version zurück +kubectl rollout undo deployment/laravel-app -n laravel-app +``` + +## 🧹 Cleanup + +### Nur App löschen (DB behalten) + +```bash +terraform destroy -target=kubernetes_deployment.app +terraform destroy -target=kubernetes_service.app +``` + +### Alles löschen + +```bash +terraform destroy +``` + +## 📚 Weitere Informationen + +- Ausführliche Dokumentation: [README.md](README.md) +- Terraform Outputs: `terraform output` +- Azure Portal: https://portal.azure.com + +## 🆘 Hilfe benötigt? + +```bash +# Terraform Outputs anzeigen +terraform output deployment_instructions + +# Kubernetes Events prüfen +kubectl get events -n laravel-app --sort-by='.lastTimestamp' + +# Resource Status +kubectl describe deployment laravel-app -n laravel-app +kubectl describe svc laravel-app -n laravel-app +``` + +## ✨ Next Steps + +Nach erfolgreichem Deployment: + +1. **Domain konfigurieren** (optional): + ```hcl + # In terraform.tfvars + ingress_host = "app.yourdomain.com" + ssl_enabled = true + ssl_issuer_email = "admin@yourdomain.com" + ``` + + ```bash + terraform apply + ``` + +2. **Monitoring einrichten**: Prüfe Azure Monitor in Azure Portal + +3. **CI/CD Pipeline**: Erstelle GitHub Actions oder Azure DevOps Pipeline + +4. **Backups testen**: Teste Restore-Prozess + +5. **Security Hardening**: + - VNet Integration für PostgreSQL + - Private Endpoints + - RBAC konfigurieren + +--- + +**Deployment Time:** ~30 Minuten +**Kosten:** ~50-100€/Monat (abhängig von Ressourcen) +**Scaling:** Horizontal (mehr Pods) & Vertikal (größere VMs) diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/README.md b/beispiel-terraform/beispiel-k8s-cluster-afc/README.md new file mode 100644 index 0000000..39d7395 --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/README.md @@ -0,0 +1,841 @@ +# Laravel Application Deployment auf Azure Kubernetes Service (AKS) + +Diese Terraform-Konfiguration stellt deine Laravel-Anwendung auf Azure Kubernetes Service (AKS) bereit und erstellt eine Azure PostgreSQL Datenbank. + +## 📋 Inhaltsverzeichnis + +- [Voraussetzungen](#voraussetzungen) +- [Architektur-Übersicht](#architektur-übersicht) +- [Schnellstart](#schnellstart) +- [Detaillierte Anleitung](#detaillierte-anleitung) +- [Konfiguration](#konfiguration) +- [Deployment](#deployment) +- [Datenbank-Wiederherstellung](#datenbank-wiederherstellung) +- [Troubleshooting](#troubleshooting) +- [Wartung](#wartung) + +## Voraussetzungen + +Folgende Tools müssen installiert sein: + +- [Terraform](https://www.terraform.io/downloads.html) >= 1.9.0 +- [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) >= 2.0 +- [kubectl](https://kubernetes.io/docs/tasks/tools/) >= 1.28 +- [Docker](https://docs.docker.com/get-docker/) >= 20.10 +- [PHP](https://www.php.net/downloads) >= 8.2 (für lokale Laravel-Befehle) + +## Architektur-Übersicht + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Azure Cloud │ +│ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Resource Group: trusted_ai_demo_rg │ │ +│ │ │ │ +│ │ ┌──────────────────────────────────────────────────┐ │ │ +│ │ │ AKS Cluster: trai_k8s_cluster │ │ │ +│ │ │ │ │ │ +│ │ │ ┌────────────────────────────────────────────┐ │ │ │ +│ │ │ │ Namespace: laravel-app │ │ │ │ +│ │ │ │ │ │ │ │ +│ │ │ │ • Deployment (2+ Pods) │ │ │ │ +│ │ │ │ • Service (ClusterIP) │ │ │ │ +│ │ │ │ • ConfigMaps & Secrets │ │ │ │ +│ │ │ │ • Horizontal Pod Autoscaler │ │ │ │ +│ │ │ └────────────────────────────────────────────┘ │ │ │ +│ │ │ │ │ │ +│ │ │ ┌────────────────────────────────────────────┐ │ │ │ +│ │ │ │ Namespace: ingress-nginx │ │ │ │ +│ │ │ │ │ │ │ │ +│ │ │ │ • Nginx Ingress Controller │ │ │ │ +│ │ │ │ • LoadBalancer Service (Public IP) │ │ │ │ +│ │ │ └────────────────────────────────────────────┘ │ │ │ +│ │ └──────────────────────────────────────────────────┘ │ │ +│ │ │ │ +│ │ ┌──────────────────────────────────────────────────┐ │ │ +│ │ │ Azure PostgreSQL Flexible Server │ │ │ +│ │ │ │ │ │ +│ │ │ • PostgreSQL 16 │ │ │ +│ │ │ • 32 GB Storage │ │ │ +│ │ │ • Automated Backups (7 days) │ │ │ +│ │ └──────────────────────────────────────────────────┘ │ │ +│ └────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Komponenten: + +1. **AKS Cluster**: Bestehender Kubernetes-Cluster +2. **Laravel Application**: Containerisierte Laravel-App mit Nginx + PHP-FPM +3. **PostgreSQL Database**: Azure Database for PostgreSQL Flexible Server +4. **Nginx Ingress Controller**: Load Balancer für externen Zugriff +5. **Secrets Management**: Kubernetes Secrets für sensible Daten + +## Schnellstart + +### 1. Azure CLI Login + +```bash +az login +az account set --subscription "77677a80-2dea-493d-9867-f1c961b80fb3" +``` + +### 2. Docker Image erstellen und pushen + +Erstelle zuerst eine Azure Container Registry (falls noch nicht vorhanden): + +```bash +# Container Registry erstellen +az acr create \ + --resource-group trusted_ai_demo_rg \ + --name mylaravelregistry \ + --sku Basic \ + --location germanywestcentral + +# In Registry einloggen +az acr login --name mylaravelregistry +``` + +Dann baue und pushe das Docker Image: + +```bash +# Zurück zum Projekt-Root +cd /Users/sebastianfrohlich/Herd/frontend + +# Docker Image bauen +docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.0 . + +# Image pushen +docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.0 +``` + +### 3. Terraform konfigurieren + +```bash +cd terraform + +# Kopiere die Beispiel-Konfiguration +cp terraform.tfvars.example terraform.tfvars + +# Bearbeite terraform.tfvars +nano terraform.tfvars +``` + +Wichtige Werte in `terraform.tfvars`: + +```hcl +docker_image = "mylaravelregistry.azurecr.io/laravel-app:v1.0.0" +app_key = "base64:YOUR_GENERATED_KEY" # Generiere mit: php artisan key:generate --show +postgresql_admin_password = "YourSecurePassword123!" +alert_email_address = "your-email@example.com" +``` + +### 4. Deployment ausführen + +```bash +# Automatisches Deployment mit Script +./scripts/deploy.sh +``` + +Oder manuell: + +```bash +# Terraform initialisieren +terraform init + +# Plan überprüfen +terraform plan + +# Deployment ausführen +terraform apply +``` + +### 5. Datenbank wiederherstellen + +```bash +# Interaktives Restore-Script +./scripts/restore-db.sh +``` + +Oder manuell: + +```bash +# Setze db_restore_enabled = true in terraform.tfvars +terraform apply +``` + +## Detaillierte Anleitung + +### Schritt 1: Azure Container Registry (ACR) Setup + +Die Laravel-Anwendung muss als Docker Image bereitgestellt werden: + +```bash +# ACR erstellen (wenn nicht vorhanden) +az acr create \ + --resource-group trusted_ai_demo_rg \ + --name mylaravelregistry \ + --sku Basic + +# Admin-Zugriff aktivieren (optional, für einfachere Handhabung) +az acr update -n mylaravelregistry --admin-enabled true + +# Login credentials abrufen +az acr credential show --name mylaravelregistry + +# Docker login +az acr login --name mylaravelregistry +``` + +### Schritt 2: Docker Image Build & Push + +```bash +cd /Users/sebastianfrohlich/Herd/frontend + +# Image mit Tag bauen +docker build -t mylaravelregistry.azurecr.io/laravel-app:latest . +docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.0 . + +# Image pushen +docker push mylaravelregistry.azurecr.io/laravel-app:latest +docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.0 +``` + +### Schritt 3: AKS Pull-Berechtigung für ACR + +```bash +# AKS Pull-Berechtigung für ACR erteilen +az aks update \ + --resource-group trusted_ai_demo_rg \ + --name trai_k8s_cluster \ + --attach-acr mylaravelregistry +``` + +### Schritt 4: Laravel APP_KEY generieren + +```bash +cd /Users/sebastianfrohlich/Herd/frontend +php artisan key:generate --show +``` + +Kopiere den Output (z.B. `base64:xyz...`) in deine `terraform.tfvars`. + +### Schritt 5: Terraform Konfiguration + +Erstelle `terraform/terraform.tfvars`: + +```hcl +# Azure Configuration +subscription_id = "77677a80-2dea-493d-9867-f1c961b80fb3" +resource_group_name = "trusted_ai_demo_rg" +location = "germanywestcentral" +aks_cluster_name = "trai_k8s_cluster" + +# Application Configuration +app_name = "laravel-app" +app_namespace = "laravel-app" +app_env = "production" +app_debug = false +app_replicas = 2 + +# Docker Image +docker_image = "mylaravelregistry.azurecr.io/laravel-app:v1.0.0" + +# Laravel APP_KEY (generiert mit: php artisan key:generate --show) +app_key = "base64:YOUR_ACTUAL_KEY_HERE" + +# PostgreSQL Configuration +postgresql_admin_username = "pgadmin" +postgresql_admin_password = "YourVerySecurePassword123!" +postgresql_sku_name = "B_Standard_B1ms" +postgresql_storage_mb = 32768 +postgresql_version = "16" +postgresql_backup_retention_days = 7 + +# Ingress Configuration +ingress_enabled = true +ingress_host = "" # Leer lassen für IP-basierten Zugriff + +# SSL/TLS Configuration (optional, später aktivieren) +ssl_enabled = false +ssl_issuer_email = "" + +# Database Restore Configuration +db_restore_enabled = true +db_backup_file_path = "../backups/backup_backend_20251203_101741.dump" + +# Alert Configuration +alert_email_address = "your-email@example.com" +``` + +### Schritt 6: Terraform Deployment + +```bash +cd /Users/sebastianfrohlich/Herd/frontend/terraform + +# Terraform initialisieren +terraform init + +# Validierung +terraform validate + +# Plan erstellen und überprüfen +terraform plan -out=tfplan + +# Deployment ausführen +terraform apply tfplan + +# Outputs anzeigen +terraform output +terraform output deployment_instructions +``` + +### Schritt 7: kubectl konfigurieren + +```bash +# kubectl Credentials abrufen +az aks get-credentials \ + --resource-group trusted_ai_demo_rg \ + --name trai_k8s_cluster \ + --overwrite-existing + +# Cluster-Zugriff testen +kubectl get nodes + +# Pods prüfen +kubectl get pods -n laravel-app + +# Services prüfen +kubectl get svc -n laravel-app +kubectl get svc -n ingress-nginx +``` + +### Schritt 8: Anwendung testen + +```bash +# LoadBalancer IP abrufen +kubectl get svc ingress-nginx-controller -n ingress-nginx + +# External IP sollte angezeigt werden, z.B.: +# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) +# ingress-nginx-controller LoadBalancer 10.0.45.123 20.79.123.456 80:31234/TCP,443:31567/TCP + +# Anwendung im Browser öffnen: +# http:// +``` + +### HTTP Basic Authentication + +Die Anwendung ist mit HTTP Basic Auth geschützt: + +- **Benutzer**: `afc-user` +- **Passwort**: `vDZFrZ+*~4gW=3^?-,:8{ga=7M.o5k,G` + +Die Credentials sind in [`deploy/nginx/.htpasswd`](../deploy/nginx/.htpasswd) konfiguriert und werden während des Docker-Builds in das Image kopiert. + +### Aktueller Deployment-Status + +**Aktuell deployed**: Version `1.0.7` + +**Aktuelle Konfiguration**: + +- URL: `http://72.144.113.194/` +- Docker Image: `laravelappreg.azurecr.io/laravel-app:1.0.7` +- Replicas: 2 +- Datenbank: `laravel_app` (PostgreSQL 16) mit 3 Schemas +- HTTP Basic Auth: Aktiv + +**Letzte Änderungen (v1.0.7)**: + +- HTTP Basic Authentication hinzugefügt +- Nginx Redirect für Livewire JavaScript (`.min.js` → `.js`) +- Alle 3 Datenbank-Schemas nach `laravel_app` migriert +- Datenbankverbindungen in `config/database.php` aktualisiert +- Session-Driver auf `database` umgestellt (Multi-Pod Support) + +## Konfiguration + +### Umgebungsvariablen + +Die Laravel-Umgebungsvariablen werden über Kubernetes ConfigMaps und Secrets verwaltet: + +**ConfigMap** (`kubernetes_config_map.app_config`): +- APP_NAME, APP_ENV, APP_DEBUG, APP_URL +- Session, Cache, Queue, Mail Konfiguration + +**Secrets** (`kubernetes_secret.app_secrets`): +- APP_KEY + +**Database Secrets** (`kubernetes_secret.db_credentials`): +- DB_CONNECTION, DB_HOST, DB_PORT, DB_DATABASE +- DB_USERNAME, DB_PASSWORD + +### Resource Limits + +Standard-Einstellungen in `variables.tf`: + +```hcl +app_resources_requests_cpu = "100m" +app_resources_requests_memory = "256Mi" +app_resources_limits_cpu = "500m" +app_resources_limits_memory = "512Mi" +``` + +Diese können in `terraform.tfvars` überschrieben werden. + +### Horizontal Pod Autoscaling + +Die Anwendung skaliert automatisch basierend auf CPU und Memory: + +- Min Replicas: `app_replicas` (default: 2) +- Max Replicas: `app_replicas * 3` (default: 6) +- CPU Threshold: 80% +- Memory Threshold: 80% + +## Datenbank-Konfiguration + +### Datenbank-Struktur + +Die Anwendung verwendet **eine PostgreSQL-Datenbank** (`laravel_app`) mit **drei Schemas**: + +#### 1. **public** Schema (12 Tabellen) +Standard Laravel-Tabellen und Application-Daten: +- backend_data_pool, cache, cache_locks +- companies, failed_jobs, job_batches, jobs +- migrations, password_reset_tokens, sessions +- transactions, users + +**Connection**: `DB::connection('pgsql_second')` oder `DB::connection('pgsql')` + +#### 2. **backend** Schema (11 Tabellen) +Backend-spezifische Daten: +- entity_corporate_context, evidence_registry +- fallback_events, internal_context_blobs +- prompt_results, prompt_runs, prompt_templates, prompt_token_metrics +- sources, transaction_outputs, transactions + +**Connection**: `DB::connection('backend')` + +#### 3. **devbackend** Schema (13 Tabellen) +Development/Testing Backend-Daten: +- document_facts, entity_corporate_context +- evidence_registry, fallback_events +- internal_context_blobs, pdf_documents +- prompt_results, prompt_runs, prompt_templates, prompt_token_metrics +- sources, transaction_outputs, transactions + +**Connection**: `DB::connection('devbackend')` + +### Verwendung in Laravel + +```php +// Public Schema (Standard) +$users = DB::connection('pgsql')->table('users')->get(); +$users = DB::connection('pgsql_second')->table('users')->get(); + +// Backend Schema +$transactions = DB::connection('backend')->table('transactions')->get(); + +// DevBackend Schema +$documents = DB::connection('devbackend')->table('pdf_documents')->get(); +``` + +Alle Connections sind in [config/database.php](../config/database.php) konfiguriert und zeigen auf dieselbe Datenbank `laravel_app` mit unterschiedlichen `search_path` Einstellungen. + +## Datenbank-Wiederherstellung + +### Automatische Wiederherstellung während Deployment + +Die Datenbank-Wiederherstellung importiert alle drei Schemas in die `laravel_app` Datenbank: + +```bash +# Backups befinden sich in: +/Users/sebastianfrohlich/Herd/frontend/backups/ +- backup_public_20251203_101723.dump # Public Schema +- backup_backend_20251203_101741.dump # Backend Schema +- backup_devbackend_20251203_103857.dump # DevBackend Schema +``` + +Setze in `terraform.tfvars`: + +```hcl +db_restore_enabled = true +db_backup_file_path = "../backups/backup_backend_20251203_101741.dump" +``` + +Das Backup wird als Kubernetes Job ausgeführt. + +### Manuelle Wiederherstellung + +```bash +# Interaktives Script +./scripts/restore-db.sh + +# Script fragt nach: +# - Welche Backup-Datei verwendet werden soll +# - Bestätigung zum Überschreiben der Datenbank +# - Zeigt Fortschritt und Logs an +``` + +### Manuelles DB-Restore (kubectl) + +```bash +# ConfigMap mit Backup erstellen +kubectl create configmap db-restore-backup \ + --from-file=backup.dump=/Users/sebastianfrohlich/Herd/frontend/backups/backup_backend_20251203_101741.dump \ + -n laravel-app + +# Job aus db-restore.tf verwenden oder manuell erstellen +kubectl apply -f - < -n laravel-app + +# Pod-Logs anzeigen +kubectl logs -n laravel-app + +# Vorherige Pod-Logs (bei CrashLoopBackOff) +kubectl logs -n laravel-app --previous +``` + +### Datenbank-Verbindungsprobleme + +```bash +# Secrets prüfen +kubectl get secret laravel-app-db-credentials -n laravel-app -o yaml + +# PostgreSQL Server Firewall prüfen +az postgres flexible-server firewall-rule list \ + --resource-group trusted_ai_demo_rg \ + --name + +# Verbindung vom Pod aus testen +kubectl run -it --rm debug --image=postgres:16-alpine -n laravel-app -- \ + psql "postgresql://user:pass@host:5432/database" +``` + +### Ingress funktioniert nicht + +```bash +# Ingress Status prüfen +kubectl get ingress -n laravel-app + +# Ingress Controller Logs +kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller + +# LoadBalancer Service prüfen +kubectl get svc -n ingress-nginx + +# Ingress Events prüfen +kubectl describe ingress laravel-app-ingress -n laravel-app +``` + +### SSL/TLS Zertifikat-Probleme + +```bash +# cert-manager Pods prüfen +kubectl get pods -n cert-manager + +# Certificate Status prüfen +kubectl get certificate -n laravel-app + +# Certificate Details +kubectl describe certificate laravel-app-tls -n laravel-app + +# cert-manager Logs +kubectl logs -n cert-manager -l app=cert-manager +``` + +### Performance-Probleme + +```bash +# Resource Usage prüfen +kubectl top pods -n laravel-app +kubectl top nodes + +# HPA Status prüfen +kubectl get hpa -n laravel-app + +# Events prüfen +kubectl get events -n laravel-app --sort-by='.lastTimestamp' +``` + +## Wartung + +### Backups + +PostgreSQL Flexible Server erstellt automatische Backups: + +```bash +# Backup-Konfiguration prüfen +az postgres flexible-server show \ + --resource-group trusted_ai_demo_rg \ + --name \ + --query "{backupRetentionDays:backup.backupRetentionDays,geoRedundantBackup:backup.geoRedundantBackup}" + +# Manuelles Backup erstellen +az postgres flexible-server backup create \ + --resource-group trusted_ai_demo_rg \ + --name \ + --backup-name manual-backup-$(date +%Y%m%d) +``` + +### Updates + +#### Terraform Updates + +```bash +# Terraform Zustand aktualisieren +terraform refresh + +# Änderungen planen +terraform plan + +# Änderungen anwenden +terraform apply +``` + +#### Laravel Updates + +```bash +# Composer Dependencies aktualisieren +composer update + +# NPM Dependencies aktualisieren +npm update + +# Neues Image bauen und deployen +docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.2 . +docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.2 + +# Deployment aktualisieren +kubectl set image deployment/laravel-app \ + laravel-app=mylaravelregistry.azurecr.io/laravel-app:v1.0.2 \ + -n laravel-app +``` + +### Monitoring + +```bash +# Log Analytics Workspace +az monitor log-analytics workspace show \ + --resource-group trusted_ai_demo_rg \ + --workspace-name + +# Azure Monitor für Container +# Im Azure Portal: AKS Cluster > Monitoring > Insights + +# Prometheus Metrics (wenn aktiviert) +kubectl port-forward -n ingress-nginx \ + svc/ingress-nginx-controller-metrics 10254:10254 +# Dann: http://localhost:10254/metrics +``` + +### Cleanup + +```bash +# Einzelne Resources löschen +terraform destroy -target=kubernetes_deployment.app +terraform destroy -target=azurerm_postgresql_flexible_server.main + +# Alles löschen +terraform destroy + +# Namespace löschen (löscht alle Resources im Namespace) +kubectl delete namespace laravel-app +``` + +## Kosten-Optimierung + +### Development/Staging + +Für Dev/Staging Umgebungen kannst du Kosten sparen: + +```hcl +# terraform.tfvars für Staging +postgresql_sku_name = "B_Standard_B1ms" # Burstable tier +postgresql_storage_mb = 32768 # 32 GB +app_replicas = 1 # Weniger Replicas +aks_node_pool_min_count = 1 # Weniger Nodes +``` + +### Production + +Für Production empfohlen: + +```hcl +postgresql_sku_name = "GP_Standard_D2s_v3" # General Purpose +postgresql_storage_mb = 131072 # 128 GB +postgresql_backup_retention_days = 35 # Längere Retention +app_replicas = 3 # Mehr Replicas +``` + +## Sicherheit + +### Best Practices + +1. **Secrets Management**: + - Verwende Azure Key Vault für Production + - Rotiere Passwörter regelmäßig + - Verwende starke, generierte Passwörter + +2. **Network Security**: + - Aktiviere VNet Integration für PostgreSQL + - Verwende Private Endpoints + - Beschränke Firewall-Regeln + +3. **SSL/TLS**: + - Aktiviere SSL für Production: + ```hcl + ssl_enabled = true + ssl_issuer_email = "admin@yourdomain.com" + ingress_host = "app.yourdomain.com" + ``` + +4. **RBAC**: + - Verwende Kubernetes RBAC + - Minimale Berechtigungen für Service Accounts + +## Support und Kontakt + +Bei Fragen oder Problemen: + +1. Prüfe die [Troubleshooting](#troubleshooting) Sektion +2. Prüfe Kubernetes Events: `kubectl get events -n laravel-app` +3. Prüfe Logs: `kubectl logs -n laravel-app -l app=laravel-app` + +## Anhang + +### Nützliche Befehle + +```bash +# Alle Resources in einem Namespace anzeigen +kubectl get all -n laravel-app + +# Port-forward für lokalen Zugriff +kubectl port-forward -n laravel-app svc/laravel-app 8080:80 + +# Shell in einem Pod öffnen +kubectl exec -it -n laravel-app -- /bin/sh + +# ConfigMap/Secret bearbeiten +kubectl edit configmap laravel-app-config -n laravel-app + +# Resource Usage live monitoren +watch kubectl top pods -n laravel-app + +# Cluster Info +kubectl cluster-info +kubectl get nodes -o wide +``` + +### Terraform State Management + +Für Team-Arbeit solltest du Remote State verwenden: + +```hcl +# backend.hcl +resource_group_name = "trusted_ai_demo_rg" +storage_account_name = "tfstate" +container_name = "tfstate" +key = "laravel-app.terraform.tfstate" +``` + +```bash +# Backend initialisieren +terraform init -backend-config=backend.hcl +``` diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/db-restore.tf b/beispiel-terraform/beispiel-k8s-cluster-afc/db-restore.tf new file mode 100644 index 0000000..cf08299 --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/db-restore.tf @@ -0,0 +1,15 @@ +# Note: Database backup files are too large for ConfigMaps (>1MB limit) +# Use manual restore via kubectl after deployment instead: +# +# kubectl cp ../backups/backup_backend_20251203_101741.dump \ +# laravel-app/postgresql-0:/tmp/backup.dump +# +# kubectl exec -it -n laravel-app postgresql-0 -- \ +# pg_restore -U pgadmin -d laravel_app --clean --if-exists \ +# /tmp/backup.dump +# +# Or use the restore script: ./scripts/restore-db.sh + +# Disabled: Automatic database restore via Terraform +# The ConfigMap approach doesn't work for files >1MB +# Use manual restore after deployment (see comments above) diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/ingress.tf b/beispiel-terraform/beispiel-k8s-cluster-afc/ingress.tf new file mode 100644 index 0000000..3910fef --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/ingress.tf @@ -0,0 +1,230 @@ +# Install nginx-ingress-controller via Helm +resource "helm_release" "nginx_ingress" { + count = var.ingress_enabled ? 1 : 0 + + name = "ingress-nginx" + repository = "https://kubernetes.github.io/ingress-nginx" + chart = "ingress-nginx" + version = "4.11.3" + namespace = "ingress-nginx" + create_namespace = true + + set { + name = "controller.service.type" + value = "LoadBalancer" + } + + set { + name = "controller.service.annotations.service\\.beta\\.kubernetes\\.io/azure-load-balancer-health-probe-request-path" + value = "/healthz" + } + + set { + name = "controller.metrics.enabled" + value = "true" + } + + set { + name = "controller.podAnnotations.prometheus\\.io/scrape" + value = "true" + } + + set { + name = "controller.podAnnotations.prometheus\\.io/port" + value = "10254" + } + + # Resource limits + set { + name = "controller.resources.requests.cpu" + value = "100m" + } + + set { + name = "controller.resources.requests.memory" + value = "128Mi" + } + + set { + name = "controller.resources.limits.cpu" + value = "500m" + } + + set { + name = "controller.resources.limits.memory" + value = "512Mi" + } + + # Enable rate limiting + set { + name = "controller.config.limit-req-status-code" + value = "429" + } + + timeout = 600 +} + +# Wait for nginx-ingress to be ready and get LoadBalancer IP +data "kubernetes_service" "ingress_nginx" { + count = var.ingress_enabled ? 1 : 0 + + metadata { + name = "ingress-nginx-controller" + namespace = "ingress-nginx" + } + + depends_on = [ + helm_release.nginx_ingress + ] +} + +# Install cert-manager for SSL/TLS (optional) +resource "helm_release" "cert_manager" { + count = var.ssl_enabled ? 1 : 0 + + name = "cert-manager" + repository = "https://charts.jetstack.io" + chart = "cert-manager" + version = "v1.16.2" + namespace = "cert-manager" + create_namespace = true + + set { + name = "crds.enabled" + value = "true" + } + + set { + name = "global.leaderElection.namespace" + value = "cert-manager" + } + + timeout = 600 +} + +# ClusterIssuer for Let's Encrypt (production) +resource "kubernetes_manifest" "letsencrypt_prod" { + count = var.ssl_enabled && var.ssl_issuer_email != "" ? 1 : 0 + + manifest = { + apiVersion = "cert-manager.io/v1" + kind = "ClusterIssuer" + metadata = { + name = "letsencrypt-prod" + } + spec = { + acme = { + server = "https://acme-v02.api.letsencrypt.org/directory" + email = var.ssl_issuer_email + privateKeySecretRef = { + name = "letsencrypt-prod" + } + solvers = [ + { + http01 = { + ingress = { + class = "nginx" + } + } + } + ] + } + } + } + + depends_on = [ + helm_release.cert_manager + ] +} + +# ClusterIssuer for Let's Encrypt (staging) - for testing +resource "kubernetes_manifest" "letsencrypt_staging" { + count = var.ssl_enabled && var.ssl_issuer_email != "" ? 1 : 0 + + manifest = { + apiVersion = "cert-manager.io/v1" + kind = "ClusterIssuer" + metadata = { + name = "letsencrypt-staging" + } + spec = { + acme = { + server = "https://acme-staging-v02.api.letsencrypt.org/directory" + email = var.ssl_issuer_email + privateKeySecretRef = { + name = "letsencrypt-staging" + } + solvers = [ + { + http01 = { + ingress = { + class = "nginx" + } + } + } + ] + } + } + } + + depends_on = [ + helm_release.cert_manager + ] +} + +# Kubernetes Ingress for Laravel Application +resource "kubernetes_ingress_v1" "app" { + count = var.ingress_enabled ? 1 : 0 + + metadata { + name = "${var.app_name}-ingress" + namespace = kubernetes_namespace.app.metadata[0].name + labels = local.common_labels + + annotations = merge( + { + "kubernetes.io/ingress.class" = "nginx" + "nginx.ingress.kubernetes.io/rewrite-target" = "/" + "nginx.ingress.kubernetes.io/ssl-redirect" = var.ssl_enabled ? "true" : "false" + }, + var.ssl_enabled && var.ingress_host != "" ? { + "cert-manager.io/cluster-issuer" = "letsencrypt-prod" + } : {} + ) + } + + spec { + dynamic "tls" { + for_each = var.ssl_enabled && var.ingress_host != "" ? [1] : [] + content { + hosts = [var.ingress_host] + secret_name = "${var.app_name}-tls" + } + } + + rule { + host = var.ingress_host != "" ? var.ingress_host : null + + http { + path { + path = "/" + path_type = "Prefix" + + backend { + service { + name = kubernetes_service.app.metadata[0].name + port { + number = 80 + } + } + } + } + } + } + } + + depends_on = [ + helm_release.nginx_ingress, + kubernetes_service.app + ] +} diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/kubernetes.tf b/beispiel-terraform/beispiel-k8s-cluster-afc/kubernetes.tf new file mode 100644 index 0000000..27a9b9d --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/kubernetes.tf @@ -0,0 +1,274 @@ +# Kubernetes Namespace +resource "kubernetes_namespace" "app" { + metadata { + name = var.app_namespace + labels = merge( + local.common_labels, + { + name = var.app_namespace + } + ) + } +} + +# Kubernetes Secret for Database Connection +resource "kubernetes_secret" "db_credentials" { + metadata { + name = "${var.app_name}-db-credentials" + namespace = kubernetes_namespace.app.metadata[0].name + labels = local.common_labels + } + + data = { + # Primary connection uses DB_HOST2 variables (for pgsql_second connection) + DB_CONNECTION2 = "pgsql" + DB_HOST2 = "postgresql.${kubernetes_namespace.app.metadata[0].name}.svc.cluster.local" + DB_PORT2 = "5432" + DB_DATABASE2 = local.postgresql_db_name + DB_USERNAME2 = var.postgresql_admin_username + DB_PASSWORD2 = local.postgresql_admin_pass + + # Also set standard DB_ variables for pgsql connection (used by sessions) + DB_CONNECTION = "pgsql" + DB_HOST = "postgresql.${kubernetes_namespace.app.metadata[0].name}.svc.cluster.local" + DB_PORT = "5432" + DB_DATABASE = local.postgresql_db_name + DB_USERNAME = var.postgresql_admin_username + DB_PASSWORD = local.postgresql_admin_pass + } + + type = "Opaque" + + depends_on = [ + kubernetes_service.postgresql + ] +} + +# Kubernetes Secret for Laravel Application +resource "kubernetes_secret" "app_secrets" { + metadata { + name = "${var.app_name}-secrets" + namespace = kubernetes_namespace.app.metadata[0].name + labels = local.common_labels + } + + data = { + APP_KEY = var.app_key + } + + type = "Opaque" +} + +# Kubernetes ConfigMap for Laravel Application +resource "kubernetes_config_map" "app_config" { + metadata { + name = "${var.app_name}-config" + namespace = kubernetes_namespace.app.metadata[0].name + labels = local.common_labels + } + + data = { + APP_NAME = var.app_name + APP_ENV = var.app_env + APP_DEBUG = tostring(var.app_debug) + APP_URL = local.app_url + APP_LOCALE = "en" + APP_FALLBACK_LOCALE = "en" + APP_FAKER_LOCALE = "en_US" + LOG_CHANNEL = "stack" + LOG_STACK = "single" + LOG_LEVEL = "info" + SESSION_DRIVER = "database" + SESSION_CONNECTION = "pgsql" + SESSION_LIFETIME = "120" + SESSION_ENCRYPT = "false" + CACHE_STORE = "database" + QUEUE_CONNECTION = "database" + BROADCAST_CONNECTION = "log" + FILESYSTEM_DISK = "local" + MAIL_MAILER = "log" + VITE_APP_NAME = var.app_name + } +} + +# Kubernetes Deployment for Laravel Application +resource "kubernetes_deployment" "app" { + metadata { + name = var.app_name + namespace = kubernetes_namespace.app.metadata[0].name + labels = local.common_labels + } + + spec { + replicas = var.app_replicas + + selector { + match_labels = { + app = var.app_name + } + } + + template { + metadata { + labels = merge( + local.common_labels, + { + version = "latest" + } + ) + } + + spec { + image_pull_secrets { + name = "acr-secret" + } + + container { + name = var.app_name + image = var.docker_image + + port { + name = "http" + container_port = 80 + protocol = "TCP" + } + + env_from { + config_map_ref { + name = kubernetes_config_map.app_config.metadata[0].name + } + } + + env_from { + secret_ref { + name = kubernetes_secret.app_secrets.metadata[0].name + } + } + + env_from { + secret_ref { + name = kubernetes_secret.db_credentials.metadata[0].name + } + } + + resources { + requests = { + cpu = var.app_resources_requests_cpu + memory = var.app_resources_requests_memory + } + limits = { + cpu = var.app_resources_limits_cpu + memory = var.app_resources_limits_memory + } + } + + liveness_probe { + exec { + command = ["sh", "-c", "ps aux | grep -v grep | grep -q nginx && ps aux | grep -v grep | grep -q php-fpm"] + } + initial_delay_seconds = 30 + period_seconds = 10 + timeout_seconds = 5 + failure_threshold = 3 + } + + readiness_probe { + exec { + command = ["sh", "-c", "ps aux | grep -v grep | grep -q nginx && ps aux | grep -v grep | grep -q php-fpm"] + } + initial_delay_seconds = 10 + period_seconds = 5 + timeout_seconds = 3 + failure_threshold = 3 + } + } + + restart_policy = "Always" + } + } + + strategy { + type = "RollingUpdate" + rolling_update { + max_surge = "1" + max_unavailable = "0" + } + } + } + + depends_on = [ + kubernetes_secret.db_credentials, + kubernetes_secret.app_secrets, + kubernetes_config_map.app_config + ] +} + +# Kubernetes Service for Laravel Application +resource "kubernetes_service" "app" { + metadata { + name = var.app_name + namespace = kubernetes_namespace.app.metadata[0].name + labels = local.common_labels + } + + spec { + selector = { + app = var.app_name + } + + port { + name = "http" + port = 80 + target_port = 80 + protocol = "TCP" + } + + type = "ClusterIP" + } + + depends_on = [ + kubernetes_deployment.app + ] +} + +# Horizontal Pod Autoscaler +resource "kubernetes_horizontal_pod_autoscaler_v2" "app" { + metadata { + name = "${var.app_name}-hpa" + namespace = kubernetes_namespace.app.metadata[0].name + labels = local.common_labels + } + + spec { + scale_target_ref { + api_version = "apps/v1" + kind = "Deployment" + name = kubernetes_deployment.app.metadata[0].name + } + + min_replicas = var.app_replicas + max_replicas = var.app_replicas * 3 + + metric { + type = "Resource" + resource { + name = "cpu" + target { + type = "Utilization" + average_utilization = 80 + } + } + } + + metric { + type = "Resource" + resource { + name = "memory" + target { + type = "Utilization" + average_utilization = 80 + } + } + } + } +} diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/main.tf b/beispiel-terraform/beispiel-k8s-cluster-afc/main.tf new file mode 100644 index 0000000..6773c60 --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/main.tf @@ -0,0 +1,40 @@ +# Data sources for existing resources +data "azurerm_resource_group" "main" { + name = var.resource_group_name +} + +data "azurerm_kubernetes_cluster" "main" { + name = var.aks_cluster_name + resource_group_name = var.resource_group_name +} + +data "azurerm_subscription" "current" {} + +# Generate random suffix for unique resource names +resource "random_string" "suffix" { + length = 8 + special = false + upper = false +} + +# Generate random password for PostgreSQL if not provided +resource "random_password" "postgresql_admin_password" { + count = var.postgresql_admin_password == null ? 1 : 0 + length = 32 + special = true +} + +# Local variables +locals { + app_name_safe = replace(var.app_name, "_", "-") + postgresql_server_name = "${local.app_name_safe}-psql-${random_string.suffix.result}" + postgresql_db_name = replace(var.app_name, "-", "_") + postgresql_admin_pass = var.postgresql_admin_password != null ? var.postgresql_admin_password : random_password.postgresql_admin_password[0].result + app_url = var.app_url != "" ? var.app_url : (var.ingress_host != "" ? "https://${var.ingress_host}" : (var.ingress_enabled ? "http://${try(data.kubernetes_service.ingress_nginx[0].status[0].load_balancer[0].ingress[0].ip, "pending")}" : "ingress-not-enabled")) + + common_labels = { + app = var.app_name + environment = var.app_env + managed-by = "terraform" + } +} diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/outputs.tf b/beispiel-terraform/beispiel-k8s-cluster-afc/outputs.tf new file mode 100644 index 0000000..31e442a --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/outputs.tf @@ -0,0 +1,112 @@ +output "resource_group_name" { + description = "Name of the resource group" + value = data.azurerm_resource_group.main.name +} + +output "aks_cluster_name" { + description = "Name of the AKS cluster" + value = data.azurerm_kubernetes_cluster.main.name +} + +output "postgresql_service_name" { + description = "Name of the PostgreSQL service (in-cluster)" + value = kubernetes_service.postgresql.metadata[0].name +} + +output "postgresql_service_fqdn" { + description = "Internal FQDN of the PostgreSQL service (in-cluster)" + value = "postgresql.${kubernetes_namespace.app.metadata[0].name}.svc.cluster.local" +} + +output "postgresql_database_name" { + description = "Name of the PostgreSQL database" + value = local.postgresql_db_name +} + +output "postgresql_admin_username" { + description = "Administrator username for PostgreSQL" + value = var.postgresql_admin_username + sensitive = true +} + +output "postgresql_connection_string" { + description = "PostgreSQL connection string (from within cluster)" + value = "postgresql://${var.postgresql_admin_username}:${nonsensitive(local.postgresql_admin_pass)}@postgresql.${kubernetes_namespace.app.metadata[0].name}.svc.cluster.local:5432/${local.postgresql_db_name}" + sensitive = true +} + +output "app_namespace" { + description = "Kubernetes namespace for the application" + value = kubernetes_namespace.app.metadata[0].name +} + +output "app_service_name" { + description = "Kubernetes service name for the application" + value = kubernetes_service.app.metadata[0].name +} + +output "ingress_enabled" { + description = "Whether ingress is enabled" + value = var.ingress_enabled +} + +output "ingress_ip" { + description = "IP address of the ingress controller load balancer" + value = var.ingress_enabled ? try(data.kubernetes_service.ingress_nginx[0].status[0].load_balancer[0].ingress[0].ip, "pending") : "not enabled" +} + +output "app_url" { + description = "URL of the Laravel application" + value = var.ingress_enabled ? (var.ingress_host != "" ? "https://${var.ingress_host}" : "http://${try(data.kubernetes_service.ingress_nginx[0].status[0].load_balancer[0].ingress[0].ip, "pending")}") : "ingress not enabled" +} + +output "ssl_enabled" { + description = "Whether SSL/TLS is enabled" + value = var.ssl_enabled +} + +output "db_restore_info" { + description = "Database restore information" + value = "Database restore disabled in Terraform. Use manual restore via kubectl (see db-restore.tf for instructions)" +} + +output "deployment_instructions" { + description = "Next steps after deployment" + value = <<-EOT + ======================================== + Deployment Complete! + ======================================== + + 1. Get kubectl credentials: + az aks get-credentials --resource-group ${data.azurerm_resource_group.main.name} --name ${data.azurerm_kubernetes_cluster.main.name} + + 2. Check application status: + kubectl get pods -n ${kubernetes_namespace.app.metadata[0].name} + kubectl get svc -n ${kubernetes_namespace.app.metadata[0].name} + kubectl get ingress -n ${kubernetes_namespace.app.metadata[0].name} + + 3. Check database restore job (if enabled): + kubectl get jobs -n ${kubernetes_namespace.app.metadata[0].name} + kubectl logs -n ${kubernetes_namespace.app.metadata[0].name} -l job-type=database-restore + + 4. Access the application: + ${var.ingress_enabled ? (var.ingress_host != "" ? "https://${var.ingress_host}" : "http://${try(data.kubernetes_service.ingress_nginx[0].status[0].load_balancer[0].ingress[0].ip, "pending")}") : "ingress not enabled - use port-forward"} + + 5. Port-forward (if ingress not ready): + kubectl port-forward -n ${kubernetes_namespace.app.metadata[0].name} svc/${kubernetes_service.app.metadata[0].name} 8080:80 + + 6. View logs: + kubectl logs -n ${kubernetes_namespace.app.metadata[0].name} -l app=${var.app_name} -f + + 7. Database connection details (in-cluster): + Host: postgresql.${kubernetes_namespace.app.metadata[0].name}.svc.cluster.local + Database: ${local.postgresql_db_name} + Username: ${var.postgresql_admin_username} + + 8. Restore database manually: + kubectl cp ../backups/backup_backend_20251203_101741.dump ${kubernetes_namespace.app.metadata[0].name}/postgresql-0:/tmp/backup.dump + kubectl exec -it -n ${kubernetes_namespace.app.metadata[0].name} postgresql-0 -- pg_restore -U ${var.postgresql_admin_username} -d ${local.postgresql_db_name} --clean --if-exists /tmp/backup.dump + + ======================================== + EOT +} diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/postgresql-k8s.tf b/beispiel-terraform/beispiel-k8s-cluster-afc/postgresql-k8s.tf new file mode 100644 index 0000000..c5a2b57 --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/postgresql-k8s.tf @@ -0,0 +1,168 @@ +# PostgreSQL in Kubernetes Cluster +# Using StatefulSet with PersistentVolume for data persistence + +# Kubernetes Secret for PostgreSQL +resource "kubernetes_secret" "postgresql" { + metadata { + name = "postgresql-credentials" + namespace = kubernetes_namespace.app.metadata[0].name + labels = local.common_labels + } + + data = { + POSTGRES_USER = var.postgresql_admin_username + POSTGRES_PASSWORD = var.postgresql_admin_password + POSTGRES_DB = local.postgresql_db_name + } + + type = "Opaque" +} + +# PersistentVolumeClaim for PostgreSQL data +resource "kubernetes_persistent_volume_claim" "postgresql" { + metadata { + name = "postgresql-pvc" + namespace = kubernetes_namespace.app.metadata[0].name + labels = local.common_labels + } + + spec { + access_modes = ["ReadWriteOnce"] + + resources { + requests = { + storage = "10Gi" + } + } + } + + wait_until_bound = false +} + +# PostgreSQL StatefulSet +resource "kubernetes_stateful_set" "postgresql" { + metadata { + name = "postgresql" + namespace = kubernetes_namespace.app.metadata[0].name + labels = local.common_labels + } + + spec { + service_name = "postgresql" + replicas = 1 + + selector { + match_labels = { + app = "postgresql" + } + } + + template { + metadata { + labels = merge( + local.common_labels, + { + app = "postgresql" + } + ) + } + + spec { + container { + name = "postgresql" + image = "postgres:16-alpine" + + port { + container_port = 5432 + name = "postgresql" + } + + env_from { + secret_ref { + name = kubernetes_secret.postgresql.metadata[0].name + } + } + + volume_mount { + name = "postgresql-storage" + mount_path = "/var/lib/postgresql/data" + sub_path = "postgres" + } + + resources { + requests = { + cpu = "250m" + memory = "512Mi" + } + limits = { + cpu = "1000m" + memory = "1Gi" + } + } + + liveness_probe { + exec { + command = ["pg_isready", "-U", var.postgresql_admin_username] + } + initial_delay_seconds = 30 + period_seconds = 10 + timeout_seconds = 5 + failure_threshold = 3 + } + + readiness_probe { + exec { + command = ["pg_isready", "-U", var.postgresql_admin_username] + } + initial_delay_seconds = 5 + period_seconds = 5 + timeout_seconds = 3 + failure_threshold = 3 + } + } + + volume { + name = "postgresql-storage" + persistent_volume_claim { + claim_name = kubernetes_persistent_volume_claim.postgresql.metadata[0].name + } + } + } + } + } + + depends_on = [ + kubernetes_persistent_volume_claim.postgresql, + kubernetes_secret.postgresql + ] +} + +# PostgreSQL Service +resource "kubernetes_service" "postgresql" { + metadata { + name = "postgresql" + namespace = kubernetes_namespace.app.metadata[0].name + labels = local.common_labels + } + + spec { + selector = { + app = "postgresql" + } + + port { + name = "postgresql" + port = 5432 + target_port = 5432 + protocol = "TCP" + } + + type = "ClusterIP" + cluster_ip = "None" # Headless service for StatefulSet + session_affinity = "None" + } + + depends_on = [ + kubernetes_stateful_set.postgresql + ] +} diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/postgresql.tf.disabled b/beispiel-terraform/beispiel-k8s-cluster-afc/postgresql.tf.disabled new file mode 100644 index 0000000..24a36c1 --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/postgresql.tf.disabled @@ -0,0 +1,70 @@ +# Azure Database for PostgreSQL Flexible Server +resource "azurerm_postgresql_flexible_server" "main" { + name = local.postgresql_server_name + resource_group_name = data.azurerm_resource_group.main.name + location = var.location + + administrator_login = var.postgresql_admin_username + administrator_password = local.postgresql_admin_pass + + sku_name = var.postgresql_sku_name + version = var.postgresql_version + storage_mb = var.postgresql_storage_mb + + backup_retention_days = var.postgresql_backup_retention_days + geo_redundant_backup_enabled = false + + # Public access for initial setup (can be restricted later) + # For production, consider using private endpoints or VNet integration + public_network_access_enabled = true + + zone = "1" + + tags = merge( + local.common_labels, + { + purpose = "database" + } + ) +} + +# PostgreSQL Firewall Rule - Allow Azure Services +resource "azurerm_postgresql_flexible_server_firewall_rule" "allow_azure_services" { + name = "AllowAzureServices" + server_id = azurerm_postgresql_flexible_server.main.id + start_ip_address = "0.0.0.0" + end_ip_address = "0.0.0.0" +} + +# PostgreSQL Firewall Rule - Allow AKS Outbound IPs +# Note: In production, consider using VNet integration or private endpoints +resource "azurerm_postgresql_flexible_server_firewall_rule" "allow_all_temporary" { + name = "AllowAllTemporary" + server_id = azurerm_postgresql_flexible_server.main.id + start_ip_address = "0.0.0.0" + end_ip_address = "255.255.255.255" + + # This is a temporary rule for initial setup + # Replace with specific IP ranges or VNet integration in production +} + +# PostgreSQL Database +resource "azurerm_postgresql_flexible_server_database" "main" { + name = local.postgresql_db_name + server_id = azurerm_postgresql_flexible_server.main.id + collation = "en_US.utf8" + charset = "utf8" +} + +# PostgreSQL Configuration - Optimize for Laravel +resource "azurerm_postgresql_flexible_server_configuration" "max_connections" { + name = "max_connections" + server_id = azurerm_postgresql_flexible_server.main.id + value = "200" +} + +resource "azurerm_postgresql_flexible_server_configuration" "timezone" { + name = "timezone" + server_id = azurerm_postgresql_flexible_server.main.id + value = "UTC" +} diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/provider.tf b/beispiel-terraform/beispiel-k8s-cluster-afc/provider.tf new file mode 100644 index 0000000..8c820e4 --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/provider.tf @@ -0,0 +1,27 @@ +provider "azurerm" { + features { + resource_group { + prevent_deletion_if_contains_resources = false + } + } + subscription_id = var.subscription_id + environment = "public" + use_cli = true + resource_provider_registrations = "none" +} + +provider "kubernetes" { + host = data.azurerm_kubernetes_cluster.main.kube_config[0].host + client_certificate = base64decode(data.azurerm_kubernetes_cluster.main.kube_config[0].client_certificate) + client_key = base64decode(data.azurerm_kubernetes_cluster.main.kube_config[0].client_key) + cluster_ca_certificate = base64decode(data.azurerm_kubernetes_cluster.main.kube_config[0].cluster_ca_certificate) +} + +provider "helm" { + kubernetes { + host = data.azurerm_kubernetes_cluster.main.kube_config[0].host + client_certificate = base64decode(data.azurerm_kubernetes_cluster.main.kube_config[0].client_certificate) + client_key = base64decode(data.azurerm_kubernetes_cluster.main.kube_config[0].client_key) + cluster_ca_certificate = base64decode(data.azurerm_kubernetes_cluster.main.kube_config[0].cluster_ca_certificate) + } +} diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tf b/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tf new file mode 100644 index 0000000..8e61ce1 --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tf @@ -0,0 +1,29 @@ +terraform { + required_version = ">= 1.9.0" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~> 4.0" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "~> 2.33" + } + helm = { + source = "hashicorp/helm" + version = "~> 2.16" + } + random = { + source = "hashicorp/random" + version = "~> 3.6" + } + } + + # Use local backend for initial deployment + # For production, consider using remote backend (Azure Storage or Terraform Cloud) + # backend "azurerm" { + # # Backend configuration will be provided via backend-config file or CLI + # # Example: terraform init -backend-config=backend.hcl + # } +} diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tfstate b/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tfstate new file mode 100644 index 0000000..dfdfddf --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tfstate @@ -0,0 +1,1618 @@ +{ + "version": 4, + "terraform_version": "1.14.3", + "serial": 23, + "lineage": "a5bf7cb3-3a02-ecee-c1d3-629bb386547f", + "outputs": { + "aks_cluster_name": { + "value": "trai_k8s_cluster", + "type": "string" + }, + "app_namespace": { + "value": "laravel-app", + "type": "string" + }, + "app_service_name": { + "value": "laravel-app", + "type": "string" + }, + "app_url": { + "value": "http://72.144.113.194", + "type": "string" + }, + "db_restore_info": { + "value": "Database restore disabled in Terraform. Use manual restore via kubectl (see db-restore.tf for instructions)", + "type": "string" + }, + "deployment_instructions": { + "value": "========================================\nDeployment Complete!\n========================================\n\n1. Get kubectl credentials:\n az aks get-credentials --resource-group trusted_ai_demo_rg --name trai_k8s_cluster\n\n2. Check application status:\n kubectl get pods -n laravel-app\n kubectl get svc -n laravel-app\n kubectl get ingress -n laravel-app\n\n3. Check database restore job (if enabled):\n kubectl get jobs -n laravel-app\n kubectl logs -n laravel-app -l job-type=database-restore\n\n4. Access the application:\n http://72.144.113.194\n\n5. Port-forward (if ingress not ready):\n kubectl port-forward -n laravel-app svc/laravel-app 8080:80\n\n6. View logs:\n kubectl logs -n laravel-app -l app=laravel-app -f\n\n7. Database connection details (in-cluster):\n Host: postgresql.laravel-app.svc.cluster.local\n Database: laravel_app\n Username: pgadmin\n\n8. Restore database manually:\n kubectl cp ../backups/backup_backend_20251203_101741.dump laravel-app/postgresql-0:/tmp/backup.dump\n kubectl exec -it -n laravel-app postgresql-0 -- pg_restore -U pgadmin -d laravel_app --clean --if-exists /tmp/backup.dump\n\n========================================\n", + "type": "string" + }, + "ingress_enabled": { + "value": true, + "type": "bool" + }, + "ingress_ip": { + "value": "72.144.113.194", + "type": "string" + }, + "postgresql_admin_username": { + "value": "pgadmin", + "type": "string", + "sensitive": true + }, + "postgresql_connection_string": { + "value": "postgresql://pgadmin:SecureLaravelPwd2024!@postgresql.laravel-app.svc.cluster.local:5432/laravel_app", + "type": "string", + "sensitive": true + }, + "postgresql_database_name": { + "value": "laravel_app", + "type": "string" + }, + "postgresql_service_fqdn": { + "value": "postgresql.laravel-app.svc.cluster.local", + "type": "string" + }, + "postgresql_service_name": { + "value": "postgresql", + "type": "string" + }, + "resource_group_name": { + "value": "trusted_ai_demo_rg", + "type": "string" + }, + "ssl_enabled": { + "value": false, + "type": "bool" + } + }, + "resources": [ + { + "mode": "data", + "type": "azurerm_kubernetes_cluster", + "name": "main", + "provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "aci_connector_linux": [], + "agent_pool_profile": [ + { + "auto_scaling_enabled": true, + "count": 2, + "max_count": 3, + "max_pods": 250, + "min_count": 2, + "name": "agentpool", + "node_labels": {}, + "node_public_ip_enabled": false, + "node_public_ip_prefix_id": "", + "node_taints": [], + "orchestrator_version": "1.33", + "os_disk_size_gb": 128, + "os_type": "Linux", + "tags": {}, + "type": "VirtualMachineScaleSets", + "upgrade_settings": [ + { + "drain_timeout_in_minutes": 0, + "max_surge": "10%", + "max_unavailable": "0", + "node_soak_duration_in_minutes": 0, + "undrainable_node_behavior": "" + } + ], + "vm_size": "Standard_D2as_v6", + "vnet_subnet_id": "", + "zones": [ + "1", + "2" + ] + } + ], + "api_server_authorized_ip_ranges": [], + "azure_active_directory_role_based_access_control": [], + "azure_policy_enabled": false, + "current_kubernetes_version": "1.33.5", + "disk_encryption_set_id": "", + "dns_prefix": "traik8scluster", + "fqdn": "traik8scluster-op0sir9n.hcp.germanywestcentral.azmk8s.io", + "http_application_routing_enabled": false, + "http_application_routing_zone_name": "", + "id": "/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/trusted_ai_demo_rg/providers/Microsoft.ContainerService/managedClusters/trai_k8s_cluster", + "identity": [ + { + "identity_ids": [], + "principal_id": "d381648c-78a6-44e2-bd90-f1e4c4d72cfc", + "tenant_id": "73c07a52-1c4d-40ac-b3b3-2f842e5c8919", + "type": "SystemAssigned" + } + ], + "ingress_application_gateway": [], + "key_management_service": [], + "key_vault_secrets_provider": [], + "kube_admin_config": [], + "kube_admin_config_raw": "", + "kube_config": [ + { + "client_certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZIakNDQXdhZ0F3SUJBZ0lSQUt4cUdaR3NjVVVjV2pnY0ZNTE95cDh3RFFZSktvWklodmNOQVFFTEJRQXcKRFRFTE1Ba0dBMVVFQXhNQ1kyRXdIaGNOTWpVeE1qRTRNVEl3TkRRMFdoY05NamN4TWpFNE1USXhORFEwV2pBdwpNUmN3RlFZRFZRUUtFdzV6ZVhOMFpXMDZiV0Z6ZEdWeWN6RVZNQk1HQTFVRUF4TU1iV0Z6ZEdWeVkyeHBaVzUwCk1JSUNJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBd000T3o3NkI1MFFXeTB2UzdYYnEKR1RiMTVNSWUzZmtMUDliM3hNS1lWZVRCeWNsWExmSkJjRHRNRWlDa1Ixbi9FdGxIV0RwbGx5Qkx3Mjl2UWpOUgpsblFLMDZzY1gvNDdvSTJFODkxUjYvTC9NaWIxV2RiTUJ2R2pvdkhVNnZOdE9tbmdOaFVOT2tXYXRabGVPQUMyCkVxNE5WWFlKWW85SnlKRXlhNlgzd3lDazhJZzQyeEtYZjFSZDJVK0RYVTZndCtIYWk5THpsellob01ra20rdWQKc2RDSFp0S0lIb3cvV013NkdyTHczelQ0dm5jWVNoL2hOYUVZUm1yNytpRWFQbmEwRnNXa1FtYmE4R3RvZmdodQpDLzNnTTBNSHY5VnFQTDhOWDNabXA3VERyODhVclpLSnNWTVBQLzFOL2ZER3k5bzVQS3hqb2c2SUJGNStxcWdqCkk0MFZpWHdEcW9Md3RpeHhDNytRYmRLMFZBSGI5UzlmcnZaZWZCeU9qdUlzR21MOHAxem5RUlhmRk5vRENhdmMKVk9mOFgzU3BNMEpJRFBMVUpqTitnRzdaYTlHU2JubFZlMVBwK3dZcHRDUXRqVjNScXFGOUpJd2lXRVdLQ2FaTApWSXV3VENHOU0yeEYxNjZjMWk1dGcwUjZkTGwzdW9BYkVleXdNUE1TcWloMUxLdmFZNUcyQjJiclp0K3hjM3NYCldKQW5TamJhOUtnZnk5d3BlNVI0bnUwejVjMUdrSGRLQjVwZDBZVWxINlRpY0EvV0Q5SXdDNFZDdWlJcTlmNGsKR1hpS2JEQXp6UFZwZjZFcjVCV0pVZzhJWjBnb0svaWxOdXJWNjhvSmlmRW9qdXFIWTJDRm9PMFhRanhtZkxwVgo5cFVoVGc5YjEzNlNKWDF5YlN6eFZmc0NBd0VBQWFOV01GUXdEZ1lEVlIwUEFRSC9CQVFEQWdXZ01CTUdBMVVkCkpRUU1NQW9HQ0NzR0FRVUZCd01DTUF3R0ExVWRFd0VCL3dRQ01BQXdId1lEVlIwakJCZ3dGb0FVVXZKU1RHR0gKUzRhd3dKZ29lV3VGYk55ZElvMHdEUVlKS29aSWh2Y05BUUVMQlFBRGdnSUJBQ2I5Sy82cnV1QlNRdk04c1VmQQpNWWkvOUZUaVNHR2pqeVRRdFlBMncxRVA2aHpocVMwWXBzdWhwWGVtS3VtcXJPZzZ0QzZERDR3Z3B0OWNYdExsCm14WHQ3V2d2bUNyckt0UHB0OHlzNjN2NzBzQXNDTDNJZmg1SHoxVzlHSzdHbFRub0w5ODI5dTN6NWlESldTeUUKY0NsaDhlTzVnNVltaUcvV05qZDFzZHlvV2NJbEdlNi9FRnFaWTNkYkVVSlVSVUZHNzZLV0tndE1UejZQRlVQRwo5YXdOMjdiL0tPaVBtbmI0WkVVbmVweTRTcHhabis1UGRQT2g2Q0VFdU9FOWNZQ2pTRzc0TXZvZkY4SHlpOFVKCmYrQjJ2bVE5bEN6SGJzSWZNdVpyeFRZd21DTGhlU3RndVVWUnhqSm4zaTVVdGJwMTBkWUJQaThtU2FVV0p1b3oKS0FNRnFsTWdNOVhDc3FUZ05yWm01U3ZuYmdjUHozbW0wSmRScUhtQS9EUzhWckl6NGo2YVdueUpzdlQ0MUFDTApVMnRZbmw0MlUrSFRtYndFSGFla0J4WDRiTlBNekRYVzk1SktVT2NySWxkM25CVUpMY3draTRVSnpVQ0pkZDl4CmEzOVVLTXUzUVh2V1phRkFUOWFEN2QzWXUxN0IxOG84V2liNlhPRzFnWXB0L3U2L2hzTWcrc1lVdmxMd2lOQXIKQnpHV2xjUUtXNDZETEFJQXlBdkxuaDlhQnJ6aVY2aHkyUm0rTmZwVkFlUUd4WVAzRzgyRGdwVFhtUXZkNTBaOQo3L0pnQVo1R01PT1hMN0ozcURFVHhDY3NLeUZYdTNielE5dmhWaXJ6MTdoNi9naE5hTmZFcXF6T2pQZStRTjFNCkhjZFFoNlFWUkt1dlpzbTRrZW1aSDVOaQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", + "client_key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBd000T3o3NkI1MFFXeTB2UzdYYnFHVGIxNU1JZTNma0xQOWIzeE1LWVZlVEJ5Y2xYCkxmSkJjRHRNRWlDa1Ixbi9FdGxIV0RwbGx5Qkx3Mjl2UWpOUmxuUUswNnNjWC80N29JMkU4OTFSNi9ML01pYjEKV2RiTUJ2R2pvdkhVNnZOdE9tbmdOaFVOT2tXYXRabGVPQUMyRXE0TlZYWUpZbzlKeUpFeWE2WDN3eUNrOElnNAoyeEtYZjFSZDJVK0RYVTZndCtIYWk5THpsellob01ra20rdWRzZENIWnRLSUhvdy9XTXc2R3JMdzN6VDR2bmNZClNoL2hOYUVZUm1yNytpRWFQbmEwRnNXa1FtYmE4R3RvZmdodUMvM2dNME1IdjlWcVBMOE5YM1ptcDdURHI4OFUKclpLSnNWTVBQLzFOL2ZER3k5bzVQS3hqb2c2SUJGNStxcWdqSTQwVmlYd0Rxb0x3dGl4eEM3K1FiZEswVkFIYgo5UzlmcnZaZWZCeU9qdUlzR21MOHAxem5RUlhmRk5vRENhdmNWT2Y4WDNTcE0wSklEUExVSmpOK2dHN1phOUdTCmJubFZlMVBwK3dZcHRDUXRqVjNScXFGOUpJd2lXRVdLQ2FaTFZJdXdUQ0c5TTJ4RjE2NmMxaTV0ZzBSNmRMbDMKdW9BYkVleXdNUE1TcWloMUxLdmFZNUcyQjJiclp0K3hjM3NYV0pBblNqYmE5S2dmeTl3cGU1UjRudTB6NWMxRwprSGRLQjVwZDBZVWxINlRpY0EvV0Q5SXdDNFZDdWlJcTlmNGtHWGlLYkRBenpQVnBmNkVyNUJXSlVnOElaMGdvCksvaWxOdXJWNjhvSmlmRW9qdXFIWTJDRm9PMFhRanhtZkxwVjlwVWhUZzliMTM2U0pYMXliU3p4VmZzQ0F3RUEKQVFLQ0FnQXJhZnF4MHA3am1WUmNvSTAxUlZKL0xQMy9iYXFQcHZWZWdhUTIzTzU2WU5FMkE2MHF2bUwzeDVKZwpmcE1mTWVlYXNldzd0S0tOZmdzeFRaVkszYzFnU0c3cnJKcnVEb3dVb2VrdkZFWFBFRjlnUnA5MmhFUmF4K3hsCjAzaHpFeThrT1JUd2VKc0s1UU5xSXNleGFvK1U4ell6YVNuQjFLdnppcVpFMFJGU0ViYXlKcXBPd2dmMDl1Vk8KK25XRS9DQjZaMVBUcWtPSlovbjRmM0pMM2FrYmtlMzdQQ3JFWGlZaGM1TWQwZThsQTRPRE44UHpaT3hNTENNUQpNQ1RlOUxjNUczZC9yUzlJZUdxU09rMUVHOWZUd1czTGZTdHdpYmlyUndYVzJnMVJkQVdnUDh2VExhenRqZHRnCk9FUm9pdC9vcFJIQVZMejJqN2tXNnNRdzN6MVI3a3d2bDJ5aHlPL1l6NWp5K1ZHOUgra0pmeEwzM0o4OWxmSEEKblNIbm9aZGhzN1Zxa3JLN3pIbnlsZEUzZVBqenRUS2JMaGRoeWVqTU5lRmlmeXdzVTFKQkpnYnZRSDBHSmpxbQpNMkRXRWtQVzA5L2ZHbVpKM2NVVy8zVmM4MG5FUC9lYWxxZnVPM3QvamwxdEtUaGg3WC9lTXlqUDRvSTFpV0ZpCkZ3eDByWGxOSVcrSnVuNjFtVTN6eENpZzhDVXI1SmNsMURPZjNRY2VaeklmV0dxYWNRK1lWeDErQTErVE01d0wKdkl1ek92MkFoamVscE5sVEFTMUtOemxidVRwWXVubnVLUERKK3FQWWhDV3cxMGJXcXJvTEw1dzgwQUJXaGRGLwpCNjBBNWNuSSs3eVNJT1crbzAvNW1BUm9hVHhiY1JUTVV2aVFVMHplK0UzN3NJTmt5UUtDQVFFQXhoOHl3Qnh5CkpySW5mVnl0TkFvTDFVK2NyRmliekRRQTI5MlpUTHl0MG5VLzlCZ2t5ZXlZL2JId2pzeitwTzR6anNERUFYQSsKcjZ1NmZySm9PMU9lbzlkZUs5UW5lc1NadHZLSG9HU1MwS2VseWlLcmZTM29YL1RGb3dBZzJNaXMyWFpuNy9WVAp0MVhwSFdMV3ljeTFjZVhibEU5cTZ2YmdIdWUwMWZPUmtna09JSHJpVXdwcmxNZFhvNHdKWExNT0Z5NUdRNXp4CnVRV2c5TWZXUmxuRVZmcE5OTnV3MlBsSmYybG1oOFJablJHOTdhOWZ5OXp5QWlYLy82VURSdnJnbzFzd1d6QU4KZFZsQXd5S0tzczQzaldMSjdmRVBCbDhsVTJidm9OVnR0THB0ai9rWk9HVjZXeGVRMEY4UlRQZmFwVkN6bHlhRwpGMHZkUWRWemhjdjRsd0tDQVFFQStTRTVaK0pwUlY1L3RJTjRydS9ES1E0Rnk3WlZ6Yk41YWxUcXo2TXFkOTZnClhtMTNHdlpoWmgrN0htZktqMHNJWWZRS21oK2xxNWsyK3YvMXBWYUZLa0VSd1FNdGM0VHp6bmFNeHRqZE16a2oKc1E3MjZoRUZocnZZWmRtbVVLbjBvMHJVbUNocWJkNU9WRGNLRmR3ekwrNldBVFo3c1RWVlVsc01JSG82YnlYRApqM0FzVThaZjRtTFc4SktkMm4ydjhZRDZ1NE40ZEE0bnozd0tIdUkxY2txRlBZa3BEYlpOcS9GS0VpY3FiVkk5ClVhRDVVOVd0cHg3MVNCYWcrdlJodFA0N2wybngybW5QSFJNUVo0cXY1UitvUUxUeUthMzlYSVU2TmUrcDVxYTkKWXlJaERVcDRRbmRnWDZqNHFUaWdXQnJsaFF3cUFsZWJtbFVha2ZyMlBRS0NBUUJsT0kxS1AzNUdyT0NtR05KWQo4d1VHcjQwQk5LZ3NsU2tYSzhsbHZrUWtjVWtDWWM5eDV0Sm5VeUR2SkdhNVJ3WEtXa3dKM0JuNjBJY1FzZiswCng4MFdNak5udXhXTWtLTmtKOWNKS0RrbUozRDVxK3Jub3F2RlVwNGk0QzduOHViMGw1MFVxTWRqdEozTUtMSksKR2E5ZUNLeTl3akdDNkN5clBZQlRpd1NGa3VESDBXQ09RcFdpUnBKTStYL1MvNXdrVmIwS1VFWGpYSy9XUDc4Rwo1RmYrQVVxdXhjczluMmRnbEM0UkVVMGpoV3YrNG9uVDh3Y2w5OW9wOTFtTVN5SzM4eC9xaHN0cGQvY3hleFZYCnN5b0lyejh4dE9ZVFFFc21xRUduYjBPRWdYYjJockxPOEhHcDFvbGpIQjhQcnRYamZpNnkrN2lvY0ZjVGlvVmkKQjByekFvSUJBUUNpT3cxNzVjZHhvZUQ5ZHVPKzMya3lNZXg2Uk1sSlE0ZGNRK0lJeHBNZ0xmdzJvWWYvS2RReApRWmFEYlVsTjJLbWp4OXlqa3RvNmpYSnRMd01TNE8ya24yemxTS0Z2YVhPdXppcE1UdXAwc2MvWlBBcDBVQk5PCk1zTHZTRFZ5cjhvY0Q5MnpIQlo2WjFxUW9xclJvMnc5MlZzYmZSUTlTS0xBRjRRbEhoTXhUK0V5WmZTbnRUVzUKWVczZ2lVbDcvMzcwZ0c1R2I0MmYzWnYxRkN3dkFsV1ZrSXVGaiswSS9kOUVpY01YWVNQU3Zzd3BmTzcvdGRUNApVRU9mNXdSN2gycWRCelZkRzJPZ1BHamY0UXM4c2VackNSdHl6NkltbkNPeC9SdjBCWmFaR1ptWWxaLzZYTFZGCmF5RjZDYm1yMExFeHpvb0tBdXJVVGFLSmRNUHhyZ0J0QW9JQkFHeU9sN2xFOTdSRURsbFdFbGx5REpqelJxd3kKcldlcXNyRXFDclJtN0FDK0xGL3BJQ0dacEk0QzF1QmRJVUlVY01INDNrSHowazY0NVZ4OCt2UE5PRjYzQjdNNwpPRVhlZmd0Nmo4eW1oWjhFVUQ5VmdQTGhWK3ZqRGNKbkFaVFhqV0xrclR1K3ZEUVBpUGNTVm5UMEtNNjhBR3RGCmh3bjFKaHZoSmJiMVFSaFRocGxBWVE4SlhjcUxtUW93Nng5aG9MVGVxY1ZDazkreGZ6bWUvYXNmclp6NDBlbU4KYjF2VWpidFNrOVZkZU9tOExCVXZqZkwwZmowYUNIMkFCU1RRY1dYWFZIMldQRzB6VExKMjhIb0k1dGw4RzA3Ngp0NGtpQm9BRC8vUW4zM2doSy9wZTJDdFd3Y2VuY3Rxa2lNd21sTVIyckJZNlF2YVUzN1NGZXM3Mno0WT0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K", + "cluster_ca_certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUU2VENDQXRHZ0F3SUJBZ0lSQU9acHRKdjBGdzFmYXg3RFhFbVpsLzR3RFFZSktvWklodmNOQVFFTEJRQXcKRFRFTE1Ba0dBMVVFQXhNQ1kyRXdJQmNOTWpVeE1qRTRNVEl3TkRRMFdoZ1BNakExTlRFeU1UZ3hNakUwTkRSYQpNQTB4Q3pBSkJnTlZCQU1UQW1OaE1JSUNJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBCm9GVDlYTnRUd3BUaXdHcThRN2xROWNlS01MQkR2eDEwRVZHbElzUzlsQ3ltWXluN0EyOVVCbEcwZHg2QzdwY3EKa3ExekRwbHBWOEF2eUtsVDQ0dDhFS1pSa1dObEZybEhSS0VZNE5EazZ2NjVlRzdxVWlQc3lQUW5DOFVPaHJSMwpUQVgvMmN3M0twUXlvVmhzcStsRlUrMVp4MjUraVY5UFd3VjFXYXFsQkkyUzMzZkNzKy9WdUZUdWZTYjdYblQ2CnN0d3FEd0hFTVhMUGJPY1d4bUwwY1NhY3pYT3RxVDQxUzJ5SjRnbkNDMVBzaFdhenBQUXBUWmxuUmt0NzI4QXgKU3NPcXEyVDJhV2VSMWgxNCtkUDZ2YmhBVTA0YW5RT1Jac0Nmei8zSkxyeHpDK0tlMDJEOU5GalNJRmFjcG80dwo5VW1HRXJBOXZaUGhmUGt1NDZyenEwYWtQcTQ5cGZSeWh3Zy9jMGlHNXpQeENwZjhLbEtmdmFDelBweDBRNUJPCkZaRlBxWUR2Q0ZLejJBMEFUNkE1MFFPR1RuNk1WWTBsTGFkR1ZTRHhPYlFicWJEeHBZU3c4bGpzVTlxdkxLVkgKYWNPUVpNQjVyUzdzSlpZQ3doZ01YSHQ2VnAwcnlMRnk5ZkY2THFtZUsyeWlSbU9wb1VLeTdEd2NPRTU2Z0YzcApMVTlvNUt2SGZLaUMrTDNCVW82ZFJRWFkyWHRuUXNZakpYRHFGRERRSEUrNUxIY3lHbDcrOE1mdkIrNHp1MnFNCjVmRVRLSnlaOENMQkxyWDM2SjhOamZhdXR4U3MvcmVhRitYZ3dFL3VLb3FGVjhSWUIwUzU3dk9jdEhlbUpBRWkKM040bUR1U0NydzhDQzZyWUxZekNtT3lua1EycnhJWnBUbGFzTlFQR215TUNBd0VBQWFOQ01FQXdEZ1lEVlIwUApBUUgvQkFRREFnS2tNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHdIUVlEVlIwT0JCWUVGRkx5VWt4aGgwdUdzTUNZCktIbHJoV3pjblNLTk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQ0FRQUc5MVQ5MGE5bW52aHRKNEczemVZR2JXOWEKazh4RitMNlF5a01hNVRUQnJzRkFrV1NQK3JLWXJoR29LRUVMOG5wNXltbVhmZ1ZydytnSklydmR0em4vcVRmSgpnS29ZZXpDWXEyRGo1SHZ3UHZTNS9kbVRNQ2FMTTZPRURFSU50c0QrcVRYM2s2VDNjWEZEM2dha21KdnhxdjlzCldiN3lhdk9tMUlaOVhLT0RXWGVZOFFXNGNrMHN2QTlDVnNaQlRYWUVwcWRWSW5rdVpia2MzQThiQlFJcXZ5d1AKbytQZW85Q1FYSkIrU0RvVGlDc1RSTHBaN0hIUVlmbnN6ZmpYSmhpZXhVc0xGYU9PM3UwZ3Y5VWtWNzBDdTNvTApGWTNPNDVMVGdjVzJUSnFSejNjNnhidUZzTXN2dTM3TS9IOFp6TEREK1F3RFhMK0lqMm53aStNcStnREtoMW54CmVZVkJnUGJ6amhYNk1rcVRPMmxlTTZmeVVsUXM4TU5tQjZrb0dpWkFUYnQycHZSSEY5YWozOWpmSjJOaFNpaEMKbnpNK1hNUXlnQ25QNklpY01jZ3h5SHRTUkRhL0tnWldJelp5SkRnVmZCUEhwY1VaTlg0WkVNZWZuckZSOEpXUgp5VU9Wa0VvOXNrdXFZSmVVYnBBRTV4d1F3U01tRWlSVVM4VUdGMGR1bWp4dlZvdVVENGZvWjNHelBCNFZ1allqCnE1WndncVlCL3NFNXNtNGUyRmRPK2pFNFJFcmJoMW5uVjBIbjBGcFVsQzlmSEVwSWg3dEZLbndiT05NZU1mL1gKWWJrS0dyelpHY1NGMUczazN4Mjd6WVk5eUJxayt4dWZSWGVkczhnVjRQYkt6cFVVOWZVTUJ1WXpCL2haNTM0Zwptam9rQ1lPNWdQMzduVGVTM1E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", + "host": "https://traik8scluster-op0sir9n.hcp.germanywestcentral.azmk8s.io:443", + "password": "0w0vs639mwl6rzwt1ryicepjylnxirlchfs54fyop2vaezl7hl9o6ghu2bljlypok7nqtwlr5ejlvktasozjwzow60qqblph96ua28dqa6kv6ftz54vviq2v88mo1697", + "username": "clusterUser_trusted_ai_demo_rg_trai_k8s_cluster" + } + ], + "kube_config_raw": "apiVersion: v1\nclusters:\n- cluster:\n certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUU2VENDQXRHZ0F3SUJBZ0lSQU9acHRKdjBGdzFmYXg3RFhFbVpsLzR3RFFZSktvWklodmNOQVFFTEJRQXcKRFRFTE1Ba0dBMVVFQXhNQ1kyRXdJQmNOTWpVeE1qRTRNVEl3TkRRMFdoZ1BNakExTlRFeU1UZ3hNakUwTkRSYQpNQTB4Q3pBSkJnTlZCQU1UQW1OaE1JSUNJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBCm9GVDlYTnRUd3BUaXdHcThRN2xROWNlS01MQkR2eDEwRVZHbElzUzlsQ3ltWXluN0EyOVVCbEcwZHg2QzdwY3EKa3ExekRwbHBWOEF2eUtsVDQ0dDhFS1pSa1dObEZybEhSS0VZNE5EazZ2NjVlRzdxVWlQc3lQUW5DOFVPaHJSMwpUQVgvMmN3M0twUXlvVmhzcStsRlUrMVp4MjUraVY5UFd3VjFXYXFsQkkyUzMzZkNzKy9WdUZUdWZTYjdYblQ2CnN0d3FEd0hFTVhMUGJPY1d4bUwwY1NhY3pYT3RxVDQxUzJ5SjRnbkNDMVBzaFdhenBQUXBUWmxuUmt0NzI4QXgKU3NPcXEyVDJhV2VSMWgxNCtkUDZ2YmhBVTA0YW5RT1Jac0Nmei8zSkxyeHpDK0tlMDJEOU5GalNJRmFjcG80dwo5VW1HRXJBOXZaUGhmUGt1NDZyenEwYWtQcTQ5cGZSeWh3Zy9jMGlHNXpQeENwZjhLbEtmdmFDelBweDBRNUJPCkZaRlBxWUR2Q0ZLejJBMEFUNkE1MFFPR1RuNk1WWTBsTGFkR1ZTRHhPYlFicWJEeHBZU3c4bGpzVTlxdkxLVkgKYWNPUVpNQjVyUzdzSlpZQ3doZ01YSHQ2VnAwcnlMRnk5ZkY2THFtZUsyeWlSbU9wb1VLeTdEd2NPRTU2Z0YzcApMVTlvNUt2SGZLaUMrTDNCVW82ZFJRWFkyWHRuUXNZakpYRHFGRERRSEUrNUxIY3lHbDcrOE1mdkIrNHp1MnFNCjVmRVRLSnlaOENMQkxyWDM2SjhOamZhdXR4U3MvcmVhRitYZ3dFL3VLb3FGVjhSWUIwUzU3dk9jdEhlbUpBRWkKM040bUR1U0NydzhDQzZyWUxZekNtT3lua1EycnhJWnBUbGFzTlFQR215TUNBd0VBQWFOQ01FQXdEZ1lEVlIwUApBUUgvQkFRREFnS2tNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHdIUVlEVlIwT0JCWUVGRkx5VWt4aGgwdUdzTUNZCktIbHJoV3pjblNLTk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQ0FRQUc5MVQ5MGE5bW52aHRKNEczemVZR2JXOWEKazh4RitMNlF5a01hNVRUQnJzRkFrV1NQK3JLWXJoR29LRUVMOG5wNXltbVhmZ1ZydytnSklydmR0em4vcVRmSgpnS29ZZXpDWXEyRGo1SHZ3UHZTNS9kbVRNQ2FMTTZPRURFSU50c0QrcVRYM2s2VDNjWEZEM2dha21KdnhxdjlzCldiN3lhdk9tMUlaOVhLT0RXWGVZOFFXNGNrMHN2QTlDVnNaQlRYWUVwcWRWSW5rdVpia2MzQThiQlFJcXZ5d1AKbytQZW85Q1FYSkIrU0RvVGlDc1RSTHBaN0hIUVlmbnN6ZmpYSmhpZXhVc0xGYU9PM3UwZ3Y5VWtWNzBDdTNvTApGWTNPNDVMVGdjVzJUSnFSejNjNnhidUZzTXN2dTM3TS9IOFp6TEREK1F3RFhMK0lqMm53aStNcStnREtoMW54CmVZVkJnUGJ6amhYNk1rcVRPMmxlTTZmeVVsUXM4TU5tQjZrb0dpWkFUYnQycHZSSEY5YWozOWpmSjJOaFNpaEMKbnpNK1hNUXlnQ25QNklpY01jZ3h5SHRTUkRhL0tnWldJelp5SkRnVmZCUEhwY1VaTlg0WkVNZWZuckZSOEpXUgp5VU9Wa0VvOXNrdXFZSmVVYnBBRTV4d1F3U01tRWlSVVM4VUdGMGR1bWp4dlZvdVVENGZvWjNHelBCNFZ1allqCnE1WndncVlCL3NFNXNtNGUyRmRPK2pFNFJFcmJoMW5uVjBIbjBGcFVsQzlmSEVwSWg3dEZLbndiT05NZU1mL1gKWWJrS0dyelpHY1NGMUczazN4Mjd6WVk5eUJxayt4dWZSWGVkczhnVjRQYkt6cFVVOWZVTUJ1WXpCL2haNTM0Zwptam9rQ1lPNWdQMzduVGVTM1E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==\n server: https://traik8scluster-op0sir9n.hcp.germanywestcentral.azmk8s.io:443\n name: trai_k8s_cluster\ncontexts:\n- context:\n cluster: trai_k8s_cluster\n user: clusterUser_trusted_ai_demo_rg_trai_k8s_cluster\n name: trai_k8s_cluster\ncurrent-context: trai_k8s_cluster\nkind: Config\npreferences: {}\nusers:\n- name: clusterUser_trusted_ai_demo_rg_trai_k8s_cluster\n user:\n client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZIakNDQXdhZ0F3SUJBZ0lSQUt4cUdaR3NjVVVjV2pnY0ZNTE95cDh3RFFZSktvWklodmNOQVFFTEJRQXcKRFRFTE1Ba0dBMVVFQXhNQ1kyRXdIaGNOTWpVeE1qRTRNVEl3TkRRMFdoY05NamN4TWpFNE1USXhORFEwV2pBdwpNUmN3RlFZRFZRUUtFdzV6ZVhOMFpXMDZiV0Z6ZEdWeWN6RVZNQk1HQTFVRUF4TU1iV0Z6ZEdWeVkyeHBaVzUwCk1JSUNJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBd000T3o3NkI1MFFXeTB2UzdYYnEKR1RiMTVNSWUzZmtMUDliM3hNS1lWZVRCeWNsWExmSkJjRHRNRWlDa1Ixbi9FdGxIV0RwbGx5Qkx3Mjl2UWpOUgpsblFLMDZzY1gvNDdvSTJFODkxUjYvTC9NaWIxV2RiTUJ2R2pvdkhVNnZOdE9tbmdOaFVOT2tXYXRabGVPQUMyCkVxNE5WWFlKWW85SnlKRXlhNlgzd3lDazhJZzQyeEtYZjFSZDJVK0RYVTZndCtIYWk5THpsellob01ra20rdWQKc2RDSFp0S0lIb3cvV013NkdyTHczelQ0dm5jWVNoL2hOYUVZUm1yNytpRWFQbmEwRnNXa1FtYmE4R3RvZmdodQpDLzNnTTBNSHY5VnFQTDhOWDNabXA3VERyODhVclpLSnNWTVBQLzFOL2ZER3k5bzVQS3hqb2c2SUJGNStxcWdqCkk0MFZpWHdEcW9Md3RpeHhDNytRYmRLMFZBSGI5UzlmcnZaZWZCeU9qdUlzR21MOHAxem5RUlhmRk5vRENhdmMKVk9mOFgzU3BNMEpJRFBMVUpqTitnRzdaYTlHU2JubFZlMVBwK3dZcHRDUXRqVjNScXFGOUpJd2lXRVdLQ2FaTApWSXV3VENHOU0yeEYxNjZjMWk1dGcwUjZkTGwzdW9BYkVleXdNUE1TcWloMUxLdmFZNUcyQjJiclp0K3hjM3NYCldKQW5TamJhOUtnZnk5d3BlNVI0bnUwejVjMUdrSGRLQjVwZDBZVWxINlRpY0EvV0Q5SXdDNFZDdWlJcTlmNGsKR1hpS2JEQXp6UFZwZjZFcjVCV0pVZzhJWjBnb0svaWxOdXJWNjhvSmlmRW9qdXFIWTJDRm9PMFhRanhtZkxwVgo5cFVoVGc5YjEzNlNKWDF5YlN6eFZmc0NBd0VBQWFOV01GUXdEZ1lEVlIwUEFRSC9CQVFEQWdXZ01CTUdBMVVkCkpRUU1NQW9HQ0NzR0FRVUZCd01DTUF3R0ExVWRFd0VCL3dRQ01BQXdId1lEVlIwakJCZ3dGb0FVVXZKU1RHR0gKUzRhd3dKZ29lV3VGYk55ZElvMHdEUVlKS29aSWh2Y05BUUVMQlFBRGdnSUJBQ2I5Sy82cnV1QlNRdk04c1VmQQpNWWkvOUZUaVNHR2pqeVRRdFlBMncxRVA2aHpocVMwWXBzdWhwWGVtS3VtcXJPZzZ0QzZERDR3Z3B0OWNYdExsCm14WHQ3V2d2bUNyckt0UHB0OHlzNjN2NzBzQXNDTDNJZmg1SHoxVzlHSzdHbFRub0w5ODI5dTN6NWlESldTeUUKY0NsaDhlTzVnNVltaUcvV05qZDFzZHlvV2NJbEdlNi9FRnFaWTNkYkVVSlVSVUZHNzZLV0tndE1UejZQRlVQRwo5YXdOMjdiL0tPaVBtbmI0WkVVbmVweTRTcHhabis1UGRQT2g2Q0VFdU9FOWNZQ2pTRzc0TXZvZkY4SHlpOFVKCmYrQjJ2bVE5bEN6SGJzSWZNdVpyeFRZd21DTGhlU3RndVVWUnhqSm4zaTVVdGJwMTBkWUJQaThtU2FVV0p1b3oKS0FNRnFsTWdNOVhDc3FUZ05yWm01U3ZuYmdjUHozbW0wSmRScUhtQS9EUzhWckl6NGo2YVdueUpzdlQ0MUFDTApVMnRZbmw0MlUrSFRtYndFSGFla0J4WDRiTlBNekRYVzk1SktVT2NySWxkM25CVUpMY3draTRVSnpVQ0pkZDl4CmEzOVVLTXUzUVh2V1phRkFUOWFEN2QzWXUxN0IxOG84V2liNlhPRzFnWXB0L3U2L2hzTWcrc1lVdmxMd2lOQXIKQnpHV2xjUUtXNDZETEFJQXlBdkxuaDlhQnJ6aVY2aHkyUm0rTmZwVkFlUUd4WVAzRzgyRGdwVFhtUXZkNTBaOQo3L0pnQVo1R01PT1hMN0ozcURFVHhDY3NLeUZYdTNielE5dmhWaXJ6MTdoNi9naE5hTmZFcXF6T2pQZStRTjFNCkhjZFFoNlFWUkt1dlpzbTRrZW1aSDVOaQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==\n client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBd000T3o3NkI1MFFXeTB2UzdYYnFHVGIxNU1JZTNma0xQOWIzeE1LWVZlVEJ5Y2xYCkxmSkJjRHRNRWlDa1Ixbi9FdGxIV0RwbGx5Qkx3Mjl2UWpOUmxuUUswNnNjWC80N29JMkU4OTFSNi9ML01pYjEKV2RiTUJ2R2pvdkhVNnZOdE9tbmdOaFVOT2tXYXRabGVPQUMyRXE0TlZYWUpZbzlKeUpFeWE2WDN3eUNrOElnNAoyeEtYZjFSZDJVK0RYVTZndCtIYWk5THpsellob01ra20rdWRzZENIWnRLSUhvdy9XTXc2R3JMdzN6VDR2bmNZClNoL2hOYUVZUm1yNytpRWFQbmEwRnNXa1FtYmE4R3RvZmdodUMvM2dNME1IdjlWcVBMOE5YM1ptcDdURHI4OFUKclpLSnNWTVBQLzFOL2ZER3k5bzVQS3hqb2c2SUJGNStxcWdqSTQwVmlYd0Rxb0x3dGl4eEM3K1FiZEswVkFIYgo5UzlmcnZaZWZCeU9qdUlzR21MOHAxem5RUlhmRk5vRENhdmNWT2Y4WDNTcE0wSklEUExVSmpOK2dHN1phOUdTCmJubFZlMVBwK3dZcHRDUXRqVjNScXFGOUpJd2lXRVdLQ2FaTFZJdXdUQ0c5TTJ4RjE2NmMxaTV0ZzBSNmRMbDMKdW9BYkVleXdNUE1TcWloMUxLdmFZNUcyQjJiclp0K3hjM3NYV0pBblNqYmE5S2dmeTl3cGU1UjRudTB6NWMxRwprSGRLQjVwZDBZVWxINlRpY0EvV0Q5SXdDNFZDdWlJcTlmNGtHWGlLYkRBenpQVnBmNkVyNUJXSlVnOElaMGdvCksvaWxOdXJWNjhvSmlmRW9qdXFIWTJDRm9PMFhRanhtZkxwVjlwVWhUZzliMTM2U0pYMXliU3p4VmZzQ0F3RUEKQVFLQ0FnQXJhZnF4MHA3am1WUmNvSTAxUlZKL0xQMy9iYXFQcHZWZWdhUTIzTzU2WU5FMkE2MHF2bUwzeDVKZwpmcE1mTWVlYXNldzd0S0tOZmdzeFRaVkszYzFnU0c3cnJKcnVEb3dVb2VrdkZFWFBFRjlnUnA5MmhFUmF4K3hsCjAzaHpFeThrT1JUd2VKc0s1UU5xSXNleGFvK1U4ell6YVNuQjFLdnppcVpFMFJGU0ViYXlKcXBPd2dmMDl1Vk8KK25XRS9DQjZaMVBUcWtPSlovbjRmM0pMM2FrYmtlMzdQQ3JFWGlZaGM1TWQwZThsQTRPRE44UHpaT3hNTENNUQpNQ1RlOUxjNUczZC9yUzlJZUdxU09rMUVHOWZUd1czTGZTdHdpYmlyUndYVzJnMVJkQVdnUDh2VExhenRqZHRnCk9FUm9pdC9vcFJIQVZMejJqN2tXNnNRdzN6MVI3a3d2bDJ5aHlPL1l6NWp5K1ZHOUgra0pmeEwzM0o4OWxmSEEKblNIbm9aZGhzN1Zxa3JLN3pIbnlsZEUzZVBqenRUS2JMaGRoeWVqTU5lRmlmeXdzVTFKQkpnYnZRSDBHSmpxbQpNMkRXRWtQVzA5L2ZHbVpKM2NVVy8zVmM4MG5FUC9lYWxxZnVPM3QvamwxdEtUaGg3WC9lTXlqUDRvSTFpV0ZpCkZ3eDByWGxOSVcrSnVuNjFtVTN6eENpZzhDVXI1SmNsMURPZjNRY2VaeklmV0dxYWNRK1lWeDErQTErVE01d0wKdkl1ek92MkFoamVscE5sVEFTMUtOemxidVRwWXVubnVLUERKK3FQWWhDV3cxMGJXcXJvTEw1dzgwQUJXaGRGLwpCNjBBNWNuSSs3eVNJT1crbzAvNW1BUm9hVHhiY1JUTVV2aVFVMHplK0UzN3NJTmt5UUtDQVFFQXhoOHl3Qnh5CkpySW5mVnl0TkFvTDFVK2NyRmliekRRQTI5MlpUTHl0MG5VLzlCZ2t5ZXlZL2JId2pzeitwTzR6anNERUFYQSsKcjZ1NmZySm9PMU9lbzlkZUs5UW5lc1NadHZLSG9HU1MwS2VseWlLcmZTM29YL1RGb3dBZzJNaXMyWFpuNy9WVAp0MVhwSFdMV3ljeTFjZVhibEU5cTZ2YmdIdWUwMWZPUmtna09JSHJpVXdwcmxNZFhvNHdKWExNT0Z5NUdRNXp4CnVRV2c5TWZXUmxuRVZmcE5OTnV3MlBsSmYybG1oOFJablJHOTdhOWZ5OXp5QWlYLy82VURSdnJnbzFzd1d6QU4KZFZsQXd5S0tzczQzaldMSjdmRVBCbDhsVTJidm9OVnR0THB0ai9rWk9HVjZXeGVRMEY4UlRQZmFwVkN6bHlhRwpGMHZkUWRWemhjdjRsd0tDQVFFQStTRTVaK0pwUlY1L3RJTjRydS9ES1E0Rnk3WlZ6Yk41YWxUcXo2TXFkOTZnClhtMTNHdlpoWmgrN0htZktqMHNJWWZRS21oK2xxNWsyK3YvMXBWYUZLa0VSd1FNdGM0VHp6bmFNeHRqZE16a2oKc1E3MjZoRUZocnZZWmRtbVVLbjBvMHJVbUNocWJkNU9WRGNLRmR3ekwrNldBVFo3c1RWVlVsc01JSG82YnlYRApqM0FzVThaZjRtTFc4SktkMm4ydjhZRDZ1NE40ZEE0bnozd0tIdUkxY2txRlBZa3BEYlpOcS9GS0VpY3FiVkk5ClVhRDVVOVd0cHg3MVNCYWcrdlJodFA0N2wybngybW5QSFJNUVo0cXY1UitvUUxUeUthMzlYSVU2TmUrcDVxYTkKWXlJaERVcDRRbmRnWDZqNHFUaWdXQnJsaFF3cUFsZWJtbFVha2ZyMlBRS0NBUUJsT0kxS1AzNUdyT0NtR05KWQo4d1VHcjQwQk5LZ3NsU2tYSzhsbHZrUWtjVWtDWWM5eDV0Sm5VeUR2SkdhNVJ3WEtXa3dKM0JuNjBJY1FzZiswCng4MFdNak5udXhXTWtLTmtKOWNKS0RrbUozRDVxK3Jub3F2RlVwNGk0QzduOHViMGw1MFVxTWRqdEozTUtMSksKR2E5ZUNLeTl3akdDNkN5clBZQlRpd1NGa3VESDBXQ09RcFdpUnBKTStYL1MvNXdrVmIwS1VFWGpYSy9XUDc4Rwo1RmYrQVVxdXhjczluMmRnbEM0UkVVMGpoV3YrNG9uVDh3Y2w5OW9wOTFtTVN5SzM4eC9xaHN0cGQvY3hleFZYCnN5b0lyejh4dE9ZVFFFc21xRUduYjBPRWdYYjJockxPOEhHcDFvbGpIQjhQcnRYamZpNnkrN2lvY0ZjVGlvVmkKQjByekFvSUJBUUNpT3cxNzVjZHhvZUQ5ZHVPKzMya3lNZXg2Uk1sSlE0ZGNRK0lJeHBNZ0xmdzJvWWYvS2RReApRWmFEYlVsTjJLbWp4OXlqa3RvNmpYSnRMd01TNE8ya24yemxTS0Z2YVhPdXppcE1UdXAwc2MvWlBBcDBVQk5PCk1zTHZTRFZ5cjhvY0Q5MnpIQlo2WjFxUW9xclJvMnc5MlZzYmZSUTlTS0xBRjRRbEhoTXhUK0V5WmZTbnRUVzUKWVczZ2lVbDcvMzcwZ0c1R2I0MmYzWnYxRkN3dkFsV1ZrSXVGaiswSS9kOUVpY01YWVNQU3Zzd3BmTzcvdGRUNApVRU9mNXdSN2gycWRCelZkRzJPZ1BHamY0UXM4c2VackNSdHl6NkltbkNPeC9SdjBCWmFaR1ptWWxaLzZYTFZGCmF5RjZDYm1yMExFeHpvb0tBdXJVVGFLSmRNUHhyZ0J0QW9JQkFHeU9sN2xFOTdSRURsbFdFbGx5REpqelJxd3kKcldlcXNyRXFDclJtN0FDK0xGL3BJQ0dacEk0QzF1QmRJVUlVY01INDNrSHowazY0NVZ4OCt2UE5PRjYzQjdNNwpPRVhlZmd0Nmo4eW1oWjhFVUQ5VmdQTGhWK3ZqRGNKbkFaVFhqV0xrclR1K3ZEUVBpUGNTVm5UMEtNNjhBR3RGCmh3bjFKaHZoSmJiMVFSaFRocGxBWVE4SlhjcUxtUW93Nng5aG9MVGVxY1ZDazkreGZ6bWUvYXNmclp6NDBlbU4KYjF2VWpidFNrOVZkZU9tOExCVXZqZkwwZmowYUNIMkFCU1RRY1dYWFZIMldQRzB6VExKMjhIb0k1dGw4RzA3Ngp0NGtpQm9BRC8vUW4zM2doSy9wZTJDdFd3Y2VuY3Rxa2lNd21sTVIyckJZNlF2YVUzN1NGZXM3Mno0WT0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K\n token: 0w0vs639mwl6rzwt1ryicepjylnxirlchfs54fyop2vaezl7hl9o6ghu2bljlypok7nqtwlr5ejlvktasozjwzow60qqblph96ua28dqa6kv6ftz54vviq2v88mo1697\n", + "kubelet_identity": [ + { + "client_id": "342e0ba1-a9c4-4f0f-8b02-1c0d3c40ba88", + "object_id": "8a75f0be-74bf-4b31-8a31-58f6e0792678", + "user_assigned_identity_id": "/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/MC_trusted_ai_demo_rg_trai_k8s_cluster_germanywestcentral/providers/Microsoft.ManagedIdentity/userAssignedIdentities/trai_k8s_cluster-agentpool" + } + ], + "kubernetes_version": "1.33", + "linux_profile": [ + { + "admin_username": "", + "ssh_key": [] + } + ], + "location": "germanywestcentral", + "microsoft_defender": [], + "name": "trai_k8s_cluster", + "network_profile": [ + { + "dns_service_ip": "10.0.0.10", + "docker_bridge_cidr": "", + "load_balancer_sku": "standard", + "network_plugin": "azure", + "network_policy": "none", + "pod_cidr": "10.244.0.0/16", + "service_cidr": "10.0.0.0/16" + } + ], + "node_resource_group": "MC_trusted_ai_demo_rg_trai_k8s_cluster_germanywestcentral", + "node_resource_group_id": "/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/MC_trusted_ai_demo_rg_trai_k8s_cluster_germanywestcentral", + "oidc_issuer_enabled": true, + "oidc_issuer_url": "https://germanywestcentral.oic.prod-aks.azure.com/73c07a52-1c4d-40ac-b3b3-2f842e5c8919/18cdbaf9-491d-45a7-91e7-15cd142de9de/", + "oms_agent": [ + { + "log_analytics_workspace_id": "/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/trusted_ai_demo_rg/providers/Microsoft.OperationalInsights/workspaces/aksloganalyticstraik8scluster", + "msi_auth_for_monitoring_enabled": true, + "oms_agent_identity": [] + } + ], + "open_service_mesh_enabled": false, + "private_cluster_enabled": null, + "private_fqdn": "", + "resource_group_name": "trusted_ai_demo_rg", + "role_based_access_control_enabled": true, + "service_mesh_profile": [], + "service_principal": [ + { + "client_id": "msi" + } + ], + "storage_profile": [ + { + "blob_driver_enabled": false, + "disk_driver_enabled": true, + "file_driver_enabled": true, + "snapshot_controller_enabled": true + } + ], + "tags": {}, + "timeouts": null, + "windows_profile": [] + }, + "sensitive_attributes": [ + [ + { + "type": "get_attr", + "value": "kube_admin_config" + } + ], + [ + { + "type": "get_attr", + "value": "kube_admin_config_raw" + } + ], + [ + { + "type": "get_attr", + "value": "kube_config" + } + ], + [ + { + "type": "get_attr", + "value": "kube_config_raw" + } + ] + ], + "identity_schema_version": 0 + } + ] + }, + { + "mode": "data", + "type": "azurerm_resource_group", + "name": "main", + "provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/trusted_ai_demo_rg", + "location": "germanywestcentral", + "managed_by": "", + "name": "trusted_ai_demo_rg", + "tags": {}, + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0 + } + ] + }, + { + "mode": "data", + "type": "azurerm_subscription", + "name": "current", + "provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "display_name": "Azure-Abonnement 1", + "id": "/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3", + "location_placement_id": "Public_2014-09-01", + "quota_id": "PayAsYouGo_2014-09-01", + "spending_limit": "Off", + "state": "Enabled", + "subscription_id": "77677a80-2dea-493d-9867-f1c961b80fb3", + "tags": {}, + "tenant_id": "73c07a52-1c4d-40ac-b3b3-2f842e5c8919", + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0 + } + ] + }, + { + "mode": "data", + "type": "kubernetes_service", + "name": "ingress_nginx", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "index_key": 0, + "schema_version": 0, + "attributes": { + "id": "ingress-nginx/ingress-nginx-controller", + "metadata": [ + { + "annotations": { + "meta.helm.sh/release-name": "ingress-nginx", + "meta.helm.sh/release-namespace": "ingress-nginx", + "service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path": "/healthz" + }, + "generation": 0, + "labels": { + "app.kubernetes.io/component": "controller", + "app.kubernetes.io/instance": "ingress-nginx", + "app.kubernetes.io/managed-by": "Helm", + "app.kubernetes.io/name": "ingress-nginx", + "app.kubernetes.io/part-of": "ingress-nginx", + "app.kubernetes.io/version": "1.11.3", + "helm.sh/chart": "ingress-nginx-4.11.3" + }, + "name": "ingress-nginx-controller", + "namespace": "ingress-nginx", + "resource_version": "1540906", + "uid": "a2779065-00d7-42c3-86ba-5e261a47aac2" + } + ], + "spec": [ + { + "allocate_load_balancer_node_ports": true, + "cluster_ip": "10.0.100.184", + "cluster_ips": [ + "10.0.100.184" + ], + "external_ips": [], + "external_name": "", + "external_traffic_policy": "Cluster", + "health_check_node_port": 0, + "internal_traffic_policy": "Cluster", + "ip_families": [ + "IPv4" + ], + "ip_family_policy": "SingleStack", + "load_balancer_class": "", + "load_balancer_ip": "", + "load_balancer_source_ranges": [], + "port": [ + { + "app_protocol": "http", + "name": "http", + "node_port": 30187, + "port": 80, + "protocol": "TCP", + "target_port": "http" + }, + { + "app_protocol": "https", + "name": "https", + "node_port": 30956, + "port": 443, + "protocol": "TCP", + "target_port": "https" + } + ], + "publish_not_ready_addresses": false, + "selector": { + "app.kubernetes.io/component": "controller", + "app.kubernetes.io/instance": "ingress-nginx", + "app.kubernetes.io/name": "ingress-nginx" + }, + "session_affinity": "None", + "session_affinity_config": [], + "type": "LoadBalancer" + } + ], + "status": [ + { + "load_balancer": [ + { + "ingress": [ + { + "hostname": "", + "ip": "72.144.113.194" + } + ] + } + ] + } + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0 + } + ] + }, + { + "mode": "managed", + "type": "helm_release", + "name": "nginx_ingress", + "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", + "instances": [ + { + "index_key": 0, + "schema_version": 1, + "attributes": { + "atomic": false, + "chart": "ingress-nginx", + "cleanup_on_fail": false, + "create_namespace": true, + "dependency_update": false, + "description": null, + "devel": null, + "disable_crd_hooks": false, + "disable_openapi_validation": false, + "disable_webhooks": false, + "force_update": false, + "id": "ingress-nginx", + "keyring": null, + "lint": false, + "manifest": null, + "max_history": 0, + "metadata": [ + { + "app_version": "1.11.3", + "chart": "ingress-nginx", + "first_deployed": 1766395333, + "last_deployed": 1766395333, + "name": "ingress-nginx", + "namespace": "ingress-nginx", + "notes": "The ingress-nginx controller has been installed.\nIt may take a few minutes for the load balancer IP to be available.\nYou can watch the status by running 'kubectl get service --namespace ingress-nginx ingress-nginx-controller --output wide --watch'\n\nAn example Ingress that makes use of the controller:\n apiVersion: networking.k8s.io/v1\n kind: Ingress\n metadata:\n name: example\n namespace: foo\n spec:\n ingressClassName: nginx\n rules:\n - host: www.example.com\n http:\n paths:\n - pathType: Prefix\n backend:\n service:\n name: exampleService\n port:\n number: 80\n path: /\n # This section is only required if TLS is to be enabled for the Ingress\n tls:\n - hosts:\n - www.example.com\n secretName: example-tls\n\nIf TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided:\n\n apiVersion: v1\n kind: Secret\n metadata:\n name: example-tls\n namespace: foo\n data:\n tls.crt: \u003cbase64 encoded cert\u003e\n tls.key: \u003cbase64 encoded key\u003e\n type: kubernetes.io/tls\n", + "revision": 1, + "values": "{\"controller\":{\"config\":{\"limit-req-status-code\":429},\"metrics\":{\"enabled\":true},\"podAnnotations\":{\"prometheus.io/port\":10254,\"prometheus.io/scrape\":true},\"resources\":{\"limits\":{\"cpu\":\"500m\",\"memory\":\"512Mi\"},\"requests\":{\"cpu\":\"100m\",\"memory\":\"128Mi\"}},\"service\":{\"annotations\":{\"service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path\":\"/healthz\"},\"type\":\"LoadBalancer\"}}}", + "version": "4.11.3" + } + ], + "name": "ingress-nginx", + "namespace": "ingress-nginx", + "pass_credentials": false, + "postrender": [], + "recreate_pods": false, + "render_subchart_notes": true, + "replace": false, + "repository": "https://kubernetes.github.io/ingress-nginx", + "repository_ca_file": null, + "repository_cert_file": null, + "repository_key_file": null, + "repository_password": null, + "repository_username": null, + "reset_values": false, + "reuse_values": false, + "set": [ + { + "name": "controller.config.limit-req-status-code", + "type": "", + "value": "429" + }, + { + "name": "controller.metrics.enabled", + "type": "", + "value": "true" + }, + { + "name": "controller.podAnnotations.prometheus\\.io/port", + "type": "", + "value": "10254" + }, + { + "name": "controller.podAnnotations.prometheus\\.io/scrape", + "type": "", + "value": "true" + }, + { + "name": "controller.resources.limits.cpu", + "type": "", + "value": "500m" + }, + { + "name": "controller.resources.limits.memory", + "type": "", + "value": "512Mi" + }, + { + "name": "controller.resources.requests.cpu", + "type": "", + "value": "100m" + }, + { + "name": "controller.resources.requests.memory", + "type": "", + "value": "128Mi" + }, + { + "name": "controller.service.annotations.service\\.beta\\.kubernetes\\.io/azure-load-balancer-health-probe-request-path", + "type": "", + "value": "/healthz" + }, + { + "name": "controller.service.type", + "type": "", + "value": "LoadBalancer" + } + ], + "set_list": [], + "set_sensitive": [], + "skip_crds": false, + "status": "deployed", + "timeout": 600, + "upgrade_install": null, + "values": null, + "verify": false, + "version": "4.11.3", + "wait": true, + "wait_for_jobs": false + }, + "sensitive_attributes": [ + [ + { + "type": "get_attr", + "value": "repository_password" + } + ] + ], + "identity_schema_version": 0, + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_config_map", + "name": "app_config", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "binary_data": {}, + "data": { + "APP_DEBUG": "false", + "APP_ENV": "production", + "APP_FAKER_LOCALE": "en_US", + "APP_FALLBACK_LOCALE": "en", + "APP_LOCALE": "en", + "APP_NAME": "laravel-app", + "APP_URL": "http://72.144.113.194", + "BROADCAST_CONNECTION": "log", + "CACHE_STORE": "database", + "FILESYSTEM_DISK": "local", + "LOG_CHANNEL": "stack", + "LOG_LEVEL": "info", + "LOG_STACK": "single", + "MAIL_MAILER": "log", + "QUEUE_CONNECTION": "database", + "SESSION_CONNECTION": "pgsql", + "SESSION_DRIVER": "database", + "SESSION_ENCRYPT": "false", + "SESSION_LIFETIME": "120", + "VITE_APP_NAME": "laravel-app" + }, + "id": "laravel-app/laravel-app-config", + "immutable": false, + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "laravel-app-config", + "namespace": "laravel-app", + "resource_version": "1557127", + "uid": "d34a412e-321d-4b8c-91d8-0c823ee1a34b" + } + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "ConfigMap", + "name": "laravel-app-config", + "namespace": "laravel-app" + }, + "private": "bnVsbA==", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "data.kubernetes_service.ingress_nginx", + "helm_release.nginx_ingress", + "kubernetes_namespace.app" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_deployment", + "name": "app", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "status": "tainted", + "schema_version": 1, + "attributes": { + "id": "laravel-app/laravel-app", + "metadata": [ + { + "annotations": null, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "laravel-app", + "namespace": "laravel-app", + "resource_version": "", + "uid": "" + } + ], + "spec": [ + { + "min_ready_seconds": 0, + "paused": false, + "progress_deadline_seconds": 600, + "replicas": "2", + "revision_history_limit": 10, + "selector": [ + { + "match_expressions": [], + "match_labels": { + "app": "laravel-app" + } + } + ], + "strategy": [ + { + "rolling_update": [ + { + "max_surge": "1", + "max_unavailable": "0" + } + ], + "type": "RollingUpdate" + } + ], + "template": [ + { + "metadata": [ + { + "annotations": null, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform", + "version": "latest" + }, + "name": "", + "namespace": "", + "resource_version": "", + "uid": "" + } + ], + "spec": [ + { + "active_deadline_seconds": 0, + "affinity": [], + "automount_service_account_token": true, + "container": [ + { + "args": null, + "command": null, + "env": [], + "env_from": [ + { + "config_map_ref": [ + { + "name": "laravel-app-config", + "optional": false + } + ], + "prefix": "", + "secret_ref": [] + }, + { + "config_map_ref": [], + "prefix": "", + "secret_ref": [ + { + "name": "laravel-app-secrets", + "optional": false + } + ] + }, + { + "config_map_ref": [], + "prefix": "", + "secret_ref": [ + { + "name": "laravel-app-db-credentials", + "optional": false + } + ] + } + ], + "image": "laravelappreg.azurecr.io/laravel-app:v1.0.0", + "image_pull_policy": "", + "lifecycle": [], + "liveness_probe": [ + { + "exec": [], + "failure_threshold": 3, + "grpc": [], + "http_get": [ + { + "host": "", + "http_header": [], + "path": "/", + "port": "80", + "scheme": "HTTP" + } + ], + "initial_delay_seconds": 30, + "period_seconds": 10, + "success_threshold": 1, + "tcp_socket": [], + "timeout_seconds": 5 + } + ], + "name": "laravel-app", + "port": [ + { + "container_port": 80, + "host_ip": "", + "host_port": 0, + "name": "http", + "protocol": "TCP" + } + ], + "readiness_probe": [ + { + "exec": [], + "failure_threshold": 3, + "grpc": [], + "http_get": [ + { + "host": "", + "http_header": [], + "path": "/", + "port": "80", + "scheme": "HTTP" + } + ], + "initial_delay_seconds": 10, + "period_seconds": 5, + "success_threshold": 1, + "tcp_socket": [], + "timeout_seconds": 3 + } + ], + "resources": [ + { + "limits": { + "cpu": "500m", + "memory": "512Mi" + }, + "requests": { + "cpu": "100m", + "memory": "256Mi" + } + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "", + "tty": false, + "volume_device": [], + "volume_mount": [], + "working_dir": "" + } + ], + "dns_config": [], + "dns_policy": "ClusterFirst", + "enable_service_links": true, + "host_aliases": [], + "host_ipc": false, + "host_network": false, + "host_pid": false, + "hostname": "", + "image_pull_secrets": [ + { + "name": "acr-secret" + } + ], + "init_container": [], + "node_name": "", + "node_selector": null, + "os": [], + "priority_class_name": "", + "readiness_gate": [], + "restart_policy": "Always", + "runtime_class_name": "", + "scheduler_name": "", + "security_context": [], + "service_account_name": "", + "share_process_namespace": false, + "subdomain": "", + "termination_grace_period_seconds": 30, + "toleration": [], + "topology_spread_constraint": [], + "volume": [] + } + ] + } + ] + } + ], + "timeouts": null, + "wait_for_rollout": true + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": null, + "kind": null, + "name": null, + "namespace": null + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6NjAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "data.kubernetes_service.ingress_nginx", + "helm_release.nginx_ingress", + "kubernetes_config_map.app_config", + "kubernetes_namespace.app", + "kubernetes_persistent_volume_claim.postgresql", + "kubernetes_secret.app_secrets", + "kubernetes_secret.db_credentials", + "kubernetes_secret.postgresql", + "kubernetes_service.postgresql", + "kubernetes_stateful_set.postgresql", + "random_password.postgresql_admin_password" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_namespace", + "name": "app", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "laravel-app", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform", + "name": "laravel-app" + }, + "name": "laravel-app", + "resource_version": "1540685", + "uid": "6b28a548-a8de-4a53-a684-4afe2854a522" + } + ], + "timeouts": null, + "wait_for_default_service_account": false + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Namespace", + "name": "laravel-app" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjozMDAwMDAwMDAwMDB9fQ==", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_persistent_volume_claim", + "name": "postgresql", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "laravel-app/postgresql-pvc", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "postgresql-pvc", + "namespace": "laravel-app", + "resource_version": "1543411", + "uid": "0f1153a7-dce9-40fe-bf26-9626e8af624c" + } + ], + "spec": [ + { + "access_modes": [ + "ReadWriteOnce" + ], + "resources": [ + { + "limits": {}, + "requests": { + "storage": "10Gi" + } + } + ], + "selector": [], + "storage_class_name": "default", + "volume_mode": "Filesystem", + "volume_name": "pvc-0f1153a7-dce9-40fe-bf26-9626e8af624c" + } + ], + "timeouts": null, + "wait_until_bound": false + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDB9fQ==", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "kubernetes_namespace.app" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_secret", + "name": "app_secrets", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "binary_data": null, + "binary_data_wo": null, + "binary_data_wo_revision": null, + "data": { + "APP_KEY": "base64:hcb6PXhQOot3SKMl7USAimUWY3c3OGVaRFtaJkDRiTc=" + }, + "data_wo": null, + "data_wo_revision": null, + "id": "laravel-app/laravel-app-secrets", + "immutable": false, + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "laravel-app-secrets", + "namespace": "laravel-app", + "resource_version": "1540689", + "uid": "7822f400-5b2f-43d4-8c2a-ed450d9513a3" + } + ], + "timeouts": null, + "type": "Opaque", + "wait_for_service_account_token": true + }, + "sensitive_attributes": [ + [ + { + "type": "get_attr", + "value": "binary_data" + } + ], + [ + { + "type": "get_attr", + "value": "data" + } + ], + [ + { + "type": "get_attr", + "value": "data" + }, + { + "type": "index", + "value": { + "value": "APP_KEY", + "type": "string" + } + } + ] + ], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Secret", + "name": "laravel-app-secrets", + "namespace": "laravel-app" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMH19", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "kubernetes_namespace.app" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_secret", + "name": "db_credentials", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "binary_data": null, + "binary_data_wo": null, + "binary_data_wo_revision": null, + "data": { + "DB_CONNECTION": "pgsql", + "DB_CONNECTION2": "pgsql", + "DB_DATABASE": "laravel_app", + "DB_DATABASE2": "laravel_app", + "DB_HOST": "postgresql.laravel-app.svc.cluster.local", + "DB_HOST2": "postgresql.laravel-app.svc.cluster.local", + "DB_PASSWORD": "SecureLaravelPwd2024!", + "DB_PASSWORD2": "SecureLaravelPwd2024!", + "DB_PORT": "5432", + "DB_PORT2": "5432", + "DB_USERNAME": "pgadmin", + "DB_USERNAME2": "pgadmin" + }, + "data_wo": null, + "data_wo_revision": null, + "id": "laravel-app/laravel-app-db-credentials", + "immutable": false, + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "laravel-app-db-credentials", + "namespace": "laravel-app", + "resource_version": "1557128", + "uid": "dee29f69-423b-4d9d-8d8d-d089cc6cf054" + } + ], + "timeouts": null, + "type": "Opaque", + "wait_for_service_account_token": true + }, + "sensitive_attributes": [ + [ + { + "type": "get_attr", + "value": "binary_data" + } + ], + [ + { + "type": "get_attr", + "value": "data" + } + ], + [ + { + "type": "get_attr", + "value": "data" + }, + { + "type": "index", + "value": { + "value": "DB_PASSWORD", + "type": "string" + } + } + ], + [ + { + "type": "get_attr", + "value": "data" + }, + { + "type": "index", + "value": { + "value": "DB_PASSWORD2", + "type": "string" + } + } + ] + ], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Secret", + "name": "laravel-app-db-credentials", + "namespace": "laravel-app" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMH19", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "kubernetes_namespace.app", + "kubernetes_persistent_volume_claim.postgresql", + "kubernetes_secret.postgresql", + "kubernetes_service.postgresql", + "kubernetes_stateful_set.postgresql", + "random_password.postgresql_admin_password" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_secret", + "name": "postgresql", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "binary_data": null, + "binary_data_wo": null, + "binary_data_wo_revision": null, + "data": { + "POSTGRES_DB": "laravel_app", + "POSTGRES_PASSWORD": "SecureLaravelPwd2024!", + "POSTGRES_USER": "pgadmin" + }, + "data_wo": null, + "data_wo_revision": null, + "id": "laravel-app/postgresql-credentials", + "immutable": false, + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "postgresql-credentials", + "namespace": "laravel-app", + "resource_version": "1543374", + "uid": "07259e48-3427-45cd-b5c4-455ee73cf233" + } + ], + "timeouts": null, + "type": "Opaque", + "wait_for_service_account_token": true + }, + "sensitive_attributes": [ + [ + { + "type": "get_attr", + "value": "binary_data" + } + ], + [ + { + "type": "get_attr", + "value": "data" + } + ], + [ + { + "type": "get_attr", + "value": "data" + }, + { + "type": "index", + "value": { + "value": "POSTGRES_PASSWORD", + "type": "string" + } + } + ] + ], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Secret", + "name": "postgresql-credentials", + "namespace": "laravel-app" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMH19", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "kubernetes_namespace.app" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_service", + "name": "postgresql", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "id": "laravel-app/postgresql", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "postgresql", + "namespace": "laravel-app", + "resource_version": "1543628", + "uid": "0b24bf47-f0e1-4807-8815-a0dbf44442a5" + } + ], + "spec": [ + { + "allocate_load_balancer_node_ports": true, + "cluster_ip": "None", + "cluster_ips": [ + "None" + ], + "external_ips": [], + "external_name": "", + "external_traffic_policy": "", + "health_check_node_port": 0, + "internal_traffic_policy": "Cluster", + "ip_families": [ + "IPv4" + ], + "ip_family_policy": "SingleStack", + "load_balancer_class": "", + "load_balancer_ip": "", + "load_balancer_source_ranges": [], + "port": [ + { + "app_protocol": "", + "name": "postgresql", + "node_port": 0, + "port": 5432, + "protocol": "TCP", + "target_port": "5432" + } + ], + "publish_not_ready_addresses": false, + "selector": { + "app": "postgresql" + }, + "session_affinity": "None", + "session_affinity_config": [], + "type": "ClusterIP" + } + ], + "status": [ + { + "load_balancer": [ + { + "ingress": [] + } + ] + } + ], + "timeouts": null, + "wait_for_load_balancer": true + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Service", + "name": "postgresql", + "namespace": "laravel-app" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDB9LCJzY2hlbWFfdmVyc2lvbiI6IjEifQ==", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "kubernetes_namespace.app", + "kubernetes_persistent_volume_claim.postgresql", + "kubernetes_secret.postgresql", + "kubernetes_stateful_set.postgresql" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_stateful_set", + "name": "postgresql", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "id": "laravel-app/postgresql", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 1, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "postgresql", + "namespace": "laravel-app", + "resource_version": "1543602", + "uid": "2bd31dfb-e27e-499b-ac62-115d90750900" + } + ], + "spec": [ + { + "min_ready_seconds": 0, + "persistent_volume_claim_retention_policy": [ + { + "when_deleted": "Retain", + "when_scaled": "Retain" + } + ], + "pod_management_policy": "OrderedReady", + "replicas": "1", + "revision_history_limit": 0, + "selector": [ + { + "match_expressions": [], + "match_labels": { + "app": "postgresql" + } + } + ], + "service_name": "postgresql", + "template": [ + { + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "postgresql", + "environment": "production", + "managed-by": "terraform" + }, + "name": "", + "namespace": "", + "resource_version": "", + "uid": "" + } + ], + "spec": [ + { + "active_deadline_seconds": 0, + "affinity": [], + "automount_service_account_token": true, + "container": [ + { + "args": [], + "command": [], + "env": [], + "env_from": [ + { + "config_map_ref": [], + "prefix": "", + "secret_ref": [ + { + "name": "postgresql-credentials", + "optional": false + } + ] + } + ], + "image": "postgres:16-alpine", + "image_pull_policy": "IfNotPresent", + "lifecycle": [], + "liveness_probe": [ + { + "exec": [ + { + "command": [ + "pg_isready", + "-U", + "pgadmin" + ] + } + ], + "failure_threshold": 3, + "grpc": [], + "http_get": [], + "initial_delay_seconds": 30, + "period_seconds": 10, + "success_threshold": 1, + "tcp_socket": [], + "timeout_seconds": 5 + } + ], + "name": "postgresql", + "port": [ + { + "container_port": 5432, + "host_ip": "", + "host_port": 0, + "name": "postgresql", + "protocol": "TCP" + } + ], + "readiness_probe": [ + { + "exec": [ + { + "command": [ + "pg_isready", + "-U", + "pgadmin" + ] + } + ], + "failure_threshold": 3, + "grpc": [], + "http_get": [], + "initial_delay_seconds": 5, + "period_seconds": 5, + "success_threshold": 1, + "tcp_socket": [], + "timeout_seconds": 3 + } + ], + "resources": [ + { + "limits": { + "cpu": "1", + "memory": "1Gi" + }, + "requests": { + "cpu": "250m", + "memory": "512Mi" + } + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "File", + "tty": false, + "volume_device": [], + "volume_mount": [ + { + "mount_path": "/var/lib/postgresql/data", + "mount_propagation": "None", + "name": "postgresql-storage", + "read_only": false, + "sub_path": "postgres", + "sub_path_expr": "" + } + ], + "working_dir": "" + } + ], + "dns_config": [], + "dns_policy": "ClusterFirst", + "enable_service_links": true, + "host_aliases": [], + "host_ipc": false, + "host_network": false, + "host_pid": false, + "hostname": "", + "image_pull_secrets": [], + "init_container": [], + "node_name": "", + "node_selector": {}, + "os": [], + "priority_class_name": "", + "readiness_gate": [], + "restart_policy": "Always", + "runtime_class_name": "", + "scheduler_name": "default-scheduler", + "security_context": [], + "service_account_name": "", + "share_process_namespace": false, + "subdomain": "", + "termination_grace_period_seconds": 30, + "toleration": [], + "topology_spread_constraint": [], + "volume": [ + { + "aws_elastic_block_store": [], + "azure_disk": [], + "azure_file": [], + "ceph_fs": [], + "cinder": [], + "config_map": [], + "csi": [], + "downward_api": [], + "empty_dir": [], + "ephemeral": [], + "fc": [], + "flex_volume": [], + "flocker": [], + "gce_persistent_disk": [], + "git_repo": [], + "glusterfs": [], + "host_path": [], + "iscsi": [], + "local": [], + "name": "postgresql-storage", + "nfs": [], + "persistent_volume_claim": [ + { + "claim_name": "postgresql-pvc", + "read_only": false + } + ], + "photon_persistent_disk": [], + "projected": [], + "quobyte": [], + "rbd": [], + "secret": [], + "vsphere_volume": [] + } + ] + } + ] + } + ], + "update_strategy": [], + "volume_claim_template": [] + } + ], + "timeouts": null, + "wait_for_rollout": true + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": "apps/v1", + "kind": "StatefulSet", + "name": "postgresql", + "namespace": "laravel-app" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6NjAwMDAwMDAwMDAwLCJyZWFkIjo2MDAwMDAwMDAwMDAsInVwZGF0ZSI6NjAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIxIn0=", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "kubernetes_namespace.app", + "kubernetes_persistent_volume_claim.postgresql", + "kubernetes_secret.postgresql" + ] + } + ] + }, + { + "mode": "managed", + "type": "random_string", + "name": "suffix", + "provider": "provider[\"registry.terraform.io/hashicorp/random\"]", + "instances": [ + { + "schema_version": 2, + "attributes": { + "id": "sab31xmv", + "keepers": null, + "length": 8, + "lower": true, + "min_lower": 0, + "min_numeric": 0, + "min_special": 0, + "min_upper": 0, + "number": true, + "numeric": true, + "override_special": null, + "result": "sab31xmv", + "special": false, + "upper": false + }, + "sensitive_attributes": [], + "identity_schema_version": 0 + } + ] + } + ], + "check_results": null +} diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tfstate.backup b/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tfstate.backup new file mode 100644 index 0000000..67276d1 --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tfstate.backup @@ -0,0 +1,1598 @@ +{ + "version": 4, + "terraform_version": "1.14.3", + "serial": 20, + "lineage": "a5bf7cb3-3a02-ecee-c1d3-629bb386547f", + "outputs": { + "aks_cluster_name": { + "value": "trai_k8s_cluster", + "type": "string" + }, + "app_namespace": { + "value": "laravel-app", + "type": "string" + }, + "app_service_name": { + "value": "laravel-app", + "type": "string" + }, + "app_url": { + "value": "http://72.144.113.194", + "type": "string" + }, + "db_restore_info": { + "value": "Database restore disabled in Terraform. Use manual restore via kubectl (see db-restore.tf for instructions)", + "type": "string" + }, + "deployment_instructions": { + "value": "========================================\nDeployment Complete!\n========================================\n\n1. Get kubectl credentials:\n az aks get-credentials --resource-group trusted_ai_demo_rg --name trai_k8s_cluster\n\n2. Check application status:\n kubectl get pods -n laravel-app\n kubectl get svc -n laravel-app\n kubectl get ingress -n laravel-app\n\n3. Check database restore job (if enabled):\n kubectl get jobs -n laravel-app\n kubectl logs -n laravel-app -l job-type=database-restore\n\n4. Access the application:\n http://72.144.113.194\n\n5. Port-forward (if ingress not ready):\n kubectl port-forward -n laravel-app svc/laravel-app 8080:80\n\n6. View logs:\n kubectl logs -n laravel-app -l app=laravel-app -f\n\n7. Database connection details (in-cluster):\n Host: postgresql.laravel-app.svc.cluster.local\n Database: laravel_app\n Username: pgadmin\n\n8. Restore database manually:\n kubectl cp ../backups/backup_backend_20251203_101741.dump laravel-app/postgresql-0:/tmp/backup.dump\n kubectl exec -it -n laravel-app postgresql-0 -- pg_restore -U pgadmin -d laravel_app --clean --if-exists /tmp/backup.dump\n\n========================================\n", + "type": "string" + }, + "ingress_enabled": { + "value": true, + "type": "bool" + }, + "ingress_ip": { + "value": "72.144.113.194", + "type": "string" + }, + "postgresql_admin_username": { + "value": "pgadmin", + "type": "string", + "sensitive": true + }, + "postgresql_connection_string": { + "value": "postgresql://pgadmin:SecureLaravelPwd2024!@postgresql.laravel-app.svc.cluster.local:5432/laravel_app", + "type": "string", + "sensitive": true + }, + "postgresql_database_name": { + "value": "laravel_app", + "type": "string" + }, + "postgresql_service_fqdn": { + "value": "postgresql.laravel-app.svc.cluster.local", + "type": "string" + }, + "postgresql_service_name": { + "value": "postgresql", + "type": "string" + }, + "resource_group_name": { + "value": "trusted_ai_demo_rg", + "type": "string" + }, + "ssl_enabled": { + "value": false, + "type": "bool" + } + }, + "resources": [ + { + "mode": "data", + "type": "azurerm_kubernetes_cluster", + "name": "main", + "provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "aci_connector_linux": [], + "agent_pool_profile": [ + { + "auto_scaling_enabled": true, + "count": 2, + "max_count": 3, + "max_pods": 250, + "min_count": 2, + "name": "agentpool", + "node_labels": {}, + "node_public_ip_enabled": false, + "node_public_ip_prefix_id": "", + "node_taints": [], + "orchestrator_version": "1.33", + "os_disk_size_gb": 128, + "os_type": "Linux", + "tags": {}, + "type": "VirtualMachineScaleSets", + "upgrade_settings": [ + { + "drain_timeout_in_minutes": 0, + "max_surge": "10%", + "max_unavailable": "0", + "node_soak_duration_in_minutes": 0, + "undrainable_node_behavior": "" + } + ], + "vm_size": "Standard_D2as_v6", + "vnet_subnet_id": "", + "zones": [ + "1", + "2" + ] + } + ], + "api_server_authorized_ip_ranges": [], + "azure_active_directory_role_based_access_control": [], + "azure_policy_enabled": false, + "current_kubernetes_version": "1.33.5", + "disk_encryption_set_id": "", + "dns_prefix": "traik8scluster", + "fqdn": "traik8scluster-op0sir9n.hcp.germanywestcentral.azmk8s.io", + "http_application_routing_enabled": false, + "http_application_routing_zone_name": "", + "id": "/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/trusted_ai_demo_rg/providers/Microsoft.ContainerService/managedClusters/trai_k8s_cluster", + "identity": [ + { + "identity_ids": [], + "principal_id": "d381648c-78a6-44e2-bd90-f1e4c4d72cfc", + "tenant_id": "73c07a52-1c4d-40ac-b3b3-2f842e5c8919", + "type": "SystemAssigned" + } + ], + "ingress_application_gateway": [], + "key_management_service": [], + "key_vault_secrets_provider": [], + "kube_admin_config": [], + "kube_admin_config_raw": "", + "kube_config": [ + { + "client_certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZIakNDQXdhZ0F3SUJBZ0lSQUt4cUdaR3NjVVVjV2pnY0ZNTE95cDh3RFFZSktvWklodmNOQVFFTEJRQXcKRFRFTE1Ba0dBMVVFQXhNQ1kyRXdIaGNOTWpVeE1qRTRNVEl3TkRRMFdoY05NamN4TWpFNE1USXhORFEwV2pBdwpNUmN3RlFZRFZRUUtFdzV6ZVhOMFpXMDZiV0Z6ZEdWeWN6RVZNQk1HQTFVRUF4TU1iV0Z6ZEdWeVkyeHBaVzUwCk1JSUNJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBd000T3o3NkI1MFFXeTB2UzdYYnEKR1RiMTVNSWUzZmtMUDliM3hNS1lWZVRCeWNsWExmSkJjRHRNRWlDa1Ixbi9FdGxIV0RwbGx5Qkx3Mjl2UWpOUgpsblFLMDZzY1gvNDdvSTJFODkxUjYvTC9NaWIxV2RiTUJ2R2pvdkhVNnZOdE9tbmdOaFVOT2tXYXRabGVPQUMyCkVxNE5WWFlKWW85SnlKRXlhNlgzd3lDazhJZzQyeEtYZjFSZDJVK0RYVTZndCtIYWk5THpsellob01ra20rdWQKc2RDSFp0S0lIb3cvV013NkdyTHczelQ0dm5jWVNoL2hOYUVZUm1yNytpRWFQbmEwRnNXa1FtYmE4R3RvZmdodQpDLzNnTTBNSHY5VnFQTDhOWDNabXA3VERyODhVclpLSnNWTVBQLzFOL2ZER3k5bzVQS3hqb2c2SUJGNStxcWdqCkk0MFZpWHdEcW9Md3RpeHhDNytRYmRLMFZBSGI5UzlmcnZaZWZCeU9qdUlzR21MOHAxem5RUlhmRk5vRENhdmMKVk9mOFgzU3BNMEpJRFBMVUpqTitnRzdaYTlHU2JubFZlMVBwK3dZcHRDUXRqVjNScXFGOUpJd2lXRVdLQ2FaTApWSXV3VENHOU0yeEYxNjZjMWk1dGcwUjZkTGwzdW9BYkVleXdNUE1TcWloMUxLdmFZNUcyQjJiclp0K3hjM3NYCldKQW5TamJhOUtnZnk5d3BlNVI0bnUwejVjMUdrSGRLQjVwZDBZVWxINlRpY0EvV0Q5SXdDNFZDdWlJcTlmNGsKR1hpS2JEQXp6UFZwZjZFcjVCV0pVZzhJWjBnb0svaWxOdXJWNjhvSmlmRW9qdXFIWTJDRm9PMFhRanhtZkxwVgo5cFVoVGc5YjEzNlNKWDF5YlN6eFZmc0NBd0VBQWFOV01GUXdEZ1lEVlIwUEFRSC9CQVFEQWdXZ01CTUdBMVVkCkpRUU1NQW9HQ0NzR0FRVUZCd01DTUF3R0ExVWRFd0VCL3dRQ01BQXdId1lEVlIwakJCZ3dGb0FVVXZKU1RHR0gKUzRhd3dKZ29lV3VGYk55ZElvMHdEUVlKS29aSWh2Y05BUUVMQlFBRGdnSUJBQ2I5Sy82cnV1QlNRdk04c1VmQQpNWWkvOUZUaVNHR2pqeVRRdFlBMncxRVA2aHpocVMwWXBzdWhwWGVtS3VtcXJPZzZ0QzZERDR3Z3B0OWNYdExsCm14WHQ3V2d2bUNyckt0UHB0OHlzNjN2NzBzQXNDTDNJZmg1SHoxVzlHSzdHbFRub0w5ODI5dTN6NWlESldTeUUKY0NsaDhlTzVnNVltaUcvV05qZDFzZHlvV2NJbEdlNi9FRnFaWTNkYkVVSlVSVUZHNzZLV0tndE1UejZQRlVQRwo5YXdOMjdiL0tPaVBtbmI0WkVVbmVweTRTcHhabis1UGRQT2g2Q0VFdU9FOWNZQ2pTRzc0TXZvZkY4SHlpOFVKCmYrQjJ2bVE5bEN6SGJzSWZNdVpyeFRZd21DTGhlU3RndVVWUnhqSm4zaTVVdGJwMTBkWUJQaThtU2FVV0p1b3oKS0FNRnFsTWdNOVhDc3FUZ05yWm01U3ZuYmdjUHozbW0wSmRScUhtQS9EUzhWckl6NGo2YVdueUpzdlQ0MUFDTApVMnRZbmw0MlUrSFRtYndFSGFla0J4WDRiTlBNekRYVzk1SktVT2NySWxkM25CVUpMY3draTRVSnpVQ0pkZDl4CmEzOVVLTXUzUVh2V1phRkFUOWFEN2QzWXUxN0IxOG84V2liNlhPRzFnWXB0L3U2L2hzTWcrc1lVdmxMd2lOQXIKQnpHV2xjUUtXNDZETEFJQXlBdkxuaDlhQnJ6aVY2aHkyUm0rTmZwVkFlUUd4WVAzRzgyRGdwVFhtUXZkNTBaOQo3L0pnQVo1R01PT1hMN0ozcURFVHhDY3NLeUZYdTNielE5dmhWaXJ6MTdoNi9naE5hTmZFcXF6T2pQZStRTjFNCkhjZFFoNlFWUkt1dlpzbTRrZW1aSDVOaQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", + "client_key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBd000T3o3NkI1MFFXeTB2UzdYYnFHVGIxNU1JZTNma0xQOWIzeE1LWVZlVEJ5Y2xYCkxmSkJjRHRNRWlDa1Ixbi9FdGxIV0RwbGx5Qkx3Mjl2UWpOUmxuUUswNnNjWC80N29JMkU4OTFSNi9ML01pYjEKV2RiTUJ2R2pvdkhVNnZOdE9tbmdOaFVOT2tXYXRabGVPQUMyRXE0TlZYWUpZbzlKeUpFeWE2WDN3eUNrOElnNAoyeEtYZjFSZDJVK0RYVTZndCtIYWk5THpsellob01ra20rdWRzZENIWnRLSUhvdy9XTXc2R3JMdzN6VDR2bmNZClNoL2hOYUVZUm1yNytpRWFQbmEwRnNXa1FtYmE4R3RvZmdodUMvM2dNME1IdjlWcVBMOE5YM1ptcDdURHI4OFUKclpLSnNWTVBQLzFOL2ZER3k5bzVQS3hqb2c2SUJGNStxcWdqSTQwVmlYd0Rxb0x3dGl4eEM3K1FiZEswVkFIYgo5UzlmcnZaZWZCeU9qdUlzR21MOHAxem5RUlhmRk5vRENhdmNWT2Y4WDNTcE0wSklEUExVSmpOK2dHN1phOUdTCmJubFZlMVBwK3dZcHRDUXRqVjNScXFGOUpJd2lXRVdLQ2FaTFZJdXdUQ0c5TTJ4RjE2NmMxaTV0ZzBSNmRMbDMKdW9BYkVleXdNUE1TcWloMUxLdmFZNUcyQjJiclp0K3hjM3NYV0pBblNqYmE5S2dmeTl3cGU1UjRudTB6NWMxRwprSGRLQjVwZDBZVWxINlRpY0EvV0Q5SXdDNFZDdWlJcTlmNGtHWGlLYkRBenpQVnBmNkVyNUJXSlVnOElaMGdvCksvaWxOdXJWNjhvSmlmRW9qdXFIWTJDRm9PMFhRanhtZkxwVjlwVWhUZzliMTM2U0pYMXliU3p4VmZzQ0F3RUEKQVFLQ0FnQXJhZnF4MHA3am1WUmNvSTAxUlZKL0xQMy9iYXFQcHZWZWdhUTIzTzU2WU5FMkE2MHF2bUwzeDVKZwpmcE1mTWVlYXNldzd0S0tOZmdzeFRaVkszYzFnU0c3cnJKcnVEb3dVb2VrdkZFWFBFRjlnUnA5MmhFUmF4K3hsCjAzaHpFeThrT1JUd2VKc0s1UU5xSXNleGFvK1U4ell6YVNuQjFLdnppcVpFMFJGU0ViYXlKcXBPd2dmMDl1Vk8KK25XRS9DQjZaMVBUcWtPSlovbjRmM0pMM2FrYmtlMzdQQ3JFWGlZaGM1TWQwZThsQTRPRE44UHpaT3hNTENNUQpNQ1RlOUxjNUczZC9yUzlJZUdxU09rMUVHOWZUd1czTGZTdHdpYmlyUndYVzJnMVJkQVdnUDh2VExhenRqZHRnCk9FUm9pdC9vcFJIQVZMejJqN2tXNnNRdzN6MVI3a3d2bDJ5aHlPL1l6NWp5K1ZHOUgra0pmeEwzM0o4OWxmSEEKblNIbm9aZGhzN1Zxa3JLN3pIbnlsZEUzZVBqenRUS2JMaGRoeWVqTU5lRmlmeXdzVTFKQkpnYnZRSDBHSmpxbQpNMkRXRWtQVzA5L2ZHbVpKM2NVVy8zVmM4MG5FUC9lYWxxZnVPM3QvamwxdEtUaGg3WC9lTXlqUDRvSTFpV0ZpCkZ3eDByWGxOSVcrSnVuNjFtVTN6eENpZzhDVXI1SmNsMURPZjNRY2VaeklmV0dxYWNRK1lWeDErQTErVE01d0wKdkl1ek92MkFoamVscE5sVEFTMUtOemxidVRwWXVubnVLUERKK3FQWWhDV3cxMGJXcXJvTEw1dzgwQUJXaGRGLwpCNjBBNWNuSSs3eVNJT1crbzAvNW1BUm9hVHhiY1JUTVV2aVFVMHplK0UzN3NJTmt5UUtDQVFFQXhoOHl3Qnh5CkpySW5mVnl0TkFvTDFVK2NyRmliekRRQTI5MlpUTHl0MG5VLzlCZ2t5ZXlZL2JId2pzeitwTzR6anNERUFYQSsKcjZ1NmZySm9PMU9lbzlkZUs5UW5lc1NadHZLSG9HU1MwS2VseWlLcmZTM29YL1RGb3dBZzJNaXMyWFpuNy9WVAp0MVhwSFdMV3ljeTFjZVhibEU5cTZ2YmdIdWUwMWZPUmtna09JSHJpVXdwcmxNZFhvNHdKWExNT0Z5NUdRNXp4CnVRV2c5TWZXUmxuRVZmcE5OTnV3MlBsSmYybG1oOFJablJHOTdhOWZ5OXp5QWlYLy82VURSdnJnbzFzd1d6QU4KZFZsQXd5S0tzczQzaldMSjdmRVBCbDhsVTJidm9OVnR0THB0ai9rWk9HVjZXeGVRMEY4UlRQZmFwVkN6bHlhRwpGMHZkUWRWemhjdjRsd0tDQVFFQStTRTVaK0pwUlY1L3RJTjRydS9ES1E0Rnk3WlZ6Yk41YWxUcXo2TXFkOTZnClhtMTNHdlpoWmgrN0htZktqMHNJWWZRS21oK2xxNWsyK3YvMXBWYUZLa0VSd1FNdGM0VHp6bmFNeHRqZE16a2oKc1E3MjZoRUZocnZZWmRtbVVLbjBvMHJVbUNocWJkNU9WRGNLRmR3ekwrNldBVFo3c1RWVlVsc01JSG82YnlYRApqM0FzVThaZjRtTFc4SktkMm4ydjhZRDZ1NE40ZEE0bnozd0tIdUkxY2txRlBZa3BEYlpOcS9GS0VpY3FiVkk5ClVhRDVVOVd0cHg3MVNCYWcrdlJodFA0N2wybngybW5QSFJNUVo0cXY1UitvUUxUeUthMzlYSVU2TmUrcDVxYTkKWXlJaERVcDRRbmRnWDZqNHFUaWdXQnJsaFF3cUFsZWJtbFVha2ZyMlBRS0NBUUJsT0kxS1AzNUdyT0NtR05KWQo4d1VHcjQwQk5LZ3NsU2tYSzhsbHZrUWtjVWtDWWM5eDV0Sm5VeUR2SkdhNVJ3WEtXa3dKM0JuNjBJY1FzZiswCng4MFdNak5udXhXTWtLTmtKOWNKS0RrbUozRDVxK3Jub3F2RlVwNGk0QzduOHViMGw1MFVxTWRqdEozTUtMSksKR2E5ZUNLeTl3akdDNkN5clBZQlRpd1NGa3VESDBXQ09RcFdpUnBKTStYL1MvNXdrVmIwS1VFWGpYSy9XUDc4Rwo1RmYrQVVxdXhjczluMmRnbEM0UkVVMGpoV3YrNG9uVDh3Y2w5OW9wOTFtTVN5SzM4eC9xaHN0cGQvY3hleFZYCnN5b0lyejh4dE9ZVFFFc21xRUduYjBPRWdYYjJockxPOEhHcDFvbGpIQjhQcnRYamZpNnkrN2lvY0ZjVGlvVmkKQjByekFvSUJBUUNpT3cxNzVjZHhvZUQ5ZHVPKzMya3lNZXg2Uk1sSlE0ZGNRK0lJeHBNZ0xmdzJvWWYvS2RReApRWmFEYlVsTjJLbWp4OXlqa3RvNmpYSnRMd01TNE8ya24yemxTS0Z2YVhPdXppcE1UdXAwc2MvWlBBcDBVQk5PCk1zTHZTRFZ5cjhvY0Q5MnpIQlo2WjFxUW9xclJvMnc5MlZzYmZSUTlTS0xBRjRRbEhoTXhUK0V5WmZTbnRUVzUKWVczZ2lVbDcvMzcwZ0c1R2I0MmYzWnYxRkN3dkFsV1ZrSXVGaiswSS9kOUVpY01YWVNQU3Zzd3BmTzcvdGRUNApVRU9mNXdSN2gycWRCelZkRzJPZ1BHamY0UXM4c2VackNSdHl6NkltbkNPeC9SdjBCWmFaR1ptWWxaLzZYTFZGCmF5RjZDYm1yMExFeHpvb0tBdXJVVGFLSmRNUHhyZ0J0QW9JQkFHeU9sN2xFOTdSRURsbFdFbGx5REpqelJxd3kKcldlcXNyRXFDclJtN0FDK0xGL3BJQ0dacEk0QzF1QmRJVUlVY01INDNrSHowazY0NVZ4OCt2UE5PRjYzQjdNNwpPRVhlZmd0Nmo4eW1oWjhFVUQ5VmdQTGhWK3ZqRGNKbkFaVFhqV0xrclR1K3ZEUVBpUGNTVm5UMEtNNjhBR3RGCmh3bjFKaHZoSmJiMVFSaFRocGxBWVE4SlhjcUxtUW93Nng5aG9MVGVxY1ZDazkreGZ6bWUvYXNmclp6NDBlbU4KYjF2VWpidFNrOVZkZU9tOExCVXZqZkwwZmowYUNIMkFCU1RRY1dYWFZIMldQRzB6VExKMjhIb0k1dGw4RzA3Ngp0NGtpQm9BRC8vUW4zM2doSy9wZTJDdFd3Y2VuY3Rxa2lNd21sTVIyckJZNlF2YVUzN1NGZXM3Mno0WT0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K", + "cluster_ca_certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUU2VENDQXRHZ0F3SUJBZ0lSQU9acHRKdjBGdzFmYXg3RFhFbVpsLzR3RFFZSktvWklodmNOQVFFTEJRQXcKRFRFTE1Ba0dBMVVFQXhNQ1kyRXdJQmNOTWpVeE1qRTRNVEl3TkRRMFdoZ1BNakExTlRFeU1UZ3hNakUwTkRSYQpNQTB4Q3pBSkJnTlZCQU1UQW1OaE1JSUNJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBCm9GVDlYTnRUd3BUaXdHcThRN2xROWNlS01MQkR2eDEwRVZHbElzUzlsQ3ltWXluN0EyOVVCbEcwZHg2QzdwY3EKa3ExekRwbHBWOEF2eUtsVDQ0dDhFS1pSa1dObEZybEhSS0VZNE5EazZ2NjVlRzdxVWlQc3lQUW5DOFVPaHJSMwpUQVgvMmN3M0twUXlvVmhzcStsRlUrMVp4MjUraVY5UFd3VjFXYXFsQkkyUzMzZkNzKy9WdUZUdWZTYjdYblQ2CnN0d3FEd0hFTVhMUGJPY1d4bUwwY1NhY3pYT3RxVDQxUzJ5SjRnbkNDMVBzaFdhenBQUXBUWmxuUmt0NzI4QXgKU3NPcXEyVDJhV2VSMWgxNCtkUDZ2YmhBVTA0YW5RT1Jac0Nmei8zSkxyeHpDK0tlMDJEOU5GalNJRmFjcG80dwo5VW1HRXJBOXZaUGhmUGt1NDZyenEwYWtQcTQ5cGZSeWh3Zy9jMGlHNXpQeENwZjhLbEtmdmFDelBweDBRNUJPCkZaRlBxWUR2Q0ZLejJBMEFUNkE1MFFPR1RuNk1WWTBsTGFkR1ZTRHhPYlFicWJEeHBZU3c4bGpzVTlxdkxLVkgKYWNPUVpNQjVyUzdzSlpZQ3doZ01YSHQ2VnAwcnlMRnk5ZkY2THFtZUsyeWlSbU9wb1VLeTdEd2NPRTU2Z0YzcApMVTlvNUt2SGZLaUMrTDNCVW82ZFJRWFkyWHRuUXNZakpYRHFGRERRSEUrNUxIY3lHbDcrOE1mdkIrNHp1MnFNCjVmRVRLSnlaOENMQkxyWDM2SjhOamZhdXR4U3MvcmVhRitYZ3dFL3VLb3FGVjhSWUIwUzU3dk9jdEhlbUpBRWkKM040bUR1U0NydzhDQzZyWUxZekNtT3lua1EycnhJWnBUbGFzTlFQR215TUNBd0VBQWFOQ01FQXdEZ1lEVlIwUApBUUgvQkFRREFnS2tNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHdIUVlEVlIwT0JCWUVGRkx5VWt4aGgwdUdzTUNZCktIbHJoV3pjblNLTk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQ0FRQUc5MVQ5MGE5bW52aHRKNEczemVZR2JXOWEKazh4RitMNlF5a01hNVRUQnJzRkFrV1NQK3JLWXJoR29LRUVMOG5wNXltbVhmZ1ZydytnSklydmR0em4vcVRmSgpnS29ZZXpDWXEyRGo1SHZ3UHZTNS9kbVRNQ2FMTTZPRURFSU50c0QrcVRYM2s2VDNjWEZEM2dha21KdnhxdjlzCldiN3lhdk9tMUlaOVhLT0RXWGVZOFFXNGNrMHN2QTlDVnNaQlRYWUVwcWRWSW5rdVpia2MzQThiQlFJcXZ5d1AKbytQZW85Q1FYSkIrU0RvVGlDc1RSTHBaN0hIUVlmbnN6ZmpYSmhpZXhVc0xGYU9PM3UwZ3Y5VWtWNzBDdTNvTApGWTNPNDVMVGdjVzJUSnFSejNjNnhidUZzTXN2dTM3TS9IOFp6TEREK1F3RFhMK0lqMm53aStNcStnREtoMW54CmVZVkJnUGJ6amhYNk1rcVRPMmxlTTZmeVVsUXM4TU5tQjZrb0dpWkFUYnQycHZSSEY5YWozOWpmSjJOaFNpaEMKbnpNK1hNUXlnQ25QNklpY01jZ3h5SHRTUkRhL0tnWldJelp5SkRnVmZCUEhwY1VaTlg0WkVNZWZuckZSOEpXUgp5VU9Wa0VvOXNrdXFZSmVVYnBBRTV4d1F3U01tRWlSVVM4VUdGMGR1bWp4dlZvdVVENGZvWjNHelBCNFZ1allqCnE1WndncVlCL3NFNXNtNGUyRmRPK2pFNFJFcmJoMW5uVjBIbjBGcFVsQzlmSEVwSWg3dEZLbndiT05NZU1mL1gKWWJrS0dyelpHY1NGMUczazN4Mjd6WVk5eUJxayt4dWZSWGVkczhnVjRQYkt6cFVVOWZVTUJ1WXpCL2haNTM0Zwptam9rQ1lPNWdQMzduVGVTM1E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", + "host": "https://traik8scluster-op0sir9n.hcp.germanywestcentral.azmk8s.io:443", + "password": "0w0vs639mwl6rzwt1ryicepjylnxirlchfs54fyop2vaezl7hl9o6ghu2bljlypok7nqtwlr5ejlvktasozjwzow60qqblph96ua28dqa6kv6ftz54vviq2v88mo1697", + "username": "clusterUser_trusted_ai_demo_rg_trai_k8s_cluster" + } + ], + "kube_config_raw": "apiVersion: v1\nclusters:\n- cluster:\n certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUU2VENDQXRHZ0F3SUJBZ0lSQU9acHRKdjBGdzFmYXg3RFhFbVpsLzR3RFFZSktvWklodmNOQVFFTEJRQXcKRFRFTE1Ba0dBMVVFQXhNQ1kyRXdJQmNOTWpVeE1qRTRNVEl3TkRRMFdoZ1BNakExTlRFeU1UZ3hNakUwTkRSYQpNQTB4Q3pBSkJnTlZCQU1UQW1OaE1JSUNJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBCm9GVDlYTnRUd3BUaXdHcThRN2xROWNlS01MQkR2eDEwRVZHbElzUzlsQ3ltWXluN0EyOVVCbEcwZHg2QzdwY3EKa3ExekRwbHBWOEF2eUtsVDQ0dDhFS1pSa1dObEZybEhSS0VZNE5EazZ2NjVlRzdxVWlQc3lQUW5DOFVPaHJSMwpUQVgvMmN3M0twUXlvVmhzcStsRlUrMVp4MjUraVY5UFd3VjFXYXFsQkkyUzMzZkNzKy9WdUZUdWZTYjdYblQ2CnN0d3FEd0hFTVhMUGJPY1d4bUwwY1NhY3pYT3RxVDQxUzJ5SjRnbkNDMVBzaFdhenBQUXBUWmxuUmt0NzI4QXgKU3NPcXEyVDJhV2VSMWgxNCtkUDZ2YmhBVTA0YW5RT1Jac0Nmei8zSkxyeHpDK0tlMDJEOU5GalNJRmFjcG80dwo5VW1HRXJBOXZaUGhmUGt1NDZyenEwYWtQcTQ5cGZSeWh3Zy9jMGlHNXpQeENwZjhLbEtmdmFDelBweDBRNUJPCkZaRlBxWUR2Q0ZLejJBMEFUNkE1MFFPR1RuNk1WWTBsTGFkR1ZTRHhPYlFicWJEeHBZU3c4bGpzVTlxdkxLVkgKYWNPUVpNQjVyUzdzSlpZQ3doZ01YSHQ2VnAwcnlMRnk5ZkY2THFtZUsyeWlSbU9wb1VLeTdEd2NPRTU2Z0YzcApMVTlvNUt2SGZLaUMrTDNCVW82ZFJRWFkyWHRuUXNZakpYRHFGRERRSEUrNUxIY3lHbDcrOE1mdkIrNHp1MnFNCjVmRVRLSnlaOENMQkxyWDM2SjhOamZhdXR4U3MvcmVhRitYZ3dFL3VLb3FGVjhSWUIwUzU3dk9jdEhlbUpBRWkKM040bUR1U0NydzhDQzZyWUxZekNtT3lua1EycnhJWnBUbGFzTlFQR215TUNBd0VBQWFOQ01FQXdEZ1lEVlIwUApBUUgvQkFRREFnS2tNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHdIUVlEVlIwT0JCWUVGRkx5VWt4aGgwdUdzTUNZCktIbHJoV3pjblNLTk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQ0FRQUc5MVQ5MGE5bW52aHRKNEczemVZR2JXOWEKazh4RitMNlF5a01hNVRUQnJzRkFrV1NQK3JLWXJoR29LRUVMOG5wNXltbVhmZ1ZydytnSklydmR0em4vcVRmSgpnS29ZZXpDWXEyRGo1SHZ3UHZTNS9kbVRNQ2FMTTZPRURFSU50c0QrcVRYM2s2VDNjWEZEM2dha21KdnhxdjlzCldiN3lhdk9tMUlaOVhLT0RXWGVZOFFXNGNrMHN2QTlDVnNaQlRYWUVwcWRWSW5rdVpia2MzQThiQlFJcXZ5d1AKbytQZW85Q1FYSkIrU0RvVGlDc1RSTHBaN0hIUVlmbnN6ZmpYSmhpZXhVc0xGYU9PM3UwZ3Y5VWtWNzBDdTNvTApGWTNPNDVMVGdjVzJUSnFSejNjNnhidUZzTXN2dTM3TS9IOFp6TEREK1F3RFhMK0lqMm53aStNcStnREtoMW54CmVZVkJnUGJ6amhYNk1rcVRPMmxlTTZmeVVsUXM4TU5tQjZrb0dpWkFUYnQycHZSSEY5YWozOWpmSjJOaFNpaEMKbnpNK1hNUXlnQ25QNklpY01jZ3h5SHRTUkRhL0tnWldJelp5SkRnVmZCUEhwY1VaTlg0WkVNZWZuckZSOEpXUgp5VU9Wa0VvOXNrdXFZSmVVYnBBRTV4d1F3U01tRWlSVVM4VUdGMGR1bWp4dlZvdVVENGZvWjNHelBCNFZ1allqCnE1WndncVlCL3NFNXNtNGUyRmRPK2pFNFJFcmJoMW5uVjBIbjBGcFVsQzlmSEVwSWg3dEZLbndiT05NZU1mL1gKWWJrS0dyelpHY1NGMUczazN4Mjd6WVk5eUJxayt4dWZSWGVkczhnVjRQYkt6cFVVOWZVTUJ1WXpCL2haNTM0Zwptam9rQ1lPNWdQMzduVGVTM1E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==\n server: https://traik8scluster-op0sir9n.hcp.germanywestcentral.azmk8s.io:443\n name: trai_k8s_cluster\ncontexts:\n- context:\n cluster: trai_k8s_cluster\n user: clusterUser_trusted_ai_demo_rg_trai_k8s_cluster\n name: trai_k8s_cluster\ncurrent-context: trai_k8s_cluster\nkind: Config\npreferences: {}\nusers:\n- name: clusterUser_trusted_ai_demo_rg_trai_k8s_cluster\n user:\n client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZIakNDQXdhZ0F3SUJBZ0lSQUt4cUdaR3NjVVVjV2pnY0ZNTE95cDh3RFFZSktvWklodmNOQVFFTEJRQXcKRFRFTE1Ba0dBMVVFQXhNQ1kyRXdIaGNOTWpVeE1qRTRNVEl3TkRRMFdoY05NamN4TWpFNE1USXhORFEwV2pBdwpNUmN3RlFZRFZRUUtFdzV6ZVhOMFpXMDZiV0Z6ZEdWeWN6RVZNQk1HQTFVRUF4TU1iV0Z6ZEdWeVkyeHBaVzUwCk1JSUNJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBd000T3o3NkI1MFFXeTB2UzdYYnEKR1RiMTVNSWUzZmtMUDliM3hNS1lWZVRCeWNsWExmSkJjRHRNRWlDa1Ixbi9FdGxIV0RwbGx5Qkx3Mjl2UWpOUgpsblFLMDZzY1gvNDdvSTJFODkxUjYvTC9NaWIxV2RiTUJ2R2pvdkhVNnZOdE9tbmdOaFVOT2tXYXRabGVPQUMyCkVxNE5WWFlKWW85SnlKRXlhNlgzd3lDazhJZzQyeEtYZjFSZDJVK0RYVTZndCtIYWk5THpsellob01ra20rdWQKc2RDSFp0S0lIb3cvV013NkdyTHczelQ0dm5jWVNoL2hOYUVZUm1yNytpRWFQbmEwRnNXa1FtYmE4R3RvZmdodQpDLzNnTTBNSHY5VnFQTDhOWDNabXA3VERyODhVclpLSnNWTVBQLzFOL2ZER3k5bzVQS3hqb2c2SUJGNStxcWdqCkk0MFZpWHdEcW9Md3RpeHhDNytRYmRLMFZBSGI5UzlmcnZaZWZCeU9qdUlzR21MOHAxem5RUlhmRk5vRENhdmMKVk9mOFgzU3BNMEpJRFBMVUpqTitnRzdaYTlHU2JubFZlMVBwK3dZcHRDUXRqVjNScXFGOUpJd2lXRVdLQ2FaTApWSXV3VENHOU0yeEYxNjZjMWk1dGcwUjZkTGwzdW9BYkVleXdNUE1TcWloMUxLdmFZNUcyQjJiclp0K3hjM3NYCldKQW5TamJhOUtnZnk5d3BlNVI0bnUwejVjMUdrSGRLQjVwZDBZVWxINlRpY0EvV0Q5SXdDNFZDdWlJcTlmNGsKR1hpS2JEQXp6UFZwZjZFcjVCV0pVZzhJWjBnb0svaWxOdXJWNjhvSmlmRW9qdXFIWTJDRm9PMFhRanhtZkxwVgo5cFVoVGc5YjEzNlNKWDF5YlN6eFZmc0NBd0VBQWFOV01GUXdEZ1lEVlIwUEFRSC9CQVFEQWdXZ01CTUdBMVVkCkpRUU1NQW9HQ0NzR0FRVUZCd01DTUF3R0ExVWRFd0VCL3dRQ01BQXdId1lEVlIwakJCZ3dGb0FVVXZKU1RHR0gKUzRhd3dKZ29lV3VGYk55ZElvMHdEUVlKS29aSWh2Y05BUUVMQlFBRGdnSUJBQ2I5Sy82cnV1QlNRdk04c1VmQQpNWWkvOUZUaVNHR2pqeVRRdFlBMncxRVA2aHpocVMwWXBzdWhwWGVtS3VtcXJPZzZ0QzZERDR3Z3B0OWNYdExsCm14WHQ3V2d2bUNyckt0UHB0OHlzNjN2NzBzQXNDTDNJZmg1SHoxVzlHSzdHbFRub0w5ODI5dTN6NWlESldTeUUKY0NsaDhlTzVnNVltaUcvV05qZDFzZHlvV2NJbEdlNi9FRnFaWTNkYkVVSlVSVUZHNzZLV0tndE1UejZQRlVQRwo5YXdOMjdiL0tPaVBtbmI0WkVVbmVweTRTcHhabis1UGRQT2g2Q0VFdU9FOWNZQ2pTRzc0TXZvZkY4SHlpOFVKCmYrQjJ2bVE5bEN6SGJzSWZNdVpyeFRZd21DTGhlU3RndVVWUnhqSm4zaTVVdGJwMTBkWUJQaThtU2FVV0p1b3oKS0FNRnFsTWdNOVhDc3FUZ05yWm01U3ZuYmdjUHozbW0wSmRScUhtQS9EUzhWckl6NGo2YVdueUpzdlQ0MUFDTApVMnRZbmw0MlUrSFRtYndFSGFla0J4WDRiTlBNekRYVzk1SktVT2NySWxkM25CVUpMY3draTRVSnpVQ0pkZDl4CmEzOVVLTXUzUVh2V1phRkFUOWFEN2QzWXUxN0IxOG84V2liNlhPRzFnWXB0L3U2L2hzTWcrc1lVdmxMd2lOQXIKQnpHV2xjUUtXNDZETEFJQXlBdkxuaDlhQnJ6aVY2aHkyUm0rTmZwVkFlUUd4WVAzRzgyRGdwVFhtUXZkNTBaOQo3L0pnQVo1R01PT1hMN0ozcURFVHhDY3NLeUZYdTNielE5dmhWaXJ6MTdoNi9naE5hTmZFcXF6T2pQZStRTjFNCkhjZFFoNlFWUkt1dlpzbTRrZW1aSDVOaQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==\n client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBd000T3o3NkI1MFFXeTB2UzdYYnFHVGIxNU1JZTNma0xQOWIzeE1LWVZlVEJ5Y2xYCkxmSkJjRHRNRWlDa1Ixbi9FdGxIV0RwbGx5Qkx3Mjl2UWpOUmxuUUswNnNjWC80N29JMkU4OTFSNi9ML01pYjEKV2RiTUJ2R2pvdkhVNnZOdE9tbmdOaFVOT2tXYXRabGVPQUMyRXE0TlZYWUpZbzlKeUpFeWE2WDN3eUNrOElnNAoyeEtYZjFSZDJVK0RYVTZndCtIYWk5THpsellob01ra20rdWRzZENIWnRLSUhvdy9XTXc2R3JMdzN6VDR2bmNZClNoL2hOYUVZUm1yNytpRWFQbmEwRnNXa1FtYmE4R3RvZmdodUMvM2dNME1IdjlWcVBMOE5YM1ptcDdURHI4OFUKclpLSnNWTVBQLzFOL2ZER3k5bzVQS3hqb2c2SUJGNStxcWdqSTQwVmlYd0Rxb0x3dGl4eEM3K1FiZEswVkFIYgo5UzlmcnZaZWZCeU9qdUlzR21MOHAxem5RUlhmRk5vRENhdmNWT2Y4WDNTcE0wSklEUExVSmpOK2dHN1phOUdTCmJubFZlMVBwK3dZcHRDUXRqVjNScXFGOUpJd2lXRVdLQ2FaTFZJdXdUQ0c5TTJ4RjE2NmMxaTV0ZzBSNmRMbDMKdW9BYkVleXdNUE1TcWloMUxLdmFZNUcyQjJiclp0K3hjM3NYV0pBblNqYmE5S2dmeTl3cGU1UjRudTB6NWMxRwprSGRLQjVwZDBZVWxINlRpY0EvV0Q5SXdDNFZDdWlJcTlmNGtHWGlLYkRBenpQVnBmNkVyNUJXSlVnOElaMGdvCksvaWxOdXJWNjhvSmlmRW9qdXFIWTJDRm9PMFhRanhtZkxwVjlwVWhUZzliMTM2U0pYMXliU3p4VmZzQ0F3RUEKQVFLQ0FnQXJhZnF4MHA3am1WUmNvSTAxUlZKL0xQMy9iYXFQcHZWZWdhUTIzTzU2WU5FMkE2MHF2bUwzeDVKZwpmcE1mTWVlYXNldzd0S0tOZmdzeFRaVkszYzFnU0c3cnJKcnVEb3dVb2VrdkZFWFBFRjlnUnA5MmhFUmF4K3hsCjAzaHpFeThrT1JUd2VKc0s1UU5xSXNleGFvK1U4ell6YVNuQjFLdnppcVpFMFJGU0ViYXlKcXBPd2dmMDl1Vk8KK25XRS9DQjZaMVBUcWtPSlovbjRmM0pMM2FrYmtlMzdQQ3JFWGlZaGM1TWQwZThsQTRPRE44UHpaT3hNTENNUQpNQ1RlOUxjNUczZC9yUzlJZUdxU09rMUVHOWZUd1czTGZTdHdpYmlyUndYVzJnMVJkQVdnUDh2VExhenRqZHRnCk9FUm9pdC9vcFJIQVZMejJqN2tXNnNRdzN6MVI3a3d2bDJ5aHlPL1l6NWp5K1ZHOUgra0pmeEwzM0o4OWxmSEEKblNIbm9aZGhzN1Zxa3JLN3pIbnlsZEUzZVBqenRUS2JMaGRoeWVqTU5lRmlmeXdzVTFKQkpnYnZRSDBHSmpxbQpNMkRXRWtQVzA5L2ZHbVpKM2NVVy8zVmM4MG5FUC9lYWxxZnVPM3QvamwxdEtUaGg3WC9lTXlqUDRvSTFpV0ZpCkZ3eDByWGxOSVcrSnVuNjFtVTN6eENpZzhDVXI1SmNsMURPZjNRY2VaeklmV0dxYWNRK1lWeDErQTErVE01d0wKdkl1ek92MkFoamVscE5sVEFTMUtOemxidVRwWXVubnVLUERKK3FQWWhDV3cxMGJXcXJvTEw1dzgwQUJXaGRGLwpCNjBBNWNuSSs3eVNJT1crbzAvNW1BUm9hVHhiY1JUTVV2aVFVMHplK0UzN3NJTmt5UUtDQVFFQXhoOHl3Qnh5CkpySW5mVnl0TkFvTDFVK2NyRmliekRRQTI5MlpUTHl0MG5VLzlCZ2t5ZXlZL2JId2pzeitwTzR6anNERUFYQSsKcjZ1NmZySm9PMU9lbzlkZUs5UW5lc1NadHZLSG9HU1MwS2VseWlLcmZTM29YL1RGb3dBZzJNaXMyWFpuNy9WVAp0MVhwSFdMV3ljeTFjZVhibEU5cTZ2YmdIdWUwMWZPUmtna09JSHJpVXdwcmxNZFhvNHdKWExNT0Z5NUdRNXp4CnVRV2c5TWZXUmxuRVZmcE5OTnV3MlBsSmYybG1oOFJablJHOTdhOWZ5OXp5QWlYLy82VURSdnJnbzFzd1d6QU4KZFZsQXd5S0tzczQzaldMSjdmRVBCbDhsVTJidm9OVnR0THB0ai9rWk9HVjZXeGVRMEY4UlRQZmFwVkN6bHlhRwpGMHZkUWRWemhjdjRsd0tDQVFFQStTRTVaK0pwUlY1L3RJTjRydS9ES1E0Rnk3WlZ6Yk41YWxUcXo2TXFkOTZnClhtMTNHdlpoWmgrN0htZktqMHNJWWZRS21oK2xxNWsyK3YvMXBWYUZLa0VSd1FNdGM0VHp6bmFNeHRqZE16a2oKc1E3MjZoRUZocnZZWmRtbVVLbjBvMHJVbUNocWJkNU9WRGNLRmR3ekwrNldBVFo3c1RWVlVsc01JSG82YnlYRApqM0FzVThaZjRtTFc4SktkMm4ydjhZRDZ1NE40ZEE0bnozd0tIdUkxY2txRlBZa3BEYlpOcS9GS0VpY3FiVkk5ClVhRDVVOVd0cHg3MVNCYWcrdlJodFA0N2wybngybW5QSFJNUVo0cXY1UitvUUxUeUthMzlYSVU2TmUrcDVxYTkKWXlJaERVcDRRbmRnWDZqNHFUaWdXQnJsaFF3cUFsZWJtbFVha2ZyMlBRS0NBUUJsT0kxS1AzNUdyT0NtR05KWQo4d1VHcjQwQk5LZ3NsU2tYSzhsbHZrUWtjVWtDWWM5eDV0Sm5VeUR2SkdhNVJ3WEtXa3dKM0JuNjBJY1FzZiswCng4MFdNak5udXhXTWtLTmtKOWNKS0RrbUozRDVxK3Jub3F2RlVwNGk0QzduOHViMGw1MFVxTWRqdEozTUtMSksKR2E5ZUNLeTl3akdDNkN5clBZQlRpd1NGa3VESDBXQ09RcFdpUnBKTStYL1MvNXdrVmIwS1VFWGpYSy9XUDc4Rwo1RmYrQVVxdXhjczluMmRnbEM0UkVVMGpoV3YrNG9uVDh3Y2w5OW9wOTFtTVN5SzM4eC9xaHN0cGQvY3hleFZYCnN5b0lyejh4dE9ZVFFFc21xRUduYjBPRWdYYjJockxPOEhHcDFvbGpIQjhQcnRYamZpNnkrN2lvY0ZjVGlvVmkKQjByekFvSUJBUUNpT3cxNzVjZHhvZUQ5ZHVPKzMya3lNZXg2Uk1sSlE0ZGNRK0lJeHBNZ0xmdzJvWWYvS2RReApRWmFEYlVsTjJLbWp4OXlqa3RvNmpYSnRMd01TNE8ya24yemxTS0Z2YVhPdXppcE1UdXAwc2MvWlBBcDBVQk5PCk1zTHZTRFZ5cjhvY0Q5MnpIQlo2WjFxUW9xclJvMnc5MlZzYmZSUTlTS0xBRjRRbEhoTXhUK0V5WmZTbnRUVzUKWVczZ2lVbDcvMzcwZ0c1R2I0MmYzWnYxRkN3dkFsV1ZrSXVGaiswSS9kOUVpY01YWVNQU3Zzd3BmTzcvdGRUNApVRU9mNXdSN2gycWRCelZkRzJPZ1BHamY0UXM4c2VackNSdHl6NkltbkNPeC9SdjBCWmFaR1ptWWxaLzZYTFZGCmF5RjZDYm1yMExFeHpvb0tBdXJVVGFLSmRNUHhyZ0J0QW9JQkFHeU9sN2xFOTdSRURsbFdFbGx5REpqelJxd3kKcldlcXNyRXFDclJtN0FDK0xGL3BJQ0dacEk0QzF1QmRJVUlVY01INDNrSHowazY0NVZ4OCt2UE5PRjYzQjdNNwpPRVhlZmd0Nmo4eW1oWjhFVUQ5VmdQTGhWK3ZqRGNKbkFaVFhqV0xrclR1K3ZEUVBpUGNTVm5UMEtNNjhBR3RGCmh3bjFKaHZoSmJiMVFSaFRocGxBWVE4SlhjcUxtUW93Nng5aG9MVGVxY1ZDazkreGZ6bWUvYXNmclp6NDBlbU4KYjF2VWpidFNrOVZkZU9tOExCVXZqZkwwZmowYUNIMkFCU1RRY1dYWFZIMldQRzB6VExKMjhIb0k1dGw4RzA3Ngp0NGtpQm9BRC8vUW4zM2doSy9wZTJDdFd3Y2VuY3Rxa2lNd21sTVIyckJZNlF2YVUzN1NGZXM3Mno0WT0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K\n token: 0w0vs639mwl6rzwt1ryicepjylnxirlchfs54fyop2vaezl7hl9o6ghu2bljlypok7nqtwlr5ejlvktasozjwzow60qqblph96ua28dqa6kv6ftz54vviq2v88mo1697\n", + "kubelet_identity": [ + { + "client_id": "342e0ba1-a9c4-4f0f-8b02-1c0d3c40ba88", + "object_id": "8a75f0be-74bf-4b31-8a31-58f6e0792678", + "user_assigned_identity_id": "/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/MC_trusted_ai_demo_rg_trai_k8s_cluster_germanywestcentral/providers/Microsoft.ManagedIdentity/userAssignedIdentities/trai_k8s_cluster-agentpool" + } + ], + "kubernetes_version": "1.33", + "linux_profile": [ + { + "admin_username": "", + "ssh_key": [] + } + ], + "location": "germanywestcentral", + "microsoft_defender": [], + "name": "trai_k8s_cluster", + "network_profile": [ + { + "dns_service_ip": "10.0.0.10", + "docker_bridge_cidr": "", + "load_balancer_sku": "standard", + "network_plugin": "azure", + "network_policy": "none", + "pod_cidr": "10.244.0.0/16", + "service_cidr": "10.0.0.0/16" + } + ], + "node_resource_group": "MC_trusted_ai_demo_rg_trai_k8s_cluster_germanywestcentral", + "node_resource_group_id": "/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/MC_trusted_ai_demo_rg_trai_k8s_cluster_germanywestcentral", + "oidc_issuer_enabled": true, + "oidc_issuer_url": "https://germanywestcentral.oic.prod-aks.azure.com/73c07a52-1c4d-40ac-b3b3-2f842e5c8919/18cdbaf9-491d-45a7-91e7-15cd142de9de/", + "oms_agent": [ + { + "log_analytics_workspace_id": "/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/trusted_ai_demo_rg/providers/Microsoft.OperationalInsights/workspaces/aksloganalyticstraik8scluster", + "msi_auth_for_monitoring_enabled": true, + "oms_agent_identity": [] + } + ], + "open_service_mesh_enabled": false, + "private_cluster_enabled": null, + "private_fqdn": "", + "resource_group_name": "trusted_ai_demo_rg", + "role_based_access_control_enabled": true, + "service_mesh_profile": [], + "service_principal": [ + { + "client_id": "msi" + } + ], + "storage_profile": [ + { + "blob_driver_enabled": false, + "disk_driver_enabled": true, + "file_driver_enabled": true, + "snapshot_controller_enabled": true + } + ], + "tags": {}, + "timeouts": null, + "windows_profile": [] + }, + "sensitive_attributes": [ + [ + { + "type": "get_attr", + "value": "kube_admin_config" + } + ], + [ + { + "type": "get_attr", + "value": "kube_admin_config_raw" + } + ], + [ + { + "type": "get_attr", + "value": "kube_config" + } + ], + [ + { + "type": "get_attr", + "value": "kube_config_raw" + } + ] + ], + "identity_schema_version": 0 + } + ] + }, + { + "mode": "data", + "type": "azurerm_resource_group", + "name": "main", + "provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/trusted_ai_demo_rg", + "location": "germanywestcentral", + "managed_by": "", + "name": "trusted_ai_demo_rg", + "tags": {}, + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0 + } + ] + }, + { + "mode": "data", + "type": "azurerm_subscription", + "name": "current", + "provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "display_name": "Azure-Abonnement 1", + "id": "/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3", + "location_placement_id": "Public_2014-09-01", + "quota_id": "PayAsYouGo_2014-09-01", + "spending_limit": "Off", + "state": "Enabled", + "subscription_id": "77677a80-2dea-493d-9867-f1c961b80fb3", + "tags": {}, + "tenant_id": "73c07a52-1c4d-40ac-b3b3-2f842e5c8919", + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0 + } + ] + }, + { + "mode": "data", + "type": "kubernetes_service", + "name": "ingress_nginx", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "index_key": 0, + "schema_version": 0, + "attributes": { + "id": "ingress-nginx/ingress-nginx-controller", + "metadata": [ + { + "annotations": { + "meta.helm.sh/release-name": "ingress-nginx", + "meta.helm.sh/release-namespace": "ingress-nginx", + "service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path": "/healthz" + }, + "generation": 0, + "labels": { + "app.kubernetes.io/component": "controller", + "app.kubernetes.io/instance": "ingress-nginx", + "app.kubernetes.io/managed-by": "Helm", + "app.kubernetes.io/name": "ingress-nginx", + "app.kubernetes.io/part-of": "ingress-nginx", + "app.kubernetes.io/version": "1.11.3", + "helm.sh/chart": "ingress-nginx-4.11.3" + }, + "name": "ingress-nginx-controller", + "namespace": "ingress-nginx", + "resource_version": "1540906", + "uid": "a2779065-00d7-42c3-86ba-5e261a47aac2" + } + ], + "spec": [ + { + "allocate_load_balancer_node_ports": true, + "cluster_ip": "10.0.100.184", + "cluster_ips": [ + "10.0.100.184" + ], + "external_ips": [], + "external_name": "", + "external_traffic_policy": "Cluster", + "health_check_node_port": 0, + "internal_traffic_policy": "Cluster", + "ip_families": [ + "IPv4" + ], + "ip_family_policy": "SingleStack", + "load_balancer_class": "", + "load_balancer_ip": "", + "load_balancer_source_ranges": [], + "port": [ + { + "app_protocol": "http", + "name": "http", + "node_port": 30187, + "port": 80, + "protocol": "TCP", + "target_port": "http" + }, + { + "app_protocol": "https", + "name": "https", + "node_port": 30956, + "port": 443, + "protocol": "TCP", + "target_port": "https" + } + ], + "publish_not_ready_addresses": false, + "selector": { + "app.kubernetes.io/component": "controller", + "app.kubernetes.io/instance": "ingress-nginx", + "app.kubernetes.io/name": "ingress-nginx" + }, + "session_affinity": "None", + "session_affinity_config": [], + "type": "LoadBalancer" + } + ], + "status": [ + { + "load_balancer": [ + { + "ingress": [ + { + "hostname": "", + "ip": "72.144.113.194" + } + ] + } + ] + } + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0 + } + ] + }, + { + "mode": "managed", + "type": "helm_release", + "name": "nginx_ingress", + "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", + "instances": [ + { + "index_key": 0, + "schema_version": 1, + "attributes": { + "atomic": false, + "chart": "ingress-nginx", + "cleanup_on_fail": false, + "create_namespace": true, + "dependency_update": false, + "description": null, + "devel": null, + "disable_crd_hooks": false, + "disable_openapi_validation": false, + "disable_webhooks": false, + "force_update": false, + "id": "ingress-nginx", + "keyring": null, + "lint": false, + "manifest": null, + "max_history": 0, + "metadata": [ + { + "app_version": "1.11.3", + "chart": "ingress-nginx", + "first_deployed": 1766395333, + "last_deployed": 1766395333, + "name": "ingress-nginx", + "namespace": "ingress-nginx", + "notes": "The ingress-nginx controller has been installed.\nIt may take a few minutes for the load balancer IP to be available.\nYou can watch the status by running 'kubectl get service --namespace ingress-nginx ingress-nginx-controller --output wide --watch'\n\nAn example Ingress that makes use of the controller:\n apiVersion: networking.k8s.io/v1\n kind: Ingress\n metadata:\n name: example\n namespace: foo\n spec:\n ingressClassName: nginx\n rules:\n - host: www.example.com\n http:\n paths:\n - pathType: Prefix\n backend:\n service:\n name: exampleService\n port:\n number: 80\n path: /\n # This section is only required if TLS is to be enabled for the Ingress\n tls:\n - hosts:\n - www.example.com\n secretName: example-tls\n\nIf TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided:\n\n apiVersion: v1\n kind: Secret\n metadata:\n name: example-tls\n namespace: foo\n data:\n tls.crt: \u003cbase64 encoded cert\u003e\n tls.key: \u003cbase64 encoded key\u003e\n type: kubernetes.io/tls\n", + "revision": 1, + "values": "{\"controller\":{\"config\":{\"limit-req-status-code\":429},\"metrics\":{\"enabled\":true},\"podAnnotations\":{\"prometheus.io/port\":10254,\"prometheus.io/scrape\":true},\"resources\":{\"limits\":{\"cpu\":\"500m\",\"memory\":\"512Mi\"},\"requests\":{\"cpu\":\"100m\",\"memory\":\"128Mi\"}},\"service\":{\"annotations\":{\"service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path\":\"/healthz\"},\"type\":\"LoadBalancer\"}}}", + "version": "4.11.3" + } + ], + "name": "ingress-nginx", + "namespace": "ingress-nginx", + "pass_credentials": false, + "postrender": [], + "recreate_pods": false, + "render_subchart_notes": true, + "replace": false, + "repository": "https://kubernetes.github.io/ingress-nginx", + "repository_ca_file": null, + "repository_cert_file": null, + "repository_key_file": null, + "repository_password": null, + "repository_username": null, + "reset_values": false, + "reuse_values": false, + "set": [ + { + "name": "controller.config.limit-req-status-code", + "type": "", + "value": "429" + }, + { + "name": "controller.metrics.enabled", + "type": "", + "value": "true" + }, + { + "name": "controller.podAnnotations.prometheus\\.io/port", + "type": "", + "value": "10254" + }, + { + "name": "controller.podAnnotations.prometheus\\.io/scrape", + "type": "", + "value": "true" + }, + { + "name": "controller.resources.limits.cpu", + "type": "", + "value": "500m" + }, + { + "name": "controller.resources.limits.memory", + "type": "", + "value": "512Mi" + }, + { + "name": "controller.resources.requests.cpu", + "type": "", + "value": "100m" + }, + { + "name": "controller.resources.requests.memory", + "type": "", + "value": "128Mi" + }, + { + "name": "controller.service.annotations.service\\.beta\\.kubernetes\\.io/azure-load-balancer-health-probe-request-path", + "type": "", + "value": "/healthz" + }, + { + "name": "controller.service.type", + "type": "", + "value": "LoadBalancer" + } + ], + "set_list": [], + "set_sensitive": [], + "skip_crds": false, + "status": "deployed", + "timeout": 600, + "upgrade_install": null, + "values": null, + "verify": false, + "version": "4.11.3", + "wait": true, + "wait_for_jobs": false + }, + "sensitive_attributes": [ + [ + { + "type": "get_attr", + "value": "repository_password" + } + ] + ], + "identity_schema_version": 0, + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_config_map", + "name": "app_config", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "binary_data": {}, + "data": { + "APP_DEBUG": "false", + "APP_ENV": "production", + "APP_FAKER_LOCALE": "en_US", + "APP_FALLBACK_LOCALE": "en", + "APP_LOCALE": "en", + "APP_NAME": "laravel-app", + "APP_URL": "http://72.144.113.194", + "BROADCAST_CONNECTION": "log", + "CACHE_STORE": "database", + "FILESYSTEM_DISK": "local", + "LOG_CHANNEL": "stack", + "LOG_LEVEL": "info", + "LOG_STACK": "single", + "MAIL_MAILER": "log", + "QUEUE_CONNECTION": "database", + "SESSION_DRIVER": "database", + "SESSION_ENCRYPT": "false", + "SESSION_LIFETIME": "120", + "VITE_APP_NAME": "laravel-app" + }, + "id": "laravel-app/laravel-app-config", + "immutable": false, + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "laravel-app-config", + "namespace": "laravel-app", + "resource_version": "1541031", + "uid": "d34a412e-321d-4b8c-91d8-0c823ee1a34b" + } + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "ConfigMap", + "name": "laravel-app-config", + "namespace": "laravel-app" + }, + "private": "bnVsbA==", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "data.kubernetes_service.ingress_nginx", + "helm_release.nginx_ingress", + "kubernetes_namespace.app" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_deployment", + "name": "app", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "status": "tainted", + "schema_version": 1, + "attributes": { + "id": "laravel-app/laravel-app", + "metadata": [ + { + "annotations": null, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "laravel-app", + "namespace": "laravel-app", + "resource_version": "", + "uid": "" + } + ], + "spec": [ + { + "min_ready_seconds": 0, + "paused": false, + "progress_deadline_seconds": 600, + "replicas": "2", + "revision_history_limit": 10, + "selector": [ + { + "match_expressions": [], + "match_labels": { + "app": "laravel-app" + } + } + ], + "strategy": [ + { + "rolling_update": [ + { + "max_surge": "1", + "max_unavailable": "0" + } + ], + "type": "RollingUpdate" + } + ], + "template": [ + { + "metadata": [ + { + "annotations": null, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform", + "version": "latest" + }, + "name": "", + "namespace": "", + "resource_version": "", + "uid": "" + } + ], + "spec": [ + { + "active_deadline_seconds": 0, + "affinity": [], + "automount_service_account_token": true, + "container": [ + { + "args": null, + "command": null, + "env": [], + "env_from": [ + { + "config_map_ref": [ + { + "name": "laravel-app-config", + "optional": false + } + ], + "prefix": "", + "secret_ref": [] + }, + { + "config_map_ref": [], + "prefix": "", + "secret_ref": [ + { + "name": "laravel-app-secrets", + "optional": false + } + ] + }, + { + "config_map_ref": [], + "prefix": "", + "secret_ref": [ + { + "name": "laravel-app-db-credentials", + "optional": false + } + ] + } + ], + "image": "laravelappreg.azurecr.io/laravel-app:v1.0.0", + "image_pull_policy": "", + "lifecycle": [], + "liveness_probe": [ + { + "exec": [], + "failure_threshold": 3, + "grpc": [], + "http_get": [ + { + "host": "", + "http_header": [], + "path": "/", + "port": "80", + "scheme": "HTTP" + } + ], + "initial_delay_seconds": 30, + "period_seconds": 10, + "success_threshold": 1, + "tcp_socket": [], + "timeout_seconds": 5 + } + ], + "name": "laravel-app", + "port": [ + { + "container_port": 80, + "host_ip": "", + "host_port": 0, + "name": "http", + "protocol": "TCP" + } + ], + "readiness_probe": [ + { + "exec": [], + "failure_threshold": 3, + "grpc": [], + "http_get": [ + { + "host": "", + "http_header": [], + "path": "/", + "port": "80", + "scheme": "HTTP" + } + ], + "initial_delay_seconds": 10, + "period_seconds": 5, + "success_threshold": 1, + "tcp_socket": [], + "timeout_seconds": 3 + } + ], + "resources": [ + { + "limits": { + "cpu": "500m", + "memory": "512Mi" + }, + "requests": { + "cpu": "100m", + "memory": "256Mi" + } + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "", + "tty": false, + "volume_device": [], + "volume_mount": [], + "working_dir": "" + } + ], + "dns_config": [], + "dns_policy": "ClusterFirst", + "enable_service_links": true, + "host_aliases": [], + "host_ipc": false, + "host_network": false, + "host_pid": false, + "hostname": "", + "image_pull_secrets": [ + { + "name": "acr-secret" + } + ], + "init_container": [], + "node_name": "", + "node_selector": null, + "os": [], + "priority_class_name": "", + "readiness_gate": [], + "restart_policy": "Always", + "runtime_class_name": "", + "scheduler_name": "", + "security_context": [], + "service_account_name": "", + "share_process_namespace": false, + "subdomain": "", + "termination_grace_period_seconds": 30, + "toleration": [], + "topology_spread_constraint": [], + "volume": [] + } + ] + } + ] + } + ], + "timeouts": null, + "wait_for_rollout": true + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": null, + "kind": null, + "name": null, + "namespace": null + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6NjAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "data.kubernetes_service.ingress_nginx", + "helm_release.nginx_ingress", + "kubernetes_config_map.app_config", + "kubernetes_namespace.app", + "kubernetes_persistent_volume_claim.postgresql", + "kubernetes_secret.app_secrets", + "kubernetes_secret.db_credentials", + "kubernetes_secret.postgresql", + "kubernetes_service.postgresql", + "kubernetes_stateful_set.postgresql", + "random_password.postgresql_admin_password" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_namespace", + "name": "app", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "laravel-app", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform", + "name": "laravel-app" + }, + "name": "laravel-app", + "resource_version": "1540685", + "uid": "6b28a548-a8de-4a53-a684-4afe2854a522" + } + ], + "timeouts": null, + "wait_for_default_service_account": false + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Namespace", + "name": "laravel-app" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjozMDAwMDAwMDAwMDB9fQ==", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_persistent_volume_claim", + "name": "postgresql", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "laravel-app/postgresql-pvc", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "postgresql-pvc", + "namespace": "laravel-app", + "resource_version": "1543411", + "uid": "0f1153a7-dce9-40fe-bf26-9626e8af624c" + } + ], + "spec": [ + { + "access_modes": [ + "ReadWriteOnce" + ], + "resources": [ + { + "limits": {}, + "requests": { + "storage": "10Gi" + } + } + ], + "selector": [], + "storage_class_name": "default", + "volume_mode": "Filesystem", + "volume_name": "pvc-0f1153a7-dce9-40fe-bf26-9626e8af624c" + } + ], + "timeouts": null, + "wait_until_bound": false + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDB9fQ==", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "kubernetes_namespace.app" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_secret", + "name": "app_secrets", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "binary_data": null, + "binary_data_wo": null, + "binary_data_wo_revision": null, + "data": { + "APP_KEY": "base64:hcb6PXhQOot3SKMl7USAimUWY3c3OGVaRFtaJkDRiTc=" + }, + "data_wo": null, + "data_wo_revision": null, + "id": "laravel-app/laravel-app-secrets", + "immutable": false, + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "laravel-app-secrets", + "namespace": "laravel-app", + "resource_version": "1540689", + "uid": "7822f400-5b2f-43d4-8c2a-ed450d9513a3" + } + ], + "timeouts": null, + "type": "Opaque", + "wait_for_service_account_token": true + }, + "sensitive_attributes": [ + [ + { + "type": "get_attr", + "value": "binary_data" + } + ], + [ + { + "type": "get_attr", + "value": "data" + } + ], + [ + { + "type": "get_attr", + "value": "data" + }, + { + "type": "index", + "value": { + "value": "APP_KEY", + "type": "string" + } + } + ] + ], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Secret", + "name": "laravel-app-secrets", + "namespace": "laravel-app" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMH19", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "kubernetes_namespace.app" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_secret", + "name": "db_credentials", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "binary_data": null, + "binary_data_wo": null, + "binary_data_wo_revision": null, + "data": { + "DB_CONNECTION2": "pgsql", + "DB_DATABASE2": "laravel_app", + "DB_HOST2": "postgresql.laravel-app.svc.cluster.local", + "DB_PASSWORD2": "SecureLaravelPwd2024!", + "DB_PORT2": "5432", + "DB_USERNAME2": "pgadmin" + }, + "data_wo": null, + "data_wo_revision": null, + "id": "laravel-app/laravel-app-db-credentials", + "immutable": false, + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "laravel-app-db-credentials", + "namespace": "laravel-app", + "resource_version": "1554763", + "uid": "dee29f69-423b-4d9d-8d8d-d089cc6cf054" + } + ], + "timeouts": null, + "type": "Opaque", + "wait_for_service_account_token": true + }, + "sensitive_attributes": [ + [ + { + "type": "get_attr", + "value": "binary_data" + } + ], + [ + { + "type": "get_attr", + "value": "data" + } + ], + [ + { + "type": "get_attr", + "value": "data" + }, + { + "type": "index", + "value": { + "value": "DB_PASSWORD2", + "type": "string" + } + } + ] + ], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Secret", + "name": "laravel-app-db-credentials", + "namespace": "laravel-app" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMH19", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "kubernetes_namespace.app", + "kubernetes_persistent_volume_claim.postgresql", + "kubernetes_secret.postgresql", + "kubernetes_service.postgresql", + "kubernetes_stateful_set.postgresql", + "random_password.postgresql_admin_password" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_secret", + "name": "postgresql", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "binary_data": null, + "binary_data_wo": null, + "binary_data_wo_revision": null, + "data": { + "POSTGRES_DB": "laravel_app", + "POSTGRES_PASSWORD": "SecureLaravelPwd2024!", + "POSTGRES_USER": "pgadmin" + }, + "data_wo": null, + "data_wo_revision": null, + "id": "laravel-app/postgresql-credentials", + "immutable": false, + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "postgresql-credentials", + "namespace": "laravel-app", + "resource_version": "1543374", + "uid": "07259e48-3427-45cd-b5c4-455ee73cf233" + } + ], + "timeouts": null, + "type": "Opaque", + "wait_for_service_account_token": true + }, + "sensitive_attributes": [ + [ + { + "type": "get_attr", + "value": "binary_data" + } + ], + [ + { + "type": "get_attr", + "value": "data" + } + ], + [ + { + "type": "get_attr", + "value": "data" + }, + { + "type": "index", + "value": { + "value": "POSTGRES_PASSWORD", + "type": "string" + } + } + ] + ], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Secret", + "name": "postgresql-credentials", + "namespace": "laravel-app" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMH19", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "kubernetes_namespace.app" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_service", + "name": "postgresql", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "id": "laravel-app/postgresql", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "postgresql", + "namespace": "laravel-app", + "resource_version": "1543628", + "uid": "0b24bf47-f0e1-4807-8815-a0dbf44442a5" + } + ], + "spec": [ + { + "allocate_load_balancer_node_ports": true, + "cluster_ip": "None", + "cluster_ips": [ + "None" + ], + "external_ips": [], + "external_name": "", + "external_traffic_policy": "", + "health_check_node_port": 0, + "internal_traffic_policy": "Cluster", + "ip_families": [ + "IPv4" + ], + "ip_family_policy": "SingleStack", + "load_balancer_class": "", + "load_balancer_ip": "", + "load_balancer_source_ranges": [], + "port": [ + { + "app_protocol": "", + "name": "postgresql", + "node_port": 0, + "port": 5432, + "protocol": "TCP", + "target_port": "5432" + } + ], + "publish_not_ready_addresses": false, + "selector": { + "app": "postgresql" + }, + "session_affinity": "None", + "session_affinity_config": [], + "type": "ClusterIP" + } + ], + "status": [ + { + "load_balancer": [ + { + "ingress": [] + } + ] + } + ], + "timeouts": null, + "wait_for_load_balancer": true + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Service", + "name": "postgresql", + "namespace": "laravel-app" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDB9LCJzY2hlbWFfdmVyc2lvbiI6IjEifQ==", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "kubernetes_namespace.app", + "kubernetes_persistent_volume_claim.postgresql", + "kubernetes_secret.postgresql", + "kubernetes_stateful_set.postgresql" + ] + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_stateful_set", + "name": "postgresql", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "id": "laravel-app/postgresql", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 1, + "labels": { + "app": "laravel-app", + "environment": "production", + "managed-by": "terraform" + }, + "name": "postgresql", + "namespace": "laravel-app", + "resource_version": "1543602", + "uid": "2bd31dfb-e27e-499b-ac62-115d90750900" + } + ], + "spec": [ + { + "min_ready_seconds": 0, + "persistent_volume_claim_retention_policy": [ + { + "when_deleted": "Retain", + "when_scaled": "Retain" + } + ], + "pod_management_policy": "OrderedReady", + "replicas": "1", + "revision_history_limit": 0, + "selector": [ + { + "match_expressions": [], + "match_labels": { + "app": "postgresql" + } + } + ], + "service_name": "postgresql", + "template": [ + { + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "postgresql", + "environment": "production", + "managed-by": "terraform" + }, + "name": "", + "namespace": "", + "resource_version": "", + "uid": "" + } + ], + "spec": [ + { + "active_deadline_seconds": 0, + "affinity": [], + "automount_service_account_token": true, + "container": [ + { + "args": [], + "command": [], + "env": [], + "env_from": [ + { + "config_map_ref": [], + "prefix": "", + "secret_ref": [ + { + "name": "postgresql-credentials", + "optional": false + } + ] + } + ], + "image": "postgres:16-alpine", + "image_pull_policy": "IfNotPresent", + "lifecycle": [], + "liveness_probe": [ + { + "exec": [ + { + "command": [ + "pg_isready", + "-U", + "pgadmin" + ] + } + ], + "failure_threshold": 3, + "grpc": [], + "http_get": [], + "initial_delay_seconds": 30, + "period_seconds": 10, + "success_threshold": 1, + "tcp_socket": [], + "timeout_seconds": 5 + } + ], + "name": "postgresql", + "port": [ + { + "container_port": 5432, + "host_ip": "", + "host_port": 0, + "name": "postgresql", + "protocol": "TCP" + } + ], + "readiness_probe": [ + { + "exec": [ + { + "command": [ + "pg_isready", + "-U", + "pgadmin" + ] + } + ], + "failure_threshold": 3, + "grpc": [], + "http_get": [], + "initial_delay_seconds": 5, + "period_seconds": 5, + "success_threshold": 1, + "tcp_socket": [], + "timeout_seconds": 3 + } + ], + "resources": [ + { + "limits": { + "cpu": "1", + "memory": "1Gi" + }, + "requests": { + "cpu": "250m", + "memory": "512Mi" + } + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "File", + "tty": false, + "volume_device": [], + "volume_mount": [ + { + "mount_path": "/var/lib/postgresql/data", + "mount_propagation": "None", + "name": "postgresql-storage", + "read_only": false, + "sub_path": "postgres", + "sub_path_expr": "" + } + ], + "working_dir": "" + } + ], + "dns_config": [], + "dns_policy": "ClusterFirst", + "enable_service_links": true, + "host_aliases": [], + "host_ipc": false, + "host_network": false, + "host_pid": false, + "hostname": "", + "image_pull_secrets": [], + "init_container": [], + "node_name": "", + "node_selector": {}, + "os": [], + "priority_class_name": "", + "readiness_gate": [], + "restart_policy": "Always", + "runtime_class_name": "", + "scheduler_name": "default-scheduler", + "security_context": [], + "service_account_name": "", + "share_process_namespace": false, + "subdomain": "", + "termination_grace_period_seconds": 30, + "toleration": [], + "topology_spread_constraint": [], + "volume": [ + { + "aws_elastic_block_store": [], + "azure_disk": [], + "azure_file": [], + "ceph_fs": [], + "cinder": [], + "config_map": [], + "csi": [], + "downward_api": [], + "empty_dir": [], + "ephemeral": [], + "fc": [], + "flex_volume": [], + "flocker": [], + "gce_persistent_disk": [], + "git_repo": [], + "glusterfs": [], + "host_path": [], + "iscsi": [], + "local": [], + "name": "postgresql-storage", + "nfs": [], + "persistent_volume_claim": [ + { + "claim_name": "postgresql-pvc", + "read_only": false + } + ], + "photon_persistent_disk": [], + "projected": [], + "quobyte": [], + "rbd": [], + "secret": [], + "vsphere_volume": [] + } + ] + } + ] + } + ], + "update_strategy": [], + "volume_claim_template": [] + } + ], + "timeouts": null, + "wait_for_rollout": true + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": "apps/v1", + "kind": "StatefulSet", + "name": "postgresql", + "namespace": "laravel-app" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6NjAwMDAwMDAwMDAwLCJyZWFkIjo2MDAwMDAwMDAwMDAsInVwZGF0ZSI6NjAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIxIn0=", + "dependencies": [ + "data.azurerm_kubernetes_cluster.main", + "kubernetes_namespace.app", + "kubernetes_persistent_volume_claim.postgresql", + "kubernetes_secret.postgresql" + ] + } + ] + }, + { + "mode": "managed", + "type": "random_string", + "name": "suffix", + "provider": "provider[\"registry.terraform.io/hashicorp/random\"]", + "instances": [ + { + "schema_version": 2, + "attributes": { + "id": "sab31xmv", + "keepers": null, + "length": 8, + "lower": true, + "min_lower": 0, + "min_numeric": 0, + "min_special": 0, + "min_upper": 0, + "number": true, + "numeric": true, + "override_special": null, + "result": "sab31xmv", + "special": false, + "upper": false + }, + "sensitive_attributes": [], + "identity_schema_version": 0 + } + ] + } + ], + "check_results": null +} diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tfvars b/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tfvars new file mode 100644 index 0000000..2da12b7 --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tfvars @@ -0,0 +1,47 @@ +# Azure Configuration +subscription_id = "77677a80-2dea-493d-9867-f1c961b80fb3" +resource_group_name = "trusted_ai_demo_rg" +location = "germanywestcentral" +aks_cluster_name = "trai_k8s_cluster" + +# Application Configuration +app_name = "laravel-app" +app_namespace = "laravel-app" +app_env = "production" +app_debug = false +app_replicas = 2 + +# Docker Image +docker_image = "laravelappreg.azurecr.io/laravel-app:v1.0.1" + +# Laravel Application Key +app_key = "base64:hcb6PXhQOot3SKMl7USAimUWY3c3OGVaRFtaJkDRiTc=" + +# PostgreSQL Configuration +postgresql_admin_username = "pgadmin" +postgresql_admin_password = "SecureLaravelPwd2024!" +postgresql_sku_name = "B_Standard_B1ms" +postgresql_storage_mb = 32768 +postgresql_version = "16" +postgresql_backup_retention_days = 7 + +# Ingress Configuration +ingress_enabled = true +ingress_host = "" # Leave empty for IP-based access, or set to your domain + +# SSL/TLS Configuration +ssl_enabled = false +ssl_issuer_email = "" # Required if ssl_enabled = true + +# Database Restore Configuration +db_restore_enabled = true +db_backup_file_path = "../backups/backup_backend_20251203_101741.dump" + +# Resource Limits +app_resources_requests_cpu = "100m" +app_resources_requests_memory = "256Mi" +app_resources_limits_cpu = "500m" +app_resources_limits_memory = "512Mi" + +# Alert Configuration +alert_email_address = "your-email@example.com" diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tfvars.example b/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tfvars.example new file mode 100644 index 0000000..054a6be --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/terraform.tfvars.example @@ -0,0 +1,47 @@ +# Azure Configuration +subscription_id = "77677a80-2dea-493d-9867-f1c961b80fb3" +resource_group_name = "trusted_ai_demo_rg" +location = "germanywestcentral" +aks_cluster_name = "trai_k8s_cluster" + +# Application Configuration +app_name = "laravel-app" +app_namespace = "laravel-app" +app_env = "production" +app_debug = false +app_replicas = 2 + +# Docker Image (Update this with your actual image) +docker_image = "myregistry.azurecr.io/laravel-app:latest" + +# Laravel Application Key (Generate with: php artisan key:generate --show) +app_key = "base64:YOUR_APP_KEY_HERE" + +# PostgreSQL Configuration +postgresql_admin_username = "pgadmin" +postgresql_admin_password = "ChangeThisPassword123!" +postgresql_sku_name = "B_Standard_B1ms" +postgresql_storage_mb = 32768 +postgresql_version = "16" +postgresql_backup_retention_days = 7 + +# Ingress Configuration +ingress_enabled = true +ingress_host = "" # Leave empty for IP-based access, or set to your domain + +# SSL/TLS Configuration +ssl_enabled = false +ssl_issuer_email = "" # Required if ssl_enabled = true + +# Database Restore Configuration +db_restore_enabled = false +db_backup_file_path = "../backups/backup_backend_20251203_101741.dump" + +# Resource Limits +app_resources_requests_cpu = "100m" +app_resources_requests_memory = "256Mi" +app_resources_limits_cpu = "500m" +app_resources_limits_memory = "512Mi" + +# Alert Configuration +alert_email_address = "your-email@example.com" diff --git a/beispiel-terraform/beispiel-k8s-cluster-afc/variables.tf b/beispiel-terraform/beispiel-k8s-cluster-afc/variables.tf new file mode 100644 index 0000000..59d49c2 --- /dev/null +++ b/beispiel-terraform/beispiel-k8s-cluster-afc/variables.tf @@ -0,0 +1,180 @@ +variable "subscription_id" { + description = "Azure subscription ID" + type = string + default = "77677a80-2dea-493d-9867-f1c961b80fb3" +} + +variable "resource_group_name" { + description = "Name of the existing resource group" + type = string + default = "trusted_ai_demo_rg" +} + +variable "location" { + description = "Azure region for resources" + type = string + default = "germanywestcentral" +} + +variable "aks_cluster_name" { + description = "Name of the existing AKS cluster" + type = string + default = "trai_k8s_cluster" +} + +variable "app_name" { + description = "Name of the application" + type = string + default = "laravel-app" +} + +variable "app_namespace" { + description = "Kubernetes namespace for the application" + type = string + default = "laravel-app" +} + +variable "docker_image" { + description = "Docker image for the Laravel application" + type = string + # Format: /: + # Example: "myregistry.azurecr.io/laravel-app:latest" +} + +variable "app_replicas" { + description = "Number of application replicas" + type = number + default = 2 +} + +variable "app_resources_requests_cpu" { + description = "CPU resource requests for application pods" + type = string + default = "100m" +} + +variable "app_resources_requests_memory" { + description = "Memory resource requests for application pods" + type = string + default = "256Mi" +} + +variable "app_resources_limits_cpu" { + description = "CPU resource limits for application pods" + type = string + default = "500m" +} + +variable "app_resources_limits_memory" { + description = "Memory resource limits for application pods" + type = string + default = "512Mi" +} + +# PostgreSQL Variables +variable "postgresql_admin_username" { + description = "Administrator username for PostgreSQL" + type = string + default = "pgadmin" +} + +variable "postgresql_admin_password" { + description = "Administrator password for PostgreSQL" + type = string + sensitive = true +} + +variable "postgresql_sku_name" { + description = "SKU name for PostgreSQL (e.g., B_Standard_B1ms, GP_Standard_D2s_v3)" + type = string + default = "B_Standard_B1ms" +} + +variable "postgresql_storage_mb" { + description = "Storage size in MB for PostgreSQL" + type = number + default = 32768 # 32 GB +} + +variable "postgresql_version" { + description = "PostgreSQL version" + type = string + default = "16" +} + +variable "postgresql_backup_retention_days" { + description = "Backup retention days for PostgreSQL" + type = number + default = 7 +} + +# Laravel Application Variables +variable "app_key" { + description = "Laravel application key (base64 encoded)" + type = string + sensitive = true +} + +variable "app_env" { + description = "Laravel application environment" + type = string + default = "production" +} + +variable "app_debug" { + description = "Enable Laravel debug mode" + type = bool + default = false +} + +variable "app_url" { + description = "Laravel application URL" + type = string + # Will be set based on ingress if not provided + default = "" +} + +# Ingress Variables +variable "ingress_enabled" { + description = "Enable ingress controller" + type = bool + default = true +} + +variable "ingress_host" { + description = "Hostname for the ingress (leave empty for IP-based access)" + type = string + default = "" +} + +variable "ssl_enabled" { + description = "Enable SSL/TLS (requires cert-manager)" + type = bool + default = false +} + +variable "ssl_issuer_email" { + description = "Email for Let's Encrypt certificate issuer" + type = string + default = "" +} + +# Database Restore Variables +variable "db_restore_enabled" { + description = "Enable database restore job on deployment" + type = bool + default = false +} + +variable "db_backup_file_path" { + description = "Path to the database backup file (local path that will be uploaded)" + type = string + default = "../backups/backup_backend_20251203_101741.dump" +} + +# Alert Variables +variable "alert_email_address" { + description = "Email address for alert notifications" + type = string + sensitive = true +} diff --git a/beispiel-terraform/beispiel-vm/README.md b/beispiel-terraform/beispiel-vm/README.md new file mode 100644 index 0000000..fc791a8 --- /dev/null +++ b/beispiel-terraform/beispiel-vm/README.md @@ -0,0 +1,128 @@ +# Trusted AI Demo - OpenTofu Infrastructure + +This project contains OpenTofu/Terraform configuration for the Trusted AI Demo infrastructure on Azure. + +## Infrastructure Components + +This configuration creates: + +- **Resource Group**: Container for all Azure resources +- **Virtual Network**: 10.0.0.0/16 address space +- **Subnet**: 10.0.1.0/24 for VM placement +- **Network Security Group**: With RDP (port 3389) rule for remote access +- **Public IP**: Static public IP for VM access +- **Network Interface**: Connects VM to the virtual network +- **Windows Server 2022 VM**: Standard_B2s (2 vCPUs, 4 GB RAM) + +## Prerequisites + +- [OpenTofu](https://opentofu.org/) >= 1.0 or [Terraform](https://www.terraform.io/) >= 1.0 +- Azure CLI configured with appropriate credentials +- Azure subscription with necessary permissions + +## Getting Started + +### 1. Authenticate with Azure + +```bash +az login +az account set --subscription "" +``` + +### 2. Initialize OpenTofu + +```bash +tofu init +``` + +Or if using Terraform: + +```bash +terraform init +``` + +### 3. Review the Plan + +```bash +tofu plan +``` + +### 4. Apply the Configuration + +```bash +tofu apply +``` + +## Project Structure + +- `main.tf` - Main infrastructure configuration +- `variables.tf` - Input variable definitions +- `outputs.tf` - Output value definitions +- `terraform.tfvars.example` - Example variable values (copy to `terraform.tfvars`) + +## Configuration + +### Required Configuration + +1. Copy the example variables file: + ```bash + cp terraform.tfvars.example terraform.tfvars + ``` + +2. Edit `terraform.tfvars` and set your admin password: + ```hcl + admin_password = "YourSecurePassword123!" + ``` + + **Important**: The password must be at least 12 characters long and contain uppercase, lowercase, and numbers. + +3. **Security Recommendation**: Change `allowed_rdp_source` to your public IP address instead of `"*"`: + ```hcl + allowed_rdp_source = "YOUR_PUBLIC_IP/32" + ``` + + You can find your public IP with: + ```bash + curl ifconfig.me + ``` + +### Optional Customization + +You can also customize: +- `resource_group_name`: Name of the resource group +- `location`: Azure region (default: westeurope) +- `vm_name`: Name of the virtual machine +- `vm_size`: VM size (default: Standard_B2s) +- `vnet_address_space`: Virtual network address space +- `subnet_address_prefix`: Subnet address prefix + +## Connecting to the VM + +After the infrastructure is created, you can connect to the Windows VM via RDP: + +1. Get the public IP address: + ```bash + tofu output vm_public_ip + ``` + +2. Connect using Remote Desktop: + - **Windows**: Use the connection string from output: + ```bash + tofu output rdp_connection_string + ``` + - **macOS**: Use Microsoft Remote Desktop app + - **Linux**: Use Remmina or similar RDP client + +3. Login credentials: + - Username: The value you set for `admin_username` (default: azureadmin) + - Password: The password you set in `terraform.tfvars` + +## Cleanup + +To destroy all resources: + +```bash +tofu destroy +``` + +**Warning**: This will permanently delete all resources created by this configuration. diff --git a/beispiel-terraform/beispiel-vm/main.tf b/beispiel-terraform/beispiel-vm/main.tf new file mode 100644 index 0000000..06c4cca --- /dev/null +++ b/beispiel-terraform/beispiel-vm/main.tf @@ -0,0 +1,123 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~> 3.0" + } + } +} + +provider "azurerm" { + features {} +} + +# Resource Group +resource "azurerm_resource_group" "main" { + name = var.resource_group_name + location = var.location + + tags = var.tags +} + +# Virtual Network +resource "azurerm_virtual_network" "main" { + name = "${var.resource_group_name}-vnet" + address_space = [var.vnet_address_space] + location = azurerm_resource_group.main.location + resource_group_name = azurerm_resource_group.main.name + + tags = var.tags +} + +# Subnet +resource "azurerm_subnet" "main" { + name = "default" + resource_group_name = azurerm_resource_group.main.name + virtual_network_name = azurerm_virtual_network.main.name + address_prefixes = [var.subnet_address_prefix] +} + +# Network Security Group +resource "azurerm_network_security_group" "main" { + name = "${var.vm_name}-nsg" + location = azurerm_resource_group.main.location + resource_group_name = azurerm_resource_group.main.name + + security_rule { + name = "SSH" + priority = 1001 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "22" + source_address_prefix = var.allowed_ssh_source + destination_address_prefix = "*" + } + + tags = var.tags +} + +# Public IP +resource "azurerm_public_ip" "main" { + name = "${var.vm_name}-pip" + location = azurerm_resource_group.main.location + resource_group_name = azurerm_resource_group.main.name + allocation_method = "Static" + sku = "Standard" + + tags = var.tags +} + +# Network Interface +resource "azurerm_network_interface" "main" { + name = "${var.vm_name}-nic" + location = azurerm_resource_group.main.location + resource_group_name = azurerm_resource_group.main.name + + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.main.id + private_ip_address_allocation = "Dynamic" + public_ip_address_id = azurerm_public_ip.main.id + } + + tags = var.tags +} + +# Associate NSG with Network Interface +resource "azurerm_network_interface_security_group_association" "main" { + network_interface_id = azurerm_network_interface.main.id + network_security_group_id = azurerm_network_security_group.main.id +} + +# Linux Virtual Machine +resource "azurerm_linux_virtual_machine" "main" { + name = var.vm_name + resource_group_name = azurerm_resource_group.main.name + location = azurerm_resource_group.main.location + size = var.vm_size + admin_username = var.admin_username + admin_password = var.admin_password + disable_password_authentication = false + + network_interface_ids = [ + azurerm_network_interface.main.id, + ] + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "Canonical" + offer = "0001-com-ubuntu-server-jammy" + sku = "22_04-lts-gen2" + version = "latest" + } + + tags = var.tags +} diff --git a/beispiel-terraform/beispiel-vm/outputs.tf b/beispiel-terraform/beispiel-vm/outputs.tf new file mode 100644 index 0000000..afefb82 --- /dev/null +++ b/beispiel-terraform/beispiel-vm/outputs.tf @@ -0,0 +1,56 @@ +output "resource_group_name" { + description = "Name of the created resource group" + value = azurerm_resource_group.main.name +} + +output "resource_group_id" { + description = "ID of the created resource group" + value = azurerm_resource_group.main.id +} + +output "location" { + description = "Location of the resource group" + value = azurerm_resource_group.main.location +} + +# Network Outputs +output "vnet_name" { + description = "Name of the virtual network" + value = azurerm_virtual_network.main.name +} + +output "vnet_id" { + description = "ID of the virtual network" + value = azurerm_virtual_network.main.id +} + +output "subnet_id" { + description = "ID of the subnet" + value = azurerm_subnet.main.id +} + +# VM Outputs +output "vm_name" { + description = "Name of the virtual machine" + value = azurerm_linux_virtual_machine.main.name +} + +output "vm_id" { + description = "ID of the virtual machine" + value = azurerm_linux_virtual_machine.main.id +} + +output "vm_private_ip" { + description = "Private IP address of the VM" + value = azurerm_network_interface.main.private_ip_address +} + +output "vm_public_ip" { + description = "Public IP address of the VM" + value = azurerm_public_ip.main.ip_address +} + +output "ssh_connection_string" { + description = "SSH connection string for the VM" + value = "ssh ${azurerm_linux_virtual_machine.main.admin_username}@${azurerm_public_ip.main.ip_address}" +} diff --git a/beispiel-terraform/beispiel-vm/terraform.tfstate b/beispiel-terraform/beispiel-vm/terraform.tfstate new file mode 100644 index 0000000..3353b08 --- /dev/null +++ b/beispiel-terraform/beispiel-vm/terraform.tfstate @@ -0,0 +1 @@ +{"version":4,"terraform_version":"1.11.1","serial":11,"lineage":"cad35d7f-13a9-933b-a7ef-af99f16a5d08","outputs":{},"resources":[],"check_results":null} diff --git a/beispiel-terraform/beispiel-vm/terraform.tfstate.backup b/beispiel-terraform/beispiel-vm/terraform.tfstate.backup new file mode 100644 index 0000000..fd79379 --- /dev/null +++ b/beispiel-terraform/beispiel-vm/terraform.tfstate.backup @@ -0,0 +1 @@ +{"version":4,"terraform_version":"1.11.1","serial":7,"lineage":"cad35d7f-13a9-933b-a7ef-af99f16a5d08","outputs":{"location":{"value":"austriaeast","type":"string"},"resource_group_id":{"value":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo","type":"string"},"resource_group_name":{"value":"rg-trusted-ai-demo","type":"string"},"ssh_connection_string":{"value":"ssh azureadmin@68.210.97.125","type":"string"},"subnet_id":{"value":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/virtualNetworks/rg-trusted-ai-demo-vnet/subnets/default","type":"string"},"vm_id":{"value":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Compute/virtualMachines/vm-trusted-ai","type":"string"},"vm_name":{"value":"vm-trusted-ai","type":"string"},"vm_private_ip":{"value":"10.0.1.4","type":"string"},"vm_public_ip":{"value":"68.210.97.125","type":"string"},"vnet_id":{"value":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/virtualNetworks/rg-trusted-ai-demo-vnet","type":"string"},"vnet_name":{"value":"rg-trusted-ai-demo-vnet","type":"string"}},"resources":[{"mode":"managed","type":"azurerm_linux_virtual_machine","name":"main","provider":"provider[\"registry.opentofu.org/hashicorp/azurerm\"]","instances":[{"schema_version":0,"attributes":{"additional_capabilities":[],"admin_password":"Hab2009Keins!","admin_ssh_key":[],"admin_username":"azureadmin","allow_extension_operations":true,"availability_set_id":"","boot_diagnostics":[],"bypass_platform_safety_checks_on_user_schedule_enabled":false,"capacity_reservation_group_id":"","computer_name":"vm-trusted-ai","custom_data":null,"dedicated_host_group_id":"","dedicated_host_id":"","disable_password_authentication":false,"disk_controller_type":"SCSI","edge_zone":"","encryption_at_host_enabled":false,"eviction_policy":"","extensions_time_budget":"PT1H30M","gallery_application":[],"id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Compute/virtualMachines/vm-trusted-ai","identity":[],"license_type":"","location":"austriaeast","max_bid_price":-1,"name":"vm-trusted-ai","network_interface_ids":["/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/networkInterfaces/vm-trusted-ai-nic"],"os_disk":[{"caching":"ReadWrite","diff_disk_settings":[],"disk_encryption_set_id":"","disk_size_gb":30,"name":"vm-trusted-ai_OsDisk_1_26ed4d6fa684410abb7d858ef85be362","secure_vm_disk_encryption_set_id":"","security_encryption_type":"","storage_account_type":"Standard_LRS","write_accelerator_enabled":false}],"os_image_notification":[],"patch_assessment_mode":"ImageDefault","patch_mode":"ImageDefault","plan":[],"platform_fault_domain":-1,"priority":"Regular","private_ip_address":"10.0.1.4","private_ip_addresses":["10.0.1.4"],"provision_vm_agent":true,"proximity_placement_group_id":"","public_ip_address":"68.210.97.125","public_ip_addresses":["68.210.97.125"],"reboot_setting":"","resource_group_name":"rg-trusted-ai-demo","secret":[],"secure_boot_enabled":false,"size":"Standard_B2ats_v2","source_image_id":"","source_image_reference":[{"offer":"0001-com-ubuntu-server-jammy","publisher":"Canonical","sku":"22_04-lts-gen2","version":"latest"}],"tags":{"Environment":"dev","ManagedBy":"OpenTofu","Project":"trusted-ai-demo"},"termination_notification":null,"timeouts":null,"user_data":"","virtual_machine_id":"c0bc058a-065f-44e0-9bf7-e9343e91e666","virtual_machine_scale_set_id":"","vm_agent_platform_updates_enabled":false,"vtpm_enabled":false,"zone":""},"sensitive_attributes":[[{"type":"get_attr","value":"admin_password"}],[{"type":"get_attr","value":"custom_data"}]],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoyNzAwMDAwMDAwMDAwLCJkZWxldGUiOjI3MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMCwidXBkYXRlIjoyNzAwMDAwMDAwMDAwfX0=","dependencies":["azurerm_network_interface.main","azurerm_public_ip.main","azurerm_resource_group.main","azurerm_subnet.main","azurerm_virtual_network.main"]}]},{"mode":"managed","type":"azurerm_network_interface","name":"main","provider":"provider[\"registry.opentofu.org/hashicorp/azurerm\"]","instances":[{"schema_version":0,"attributes":{"accelerated_networking_enabled":false,"applied_dns_servers":[],"auxiliary_mode":"","auxiliary_sku":"","dns_servers":[],"edge_zone":"","enable_accelerated_networking":false,"enable_ip_forwarding":false,"id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/networkInterfaces/vm-trusted-ai-nic","internal_dns_name_label":"","internal_domain_name_suffix":"cu22vk1awgdetoispofbty41oa.viex.internal.cloudapp.net","ip_configuration":[{"gateway_load_balancer_frontend_ip_configuration_id":"","name":"internal","primary":true,"private_ip_address":"10.0.1.4","private_ip_address_allocation":"Dynamic","private_ip_address_version":"IPv4","public_ip_address_id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/publicIPAddresses/vm-trusted-ai-pip","subnet_id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/virtualNetworks/rg-trusted-ai-demo-vnet/subnets/default"}],"ip_forwarding_enabled":false,"location":"austriaeast","mac_address":"7C-1E-52-8E-8B-F8","name":"vm-trusted-ai-nic","private_ip_address":"10.0.1.4","private_ip_addresses":["10.0.1.4"],"resource_group_name":"rg-trusted-ai-demo","tags":{"Environment":"dev","ManagedBy":"OpenTofu","Project":"trusted-ai-demo"},"timeouts":null,"virtual_machine_id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Compute/virtualMachines/vm-trusted-ai"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxODAwMDAwMDAwMDAwLCJkZWxldGUiOjE4MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMCwidXBkYXRlIjoxODAwMDAwMDAwMDAwfX0=","dependencies":["azurerm_public_ip.main","azurerm_resource_group.main","azurerm_subnet.main","azurerm_virtual_network.main"]}]},{"mode":"managed","type":"azurerm_network_interface_security_group_association","name":"main","provider":"provider[\"registry.opentofu.org/hashicorp/azurerm\"]","instances":[{"schema_version":0,"attributes":{"id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/networkInterfaces/vm-trusted-ai-nic|/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/networkSecurityGroups/vm-trusted-ai-nsg","network_interface_id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/networkInterfaces/vm-trusted-ai-nic","network_security_group_id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/networkSecurityGroups/vm-trusted-ai-nsg","timeouts":null},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxODAwMDAwMDAwMDAwLCJkZWxldGUiOjE4MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMH19","dependencies":["azurerm_network_interface.main","azurerm_network_security_group.main","azurerm_public_ip.main","azurerm_resource_group.main","azurerm_subnet.main","azurerm_virtual_network.main"]}]},{"mode":"managed","type":"azurerm_network_security_group","name":"main","provider":"provider[\"registry.opentofu.org/hashicorp/azurerm\"]","instances":[{"schema_version":0,"attributes":{"id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/networkSecurityGroups/vm-trusted-ai-nsg","location":"austriaeast","name":"vm-trusted-ai-nsg","resource_group_name":"rg-trusted-ai-demo","security_rule":[{"access":"Allow","description":"","destination_address_prefix":"*","destination_address_prefixes":[],"destination_application_security_group_ids":[],"destination_port_range":"22","destination_port_ranges":[],"direction":"Inbound","name":"SSH","priority":1001,"protocol":"Tcp","source_address_prefix":"*","source_address_prefixes":[],"source_application_security_group_ids":[],"source_port_range":"*","source_port_ranges":[]}],"tags":{"Environment":"dev","ManagedBy":"OpenTofu","Project":"trusted-ai-demo"},"timeouts":null},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxODAwMDAwMDAwMDAwLCJkZWxldGUiOjE4MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMCwidXBkYXRlIjoxODAwMDAwMDAwMDAwfX0=","dependencies":["azurerm_resource_group.main"]}]},{"mode":"managed","type":"azurerm_public_ip","name":"main","provider":"provider[\"registry.opentofu.org/hashicorp/azurerm\"]","instances":[{"schema_version":0,"attributes":{"allocation_method":"Static","ddos_protection_mode":"VirtualNetworkInherited","ddos_protection_plan_id":null,"domain_name_label":null,"edge_zone":"","fqdn":null,"id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/publicIPAddresses/vm-trusted-ai-pip","idle_timeout_in_minutes":4,"ip_address":"68.210.97.125","ip_tags":{},"ip_version":"IPv4","location":"austriaeast","name":"vm-trusted-ai-pip","public_ip_prefix_id":null,"resource_group_name":"rg-trusted-ai-demo","reverse_fqdn":null,"sku":"Standard","sku_tier":"Regional","tags":{"Environment":"dev","ManagedBy":"OpenTofu","Project":"trusted-ai-demo"},"timeouts":null,"zones":[]},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxODAwMDAwMDAwMDAwLCJkZWxldGUiOjE4MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMCwidXBkYXRlIjoxODAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIwIn0=","dependencies":["azurerm_resource_group.main"]}]},{"mode":"managed","type":"azurerm_resource_group","name":"main","provider":"provider[\"registry.opentofu.org/hashicorp/azurerm\"]","instances":[{"schema_version":0,"attributes":{"id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo","location":"austriaeast","managed_by":"","name":"rg-trusted-ai-demo","tags":{"Environment":"dev","ManagedBy":"OpenTofu","Project":"trusted-ai-demo"},"timeouts":null},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo1NDAwMDAwMDAwMDAwLCJkZWxldGUiOjU0MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMCwidXBkYXRlIjo1NDAwMDAwMDAwMDAwfX0="}]},{"mode":"managed","type":"azurerm_subnet","name":"main","provider":"provider[\"registry.opentofu.org/hashicorp/azurerm\"]","instances":[{"schema_version":0,"attributes":{"address_prefixes":["10.0.1.0/24"],"default_outbound_access_enabled":true,"delegation":[],"enforce_private_link_endpoint_network_policies":false,"enforce_private_link_service_network_policies":false,"id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/virtualNetworks/rg-trusted-ai-demo-vnet/subnets/default","name":"default","private_endpoint_network_policies":"Enabled","private_endpoint_network_policies_enabled":true,"private_link_service_network_policies_enabled":true,"resource_group_name":"rg-trusted-ai-demo","service_endpoint_policy_ids":[],"service_endpoints":[],"timeouts":null,"virtual_network_name":"rg-trusted-ai-demo-vnet"},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxODAwMDAwMDAwMDAwLCJkZWxldGUiOjE4MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMCwidXBkYXRlIjoxODAwMDAwMDAwMDAwfX0=","dependencies":["azurerm_resource_group.main","azurerm_virtual_network.main"]}]},{"mode":"managed","type":"azurerm_virtual_network","name":"main","provider":"provider[\"registry.opentofu.org/hashicorp/azurerm\"]","instances":[{"schema_version":0,"attributes":{"address_space":["10.0.0.0/16"],"bgp_community":"","ddos_protection_plan":[],"dns_servers":[],"edge_zone":"","encryption":[],"flow_timeout_in_minutes":0,"guid":"abca3915-b160-4986-b912-7b8a19e3db70","id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/virtualNetworks/rg-trusted-ai-demo-vnet","location":"austriaeast","name":"rg-trusted-ai-demo-vnet","resource_group_name":"rg-trusted-ai-demo","subnet":[{"address_prefix":"10.0.1.0/24","id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/virtualNetworks/rg-trusted-ai-demo-vnet/subnets/default","name":"default","security_group":""}],"tags":{"Environment":"dev","ManagedBy":"OpenTofu","Project":"trusted-ai-demo"},"timeouts":null},"sensitive_attributes":[],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxODAwMDAwMDAwMDAwLCJkZWxldGUiOjE4MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMCwidXBkYXRlIjoxODAwMDAwMDAwMDAwfX0=","dependencies":["azurerm_resource_group.main"]}]}],"check_results":null} diff --git a/beispiel-terraform/beispiel-vm/terraform.tfvars b/beispiel-terraform/beispiel-vm/terraform.tfvars new file mode 100644 index 0000000..0366df6 --- /dev/null +++ b/beispiel-terraform/beispiel-vm/terraform.tfvars @@ -0,0 +1,23 @@ +# Copy this file to terraform.tfvars and customize as needed + +# Resource Group Configuration +resource_group_name = "rg-trusted-ai-demo" +location = "austriaeast" + +# Network Configuration +vnet_address_space = "10.0.0.0/16" +subnet_address_prefix = "10.0.1.0/24" +allowed_ssh_source = "*" # Change to your public IP for better security, e.g., "1.2.3.4/32" + +# Virtual Machine Configuration +vm_name = "vm-trusted-ai" +vm_size = "Standard_B2ats_v2" +admin_username = "azureadmin" +admin_password = "Hab2009Keins!" + +# Tags +tags = { + Environment = "dev" + Project = "trusted-ai-demo" + ManagedBy = "OpenTofu" +} diff --git a/beispiel-terraform/beispiel-vm/terraform.tfvars.example b/beispiel-terraform/beispiel-vm/terraform.tfvars.example new file mode 100644 index 0000000..353f170 --- /dev/null +++ b/beispiel-terraform/beispiel-vm/terraform.tfvars.example @@ -0,0 +1,23 @@ +# Copy this file to terraform.tfvars and customize as needed + +# Resource Group Configuration +resource_group_name = "rg-trusted-ai-demo" +location = "westus" + +# Network Configuration +vnet_address_space = "10.0.0.0/16" +subnet_address_prefix = "10.0.1.0/24" +allowed_rdp_source = "*" # Change to your public IP for better security, e.g., "1.2.3.4/32" + +# Virtual Machine Configuration +vm_name = "vm-trusted-ai" +vm_size = "Standard_B2s" +admin_username = "azureadmin" +admin_password = "YourSecurePassword123!" # IMPORTANT: Change this! Min 12 characters, must include upper, lower, number + +# Tags +tags = { + Environment = "dev" + Project = "trusted-ai-demo" + ManagedBy = "OpenTofu" +} diff --git a/beispiel-terraform/beispiel-vm/variables.tf b/beispiel-terraform/beispiel-vm/variables.tf new file mode 100644 index 0000000..d2abc25 --- /dev/null +++ b/beispiel-terraform/beispiel-vm/variables.tf @@ -0,0 +1,65 @@ +variable "resource_group_name" { + description = "Name of the resource group" + type = string + default = "rg-trusted-ai-demo" +} + +variable "location" { + description = "Azure region for resources" + type = string + default = "austriaeast" +} + +variable "tags" { + description = "Tags to apply to all resources" + type = map(string) + default = { + Environment = "dev" + Project = "trusted-ai-demo" + ManagedBy = "OpenTofu" + } +} + +# Network Configuration +variable "vnet_address_space" { + description = "Address space for the virtual network" + type = string + default = "10.0.0.0/16" +} + +variable "subnet_address_prefix" { + description = "Address prefix for the subnet" + type = string + default = "10.0.1.0/24" +} + +variable "allowed_ssh_source" { + description = "Source IP address or range allowed to SSH to the VM (use your public IP or '*' for any - not recommended)" + type = string + default = "*" +} + +# Virtual Machine Configuration +variable "vm_name" { + description = "Name of the virtual machine" + type = string + default = "vm-trusted-ai" +} + +variable "vm_size" { + description = "Size of the virtual machine" + type = string + default = "Standard_B2s" +} + +variable "admin_username" { + description = "Admin username for the virtual machine" + type = string + default = "azureadmin" +} + +variable "admin_password" { + description = "Admin password for the virtual machine (use strong password, min 12 characters)" + type = string + sensitive = true +}