Skip to content

Instantly share code, notes, and snippets.

@yankcrime
Last active July 13, 2022 13:59
Show Gist options
  • Save yankcrime/bc99114b7bcc67b6101e49bca00eb5cb to your computer and use it in GitHub Desktop.
Save yankcrime/bc99114b7bcc67b6101e49bca00eb5cb to your computer and use it in GitHub Desktop.
Terraform for Kubernetes and Rancher on existing nodes

Terraform example for deploying Kubernetes and Rancher on existing infrastructure

main.tf

resource "rke_cluster" "rancher" {
  ssh_agent_auth        = true
  ignore_docker_version = true
  kubernetes_version    = var.kubernetes_version

  dynamic "nodes" {
    for_each = var.rancher_nodes
    content {
      address           = nodes.key
      hostname_override = nodes.value
      user              = var.ssh_user
      role              = ["controlplane", "etcd", "worker"]
    }
  }
}

resource "kubernetes_namespace" "cattle-system" {
  metadata {
    name = "cattle-system"
  }
}

resource "kubernetes_secret" "tls_ca" {
  metadata {
    name      = "tls-ca"
    namespace = "cattle-system"
  }

  data = {
    "cacerts.pem" = file("${path.module}/resources/cacerts.pem")
  }
  depends_on = [
    rke_cluster.rancher
  ]
}

resource "kubernetes_secret" "tls_rancher_ingress" {
  metadata {
    name      = "tls-rancher-ingress"
    namespace = "cattle-system"
  }

  type = "kubernetes.io/tls"

  data = {
    "tls.crt" = file("${path.module}/resources/tls.crt")
    "tls.key" = file("${path.module}/resources/tls.key")
  }
  depends_on = [
    rke_cluster.rancher
  ]
}

resource "helm_release" "rancher" {
  name             = "rancher"
  chart            = "rancher"
  version          = var.rancher_version
  namespace        = "cattle-system"
  create_namespace = true
  repository       = "https://releases.rancher.com/server-charts/latest"
  timeout          = 600

  set {
    name  = "hostname"
    value = var.rancher_hostname
  }

  set {
    name  = "antiAffinity"
    value = "required"
  }

  set {
    name  = "ingress.tls.source"
    value = "secret"
  }

  set {
    name  = "privateCA"
    value = "true"
  }

  depends_on = [
    kubernetes_secret.tls_ca,
    kubernetes_secret.tls_rancher_ingress
  ]
}

resource "null_resource" "wait_for_rancher" {
  provisioner "local-exec" {
    command = <<EOF
while [ "$${resp}" != pong ]; do
  resp=$(curl -sSk -m 2 --insecure "https://$${RANCHER_HOSTNAME}/ping")
  echo "Rancher response: $${resp}"
  if [ "$${resp}" != "pong" ]; then
    sleep 10
  fi
done
EOF

    environment = {
      RANCHER_HOSTNAME = var.rancher_hostname
    }
  }
  depends_on = [
    helm_release.rancher
  ]
}

resource "rancher2_bootstrap" "admin" {
  provider = rancher2.bootstrap

  password  = var.admin_password
  telemetry = true

  depends_on = [
    null_resource.wait_for_rancher
  ]
}

resource "rancher2_token" "rancher-token" {
  provider = rancher2
}

resource "local_file" "kube_cluster_yaml" {
  filename   = "${path.root}/kube_config_cluster.yml"
  content    = rke_cluster.rancher.kube_config_yaml
  depends_on = [rke_cluster.rancher]
}

output "rancher_url" {
  value = var.rancher_hostname
}

data "rancher2_user" "admin" {
  username   = "admin"
  depends_on = [rancher2_bootstrap.admin]
}

resource "rancher2_auth_config_activedirectory" "activedirectory" {
  servers                         = var.ad_server
  tls                             = false
  port                            = 389
  service_account_username        = var.ad_username
  service_account_password        = var.ad_password
  test_username                   = var.ad_username
  test_password                   = var.ad_password
  default_login_domain            = var.ad_default_login_domain
  user_search_base                = var.ad_user_search_base
  group_search_base               = var.ad_group_search_base
  nested_group_membership_enabled = true
  access_mode                     = "unrestricted"
  allowed_principal_ids           = ["local://${data.rancher2_user.admin.id}", "activedirectory_group://CN=SRE,OU=Groups,DC=tetromino,DC=local"]

  count = var.enable_active_directory ? 1 : 0
}

