cloudparts

Upjet

A code generator and runtime that turns a Terraform provider into a Crossplane provider, so Terraform’s resource code sits behind Kubernetes-style managed resources.

CNCF · Graduated
Type
Open source
Foundation
Cloud Native Computing Foundation, graduated. Part of Crossplane. Part of the Linux Foundation
License
Apache 2.0
Facts checked
13 October 2026

What it is

At build time Upjet reads a Terraform provider’s schema and generates Go types, CRDs and reconciling controllers that follow Crossplane’s resource model, plus example manifests and API docs scraped from the Terraform documentation. The provider author configures each resource in Go: its external name (how it is identified and imported), references to other resources, sensitive fields, and whether calls are sync or async. Only resources with an external name configuration get generated. Upjet builds the official AWS, Azure and GCP providers and more than 50 community ones, such as Grafana and GitHub.

At runtime the generated controllers run Terraform’s resource logic while users see only Kubernetes objects. The first versions wrote JSON and ran the Terraform CLI for every operation. Since 2024 the controllers call the provider’s Go code directly (Terraform Plugin SDK v2) or speak its plugin protocol (Plugin Framework), with the CLI kept as one of three modes. Upjet v2 (October 2025) generates both the legacy cluster-scoped resources and namespaced ones for Crossplane v2, in API groups with an .m. such as s3.aws.m.upbound.io.

Why teams pick it

  • Broad coverage fast: anything with a Terraform provider can become a set of managed resources, reusing resource logic the Terraform community already maintains
  • Skipping the Terraform CLI made providers much cheaper: creating 1,000 AWS RolePolicyAttachments, average time to ready fell from 19.55 minutes to about 1 second and peak memory from 1.79 GiB to 421 MiB
  • Generates the repetitive parts at scale (CRDs, controllers, references, examples, docs) and adds frameworks for API version conversion and migrating between providers
  • Apache 2.0 under the Crossplane project; without the Terraform CLI, providers avoid HashiCorp’s BSL license, and Terraform providers themselves stay MPL 2.0

Watch out for

  • Resources behave the way the underlying Terraform provider does, and each one needs hand-written external name configuration before it is generated
  • Its own README says to write a native provider instead when no Terraform provider exists, when you need custom reconcile logic or exact control of the API, or when performance needs exceed what Terraform execution allows
  • Generated providers are large: a whole cloud means hundreds of CRDs, and Terraform schema changes have to be managed as CRD version changes
  • It is a framework for provider authors in Go; most Crossplane users only meet it through the providers it generates

How it fits with other technologies

Configuring two GitHub resources in an Upjet provider, then `make generate` · go

// config/external_name.go: which Terraform resources to generate, and how each is identified
var ExternalNameConfigs = map[string]config.ExternalName{
	"github_repository": config.NameAsIdentifier,
	"github_branch": config.TemplatedStringAsIdentifier("branch", "{{ .parameters.repository }}:{{ .external_name }}:{{ .parameters.source_branch }}"),
}

// config/namespaced/branch/config.go
func Configure(p *config.Provider) {
	p.AddResourceConfigurator("github_branch", func(r *config.Resource) {
		r.ShortGroup = "branch"
		// a Branch refers to a Repository object instead of a raw name
		r.References["repository"] = config.Reference{
			Type: "github.com/myorg/provider-github/apis/namespaced/repository/v1alpha1.Repository",
		}
	})
}

See it in a real stack

No architecture uses Upjet yet. Drop it into the playground and wire it up yourself.Open playground →
Where it came from2021–2025

Crossplane is only as useful as its providers, and writing a controller by hand for every cloud resource was slow; the sheer number of resources was “way too many for one to develop one by one”. Earlier code generators, such as crossplane-tools and the AWS Controllers for Kubernetes pipeline, still needed resource-specific logic written by hand, because cloud APIs are not consistent enough for one generic pipeline. Terraform providers had already solved the same problem of talking to each cloud’s API.

Muvaffak Onus’s Terrajet design, approved by the Crossplane maintainers, chose to drive the Terraform CLI with generated JSON. It called the CLI “not the cleanest option”, but the fastest route to broad coverage, reusing Terraform’s tested diff and import logic. Terrajet appeared in 2021 with AWS, Azure and GCP providers; Upbound then created Upjet as its successor in 2022 to build its own official providers, and Terrajet was archived.

In September 2023 Upbound donated Upjet and its AWS, Azure and GCP providers to the Crossplane project. The Crossplane blog called it a governance change that stops Upbound from ever relicensing them under something more restrictive. In March 2024 the providers stopped running the Terraform CLI, which Upbound said was never built for continuous reconciliation, and called the provider code directly, for up to four times lower running cost. Upjet 1.0 shipped in November 2023, and 2.0, for Crossplane v2’s namespaced resources, in October 2025.

Why it’s built this way

Generate from Terraform providers, not from cloud APIs
Cloud APIs are too inconsistent for one generic generator, and Terraform providers already handle each API’s quirks, so the community could “build on top of the shoulders of the giants”.
Drive the Terraform CLI at first
Providers are written to be consumed by the CLI and hide much of their logic; calling the CLI gave Terraform’s own diff and import behavior and the quickest path to coverage, behind an interface that could change later.
Later bypass the CLI
The CLI caused heavy overhead in a control plane that reconciles continuously, and fixing it would have been too large an effort; calling providers directly also removed the BSL question.
Donate it to the CNCF project
Moving from a vendor project under Upbound’s CLA to Crossplane governance means Upjet and its providers must stay Apache 2.0.

Timeline

  1. 2021

    Terrajet, Upjet’s predecessor, starts generating Crossplane providers through the Terraform CLI

  2. 2022

    Upbound releases Upjet as Terrajet’s successor

  3. 2023

    Donated to the Crossplane project in September; Upjet 1.0 in November

  4. 2024

    Generated providers drop the Terraform CLI and call provider code directly

  5. 2025

    Upjet 2.0 in October generates namespaced resources for Crossplane v2

Sources7 · facts checked 13 October 2026

Improve this page on GitHub