RKE2 Terraform Automation for Airgapped and Cloud Deployments

RKE2 is a CNCF-conformant Kubernetes distribution that emphasizes security, minimalism, and ease of operation. Provisioning RKE2 clusters at scale with repeatable, declarative workflows is where Terraform becomes essential. Terraform allows infrastructure for RKE2 control planes, worker node pools, load balancers, and supporting components to be defined as code, reducing manual toil and enabling both airgapped on-prem environments such as Harvester and cloud-native AWS deployments.

The reference implementations demonstrate two distinct patterns: an airgapped RKE2 and Rancher bootstrap on Harvester using prebuilt VM images and an AWS-focused module set that provisions RKE2 servers, a Network Load Balancer control plane, and autoscaling agent node pools. Both patterns rely on Terraform for stateful, declarative provisioning and share common concerns around image distribution, kubeconfig handling, and secure bootstrapping.

Airgapped RKE2 Provisioning with Terraform

Airgapping is a security posture where a Kubernetes environment has no direct connectivity to the public internet. This is usually called generically an Image Store. This location is a centralized source of truth that your RKE2 cluster, Rancher Cluster Manager, and all downstream clusters in this environment can pull their container images from as opposed to the public cloud.

In order to do this, you'll need to pull down all of these images onto a local workstation and then either copy them into your soft-airgap via jumpbox or bring into your hard airgap using physical media like BluRay or a USB key drive. Be aware that the total image size is >30Gbi! The instructions for this process and some very helpful scripts are provided here and details around them defined in the manual install doc.

Provisioning with Terraform is an infrastructure provisioning tool that we can use to spin up airgapped RKE2 and Rancher instances in a more automated way. This also provides the benefits of defining these components in a declaractive, stateful way. This greatly reduces toil and mostly eliminates all of the steps in the previous section.

We can also find a shortcut in this process speed-wise by creating prebuilt VM images that already contain every necessary component on them and them import those VM images into our airgap as well.

Harvester OS Image Preparation

Prep Harvester's Ubuntu OS Image. Please see the doc located in the vm image doc to dive into how I created the OS images here.

The airgap workflow for Harvester begins with a prebuilt Ubuntu OS image that contains RKE2 and all required airgap components. Terraform then imports the image and creates virtual machines from it. The variable configuration for the image is typically defined in terraform.tfvars.

The final terraform.tfvars file example for Harvester is:

hcl ubuntu_image_name = "ubuntu-rke2-airgap-harvester" rke2_image_url = "http://10.10.0.248:9900/ubuntu-rke2-airgap-harvester.img" rke2_registry = "harbor.sienarfleet.systems" rancher_server_dns = "rancher.home.sienarfleet.systems" master_vip = "10.10.5.4"

Due to Terraform's cloud-connected nature, we need to prepopulate our terraform directory with everything we need to run within our environment and then copy everything into our airgap:

bash cd terraform terraform init terraform plan

Terraform Tour. One of the powerful features of Terraform is it allows us to templatize things like cloud-init where we can write a generic expression that then can work for all similar cases with simple variable tweaks. We're not crazy here, but I've extracted a few hardcoded values into a variables.tf file that allows us to define some defaults and potentially override them later using the above-defined terraform.tfvars file if we want to try different options out.

Internally kube-vip is used to define an additional static IP for my control-plane node to support HA control-planes and generally keep things simpler. Part of that is defining the IP I want, which is a variable called master_vip.

Terraform Execution Flow for Harvester

Resource actions are indicated with the following symbols:
+ create

Terraform will perform the following actions:
...
Plan: 9 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes

Example resource creation output:

harvester_image.ubuntu-rke2: Creating... tls_private_key.rsa_key: Creating... tls_private_key.rsa_key: Creation complete after 1s [id=1159e974801abcb6659d1f966364998a2afb60c4] harvester_ssh_key.rke2-key: Creating... harvester_ssh_key.rke2-key: Creation complete after 0s [id=default/rke2-key] harvester_image.ubuntu-rke2: Still creating... [10s elapsed] harvester_image.ubuntu-rke2: Still creating... [20s elapsed] harvester_image.ubuntu-rke2: Still creating... [30s elapsed] harvester_image.ubuntu-rke2: Creation complete after 30s [id=default/ubuntu-rke2-airgap-harvester] harvester_virtualmachine.cp-node: Creating... ... harvester_virtualmachine.cp-node: Still creating... [2m0s elapsed] harvester_virtualmachine.cp-node (remote-exec): Completed cloud-init! harvester_virtualmachine.cp-node: Creation complete after 2m10s [id=default/rke2-airgap-cp] ... harvester_virtualmachine.worker-node: Still creating..

In this repo, we're going to cover the creation of a RKE2 cluster into Harvester using an airgapped process aka Bootstrap! We'll follow that up by installing Rancher on top of RKE2. We're going to do all of this using Terraform. Terraform allows us to declaratively define our environment in code and then create that environment in a straight-forward and sustainable way.

Watch the Video. Learn the ins and outs of airgap-installations for RKE2 and Rancher Multi-Cluster Manager MCM onto Harvester with RGS Field Engineer, Brian Durden watch the video to learn more.

TL;DR. You're here and you want to go REAL fast? There's a couple things you'll need to ensure are setup before you're done. All of that is defined here and within the manual install doc.

Kubeconfig Retrieval and Correction

A repository that creates a RKE2 kubernetes cluster using Terraform.

In the file terraform.tfvars you can set the password. The default is "softica".

To create a new password, run the following command and give it the password when prompted:

bash mkpasswd --method=SHA-512 --rounds=4096

or

bash echo "your-password" | mkpasswd --method=SHA-512 --rounds=4096 -

The password is needed by terraform, so we keep a version of it in plain text as well. So remember to change the variable kubernetesnodesshpasswordplain as well.

SSH into the first master node and run the following command

bash cat /etc/rancher/rke2/rke2.yaml

or get it to a local file like this with the ssh username and ssh password you have chosen.

The command will overwrite your current config file !

bash ssh [email protected] cat /etc/rancher/rke2/rke2.yaml > ~/.kube/config

In the config file, it refers to the conrolplane at 127.0.0.1 which doesnt work for us. So edit the file and change 127.0.0.1 with the ip of the control plane node default 10.18.3.10.

AWS RKE2 Terraform Modules

The AWS implementation provides two terraform modules intended for user consumption for provisioning RKE2 server nodes and controlplane load balancer and provisioning Auto Scaling Group of agents to auto-join cluster.

The deployment model of this repository is designed to feel very similar to the major cloud providers kubernetes distributions. It revolves around provisioning the following:

  • Nodepools: Self-bootstrapping and auto-joining groups of EC2 instances
  • AWS NLB: Controlplane static address

This iac leverages the ease of use of rke2 to provide a simple sshless bootstrapping process for sets of cluster nodes, known as.

Module Usage

Provision rke2 server(s) and controlplane loadbalancer

hcl module "rke2" { source = "git::https://github.com/rancherfederal/rke2-aws-tf.git" name = "quickstart" vpc_id = "vpc-###" subnets = ["subnet-###"] ami = "ami-###" }

Provision Auto Scaling Group of agents to auto-join cluster