resource "rancher2_global_role_binding" "sre" {
  name               = "sre"
  global_role_id     = "admin"
  group_principal_id = "activedirectory_group://cn=sre,${var.ad_group_search_base}"
}

resource "rancher2_global_role_binding" "dev" {
  name               = "dev"
  global_role_id     = "user"
  group_principal_id = "activedirectory_group://cn=Dev,${var.ad_group_search_base}"
}

providers.tf

provider "kubernetes" {
  config_context = "local"
  config_path    = local_file.kube_cluster_yaml.filename
}

provider "helm" {
  kubernetes {
    config_path = local_file.kube_cluster_yaml.filename
  }
}

provider "rancher2" {
  alias = "bootstrap"

  api_url   = "https://${var.rancher_hostname}"
  bootstrap = true
  insecure  = true
}

provider "rancher2" {
  api_url   = rancher2_bootstrap.admin.url
  token_key = rancher2_bootstrap.admin.token
  insecure  = true
}

variables.tf

variable "kubernetes_version" {
  description = "Version of Kubernetes to deploy"
  default     = "v1.19.3-rancher1-2"
}

variable "rancher_version" {
  description = "Version of Rancher Server to deploy"
  default     = "2.5.5"
}

variable "rancher_hostname" {
  description = "Rancher FQDN"
  default     = "rancher.192.168.1.210.dnsify.me"
}

variable "ssh_user" {
  description = "Username for SSH access to VMs"
  default     = "nick"
}

variable "admin_password" {
  description = "Rancher admin password"
  default     = "admin"
}

variable "downstream_kubernetes_version" {
  description = "Version of Kubernetes to deploy on downstream cluster"
  default     = "v1.19.4-rancher1-1"
}

variable "rancher_nodes" {
  type = map(string)
  default = {
    "192.168.1.86" = "rancher0"
    "192.168.1.9"  = "rancher1"
    "192.168.1.7"  = "rancher2"
  }
}

variable "downstream_cluster_nodes" {
  type = map(string)
  default = {
    "192.168.1.50" = "--controlplane --etcd"
    "192.168.1.51" = "--worker"
  }
}

variable "enable_active_directory" {
  description = "Whether or not to enable Rancher AD integration"
  default     = false
}

variable "ad_server" {
  description = "Active Directory server(s) IP or hostname"
  default     = [""]
}

variable "ad_username" {
  description = "Active Directory service account used for lookups"
  default     = ""
}

variable "ad_password" {
  description = "Active Directory password"
  default     = ""
}

variable "ad_user_search_base" {
  description = "AD user search base"
  default     = ""
}

variable "ad_group_search_base" {
  description = "AD group search base"
  default     = ""
}

variable "ad_default_login_domain" {
  description = "Default AD login domain"
  default     = ""
}

terraform.tfvars

rancher_hostname = "rancher.192.168.1.210.dnsify.me"

enable_active_directory = true
ad_username             = "TETROMINO\\SArancher"
ad_password             = "2a09a433-fa38-4d3f-9009-2527baf27434"
ad_server               = ["192.168.1.220"]
ad_user_search_base     = "dc=tetromino,dc=local"
ad_group_search_base    = "OU=Groups,DC=tetromino,DC=local"
ad_default_login_domain = "TETROMINO"

rancher_nodes = {
  "192.168.1.42" = "rancher0"
  "192.168.1.14" = "rancher1"
  "192.168.1.30" = "rancher2"
}

kubernetes_version = "v1.19.6-rancher1-1"

versions.tf

terraform {
  required_providers {
    helm = {
      source = "hashicorp/helm"
    }
    kubernetes = {
      source = "hashicorp/kubernetes"
    }
    local = {
      source = "hashicorp/local"
    }
    null = {
      source = "hashicorp/null"
    }
    rancher2 = {
      source = "rancher/rancher2"
    }
    rke = {
      source  = "rancher/rke"
      version = ">= 1.1.7"
    }
  }
  required_version = ">= 0.13"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment