From 85b2066f668db9faf2cb96b7e118fd3a154d3e7a Mon Sep 17 00:00:00 2001 From: Benjamin Gervais Date: Mon, 20 Apr 2026 15:23:34 -0400 Subject: [PATCH 1/4] feat: add AWS, GCP, and Azure skill files for on-demand loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The agent can now load_skill('aws'), load_skill('gcp'), or load_skill('azure') to get provider-specific CLI commands, Terraform examples, error recovery steps, and critical rules — instead of relying solely on the generic cloud_access.md core prompt. Each skill follows the same structure as OVH/Scaleway: - Discovery, compute, Kubernetes, storage, database, networking commands - Terraform resource examples (provider block auto-generated) - Multi-account support (AWS), resource group rules (Azure), project setup (GCP) - Error recovery playbook per provider Co-Authored-By: Claude Opus 4.6 --- .../agent/skills/integrations/aws/SKILL.md | 162 +++++++++++++++ .../agent/skills/integrations/azure/SKILL.md | 194 ++++++++++++++++++ .../agent/skills/integrations/gcp/SKILL.md | 184 +++++++++++++++++ 3 files changed, 540 insertions(+) create mode 100644 server/chat/backend/agent/skills/integrations/aws/SKILL.md create mode 100644 server/chat/backend/agent/skills/integrations/azure/SKILL.md create mode 100644 server/chat/backend/agent/skills/integrations/gcp/SKILL.md diff --git a/server/chat/backend/agent/skills/integrations/aws/SKILL.md b/server/chat/backend/agent/skills/integrations/aws/SKILL.md new file mode 100644 index 000000000..0e29fa44a --- /dev/null +++ b/server/chat/backend/agent/skills/integrations/aws/SKILL.md @@ -0,0 +1,162 @@ +--- +name: aws +id: aws +description: "AWS integration for managing EC2, RDS, S3, Lambda, EKS, CloudWatch, IAM, and other services via CLI and Terraform" +category: cloud_provider +connection_check: + method: provider_in_preference +tools: + - cloud_exec + - iac_tool +index: "AWS — EC2, RDS, S3, Lambda, EKS, CloudWatch, IAM, Terraform IaC" +rca_priority: 10 +allowed-tools: cloud_exec, iac_tool +metadata: + author: aurora + version: "1.0" +--- + +# AWS Integration + +## Overview +Amazon Web Services cloud provider for managing compute, storage, databases, containers, serverless, networking, and observability. + +## Instructions + +### CLI COMMANDS (use cloud_exec with 'aws') + +**CRITICAL: Always use cloud_exec('aws', 'COMMAND') — NOT terminal_exec!** +Authentication and credentials are auto-configured. The `aws` CLI is available. +Additional CLIs: `kubectl`, `eksctl`, `sam`, `cdk`, `helm`, `terraform`. + +**MULTI-ACCOUNT SUPPORT:** +- First call without `account_id` fans out to ALL connected accounts and returns `results_by_account`. +- Identify the relevant account from the results. +- ALL subsequent calls MUST include `account_id=''` to target that account. +- Never keep querying all accounts after you know which one matters. + +**Discovery Commands:** +- Caller identity: `cloud_exec('aws', 'sts get-caller-identity')` +- List regions: `cloud_exec('aws', 'ec2 describe-regions --output table', account_id='')` +- List services: `cloud_exec('aws', 'service-quotas list-services', account_id='')` + +**EC2 (Compute):** +- List instances: `cloud_exec('aws', 'ec2 describe-instances --query "Reservations[].Instances[].{ID:InstanceId,Type:InstanceType,State:State.Name,Name:Tags[?Key==`Name`].Value|[0]}" --output table', account_id='')` +- Start/stop: `cloud_exec('aws', 'ec2 start-instances --instance-ids ', account_id='')` +- Describe: `cloud_exec('aws', 'ec2 describe-instances --instance-ids ', account_id='')` +- Security groups: `cloud_exec('aws', 'ec2 describe-security-groups --group-ids ', account_id='')` + +**EKS (Kubernetes):** +- List clusters: `cloud_exec('aws', 'eks list-clusters', account_id='')` +- Describe cluster: `cloud_exec('aws', 'eks describe-cluster --name ', account_id='')` +- Get kubeconfig: `cloud_exec('aws', 'eks update-kubeconfig --name --region ', account_id='')` +- Then kubectl: `cloud_exec('aws', 'kubectl get pods -n -o wide', account_id='')` + +**S3 (Storage):** +- List buckets: `cloud_exec('aws', 's3 ls', account_id='')` +- List objects: `cloud_exec('aws', 's3 ls s3:///', account_id='')` +- Copy: `cloud_exec('aws', 's3 cp ', account_id='')` + +**RDS (Databases):** +- List instances: `cloud_exec('aws', 'rds describe-db-instances --query "DBInstances[].{ID:DBInstanceIdentifier,Engine:Engine,Status:DBInstanceStatus}" --output table', account_id='')` +- Describe: `cloud_exec('aws', 'rds describe-db-instances --db-instance-identifier ', account_id='')` + +**Lambda (Serverless):** +- List functions: `cloud_exec('aws', 'lambda list-functions --query "Functions[].{Name:FunctionName,Runtime:Runtime}" --output table', account_id='')` +- Invoke: `cloud_exec('aws', 'lambda invoke --function-name /dev/stdout', account_id='')` +- Get logs: `cloud_exec('aws', 'logs filter-log-events --log-group-name /aws/lambda/ --start-time --limit 50', account_id='')` + +**CloudWatch (Monitoring):** +- Query logs: `cloud_exec('aws', 'logs filter-log-events --log-group-name --start-time --filter-pattern ""', account_id='')` +- List log groups: `cloud_exec('aws', 'logs describe-log-groups', account_id='')` +- Get metrics: `cloud_exec('aws', 'cloudwatch get-metric-statistics --namespace --metric-name --dimensions Name=,Value= --start-time --end-time --period 300 --statistics Average', account_id='')` +- List alarms: `cloud_exec('aws', 'cloudwatch describe-alarms --state-value ALARM', account_id='')` + +**IAM:** +- List roles: `cloud_exec('aws', 'iam list-roles --query "Roles[].{Name:RoleName,Arn:Arn}" --output table', account_id='')` +- Get role policy: `cloud_exec('aws', 'iam list-attached-role-policies --role-name ', account_id='')` + +**Networking:** +- List VPCs: `cloud_exec('aws', 'ec2 describe-vpcs --output table', account_id='')` +- List subnets: `cloud_exec('aws', 'ec2 describe-subnets --filters "Name=vpc-id,Values=" --output table', account_id='')` +- List load balancers: `cloud_exec('aws', 'elbv2 describe-load-balancers --output table', account_id='')` +- Route tables: `cloud_exec('aws', 'ec2 describe-route-tables --filters "Name=vpc-id,Values="', account_id='')` + +### TERRAFORM FOR AWS +Use iac_tool — provider.tf is AUTO-GENERATED, just write the resource! + +**PREREQUISITE:** Get account ID first: +`cloud_exec('aws', "sts get-caller-identity --query 'Account' --output text", account_id='')` + +**EC2 INSTANCE EXAMPLE:** +```hcl +resource "aws_instance" "vm" { + ami = "ami-0c55b159cbfafe1f0" + instance_type = "t3.micro" + subnet_id = "" + + tags = { + Name = "my-vm" + } +} +``` + +**EKS CLUSTER:** +```hcl +resource "aws_eks_cluster" "cluster" { + name = "my-cluster" + role_arn = "" + + vpc_config { + subnet_ids = ["", ""] + } +} +``` + +**S3 BUCKET:** +```hcl +resource "aws_s3_bucket" "bucket" { + bucket = "my-bucket-unique-name" +} +``` + +**RDS INSTANCE:** +```hcl +resource "aws_db_instance" "db" { + allocated_storage = 20 + engine = "postgres" + engine_version = "15" + instance_class = "db.t3.micro" + db_name = "mydb" + username = "admin" + manage_master_user_password = true + skip_final_snapshot = true +} +``` + +**Common AWS Terraform resources:** +- `aws_instance` — EC2 virtual machines +- `aws_security_group` — Firewall rules +- `aws_eks_cluster`, `aws_eks_node_group` — Kubernetes +- `aws_s3_bucket` — Object storage +- `aws_db_instance` — RDS databases +- `aws_lambda_function` — Serverless functions +- `aws_vpc`, `aws_subnet` — Networking +- `aws_lb`, `aws_lb_target_group` — Load balancers +- `aws_iam_role`, `aws_iam_policy` — IAM + +DO NOT write terraform{} or provider{} blocks — they are auto-generated! + +### CRITICAL RULES +- ALWAYS target a specific account_id after the first fan-out call +- Use `--output table` or `--query` with JMESPath for readable output +- AMI IDs are region-specific — look them up or use data sources +- Default region: us-east-1 unless user specifies otherwise +- For EKS: always run `update-kubeconfig` before kubectl commands +- Get real values (VPC IDs, subnet IDs, AMI IDs) from CLI before writing Terraform + +### ON ANY AWS ERROR +1. Permission denied → Check IAM role/policy: `cloud_exec('aws', 'iam get-role --role-name ', account_id='')` +2. Service not enabled → Not applicable for AWS (services are always available) +3. CLI syntax error → Use `cloud_exec('aws', ' help')` to check correct subcommand +4. Terraform failure → Run `cloud_exec('aws', ...)` to verify resources exist, then fix the manifest diff --git a/server/chat/backend/agent/skills/integrations/azure/SKILL.md b/server/chat/backend/agent/skills/integrations/azure/SKILL.md new file mode 100644 index 000000000..7bba13b30 --- /dev/null +++ b/server/chat/backend/agent/skills/integrations/azure/SKILL.md @@ -0,0 +1,194 @@ +--- +name: azure +id: azure +description: "Microsoft Azure integration for managing VMs, AKS, SQL, Storage, App Service, and other services via CLI and Terraform" +category: cloud_provider +connection_check: + method: provider_in_preference +tools: + - cloud_exec + - iac_tool +index: "Azure — VMs, AKS, SQL, Storage, App Service, Terraform IaC" +rca_priority: 10 +allowed-tools: cloud_exec, iac_tool +metadata: + author: aurora + version: "1.0" +--- + +# Microsoft Azure Integration + +## Overview +Azure cloud provider for managing compute, Kubernetes, databases, storage, serverless, networking, and monitoring. + +## Instructions + +### CLI COMMANDS (use cloud_exec with 'azure') + +**CRITICAL: Always use cloud_exec('azure', 'COMMAND') — NOT terminal_exec!** +Authentication and subscription are auto-configured. The `az` CLI is available. +Additional CLIs: `kubectl`. + +**SUBSCRIPTION & RESOURCE GROUP SETUP:** +- Get subscription: `cloud_exec('azure', "account show --query 'id' -o tsv")` +- List resource groups: `cloud_exec('azure', 'group list --output table')` +- Create resource group: `cloud_exec('azure', 'group create --name --location ')` +- **MANDATORY:** Every Azure resource lives in a resource group. Check for existing ones before creating new ones. + +**Discovery Commands:** +- Account info: `cloud_exec('azure', 'account show')` +- List locations: `cloud_exec('azure', 'account list-locations --output table')` +- List providers: `cloud_exec('azure', 'provider list --output table')` +- Register provider: `cloud_exec('azure', 'provider register --namespace Microsoft.')` +- List resources in RG: `cloud_exec('azure', 'resource list --resource-group --output table')` + +**Virtual Machines:** +- List VMs: `cloud_exec('azure', 'vm list --output table')` +- Create VM: `cloud_exec('azure', 'vm create --resource-group --name --image Ubuntu2204 --size Standard_B2s --generate-ssh-keys')` +- Start/stop/restart: `cloud_exec('azure', 'vm start|stop|restart --resource-group --name ')` +- Describe: `cloud_exec('azure', 'vm show --resource-group --name ')` +- List sizes: `cloud_exec('azure', 'vm list-sizes --location --output table')` + +**AKS (Kubernetes):** +- List clusters: `cloud_exec('azure', 'aks list --output table')` +- Describe cluster: `cloud_exec('azure', 'aks show --name --resource-group ')` +- Get credentials: `cloud_exec('azure', 'aks get-credentials --name --resource-group ')` +- Then kubectl: `cloud_exec('azure', 'kubectl get pods -n -o wide')` +- Node pools: `cloud_exec('azure', 'aks nodepool list --cluster-name --resource-group --output table')` + +**Storage:** +- List accounts: `cloud_exec('azure', 'storage account list --output table')` +- Create account: `cloud_exec('azure', 'storage account create --name --resource-group --location --sku Standard_LRS')` +- List containers: `cloud_exec('azure', 'storage container list --account-name --output table')` +- List blobs: `cloud_exec('azure', 'storage blob list --account-name --container-name --output table')` + +**SQL Database:** +- List servers: `cloud_exec('azure', 'sql server list --output table')` +- List databases: `cloud_exec('azure', 'sql db list --server --resource-group --output table')` +- Create server: `cloud_exec('azure', 'sql server create --name --resource-group --location --admin-user --admin-password ')` + +**App Service (Web Apps):** +- List apps: `cloud_exec('azure', 'webapp list --output table')` +- Create plan: `cloud_exec('azure', 'appservice plan create --name --resource-group --sku B1 --is-linux')` +- Create app: `cloud_exec('azure', 'webapp create --name --resource-group --plan --runtime "PYTHON:3.11"')` +- View logs: `cloud_exec('azure', 'webapp log tail --name --resource-group ')` + +**Azure Monitor & Log Analytics:** +- Query logs: `cloud_exec('azure', 'monitor log-analytics query -w --analytics-query ""')` +- List alerts: `cloud_exec('azure', 'monitor alert list --output table')` +- Metrics: `cloud_exec('azure', 'monitor metrics list --resource --metric "" --interval PT1H')` +- Activity log: `cloud_exec('azure', 'monitor activity-log list --start-time --end-time ')` + +**Networking:** +- List VNets: `cloud_exec('azure', 'network vnet list --output table')` +- List NSGs: `cloud_exec('azure', 'network nsg list --output table')` +- NSG rules: `cloud_exec('azure', 'network nsg rule list --nsg-name --resource-group --output table')` +- List public IPs: `cloud_exec('azure', 'network public-ip list --output table')` +- List load balancers: `cloud_exec('azure', 'network lb list --output table')` + +### TERRAFORM FOR AZURE +Use iac_tool — provider.tf is AUTO-GENERATED, just write the resource! + +**PREREQUISITE:** Get subscription ID first: +`cloud_exec('azure', "account show --query 'id' -o tsv")` + +**IMPORTANT:** The system auto-generates strong admin passwords using Terraform's `random_password` resource. Do NOT ask users for passwords. + +**RESOURCE GROUP (always needed first):** +```hcl +resource "azurerm_resource_group" "rg" { + name = "my-rg" + location = "eastus" +} +``` + +**VIRTUAL MACHINE:** +```hcl +resource "azurerm_linux_virtual_machine" "vm" { + name = "my-vm" + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + size = "Standard_B2s" + admin_username = "adminuser" + + admin_ssh_key { + username = "adminuser" + public_key = file("~/.ssh/id_rsa.pub") + } + + network_interface_ids = [azurerm_network_interface.nic.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" + version = "latest" + } +} +``` + +**AKS CLUSTER:** +```hcl +resource "azurerm_kubernetes_cluster" "aks" { + name = "my-aks" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + dns_prefix = "myaks" + + default_node_pool { + name = "default" + node_count = 3 + vm_size = "Standard_B2s" + } + + identity { + type = "SystemAssigned" + } +} +``` + +**STORAGE ACCOUNT:** +```hcl +resource "azurerm_storage_account" "storage" { + name = "mystorageaccount" + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + account_tier = "Standard" + account_replication_type = "LRS" +} +``` + +**Common Azure Terraform resources:** +- `azurerm_resource_group` — Resource groups (required for everything) +- `azurerm_linux_virtual_machine` — Linux VMs +- `azurerm_network_interface`, `azurerm_virtual_network`, `azurerm_subnet` — Networking +- `azurerm_network_security_group` — Firewall rules +- `azurerm_kubernetes_cluster` — AKS +- `azurerm_storage_account`, `azurerm_storage_container` — Blob storage +- `azurerm_mssql_server`, `azurerm_mssql_database` — SQL databases +- `azurerm_service_plan`, `azurerm_linux_web_app` — App Service +- `azurerm_lb` — Load balancers + +DO NOT write terraform{} or provider{} blocks — they are auto-generated! + +### CRITICAL RULES +- **ALWAYS** specify `--resource-group` for Azure operations +- Check existing resource groups before creating new ones +- Get subscription ID before writing Terraform +- Use `--output table` for readable CLI output +- AKS: always run `get-credentials` before kubectl commands +- VM passwords are auto-generated by Terraform — never ask the user +- Default location: eastus unless user specifies otherwise +- Resource names in Azure must often be globally unique (storage accounts, web apps) + +### ON ANY AZURE ERROR +1. Provider not registered → `cloud_exec('azure', 'provider register --namespace Microsoft.')` +2. Permission denied → Check role assignments: `cloud_exec('azure', 'role assignment list --assignee ')` +3. Resource group missing → Create one: `cloud_exec('azure', 'group create --name --location ')` +4. CLI syntax error → `cloud_exec('azure', ' --help')` +5. Terraform failure → Verify resources with CLI, then fix the manifest diff --git a/server/chat/backend/agent/skills/integrations/gcp/SKILL.md b/server/chat/backend/agent/skills/integrations/gcp/SKILL.md new file mode 100644 index 000000000..00b179c69 --- /dev/null +++ b/server/chat/backend/agent/skills/integrations/gcp/SKILL.md @@ -0,0 +1,184 @@ +--- +name: gcp +id: gcp +description: "Google Cloud Platform integration for managing Compute Engine, GKE, Cloud SQL, Cloud Storage, Cloud Run, and other services via CLI and Terraform" +category: cloud_provider +connection_check: + method: provider_in_preference +tools: + - cloud_exec + - iac_tool +index: "GCP — Compute Engine, GKE, Cloud SQL, Cloud Storage, Cloud Run, Terraform IaC" +rca_priority: 10 +allowed-tools: cloud_exec, iac_tool +metadata: + author: aurora + version: "1.0" +--- + +# Google Cloud Platform Integration + +## Overview +GCP cloud provider for managing compute, Kubernetes, databases, storage, serverless, networking, and monitoring. + +## Instructions + +### CLI COMMANDS (use cloud_exec with 'gcp') + +**CRITICAL: Always use cloud_exec('gcp', 'COMMAND') — NOT terminal_exec!** +Authentication and project setup are auto-configured. The `gcloud` CLI is available. +Additional CLIs: `gsutil`, `bq`, `kubectl`. + +**PROJECT SETUP:** +- Get current project: `cloud_exec('gcp', 'config get-value project')` +- Set project: `cloud_exec('gcp', 'config set project ')` +- If the user specifies a project, set it. Otherwise fetch the current one and reuse it. + +**Discovery Commands:** +- List projects: `cloud_exec('gcp', 'projects list')` +- List regions: `cloud_exec('gcp', 'compute regions list')` +- List zones: `cloud_exec('gcp', 'compute zones list --filter="region:(us-central1)"')` +- List services: `cloud_exec('gcp', 'services list --enabled')` +- Enable service: `cloud_exec('gcp', 'services enable ')` + +**Compute Engine:** +- List instances: `cloud_exec('gcp', 'compute instances list')` +- Create instance: `cloud_exec('gcp', 'compute instances create --zone= --machine-type=e2-medium --image-family=debian-12 --image-project=debian-cloud')` +- Start/stop: `cloud_exec('gcp', 'compute instances start|stop --zone=')` +- Describe: `cloud_exec('gcp', 'compute instances describe --zone=')` +- SSH: `cloud_exec('gcp', 'compute ssh --zone= --command=""')` + +**GKE (Kubernetes):** +- List clusters: `cloud_exec('gcp', 'container clusters list')` +- Describe cluster: `cloud_exec('gcp', 'container clusters describe --region=')` +- Get credentials: `cloud_exec('gcp', 'container clusters get-credentials --region=')` +- Then kubectl: `cloud_exec('gcp', 'kubectl get pods -n -o wide')` +- Node pools: `cloud_exec('gcp', 'container node-pools list --cluster= --region=')` + +**Cloud Storage (gsutil):** +- List buckets: `cloud_exec('gcp', 'gsutil ls')` +- List objects: `cloud_exec('gcp', 'gsutil ls gs:///')` +- Copy: `cloud_exec('gcp', 'gsutil cp ')` +- Bucket info: `cloud_exec('gcp', 'gsutil du -s gs://')` + +**Cloud SQL (Databases):** +- List instances: `cloud_exec('gcp', 'sql instances list')` +- Describe: `cloud_exec('gcp', 'sql instances describe ')` +- List databases: `cloud_exec('gcp', 'sql databases list --instance=')` + +**Cloud Run (Serverless):** +- List services: `cloud_exec('gcp', 'run services list --region=')` +- Describe: `cloud_exec('gcp', 'run services describe --region=')` +- Deploy: `cloud_exec('gcp', 'run deploy --image= --region= --allow-unauthenticated')` +- View logs: `cloud_exec('gcp', 'run services logs read --region= --limit=50')` + +**Cloud Logging & Monitoring:** +- Read logs: `cloud_exec('gcp', 'logging read "resource.type=k8s_container AND severity>=ERROR" --limit=50 --freshness=1h')` +- Specific resource: `cloud_exec('gcp', 'logging read "resource.type=gce_instance AND resource.labels.instance_id=" --limit=50')` +- List metrics: `cloud_exec('gcp', 'monitoring dashboards list')` +- Alert policies: `cloud_exec('gcp', 'alpha monitoring policies list')` + +**IAM:** +- List bindings: `cloud_exec('gcp', 'projects get-iam-policy --flatten="bindings[].members" --format="table(bindings.role,bindings.members)"')` +- Service accounts: `cloud_exec('gcp', 'iam service-accounts list')` + +**Networking:** +- List VPCs: `cloud_exec('gcp', 'compute networks list')` +- List subnets: `cloud_exec('gcp', 'compute networks subnets list')` +- Firewall rules: `cloud_exec('gcp', 'compute firewall-rules list')` +- Load balancers: `cloud_exec('gcp', 'compute forwarding-rules list')` + +**BigQuery (bq):** +- List datasets: `cloud_exec('gcp', 'bq ls')` +- Query: `cloud_exec('gcp', 'bq query --use_legacy_sql=false "SELECT * FROM dataset.table LIMIT 10"')` + +### TERRAFORM FOR GCP +Use iac_tool — provider.tf is AUTO-GENERATED, just write the resource! + +**PREREQUISITE:** Get project ID first: +`cloud_exec('gcp', 'config get-value project')` + +**COMPUTE INSTANCE EXAMPLE:** +```hcl +resource "google_compute_instance" "vm" { + name = "my-vm" + machine_type = "e2-medium" + zone = "us-central1-b" + + boot_disk { + initialize_params { + image = "debian-cloud/debian-12" + } + } + + network_interface { + network = "default" + access_config {} + } +} +``` + +**GKE CLUSTER:** +```hcl +resource "google_container_cluster" "cluster" { + name = "my-cluster" + location = "us-central1" + + initial_node_count = 3 + + node_config { + machine_type = "e2-medium" + } +} +``` + +**CLOUD SQL:** +```hcl +resource "google_sql_database_instance" "db" { + name = "my-db" + database_version = "POSTGRES_15" + region = "us-central1" + + settings { + tier = "db-f1-micro" + } + + deletion_protection = false +} +``` + +**CLOUD STORAGE BUCKET:** +```hcl +resource "google_storage_bucket" "bucket" { + name = "my-bucket-unique-name" + location = "US" +} +``` + +**Common GCP Terraform resources:** +- `google_compute_instance` — Virtual machines +- `google_compute_firewall` — Firewall rules +- `google_container_cluster`, `google_container_node_pool` — GKE +- `google_storage_bucket` — Cloud Storage +- `google_sql_database_instance` — Cloud SQL +- `google_cloud_run_v2_service` — Cloud Run +- `google_compute_network`, `google_compute_subnetwork` — VPC +- `google_compute_global_forwarding_rule` — Load balancers +- `google_project_iam_member` — IAM bindings + +DO NOT write terraform{} or provider{} blocks — they are auto-generated! + +### CRITICAL RULES +- Always get the project ID before writing Terraform or running commands +- Use `--region` for regional resources, `--zone` for zonal resources +- GKE: always run `get-credentials` before kubectl commands +- Enable required APIs before creating resources: `services enable ` +- Default zone: us-central1-b unless user specifies otherwise +- For beta features, use `cloud_exec('gcp', 'beta ')` + +### ON ANY GCP ERROR +1. API not enabled → `cloud_exec('gcp', 'services enable .googleapis.com')` +2. Permission denied → Check IAM: `cloud_exec('gcp', 'projects get-iam-policy ')` +3. CLI syntax error → `cloud_exec('gcp', ' --help')` +4. Try beta: `cloud_exec('gcp', 'beta --help')` +5. Terraform failure → Verify resources with CLI, then fix the manifest From 7ea1b6c14b9289e76f9eb8f722540576edfc140a Mon Sep 17 00:00:00 2001 From: Benjamin Gervais Date: Mon, 20 Apr 2026 15:33:59 -0400 Subject: [PATCH 2/4] feat: deep cloud provider skills + trim cloud_access core prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite AWS, GCP, and Azure SKILL.md files from surface-level cheat sheets into authoritative references the agent actually needs: - Full CLI reference per service (EC2/EKS/RDS/S3/Lambda/CloudWatch, Compute/GKE/SQL/Storage/Run/Logging, VMs/AKS/SQL/Storage/AppService) - CloudWatch Insights query patterns, Cloud Logging filter syntax, KQL Log Analytics queries — real investigation tooling - Complete Terraform examples sourced from Context7 provider docs (aws_eks_cluster with IAM, google_container_cluster with node pools, azurerm_kubernetes_cluster with identity) - RCA investigation workflows per provider (10+ step playbooks) - Error recovery with Context7 library IDs for each provider - Region mapping per provider - Provider-specific gotchas (AWS multi-account, Azure resource groups, GCP API enablement) Trim cloud_access.md from 73 lines to 10 — just the cloud_exec signature and "load the skill for details". The skills are now the authoritative source; the core prompt just explains the tool exists. Co-Authored-By: Claude Opus 4.6 --- .../backend/agent/skills/core/cloud_access.md | 82 +-- .../agent/skills/integrations/aws/SKILL.md | 468 +++++++++++++----- .../agent/skills/integrations/azure/SKILL.md | 448 +++++++++++++---- .../agent/skills/integrations/gcp/SKILL.md | 441 ++++++++++++----- 4 files changed, 1028 insertions(+), 411 deletions(-) diff --git a/server/chat/backend/agent/skills/core/cloud_access.md b/server/chat/backend/agent/skills/core/cloud_access.md index ab022526a..017a50ce8 100644 --- a/server/chat/backend/agent/skills/core/cloud_access.md +++ b/server/chat/backend/agent/skills/core/cloud_access.md @@ -1,72 +1,10 @@ -UNIVERSAL CLOUD ACCESS: -cloud_exec(provider, 'COMMAND') gives you COMPLETE access to cloud platforms: -- GCP: cloud_exec('gcp', 'ANY_GCLOUD_COMMAND') - Full Google Cloud access -- Azure: cloud_exec('azure', 'ANY_AZ_COMMAND') - Full Microsoft Azure access -- AWS: cloud_exec('aws', 'ANY_AWS_COMMAND') - Full Amazon Web Services access -- OVH: cloud_exec('ovh', 'ANY_OVHCLOUD_COMMAND') - Full OVHcloud access -- Scaleway: cloud_exec('scaleway', 'ANY_SCW_COMMAND') - Full Scaleway access -- Authentication and project/subscription setup handled automatically -- NEVER give manual console instructions when a CLI command exists - -AZURE RESOURCE GROUP REQUIREMENTS: -When working with Azure, resources MUST be created within a resource group. Before creating any Azure resources: -1. ALWAYS check for existing resource groups first: cloud_exec('azure', 'group list') -2. If suitable resource groups exist, use one of them for your resources -3. If no suitable resource group exists, create a new one: cloud_exec('azure', 'group create --name --location ') -4. Then proceed with resource creation, always specifying the resource group -- This applies to ALL Azure resources: VMs, storage accounts, networks, databases, etc. - -CAPABILITY DISCOVERY: -When facing ANY cloud management task you're unsure about: - -For GCP: -1. EXPLORE the gcloud CLI: cloud_exec('gcp', 'help | grep KEYWORD') -2. Get command help: cloud_exec('gcp', 'CATEGORY --help') -3. Try beta commands: cloud_exec('gcp', 'beta CATEGORY --help') -4. List services: cloud_exec('gcp', 'services list --available') - -For Azure: -1. EXPLORE the az CLI: cloud_exec('azure', 'help | grep KEYWORD') -2. Get command help: cloud_exec('azure', 'CATEGORY --help') -3. List services: cloud_exec('azure', 'provider list') -4. Find resources: cloud_exec('azure', 'resource list') - -For OVH (CRITICAL - follow this EXACT workflow for instance creation): -1. **Get project ID**: cloud_exec('ovh', 'cloud project list --json') -2. **Get ACTUAL regions** (DO NOT assume - US/EU accounts have different regions!): - cloud_exec('ovh', 'cloud region list --cloud-project --json') -3. **Get flavors for region**: cloud_exec('ovh', 'cloud reference list-flavors --cloud-project --region --json') -4. **Get images**: cloud_exec('ovh', 'cloud reference list-images --cloud-project --region --json') -5. **Create instance WITH inline SSH key** (REQUIRED - use this exact syntax): - cloud_exec('ovh', 'cloud instance create --name --boot-from.image --flavor --network.public --ssh-key.create.name --ssh-key.create.public-key "" --cloud-project --wait --json') -KEY RULES: --cloud-project (NOT --project-id), region is POSITIONAL, --network.public (NEVER --network ) - -For Scaleway: -1. **ALWAYS use cloud_exec('scaleway', ...)** - NOT terminal_exec! (credentials are auto-configured) -2. List instances: cloud_exec('scaleway', 'instance server list') -3. Get help: cloud_exec('scaleway', 'instance server create --help') -4. Create instance: cloud_exec('scaleway', 'instance server create type=DEV1-S image=ubuntu_jammy name=my-vm') -5. Scaleway uses key=value syntax, NOT --key value - -All CLIs can do EVERYTHING - quotas, billing, IAM, networking, storage, compute, etc. -Your job is to DISCOVER and USE the right commands, not give manual instructions. - -The system uses service account/service principal authentication automatically - no manual auth needed. - -IMPORTANT VM CREATION RULES: -- Azure VMs: The system automatically generates strong admin passwords using Terraform's random_password resource. You do NOT need to ask users for passwords or SSH keys. -- When deploying Azure VMs, proceed directly with deployment - authentication is handled automatically. - -IMPORTANT: When writing custom Terraform code: -- DO NOT just add comments saying to adjust regions -- ACTUALLY USE the correct zone in your code -- The zone in your terraform MUST match the user's geographic requirements - -REGION MAPPING (use when user specifies a geography): -- Canada: GCP northamerica-northeast1-a / northamerica-northeast2-a, AWS ca-central-1, Azure canadacentral -- Belgium/EU: GCP europe-west1-a, AWS eu-west-1, Azure westeurope -- London/UK: GCP europe-west2-a, AWS eu-west-2, Azure uksouth -- Singapore/SEA: GCP asia-southeast1-a, AWS ap-southeast-1, Azure southeastasia -- Tokyo/Japan: GCP asia-northeast1-a, AWS ap-northeast-1, Azure japaneast -- US (default): GCP us-central1-b, AWS us-east-1, Azure eastus -- If user says 'NOT US', prefer Canada (northamerica-northeast1-a / ca-central-1) +CLOUD ACCESS: +cloud_exec(provider, 'COMMAND') gives you full access to cloud platforms: +- GCP: cloud_exec('gcp', 'gcloud/gsutil/bq/kubectl COMMAND') +- AWS: cloud_exec('aws', 'aws/kubectl/eksctl COMMAND') +- Azure: cloud_exec('azure', 'az/kubectl COMMAND') +- OVH: cloud_exec('ovh', 'ovhcloud COMMAND') +- Scaleway: cloud_exec('scaleway', 'scw COMMAND') + +Authentication is automatic — never ask users for credentials or give manual console instructions. +For detailed CLI references, Terraform examples, and investigation workflows, call load_skill with the provider name (e.g., load_skill('aws')). diff --git a/server/chat/backend/agent/skills/integrations/aws/SKILL.md b/server/chat/backend/agent/skills/integrations/aws/SKILL.md index 0e29fa44a..6502543a1 100644 --- a/server/chat/backend/agent/skills/integrations/aws/SKILL.md +++ b/server/chat/backend/agent/skills/integrations/aws/SKILL.md @@ -1,97 +1,236 @@ --- name: aws id: aws -description: "AWS integration for managing EC2, RDS, S3, Lambda, EKS, CloudWatch, IAM, and other services via CLI and Terraform" +description: "AWS integration — EC2, EKS, RDS, S3, Lambda, CloudWatch, IAM, VPC, ELB via CLI and Terraform" category: cloud_provider connection_check: method: provider_in_preference tools: - cloud_exec - iac_tool -index: "AWS — EC2, RDS, S3, Lambda, EKS, CloudWatch, IAM, Terraform IaC" +index: "AWS — EC2, EKS, RDS, S3, Lambda, CloudWatch, IAM, VPC, Terraform IaC" rca_priority: 10 allowed-tools: cloud_exec, iac_tool metadata: author: aurora - version: "1.0" + version: "2.0" --- # AWS Integration ## Overview -Amazon Web Services cloud provider for managing compute, storage, databases, containers, serverless, networking, and observability. - -## Instructions - -### CLI COMMANDS (use cloud_exec with 'aws') - -**CRITICAL: Always use cloud_exec('aws', 'COMMAND') — NOT terminal_exec!** -Authentication and credentials are auto-configured. The `aws` CLI is available. -Additional CLIs: `kubectl`, `eksctl`, `sam`, `cdk`, `helm`, `terraform`. - -**MULTI-ACCOUNT SUPPORT:** -- First call without `account_id` fans out to ALL connected accounts and returns `results_by_account`. -- Identify the relevant account from the results. -- ALL subsequent calls MUST include `account_id=''` to target that account. -- Never keep querying all accounts after you know which one matters. - -**Discovery Commands:** -- Caller identity: `cloud_exec('aws', 'sts get-caller-identity')` -- List regions: `cloud_exec('aws', 'ec2 describe-regions --output table', account_id='')` -- List services: `cloud_exec('aws', 'service-quotas list-services', account_id='')` - -**EC2 (Compute):** -- List instances: `cloud_exec('aws', 'ec2 describe-instances --query "Reservations[].Instances[].{ID:InstanceId,Type:InstanceType,State:State.Name,Name:Tags[?Key==`Name`].Value|[0]}" --output table', account_id='')` -- Start/stop: `cloud_exec('aws', 'ec2 start-instances --instance-ids ', account_id='')` -- Describe: `cloud_exec('aws', 'ec2 describe-instances --instance-ids ', account_id='')` -- Security groups: `cloud_exec('aws', 'ec2 describe-security-groups --group-ids ', account_id='')` - -**EKS (Kubernetes):** -- List clusters: `cloud_exec('aws', 'eks list-clusters', account_id='')` -- Describe cluster: `cloud_exec('aws', 'eks describe-cluster --name ', account_id='')` -- Get kubeconfig: `cloud_exec('aws', 'eks update-kubeconfig --name --region ', account_id='')` -- Then kubectl: `cloud_exec('aws', 'kubectl get pods -n -o wide', account_id='')` - -**S3 (Storage):** -- List buckets: `cloud_exec('aws', 's3 ls', account_id='')` -- List objects: `cloud_exec('aws', 's3 ls s3:///', account_id='')` -- Copy: `cloud_exec('aws', 's3 cp ', account_id='')` - -**RDS (Databases):** -- List instances: `cloud_exec('aws', 'rds describe-db-instances --query "DBInstances[].{ID:DBInstanceIdentifier,Engine:Engine,Status:DBInstanceStatus}" --output table', account_id='')` -- Describe: `cloud_exec('aws', 'rds describe-db-instances --db-instance-identifier ', account_id='')` - -**Lambda (Serverless):** -- List functions: `cloud_exec('aws', 'lambda list-functions --query "Functions[].{Name:FunctionName,Runtime:Runtime}" --output table', account_id='')` -- Invoke: `cloud_exec('aws', 'lambda invoke --function-name /dev/stdout', account_id='')` -- Get logs: `cloud_exec('aws', 'logs filter-log-events --log-group-name /aws/lambda/ --start-time --limit 50', account_id='')` - -**CloudWatch (Monitoring):** -- Query logs: `cloud_exec('aws', 'logs filter-log-events --log-group-name --start-time --filter-pattern ""', account_id='')` -- List log groups: `cloud_exec('aws', 'logs describe-log-groups', account_id='')` -- Get metrics: `cloud_exec('aws', 'cloudwatch get-metric-statistics --namespace --metric-name --dimensions Name=,Value= --start-time --end-time --period 300 --statistics Average', account_id='')` -- List alarms: `cloud_exec('aws', 'cloudwatch describe-alarms --state-value ALARM', account_id='')` - -**IAM:** -- List roles: `cloud_exec('aws', 'iam list-roles --query "Roles[].{Name:RoleName,Arn:Arn}" --output table', account_id='')` -- Get role policy: `cloud_exec('aws', 'iam list-attached-role-policies --role-name ', account_id='')` - -**Networking:** -- List VPCs: `cloud_exec('aws', 'ec2 describe-vpcs --output table', account_id='')` -- List subnets: `cloud_exec('aws', 'ec2 describe-subnets --filters "Name=vpc-id,Values=" --output table', account_id='')` -- List load balancers: `cloud_exec('aws', 'elbv2 describe-load-balancers --output table', account_id='')` -- Route tables: `cloud_exec('aws', 'ec2 describe-route-tables --filters "Name=vpc-id,Values="', account_id='')` - -### TERRAFORM FOR AWS -Use iac_tool — provider.tf is AUTO-GENERATED, just write the resource! - -**PREREQUISITE:** Get account ID first: -`cloud_exec('aws', "sts get-caller-identity --query 'Account' --output text", account_id='')` - -**EC2 INSTANCE EXAMPLE:** +Full Amazon Web Services access via `cloud_exec('aws', 'COMMAND')`. +Available CLIs: `aws`, `kubectl`, `eksctl`, `sam`, `cdk`, `helm`, `terraform`. +Authentication is automatic — never ask users for credentials. + +## Multi-Account Support (CRITICAL) +- First `cloud_exec('aws', ...)` call (without `account_id`) fans out to ALL connected accounts and returns `results_by_account`. +- Inspect results to identify which account(s) are relevant. +- ALL subsequent calls MUST include `account_id=''`. +- NEVER keep querying all accounts after you've identified the right one. + +```python +# Step 1: fan-out discovery +cloud_exec('aws', 'ec2 describe-instances --query "Reservations[].Instances[].{ID:InstanceId,State:State.Name}" --output json') +# Step 2: target specific account +cloud_exec('aws', 'ec2 describe-instances --instance-ids i-abc123', account_id='123456789012') +``` + +## CLI Reference + +### Identity & Discovery +```python +cloud_exec('aws', 'sts get-caller-identity', account_id='') +cloud_exec('aws', 'ec2 describe-regions --output table', account_id='') +cloud_exec('aws', 'organizations describe-account --account-id ', account_id='') +``` + +### EC2 (Compute) +```python +cloud_exec('aws', 'ec2 describe-instances --query "Reservations[].Instances[].{ID:InstanceId,Type:InstanceType,State:State.Name,Name:Tags[?Key==`Name`].Value|[0],AZ:Placement.AvailabilityZone}" --output table', account_id='') +cloud_exec('aws', 'ec2 describe-instances --instance-ids --output json', account_id='') +cloud_exec('aws', 'ec2 start-instances --instance-ids ', account_id='') +cloud_exec('aws', 'ec2 stop-instances --instance-ids ', account_id='') +cloud_exec('aws', 'ec2 terminate-instances --instance-ids ', account_id='') +cloud_exec('aws', 'ec2 describe-instance-status --instance-ids ', account_id='') +# Filter by tag: +cloud_exec('aws', 'ec2 describe-instances --filters "Name=tag:Environment,Values=production" --output table', account_id='') +# Filter by state: +cloud_exec('aws', 'ec2 describe-instances --filters "Name=instance-state-name,Values=running" --output table', account_id='') +``` + +### EKS (Kubernetes) +```python +cloud_exec('aws', 'eks list-clusters', account_id='') +cloud_exec('aws', 'eks describe-cluster --name ', account_id='') +# MANDATORY before any kubectl: get kubeconfig +cloud_exec('aws', 'eks update-kubeconfig --name --region ', account_id='') +# Then kubectl works: +cloud_exec('aws', 'kubectl get pods -n -o wide', account_id='') +cloud_exec('aws', 'kubectl describe pod -n ', account_id='') +cloud_exec('aws', 'kubectl logs -n --since=1h --tail=200', account_id='') +cloud_exec('aws', 'kubectl get events -n --sort-by=.lastTimestamp', account_id='') +cloud_exec('aws', 'kubectl top pods -n ', account_id='') +cloud_exec('aws', 'kubectl get hpa -n ', account_id='') +cloud_exec('aws', 'kubectl rollout history deployment/ -n ', account_id='') +# Node pool info: +cloud_exec('aws', 'eks list-nodegroups --cluster-name ', account_id='') +cloud_exec('aws', 'eks describe-nodegroup --cluster-name --nodegroup-name ', account_id='') +# Enable control plane logging: +cloud_exec('aws', 'eks update-cluster-config --name --logging \'{"clusterLogging": [{"types": ["api", "audit", "scheduler"], "enabled": true}]}\'', account_id='') +``` + +### S3 (Storage) +```python +cloud_exec('aws', 's3 ls', account_id='') +cloud_exec('aws', 's3 ls s3:/// --recursive --summarize', account_id='') +cloud_exec('aws', 's3 cp s3:///', account_id='') +cloud_exec('aws', 's3 rm s3:///', account_id='') +cloud_exec('aws', 's3api get-bucket-policy --bucket ', account_id='') +cloud_exec('aws', 's3api get-bucket-versioning --bucket ', account_id='') +``` + +### RDS (Databases) +```python +cloud_exec('aws', 'rds describe-db-instances --query "DBInstances[].{ID:DBInstanceIdentifier,Engine:Engine,Version:EngineVersion,Status:DBInstanceStatus,Class:DBInstanceClass,Storage:AllocatedStorage}" --output table', account_id='') +cloud_exec('aws', 'rds describe-db-instances --db-instance-identifier ', account_id='') +cloud_exec('aws', 'rds describe-db-clusters --output table', account_id='') +cloud_exec('aws', 'rds describe-events --source-identifier --source-type db-instance --duration 1440', account_id='') +# Performance Insights: +cloud_exec('aws', 'pi get-resource-metrics --service-type RDS --identifier db- --metric-queries "[{\"Metric\":\"db.load.avg\"}]" --start-time --end-time --period-in-seconds 300', account_id='') +``` + +### Lambda (Serverless) +```python +cloud_exec('aws', 'lambda list-functions --query "Functions[].{Name:FunctionName,Runtime:Runtime,Memory:MemorySize,Timeout:Timeout}" --output table', account_id='') +cloud_exec('aws', 'lambda get-function --function-name ', account_id='') +cloud_exec('aws', 'lambda invoke --function-name --payload \'{"key":"value"}\' /dev/stdout', account_id='') +cloud_exec('aws', 'lambda get-function-configuration --function-name ', account_id='') +cloud_exec('aws', 'lambda list-event-source-mappings --function-name ', account_id='') +``` + +### CloudWatch Logs +```python +# List log groups: +cloud_exec('aws', 'logs describe-log-groups --query "logGroups[].{Name:logGroupName,Stored:storedBytes}" --output table', account_id='') +# Filter log events (simple): +cloud_exec('aws', 'logs filter-log-events --log-group-name --start-time --filter-pattern "ERROR" --limit 50', account_id='') +# Tail recent logs: +cloud_exec('aws', 'logs tail --since 1h --format short', account_id='') +``` + +### CloudWatch Logs Insights (PREFERRED for complex queries) +Use `start-query` + `get-query-results` for powerful log analysis: +```python +# Start an Insights query (returns queryId): +cloud_exec('aws', 'logs start-query --log-group-name --start-time --end-time --query-string "fields @timestamp, @message | filter @message like /ERROR/ | sort @timestamp desc | limit 50"', account_id='') +# Then fetch results (may need to wait a few seconds): +cloud_exec('aws', 'logs get-query-results --query-id ', account_id='') +``` +Common Insights query patterns: +- Error frequency: `stats count(*) by bin(5m) | filter @message like /ERROR/` +- Top error messages: `filter @message like /ERROR/ | stats count(*) as cnt by @message | sort cnt desc | limit 20` +- Latency percentiles: `filter @type = "REPORT" | stats avg(@duration), pct(@duration, 95), max(@duration) by bin(5m)` +- Lambda cold starts: `filter @type = "REPORT" | filter @initDuration > 0 | stats count(*) as coldStarts by bin(10m)` + +### CloudWatch Metrics & Alarms +```python +cloud_exec('aws', 'cloudwatch describe-alarms --state-value ALARM --output table', account_id='') +cloud_exec('aws', 'cloudwatch get-metric-statistics --namespace AWS/EC2 --metric-name CPUUtilization --dimensions Name=InstanceId,Value= --start-time --end-time --period 300 --statistics Average Maximum', account_id='') +cloud_exec('aws', 'cloudwatch list-metrics --namespace AWS/RDS --metric-name FreeableMemory', account_id='') +# Common namespaces: AWS/EC2, AWS/RDS, AWS/ELB, AWS/Lambda, AWS/EKS, AWS/S3, AWS/SQS, AWS/SNS +``` + +### IAM +```python +cloud_exec('aws', 'iam list-roles --query "Roles[].{Name:RoleName,Arn:Arn}" --output table', account_id='') +cloud_exec('aws', 'iam get-role --role-name ', account_id='') +cloud_exec('aws', 'iam list-attached-role-policies --role-name ', account_id='') +cloud_exec('aws', 'iam get-policy --policy-arn ', account_id='') +cloud_exec('aws', 'iam simulate-principal-policy --policy-source-arn --action-names s3:GetObject --resource-arns "arn:aws:s3:::bucket/*"', account_id='') +``` + +### Networking (VPC) +```python +cloud_exec('aws', 'ec2 describe-vpcs --query "Vpcs[].{ID:VpcId,CIDR:CidrBlock,Name:Tags[?Key==`Name`].Value|[0]}" --output table', account_id='') +cloud_exec('aws', 'ec2 describe-subnets --filters "Name=vpc-id,Values=" --query "Subnets[].{ID:SubnetId,AZ:AvailabilityZone,CIDR:CidrBlock}" --output table', account_id='') +cloud_exec('aws', 'ec2 describe-security-groups --group-ids --output json', account_id='') +cloud_exec('aws', 'ec2 describe-route-tables --filters "Name=vpc-id,Values="', account_id='') +cloud_exec('aws', 'ec2 describe-nat-gateways --filter "Name=vpc-id,Values="', account_id='') +``` + +### Load Balancers +```python +cloud_exec('aws', 'elbv2 describe-load-balancers --query "LoadBalancers[].{Name:LoadBalancerName,DNS:DNSName,State:State.Code,Type:Type}" --output table', account_id='') +cloud_exec('aws', 'elbv2 describe-target-groups --load-balancer-arn ', account_id='') +cloud_exec('aws', 'elbv2 describe-target-health --target-group-arn ', account_id='') +cloud_exec('aws', 'elbv2 describe-listeners --load-balancer-arn ', account_id='') +``` + +### Other Services +```python +# SQS: +cloud_exec('aws', 'sqs list-queues', account_id='') +cloud_exec('aws', 'sqs get-queue-attributes --queue-url --attribute-names All', account_id='') +# SNS: +cloud_exec('aws', 'sns list-topics', account_id='') +# Route 53: +cloud_exec('aws', 'route53 list-hosted-zones', account_id='') +cloud_exec('aws', 'route53 list-resource-record-sets --hosted-zone-id ', account_id='') +# CloudFormation: +cloud_exec('aws', 'cloudformation list-stacks --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE', account_id='') +cloud_exec('aws', 'cloudformation describe-stack-events --stack-name --max-items 20', account_id='') +# ECR: +cloud_exec('aws', 'ecr describe-repositories', account_id='') +cloud_exec('aws', 'ecr describe-images --repository-name --query "imageDetails[].{Tags:imageTags,Pushed:imagePushedAt,Size:imageSizeInBytes}" --output table', account_id='') +# ECS: +cloud_exec('aws', 'ecs list-clusters', account_id='') +cloud_exec('aws', 'ecs describe-services --cluster --services ', account_id='') +cloud_exec('aws', 'ecs list-tasks --cluster --service-name ', account_id='') +``` + +## RCA / Investigation Workflow + +When investigating an AWS incident: + +1. **Identify the account**: First fan-out call to find which account has the affected resources +2. **Get cluster credentials** (if EKS): `eks update-kubeconfig --name --region ` +3. **Check resource state**: `ec2 describe-instances`, `eks describe-cluster`, `rds describe-db-instances` +4. **Check pods/containers** (if K8s): `kubectl get pods -o wide`, `kubectl describe pod`, `kubectl logs` +5. **Check events**: `kubectl get events --sort-by=.lastTimestamp`, `rds describe-events` +6. **Check logs**: CloudWatch `filter-log-events` or Insights `start-query` for patterns +7. **Check metrics**: `cloudwatch get-metric-statistics` for CPU, memory, disk, network +8. **Check alarms**: `cloudwatch describe-alarms --state-value ALARM` +9. **Check recent deployments**: `kubectl rollout history`, `cloudformation describe-stack-events` +10. **Check networking**: Security groups, NACLs, route tables, target health +11. **Compare healthy vs unhealthy**: `kubectl top pods`, instance metrics side-by-side + +## Terraform + +Use `iac_tool` — provider.tf is AUTO-GENERATED. Never write terraform{} or provider{} blocks. + +**PREREQUISITE:** Always get the account ID first: +```python +cloud_exec('aws', "sts get-caller-identity --query 'Account' --output text", account_id='') +``` + +### EC2 Instance (with AMI data source — CORRECT approach) ```hcl +data "aws_ami" "ubuntu" { + most_recent = true + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] + } + filter { + name = "virtualization-type" + values = ["hvm"] + } + owners = ["099720109477"] # Canonical +} + resource "aws_instance" "vm" { - ami = "ami-0c55b159cbfafe1f0" + ami = data.aws_ami.ubuntu.id instance_type = "t3.micro" subnet_id = "" @@ -100,63 +239,162 @@ resource "aws_instance" "vm" { } } ``` +AMI IDs are region-specific — always use `data "aws_ami"` or look up via CLI. -**EKS CLUSTER:** +### EKS Cluster (with IAM role — required) ```hcl +resource "aws_iam_role" "eks_cluster" { + name = "eks-cluster-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Action = ["sts:AssumeRole", "sts:TagSession"] + Effect = "Allow" + Principal = { Service = "eks.amazonaws.com" } + }] + }) +} + +resource "aws_iam_role_policy_attachment" "eks_policy" { + policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" + role = aws_iam_role.eks_cluster.name +} + resource "aws_eks_cluster" "cluster" { name = "my-cluster" - role_arn = "" + role_arn = aws_iam_role.eks_cluster.arn + version = "1.31" vpc_config { - subnet_ids = ["", ""] + subnet_ids = [aws_subnet.az1.id, aws_subnet.az2.id] + } + + depends_on = [aws_iam_role_policy_attachment.eks_policy] +} + +resource "aws_eks_node_group" "nodes" { + cluster_name = aws_eks_cluster.cluster.name + node_group_name = "workers" + node_role_arn = aws_iam_role.node.arn + subnet_ids = [aws_subnet.az1.id, aws_subnet.az2.id] + + scaling_config { + desired_size = 2 + max_size = 4 + min_size = 1 } + + depends_on = [ + aws_iam_role_policy_attachment.node_AmazonEKSWorkerNodePolicy, + aws_iam_role_policy_attachment.node_AmazonEKS_CNI_Policy, + aws_iam_role_policy_attachment.node_AmazonEC2ContainerRegistryReadOnly, + ] +} +``` + +### RDS (PostgreSQL) +```hcl +resource "aws_db_instance" "db" { + identifier = "my-db" + allocated_storage = 20 + engine = "postgres" + engine_version = "15" + instance_class = "db.t3.micro" + db_name = "mydb" + username = "admin" + manage_master_user_password = true + skip_final_snapshot = true + vpc_security_group_ids = [aws_security_group.db.id] + db_subnet_group_name = aws_db_subnet_group.main.name +} +``` + +### VPC + Subnets +```hcl +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" + enable_dns_hostnames = true + tags = { Name = "main-vpc" } +} + +resource "aws_subnet" "az1" { + vpc_id = aws_vpc.main.id + cidr_block = "10.0.1.0/24" + availability_zone = "us-east-1a" +} + +resource "aws_subnet" "az2" { + vpc_id = aws_vpc.main.id + cidr_block = "10.0.2.0/24" + availability_zone = "us-east-1b" +} + +resource "aws_internet_gateway" "gw" { + vpc_id = aws_vpc.main.id } ``` -**S3 BUCKET:** +### S3 Bucket ```hcl resource "aws_s3_bucket" "bucket" { - bucket = "my-bucket-unique-name" + bucket = "my-unique-bucket-name" +} + +resource "aws_s3_bucket_versioning" "versioning" { + bucket = aws_s3_bucket.bucket.id + versioning_configuration { + status = "Enabled" + } } ``` -**RDS INSTANCE:** +### Security Group ```hcl -resource "aws_db_instance" "db" { - allocated_storage = 20 - engine = "postgres" - engine_version = "15" - instance_class = "db.t3.micro" - db_name = "mydb" - username = "admin" - manage_master_user_password = true - skip_final_snapshot = true +resource "aws_security_group" "web" { + name_prefix = "web-" + vpc_id = aws_vpc.main.id + + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } } ``` -**Common AWS Terraform resources:** -- `aws_instance` — EC2 virtual machines -- `aws_security_group` — Firewall rules -- `aws_eks_cluster`, `aws_eks_node_group` — Kubernetes -- `aws_s3_bucket` — Object storage -- `aws_db_instance` — RDS databases -- `aws_lambda_function` — Serverless functions -- `aws_vpc`, `aws_subnet` — Networking -- `aws_lb`, `aws_lb_target_group` — Load balancers -- `aws_iam_role`, `aws_iam_policy` — IAM - -DO NOT write terraform{} or provider{} blocks — they are auto-generated! - -### CRITICAL RULES -- ALWAYS target a specific account_id after the first fan-out call -- Use `--output table` or `--query` with JMESPath for readable output -- AMI IDs are region-specific — look them up or use data sources -- Default region: us-east-1 unless user specifies otherwise -- For EKS: always run `update-kubeconfig` before kubectl commands -- Get real values (VPC IDs, subnet IDs, AMI IDs) from CLI before writing Terraform - -### ON ANY AWS ERROR -1. Permission denied → Check IAM role/policy: `cloud_exec('aws', 'iam get-role --role-name ', account_id='')` -2. Service not enabled → Not applicable for AWS (services are always available) -3. CLI syntax error → Use `cloud_exec('aws', ' help')` to check correct subcommand -4. Terraform failure → Run `cloud_exec('aws', ...)` to verify resources exist, then fix the manifest +### Common Terraform resources +`aws_instance`, `aws_security_group`, `aws_vpc`, `aws_subnet`, `aws_internet_gateway`, +`aws_eks_cluster`, `aws_eks_node_group`, `aws_s3_bucket`, `aws_db_instance`, `aws_db_subnet_group`, +`aws_lambda_function`, `aws_lb`, `aws_lb_target_group`, `aws_lb_listener`, +`aws_iam_role`, `aws_iam_policy`, `aws_iam_role_policy_attachment`, +`aws_route53_zone`, `aws_route53_record`, `aws_ecr_repository`, +`aws_ecs_cluster`, `aws_ecs_service`, `aws_ecs_task_definition` + +## Error Recovery + +1. **Permission denied** → Check IAM: `iam get-role`, `iam list-attached-role-policies`, `iam simulate-principal-policy` +2. **Resource not found** → Verify region: `ec2 describe-regions`, check account_id +3. **CLI syntax** → `cloud_exec('aws', ' help')` for subcommand reference +4. **Terraform failure** → Verify resources exist with CLI, then fix manifest + +### Context7 lookup on failure +For Terraform errors, query Context7 with the resource type: +`mcp_context7_get_library_docs(context7CompatibleLibraryID='/hashicorp/terraform-provider-aws', topic='aws_eks_cluster')` +For CLI errors, query: +`mcp_context7_get_library_docs(context7CompatibleLibraryID='/websites/aws_amazon_cli', topic='eks update-kubeconfig')` + +## Region Mapping +- US (default): us-east-1 +- Canada: ca-central-1 +- EU/Belgium: eu-west-1 +- UK/London: eu-west-2 +- Singapore/SEA: ap-southeast-1 +- Tokyo/Japan: ap-northeast-1 diff --git a/server/chat/backend/agent/skills/integrations/azure/SKILL.md b/server/chat/backend/agent/skills/integrations/azure/SKILL.md index 7bba13b30..b7ae662ab 100644 --- a/server/chat/backend/agent/skills/integrations/azure/SKILL.md +++ b/server/chat/backend/agent/skills/integrations/azure/SKILL.md @@ -1,100 +1,249 @@ --- name: azure id: azure -description: "Microsoft Azure integration for managing VMs, AKS, SQL, Storage, App Service, and other services via CLI and Terraform" +description: "Azure integration — VMs, AKS, SQL, Storage, App Service, Monitor, NSG, VNet via CLI and Terraform" category: cloud_provider connection_check: method: provider_in_preference tools: - cloud_exec - iac_tool -index: "Azure — VMs, AKS, SQL, Storage, App Service, Terraform IaC" +index: "Azure — VMs, AKS, SQL, Storage, App Service, Monitor, Terraform IaC" rca_priority: 10 allowed-tools: cloud_exec, iac_tool metadata: author: aurora - version: "1.0" + version: "2.0" --- # Microsoft Azure Integration ## Overview -Azure cloud provider for managing compute, Kubernetes, databases, storage, serverless, networking, and monitoring. - -## Instructions - -### CLI COMMANDS (use cloud_exec with 'azure') - -**CRITICAL: Always use cloud_exec('azure', 'COMMAND') — NOT terminal_exec!** -Authentication and subscription are auto-configured. The `az` CLI is available. -Additional CLIs: `kubectl`. - -**SUBSCRIPTION & RESOURCE GROUP SETUP:** -- Get subscription: `cloud_exec('azure', "account show --query 'id' -o tsv")` -- List resource groups: `cloud_exec('azure', 'group list --output table')` -- Create resource group: `cloud_exec('azure', 'group create --name --location ')` -- **MANDATORY:** Every Azure resource lives in a resource group. Check for existing ones before creating new ones. - -**Discovery Commands:** -- Account info: `cloud_exec('azure', 'account show')` -- List locations: `cloud_exec('azure', 'account list-locations --output table')` -- List providers: `cloud_exec('azure', 'provider list --output table')` -- Register provider: `cloud_exec('azure', 'provider register --namespace Microsoft.')` -- List resources in RG: `cloud_exec('azure', 'resource list --resource-group --output table')` - -**Virtual Machines:** -- List VMs: `cloud_exec('azure', 'vm list --output table')` -- Create VM: `cloud_exec('azure', 'vm create --resource-group --name --image Ubuntu2204 --size Standard_B2s --generate-ssh-keys')` -- Start/stop/restart: `cloud_exec('azure', 'vm start|stop|restart --resource-group --name ')` -- Describe: `cloud_exec('azure', 'vm show --resource-group --name ')` -- List sizes: `cloud_exec('azure', 'vm list-sizes --location --output table')` - -**AKS (Kubernetes):** -- List clusters: `cloud_exec('azure', 'aks list --output table')` -- Describe cluster: `cloud_exec('azure', 'aks show --name --resource-group ')` -- Get credentials: `cloud_exec('azure', 'aks get-credentials --name --resource-group ')` -- Then kubectl: `cloud_exec('azure', 'kubectl get pods -n -o wide')` -- Node pools: `cloud_exec('azure', 'aks nodepool list --cluster-name --resource-group --output table')` - -**Storage:** -- List accounts: `cloud_exec('azure', 'storage account list --output table')` -- Create account: `cloud_exec('azure', 'storage account create --name --resource-group --location --sku Standard_LRS')` -- List containers: `cloud_exec('azure', 'storage container list --account-name --output table')` -- List blobs: `cloud_exec('azure', 'storage blob list --account-name --container-name --output table')` - -**SQL Database:** -- List servers: `cloud_exec('azure', 'sql server list --output table')` -- List databases: `cloud_exec('azure', 'sql db list --server --resource-group --output table')` -- Create server: `cloud_exec('azure', 'sql server create --name --resource-group --location --admin-user --admin-password ')` - -**App Service (Web Apps):** -- List apps: `cloud_exec('azure', 'webapp list --output table')` -- Create plan: `cloud_exec('azure', 'appservice plan create --name --resource-group --sku B1 --is-linux')` -- Create app: `cloud_exec('azure', 'webapp create --name --resource-group --plan --runtime "PYTHON:3.11"')` -- View logs: `cloud_exec('azure', 'webapp log tail --name --resource-group ')` - -**Azure Monitor & Log Analytics:** -- Query logs: `cloud_exec('azure', 'monitor log-analytics query -w --analytics-query ""')` -- List alerts: `cloud_exec('azure', 'monitor alert list --output table')` -- Metrics: `cloud_exec('azure', 'monitor metrics list --resource --metric "" --interval PT1H')` -- Activity log: `cloud_exec('azure', 'monitor activity-log list --start-time --end-time ')` - -**Networking:** -- List VNets: `cloud_exec('azure', 'network vnet list --output table')` -- List NSGs: `cloud_exec('azure', 'network nsg list --output table')` -- NSG rules: `cloud_exec('azure', 'network nsg rule list --nsg-name --resource-group --output table')` -- List public IPs: `cloud_exec('azure', 'network public-ip list --output table')` -- List load balancers: `cloud_exec('azure', 'network lb list --output table')` - -### TERRAFORM FOR AZURE -Use iac_tool — provider.tf is AUTO-GENERATED, just write the resource! +Full Azure access via `cloud_exec('azure', 'COMMAND')`. +Available CLIs: `az`, `kubectl`. +Authentication and subscription are auto-configured — never ask users for credentials. + +## Resource Group Requirement (CRITICAL) +**Every Azure resource lives in a resource group.** Before creating anything: +1. List existing: `cloud_exec('azure', 'group list --output table')` +2. Reuse a suitable one, or create a new one: `cloud_exec('azure', 'group create --name --location ')` +3. Always specify `--resource-group` on subsequent commands. + +## Subscription Setup +- Get subscription ID: `cloud_exec('azure', "account show --query 'id' -o tsv")` +- ALWAYS get this before writing Terraform. + +## CLI Reference + +### Discovery +```python +cloud_exec('azure', 'account show') +cloud_exec('azure', "account show --query 'id' -o tsv") +cloud_exec('azure', 'account list-locations --output table') +cloud_exec('azure', 'group list --output table') +cloud_exec('azure', 'resource list --resource-group --output table') +cloud_exec('azure', 'provider list --output table') +cloud_exec('azure', 'provider register --namespace Microsoft.') +``` + +### Virtual Machines +```python +cloud_exec('azure', 'vm list --output table') +cloud_exec('azure', 'vm list -d --output table') # includes power state +cloud_exec('azure', 'vm show --resource-group --name --show-details') +cloud_exec('azure', 'vm create --resource-group --name --image Ubuntu2204 --size Standard_B2s --generate-ssh-keys --output json') +cloud_exec('azure', 'vm start --resource-group --name ') +cloud_exec('azure', 'vm stop --resource-group --name ') +cloud_exec('azure', 'vm restart --resource-group --name ') +cloud_exec('azure', 'vm delete --resource-group --name --yes') +cloud_exec('azure', 'vm list-sizes --location --output table') +# Diagnostics: +cloud_exec('azure', 'vm get-instance-view --resource-group --name --query "instanceView.statuses"') +cloud_exec('azure', 'vm boot-diagnostics get-boot-log --resource-group --name ') +# Disks: +cloud_exec('azure', 'disk list --resource-group --output table') +``` + +### AKS (Kubernetes) +```python +cloud_exec('azure', 'aks list --output table') +cloud_exec('azure', 'aks show --name --resource-group ') +# MANDATORY before any kubectl: +cloud_exec('azure', 'aks get-credentials --name --resource-group ') +# Then kubectl works: +cloud_exec('azure', 'kubectl get pods -n -o wide') +cloud_exec('azure', 'kubectl describe pod -n ') +cloud_exec('azure', 'kubectl logs -n --since=1h --tail=200') +cloud_exec('azure', 'kubectl logs -n -c --previous') +cloud_exec('azure', 'kubectl get events -n --sort-by=.lastTimestamp') +cloud_exec('azure', 'kubectl top pods -n ') +cloud_exec('azure', 'kubectl top nodes') +cloud_exec('azure', 'kubectl get hpa -n ') +cloud_exec('azure', 'kubectl get deployments -n ') +cloud_exec('azure', 'kubectl rollout history deployment/ -n ') +cloud_exec('azure', 'kubectl get pvc -n ') +cloud_exec('azure', 'kubectl get svc -n ') +cloud_exec('azure', 'kubectl get ingress -n ') +# Node pools: +cloud_exec('azure', 'aks nodepool list --cluster-name --resource-group --output table') +cloud_exec('azure', 'aks nodepool show --cluster-name --resource-group --nodepool-name ') +# Scale: +cloud_exec('azure', 'aks nodepool scale --cluster-name --resource-group --nodepool-name --node-count 5') +# Create cluster: +cloud_exec('azure', 'aks create --resource-group --name --node-count 3 --node-vm-size Standard_DS2_v2 --generate-ssh-keys --enable-cluster-autoscaler --min-count 1 --max-count 10') +``` + +### Storage +```python +cloud_exec('azure', 'storage account list --output table') +cloud_exec('azure', 'storage account show --resource-group --name --query "primaryEndpoints"') +cloud_exec('azure', 'storage account create --name --resource-group --location --sku Standard_LRS') +# Containers: +cloud_exec('azure', 'storage container list --account-name --output table') +cloud_exec('azure', 'storage container create --account-name --name ') +# Blobs: +cloud_exec('azure', 'storage blob list --account-name --container-name --output table') +cloud_exec('azure', 'storage blob upload --account-name --container-name --file --name ') +# Keys (for troubleshooting access): +cloud_exec('azure', 'storage account keys list --account-name --resource-group ') +cloud_exec('azure', 'storage account keys renew --account-name --resource-group --key primary') +``` + +### SQL Database +```python +cloud_exec('azure', 'sql server list --output table') +cloud_exec('azure', 'sql server show --name --resource-group ') +cloud_exec('azure', 'sql db list --server --resource-group --output table') +cloud_exec('azure', 'sql db show --server --resource-group --name ') +# Create: +cloud_exec('azure', 'sql server create --name --resource-group --location --admin-user --admin-password ') +cloud_exec('azure', 'sql db create --server --resource-group --name --service-objective S0') +# Firewall (allow Azure services): +cloud_exec('azure', 'sql server firewall-rule create --server --resource-group --name AllowAzure --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0') +# Auditing and threat detection: +cloud_exec('azure', 'sql server audit-policy show --name --resource-group ') +``` + +### App Service (Web Apps) +```python +cloud_exec('azure', 'webapp list --output table') +cloud_exec('azure', 'webapp show --name --resource-group ') +cloud_exec('azure', 'appservice plan list --output table') +cloud_exec('azure', 'appservice plan create --name --resource-group --sku B1 --is-linux') +cloud_exec('azure', 'webapp create --name --resource-group --plan --runtime "PYTHON:3.11"') +cloud_exec('azure', 'webapp log tail --name --resource-group ') +cloud_exec('azure', 'webapp deployment list-publishing-profiles --name --resource-group ') +# Configuration: +cloud_exec('azure', 'webapp config show --name --resource-group ') +cloud_exec('azure', 'webapp config appsettings list --name --resource-group ') +``` + +### Azure Monitor & Log Analytics (CRITICAL for investigations) + +#### Log Analytics Queries (KQL) +```python +# First, find workspace ID: +cloud_exec('azure', 'monitor log-analytics workspace list --output table') +cloud_exec('azure', 'monitor log-analytics workspace show --resource-group --workspace-name --query "customerId" -o tsv') + +# Query with KQL: +cloud_exec('azure', 'monitor log-analytics query -w --analytics-query "ContainerLog | where LogEntry contains \'error\' | take 50"') +cloud_exec('azure', 'monitor log-analytics query -w --analytics-query "KubeEvents | where Reason == \'BackOff\' | order by TimeGenerated desc | take 30"') +cloud_exec('azure', 'monitor log-analytics query -w --analytics-query "Perf | where ObjectName == \'K8SContainer\' and CounterName == \'memoryWorkingSetBytes\' | summarize avg(CounterValue) by bin(TimeGenerated, 5m), InstanceName | order by TimeGenerated desc"') +cloud_exec('azure', 'monitor log-analytics query -w --analytics-query "AzureActivity | where OperationNameValue contains \'Microsoft.ContainerService\' | order by TimeGenerated desc | take 20"') +``` + +Common KQL patterns: +- Container errors: `ContainerLog | where LogEntry contains "error" | order by TimeGenerated desc` +- Pod events: `KubeEvents | where Reason in ("Failed", "BackOff", "Unhealthy") | order by TimeGenerated desc` +- Performance: `Perf | where ObjectName == "K8SContainer" and CounterName == "cpuUsageNanoCores" | summarize avg(CounterValue) by bin(TimeGenerated, 5m), InstanceName` +- Node conditions: `KubeNodeInventory | where Status != "Ready" | order by TimeGenerated desc` +- Deployment changes: `AzureActivity | where OperationNameValue contains "deployments" | order by TimeGenerated desc` + +#### Metrics & Alerts +```python +cloud_exec('azure', 'monitor alert list --output table') +cloud_exec('azure', 'monitor metrics list --resource --metric "Percentage CPU" --interval PT1H --output table') +cloud_exec('azure', 'monitor metrics list --resource --metric "Available Memory Bytes" --interval PT5M --aggregation Average') +cloud_exec('azure', 'monitor metrics list-definitions --resource --output table') +``` + +#### Activity Log +```python +cloud_exec('azure', 'monitor activity-log list --start-time --end-time --output table') +cloud_exec('azure', 'monitor activity-log list --resource-group --start-time ') +cloud_exec('azure', 'monitor activity-log list --caller --start-time ') +``` + +#### Diagnostic Settings +```python +cloud_exec('azure', 'monitor diagnostic-settings list --resource ') +cloud_exec('azure', 'monitor diagnostic-settings create --name --resource --workspace --logs "[{category:kube-audit,enabled:true}]"') +``` + +### Networking +```python +cloud_exec('azure', 'network vnet list --output table') +cloud_exec('azure', 'network vnet show --resource-group --name ') +cloud_exec('azure', 'network vnet subnet list --resource-group --vnet-name --output table') +cloud_exec('azure', 'network nsg list --output table') +cloud_exec('azure', 'network nsg rule list --nsg-name --resource-group --output table') +cloud_exec('azure', 'network nsg rule create --nsg-name --resource-group --name AllowHTTPS --protocol Tcp --direction Inbound --priority 100 --destination-port-ranges 443 --access Allow') +cloud_exec('azure', 'network public-ip list --output table') +cloud_exec('azure', 'network lb list --output table') +cloud_exec('azure', 'network lb show --resource-group --name ') +# DNS: +cloud_exec('azure', 'network dns zone list --output table') +cloud_exec('azure', 'network dns record-set list --resource-group --zone-name --output table') +``` + +### Other Services +```python +# Container Registry: +cloud_exec('azure', 'acr list --output table') +cloud_exec('azure', 'acr repository list --name ') +cloud_exec('azure', 'acr repository show-tags --name --repository --orderby time_desc --top 10') +# Key Vault: +cloud_exec('azure', 'keyvault list --output table') +cloud_exec('azure', 'keyvault secret list --vault-name --output table') +# Azure Functions: +cloud_exec('azure', 'functionapp list --output table') +cloud_exec('azure', 'functionapp show --name --resource-group ') +# Service Bus: +cloud_exec('azure', 'servicebus namespace list --output table') +cloud_exec('azure', 'servicebus queue list --namespace-name --resource-group ') +``` + +## RCA / Investigation Workflow + +When investigating an Azure incident: + +1. **Get subscription context**: `account show` +2. **Find resources**: `resource list --resource-group --output table` +3. **Get AKS credentials** (if K8s): `aks get-credentials --name --resource-group ` +4. **Check resource state**: `vm show --show-details`, `aks show`, `sql server show` +5. **Check pods/containers** (if K8s): `kubectl get pods -o wide`, `kubectl describe pod`, `kubectl logs` +6. **Check Kubernetes events**: `kubectl get events --sort-by=.lastTimestamp` +7. **Query Log Analytics**: KQL queries for ContainerLog, KubeEvents, Perf counters +8. **Check metrics**: `monitor metrics list` for CPU, memory, connections +9. **Check alerts**: `monitor alert list` +10. **Check activity log**: `monitor activity-log list` for recent changes/deployments +11. **Check networking**: NSG rules, load balancer health, DNS resolution +12. **Check node health**: `kubectl describe node`, `kubectl top nodes`, node pool status +13. **Compare healthy vs unhealthy**: Pod metrics, log patterns side-by-side + +## Terraform + +Use `iac_tool` — provider.tf is AUTO-GENERATED. Never write terraform{} or provider{} blocks. **PREREQUISITE:** Get subscription ID first: -`cloud_exec('azure', "account show --query 'id' -o tsv")` +```python +cloud_exec('azure', "account show --query 'id' -o tsv") +``` -**IMPORTANT:** The system auto-generates strong admin passwords using Terraform's `random_password` resource. Do NOT ask users for passwords. +**VM passwords are auto-generated** by the system using Terraform's `random_password` resource. Never ask users for passwords. -**RESOURCE GROUP (always needed first):** +### Resource Group (always first) ```hcl resource "azurerm_resource_group" "rg" { name = "my-rg" @@ -102,22 +251,47 @@ resource "azurerm_resource_group" "rg" { } ``` -**VIRTUAL MACHINE:** +### Linux Virtual Machine ```hcl +resource "azurerm_virtual_network" "vnet" { + name = "my-vnet" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name +} + +resource "azurerm_subnet" "subnet" { + name = "my-subnet" + resource_group_name = azurerm_resource_group.rg.name + virtual_network_name = azurerm_virtual_network.vnet.name + address_prefixes = ["10.0.1.0/24"] +} + +resource "azurerm_network_interface" "nic" { + name = "my-nic" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.subnet.id + private_ip_address_allocation = "Dynamic" + } +} + resource "azurerm_linux_virtual_machine" "vm" { name = "my-vm" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location size = "Standard_B2s" admin_username = "adminuser" + network_interface_ids = [azurerm_network_interface.nic.id] admin_ssh_key { username = "adminuser" public_key = file("~/.ssh/id_rsa.pub") } - network_interface_ids = [azurerm_network_interface.nic.id] - os_disk { caching = "ReadWrite" storage_account_type = "Standard_LRS" @@ -132,7 +306,7 @@ resource "azurerm_linux_virtual_machine" "vm" { } ``` -**AKS CLUSTER:** +### AKS Cluster ```hcl resource "azurerm_kubernetes_cluster" "aks" { name = "my-aks" @@ -143,16 +317,20 @@ resource "azurerm_kubernetes_cluster" "aks" { default_node_pool { name = "default" node_count = 3 - vm_size = "Standard_B2s" + vm_size = "Standard_DS2_v2" } identity { type = "SystemAssigned" } + + tags = { + Environment = "Production" + } } ``` -**STORAGE ACCOUNT:** +### Storage Account with Network Rules ```hcl resource "azurerm_storage_account" "storage" { name = "mystorageaccount" @@ -160,35 +338,85 @@ resource "azurerm_storage_account" "storage" { location = azurerm_resource_group.rg.location account_tier = "Standard" account_replication_type = "LRS" + + network_rules { + default_action = "Deny" + ip_rules = ["100.0.0.1"] + } } ``` -**Common Azure Terraform resources:** -- `azurerm_resource_group` — Resource groups (required for everything) -- `azurerm_linux_virtual_machine` — Linux VMs -- `azurerm_network_interface`, `azurerm_virtual_network`, `azurerm_subnet` — Networking -- `azurerm_network_security_group` — Firewall rules -- `azurerm_kubernetes_cluster` — AKS -- `azurerm_storage_account`, `azurerm_storage_container` — Blob storage -- `azurerm_mssql_server`, `azurerm_mssql_database` — SQL databases -- `azurerm_service_plan`, `azurerm_linux_web_app` — App Service -- `azurerm_lb` — Load balancers - -DO NOT write terraform{} or provider{} blocks — they are auto-generated! - -### CRITICAL RULES -- **ALWAYS** specify `--resource-group` for Azure operations -- Check existing resource groups before creating new ones -- Get subscription ID before writing Terraform -- Use `--output table` for readable CLI output -- AKS: always run `get-credentials` before kubectl commands -- VM passwords are auto-generated by Terraform — never ask the user -- Default location: eastus unless user specifies otherwise -- Resource names in Azure must often be globally unique (storage accounts, web apps) - -### ON ANY AZURE ERROR -1. Provider not registered → `cloud_exec('azure', 'provider register --namespace Microsoft.')` -2. Permission denied → Check role assignments: `cloud_exec('azure', 'role assignment list --assignee ')` -3. Resource group missing → Create one: `cloud_exec('azure', 'group create --name --location ')` -4. CLI syntax error → `cloud_exec('azure', ' --help')` -5. Terraform failure → Verify resources with CLI, then fix the manifest +### SQL Server + Database +```hcl +resource "azurerm_mssql_server" "sql" { + name = "my-sql-server" + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + version = "12.0" + administrator_login = "sqladmin" + administrator_login_password = random_password.sql.result +} + +resource "azurerm_mssql_database" "db" { + name = "my-database" + server_id = azurerm_mssql_server.sql.id + sku_name = "S0" +} +``` + +### Network Security Group +```hcl +resource "azurerm_network_security_group" "nsg" { + name = "my-nsg" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + + security_rule { + name = "AllowHTTPS" + priority = 100 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "443" + source_address_prefix = "*" + destination_address_prefix = "*" + } +} +``` + +### Common Terraform resources +`azurerm_resource_group`, `azurerm_virtual_network`, `azurerm_subnet`, +`azurerm_network_interface`, `azurerm_network_security_group`, +`azurerm_linux_virtual_machine`, `azurerm_windows_virtual_machine`, +`azurerm_kubernetes_cluster`, `azurerm_kubernetes_cluster_node_pool`, +`azurerm_storage_account`, `azurerm_storage_container`, +`azurerm_mssql_server`, `azurerm_mssql_database`, +`azurerm_service_plan`, `azurerm_linux_web_app`, +`azurerm_lb`, `azurerm_lb_backend_address_pool`, `azurerm_lb_rule`, +`azurerm_public_ip`, `azurerm_container_registry`, +`azurerm_key_vault`, `azurerm_key_vault_secret`, +`azurerm_dns_zone`, `azurerm_dns_a_record` + +## Error Recovery + +1. **Provider not registered** → `cloud_exec('azure', 'provider register --namespace Microsoft.ContainerService')` — common: Microsoft.ContainerService, Microsoft.Sql, Microsoft.Storage, Microsoft.Web, Microsoft.Network +2. **Resource group missing** → Create one: `group create --name --location ` +3. **Permission denied** → Check role: `cloud_exec('azure', 'role assignment list --assignee --output table')` +4. **CLI syntax** → `cloud_exec('azure', ' --help')` for subcommand reference +5. **Storage name taken** → Storage account names must be globally unique, 3-24 chars, lowercase alphanumeric only +6. **Terraform failure** → Verify resources with CLI, then fix manifest + +### Context7 lookup on failure +For Terraform errors: +`mcp_context7_get_library_docs(context7CompatibleLibraryID='/hashicorp/terraform-provider-azurerm', topic='azurerm_kubernetes_cluster')` +For CLI errors: +`mcp_context7_get_library_docs(context7CompatibleLibraryID='/microsoftdocs/azure-docs-cli', topic='aks get-credentials')` + +## Region Mapping +- US (default): eastus +- Canada: canadacentral +- EU/Belgium: westeurope +- UK/London: uksouth +- Singapore/SEA: southeastasia +- Tokyo/Japan: japaneast diff --git a/server/chat/backend/agent/skills/integrations/gcp/SKILL.md b/server/chat/backend/agent/skills/integrations/gcp/SKILL.md index 00b179c69..7316c4fcb 100644 --- a/server/chat/backend/agent/skills/integrations/gcp/SKILL.md +++ b/server/chat/backend/agent/skills/integrations/gcp/SKILL.md @@ -1,107 +1,245 @@ --- name: gcp id: gcp -description: "Google Cloud Platform integration for managing Compute Engine, GKE, Cloud SQL, Cloud Storage, Cloud Run, and other services via CLI and Terraform" +description: "GCP integration — Compute Engine, GKE, Cloud SQL, Cloud Storage, Cloud Run, Cloud Logging, IAM via CLI and Terraform" category: cloud_provider connection_check: method: provider_in_preference tools: - cloud_exec - iac_tool -index: "GCP — Compute Engine, GKE, Cloud SQL, Cloud Storage, Cloud Run, Terraform IaC" +index: "GCP — Compute Engine, GKE, Cloud SQL, Cloud Storage, Cloud Run, Logging, Terraform IaC" rca_priority: 10 allowed-tools: cloud_exec, iac_tool metadata: author: aurora - version: "1.0" + version: "2.0" --- # Google Cloud Platform Integration ## Overview -GCP cloud provider for managing compute, Kubernetes, databases, storage, serverless, networking, and monitoring. +Full GCP access via `cloud_exec('gcp', 'COMMAND')`. +Available CLIs: `gcloud`, `gsutil`, `bq`, `kubectl`. +Authentication and project are auto-configured — never ask users for credentials. -## Instructions +## Project Setup +- Get current project: `cloud_exec('gcp', 'config get-value project')` +- Set project explicitly: `cloud_exec('gcp', 'config set project ')` +- If user specifies a project, set it. Otherwise fetch the current one and reuse it everywhere. +- ALWAYS get the project ID before writing Terraform. + +## CLI Reference + +### Discovery +```python +cloud_exec('gcp', 'config get-value project') +cloud_exec('gcp', 'projects list') +cloud_exec('gcp', 'compute regions list --format="table(name,status)"') +cloud_exec('gcp', 'compute zones list --filter="region:(us-central1)"') +cloud_exec('gcp', 'services list --enabled --format="table(name)"') +cloud_exec('gcp', 'services enable .googleapis.com') +``` + +### Compute Engine +```python +cloud_exec('gcp', 'compute instances list --format="table(name,zone,machineType,status,networkInterfaces[0].accessConfigs[0].natIP)"') +cloud_exec('gcp', 'compute instances describe --zone=') +cloud_exec('gcp', 'compute instances create --zone= --machine-type=e2-medium --image-family=debian-12 --image-project=debian-cloud --tags=http-server') +cloud_exec('gcp', 'compute instances start --zone=') +cloud_exec('gcp', 'compute instances stop --zone=') +cloud_exec('gcp', 'compute instances delete --zone= --quiet') +# SSH with command execution: +cloud_exec('gcp', 'compute ssh --zone= --command="uptime && free -m && df -h"') +# Serial port output (useful for boot issues): +cloud_exec('gcp', 'compute instances get-serial-port-output --zone=') +# Instance metadata: +cloud_exec('gcp', 'compute instances describe --zone= --format="json(metadata)"') +# List machine types: +cloud_exec('gcp', 'compute machine-types list --zones= --filter="name:(e2-*)" --format="table(name,guestCpus,memoryMb)"') +``` -### CLI COMMANDS (use cloud_exec with 'gcp') +### GKE (Kubernetes) +```python +cloud_exec('gcp', 'container clusters list --format="table(name,location,currentMasterVersion,status,currentNodeCount)"') +cloud_exec('gcp', 'container clusters describe --region=') +# MANDATORY before any kubectl: +cloud_exec('gcp', 'container clusters get-credentials --region=') +# Then kubectl works: +cloud_exec('gcp', 'kubectl get pods -n -o wide') +cloud_exec('gcp', 'kubectl describe pod -n ') +cloud_exec('gcp', 'kubectl logs -n --since=1h --tail=200') +cloud_exec('gcp', 'kubectl logs -n -c --previous') +cloud_exec('gcp', 'kubectl get events -n --sort-by=.lastTimestamp') +cloud_exec('gcp', 'kubectl top pods -n ') +cloud_exec('gcp', 'kubectl top nodes') +cloud_exec('gcp', 'kubectl get hpa -n ') +cloud_exec('gcp', 'kubectl get deployments -n ') +cloud_exec('gcp', 'kubectl rollout history deployment/ -n ') +cloud_exec('gcp', 'kubectl get pvc -n ') +cloud_exec('gcp', 'kubectl get svc -n ') +cloud_exec('gcp', 'kubectl get ingress -n ') +# Node pools: +cloud_exec('gcp', 'container node-pools list --cluster= --region=') +cloud_exec('gcp', 'container node-pools describe --cluster= --region=') +# Resize: +cloud_exec('gcp', 'container clusters resize --node-pool= --num-nodes=5 --region= --quiet') +``` -**CRITICAL: Always use cloud_exec('gcp', 'COMMAND') — NOT terminal_exec!** -Authentication and project setup are auto-configured. The `gcloud` CLI is available. -Additional CLIs: `gsutil`, `bq`, `kubectl`. +### Cloud Storage (gsutil) +```python +cloud_exec('gcp', 'gsutil ls') +cloud_exec('gcp', 'gsutil ls -la gs:///') +cloud_exec('gcp', 'gsutil du -s gs://') +cloud_exec('gcp', 'gsutil cp gs:///') +cloud_exec('gcp', 'gsutil rm gs:///') +cloud_exec('gcp', 'gsutil iam get gs://') +cloud_exec('gcp', 'gsutil versioning get gs://') +cloud_exec('gcp', 'gsutil lifecycle get gs://') +``` -**PROJECT SETUP:** -- Get current project: `cloud_exec('gcp', 'config get-value project')` -- Set project: `cloud_exec('gcp', 'config set project ')` -- If the user specifies a project, set it. Otherwise fetch the current one and reuse it. - -**Discovery Commands:** -- List projects: `cloud_exec('gcp', 'projects list')` -- List regions: `cloud_exec('gcp', 'compute regions list')` -- List zones: `cloud_exec('gcp', 'compute zones list --filter="region:(us-central1)"')` -- List services: `cloud_exec('gcp', 'services list --enabled')` -- Enable service: `cloud_exec('gcp', 'services enable ')` - -**Compute Engine:** -- List instances: `cloud_exec('gcp', 'compute instances list')` -- Create instance: `cloud_exec('gcp', 'compute instances create --zone= --machine-type=e2-medium --image-family=debian-12 --image-project=debian-cloud')` -- Start/stop: `cloud_exec('gcp', 'compute instances start|stop --zone=')` -- Describe: `cloud_exec('gcp', 'compute instances describe --zone=')` -- SSH: `cloud_exec('gcp', 'compute ssh --zone= --command=""')` - -**GKE (Kubernetes):** -- List clusters: `cloud_exec('gcp', 'container clusters list')` -- Describe cluster: `cloud_exec('gcp', 'container clusters describe --region=')` -- Get credentials: `cloud_exec('gcp', 'container clusters get-credentials --region=')` -- Then kubectl: `cloud_exec('gcp', 'kubectl get pods -n -o wide')` -- Node pools: `cloud_exec('gcp', 'container node-pools list --cluster= --region=')` - -**Cloud Storage (gsutil):** -- List buckets: `cloud_exec('gcp', 'gsutil ls')` -- List objects: `cloud_exec('gcp', 'gsutil ls gs:///')` -- Copy: `cloud_exec('gcp', 'gsutil cp ')` -- Bucket info: `cloud_exec('gcp', 'gsutil du -s gs://')` - -**Cloud SQL (Databases):** -- List instances: `cloud_exec('gcp', 'sql instances list')` -- Describe: `cloud_exec('gcp', 'sql instances describe ')` -- List databases: `cloud_exec('gcp', 'sql databases list --instance=')` - -**Cloud Run (Serverless):** -- List services: `cloud_exec('gcp', 'run services list --region=')` -- Describe: `cloud_exec('gcp', 'run services describe --region=')` -- Deploy: `cloud_exec('gcp', 'run deploy --image= --region= --allow-unauthenticated')` -- View logs: `cloud_exec('gcp', 'run services logs read --region= --limit=50')` - -**Cloud Logging & Monitoring:** -- Read logs: `cloud_exec('gcp', 'logging read "resource.type=k8s_container AND severity>=ERROR" --limit=50 --freshness=1h')` -- Specific resource: `cloud_exec('gcp', 'logging read "resource.type=gce_instance AND resource.labels.instance_id=" --limit=50')` -- List metrics: `cloud_exec('gcp', 'monitoring dashboards list')` -- Alert policies: `cloud_exec('gcp', 'alpha monitoring policies list')` - -**IAM:** -- List bindings: `cloud_exec('gcp', 'projects get-iam-policy --flatten="bindings[].members" --format="table(bindings.role,bindings.members)"')` -- Service accounts: `cloud_exec('gcp', 'iam service-accounts list')` - -**Networking:** -- List VPCs: `cloud_exec('gcp', 'compute networks list')` -- List subnets: `cloud_exec('gcp', 'compute networks subnets list')` -- Firewall rules: `cloud_exec('gcp', 'compute firewall-rules list')` -- Load balancers: `cloud_exec('gcp', 'compute forwarding-rules list')` - -**BigQuery (bq):** -- List datasets: `cloud_exec('gcp', 'bq ls')` -- Query: `cloud_exec('gcp', 'bq query --use_legacy_sql=false "SELECT * FROM dataset.table LIMIT 10"')` - -### TERRAFORM FOR GCP -Use iac_tool — provider.tf is AUTO-GENERATED, just write the resource! - -**PREREQUISITE:** Get project ID first: -`cloud_exec('gcp', 'config get-value project')` - -**COMPUTE INSTANCE EXAMPLE:** +### Cloud SQL (Databases) +```python +cloud_exec('gcp', 'sql instances list --format="table(name,databaseVersion,settings.tier,state,region)"') +cloud_exec('gcp', 'sql instances describe ') +cloud_exec('gcp', 'sql databases list --instance=') +cloud_exec('gcp', 'sql operations list --instance= --limit=10') +# Connect (via proxy): +cloud_exec('gcp', 'sql connect --user= --quiet') +# Backups: +cloud_exec('gcp', 'sql backups list --instance=') +# Patch/resize: +cloud_exec('gcp', 'sql instances patch --tier=db-custom-2-7680') +``` + +### Cloud Run (Serverless) +```python +cloud_exec('gcp', 'run services list --region= --format="table(name,status.url,status.conditions.status)"') +cloud_exec('gcp', 'run services describe --region=') +cloud_exec('gcp', 'run deploy --image= --region= --allow-unauthenticated --memory=512Mi --cpu=1') +cloud_exec('gcp', 'run revisions list --service= --region=') +# Logs: +cloud_exec('gcp', 'run services logs read --region= --limit=50') +``` + +### Cloud Logging (CRITICAL for investigations) +```python +# Recent errors across K8s: +cloud_exec('gcp', 'logging read "resource.type=k8s_container AND severity>=ERROR" --limit=50 --freshness=1h --format=json') +# Specific pod logs: +cloud_exec('gcp', 'logging read "resource.type=k8s_container AND resource.labels.pod_name= AND resource.labels.namespace_name=" --limit=100 --freshness=2h') +# Compute instance logs: +cloud_exec('gcp', 'logging read "resource.type=gce_instance AND resource.labels.instance_id=" --limit=50 --freshness=1h') +# Cloud SQL logs: +cloud_exec('gcp', 'logging read "resource.type=cloudsql_database AND resource.labels.database_id=:" --limit=50 --freshness=1h') +# Cloud Run logs: +cloud_exec('gcp', 'logging read "resource.type=cloud_run_revision AND resource.labels.service_name=" --limit=50 --freshness=1h') +# GKE audit logs: +cloud_exec('gcp', 'logging read "resource.type=k8s_cluster AND logName:\"cloudaudit.googleapis.com\"" --limit=30 --freshness=6h') +# Custom filter with text search: +cloud_exec('gcp', 'logging read "resource.type=k8s_container AND textPayload:\"OOMKilled\"" --limit=30 --freshness=24h') +``` + +Cloud Logging filter syntax reference: +- Severity: `severity>=ERROR`, `severity=WARNING` +- Text search: `textPayload:"keyword"`, `jsonPayload.message:"keyword"` +- Resource: `resource.type=k8s_container`, `resource.type=gce_instance`, `resource.type=cloudsql_database` +- Labels: `resource.labels.namespace_name="default"`, `resource.labels.pod_name="my-pod"` +- Time: Use `--freshness=1h` or `--freshness=6h` (simpler than timestamp ranges) +- Combine with AND/OR: `severity>=ERROR AND resource.labels.namespace_name="prod"` + +### Cloud Monitoring +```python +cloud_exec('gcp', 'monitoring dashboards list --format="table(displayName,name)"') +cloud_exec('gcp', 'alpha monitoring policies list --format="table(displayName,enabled,conditions.displayName)"') +# Time series (raw metrics): +cloud_exec('gcp', 'monitoring time-series list --filter="metric.type=compute.googleapis.com/instance/cpu/utilization AND resource.labels.instance_id=" --interval-start-time= --interval-end-time=') +``` + +### IAM +```python +cloud_exec('gcp', 'projects get-iam-policy --flatten="bindings[].members" --format="table(bindings.role,bindings.members)" --filter="bindings.members:"') +cloud_exec('gcp', 'iam service-accounts list --format="table(email,displayName)"') +cloud_exec('gcp', 'iam service-accounts get-iam-policy ') +cloud_exec('gcp', 'iam roles describe roles/') +# Test permissions: +cloud_exec('gcp', 'iam list-testable-permissions //cloudresourcemanager.googleapis.com/projects/') +``` + +### Networking +```python +cloud_exec('gcp', 'compute networks list') +cloud_exec('gcp', 'compute networks subnets list --network=') +cloud_exec('gcp', 'compute firewall-rules list --format="table(name,network,direction,allowed[].map().firewall_rule().ip_protocol,sourceRanges)"') +cloud_exec('gcp', 'compute firewall-rules describe ') +cloud_exec('gcp', 'compute forwarding-rules list') +cloud_exec('gcp', 'compute addresses list') +# Health checks: +cloud_exec('gcp', 'compute health-checks list') +cloud_exec('gcp', 'compute backend-services get-health --global') +``` + +### BigQuery (bq) +```python +cloud_exec('gcp', 'bq ls') +cloud_exec('gcp', 'bq ls ') +cloud_exec('gcp', 'bq show .') +cloud_exec('gcp', 'bq query --use_legacy_sql=false "SELECT * FROM `..
` LIMIT 10"') +cloud_exec('gcp', 'bq show --job ') +``` + +### Other Services +```python +# Cloud Functions: +cloud_exec('gcp', 'functions list --format="table(name,status,runtime)"') +cloud_exec('gcp', 'functions describe --region=') +cloud_exec('gcp', 'functions logs read --region= --limit=50') +# Pub/Sub: +cloud_exec('gcp', 'pubsub topics list') +cloud_exec('gcp', 'pubsub subscriptions list') +# Secret Manager: +cloud_exec('gcp', 'secrets list') +cloud_exec('gcp', 'secrets versions access latest --secret=') +# Artifact Registry / Container Registry: +cloud_exec('gcp', 'artifacts repositories list --location=') +cloud_exec('gcp', 'artifacts docker images list -docker.pkg.dev//') +``` + +## RCA / Investigation Workflow + +When investigating a GCP incident: + +1. **Get project context**: `config get-value project` +2. **Get cluster credentials** (if GKE): `container clusters get-credentials --region=` +3. **Check resource state**: `container clusters list`, `compute instances list`, `sql instances list` +4. **Check pods/containers** (if K8s): `kubectl get pods -o wide`, `kubectl describe pod`, `kubectl logs` +5. **Check Kubernetes events**: `kubectl get events --sort-by=.lastTimestamp` +6. **Query Cloud Logging**: Use filter syntax above — check severity>=ERROR, search for OOMKilled, CrashLoopBackOff +7. **Check metrics**: `kubectl top pods`, Cloud Monitoring time-series for CPU/memory +8. **Check alert policies**: `alpha monitoring policies list` +9. **Check recent deployments**: `kubectl rollout history`, GKE audit logs +10. **Check networking**: Firewall rules, health checks, backend service health +11. **Check node health**: `kubectl describe node`, `kubectl top nodes` +12. **Compare healthy vs unhealthy**: Side-by-side pod metrics and logs + +## Terraform + +Use `iac_tool` — provider.tf is AUTO-GENERATED. Never write terraform{} or provider{} blocks. + +**PREREQUISITE:** Always get project ID first: +```python +cloud_exec('gcp', 'config get-value project') +``` + +### Compute Instance ```hcl +resource "google_service_account" "default" { + account_id = "my-custom-sa" + display_name = "Custom SA for VM" +} + resource "google_compute_instance" "vm" { - name = "my-vm" + name = "my-instance" machine_type = "e2-medium" zone = "us-central1-b" @@ -115,24 +253,43 @@ resource "google_compute_instance" "vm" { network = "default" access_config {} } + + service_account { + email = google_service_account.default.email + scopes = ["cloud-platform"] + } } ``` -**GKE CLUSTER:** +### GKE Cluster ```hcl -resource "google_container_cluster" "cluster" { +resource "google_container_cluster" "primary" { name = "my-cluster" location = "us-central1" - initial_node_count = 3 + initial_node_count = 1 + remove_default_node_pool = true +} + +resource "google_container_node_pool" "primary_nodes" { + name = "primary-pool" + location = "us-central1" + cluster = google_container_cluster.primary.name + node_count = 3 node_config { machine_type = "e2-medium" + oauth_scopes = ["https://www.googleapis.com/auth/cloud-platform"] + } + + autoscaling { + min_node_count = 1 + max_node_count = 5 } } ``` -**CLOUD SQL:** +### Cloud SQL ```hcl resource "google_sql_database_instance" "db" { name = "my-db" @@ -140,45 +297,101 @@ resource "google_sql_database_instance" "db" { region = "us-central1" settings { - tier = "db-f1-micro" + tier = "db-f1-micro" + availability_type = "ZONAL" + + backup_configuration { + enabled = true + } } deletion_protection = false } + +resource "google_sql_database" "database" { + name = "mydb" + instance = google_sql_database_instance.db.name +} ``` -**CLOUD STORAGE BUCKET:** +### Cloud Storage ```hcl resource "google_storage_bucket" "bucket" { - name = "my-bucket-unique-name" + name = "my-unique-bucket-name" location = "US" + + versioning { + enabled = true + } + + lifecycle_rule { + condition { + age = 30 + } + action { + type = "Delete" + } + } +} +``` + +### VPC Network +```hcl +resource "google_compute_network" "vpc" { + name = "my-vpc" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "subnet" { + name = "my-subnet" + ip_cidr_range = "10.0.0.0/24" + region = "us-central1" + network = google_compute_network.vpc.id +} + +resource "google_compute_firewall" "allow_ssh" { + name = "allow-ssh" + network = google_compute_network.vpc.name + + allow { + protocol = "tcp" + ports = ["22"] + } + + source_ranges = ["0.0.0.0/0"] + target_tags = ["ssh-enabled"] } ``` -**Common GCP Terraform resources:** -- `google_compute_instance` — Virtual machines -- `google_compute_firewall` — Firewall rules -- `google_container_cluster`, `google_container_node_pool` — GKE -- `google_storage_bucket` — Cloud Storage -- `google_sql_database_instance` — Cloud SQL -- `google_cloud_run_v2_service` — Cloud Run -- `google_compute_network`, `google_compute_subnetwork` — VPC -- `google_compute_global_forwarding_rule` — Load balancers -- `google_project_iam_member` — IAM bindings - -DO NOT write terraform{} or provider{} blocks — they are auto-generated! - -### CRITICAL RULES -- Always get the project ID before writing Terraform or running commands -- Use `--region` for regional resources, `--zone` for zonal resources -- GKE: always run `get-credentials` before kubectl commands -- Enable required APIs before creating resources: `services enable ` -- Default zone: us-central1-b unless user specifies otherwise -- For beta features, use `cloud_exec('gcp', 'beta ')` - -### ON ANY GCP ERROR -1. API not enabled → `cloud_exec('gcp', 'services enable .googleapis.com')` -2. Permission denied → Check IAM: `cloud_exec('gcp', 'projects get-iam-policy ')` -3. CLI syntax error → `cloud_exec('gcp', ' --help')` -4. Try beta: `cloud_exec('gcp', 'beta --help')` -5. Terraform failure → Verify resources with CLI, then fix the manifest +### Common Terraform resources +`google_compute_instance`, `google_compute_firewall`, `google_compute_network`, `google_compute_subnetwork`, +`google_container_cluster`, `google_container_node_pool`, +`google_storage_bucket`, `google_sql_database_instance`, `google_sql_database`, +`google_cloud_run_v2_service`, `google_cloudfunctions_function`, +`google_compute_global_forwarding_rule`, `google_compute_health_check`, +`google_project_iam_member`, `google_service_account`, +`google_pubsub_topic`, `google_pubsub_subscription`, +`google_artifact_registry_repository`, `google_secret_manager_secret` + +## Error Recovery + +1. **API not enabled** → `cloud_exec('gcp', 'services enable .googleapis.com')` — common: container.googleapis.com, sqladmin.googleapis.com, run.googleapis.com, cloudfunctions.googleapis.com +2. **Permission denied** → Check IAM: `projects get-iam-policy `, verify service account roles +3. **Beta feature** → Prefix with beta: `cloud_exec('gcp', 'beta ')` +4. **CLI syntax** → `cloud_exec('gcp', ' --help')` for subcommand reference +5. **Terraform failure** → Verify with CLI, then fix manifest + +### Context7 lookup on failure +For Terraform errors: +`mcp_context7_get_library_docs(context7CompatibleLibraryID='/hashicorp/terraform-provider-google', topic='google_container_cluster')` +For CLI errors: +`mcp_context7_get_library_docs(context7CompatibleLibraryID='/websites/cloud_google_sdk', topic='container clusters get-credentials')` + +## Region Mapping +- US (default): us-central1-b +- Canada: northamerica-northeast1-a, northamerica-northeast2-a +- EU/Belgium: europe-west1-a +- UK/London: europe-west2-a +- Singapore/SEA: asia-southeast1-a +- Tokyo/Japan: asia-northeast1-a +- If user says "NOT US", prefer Canada (northamerica-northeast1-a) From 51b55cc305d2bc14db2b3e6408cb1299f32c38ac Mon Sep 17 00:00:00 2001 From: Benjamin Gervais Date: Mon, 20 Apr 2026 19:32:48 -0400 Subject: [PATCH 3/4] feat: rewrite OVH/Scaleway skills, strip Terraform, add failure-triggered skill loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rewrite OVH SKILL.md (125→305 lines): managed databases, block storage, load balancers, container registry, DNS, private networks, full RCA workflow - Rewrite Scaleway SKILL.md (132→364 lines): security groups, VPC, load balancers, serverless, secret manager, DNS, full RCA workflow - Remove Terraform/iac_tool from all 5 cloud provider skills (unused) - cloud_access.md: trigger load_skill on failure instead of suggesting it upfront - error_handling.md: add CLOUD COMMAND FAILURES rule to load skill before retry Co-Authored-By: Claude Opus 4.6 --- .../backend/agent/skills/core/cloud_access.md | 2 +- .../agent/skills/core/error_handling.md | 1 + .../agent/skills/integrations/aws/SKILL.md | 187 +------ .../agent/skills/integrations/azure/SKILL.md | 178 +------ .../agent/skills/integrations/gcp/SKILL.md | 162 +----- .../agent/skills/integrations/ovh/SKILL.md | 356 ++++++++++---- .../skills/integrations/scaleway/SKILL.md | 460 +++++++++++++----- 7 files changed, 627 insertions(+), 719 deletions(-) diff --git a/server/chat/backend/agent/skills/core/cloud_access.md b/server/chat/backend/agent/skills/core/cloud_access.md index 017a50ce8..f781f1b76 100644 --- a/server/chat/backend/agent/skills/core/cloud_access.md +++ b/server/chat/backend/agent/skills/core/cloud_access.md @@ -7,4 +7,4 @@ cloud_exec(provider, 'COMMAND') gives you full access to cloud platforms: - Scaleway: cloud_exec('scaleway', 'scw COMMAND') Authentication is automatic — never ask users for credentials or give manual console instructions. -For detailed CLI references, Terraform examples, and investigation workflows, call load_skill with the provider name (e.g., load_skill('aws')). +If a cloud command fails or returns unexpected results, call load_skill with the provider name (e.g., load_skill('aws')) to get the full CLI reference before retrying. diff --git a/server/chat/backend/agent/skills/core/error_handling.md b/server/chat/backend/agent/skills/core/error_handling.md index db9636cc7..1d29c1b3f 100644 --- a/server/chat/backend/agent/skills/core/error_handling.md +++ b/server/chat/backend/agent/skills/core/error_handling.md @@ -8,6 +8,7 @@ ERROR HANDLING & PERSISTENCE - CRITICAL: - ALWAYS explain what went wrong and suggest next steps or try alternative approaches - If you cannot resolve an error, clearly explain the issue to the user rather than ending without explanation - PROACTIVE ERROR RESOLUTION: If a diagnostic command fails, try alternative commands or data sources autonomously. However, if access is denied or authentication fails, report the issue and pivot to other available data sources rather than attempting to bypass the access control. +- CLOUD COMMAND FAILURES: If a cloud_exec command fails (wrong syntax, unknown flag, unexpected error), call load_skill with the provider name BEFORE retrying. The skill contains the correct CLI reference. Example: load_skill('aws'), load_skill('gcp'), load_skill('ovh'). - For unfamiliar errors or recent changes, use web_search to find current solutions: web_search('error message troubleshooting', 'provider', 3) - Check for breaking changes or deprecations: web_search('service deprecation breaking changes', 'provider', 2, True) - For application errors: If GitHub is connected, review application code, configuration files, and recent commits using GitHub MCP tools diff --git a/server/chat/backend/agent/skills/integrations/aws/SKILL.md b/server/chat/backend/agent/skills/integrations/aws/SKILL.md index 6502543a1..dc4eeb184 100644 --- a/server/chat/backend/agent/skills/integrations/aws/SKILL.md +++ b/server/chat/backend/agent/skills/integrations/aws/SKILL.md @@ -1,16 +1,15 @@ --- name: aws id: aws -description: "AWS integration — EC2, EKS, RDS, S3, Lambda, CloudWatch, IAM, VPC, ELB via CLI and Terraform" +description: "AWS integration — EC2, EKS, RDS, S3, Lambda, CloudWatch, IAM, VPC, ELB via CLI" category: cloud_provider connection_check: method: provider_in_preference tools: - cloud_exec - - iac_tool -index: "AWS — EC2, EKS, RDS, S3, Lambda, CloudWatch, IAM, VPC, Terraform IaC" +index: "AWS — EC2, EKS, RDS, S3, Lambda, CloudWatch, IAM, VPC" rca_priority: 10 -allowed-tools: cloud_exec, iac_tool +allowed-tools: cloud_exec metadata: author: aurora version: "2.0" @@ -20,7 +19,7 @@ metadata: ## Overview Full Amazon Web Services access via `cloud_exec('aws', 'COMMAND')`. -Available CLIs: `aws`, `kubectl`, `eksctl`, `sam`, `cdk`, `helm`, `terraform`. +Available CLIs: `aws`, `kubectl`, `eksctl`, `helm`. Authentication is automatic — never ask users for credentials. ## Multi-Account Support (CRITICAL) @@ -205,190 +204,14 @@ When investigating an AWS incident: 10. **Check networking**: Security groups, NACLs, route tables, target health 11. **Compare healthy vs unhealthy**: `kubectl top pods`, instance metrics side-by-side -## Terraform - -Use `iac_tool` — provider.tf is AUTO-GENERATED. Never write terraform{} or provider{} blocks. - -**PREREQUISITE:** Always get the account ID first: -```python -cloud_exec('aws', "sts get-caller-identity --query 'Account' --output text", account_id='') -``` - -### EC2 Instance (with AMI data source — CORRECT approach) -```hcl -data "aws_ami" "ubuntu" { - most_recent = true - filter { - name = "name" - values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] - } - filter { - name = "virtualization-type" - values = ["hvm"] - } - owners = ["099720109477"] # Canonical -} - -resource "aws_instance" "vm" { - ami = data.aws_ami.ubuntu.id - instance_type = "t3.micro" - subnet_id = "" - - tags = { - Name = "my-vm" - } -} -``` -AMI IDs are region-specific — always use `data "aws_ami"` or look up via CLI. - -### EKS Cluster (with IAM role — required) -```hcl -resource "aws_iam_role" "eks_cluster" { - name = "eks-cluster-role" - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [{ - Action = ["sts:AssumeRole", "sts:TagSession"] - Effect = "Allow" - Principal = { Service = "eks.amazonaws.com" } - }] - }) -} - -resource "aws_iam_role_policy_attachment" "eks_policy" { - policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" - role = aws_iam_role.eks_cluster.name -} - -resource "aws_eks_cluster" "cluster" { - name = "my-cluster" - role_arn = aws_iam_role.eks_cluster.arn - version = "1.31" - - vpc_config { - subnet_ids = [aws_subnet.az1.id, aws_subnet.az2.id] - } - - depends_on = [aws_iam_role_policy_attachment.eks_policy] -} - -resource "aws_eks_node_group" "nodes" { - cluster_name = aws_eks_cluster.cluster.name - node_group_name = "workers" - node_role_arn = aws_iam_role.node.arn - subnet_ids = [aws_subnet.az1.id, aws_subnet.az2.id] - - scaling_config { - desired_size = 2 - max_size = 4 - min_size = 1 - } - - depends_on = [ - aws_iam_role_policy_attachment.node_AmazonEKSWorkerNodePolicy, - aws_iam_role_policy_attachment.node_AmazonEKS_CNI_Policy, - aws_iam_role_policy_attachment.node_AmazonEC2ContainerRegistryReadOnly, - ] -} -``` - -### RDS (PostgreSQL) -```hcl -resource "aws_db_instance" "db" { - identifier = "my-db" - allocated_storage = 20 - engine = "postgres" - engine_version = "15" - instance_class = "db.t3.micro" - db_name = "mydb" - username = "admin" - manage_master_user_password = true - skip_final_snapshot = true - vpc_security_group_ids = [aws_security_group.db.id] - db_subnet_group_name = aws_db_subnet_group.main.name -} -``` - -### VPC + Subnets -```hcl -resource "aws_vpc" "main" { - cidr_block = "10.0.0.0/16" - enable_dns_hostnames = true - tags = { Name = "main-vpc" } -} - -resource "aws_subnet" "az1" { - vpc_id = aws_vpc.main.id - cidr_block = "10.0.1.0/24" - availability_zone = "us-east-1a" -} - -resource "aws_subnet" "az2" { - vpc_id = aws_vpc.main.id - cidr_block = "10.0.2.0/24" - availability_zone = "us-east-1b" -} - -resource "aws_internet_gateway" "gw" { - vpc_id = aws_vpc.main.id -} -``` - -### S3 Bucket -```hcl -resource "aws_s3_bucket" "bucket" { - bucket = "my-unique-bucket-name" -} - -resource "aws_s3_bucket_versioning" "versioning" { - bucket = aws_s3_bucket.bucket.id - versioning_configuration { - status = "Enabled" - } -} -``` - -### Security Group -```hcl -resource "aws_security_group" "web" { - name_prefix = "web-" - vpc_id = aws_vpc.main.id - - ingress { - from_port = 443 - to_port = 443 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } -} -``` - -### Common Terraform resources -`aws_instance`, `aws_security_group`, `aws_vpc`, `aws_subnet`, `aws_internet_gateway`, -`aws_eks_cluster`, `aws_eks_node_group`, `aws_s3_bucket`, `aws_db_instance`, `aws_db_subnet_group`, -`aws_lambda_function`, `aws_lb`, `aws_lb_target_group`, `aws_lb_listener`, -`aws_iam_role`, `aws_iam_policy`, `aws_iam_role_policy_attachment`, -`aws_route53_zone`, `aws_route53_record`, `aws_ecr_repository`, -`aws_ecs_cluster`, `aws_ecs_service`, `aws_ecs_task_definition` - ## Error Recovery 1. **Permission denied** → Check IAM: `iam get-role`, `iam list-attached-role-policies`, `iam simulate-principal-policy` 2. **Resource not found** → Verify region: `ec2 describe-regions`, check account_id 3. **CLI syntax** → `cloud_exec('aws', ' help')` for subcommand reference -4. **Terraform failure** → Verify resources exist with CLI, then fix manifest ### Context7 lookup on failure -For Terraform errors, query Context7 with the resource type: -`mcp_context7_get_library_docs(context7CompatibleLibraryID='/hashicorp/terraform-provider-aws', topic='aws_eks_cluster')` -For CLI errors, query: +For CLI errors: `mcp_context7_get_library_docs(context7CompatibleLibraryID='/websites/aws_amazon_cli', topic='eks update-kubeconfig')` ## Region Mapping diff --git a/server/chat/backend/agent/skills/integrations/azure/SKILL.md b/server/chat/backend/agent/skills/integrations/azure/SKILL.md index b7ae662ab..1806a3129 100644 --- a/server/chat/backend/agent/skills/integrations/azure/SKILL.md +++ b/server/chat/backend/agent/skills/integrations/azure/SKILL.md @@ -1,16 +1,15 @@ --- name: azure id: azure -description: "Azure integration — VMs, AKS, SQL, Storage, App Service, Monitor, NSG, VNet via CLI and Terraform" +description: "Azure integration — VMs, AKS, SQL, Storage, App Service, Monitor, NSG, VNet via CLI" category: cloud_provider connection_check: method: provider_in_preference tools: - cloud_exec - - iac_tool -index: "Azure — VMs, AKS, SQL, Storage, App Service, Monitor, Terraform IaC" +index: "Azure — VMs, AKS, SQL, Storage, App Service, Monitor" rca_priority: 10 -allowed-tools: cloud_exec, iac_tool +allowed-tools: cloud_exec metadata: author: aurora version: "2.0" @@ -31,8 +30,6 @@ Authentication and subscription are auto-configured — never ask users for cred ## Subscription Setup - Get subscription ID: `cloud_exec('azure', "account show --query 'id' -o tsv")` -- ALWAYS get this before writing Terraform. - ## CLI Reference ### Discovery @@ -232,172 +229,6 @@ When investigating an Azure incident: 12. **Check node health**: `kubectl describe node`, `kubectl top nodes`, node pool status 13. **Compare healthy vs unhealthy**: Pod metrics, log patterns side-by-side -## Terraform - -Use `iac_tool` — provider.tf is AUTO-GENERATED. Never write terraform{} or provider{} blocks. - -**PREREQUISITE:** Get subscription ID first: -```python -cloud_exec('azure', "account show --query 'id' -o tsv") -``` - -**VM passwords are auto-generated** by the system using Terraform's `random_password` resource. Never ask users for passwords. - -### Resource Group (always first) -```hcl -resource "azurerm_resource_group" "rg" { - name = "my-rg" - location = "eastus" -} -``` - -### Linux Virtual Machine -```hcl -resource "azurerm_virtual_network" "vnet" { - name = "my-vnet" - address_space = ["10.0.0.0/16"] - location = azurerm_resource_group.rg.location - resource_group_name = azurerm_resource_group.rg.name -} - -resource "azurerm_subnet" "subnet" { - name = "my-subnet" - resource_group_name = azurerm_resource_group.rg.name - virtual_network_name = azurerm_virtual_network.vnet.name - address_prefixes = ["10.0.1.0/24"] -} - -resource "azurerm_network_interface" "nic" { - name = "my-nic" - location = azurerm_resource_group.rg.location - resource_group_name = azurerm_resource_group.rg.name - - ip_configuration { - name = "internal" - subnet_id = azurerm_subnet.subnet.id - private_ip_address_allocation = "Dynamic" - } -} - -resource "azurerm_linux_virtual_machine" "vm" { - name = "my-vm" - resource_group_name = azurerm_resource_group.rg.name - location = azurerm_resource_group.rg.location - size = "Standard_B2s" - admin_username = "adminuser" - network_interface_ids = [azurerm_network_interface.nic.id] - - admin_ssh_key { - username = "adminuser" - public_key = file("~/.ssh/id_rsa.pub") - } - - os_disk { - caching = "ReadWrite" - storage_account_type = "Standard_LRS" - } - - source_image_reference { - publisher = "Canonical" - offer = "0001-com-ubuntu-server-jammy" - sku = "22_04-lts" - version = "latest" - } -} -``` - -### AKS Cluster -```hcl -resource "azurerm_kubernetes_cluster" "aks" { - name = "my-aks" - location = azurerm_resource_group.rg.location - resource_group_name = azurerm_resource_group.rg.name - dns_prefix = "myaks" - - default_node_pool { - name = "default" - node_count = 3 - vm_size = "Standard_DS2_v2" - } - - identity { - type = "SystemAssigned" - } - - tags = { - Environment = "Production" - } -} -``` - -### Storage Account with Network Rules -```hcl -resource "azurerm_storage_account" "storage" { - name = "mystorageaccount" - resource_group_name = azurerm_resource_group.rg.name - location = azurerm_resource_group.rg.location - account_tier = "Standard" - account_replication_type = "LRS" - - network_rules { - default_action = "Deny" - ip_rules = ["100.0.0.1"] - } -} -``` - -### SQL Server + Database -```hcl -resource "azurerm_mssql_server" "sql" { - name = "my-sql-server" - resource_group_name = azurerm_resource_group.rg.name - location = azurerm_resource_group.rg.location - version = "12.0" - administrator_login = "sqladmin" - administrator_login_password = random_password.sql.result -} - -resource "azurerm_mssql_database" "db" { - name = "my-database" - server_id = azurerm_mssql_server.sql.id - sku_name = "S0" -} -``` - -### Network Security Group -```hcl -resource "azurerm_network_security_group" "nsg" { - name = "my-nsg" - location = azurerm_resource_group.rg.location - resource_group_name = azurerm_resource_group.rg.name - - security_rule { - name = "AllowHTTPS" - priority = 100 - direction = "Inbound" - access = "Allow" - protocol = "Tcp" - source_port_range = "*" - destination_port_range = "443" - source_address_prefix = "*" - destination_address_prefix = "*" - } -} -``` - -### Common Terraform resources -`azurerm_resource_group`, `azurerm_virtual_network`, `azurerm_subnet`, -`azurerm_network_interface`, `azurerm_network_security_group`, -`azurerm_linux_virtual_machine`, `azurerm_windows_virtual_machine`, -`azurerm_kubernetes_cluster`, `azurerm_kubernetes_cluster_node_pool`, -`azurerm_storage_account`, `azurerm_storage_container`, -`azurerm_mssql_server`, `azurerm_mssql_database`, -`azurerm_service_plan`, `azurerm_linux_web_app`, -`azurerm_lb`, `azurerm_lb_backend_address_pool`, `azurerm_lb_rule`, -`azurerm_public_ip`, `azurerm_container_registry`, -`azurerm_key_vault`, `azurerm_key_vault_secret`, -`azurerm_dns_zone`, `azurerm_dns_a_record` - ## Error Recovery 1. **Provider not registered** → `cloud_exec('azure', 'provider register --namespace Microsoft.ContainerService')` — common: Microsoft.ContainerService, Microsoft.Sql, Microsoft.Storage, Microsoft.Web, Microsoft.Network @@ -405,11 +236,8 @@ resource "azurerm_network_security_group" "nsg" { 3. **Permission denied** → Check role: `cloud_exec('azure', 'role assignment list --assignee --output table')` 4. **CLI syntax** → `cloud_exec('azure', ' --help')` for subcommand reference 5. **Storage name taken** → Storage account names must be globally unique, 3-24 chars, lowercase alphanumeric only -6. **Terraform failure** → Verify resources with CLI, then fix manifest ### Context7 lookup on failure -For Terraform errors: -`mcp_context7_get_library_docs(context7CompatibleLibraryID='/hashicorp/terraform-provider-azurerm', topic='azurerm_kubernetes_cluster')` For CLI errors: `mcp_context7_get_library_docs(context7CompatibleLibraryID='/microsoftdocs/azure-docs-cli', topic='aks get-credentials')` diff --git a/server/chat/backend/agent/skills/integrations/gcp/SKILL.md b/server/chat/backend/agent/skills/integrations/gcp/SKILL.md index 7316c4fcb..7cbf4b9a9 100644 --- a/server/chat/backend/agent/skills/integrations/gcp/SKILL.md +++ b/server/chat/backend/agent/skills/integrations/gcp/SKILL.md @@ -1,16 +1,15 @@ --- name: gcp id: gcp -description: "GCP integration — Compute Engine, GKE, Cloud SQL, Cloud Storage, Cloud Run, Cloud Logging, IAM via CLI and Terraform" +description: "GCP integration — Compute Engine, GKE, Cloud SQL, Cloud Storage, Cloud Run, Cloud Logging, IAM via CLI" category: cloud_provider connection_check: method: provider_in_preference tools: - cloud_exec - - iac_tool -index: "GCP — Compute Engine, GKE, Cloud SQL, Cloud Storage, Cloud Run, Logging, Terraform IaC" +index: "GCP — Compute Engine, GKE, Cloud SQL, Cloud Storage, Cloud Run, Logging" rca_priority: 10 -allowed-tools: cloud_exec, iac_tool +allowed-tools: cloud_exec metadata: author: aurora version: "2.0" @@ -27,7 +26,6 @@ Authentication and project are auto-configured — never ask users for credentia - Get current project: `cloud_exec('gcp', 'config get-value project')` - Set project explicitly: `cloud_exec('gcp', 'config set project ')` - If user specifies a project, set it. Otherwise fetch the current one and reuse it everywhere. -- ALWAYS get the project ID before writing Terraform. ## CLI Reference @@ -222,168 +220,14 @@ When investigating a GCP incident: 11. **Check node health**: `kubectl describe node`, `kubectl top nodes` 12. **Compare healthy vs unhealthy**: Side-by-side pod metrics and logs -## Terraform - -Use `iac_tool` — provider.tf is AUTO-GENERATED. Never write terraform{} or provider{} blocks. - -**PREREQUISITE:** Always get project ID first: -```python -cloud_exec('gcp', 'config get-value project') -``` - -### Compute Instance -```hcl -resource "google_service_account" "default" { - account_id = "my-custom-sa" - display_name = "Custom SA for VM" -} - -resource "google_compute_instance" "vm" { - name = "my-instance" - machine_type = "e2-medium" - zone = "us-central1-b" - - boot_disk { - initialize_params { - image = "debian-cloud/debian-12" - } - } - - network_interface { - network = "default" - access_config {} - } - - service_account { - email = google_service_account.default.email - scopes = ["cloud-platform"] - } -} -``` - -### GKE Cluster -```hcl -resource "google_container_cluster" "primary" { - name = "my-cluster" - location = "us-central1" - - initial_node_count = 1 - remove_default_node_pool = true -} - -resource "google_container_node_pool" "primary_nodes" { - name = "primary-pool" - location = "us-central1" - cluster = google_container_cluster.primary.name - node_count = 3 - - node_config { - machine_type = "e2-medium" - oauth_scopes = ["https://www.googleapis.com/auth/cloud-platform"] - } - - autoscaling { - min_node_count = 1 - max_node_count = 5 - } -} -``` - -### Cloud SQL -```hcl -resource "google_sql_database_instance" "db" { - name = "my-db" - database_version = "POSTGRES_15" - region = "us-central1" - - settings { - tier = "db-f1-micro" - availability_type = "ZONAL" - - backup_configuration { - enabled = true - } - } - - deletion_protection = false -} - -resource "google_sql_database" "database" { - name = "mydb" - instance = google_sql_database_instance.db.name -} -``` - -### Cloud Storage -```hcl -resource "google_storage_bucket" "bucket" { - name = "my-unique-bucket-name" - location = "US" - - versioning { - enabled = true - } - - lifecycle_rule { - condition { - age = 30 - } - action { - type = "Delete" - } - } -} -``` - -### VPC Network -```hcl -resource "google_compute_network" "vpc" { - name = "my-vpc" - auto_create_subnetworks = false -} - -resource "google_compute_subnetwork" "subnet" { - name = "my-subnet" - ip_cidr_range = "10.0.0.0/24" - region = "us-central1" - network = google_compute_network.vpc.id -} - -resource "google_compute_firewall" "allow_ssh" { - name = "allow-ssh" - network = google_compute_network.vpc.name - - allow { - protocol = "tcp" - ports = ["22"] - } - - source_ranges = ["0.0.0.0/0"] - target_tags = ["ssh-enabled"] -} -``` - -### Common Terraform resources -`google_compute_instance`, `google_compute_firewall`, `google_compute_network`, `google_compute_subnetwork`, -`google_container_cluster`, `google_container_node_pool`, -`google_storage_bucket`, `google_sql_database_instance`, `google_sql_database`, -`google_cloud_run_v2_service`, `google_cloudfunctions_function`, -`google_compute_global_forwarding_rule`, `google_compute_health_check`, -`google_project_iam_member`, `google_service_account`, -`google_pubsub_topic`, `google_pubsub_subscription`, -`google_artifact_registry_repository`, `google_secret_manager_secret` - ## Error Recovery 1. **API not enabled** → `cloud_exec('gcp', 'services enable .googleapis.com')` — common: container.googleapis.com, sqladmin.googleapis.com, run.googleapis.com, cloudfunctions.googleapis.com 2. **Permission denied** → Check IAM: `projects get-iam-policy `, verify service account roles 3. **Beta feature** → Prefix with beta: `cloud_exec('gcp', 'beta ')` 4. **CLI syntax** → `cloud_exec('gcp', ' --help')` for subcommand reference -5. **Terraform failure** → Verify with CLI, then fix manifest ### Context7 lookup on failure -For Terraform errors: -`mcp_context7_get_library_docs(context7CompatibleLibraryID='/hashicorp/terraform-provider-google', topic='google_container_cluster')` For CLI errors: `mcp_context7_get_library_docs(context7CompatibleLibraryID='/websites/cloud_google_sdk', topic='container clusters get-credentials')` diff --git a/server/chat/backend/agent/skills/integrations/ovh/SKILL.md b/server/chat/backend/agent/skills/integrations/ovh/SKILL.md index 58f0d2cc5..04647a0a2 100644 --- a/server/chat/backend/agent/skills/integrations/ovh/SKILL.md +++ b/server/chat/backend/agent/skills/integrations/ovh/SKILL.md @@ -1,125 +1,305 @@ --- name: ovh id: ovh -description: "OVHcloud infrastructure integration for managing instances, Kubernetes clusters, networks, and object storage via CLI and Terraform" +description: "OVHcloud integration — instances, MKS Kubernetes, managed databases, object storage, private networks via CLI" category: cloud_provider connection_check: method: provider_in_preference tools: - cloud_exec - - iac_tool -index: "OVHcloud — instances, MKS Kubernetes, networks, S3 storage, Terraform IaC" +index: "OVHcloud — instances, MKS Kubernetes, managed databases, object storage, private networks" rca_priority: 10 -allowed-tools: cloud_exec, iac_tool +allowed-tools: cloud_exec metadata: author: aurora - version: "1.0" + version: "2.0" --- # OVHcloud Integration ## Overview -OVHcloud infrastructure provider for managing compute instances, Managed Kubernetes Service (MKS), networks, and object storage. +Full OVHcloud Public Cloud access via `cloud_exec('ovh', 'COMMAND')`. +Available CLI: `ovhcloud` (aliased through cloud_exec). +Authentication is automatic — never ask users for credentials. -## Instructions +## Project Context (CRITICAL) +Most OVH commands require `--cloud-project `: +```python +cloud_exec('ovh', 'cloud project list --json') +``` +Get the project ID first, then use it on every subsequent command. + +## CLI Reference -### CLI COMMANDS (use cloud_exec with 'ovh') +### Discovery +```python +cloud_exec('ovh', 'cloud project list --json') +cloud_exec('ovh', 'cloud region list --cloud-project --json') +cloud_exec('ovh', 'cloud reference list-flavors --cloud-project --region --json') +cloud_exec('ovh', 'cloud reference list-images --cloud-project --region --json') +``` -**Discovery Commands:** -- List projects: `cloud_exec('ovh', 'cloud project list --json')` -- List regions: `cloud_exec('ovh', 'cloud region list --cloud-project --json')` -- List flavors: `cloud_exec('ovh', 'cloud reference list-flavors --cloud-project --region --json')` -- List images: `cloud_exec('ovh', 'cloud reference list-images --cloud-project --region --json')` +### Compute Instances +```python +# List all instances: +cloud_exec('ovh', 'cloud instance list --cloud-project --json') +# Get instance details: +cloud_exec('ovh', 'cloud instance get --cloud-project --json') +# Create instance (region is POSITIONAL): +cloud_exec('ovh', 'cloud instance create --cloud-project --name --boot-from.image --flavor --network.public --wait --json') +# Create with SSH key: +cloud_exec('ovh', 'cloud instance create --cloud-project --name --boot-from.image --flavor --ssh-key.create.name my-key --ssh-key.create.public-key "" --network.public --wait --json') +# Create on private network: +cloud_exec('ovh', 'cloud instance create --cloud-project --name --boot-from.image --flavor --network.private --wait --json') +# With auto-backup: +cloud_exec('ovh', 'cloud instance create --cloud-project --name --boot-from.image --flavor --auto-backup.cron "0 1 * * *" --network.public --wait --json') +# With user data: +cloud_exec('ovh', 'cloud instance create --cloud-project --name --boot-from.image --flavor --user-data @/path/to/cloud-init.yaml --network.public --wait --json') +# Start/Stop/Reboot: +cloud_exec('ovh', 'cloud instance start --cloud-project ') +cloud_exec('ovh', 'cloud instance stop --cloud-project ') +cloud_exec('ovh', 'cloud instance reboot --cloud-project ') +# Soft vs hard reboot: +cloud_exec('ovh', 'cloud instance reboot --cloud-project --type soft') +cloud_exec('ovh', 'cloud instance reboot --cloud-project --type hard') +# Resize: +cloud_exec('ovh', 'cloud instance resize --cloud-project --flavor ') +# Rebuild (reinstall OS): +cloud_exec('ovh', 'cloud instance rebuild --cloud-project --image ') +# Delete: +cloud_exec('ovh', 'cloud instance delete --cloud-project ') +# Console URL (for debugging boot issues): +cloud_exec('ovh', 'cloud instance vnc --cloud-project --json') +# Instance logs (serial console output): +cloud_exec('ovh', 'cloud instance logs --cloud-project ') +``` -**Instance Management:** -- List instances: `cloud_exec('ovh', 'cloud instance list --cloud-project --json')` -- Create instance: `cloud_exec('ovh', 'cloud instance create --cloud-project --name --boot-from.image --flavor --network.public --wait --json')` -- With SSH key: `cloud_exec('ovh', 'cloud instance create --cloud-project --name --boot-from.image --flavor --ssh-key.create.name my-key --ssh-key.create.public-key "" --network.public --wait --json')` -- Stop/Start/Reboot: `cloud_exec('ovh', 'cloud instance stop|start|reboot --cloud-project ')` -- Delete: `cloud_exec('ovh', 'cloud instance delete --cloud-project ')` +Common flavors (query first — UUIDs are region-specific): +- General: `b2-7` (2 vCPU, 7GB), `b2-15` (4 vCPU, 15GB), `b2-30` (8 vCPU, 30GB), `b2-60` (16 vCPU, 60GB) +- CPU: `c2-7`, `c2-15`, `c2-30`, `c2-60` +- RAM: `r2-15`, `r2-30`, `r2-60`, `r2-120` +- GPU: `t1-45`, `t1-90`, `t2-45`, `t2-90` -**Kubernetes (MKS):** -- List clusters: `cloud_exec('ovh', 'cloud kube list --cloud-project --json')` -- Create cluster: `cloud_exec('ovh', 'cloud kube create --cloud-project --name --region --version 1.28')` -- Get kubeconfig: `cloud_exec('ovh', 'cloud kube kubeconfig generate --cloud-project ')` -- Create nodepool: `cloud_exec('ovh', 'cloud kube nodepool create --cloud-project --name worker-pool --flavor b2-7 --desired-nodes 3 --autoscale true')` +### SSH Keys +```python +cloud_exec('ovh', 'cloud ssh-key list --cloud-project --json') +cloud_exec('ovh', 'cloud ssh-key create --cloud-project --name --public-key ""') +cloud_exec('ovh', 'cloud ssh-key get --cloud-project --json') +cloud_exec('ovh', 'cloud ssh-key delete --cloud-project ') +``` + +### Managed Kubernetes Service (MKS) +```python +# List clusters: +cloud_exec('ovh', 'cloud kube list --cloud-project --json') +# Get cluster details: +cloud_exec('ovh', 'cloud kube get --cloud-project --json') +# Create cluster: +cloud_exec('ovh', 'cloud kube create --cloud-project --name --region --version 1.31') +# Get kubeconfig (CRITICAL: use output_file to avoid shell escaping): +cloud_exec('ovh', 'cloud kube kubeconfig generate --cloud-project ', output_file='/tmp/kubeconfig.yaml') +# Node pools: +cloud_exec('ovh', 'cloud kube nodepool list --cloud-project --json') +cloud_exec('ovh', 'cloud kube nodepool get --cloud-project --json') +cloud_exec('ovh', 'cloud kube nodepool create --cloud-project --name worker-pool --flavor b2-7 --desired-nodes 3 --autoscale true') +# With min/max autoscaling: +cloud_exec('ovh', 'cloud kube nodepool create --cloud-project --name worker-pool --flavor b2-7 --desired-nodes 3 --min-nodes 1 --max-nodes 10 --autoscale true') +cloud_exec('ovh', 'cloud kube nodepool scale --cloud-project --desired-nodes 5') +cloud_exec('ovh', 'cloud kube nodepool delete --cloud-project ') +# Update cluster version: +cloud_exec('ovh', 'cloud kube update --cloud-project --version 1.31') +# Reset kubeconfig: +cloud_exec('ovh', 'cloud kube kubeconfig reset --cloud-project ') +``` -**KUBECTL WORKFLOW (for OVH clusters):** +**KUBECTL WORKFLOW (for OVH MKS clusters):** 1. Save kubeconfig to file: `cloud_exec('ovh', 'cloud kube kubeconfig generate --cloud-project ', output_file='/tmp/kubeconfig.yaml')` 2. Run kubectl: `terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get pods -A')` -3. CRITICAL: Use output_file parameter to save kubeconfig directly - avoids shell escaping issues -4. Do NOT try to embed kubeconfig YAML in echo commands - it will break due to special characters - -**Networks:** -- List networks: `cloud_exec('ovh', 'cloud network list --cloud-project --json')` -- Create network: `cloud_exec('ovh', 'cloud network create --cloud-project --name --vlan-id --regions ')` - -**Object Storage (S3):** -- List S3 users: `cloud_exec('ovh', 'cloud storage-s3 list --cloud-project --json')` -- Create S3 user: `cloud_exec('ovh', 'cloud storage-s3 create --cloud-project --region ')` - -### TERRAFORM FOR OVH -Use iac_tool - provider.tf is AUTO-GENERATED, just write the resource! - -**INSTANCE EXAMPLE (MUST use nested blocks, NOT flat attributes):** -```hcl -resource "ovh_cloud_project_instance" "vm" { - service_name = "" - region = "US-EAST-VA-1" - billing_period = "hourly" - name = "my-vm" - flavor { - flavor_id = "" - } - boot_from { - image_id = "" - } - network { - public = true - } - # SSH key options (use ONE): - # Option 1: Reference existing SSH key by name - ssh_key { - name = "my-ssh-key" # Must exist in OVH first - } - # Option 2: Create new SSH key inline - # ssh_key_create { - # name = "my-new-key" - # public_key = "ssh-rsa AAAA..." - # } -} +3. CRITICAL: Use output_file parameter to save kubeconfig — avoids shell escaping issues with YAML +4. Do NOT try to embed kubeconfig YAML in echo commands — it will break + +```python +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get pods -n -o wide') +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml describe pod -n ') +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml logs -n --since=1h --tail=200') +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml logs -n -c --previous') +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get events -n --sort-by=.lastTimestamp') +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml top pods -n ') +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml top nodes') +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get hpa -n ') +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get deployments -n ') +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml rollout history deployment/ -n ') +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get pvc -n ') +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get svc -n ') +terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get ingress -n ') +``` + +### Managed Databases +```python +# List database services (all engines): +cloud_exec('ovh', 'cloud database list --cloud-project --json') +# Supported engines: postgresql, mysql, mongodb, redis, kafka, cassandra, opensearch +# Create database: +cloud_exec('ovh', 'cloud database create --cloud-project --name --region --version --plan --flavor --json') +# Example PostgreSQL: +cloud_exec('ovh', 'cloud database create postgresql --cloud-project --name my-pg --region GRA --version 15 --plan essential --flavor db1-4 --json') +# Example Kafka: +cloud_exec('ovh', 'cloud database create kafka --cloud-project --name my-kafka --region GRA --version 3.4 --plan business --flavor db1-7 --json') +# Get database details: +cloud_exec('ovh', 'cloud database get --cloud-project --json') +# List nodes: +cloud_exec('ovh', 'cloud database node list --cloud-project --json') +# Users: +cloud_exec('ovh', 'cloud database user list --cloud-project --json') +cloud_exec('ovh', 'cloud database user create --cloud-project --name --json') +# IP restrictions (IMPORTANT for connectivity issues): +cloud_exec('ovh', 'cloud database ip-restriction list --cloud-project --json') +cloud_exec('ovh', 'cloud database ip-restriction add --cloud-project --ip ') +cloud_exec('ovh', 'cloud database ip-restriction delete --cloud-project --ip ') +# Backups: +cloud_exec('ovh', 'cloud database backup list --cloud-project --json') +# Logs: +cloud_exec('ovh', 'cloud database log list --cloud-project --json') +# Metrics: +cloud_exec('ovh', 'cloud database metric list --cloud-project --json') +# Delete: +cloud_exec('ovh', 'cloud database delete --cloud-project ') +``` + +Plans: `essential` (single node), `business` (HA, 2+ nodes), `enterprise` (dedicated, HA) +Common flavors: `db1-4` (4GB RAM), `db1-7` (7GB RAM), `db1-15` (15GB RAM), `db1-30` (30GB RAM) + +### Object Storage (S3-compatible) +```python +# S3 users: +cloud_exec('ovh', 'cloud storage-s3 list --cloud-project --json') +cloud_exec('ovh', 'cloud storage-s3 create --cloud-project --region ') +cloud_exec('ovh', 'cloud storage-s3 get --cloud-project --json') +# Legacy object storage containers: +cloud_exec('ovh', 'cloud storage container list --cloud-project --json') +cloud_exec('ovh', 'cloud storage container create --cloud-project --name --region ') +cloud_exec('ovh', 'cloud storage container get --cloud-project --name --json') +cloud_exec('ovh', 'cloud storage container delete --cloud-project --name ') +# Object operations: +cloud_exec('ovh', 'cloud storage container object list --cloud-project --name ') ``` -**SSH KEY IMPORTANT:** Use `ssh_key` to reference existing key, or `ssh_key_create` to create new one inline. If unsure, query Context7 with topic='ovh_cloud_project_instance ssh_key'. +### Block Storage (Volumes) +```python +cloud_exec('ovh', 'cloud volume list --cloud-project --json') +cloud_exec('ovh', 'cloud volume get --cloud-project --json') +cloud_exec('ovh', 'cloud volume create --cloud-project --name --region --size --type high-speed') +cloud_exec('ovh', 'cloud volume attach --cloud-project --instance-id ') +cloud_exec('ovh', 'cloud volume detach --cloud-project --instance-id ') +cloud_exec('ovh', 'cloud volume upsize --cloud-project --size ') +cloud_exec('ovh', 'cloud volume delete --cloud-project ') +# Snapshots: +cloud_exec('ovh', 'cloud volume snapshot list --cloud-project --json') +cloud_exec('ovh', 'cloud volume snapshot create --cloud-project --name my-snap') +``` + +Volume types: `classic` (HDD), `high-speed` (SSD), `high-speed-gen2` (NVMe) + +### Private Networks +```python +cloud_exec('ovh', 'cloud network list --cloud-project --json') +cloud_exec('ovh', 'cloud network get --cloud-project --json') +cloud_exec('ovh', 'cloud network create --cloud-project --name --vlan-id --regions ') +cloud_exec('ovh', 'cloud network subnet list --cloud-project --json') +cloud_exec('ovh', 'cloud network subnet create --cloud-project --region --start --end --network ') +cloud_exec('ovh', 'cloud network delete --cloud-project ') +``` + +### Load Balancers +```python +cloud_exec('ovh', 'cloud loadbalancer list --cloud-project --json') +cloud_exec('ovh', 'cloud loadbalancer get --cloud-project --json') +cloud_exec('ovh', 'cloud loadbalancer create --cloud-project --name --region --flavor small --json') +cloud_exec('ovh', 'cloud loadbalancer delete --cloud-project ') +``` + +### DNS Zones +```python +cloud_exec('ovh', 'domain zone list --json') +cloud_exec('ovh', 'domain zone record list --json') +cloud_exec('ovh', 'domain zone record create --type A --target --subdomain --ttl 3600') +cloud_exec('ovh', 'domain zone record delete ') +cloud_exec('ovh', 'domain zone refresh ') +``` + +### Container Registry +```python +cloud_exec('ovh', 'cloud registry list --cloud-project --json') +cloud_exec('ovh', 'cloud registry get --cloud-project --json') +cloud_exec('ovh', 'cloud registry create --cloud-project --name --region --plan small') +cloud_exec('ovh', 'cloud registry user list --cloud-project --json') +cloud_exec('ovh', 'cloud registry user create --cloud-project --login --json') +``` + +### Instance Logs & Debugging +OVH does not have centralized logging like CloudWatch or Cloud Logging. +- For instances: check serial console via `cloud instance logs `, or SSH in and check syslog/journalctl +- For Kubernetes pods: use kubectl logs +- For databases: use `cloud database log list` +- For application logs: check Logs Data Platform if configured, or pod logs + +## RCA / Investigation Workflow -**Other resources:** `ovh_cloud_project_kube`, `ovh_cloud_project_kube_nodepool`, `ovh_cloud_project_database` +When investigating an OVH incident: -DO NOT write terraform{} or provider{} blocks - they are auto-generated! +1. **Get project context**: `cloud project list --json` — identify the project +2. **Check instance state**: `cloud instance list --cloud-project --json` — look for ERROR/STOPPED status +3. **Get instance details**: `cloud instance get --cloud-project --json` — check flavor, image, network +4. **Check serial console**: `cloud instance logs --cloud-project ` — boot issues, kernel panics +5. **Get MKS credentials** (if K8s): Save kubeconfig via `output_file`, then use kubectl +6. **Check pods/containers**: `kubectl get pods -o wide`, `kubectl describe pod`, `kubectl logs` +7. **Check K8s events**: `kubectl get events --sort-by=.lastTimestamp` +8. **Check node health**: `kubectl top nodes`, `kubectl describe node` +9. **Check node pools**: `cloud kube nodepool list ` — verify nodes are READY +10. **Check databases**: `cloud database get ` — status, node health +11. **Check DB logs**: `cloud database log list ` — recent errors +12. **Check DB IP restrictions**: `cloud database ip-restriction list` — connectivity issues often caused by missing IP allowlist +13. **Check networking**: `cloud network list` — verify private network connectivity +14. **Check volumes**: `cloud volume list` — verify attached volumes, check for full disks +15. **Check recent changes**: `kubectl rollout history`, database version upgrades +16. **Compare healthy vs unhealthy**: Pod metrics and logs side-by-side -### CRITICAL RULES -- Use **UUID** from 'id' field for flavor/image, NOT names! -- Use `--cloud-project ` NOT `--project-id` +## Critical Rules +- Use **UUID** from `id` field for flavor/image, NOT names — always query flavors/images first +- Use `--cloud-project ` on EVERY command (NOT `--project-id`) - Region is POSITIONAL in create commands: `cloud instance create ...` - Use `kube` NOT `kubernetes` subcommand -- Use `--network.public` for public IP (not `--network `) +- Use `--network.public` for public IP (NOT `--network `) +- Kubeconfig MUST use `output_file` parameter — NEVER echo kubeconfig YAML in shell +- Always query flavors/images/regions BEFORE creating resources — they are region-specific -### DYNAMIC/RUNTIME DATA (versions, flavors, images) -Context7 docs do NOT contain runtime data. For dynamic values, use CLI: -- **K8s versions**: For Terraform, omit `version` to use latest stable, or use `1.31`, `1.32` (check `cloud kube create --help` for valid versions) -- **Flavors**: `cloud_exec('ovh', 'cloud reference list-flavors --cloud-project --region --json')` -- **Images**: `cloud_exec('ovh', 'cloud reference list-images --cloud-project --region --json')` -- **Regions**: `cloud_exec('ovh', 'cloud region list --cloud-project --json')` -- Always query flavors/images/regions BEFORE creating resources. +### Dynamic Data +Context7 docs do NOT contain runtime data. For dynamic values, always use CLI: +- **K8s versions**: `cloud kube create --help` +- **Flavors**: `cloud reference list-flavors --cloud-project --region --json` +- **Images**: `cloud reference list-images --cloud-project --region --json` +- **Regions**: `cloud region list --cloud-project --json` +- **DB plans/flavors**: Check engine-specific availability per region -### MANDATORY: ON ANY OVH ERROR OR FAILURE -**YOU MUST** use Context7 MCP to look up correct syntax BEFORE retrying. Choose the RIGHT library: +## Error Recovery -**If `iac_tool` (Terraform) fails** -- Use TERRAFORM docs: -`mcp_context7_get_library_docs(context7CompatibleLibraryID='/ovh/terraform-provider-ovh', topic='ovh_cloud_project_instance')` -Topic should be the **resource type** (e.g., 'ovh_cloud_project_instance', 'ovh_cloud_project_kube', 'ssh_key block') +1. **Project not found** → Verify project ID: `cloud project list --json` +2. **Resource not found** → Check region — resources are region-specific +3. **Flavor/image UUID wrong** → Re-query: `cloud reference list-flavors`, `cloud reference list-images` +4. **Database connection refused** → Check IP restrictions: `cloud database ip-restriction list` +5. **Network connectivity** → Verify private network and subnet configuration +6. **CLI syntax** → `cloud_exec('ovh', ' --help')` for subcommand reference -**If `cloud_exec` (CLI) fails** -- Use CLI docs: +### Context7 lookup on failure +For CLI errors: `mcp_context7_get_library_docs(context7CompatibleLibraryID='/ovh/ovhcloud-cli', topic='cloud instance create')` -Topic should be the **CLI command** (e.g., 'cloud instance create', 'cloud kube list') -Do NOT mix them up! Terraform errors need Terraform docs, CLI errors need CLI docs. +## Region Mapping +- EU/France (default): GRA (Gravelines), SBG (Strasbourg), RBX (Roubaix) +- EU/Germany: DE1 (Frankfurt) +- EU/UK: UK1 (London) +- EU/Poland: WAW1 (Warsaw) +- US East: US-EAST-VA-1 (Virginia) +- US West: US-WEST-OR-1 (Oregon/Hillsboro) +- Canada: CA-EAST-BHS-1 (Beauharnois) +- Asia-Pacific: AP-SOUTH-SGP-1 (Singapore), AP-SOUTH-SYD-1 (Sydney) diff --git a/server/chat/backend/agent/skills/integrations/scaleway/SKILL.md b/server/chat/backend/agent/skills/integrations/scaleway/SKILL.md index 37bcfc46d..fa4c25b4c 100644 --- a/server/chat/backend/agent/skills/integrations/scaleway/SKILL.md +++ b/server/chat/backend/agent/skills/integrations/scaleway/SKILL.md @@ -1,132 +1,364 @@ --- name: scaleway id: scaleway -description: "Scaleway cloud integration for managing instances, Kapsule Kubernetes clusters, object storage, and managed databases via CLI and Terraform" +description: "Scaleway integration — instances, Kapsule Kubernetes, managed databases, object storage, VPC, serverless, load balancers via CLI" category: cloud_provider connection_check: method: provider_in_preference tools: - cloud_exec - - iac_tool -index: "Scaleway — instances, Kapsule Kubernetes, object storage, managed databases, Terraform IaC" +index: "Scaleway — instances, Kapsule Kubernetes, managed databases, object storage, VPC, serverless" rca_priority: 10 -allowed-tools: cloud_exec, iac_tool +allowed-tools: cloud_exec metadata: author: aurora - version: "1.0" + version: "2.0" --- # Scaleway Integration ## Overview -Scaleway cloud provider for managing compute instances, Kapsule Kubernetes clusters, object storage, and managed databases. - -## Instructions - -### CLI COMMANDS (use cloud_exec with 'scaleway') - -**CRITICAL: Always use cloud_exec('scaleway', 'command') for Scaleway commands, NOT terminal_exec!** -The cloud_exec tool has your Scaleway credentials configured. - -**Discovery Commands:** -- List projects: `cloud_exec('scaleway', 'account project list')` -- List zones: `cloud_exec('scaleway', 'instance zone list')` -- List instance types: `cloud_exec('scaleway', 'instance server-type list')` -- List images: `cloud_exec('scaleway', 'instance image list')` - -**Instance Management:** -- List instances: `cloud_exec('scaleway', 'instance server list')` -- Create instance: `cloud_exec('scaleway', 'instance server create type=DEV1-S image=ubuntu_jammy name=my-vm')` -- With zone: `cloud_exec('scaleway', 'instance server create type=DEV1-S image=ubuntu_jammy name=my-vm zone=fr-par-1')` -- Start/Stop/Reboot: `cloud_exec('scaleway', 'instance server start|stop|reboot ')` -- Delete: `cloud_exec('scaleway', 'instance server delete ')` -- SSH into server: `cloud_exec('scaleway', 'instance server ssh ')` - -**Kubernetes (Kapsule):** -- List clusters: `cloud_exec('scaleway', 'k8s cluster list')` -- Create cluster: `cloud_exec('scaleway', 'k8s cluster create name=my-cluster version=1.28 cni=cilium')` -- Get kubeconfig: `cloud_exec('scaleway', 'k8s kubeconfig get ')` -- List pools: `cloud_exec('scaleway', 'k8s pool list cluster-id=')` -- Create pool: `cloud_exec('scaleway', 'k8s pool create cluster-id= name=worker-pool node-type=DEV1-M size=3')` - -**Object Storage:** -- List buckets: `cloud_exec('scaleway', 'object bucket list')` -- Create bucket: `cloud_exec('scaleway', 'object bucket create name=my-bucket')` - -**Databases:** -- List instances: `cloud_exec('scaleway', 'rdb instance list')` -- Create instance: `cloud_exec('scaleway', 'rdb instance create name=my-db engine=PostgreSQL-15 node-type=DB-DEV-S')` - -### TERRAFORM FOR SCALEWAY -Use iac_tool - provider.tf is AUTO-GENERATED, just write the resource! -Scaleway Terraform provider: https://registry.terraform.io/providers/scaleway/scaleway/latest/docs - -**INSTANCE EXAMPLE:** -```hcl -resource "scaleway_instance_server" "vm" { - name = "my-vm" - type = "DEV1-S" - image = "ubuntu_jammy" - # Optional: specify zone (defaults to fr-par-1) - # zone = "fr-par-1" -} -``` - -**KUBERNETES (KAPSULE) CLUSTER:** -```hcl -resource "scaleway_k8s_cluster" "cluster" { - name = "my-cluster" - version = "1.28" - cni = "cilium" -} - -resource "scaleway_k8s_pool" "pool" { - cluster_id = scaleway_k8s_cluster.cluster.id - name = "worker-pool" - node_type = "DEV1-M" - size = 3 -} -``` - -**OBJECT STORAGE BUCKET:** -```hcl -resource "scaleway_object_bucket" "bucket" { - name = "my-bucket" -} -``` - -**DATABASE (RDB) INSTANCE:** -```hcl -resource "scaleway_rdb_instance" "db" { - name = "my-database" - engine = "PostgreSQL-15" - node_type = "DB-DEV-S" - is_ha_cluster = false - disable_backup = false -} -``` - -**Common Scaleway Terraform resources:** -- `scaleway_instance_server` - Virtual machines -- `scaleway_instance_ip` - Public IP addresses -- `scaleway_instance_security_group` - Firewall rules -- `scaleway_k8s_cluster` - Kubernetes clusters -- `scaleway_k8s_pool` - Kubernetes node pools -- `scaleway_object_bucket` - Object storage buckets -- `scaleway_rdb_instance` - Managed databases -- `scaleway_vpc_private_network` - Private networks -- `scaleway_lb` - Load balancers - -DO NOT write terraform{} or provider{} blocks - they are auto-generated! - -**When to use Terraform vs CLI:** -- **CLI (cloud_exec)**: Quick single resource ops, listing, inspection -- **Terraform (iac_tool)**: Complex deployments, multi-resource setups, user explicitly requests 'terraform' or 'IaC' - -### CRITICAL RULES -- **ALWAYS** use `cloud_exec('scaleway', ...)` NOT `terminal_exec` for Scaleway commands! +Full Scaleway access via `cloud_exec('scaleway', 'COMMAND')`. +Available CLI: `scw` (aliased through cloud_exec). +Authentication is automatic — never ask users for credentials. + +## CLI Syntax (CRITICAL) +Scaleway CLI uses `key=value` syntax for most parameters (NOT `--key value`): +```python +cloud_exec('scaleway', 'instance server create type=DEV1-S image=ubuntu_jammy name=my-vm') +``` + +## CLI Reference + +### Discovery +```python +cloud_exec('scaleway', 'account project list') +cloud_exec('scaleway', 'instance zone list') +cloud_exec('scaleway', 'instance server-type list') +cloud_exec('scaleway', 'instance server-type list zone=fr-par-1') +cloud_exec('scaleway', 'instance image list') +cloud_exec('scaleway', 'marketplace image list') +``` + +### Instances (Compute) +```python +# List instances: +cloud_exec('scaleway', 'instance server list') +cloud_exec('scaleway', 'instance server list zone=fr-par-1') +# Get instance details: +cloud_exec('scaleway', 'instance server get ') +# Create instance: +cloud_exec('scaleway', 'instance server create type=DEV1-S image=ubuntu_jammy name=my-vm') +# Create with zone: +cloud_exec('scaleway', 'instance server create type=DEV1-S image=ubuntu_jammy name=my-vm zone=fr-par-1') +# Create with cloud-init: +cloud_exec('scaleway', 'instance server create type=DEV1-S image=ubuntu_jammy name=my-vm cloud-init=@/path/to/init.sh') +# Create with additional block volume: +cloud_exec('scaleway', 'instance server create type=DEV1-S image=ubuntu_jammy name=my-vm additional-volumes.0=block:20G') +# Create with specific root volume size: +cloud_exec('scaleway', 'instance server create type=DEV1-S image=ubuntu_jammy name=my-vm root-volume=local:20G') +# Start/Stop/Reboot: +cloud_exec('scaleway', 'instance server start ') +cloud_exec('scaleway', 'instance server stop ') +cloud_exec('scaleway', 'instance server reboot ') +# Standby (hibernate): +cloud_exec('scaleway', 'instance server standby ') +# Terminate (delete server + IP + volumes): +cloud_exec('scaleway', 'instance server terminate with-ip=true with-block=true') +# Delete (just the server, keeps IP and volumes): +cloud_exec('scaleway', 'instance server delete ') +# SSH into server: +cloud_exec('scaleway', 'instance server ssh ') +# Serial console (for boot debugging): +cloud_exec('scaleway', 'instance server console ') +# User data: +cloud_exec('scaleway', 'instance server get-user-data key=cloud-init') +``` + +Instance types: +- Development: `DEV1-S` (2 vCPU, 2GB), `DEV1-M` (3 vCPU, 4GB), `DEV1-L` (4 vCPU, 8GB), `DEV1-XL` (4 vCPU, 12GB) +- General Purpose: `GP1-XS` (4 vCPU, 16GB), `GP1-S` (8 vCPU, 32GB), `GP1-M` (16 vCPU, 64GB), `GP1-L` (32 vCPU, 128GB) +- Production Optimized: `PRO2-XXS`, `PRO2-XS`, `PRO2-S`, `PRO2-M` +- Enterprise: `ENT1-XXS`, `ENT1-XS`, `ENT1-S`, `ENT1-M` +- Default SSH username: `root` + +### IP Addresses +```python +cloud_exec('scaleway', 'instance ip list') +cloud_exec('scaleway', 'instance ip create') +cloud_exec('scaleway', 'instance ip attach server-id=') +cloud_exec('scaleway', 'instance ip detach ') +cloud_exec('scaleway', 'instance ip delete ') +``` + +### Security Groups (Firewall) +```python +cloud_exec('scaleway', 'instance security-group list') +cloud_exec('scaleway', 'instance security-group get ') +cloud_exec('scaleway', 'instance security-group create name=my-sg inbound-default-policy=drop outbound-default-policy=accept') +cloud_exec('scaleway', 'instance security-group-rule create security-group-id= protocol=TCP direction=inbound action=accept dest-port-from=443') +cloud_exec('scaleway', 'instance security-group-rule create security-group-id= protocol=TCP direction=inbound action=accept dest-port-from=80') +cloud_exec('scaleway', 'instance security-group-rule list security-group-id=') +cloud_exec('scaleway', 'instance security-group-rule delete ') +``` + +### Block Storage (Volumes) +```python +cloud_exec('scaleway', 'instance volume list') +cloud_exec('scaleway', 'instance volume get ') +cloud_exec('scaleway', 'instance volume create name=data-vol size=50GB volume-type=b_ssd') +cloud_exec('scaleway', 'instance volume delete ') +# Snapshots: +cloud_exec('scaleway', 'instance snapshot list') +cloud_exec('scaleway', 'instance snapshot create volume-id= name=my-snap') +cloud_exec('scaleway', 'instance snapshot delete ') +``` + +Volume types: `l_ssd` (local SSD), `b_ssd` (block SSD) + +### Kapsule (Managed Kubernetes) +```python +# List clusters: +cloud_exec('scaleway', 'k8s cluster list') +# Get cluster details: +cloud_exec('scaleway', 'k8s cluster get ') +# Create cluster: +cloud_exec('scaleway', 'k8s cluster create name=my-cluster version=1.31 cni=cilium') +# Create with auto-upgrade: +cloud_exec('scaleway', 'k8s cluster create name=my-cluster version=1.31 cni=cilium auto-upgrade.enable=true auto-upgrade.maintenance-window.day=sunday auto-upgrade.maintenance-window.start-hour=3') +# Create in VPC: +cloud_exec('scaleway', 'k8s cluster create name=my-cluster version=1.31 cni=cilium private-network-id=') +# Get kubeconfig: +cloud_exec('scaleway', 'k8s kubeconfig get ') +# Install kubeconfig (writes to ~/.kube/config): +cloud_exec('scaleway', 'k8s kubeconfig install ') +# Node pools: +cloud_exec('scaleway', 'k8s pool list cluster-id=') +cloud_exec('scaleway', 'k8s pool get ') +cloud_exec('scaleway', 'k8s pool create cluster-id= name=worker-pool node-type=DEV1-M size=3') +# With autoscaling: +cloud_exec('scaleway', 'k8s pool create cluster-id= name=worker-pool node-type=GP1-XS size=3 min-size=1 max-size=10 autoscaling=true autohealing=true') +# Scale pool: +cloud_exec('scaleway', 'k8s pool update size=5') +# Delete pool: +cloud_exec('scaleway', 'k8s pool delete ') +# List nodes: +cloud_exec('scaleway', 'k8s node list cluster-id=') +# Upgrade cluster: +cloud_exec('scaleway', 'k8s cluster update version=1.31') +# Delete cluster: +cloud_exec('scaleway', 'k8s cluster delete ') +``` + +**KUBECTL WORKFLOW:** After `k8s kubeconfig install `, kubectl works directly: +```python +cloud_exec('scaleway', 'kubectl get pods -n -o wide') +cloud_exec('scaleway', 'kubectl describe pod -n ') +cloud_exec('scaleway', 'kubectl logs -n --since=1h --tail=200') +cloud_exec('scaleway', 'kubectl logs -n -c --previous') +cloud_exec('scaleway', 'kubectl get events -n --sort-by=.lastTimestamp') +cloud_exec('scaleway', 'kubectl top pods -n ') +cloud_exec('scaleway', 'kubectl top nodes') +cloud_exec('scaleway', 'kubectl get hpa -n ') +cloud_exec('scaleway', 'kubectl get deployments -n ') +cloud_exec('scaleway', 'kubectl rollout history deployment/ -n ') +cloud_exec('scaleway', 'kubectl get pvc -n ') +cloud_exec('scaleway', 'kubectl get svc -n ') +cloud_exec('scaleway', 'kubectl get ingress -n ') +``` + +### Object Storage +```python +cloud_exec('scaleway', 'object bucket list') +cloud_exec('scaleway', 'object bucket get name=') +cloud_exec('scaleway', 'object bucket create name=my-bucket') +cloud_exec('scaleway', 'object bucket delete name=') +# ACL: +cloud_exec('scaleway', 'object bucket get-acl name=') +``` + +### Managed Databases (RDB) +```python +# List instances: +cloud_exec('scaleway', 'rdb instance list') +# Get instance details: +cloud_exec('scaleway', 'rdb instance get ') +# Create PostgreSQL: +cloud_exec('scaleway', 'rdb instance create name=my-db engine=PostgreSQL-15 node-type=DB-DEV-S is-ha-cluster=false') +# Create MySQL: +cloud_exec('scaleway', 'rdb instance create name=my-db engine=MySQL-8 node-type=DB-DEV-S') +# Create with HA: +cloud_exec('scaleway', 'rdb instance create name=my-db engine=PostgreSQL-15 node-type=DB-GP-XS is-ha-cluster=true') +# Databases: +cloud_exec('scaleway', 'rdb database list instance-id=') +cloud_exec('scaleway', 'rdb database create instance-id= name=mydb') +# Users: +cloud_exec('scaleway', 'rdb user list instance-id=') +cloud_exec('scaleway', 'rdb user create instance-id= name=myuser password=') +# ACL / Network access (CRITICAL for connectivity): +cloud_exec('scaleway', 'rdb acl list instance-id=') +cloud_exec('scaleway', 'rdb acl add instance-id= rules.0.ip= rules.0.description=office') +cloud_exec('scaleway', 'rdb acl delete instance-id= acl-rule-ips.0=') +# Backups: +cloud_exec('scaleway', 'rdb backup list instance-id=') +cloud_exec('scaleway', 'rdb backup create instance-id= name=my-backup database-name=mydb') +cloud_exec('scaleway', 'rdb backup restore instance-id= database-name=mydb') +# Logs: +cloud_exec('scaleway', 'rdb log list instance-id=') +cloud_exec('scaleway', 'rdb log prepare instance-id= start-date= end-date=') +# Read replicas: +cloud_exec('scaleway', 'rdb read-replica list instance-id=') +cloud_exec('scaleway', 'rdb read-replica create instance-id=') +# Upgrade instance: +cloud_exec('scaleway', 'rdb instance upgrade node-type=DB-GP-XS') +# Metrics: +cloud_exec('scaleway', 'rdb instance get-metrics ') +# Delete: +cloud_exec('scaleway', 'rdb instance delete ') +``` + +Database node types: `DB-DEV-S` (1 vCPU, 2GB), `DB-DEV-M` (2 vCPU, 4GB), `DB-GP-XS` (4 vCPU, 16GB), `DB-GP-S` (8 vCPU, 32GB), `DB-GP-M` (16 vCPU, 64GB) + +### VPC / Private Networks +```python +# VPC (parent container): +cloud_exec('scaleway', 'vpc list') +cloud_exec('scaleway', 'vpc get ') +cloud_exec('scaleway', 'vpc create name=my-vpc') +# Private networks: +cloud_exec('scaleway', 'vpc private-network list') +cloud_exec('scaleway', 'vpc private-network get ') +cloud_exec('scaleway', 'vpc private-network create name=my-network') +# With CIDR: +cloud_exec('scaleway', 'vpc private-network create name=my-network subnets.0=192.168.1.0/24') +cloud_exec('scaleway', 'vpc private-network delete ') +# Attach instance to private network: +cloud_exec('scaleway', 'instance private-nic create server-id= private-network-id=') +cloud_exec('scaleway', 'instance private-nic list server-id=') +cloud_exec('scaleway', 'instance private-nic delete server-id= private-nic-id=') +``` + +### Load Balancers +```python +cloud_exec('scaleway', 'lb list') +cloud_exec('scaleway', 'lb get ') +cloud_exec('scaleway', 'lb create name=my-lb type=LB-S') +# Backends: +cloud_exec('scaleway', 'lb backend list lb-id=') +cloud_exec('scaleway', 'lb backend create lb-id= name=web-backend forward-port=80 forward-protocol=tcp health-check.port=80') +cloud_exec('scaleway', 'lb backend create lb-id= name=web-backend forward-port=443 forward-protocol=tcp health-check.port=443 health-check.tcp-config={}') +# Add servers to backend: +cloud_exec('scaleway', 'lb backend add-servers server-ip.0=') +cloud_exec('scaleway', 'lb backend remove-servers server-ip.0=') +# Frontends: +cloud_exec('scaleway', 'lb frontend list lb-id=') +cloud_exec('scaleway', 'lb frontend create lb-id= name=web-frontend inbound-port=443 backend-id=') +# Health checks: +cloud_exec('scaleway', 'lb backend get-healthcheck ') +# Stats: +cloud_exec('scaleway', 'lb get-stats ') +# Delete: +cloud_exec('scaleway', 'lb delete ') +``` + +LB types: `LB-S` (small), `LB-GP-M` (medium), `LB-GP-L` (large) + +### Container Registry +```python +cloud_exec('scaleway', 'registry namespace list') +cloud_exec('scaleway', 'registry namespace get ') +cloud_exec('scaleway', 'registry namespace create name=my-registry') +cloud_exec('scaleway', 'registry image list namespace-id=') +cloud_exec('scaleway', 'registry tag list image-id=') +cloud_exec('scaleway', 'registry namespace delete ') +``` + +### Serverless Functions +```python +cloud_exec('scaleway', 'function namespace list') +cloud_exec('scaleway', 'function namespace get ') +cloud_exec('scaleway', 'function namespace create name=my-funcs') +cloud_exec('scaleway', 'function function list namespace-id=') +cloud_exec('scaleway', 'function function get ') +cloud_exec('scaleway', 'function function deploy ') +cloud_exec('scaleway', 'function log list function-id=') +``` + +### Serverless Containers +```python +cloud_exec('scaleway', 'container namespace list') +cloud_exec('scaleway', 'container namespace get ') +cloud_exec('scaleway', 'container namespace create name=my-containers') +cloud_exec('scaleway', 'container container list namespace-id=') +cloud_exec('scaleway', 'container container get ') +cloud_exec('scaleway', 'container container deploy ') +cloud_exec('scaleway', 'container log list container-id=') +``` + +### DNS +```python +cloud_exec('scaleway', 'dns zone list') +cloud_exec('scaleway', 'dns record list ') +cloud_exec('scaleway', 'dns record add name= type=A data= ttl=3600') +cloud_exec('scaleway', 'dns record delete name= type=A data=') +``` + +### Secret Manager +```python +cloud_exec('scaleway', 'secret secret list') +cloud_exec('scaleway', 'secret secret get ') +cloud_exec('scaleway', 'secret secret create name=my-secret') +cloud_exec('scaleway', 'secret version list secret-id=') +cloud_exec('scaleway', 'secret version create secret-id= data=@/path/to/secret.txt') +cloud_exec('scaleway', 'secret version access ') +``` + +## RCA / Investigation Workflow + +When investigating a Scaleway incident: + +1. **List instances**: `instance server list` — check status (running/stopped/stopping) +2. **Get instance details**: `instance server get ` — check type, volumes, security groups +3. **Check serial console**: `instance server console ` — boot issues, kernel panics +4. **Get Kapsule credentials** (if K8s): `k8s kubeconfig install ` +5. **Check cluster health**: `k8s cluster get ` — version, status, node count +6. **Check pods/containers**: `kubectl get pods -o wide`, `kubectl describe pod`, `kubectl logs` +7. **Check K8s events**: `kubectl get events --sort-by=.lastTimestamp` +8. **Check node health**: `kubectl top nodes`, `kubectl describe node`, `k8s node list cluster-id=` +9. **Check node pools**: `k8s pool list cluster-id=` — verify pool status, autoscaling config +10. **Check databases**: `rdb instance get ` — status, engine version, HA status +11. **Check DB logs**: `rdb log list instance-id=` — recent errors, slow queries +12. **Check DB ACLs**: `rdb acl list instance-id=` — connectivity issues often caused by missing ACL rules +13. **Check DB backups**: `rdb backup list instance-id=` — verify backup health +14. **Check networking**: `vpc private-network list`, security group rules, private NIC attachments +15. **Check load balancer**: `lb get-stats `, `lb backend get-healthcheck ` +16. **Check serverless**: `function log list`, `container log list` — invocation errors +17. **Check recent deployments**: `kubectl rollout history`, function/container deploy history +18. **Compare healthy vs unhealthy**: Pod metrics, logs, instance status side-by-side + +## Critical Rules +- **ALWAYS** use `cloud_exec('scaleway', ...)` NOT `terminal_exec` for Scaleway commands - Scaleway CLI uses `key=value` syntax, NOT `--key value` for most parameters -- Common instance types: DEV1-S, DEV1-M, DEV1-L, GP1-XS, GP1-S, GP1-M -- Common images: ubuntu_jammy, ubuntu_focal, debian_bookworm, debian_bullseye -- Default region: fr-par, zones: fr-par-1, fr-par-2, fr-par-3 +- Default region: `fr-par`, zones: `fr-par-1`, `fr-par-2`, `fr-par-3` - Default SSH username for instances: `root` +- Instance IDs are UUIDs — always use the full UUID +- `instance server terminate` deletes server + associated resources (IP, volumes if specified) +- `instance server delete` only removes the server, keeps IP and volumes + +## Error Recovery + +1. **Resource not found** → Verify zone/region — resources are zone-specific +2. **Quota exceeded** → Check project quotas, try a different zone +3. **Permission denied** → Verify API key has correct project scope +4. **Server type unavailable** → Try different zone: `fr-par-1`, `fr-par-2`, `fr-par-3`, `nl-ams-1`, `nl-ams-2`, `pl-waw-1`, `pl-waw-2` +5. **Database connection refused** → Check ACL rules: `rdb acl list instance-id=` +6. **CLI syntax** → `cloud_exec('scaleway', ' --help')` for subcommand reference + +### Context7 lookup on failure +For resource reference: +`mcp_context7_get_library_docs(context7CompatibleLibraryID='/scaleway/terraform-provider-scaleway', topic='scaleway_instance_server')` + +## Region Mapping +- France (default): fr-par, zones: fr-par-1, fr-par-2, fr-par-3 +- Netherlands: nl-ams, zones: nl-ams-1, nl-ams-2, nl-ams-3 +- Poland: pl-waw, zones: pl-waw-1, pl-waw-2, pl-waw-3 From 9c69bdc28bcabb2a457921b38e50bf1754d1dd8f Mon Sep 17 00:00:00 2001 From: Benjamin Gervais Date: Tue, 21 Apr 2026 09:27:57 -0400 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20resolve=20PR=20review=20comments=20?= =?UTF-8?q?=E2=80=94=20OVH=20terminal=5Fexec,=20Scaleway=20Context7,=20Azu?= =?UTF-8?q?re=20formatting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OVH SKILL.md: change all 14 kubectl `terminal_exec` calls to `cloud_exec('ovh', 'kubectl ...')` - Scaleway SKILL.md: fix Context7 library ID from terraform-provider to scaleway-cli - Azure SKILL.md: add missing blank line between Subscription Setup and CLI Reference sections Co-Authored-By: Claude Opus 4.6 --- .../agent/skills/integrations/azure/SKILL.md | 1 + .../agent/skills/integrations/ovh/SKILL.md | 28 +++++++++---------- .../skills/integrations/scaleway/SKILL.md | 2 +- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/server/chat/backend/agent/skills/integrations/azure/SKILL.md b/server/chat/backend/agent/skills/integrations/azure/SKILL.md index 1806a3129..d47f5dda5 100644 --- a/server/chat/backend/agent/skills/integrations/azure/SKILL.md +++ b/server/chat/backend/agent/skills/integrations/azure/SKILL.md @@ -30,6 +30,7 @@ Authentication and subscription are auto-configured — never ask users for cred ## Subscription Setup - Get subscription ID: `cloud_exec('azure', "account show --query 'id' -o tsv")` + ## CLI Reference ### Discovery diff --git a/server/chat/backend/agent/skills/integrations/ovh/SKILL.md b/server/chat/backend/agent/skills/integrations/ovh/SKILL.md index 04647a0a2..bb70cad2d 100644 --- a/server/chat/backend/agent/skills/integrations/ovh/SKILL.md +++ b/server/chat/backend/agent/skills/integrations/ovh/SKILL.md @@ -114,24 +114,24 @@ cloud_exec('ovh', 'cloud kube kubeconfig reset --cloud-project --cloud-project ', output_file='/tmp/kubeconfig.yaml')` -2. Run kubectl: `terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get pods -A')` +2. Run kubectl: `cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml get pods -A')` 3. CRITICAL: Use output_file parameter to save kubeconfig — avoids shell escaping issues with YAML 4. Do NOT try to embed kubeconfig YAML in echo commands — it will break ```python -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get pods -n -o wide') -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml describe pod -n ') -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml logs -n --since=1h --tail=200') -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml logs -n -c --previous') -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get events -n --sort-by=.lastTimestamp') -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml top pods -n ') -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml top nodes') -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get hpa -n ') -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get deployments -n ') -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml rollout history deployment/ -n ') -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get pvc -n ') -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get svc -n ') -terminal_exec('kubectl --kubeconfig=/tmp/kubeconfig.yaml get ingress -n ') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml get pods -n -o wide') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml describe pod -n ') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml logs -n --since=1h --tail=200') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml logs -n -c --previous') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml get events -n --sort-by=.lastTimestamp') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml top pods -n ') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml top nodes') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml get hpa -n ') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml get deployments -n ') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml rollout history deployment/ -n ') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml get pvc -n ') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml get svc -n ') +cloud_exec('ovh', 'kubectl --kubeconfig=/tmp/kubeconfig.yaml get ingress -n ') ``` ### Managed Databases diff --git a/server/chat/backend/agent/skills/integrations/scaleway/SKILL.md b/server/chat/backend/agent/skills/integrations/scaleway/SKILL.md index fa4c25b4c..8876f90fe 100644 --- a/server/chat/backend/agent/skills/integrations/scaleway/SKILL.md +++ b/server/chat/backend/agent/skills/integrations/scaleway/SKILL.md @@ -356,7 +356,7 @@ When investigating a Scaleway incident: ### Context7 lookup on failure For resource reference: -`mcp_context7_get_library_docs(context7CompatibleLibraryID='/scaleway/terraform-provider-scaleway', topic='scaleway_instance_server')` +`mcp_context7_get_library_docs(context7CompatibleLibraryID='/scaleway/scaleway-cli', topic='instance server create')` ## Region Mapping - France (default): fr-par, zones: fr-par-1, fr-par-2, fr-par-3