Terraform and OpenTofu
Write a project's infrastructure as code, keep it in git and make it with one apply. The provider is a client of the public API and nothing else: it sends the same requests the dashboard and the CLI send.
Install#
The provider installs from our own registry on ibel.ai, because the public registries are not reliable from inside Iran. It works with Terraform 1.5 or later and OpenTofu 1.6 or later.
terraform {
required_providers {
cloudhost = {
source = "ibel.ai/cloudhost/cloudhost"
}
}
}
provider "cloudhost" {}Sign in#
Make an API key (dashboard, Settings, API keys; or cloudhost keys create) and give it to the provider in the environment. Without it, the provider uses the key cloudhost login saved.
export CLOUDHOST_API_KEY=chk_live_…
terraform init
terraform plan| Setting | Default |
|---|---|
api_key | CLOUDHOST_API_KEY, then the key from cloudhost login |
api_url | CLOUDHOST_API_URL, then https://api.ibel.ai |
viewer and billing keys only read; team members and webhooks need admin, and changing a member's role owner. A key belongs to one team, and everything is made in that team.Resources#
Each resource has a data source of the same name that reads one that already exists, and every resource can be brought into the state with terraform import.
| Resource | What it makes | Import id |
|---|---|---|
cloudhost_project | a function or an app (kind); renamed in place | <name> |
cloudhost_environment_variable | a variable for some environments or one branch; plain or sensitive | <project>/<KEY> |
cloudhost_domain | a custom domain; the DNS records to set in records | <domain> |
cloudhost_database | a SQL database; the password and connection string once | <name> |
cloudhost_cache | a cache for any cache client and HTTP; the token once | <name> |
cloudhost_bucket | a bucket; the team's storage key with its first bucket | <name> |
cloudhost_alert | an alert rule; any change replaces the rule | <id> |
cloudhost_team_member | an invite, until the person joins; role and project limits | <email> |
cloudhost_webhook | a signed webhook for the team or one project; the secret once | <id> |
A whole project#
resource "cloudhost_project" "api" {
name = "shop-api"
kind = "function"
}
resource "cloudhost_database" "shop" {
name = "shop"
}
resource "cloudhost_cache" "sessions" {
name = "shop-sessions"
max_memory_mb = 128
}
resource "cloudhost_environment_variable" "database_url" {
project = cloudhost_project.api.name
key = "DATABASE_URL"
value = cloudhost_database.shop.connection_string
type = "sensitive"
}
resource "cloudhost_domain" "api" {
project = cloudhost_project.api.name
name = "api.example.ir"
}
resource "cloudhost_alert" "errors" {
project = cloudhost_project.api.name
metric = "error_rate"
op = ">"
threshold = 0.05
channels = { email = true }
}
resource "cloudhost_webhook" "deploys" {
url = "https://hooks.example.ir/deploys"
events = ["deployment.ready", "deployment.failed"]
project = cloudhost_project.api.name
}
output "dns_records" {
value = [for r in cloudhost_domain.api.records : "${r.host} ${r.type} ${r.value}"]
}Ship code with cloudhost deploy or a git push as before; Terraform makes what the code runs on. A changed environment variable applies at once, without a new deployment.
Secrets in the state#
A database password, a cache token, the storage secret and a webhook's signing secret are shown by the API once, when they are made. The provider keeps them in the state so other resources can use them; keep the state where only your team can read it, and encrypted.
- After an import these values are not known. For a database, change
password_rotationto get a new password. - A
sensitivevariable's value never comes back; Terraform keeps the configured one. Aplainvalue is read back, so a change made outside Terraform shows in the plan.
What does not change in place#
- Databases, caches and buckets do not change in place: another
max_memory_mbis a new, empty cache. Read the plan. - A database, cache or bucket a live version binds cannot be deleted, nor a bucket with objects in it. Remove the binding (deploy without it) or empty the bucket first.
- Alerts have no update: a changed rule is made again.
- A team member comes by invite; until they accept,
statusisinvited. A role change on an invite withdraws it and sends a new one. - A webhook's project does not change in place: a new webhook and a new secret.