Skip to content

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.

Educational use only. These providers exist to learn how the big clouds' Terraform resources map onto a small, real cloud — not to run production estates. They cover a deliberately narrow slice of each cloud, translate loosely (many fields are ignored, some are faked for compatibility), and are distributed as a local binary, not a signed registry release. For real workloads on Kubrain, use Blueprints or the CLI.

Install

Each provider lives in its own repository:

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-aws

Do 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:

  1. the provider block — token/endpoint (Yandex) or kubrain_token/kubrain_endpoint (AWS);
  2. $KUBRAIN_TOKEN / $KUBRAIN_API;
  3. ~/.kubrain/config (written by kubrain auth login);
  4. 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"
    }
  }
}
ResourceKubrain backing
yandex_vpc_networkVPC (name maps)
yandex_vpc_subnetpassthrough (Kubrain fuses network + subnet)
yandex_kubernetes_clusterCluster control plane — zonal master → tier normal, regional → ha; async, waits for running
yandex_kubernetes_node_groupWorker pool — fixed_scale.size → workers, resources.memory → RAM
yandex_storage_bucketBucket — acl public-* → anonymous read
yandex_iam_service_accountvirtual (Kubrain identity is the tenant token)
yandex_iam_service_account_static_access_keyTenant S3 credentials (real, working keys)
yandex_dns_zoneDNS zone — zone → domain
yandex_dns_recordsetDNS record set
yandex_container_registryvirtual — 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"
    }
  }
}
ResourceKubrain backing
aws_vpcVPC — name comes from the Name tag; cidr_block is ignored (auto-allocated)
aws_subnetpassthrough — its id equals its vpc_id
aws_eks_clusterCluster control plane — vpc_config.subnet_ids[0] selects the VPC, version → k8s, kubrain-tier = "ha" tag → tier ha (else normal); async
aws_eks_node_groupWorker pool — scaling_config.desired_size → workers, instance_types[0] → RAM
aws_s3_bucketBucket — inline acl public-* → anonymous read
aws_s3_bucket_acl / aws_s3_bucket_public_access_blockReconcile the bucket’s public flag
aws_iam_role / aws_iam_uservirtual — synthesized ARNs so references resolve
aws_iam_access_keyTenant S3 credentials (real, working keys)
aws_route53_zoneDNS zone — name → domain, zone_id = domain
aws_route53_recordDNS record set
aws_ecr_repositoryvirtual — 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