dans_cloud_terraform/main.tf

491 lines
13 KiB
HCL

terraform {
required_providers {
helm = {
source = "hashicorp/helm"
version = "~> 2.11.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.23.0"
}
keycloak = {
source = "mrparkers/keycloak"
version = "~> 4.3.1"
}
proxmox = {
source = "telmate/proxmox"
version = "~> 2.9.8"
}
guacamole = {
source = "techBeck03/guacamole"
version = "~> 1.4.1"
}
}
backend "kubernetes" {
secret_suffix = "tfstate"
config_path = "~/.kube/config"
namespace = "dan-terraform"
}
}
provider "kubernetes" {
config_path = "~/.kube/config"
}
provider "helm" {
kubernetes {
config_path = "~/.kube/config"
}
}
provider "keycloak" {
client_id = "terraform"
client_secret = var.keycloak_client_secret
url = "https://keycloak.${var.domain_suffix}"
}
provider "guacamole" {
url = "https://guacamole.${var.domain_suffix}/guacamole"
token = var.guacamole_token
data_source = var.guacamole_datasource
}
provider "proxmox" {
pm_api_url = var.proxmox_api_url
pm_api_token_id = var.proxmox_token_id
pm_api_token_secret = var.proxmox_token
pm_tls_insecure = true
}
resource "helm_release" "keycloak" {
name = "keycloak"
namespace = var.keycloak_namespace
repository = "https://charts.bitnami.com/bitnami/"
chart = "keycloak"
create_namespace = true
values = [
<<-EOT
auth:
adminPassword: ${var.keycloak_admin_pass}
adminUser: admin
global:
storageClass: ${var.storageclass}
ingress:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: default-redirect-https@kubernetescrd
enabled: true
hostname: keycloak.${var.domain_suffix}
tls: true
postgresql:
auth:
password: ${var.postgres_password}
proxy: edge
EOT
]
}
resource "keycloak_realm" "realm" {
realm = var.keycloak_realm
display_name = var.keycloak_realm_display_name
enabled = true
default_signature_algorithm = "RS256"
}
resource "keycloak_openid_client" "gitea_client" {
realm_id = keycloak_realm.realm.id
client_id = "gitea"
enabled = true
access_type = "CONFIDENTIAL"
standard_flow_enabled = true
implicit_flow_enabled = true
client_secret = var.keycloak_gitea_secret
valid_redirect_uris = [
"https://git.${var.domain_suffix}/*"
]
}
resource "helm_release" "gitea" {
name = "gitea"
repository = "https://dl.gitea.com/charts/"
chart = "gitea"
namespace = var.gitea_namespace
create_namespace = true
values = [
<<-EOT
gitea:
oauth:
- autoDiscoverUrl: https://keycloak.${var.domain_suffix}/realms/${keycloak_realm.realm.realm}/.well-known/openid-configuration
key: ${keycloak_openid_client.gitea_client.client_id}
name: md1clv.im
provider: openidConnect
secret: ${keycloak_openid_client.gitea_client.client_secret}
global:
storageClass: ${var.storageclass}
ingress:
annotations:
traefik.ingress.kubernetes.io/router.middlewares: default-redirect-https@kubernetescrd
cert-manager.io/cluster-issuer: letsencrypt-prod
enabled: true
hosts:
- host: git.${var.domain_suffix}
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- git.${var.domain_suffix}
secretName: tls-gitea
persistence:
enabled: true
storageClass: ${var.storageclass}
EOT
]
}
resource "keycloak_openid_client" "nautobot_client" {
realm_id = keycloak_realm.realm.id
client_id = "nautobot"
enabled = true
access_type = "CONFIDENTIAL"
standard_flow_enabled = true
implicit_flow_enabled = true
client_secret = var.keycloak_nautobot_secret
valid_redirect_uris = [
"/*",
"https://nautobot.${var.domain_suffix}",
"https://nautobot.${var.domain_suffix}/*"
]
}
resource "helm_release" "nautobot" {
name = "nautobot"
namespace = var.nautobot_namespace
repository = "https://nautobot.github.io/helm-charts/"
chart = "nautobot"
create_namespace = true
values = [
<<EOT
ingress:
annotations:
traefik.ingress.kubernetes.io/router.middlewares: default-redirect-https@kubernetescrd
cert-manager.io/cluster-issuer: letsencrypt-prod
backendProtocol: http
enabled: true
hostname: nautobot.${var.domain_suffix}
tls: true
nautobot:
config: |
import os
import sys
from nautobot.core.settings import * # noqa F401,F403
from nautobot.core.settings_funcs import is_truthy, parse_redis_connection
if DATABASES["default"]["ENGINE"] == "django.db.backends.mysql":
DATABASES["default"]["OPTIONS"] = {"charset": "utf8mb4"}
SECRET_KEY = os.getenv("NAUTOBOT_SECRET_KEY", "#fdj#r@=om#sjb-odxae1w#!vy5&(6@tsog*&x31(1725#nwg)")
AUTHENTICATION_BACKENDS = [ "social_core.backends.keycloak.KeycloakOAuth2", "nautobot.core.authentication.ObjectPermissionBackend", "django.contrib.auth.backends.ModelBackend" ]
SOCIAL_AUTH_KEYCLOAK_KEY = '${keycloak_openid_client.nautobot_client.client_id}'
SOCIAL_AUTH_KEYCLOAK_SECRET = '${var.keycloak_nautobot_secret}'
SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx0w+FSHl757PbboHKFNwK8xEKyHwYTzDN3OCy+E0uXFBfXYf+mVqABWQaz/OwVL1H9XJzBlPZmARCIqnxB14J9QXn9ZJ42RTgXIMzJaJBUv5iKHhy4kFLAY26luzvWHZx4JYTAZ4gGOG0StapvAb5ejABcCmImP3P+PF3gZco1glZg1/wj+mMPnfD6If/uxwOb6YLHvBy6xqkfon9yyDNGGlm/6EjYJgjuoKLfw501/triw3RA4YFfZpn4z2uMqNR4tSdm5MpP84z0lDXl9KwplqI7SYvc+J9aZgBIRy+EZGplIazp3tfvKsR9910yxLxPYNzSPvOr8fJib4kqKaMQIDAQAB'
SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL = 'https://keycloak.${var.domain_suffix}/realms/${var.keycloak_realm}/protocol/openid-connect/auth'
SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL = 'https://keycloak.${var.domain_suffix}/realms/${var.keycloak_realm}/protocol/openid-connect/token'
postgresql:
auth:
password: 2wsxCDE3
global:
storageClass: ${var.storageclass}
redis:
auth:
password: 3edcVFR4
EOT
]
}
resource "helm_release" "librenms" {
name = "librenms"
namespace = var.librenms_namespace
repository = "https://midokura.github.io/helm-charts-community/"
chart = "librenms"
create_namespace = true
values = [
<<EOT
app:
persistence:
storageclass: ${var.storageclass}
ingress:
annotations:
traefik.ingress.kubernetes.io/router.middlewares: default-redirect-https@kubernetescrd
cert-manager.io/cluster-issuer: letsencrypt-prod
enabled: true
hostname: librenms.${var.domain_suffix}
ingressClassName: traefik
tls: true
syslog:
service:
type: LoadBalancer
EOT
]
}
resource "helm_release" "prometheus" {
name = "prometheus"
namespace = var.prometheus_namespace
repository = "https://charts.bitnami.com/bitnami/"
chart = "prometheus"
create_namespace = true
values = [
<<EOT
global:
storageClass: ${var.storageclass}
server:
ingress:
enabled: true
hostname: prometheus.${var.domain_suffix}
service:
type: ClusterIP
extraScrapeConfigs:
- job_name: pushgateway
scrape_interval: 300s
honor_labels: true
static_configs:
- targets: ['${helm_release.prometheus_pushgw.name}-${helm_release.prometheus_pushgw.chart}.${helm_release.prometheus_pushgw.namespace}.svc.cluster.local:9091']
alertmanager:
service:
type: ClusterIP
EOT
]
}
resource "helm_release" "prometheus_pushgw" {
name = "prometheus-pushgw"
namespace = var.prometheus_namespace
repository = "https://prometheus-community.github.io/helm-charts"
chart = "prometheus-pushgateway"
create_namespace = true
values = [
<<EOT
ingress:
enabled: true
hosts:
- pushgw.${var.domain_suffix}
tls:
- secretName: pushgw-tls
hosts:
- pushgw.${var.domain_suffix}
persistentVolume:
enabled: true
storageClass: ${var.storageclass}
EOT
]
}
resource "helm_release" "influxdb" {
name = "influxdb"
namespace = var.influxdb_namespace
repository = "https://charts.bitnami.com/bitnami/"
chart = "influxdb"
create_namespace = true
values = [
<<-EOT
auth:
admin:
password: ${var.default_password}
token: ${var.influxdb_admin_token}
global:
storageClass: ${var.storageclass}
influxdb:
server:
service:
type: ClusterIP
ingress:
enabled: true
hostname: influxdb.${var.domain_suffix}
EOT
]
}
resource "keycloak_openid_client" "grafana_client" {
realm_id = keycloak_realm.realm.id
client_id = "grafana"
enabled = true
access_type = "CONFIDENTIAL"
standard_flow_enabled = true
implicit_flow_enabled = false
use_refresh_tokens = false
client_secret = var.keycloak_grafana_secret
valid_redirect_uris = [
"https://grafana.${var.domain_suffix}/login/generic_oauth"
]
}
resource "kubernetes_config_map" "grafana_ini" {
metadata {
name = "grafana-ini"
namespace = var.grafana_namespace
}
data = {
"grafana.ini" = <<-EOT
[server]
root_url = https://grafana.${var.domain_suffix}/
[auth.generic_oauth]
enabled = true
name = md1clv.im
allow_sign_up = true
client_id = ${keycloak_openid_client.grafana_client.client_id}
client_secret = ${keycloak_openid_client.grafana_client.client_secret}
scopes = openid email profile offline_access roles
email_attribute_path = email
login_attribute_path = username
name_attribute_path = full_name
auth_url = https://keycloak.${var.domain_suffix}/realms/${keycloak_realm.realm.realm}/protocol/openid-connect/auth
token_url = https://keycloak.${var.domain_suffix}/realms/${keycloak_realm.realm.realm}/protocol/openid-connect/token
api_url = https://keycloak.${var.domain_suffix}/realms/${keycloak_realm.realm.realm}/protocol/openid-connect/userinfo
signout_redirect_url = https://keycloak.${var.domain_suffix}/realms/${keycloak_realm.realm.realm}/protocol/openid-connect/logout?redirect_uri=https%3A%2F%2Fgrafana.${var.domain_suffix}%2Flogin
role_attribute_path = contains(roles[*], 'grafanaadmin') && 'GrafanaAdmin' || contains(roles[*], 'admin') && 'Admin' || contains(roles[*], 'editor') && 'Editor' || 'Viewer'
allow_assign_grafana_admin = true
groups_attribute_path = groups
EOT
}
}
resource "helm_release" "grafana" {
name = "grafana"
namespace = var.grafana_namespace
repository = "https://charts.bitnami.com/bitnami/"
chart = "grafana"
create_namespace = true
values = [
<<-EOT
admin:
password: ${var.default_password}
config:
grafanaIniConfigMap: grafana-ini
useGrafanaIniFile: true
datasources:
secretDefinition:
apiVersion: 1
datasources:
- isDefault: true
name: InfluxDB
type: influxdb
url: http://influxdb.${var.influxdb_namespace}.svc.cluster.local:8006
- name: Prometheus
type: prometheus
url: http://prometheus-server.${var.prometheus_namespace}.svc.cluster.local
global:
storageClass: ${var.storageclass}
ingress:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
enabled: true
hostname: grafana.${var.domain_suffix}
tls: true
EOT
]
}
resource "helm_release" "freeipa" {
name = "freeipa"
namespace = var.freeipa_namespace
repository = "https://improwised.github.io/charts/"
chart = "freeipa"
create_namespace = true
values = [
<<EOT
hostname: freeipa.${var.domain_suffix}
args:
realm: MD1CLV.IM
domain: md1clv.im
dspassword: ${var.freeipa_ds_password}
adminpassword: ${var.freeipa_admin_password}
nohostdns: true
nontp: true
setupdns: false
persistence:
enabled: true
storageclass: ${var.storageclass}
ingress:
enabled: true
hostname: freeipa.${var.domain_suffix}
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
tls: true
backendProtocol: http
service:
annotations:
traefik.ingress.kubernetes.io/service.serversscheme: https
traefik.ingress.kubernetes.io/service.serverstransport: default-tls-selfsigned@kubernetescrd
EOT
]
}
resource "random_integer" "proxmox_node" {
min=1
max=2
count=var.num_routers
}
resource "proxmox_vm_qemu" "vyos_router" {
count = var.num_routers
name = "vyos-${count.index + 1}" #count.index starts at 0, so + 1 means this VM will be named test-vm-1 in proxmox
target_node = "${var.proxmox_host_prefix}${random_integer.proxmox_node[count.index].result}"
clone = var.vyos_template_name
agent = 1
os_type = "cloud-init"
cores = 2
sockets = 1
cpu = "host"
memory = 2048
scsihw = "virtio-scsi-single"
bootdisk = "scsi0"
disk {
slot = 0
size = "10G"
type = "scsi"
storage = "NFS-BIG"
iothread = 1
}
network {
model = "virtio"
bridge = "vmbr0"
}
network {
model = "virtio"
bridge = "TestNet1"
}
lifecycle {
ignore_changes = [
network,
]
}
ipconfig0 = "ip=dhcp"
sshkeys = <<EOF
${var.ssh_key}
EOF
}
resource "guacamole_connection_group" "routers" {
parent_identifier = "ROOT"
name = "Routers"
type = "organizational"
}
resource "guacamole_connection_ssh" "vyos_vms" {
for_each = { for x in proxmox_vm_qemu.vyos_router: x.name => x }
name = each.value.name
parent_identifier = guacamole_connection_group.routers.identifier
parameters {
hostname = each.value.default_ipv4_address
username = "vyos"
private_key = var.ssh_private_key
}
}