hcl module "rke2_agents" { source = "git::https://github.com/rancherfederal/rke2-aws-tf.git//modules/agent-nodepool" name = "generic" vpc_id = "vpc-###" subnets = ["subnet-###"] ami = "ami-###" # Required input sourced from parent rke2 module, contains configuration that agents use to join existing cluster cluster_data = module.rke2.cluster_data }

For more complete options, fully functioning examples are provided in the examples/ folder to meet the various use cases of rke2 clusters on AWS, ranging from:

examples/quickstart
: bare minimum rke2 server/agent cluster, start here!
examples/cloud-enabled
: aws cloud aware rke2 cluster

The AWS Cluster Autoscaler will configure rke2 to autoscale based off kubernetes resource requests.

Provider and Module Requirements

Name Version
terraform >= 1.3
aws >= 5.32
cloudinit >= 2
random >= 3
Name Version
aws >= 5.32
cloudinit >= 2
random >= 3

Module Components

Name Source Version
cp_lb ./modules/nlb n/a
iam ./modules/policies n/a
init ./modules/userdata n/a
servers ./modules/nodepool n/a
statestore ./modules/statestore n/a

Resources and Data Sources

Name Type
awsiamrolepolicy.awsautoscaler resource
awsiamrolepolicy.awsccm resource
awsiamrolepolicy.awsrequired resource
awsiamrolepolicy.gettoken resource
awsiamrolepolicy.putkubeconfig resource
awssecuritygroup.cluster resource
awssecuritygroup.server resource
awssecuritygrouprule.clusteregress resource
awssecuritygrouprule.clustershared resource
awssecuritygrouprule.servercp resource
awssecuritygrouprule.servercp_supervisor resource
random_password.token resource
random_string.uid resource
awsiampolicydocument.awsautoscaler data source
awsiampolicydocument.awsccm data source
awsiampolicydocument.awsrequired data source
awsiamrole.provided data source
cloudinit_config.this data source

Input Variables

Name Description Type Default Required
ami Server pool ami string n/a yes
associatepublicip_address n/a bool ...

When setting the 'asg' variable, you now must also set the 'termination_policy' value.

Operational Considerations

Terraform's declarative nature ensures that RKE2 clusters are reproducible. For airgapped environments, the workflow emphasizes pre-staging artifacts: container images, VM images, and Terraform providers must be present locally before init and plan. The image store becomes the single source of truth for all RKE2 and Rancher components.

In cloud environments, the module set abstracts node pool provisioning, NLB creation, IAM policies for AWS autoscaler and CCM, and state store configuration. The sshless bootstrapping relies on cloud-init templates generated by Terraform, with random tokens and user data handling secrets securely.

Password handling for SSH access in the Harvester example remains a manual variable override. The default password is softica and can be changed using mkpasswd with SHA-512 and 4096 rounds. Both the hashed password in terraform.tfvars and the plain text variable kubernetesnodesshpasswordplain must be updated together.

Kubeconfig generated on the first master node references 127.0.0.1 for the control plane. This must be edited to the actual control plane IP, such as 10.18.3.10, for external access. The master_vip variable using kube-vip allows HA control planes with a stable virtual IP.

Conclusion

RKE2 Terraform automation delivers consistent, repeatable provisioning across both airgapped on-prem Harvester deployments and AWS cloud environments. The Harvester pattern demonstrates how Terraform can orchestrate prebuilt Ubuntu OS images, Harvester VM resources, TLS keys, SSH keys, and kube-vip VIPs to deliver a fully airgapped RKE2 and Rancher stack with minimal manual intervention. Image store management, physical media transfer, and >30Gbi image sizes are handled via declarative pipelines and helper scripts.

The AWS pattern shows how modular Terraform can provision RKE2 server node pools with NLB control plane endpoints and autoscaling agent node pools that self-bootstrap and auto-join. Provider constraints, IAM policy documents, security groups, and cloud-init data sources compose a cloud-aware RKE2 cluster that mirrors managed Kubernetes offerings while retaining RKE2's lightweight operational model.

Together these approaches illustrate the core strengths of Terraform with RKE2: declarative state, reusable modules, prebuilt images for airgap efficiency, and sshless node bootstrapping for cloud scale. Proper variable management for passwords, master VIPs, registry endpoints, and image URLs ensures environments remain secure, portable, and reproducible.

Sources

  1. https://github.com/hoeghh/rke2-terraform
  2. https://ranchergovernment.com/blog/airgapping-made-easy-with-rke2-and-rancher
  3. https://github.com/ranchergovernment/rke2-aws-tf

Related Posts