# Volcengine Iac

> Use Terraform/IaC for Volcengine resources only when the user explicitly chooses Terraform/IaC, already has a Terraform workflow/state, or confirms they need plan/diff/drift/destroy safety for VKE, managed databases/cache/storage, load balancers, domains/certificates, logging/monitoring, or team-managed infrastructure.

- Skill: `volcengine/volcengine-iac` (Agent Skill, multi-file: 132 files)
- Install (CLI): `npx skillmds@latest add volcengine/volcengine-iac`
- Raw SKILL.md: https://api.skillmd.com/api/skills/volcengine/volcengine-iac/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: MIT
- Author: volcengine (https://skillmd.com/u/volcengine)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/volcengine/volcengine-iac

---


# Volcengine IaC Skill

Generate, plan, and apply Volcengine infrastructure with Terraform when the user chooses IaC. `volcengine-deploy` still owns application packaging, runtime rollout, health checks, and CLI resource-ledger deployment.

When writing new examples, prefer the Cloud Control provider `volcengine/volcenginecc`. Start from verified examples under `assets/examples/`, then read the matching `references/volcenginecc-*.md` note for validation results and pitfalls. Blocked resources are tracked in [`references/volcenginecc-blocked.md`](./references/volcenginecc-blocked.md). Existing reusable modules under `assets/modules/` still use the legacy `volcengine/volcengine` provider until each component is re-verified with `volcenginecc`.

Use this skill when one of these is true:

- the user asks for Terraform or IaC,
- the user already has Terraform state/workspace,
- plan/diff/drift detection matters,
- the infrastructure is intended for long-term team-managed operation and the user chooses IaC,
- the deployment needs VKE, managed databases/cache/storage, load balancers, domains/certificates, IAM/KMS, logging, monitoring, or Serverless triggers and the user chooses IaC.

Do not use this skill just because a deployment is long-lived, VKE-based, or has managed dependencies. Recommend IaC where appropriate, but let the user choose. Do not use this skill when the user asks for CLI, a temporary demo/quick validation, a pure ECS single-VM service with no plan/diff/destroy requirement, or when Terraform/provider installation is blocked and the target can safely use the CLI fallback.

The skill ships verified `volcenginecc` examples under `assets/examples/`, legacy reusable modules under `assets/modules/`, and wrapper scripts for tfvars generation, plan summaries, output export, and drift checks. Select files only after the target shape is known; do not load broad catalogs into context for small Terraform edits.

Resources outside the six legacy modules (CLB/ALB, VKE, CR, databases, caches, object storage) should be added as verified `volcenginecc` examples first, then wrapped only after repeated use proves the interface is stable.

---

## 0. Prerequisites

Required env vars: `VOLCENGINE_ACCESS_KEY`, `VOLCENGINE_SECRET_KEY`, `VOLCENGINE_REGION`.

Required tools: `terraform >= 1.5`, `jq`, `git`, `python3` (for `gen_tfvars.py`).

Optional: `.volcengine/deploy-choice.json` from `volcengine-prepare`/`volcengine-deploy`. If absent, ask a short batch of Terraform-specific questions.

The skill writes Terraform working files into `.volcengine/terraform/` by default. State can use a TOS S3-compatible backend when the user wants remote state; see [`references/backend-tos.md`](./references/backend-tos.md). Local state is acceptable for small one-off experiments when the user accepts the tradeoff.

If invoked by `volcengine-deploy`, return outputs in `.volcengine/iac-outputs.json` and let deploy continue with image build/push, Cloud Assistant commands, Kubernetes manifests, veFaaS release, migrations, and health checks.

---

## 1. Generation Flow

### Path A — driven by `.volcengine/deploy-choice.json`

```bash
work_dir="${work_dir:-.volcengine/terraform}"
workload="${workload:-standard}"
mkdir -p "$work_dir"

python3 scripts/gen_tfvars.py \
  --input ".volcengine/deploy-choice.json" \
  --output "$work_dir/terraform.tfvars" \
  --workload "$workload"
```

`gen_tfvars.py` derives:
- `project`, `region`, AZ pair from the choice file and environment
- `enable_vke / enable_cr / enable_rds / enable_redis / enable_tos` flags from the chosen mode and known dependencies
- Sizing (instance type, node count, RDS spec, Redis capacity) from a coarse `--workload` tier

The user can override any value before applying.

### Path B — natural language input

When no prepare report exists, ask the user **one batch of questions**, then create a Terraform working directory from verified examples or legacy modules and generate matching variable values. Required answers:

1. Project name (resource prefix)
2. Region (e.g. `cn-beijing`)
3. Need VKE? (yes/no)
4. Stateful deps needed: MySQL? PostgreSQL? Redis? TOS bucket?
5. Workload tier: light / standard / heavy

### Files written

`gen_tfvars.py` writes only `terraform.tfvars`. The Terraform configuration files come from copied verified examples under `assets/examples/` or from a small root module assembled from `assets/modules/`; edit those files to match the selected stack instead of expecting the script to generate them.

| File | Purpose |
|---|---|
| `main.tf` / `variables.tf` | Copied or assembled Terraform configuration |
| `terraform.tfvars` | Concrete values generated by `gen_tfvars.py` or edited by hand |
| `backend.tf` | Generated only when TOS remote state is selected; otherwise omit it |

The full per-module variable schema lives in [`references/modules.md`](./references/modules.md). For new `volcenginecc` work, start from a verified example under `assets/examples/` instead of these legacy modules unless the user explicitly needs the old provider.

### Path C — `volcenginecc` verified examples

Copy the relevant verified example, then run the same init/validate/plan sequence:

```bash
cp -R "assets/examples/<example-name>" "$work_dir/<component>"
cd "$work_dir/<component>"
terraform init -backend=false -input=false
terraform validate
terraform plan -out=tfplan.binary -input=false
```

Before apply, show the plan summary and require explicit user confirmation. Read the matching reference before changing inputs or destroying resources; the references contain field choices, validation notes, import IDs, and provider pitfalls.

---

## 2. Deployment stack mapping

Use the deployment shape to choose a small set of verified examples. Start with examples; wrap into reusable modules only after repeated use proves the interface stable. Typical stacks combine:

- ECS: `volcenginecc-network`, `volcenginecc-ecs`, optional TLS/CloudMonitor
- VKE: `volcenginecc-network`, `volcenginecc-vke`, `volcenginecc-cr`, CLB or ALB
- veFaaS: `volcenginecc-vefaas`, optional APIG/TLS
- Stateful web app: runtime stack plus RDS, Redis, and/or TOS examples
- Private service: runtime stack plus private NAT or network connectivity examples
- Domain entry: runtime stack plus DNS and load balancer certificate examples

This mapping is a guide, not a promise that a prebuilt module exists. Copy relevant examples into `.volcengine/terraform/<component>` and keep component boundaries clear so plan/destroy output remains readable.

For the end-to-end VKE private CR nginx path, use `assets/examples/volcengine-vke-cr-nginx/` and read `references/volcengine-vke-cr-nginx.md` first. That example exists for the CR credential addon, `core-dns`, CR token expiry, and image architecture pitfalls found in a real run.

---

## 3. Catalog

Use the filesystem as the catalog. After selecting a deployment shape, list only the relevant directories under `assets/examples/`, then read the matching `references/volcenginecc-*.md` file for validation notes, import IDs, and provider caveats. Do not load every example into context up front.

Common example families:

- `volcenginecc-network`, VPC extras, NAT, VPN, DirectConnect, CEN, TransitRouter, PrivateLink, DNS, PrivateZone
- `volcenginecc-ecs`, ECS extras, launch template versions, EBS snapshots, Auto Scaling
- `volcenginecc-vke`, `volcenginecc-cr`, CLB/ALB entry, APIG, veFaaS
- `volcenginecc-rdsmysql`, `volcenginecc-rdspostgresql`, `volcenginecc-rdsmssql`, Redis, Kafka allowlist
- `volcenginecc-tos`, TOS notification, TLS, CloudMonitor, IAM, FileNAS, EFS

Legacy `volcengine` modules are still available under `assets/modules/`; read [`references/modules.md`](./references/modules.md) before using them.

Legacy modules currently cover `network`, `vke`, `cr`, `rds-mysql`, `redis`, and `tos`. ECS and CLB/ALB stay as verified examples because their workload shapes vary too much for one stable module interface.

---

## 4. Init & Backend

```bash
cd "$work_dir"

# Map Volcengine creds to the Terraform s3 backend's required env variable names.
export AWS_ACCESS_KEY_ID="$VOLCENGINE_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="$VOLCENGINE_SECRET_KEY"
export AWS_EC2_METADATA_DISABLED=true

terraform init -input=false
tf_workspace="${tf_workspace:-default}"
terraform workspace select "$tf_workspace" || \
  terraform workspace new "$tf_workspace"
```

When remote state is requested, generate `backend.tf` from `references/backend-tos.md`. The verified TOS backend shape requires `skip_requesting_account_id = true` and must not set `force_path_style` or `use_path_style`.

Provider download (~30–60 seconds first time) goes through `registry.terraform.io`. In China networks this may be blocked or slow. If public registry access fails and the user still wants IaC, configure Terraform `provider_installation` with a filesystem or internal mirror. Otherwise return to `volcengine-deploy` and ask whether to use the CLI resource-ledger path.

For the TOS bucket prerequisite (must exist before `init`), see [`references/backend-tos.md`](./references/backend-tos.md). Without a TOS bucket, omit `backend.tf` and Terraform falls back to local state.

---

## 5. Plan

```bash
terraform plan -out=tfplan.binary -input=false
terraform show -json tfplan.binary > tfplan.json
bash scripts/plan_summary.sh tfplan.json
```

`plan_summary.sh` groups changes by action (CREATE / UPDATE / DELETE / REPLACE) and prints a one-line summary at the end. Show this to the user before any apply.

Watch for:
- **DELETEs you didn't ask for** — usually a sign of drift or a mistakenly removed module call
- **REPLACEs on stateful resources** — RDS / Redis replace = data loss; abort and inspect the diff with `terraform show tfplan.binary`

---

## 6. Apply

**Always require explicit user confirmation.** Do not pass `-auto-approve`. The pattern:

```text
The plan above will create N resources, change M, destroy K. Approve apply? [yes/no]
```

After yes:

```bash
terraform apply tfplan.binary
```

VKE cluster creation takes ~10–15 minutes. RDS HA instances take ~20 minutes. Surface the long-running message to the user once at apply start so they don't think it hung.

After apply succeeds, run `export_outputs.sh` automatically:

```bash
bash scripts/export_outputs.sh
echo "Resources ready. .volcengine/iac-outputs.json now contains downstream consumption keys."
```

---

## 7. Outputs for Downstream

`export_outputs.sh` writes `terraform output -json` to `.volcengine/iac-outputs.json` with mode `0600`. Downstream skills commonly consume VPC/subnet/security group IDs, VKE kubeconfig, CR repository data, RDS/Redis endpoints or IDs, and TOS bucket names. Some keys are conditional on which examples/modules were enabled; consumers must use defensive `jq` defaults.

---

## 8. Destroy

```bash
# Show what will be destroyed first
terraform plan -destroy -out=destroy.binary
terraform show -json destroy.binary | bash scripts/plan_summary.sh
```

Then:

```text
This will permanently delete N resources including RDS / Redis / TOS bucket data. Confirm? [yes/no]
```

On yes:

```bash
terraform destroy
# Note: provider does not accept -auto-approve=false; we rely on the agent-level prompt above.
```

**Hard rule**: never destroy in `prod` workspace without a second confirmation. The agent should explicitly re-prompt:

```text
Workspace = prod. Re-confirm destroy by typing 'destroy prod':
```

---

## 9. Drift Detection

```bash
bash scripts/check_drift.sh
```

Returns:
- `0` and `{"drift": false, ...}` — no drift
- `2` and `{"drift": true, "changed_resources": N, ...}` — N resources changed outside Terraform
- `1` and `{"drift": "error", ...}` — refresh-only plan errored

Use this:
- After known manual interventions (someone resized a node pool via console)
- Periodically as a CI job (weekly)
- Before any non-trivial `apply` to confirm baseline matches state

---

## 10. Import Existing Resources

If the user wants to adopt resources created via `volcengine-cli` or console, use `terraform import`. Import IDs differ by resource; read the matching `references/volcenginecc-*.md` or [`references/modules.md`](./references/modules.md) before importing. After import, run `terraform plan` and reconcile by editing config, not state.

---

## 11. Safety Rules

- Never read `~/.volcengine/config.json`; it may contain plaintext AK/SK.
- Never commit `terraform.tfstate*` or `.volcengine/iac-outputs.json`.
- Never pass `-auto-approve` to `terraform apply` or `terraform destroy`.
- Run `check_drift.sh` before every apply on shared environments.
- Set mode `0600` on files holding kubeconfig or secrets.
- Pin provider versions in every module/example.

The scripts enforce file permissions where possible. Apply and destroy gates are the agent's responsibility.

---

## 12. Troubleshooting

Look up by the exact error string; act on the mapped cause before suspecting unrelated layers.

| Symptom | Cause | Fix |
|---|---|---|
| `terraform init`: "Failed to query available provider packages" | outbound to `registry.terraform.io` blocked | Configure `provider_installation` with a filesystem/internal mirror and rerun `init`; otherwise return to `volcengine-deploy` and ask whether CLI resource-ledger provisioning is acceptable |
| `terraform init`: "InvalidAccessKeyId" against TOS backend | s3 backend env vars not exported | Export `AWS_ACCESS_KEY_ID="$VOLCENGINE_ACCESS_KEY"`, `AWS_SECRET_ACCESS_KEY="$VOLCENGINE_SECRET_KEY"`, `AWS_EC2_METADATA_DISABLED=true` |
| `terraform init`: TOS backend returns `InvalidPathAccess` | path-style access or unsupported workspace prefix | Use the verified template in [`references/backend-tos.md`](./references/backend-tos.md): keep `skip_requesting_account_id = true`, remove `force_path_style`/`use_path_style` |
| `apply` succeeds for VPC but subnet fails with `InvalidVpc.InvalidStatus` | VPC not yet `Available` (consistency window) | Add `depends_on = [volcengine_vpc.main]` (already wired in the network module) |
| VKE cluster stuck in `Creating` for >20 min | quota or AZ capacity | `ve vke ListClusters --body '{"Filter":{"Ids":["..."]}}' \| jq .Result.Items[0].Status` for the real reason |
| `redis` output missing endpoint | provider does not export it | Resolve via `ve redis DescribeDBInstanceDetail --InstanceId $(jq -r '.redis_instance_id.value' .volcengine/iac-outputs.json)` |
| `terraform plan` shows unexpected changes after no edits | drift, or provider patch bump silently changing a default | Run `check_drift.sh`, inspect `tfplan.json`, decide whether to accept or revert |
| Two engineers' `apply` collide | TOS backend has no DynamoDB-style locking | Coordinate manually; see [`references/backend-tos.md`](./references/backend-tos.md) |
| Resource-specific apply/import drift | provider caveat for that resource | Read the matching `references/volcenginecc-*.md` note before editing config |

