Terraform
Kubrain ships two drop-in Terraform providers that present the resource
surface of a big cloud — Yandex Cloud (yandex_*) and AWS (aws_*) — but
provision everything on Kubrain. An existing configuration runs against Kubrain by
changing only the source in required_providers; arguments that have no Kubrain
equivalent are accepted and silently ignored.
Install
Each provider lives in its own repository:
- AWS — github.com/kubrain-dev/terraform-provider-aws
- Yandex Cloud — github.com/kubrain-dev/terraform-provider-yandex
They aren’t published to a Terraform registry. Instead you get the binary — either a
prebuilt release asset or a local build — and point Terraform at it with a
dev_overrides block, which skips terraform init entirely.
Download a release binary
Grab the asset for your OS/architecture from the repo’s Releases page and save it
under the name Terraform expects (terraform-provider-<type>):
mkdir -p ~/.kubrain/tf
# pick the asset matching your platform (linux_amd64, linux_arm64,
# darwin_amd64, darwin_arm64, windows_amd64)
curl -fsSL -o ~/.kubrain/tf/terraform-provider-aws \
https://github.com/kubrain-dev/terraform-provider-aws/releases/latest/download/terraform-provider-aws_linux_amd64
chmod +x ~/.kubrain/tf/terraform-provider-awsDo the same for terraform-provider-yandex if you want the Yandex provider. No Go
toolchain needed.
Build from source
Alternatively, build it yourself (Go 1.23+):
git clone https://github.com/kubrain-dev/terraform-provider-aws
cd terraform-provider-aws && go build -o terraform-provider-aws .Point Terraform at the binary
Either way, add a dev_overrides block to ~/.terraformrc (or $TF_CLI_CONFIG_FILE)
pointing at the directory that holds the binary:
provider_installation {
dev_overrides {
# the directory containing the binary (e.g. ~/.kubrain/tf from above)
"kubrain.dev/kubrain/aws" = "/home/you/.kubrain/tf"
"kubrain.dev/kubrain/yandex" = "/home/you/.kubrain/tf"
}
direct {}
}With dev_overrides set, run terraform plan / terraform apply directly — do
not run terraform init.
Authentication
Both providers authenticate with your Kubrain tenant token, resolved in this order:
- the provider block —
token/endpoint(Yandex) orkubrain_token/kubrain_endpoint(AWS); $KUBRAIN_TOKEN/$KUBRAIN_API;~/.kubrain/config(written bykubrain auth login);- the default endpoint
https://api.kubrain.dev.
The big cloud’s own credential arguments (cloud_id, folder_id, region,
access_key, secret_key, profile, …) are accepted and ignored, so a
provider "aws" {} / provider "yandex" {} block usually needs no edits.
export KUBRAIN_TOKEN=<your tenant token>Yandex Cloud provider
terraform {
required_providers {
yandex = {
source = "kubrain.dev/kubrain/yandex"
}
}
}| Resource | Kubrain backing |
|---|---|
yandex_vpc_network | VPC (name maps) |
yandex_vpc_subnet | passthrough (Kubrain fuses network + subnet) |
yandex_kubernetes_cluster | Cluster control plane — zonal master → tier normal, regional → ha; async, waits for running |
yandex_kubernetes_node_group | Worker pool — fixed_scale.size → workers, resources.memory → RAM |
yandex_storage_bucket | Bucket — acl public-* → anonymous read |
yandex_iam_service_account | virtual (Kubrain identity is the tenant token) |
yandex_iam_service_account_static_access_key | Tenant S3 credentials (real, working keys) |
yandex_dns_zone | DNS zone — zone → domain |
yandex_dns_recordset | DNS record set |
yandex_container_registry | virtual — auto-provisioned per tenant; exposes host |
resource "yandex_vpc_network" "net" {
name = "prod-net"
}
resource "yandex_kubernetes_cluster" "cluster" {
name = "shop"
network_id = yandex_vpc_network.net.id
master {
version = "v1.31.4"
zonal { zone = "ru-central1-a" } # zonal → tier `normal`
}
}
resource "yandex_kubernetes_node_group" "workers" {
cluster_id = yandex_kubernetes_cluster.cluster.id
scale_policy { fixed_scale { size = 2 } } # → 2 workers
instance_template {
resources { memory = 8 } # → 8 GiB per worker
}
}AWS provider
terraform {
required_providers {
aws = {
source = "kubrain.dev/kubrain/aws"
}
}
}| Resource | Kubrain backing |
|---|---|
aws_vpc | VPC — name comes from the Name tag; cidr_block is ignored (auto-allocated) |
aws_subnet | passthrough — its id equals its vpc_id |
aws_eks_cluster | Cluster control plane — vpc_config.subnet_ids[0] selects the VPC, version → k8s, kubrain-tier = "ha" tag → tier ha (else normal); async |
aws_eks_node_group | Worker pool — scaling_config.desired_size → workers, instance_types[0] → RAM |
aws_s3_bucket | Bucket — inline acl public-* → anonymous read |
aws_s3_bucket_acl / aws_s3_bucket_public_access_block | Reconcile the bucket’s public flag |
aws_iam_role / aws_iam_user | virtual — synthesized ARNs so references resolve |
aws_iam_access_key | Tenant S3 credentials (real, working keys) |
aws_route53_zone | DNS zone — name → domain, zone_id = domain |
aws_route53_record | DNS record set |
aws_ecr_repository | virtual — exposes repository_url |
resource "aws_vpc" "net" {
cidr_block = "10.0.0.0/16" # ignored (Kubrain auto-allocates)
tags = { Name = "prod-net" } # becomes the Kubrain VPC name
}
resource "aws_subnet" "a" {
vpc_id = aws_vpc.net.id
}
resource "aws_eks_cluster" "cluster" {
name = "shop"
version = "v1.31.4"
vpc_config {
subnet_ids = [aws_subnet.a.id] # [0] resolves to the Kubrain VPC name
}
}
resource "aws_eks_node_group" "workers" {
cluster_name = aws_eks_cluster.cluster.name
node_group_name = "workers"
scaling_config { desired_size = 2 } # → 2 workers
instance_types = ["t3.large"] # → 8 GiB per worker (lookup table)
}The AWS node group sizes workers from instance_types via a small lookup table
(t3.medium = 4 GiB, t3.large = 8 GiB, m5.large = 8 GiB, c5.large = 4 GiB,
r5.large = 16 GiB, …); an unrecognized type falls back to a default with a
warning.
Mapping caveats
The same semantics apply to both providers — they’re the interesting part of the translation:
- Clusters and node groups are one object. Kubrain couples the control plane and workers. The cluster resource creates it (tier from the master block or a tag); the node group sets the worker count/RAM via scale + resize. Only one worker pool exists per cluster, so multiple node groups collapse onto it (last apply wins) and emit a warning.
- Cluster create is asynchronous. It blocks until Kubrain reports
running(minutes — real VMs boot). A Kubernetes version bump maps to an in-place upgrade; changing the network or tier forces replacement. - Server-decided fields. Kubrain allocates a VPC’s CIDR itself, so a
configured
cidr_block/ subnet CIDR is accepted but not honored. Omit it to see the CIDR Kubrain actually assigned. - S3 credentials are tenant-wide. Every static access key resolves to the same real, working key pair for your tenant’s buckets.
- Bucket visibility is fixed at creation and immutable; the separate AWS ACL resources reconcile read-only and warn on a mismatch rather than mutating.
- Non-empty buckets can’t be deleted — empty the bucket first (via S3), then destroy.
See also
- Blueprints — the native declarative surface for real workloads · Clusters · Buckets · Pricing.