Nectere Anvil
Runs Terraform from the browser. Packages of .tf files, variable values and cloud credentials are stored once and combined into deployments, which are planned, reviewed, applied, drift-checked and destroyed from the same UI. Every run is logged. For anything the UI doesn't cover there's a shell in the deployment's working directory.
Nectere Array › Menu › Deploy › Nectere Anvil01The app
Every panel hangs off the left sidebar. The mock is clickable and each panel has its own section below.
Sidebar items follow permissions. Command Station needs apply and destroy; Users and Backup need manage_users. Array sign-on also hides Users and Sign Out.
02Concepts
Each part is set up once and reused across deployments.
The usual order
- Packages: upload or build the Terraform code.
- Credentials: add a cloud login and press Test.
- Tfvar Tables: pick the package, fill in values.
- Deploy: choose all three, name the deployment, press Run Plan. If the plan looks right, Approve & Apply.
- Deployments: drift checks, re-applies, outputs and destroy.
03Getting Anvil
Deployed from Nectere Array with Menu › Deploy › Nectere Anvil. Name the tile (say Anvil Prod), pick a sign-in mode and press Deploy. When it shows Running, press Open. Updates, restarts and logs are on the back of the tile.
Settings
Edited with the tile's .env button in Array. Saving restarts Anvil for a few seconds without losing anything.
| Setting | Default | What it does |
|---|---|---|
| ANVIL_MAX_CONCURRENT_RUNS | 6 | Runs allowed at once, open consoles included. Not in the file by default; add the line to change it. |
| ANVIL_CREDENTIAL_KEY | set by Array | Don't touch it. A new key makes every saved credential unreadable. |
04Sign-in and setup
Array sign-on needs no setup. With Application sign-in, the first visit to a new tile opens the setup screen, which creates the first account with all eight permissions. After that everyone uses Sign In.
| Rule | Value |
|---|---|
| Sign-in lasts | 8 hours |
| Lockout | 5 wrong passwords for one username within 15 minutes locks it for 5 minutes |
| Minimum password | 8 characters |
Array sign-on
Opening the tile from Array signs you in. An expired Array session lands on "Sign in through Nectere Array" (the Array mode screen above), and reopening the tile from Array fixes it.
05Sidebar and notifications
| Item | Needs |
|---|---|
| Dashboard, Packages, Builder, Tfvar Tables, Env Tables, Credentials, Providers, Deploy, Deployments | Always shown. Buttons inside still check permissions. |
| Command Station | apply + destroy |
| Users | manage_users, hidden with Array sign-on |
| Backup | manage_users |
Footer
Notifications stack above Sign Out (green success, rose error, blue info) and fade after a few seconds. Under them are the server clock, which drift schedules use, and the Anvil version.
06Dashboard
- 1Active Runs. The orange count pulses while anything runs. Click a line for its log. Empty:
No runs in progress. - 2Deployments. Totals by status. Unapplied plans don't count.
- 3Count tiles. Click one to open that panel.
- 4Recent Runs. The 8 newest runs across all deployments.
VIEW ALL →opens Deployments.
| Run status | Badge |
|---|---|
| running | running |
| succeeded | succeeded |
| failed | failed |
| cancelled, timed-out | cancelled timed-out |
Refreshes every 20 seconds while open.
07Packages
Terraform code for one kind of thing. Leave *.tfvars files out: values come from tfvar tables at run time, which is how one package serves many environments.
| Button | Needs | Does |
|---|---|---|
| + New Package | upload_package | Empty package, filled in from the browser |
| + Upload Package | upload_package | Package from a zip of .tf files |
| Inspect | view | Providers, variables, outputs and files, with an editor per file |
| Download | view | Zip of the package |
| Open in Builder | upload_package | Loads it into the Builder (see the Builder guide) |
| Delete | upload_package | Blocked while a deployment uses the package. Otherwise also removes its tfvar tables and abandoned plan records. |
Upload and New
Upload Package
- A single
.zipfile containing your.tfsources. - Typical contents:
plan.tf,provider.tf,vars.tf(split however you like). - Subfolders are allowed; a single wrapper folder at the zip root is flattened automatically.
- Do not include
*.tfvarsor*.tfvars.json— values come from a tfvar table at apply time. - Do not include
.terraform/, state files, or provider binaries (use the Providers panel for private providers).
my-package.zip ├── plan.tf ├── provider.tf └── vars.tf
New Package
- Creates the package with no
.tffiles. - Add them from the package's Inspect view with + Add .tf File, then edit each one in the browser.
- It won't be deployable until it has at least one
.tffile.
Add .tf File
.tf is added if you leave it off.Inspect
Lists what Terraform will need. Provider sources link to registry docs. Variables show type, default and a sensitive badge where it applies. Each file has Edit, which opens a full-width Ace editor, and × to delete it.
AKS Cluster
Required Providers
Variables
Outputs
Generate an outputs.tf from this package's resources.
Files
Editing a file
provider.tf
Build Outputs
Writes outputs.tf from resource attributes you pick, replacing any existing one on save. For resources using count or for_each, tick [*] to output every instance.
Build Outputs — AKS Cluster
Click a resource to add an output, or use "+ Add output". Each output exposes one resource attribute. Saving writes outputs.tf into the package (replacing any existing one).
output "cluster_name" { value = azurerm_kubernetes_cluster.aks.name } output "kube_admin_config_raw" { value = azurerm_kubernetes_cluster.aks.kube_admin_config_raw sensitive = true }
| Message | When |
|---|---|
| Cannot delete - 2 deployment(s) still use this package: aks-csi-test (applied), aks-dev (skewed). Remove them from the Deployments panel first. | Deleting a package still in use |
| Package deleted - 2 tfvar table(s) removed: AKS Cluster - CSI, AKS Cluster dev; 1 abandoned plan record(s) cleared | Delete succeeded |
| No .tf files yet. Add one to get started. | Inspecting an empty package |
| Add at least one output. | Saving Build Outputs with no rows |
08Tfvar Tables
One set of values for one package. Pick the package first; the list filters to its tables. Runs get the table as terraform.tfvars.json.
Deploy opens the Deploy panel with package and table preselected. Download saves <name>.tfvars.json. Copy asks for a name and suggests <name>-copy. Edit, Copy and Delete need manage_varsets; Deploy needs plan.
The form
One row per variable block. An orange ⚠ marks variables with no default. A validation { contains(…) } rule becomes a dropdown, bools are checkboxes, and lists and objects get nested editors. Dropping in an existing .tfvars file fills the form; the file isn't kept. Blank rows are left out on save so Terraform uses the default.
Edit Tfvar Table
variable in the package. Stored as terraform.tfvars.json at apply time. ⚠ marks variables with no default (required).
09Env Tables
Environment variables set before Terraform runs: proxy settings, tokens for providers that read the environment, TF_LOG. A deployment can attach any number. Credential variables go on last and win a name clash.
Edit Env Table
-var="key=value" on plan, apply and destroy instead of exported. Env tables are stored in plain text, so real secrets belong in credentials.10Credentials
Terraform logs in with the credential picked at deploy time, so packages carry no secrets. Fields named secret, password, key or token are encrypted on save and never shown again. Leave one blank when editing to keep its value.
Per-provider fields
| Provider | Fields |
|---|---|
| Azure (azurerm) | Subscription ID, Tenant ID, Client ID, Client Secret |
| AWS (aws) | Access Key ID, Secret Access Key, Region, Session Token (only for temporary keys) |
| Google Cloud (google) | Project ID, Region, service account JSON key |
| Kubernetes (kubernetes) | Host URL, Bearer Token, CA Cert |
| Custom | Any JSON object. Each key becomes a Terraform variable of the same name. |
Help next to Provider opens a short guide to creating that type of credential. Custom has none.
Generating an Azure Service Principal
Terraform's azurerm provider authenticates with a service principal. The client secret is the only value Anvil treats as sensitive; the IDs are not secret.
# Grab your subscription id first az account show --query id -o tsv # Create the SP scoped to the subscription with Contributor az ad sp create-for-rbac --name "anvil-terraform" \ --role "Contributor" \ --scopes "/subscriptions/<Subscription ID>"
Output maps to the form like this:
{
"appId": "xxxxxxxx-...", <- Client ID
"password": "xxxxxxxxxxxx", <- Client Secret
"tenant": "xxxxxxxx-..." <- Tenant ID
}
Test
Test makes a real login call and reports in a sidebar notification. A pass means the secret works and the cloud is reachable.
Access report
Access (Azure, AWS, Google) reports what the identity can actually do. It runs read checks from the tenant or organization down to the subscription, account or project, then billing. Denied or partial results with a known fix get an i button that shows the granting command. A check takes 10 to 40 seconds. The last result stays, marked cached, until Refresh.
Access Report - azure-lab
| Check | Detail | Result | |
|---|---|---|---|
| Tenant · Lab Tenant | Partial | ||
| Subscriptions visible | 2 subscriptions | Allowed | |
| Management groups visible | Not permitted at tenant root | Denied | |
| Entra directory read (Graph) | Directory.Read.All not granted | Denied | |
| Subscription · Lab-Subscription | OK | ||
| Role assignments | Contributor at /subscriptions/0f3a…
| Allowed | |
| Can deploy resources | Microsoft.Resources/deployments/write | Allowed | |
| Can grant access (Owner-level) | Microsoft.Authorization/roleAssignments/write | Deniedi | |
| Grant 'User Access Administrator' at /subscriptions/0f3a9c2e-… Only needed if your packages create role assignments. az role assignment create --assignee 7c1e9d40-2b8a-4f63-a1d5-0e9b3c6f2a71 --role "User Access Administrator" --scope /subscriptions/0f3a9c2e-5b71-4d1e-9a6c-8e2f4b7d1c90 | |||
| List resource groups | 14 resource groups | Allowed | |
| Billing | Info | ||
| Cost Management query | Last 30 days readable | Allowed | |
| Billing accounts visible | Not checked for this identity type | Skipped | |
| Cloud | Levels | Checks |
|---|---|---|
| Azure | Tenant, Subscription, Billing | Subscription and management group visibility, Graph directory read, role assignments and effective permissions, deploy and Owner-level grant, resource groups, billing, Cost Management, usage |
| AWS | Organization, Account, Billing | Assume role, organization and member accounts, IAM policies and simulated permissions, S3 and EC2 reads, Cost Explorer, Budgets, CUR, contact info |
| Organization, Project, Billing | Organization and project visibility, role bindings, permission check, Compute and Storage list, billing |
11Private providers
For providers not on the public registry. Upload the binary and terraform init will find it. Only linux_amd64 builds work.
Private Providers
Upload provider binaries for non-public providers. Stored in Terraform local-mirror layout for use during plan/apply.
Upload Provider
- Nectere Anvil runs on
linux_amd64, so only Linux x86-64 provider binaries can be loaded. - Upload the unpacked executable (e.g.
terraform-provider-customcloud_v1.0.0), not the HashiCorp release zip.
Packages reference it like any other provider: source = "acme/objstore", version = "1.4.2".
12Deploy
New deployments start here. Nothing changes in the cloud until Approve & Apply.
| Field | Notes |
|---|---|
| Package | Required. Enables Run Plan. |
| Tfvar Table | Filtered to the chosen package |
| Credential (optional) | -- None (set in provider block) -- when the provider block authenticates itself |
| Env Tables (optional) | Pick one, press +. × on a chip removes it. |
| Deployment Name | Required |
| Advanced › Parallelism | 1 to 30; blank means Terraform's default of 10. Lower it if a provider rate-limits you. Saved with the deployment for every later run. |
| Advanced › Terraform -var table | An env table passed as -var flags instead of exported |
- Run Plan. The Output badge goes running and the log streams in.
- When it finishes the console switches to Tree, the deployment is
planned, and Approve & Apply and Topology are enabled. - Approve & Apply applies the saved
tfplan. On success the deployment isapplied, its outputs and a state snapshot are captured, and it moves to Deployments.
Unapplied plans don't appear in Deployments or on the dashboard. Ones older than 24 hours are deleted the next time a deployment is created or Anvil restarts.
13Reading a plan
View raw is the full log, Anvil's own lines included. Tree shows one collapsible node per resource with an action pill, errors and warnings first, summary pinned on top.
| Pill | Meaning |
|---|---|
| + create | New resource |
| ~ update | Changed in place |
| -/+ replace | Destroyed and recreated because a field forces replacement |
| - destroy | Removed |
| <= read | Data source read during apply |
| ~ change | Changed outside Terraform, found as drift on refresh |
When a run errors, the summary turns grey and gets (not applied - see errors above). Its counts only show how far Terraform got before stopping. A log with no plan in it shows No structured plan detected — use View raw.
14Deployments
Every deployment applied at least once. Rows show status, parallelism if set, package id and last run. The remaining actions are under More ▾.
| Status | Badge | Means |
|---|---|---|
| applied | applied | Last apply or drift check matched the configuration |
| skewed | skewed | A drift check found live infrastructure differs from the configuration |
| failed | failed | Last apply or destroy failed |
| destroyed | destroyed | Resources torn down. Record and history stay until Remove. |
| new, planned | planned | Not applied, so not listed |
| Action | Needs | Does |
|---|---|---|
| More › Log | view | Run modal on the latest run, with past runs in a dropdown |
| More › Outputs | view | Values captured after the last apply |
| More › Topology | view | Dependency graph from the last plan |
| More › Edit Plan | upload_package | Edit this deployment's copy of the .tf files |
| More › Edit Tfvars | manage_varsets | Edit the linked tfvar table |
| More › Env / Vars | plan | Choose which env tables and -var table load |
| More › Download | view | Deployment folder as a zip |
| Drift | plan | Schedule drift checks |
| Status | view | Drift check, refresh-only, state list, what drifted, accept drift, and Destroy |
| Re-apply | apply + plan | Plans, then applies on Confirm & Apply |
| Remove | destroy | Deletes the record, state and history. Cloud resources keep running. |
| Clear Running Jobs | plan | Cancels every run stuck in running |
The run modal
Log, Re-apply, Status and Destroy share one modal: run history dropdown on top, console in the middle, hint line and confirm button at the bottom. A run in progress also gets Cancel run.
Re-apply: aks-csi-test
succeeded| Hint line | When |
|---|---|
| Planning… review the changes before applying. | Re-apply plan still running |
| Review the plan above, then confirm to apply these changes. | Plan done, Confirm & Apply enabled |
| Plan failed — nothing was changed. | Plan errored |
| Applying… / Apply complete. / Apply failed. | After Confirm & Apply |
| Running status check… / Status check complete. / Status check failed. | Any Status action |
| Destroying… / Destroy complete. / Destroy failed. | After Destroy |
Status, drift and destroy
Status opens with read-only checks. Destroy is enabled only after one of them finishes.
Status: aks-workload-multi-01
Drift Check: aks-csi-test
Runs terraform plan -detailed-exitcode on a schedule. If live infrastructure has drifted from the configuration, the deployment is marked skewed.
Last check: 6h ago
| Status action | Runs | Ends at |
|---|---|---|
| Drift check | terraform plan -detailed-exitcode | Exit 0 → applied, exit 2 → skewed |
| Refresh-only | terraform plan -refresh-only | Status unchanged |
| List resources | terraform state list | Status unchanged |
| What drifted? | Reads the last captured plan JSON | Opens the drift detail |
| Accept drift into state | terraform apply -refresh-only, then a drift check | applied or skewed |
| Destroy | terraform destroy -auto-approve | destroyed; outputs and state snapshot cleared |
Schedules are checked every minute. Hourly fires 60 minutes after the last check; daily fires in the chosen hour if 2 hours have passed. Destroyed deployments are skipped. Drift and status runs time out at 5 minutes, plan, apply and destroy never do. Each deployment keeps its 10 newest drift-check runs.
What drifted?
Before and live values for each drifted resource, plus the live value as HCL. Accepting drift only updates state. Paste the HCL into the package too or the next plan reverts it.
Drift: aks-workload-multi-01
From plan run 20261026031502a91c. Before is what Terraform had recorded; live is what it found. Accepting drift writes the live values into state; to stop Terraform planning them away, paste the live value into the package as well.
kubernetes_stateful_set_v1.appupdate6
4
replicas = 4
kubernetes_persistent_volume_claim_v1.data[5]deleteOutputs, env tables and remove
Outputs: aks-csi-test
object · 4 keys
location = "eastus"node_count = 3
node_vm_size = "Standard_D4s_v5"
csi_plugin = "v1.1.0"
Env Tables: aks-csi-test
Select which env tables load when this deployment runs. Takes effect on the next run.
Remove: sa-logs-test
This deletes the deployment record, its local state, and run history. It does not touch cloud resources.
15Topology
Left-to-right dependency graph from the last captured plan. Outline colour is the planned action; dashed boxes are data sources. Clicking a node traces it: what it needs turns purple, what depends on it teal, and the rest fades.
- Search glows orange on nodes whose name, type or values match.
- Variable highlights resources using the chosen tfvar value.
- Values switches between config, planned and applied (state) values. State is disabled until there is some.
- Only changes + neighbours hides untouched resources unless they border a changing one. On by default when only part of the deployment changes.
- Hovering a node shows its reconstructed
resource "type" "name" { … }block; 📌 keeps it open for copying. References are tinted, drifted values get# live · state was …, unknowns read(known after apply). - The side panel lists live names, instance values and both directions of dependency. Drag its left edge to resize, double-click to reset.
16Command Station
A bash shell in the deployment's working directory with its credential and env tables loaded, for jobs like terraform import, state mv or a targeted apply. The session is recorded in run history as a console run.
- An open console blocks plan and apply on that deployment and counts toward the run limit.
- Only users with access to the tile can reach the terminal, shared link or not.
- A -var table applies to plan, destroy, import and refresh in the console, but not apply.
- Closing the tab ends the session, as does Close Session after a confirm. The transcript is saved either way.
17Users and permissions
No roles, just eight permissions ticked per user. The first account has all of them; new users start with view.
Users
Manage user accounts and the permissions each one has.
Edit User
| Permission | Lets you |
|---|---|
| view | Lists, Inspect, logs, outputs, topology, downloads |
| upload_package | Packages, Builder, Edit Plan, private providers |
| manage_varsets | Tfvar tables, env tables, Edit Tfvars |
| manage_credentials | Credentials, including Test and Access |
| plan | Run Plan, Deploy from a tfvar table, drift schedules, Env / Vars, Clear Running Jobs |
| apply | Approve & Apply, accept drift. Re-apply with plan. |
| destroy | Destroy and Remove. Command Station with apply. |
| manage_users | Users and Backup panels |
Under Array sign-on the panel shows User accounts are managed in Nectere Array.
18Backup and restore
Backup & Restore
Export or import all configuration and state.
Export downloads everything under /conf and /meta (settings plus deployments/state) as one .zip. Import overwrites all current configuration with an uploaded backup (no merge). Secrets stay encrypted with this deployment's ANVIL_CREDENTIAL_KEY. To restore on a different deployment, set that same key there first or the credentials won't decrypt. After importing, restart the app container to apply.
- Exports are named
nectereanvil-meta-YYYYMMDD-HHmmss.zipand hold everything Anvil knows about what it built. Store them carefully. - Import asks
This OVERWRITES all current configuration with the uploaded backup. Continue?, then needs a restart: State › Restart on the tile in Array.
19Deployment lifecycle
| Run status | Means |
|---|---|
| running | In progress. One per deployment at a time. |
| succeeded / failed | Finished |
| cancelled | Stopped by Cancel run or Clear Running Jobs |
| timed-out | Drift or status run passed 5 minutes |
Run dropdown labels: plan, apply, destroy, drift-check, accept-drift, console, or one of the three status checks.
Limits
| Limit | Message |
|---|---|
| One run per deployment | A run is already in progress for this deployment (run 20261026094112c41e, plan, started 09:41:12). Cancel it or restart the container to clear an orphan. |
| Run limit | Run limit reached: 6 of 6 runs in progress. Wait for one to finish, or raise ANVIL_MAX_CONCURRENT_RUNS in .env. |
| Apply without a saved plan | No saved plan for this deployment. Run Plan and review it before applying. |
20Activity log
Sign-ins, plans, applies, drift checks and console sessions are logged. Read it from the tile's Logs button in Array.
[2026-05-24T22:13:30.1773433+00:00] [INFO] [AUTH] Admin user 'admin' created via setup with full permissions [2026-05-28T17:19:13.3113728+00:00] [INFO] [AUTH] Login: admin [2026-05-28T17:20:02.4410215+00:00] [WARN] [AUTH] Failed login for 'mreyes' [2026-07-27T14:55:44.5596122+00:00] [INFO] [BUILDER] Provider load requested: hashicorp/azurerm 4.81.0 (docs) [2026-07-27T14:57:53.0138285+00:00] [INFO] [BUILDER] Package 'builder-smoke-test' saved from the builder (61add7492904) [2026-07-27T17:02:44.8181906+00:00] [INFO] [BUILDER] Package 94e69f3a1fe1 copied into builder workspace 946cd9d7d6fb [2026-10-26T03:00:04.1027731+00:00] [INFO] [DRIFT] Scheduled drift check started for 'aks-csi-test' (94c0e1b27d3a) [2026-10-26T09:41:12.9041127+00:00] [INFO] [DEPLOY] Plan started for deployment 5d21c8e0a4f7 [2026-10-26T09:47:55.0019843+00:00] [WARN] [RUN] Run 20261026094250e1d7 (drift-check) on deployment 7a1f03c9b2e4 exceeded 5 minutes - timed out [2026-10-26T10:05:31.7752040+00:00] [INFO] [CONSOLE] Console 20261026100531f00a opened on 94c0e1b27d3a by admin
Categories: AUTH, PACKAGE, BUILDER, DEPLOY, RUN, DRIFT, CONSOLE, Access, Backup.
21Things to know
- Apply uses the reviewed plan. If anything changed since, it fails and asks for a new plan.
- Deployments keep their own copy of the code. Package edits reach them only on re-apply. Edit Plan changes one deployment.
- Deployments lists package ids. The Packages panel maps them to names.
- Remove is not destroy. Anvil forgets the deployment and its resources keep running. Destroy first.
- Schedules use server time. That's the sidebar clock, usually UTC.
- Credentials in a backup are encrypted with the tile's
ANVIL_CREDENTIAL_KEY. The zip holds no readable secrets. Restore onto a tile with the same key value in its .env, or the credentials won't open. - Saving a tfvar table says "Varset". Old name, same thing.
22Troubleshooting
| You see | Do this |
|---|---|
| Run Plan stays greyed out | Pick a package. A credential isn't required. |
| Approve & Apply stays greyed out | The plan failed or is still running. Fix the errors at the top of Tree and plan again. |
A run is already in progress… | Wait, cancel it from the Run dropdown, or press Clear Running Jobs on Deployments. |
Run limit reached | Close idle consoles or wait. To raise the cap, set ANVIL_MAX_CONCURRENT_RUNS in the tile's .env in Array. |
| Credential Test fails | Expired secret or wrong tenant. Fix it, then run Access to see what it can do. |
| Plan can't find a private provider | Re-upload with the exact name and version the package asks for. Linux build only. |
| Destroy fails on something already deleted | Status › Accept drift into state, then Destroy again. |
Topology says No plan data captured yet | Run a Drift check to give it a plan. |
| Credentials won't open after restoring a backup | The tile's ANVIL_CREDENTIAL_KEY differs from the one that made the backup. Copy the original value into this tile's .env and restart it. |
| Signed out of Anvil when opening another Anvil tile | Switch both tiles to Array sign-on. |