Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add terraform documentation #41

Open
swissbuechi opened this issue Dec 7, 2023 · 0 comments
Open

Add terraform documentation #41

swissbuechi opened this issue Dec 7, 2023 · 0 comments

Comments

@swissbuechi
Copy link
Owner

swissbuechi commented Dec 7, 2023

Terraform Service Principal

az ad sp create-for-rbac --name SERVICE_GITLAB_TERRAFORM --role Contributor --scopes /subscriptions/<id>

Pipeline variables

Terraform

  • SP_CLIENT_ID
  • SP_CLIENT_SECRET
  • SUBSCRIPTION_ID
  • TENANT_ID

ots

  • TF_VAR_ots_vault_cert
  • TF_VAR_ots_vault_cert_key
  • TF_VAR_ots_vault_token

backend.tf

terraform {
  backend "http" {
  }
}

provider.tf

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "3.53.0"
    }
  }
}

provider "azurerm" {
  features {}
}

axe-ots/config/vault.hcl

backend "file" {
  path = "/vault/file"
}

listener "tcp" {
  address       = "0.0.0.0:8200"
  tls_cert_file = "/vault/cert/cert.pem"
  tls_key_file  = "/vault/cert-key/cert.key"
}

disable_mlock = true

seal "azurekeyvault" {
}

axe-ots/css/custom.css

/* Custom Font */
@font-face {
  font-family: Rubik;
  src: url(../font/Rubik.ttf);
}

body {
  font-family: 'Rubik', sans-serif;
}

button {
  font-family: 'Rubik', sans-serif;
  font-size: 18px;
}

.success-encrypted {
  font-size: 18px;
}

.subtitle {
  font-size: 18px;
}

/* Custom Wallpaper  */
body {
  /* background-image: url("../img/background/background.jpg"); */
  background-color: #343a40;
}

/* Custom Color */
button:hover {
  background-color: rgba(76, 205, 79, 0.7);
}

.encrypt {
  background-color: #4ccd4f;
}

.clipboard {
  background-color: #4ccd4f;
}

/* Custom Logo size */
.logo {
  width: 250px;
}

axe-ots.tf

resource "azurerm_resource_group" "axe-ots" {
  name     = "axe-rg-ots"
  location = var.location
}

data "azuread_client_config" "current" {}

resource "azuread_application" "axe-ots" {
  display_name     = "axe-ots"
}

resource "azuread_service_principal" "axe-ots" {
  client_id  = azuread_application.axe-ots.client_id
}

resource "azuread_application_password" "axe-ots" {
  application_id = azuread_application.axe-ots.id
}

data "azurerm_client_config" "current" {}

resource "azurerm_key_vault" "axe-ots" {
  name                        = "axe-kv-ots"
  location                    = azurerm_resource_group.axe-ots.location
  resource_group_name         = azurerm_resource_group.axe-ots.name
  enabled_for_disk_encryption = true
  tenant_id                   = data.azurerm_client_config.current.tenant_id
  soft_delete_retention_days  = 7
  purge_protection_enabled    = false
  sku_name                    = "standard"
  #enable_rbac_authorization   = true
  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = data.azurerm_client_config.current.object_id
    key_permissions = [
      "Create",
      "Delete",
      "Get",
      "Purge",
      "Recover",
      "Update",
      "GetRotationPolicy",
      "SetRotationPolicy"
    ]
  }
  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
 #   object_id = azurerm_user_assigned_identity.axe-aci-ots.principal_id
    object_id = azuread_service_principal.axe-ots.object_id
    key_permissions = [
      "Get",
      "WrapKey",
      "UnwrapKey",
    ]
  }
}

#resource "azurerm_role_assignment" "axe-ots" {
#  scope                = azurerm_key_vault.axe-ots.id
#  role_definition_name = "Key Vault Crypto User"
#  principal_id         = azuread_service_principal.axe-ots.object_id
#}

#resource "azurerm_role_assignment" "terraform-key-vault-admin" {
#  scope                = azurerm_resource_group.axe-ots.id
#  role_definition_name = "Key Vault Administrator"
#  principal_id         = data.azurerm_client_config.current.object_id
#}

#resource "azurerm_role_assignment" "terraform-key-vault-crypto-officer" {
#  scope                = azurerm_resource_group.axe-ots.id
#  role_definition_name = "Key Vault Crypto Officer"
#  principal_id         = data.azurerm_client_config.current.object_id
#}

resource "azurerm_key_vault_key" "axe-ots" {
  name         = "ots"
  key_vault_id = azurerm_key_vault.axe-ots.id
  key_type     = "RSA"
  key_size     = 4096
  key_opts = [
    "decrypt",
    "encrypt",
    "sign",
    "unwrapKey",
    "verify",
    "wrapKey"
  ]
}

resource "azurerm_storage_account" "axe-ots" {
  name                     = "axestots"
  resource_group_name      = azurerm_resource_group.axe-ots.name
  location                 = azurerm_resource_group.axe-ots.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
}

resource "azurerm_storage_share" "axe-ots" {
  name                 = "vault"
  storage_account_name = azurerm_storage_account.axe-ots.name
  quota                = 1
}

resource "azurerm_storage_share" "axe-ots-acme" {
  name                 = "acme"
  storage_account_name = azurerm_storage_account.axe-ots.name
  quota                = 1
}

resource "azurerm_storage_share" "axe-ots-icons" {
  name                 = "icons"
  storage_account_name = azurerm_storage_account.axe-ots.name
  quota                = 1
}

# resource "azurerm_storage_share" "axe-ots-background" {
#   name                 = "background"
#   storage_account_name = azurerm_storage_account.axe-ots.name
#   quota                = 1
# }

#resource "azurerm_user_assigned_identity" "axe-aci-ots" {
#  location            = azurerm_resource_group.axe-ots.location
#  name                = "axe-aci-ots"
#  resource_group_name = azurerm_resource_group.axe-ots.name
#}

resource "azurerm_container_group" "axe-ots" {
  name                = "axe-aci-ots"
  location            = azurerm_resource_group.axe-ots.location
  resource_group_name = azurerm_resource_group.axe-ots.name
  ip_address_type     = "Public"
  dns_name_label      = "axe-aci-ots"
  os_type             = "Linux"
  restart_policy      = "Never"
  exposed_port {
    port     = 80
    protocol = "TCP"
  }
  exposed_port {
    port     = 443
    protocol = "TCP"
  }
#  identity {
#    type = "UserAssigned"
#    identity_ids = [
#      azurerm_user_assigned_identity.axe-aci-ots.id
#    ]
#  }
  container {
    name   = "vault"
    image  = "hashicorp/vault:1.15"
    cpu    = "0.5"
    memory = "0.5"
    ports {
      port     = 8200
      protocol = "TCP"
    }
    commands = [
      "vault", "server", "-config=/vault/config/vault.hcl"
    ]
    environment_variables = {
      "VAULT_CACERT"                   = "/vault/cert/cert.pem"
      "VAULT_ADDR"                     = "https://localhost:8200"
      "VAULT_AZUREKEYVAULT_VAULT_NAME" = "axe-kv-ots"
      "VAULT_AZUREKEYVAULT_KEY_NAME"   = "ots"
      "AZURE_TENANT_ID"                = data.azurerm_client_config.current.tenant_id
      "AZURE_CLIENT_ID"                      = azuread_service_principal.axe-ots.client_id
      "AZURE_CLIENT_SECRET"                  = azuread_application_password.axe-ots.value
    }
    volume {
      name       = "vault-config"
      mount_path = "/vault/config"
      secret = {
        "vault.hcl" = filebase64("${path.module}/axe-ots/config/vault.hcl")
      }
    }
    volume {
      name       = "vault-cert"
      mount_path = "/vault/cert"
      secret = {
        "cert.pem" = base64encode(var.ots_vault_cert)
      }
    }
    volume {
      name       = "vault-cert-key"
      mount_path = "/vault/cert-key"
      secret = {
        "cert.key" = base64encode(var.ots_vault_cert_key)
      }
    }
    volume {
      name                 = "vault-file"
      mount_path           = "/vault/file"
      share_name           = azurerm_storage_share.axe-ots.name
      storage_account_name = azurerm_storage_account.axe-ots.name
      storage_account_key  = azurerm_storage_account.axe-ots.primary_access_key
    }
  }
  container {
    name   = "one-time-secret"
    image  = "ghcr.io/swissbuechi/one-time-secret:latest"
    cpu    = "0.5"
    memory = "0.5"
    environment_variables = {
      "VAULT_CACERT"             = "/vault/ca/cert.pem"
      "VAULT_ADDR"               = "https://localhost:8200"
      "OTS_HTTP_BINDING_ADDRESS" = ":80"
      "OTS_HTTPS_BINDING_ADDRESS"  = ":443"
      "OTS_HTTPS_REDIRECT_ENABLED" = "true"
      "OTS_TLS_AUTO_DOMAIN" = "ots.axelion.ch"
    }
    secure_environment_variables = {
      "VAULT_TOKEN" = var.ots_vault_token
    }
    volume {
      name                 = "acme"
      mount_path           = "/var/www/.cache"
      share_name           = azurerm_storage_share.axe-ots-acme.name
      storage_account_name = azurerm_storage_account.axe-ots.name
      storage_account_key  = azurerm_storage_account.axe-ots.primary_access_key
    }
    volume {
      name       = "vault-ca"
      mount_path = "/vault/ca"
      secret = {
        "cert.pem" = base64encode(var.ots_vault_cert)
      }
    }
    volume {
      name       = "css"
      mount_path = "/app/static/css"
      secret = {
        "custom.css" = filebase64("${path.module}/axe-ots/css/custom.css")
      }
    }
    volume {
      name       = "logo"
      mount_path = "/app/static/img/logo"
      secret = {
        "logo.png" = filebase64("${path.module}/axe-ots/img/logo.png")
      }
    }   
    volume {
      name       = "font"
      mount_path = "/app/static/font"
      secret = {
        "Rubik.ttf" = filebase64("${path.module}/axe-ots/font/Rubik.ttf")
      }
    }
    # volume {
    #   name       = "background"
    #   mount_path = "/app/static/img/background"
    #   share_name           = azurerm_storage_share.axe-ots-background.name
    #   storage_account_name = azurerm_storage_account.axe-ots.name
    #   storage_account_key  = azurerm_storage_account.axe-ots.primary_access_key
    # }
    volume {
      name                 = "favicon"
      mount_path           = "/app/static/icons"
      share_name           = azurerm_storage_share.axe-ots-icons.name
      storage_account_name = azurerm_storage_account.axe-ots.name
      storage_account_key  = azurerm_storage_account.axe-ots.primary_access_key
    }
    ports {
      port     = 80
      protocol = "TCP"
    }
    ports {
      port     = 443
      protocol = "TCP"
    }
  }
  depends_on = [ azurerm_key_vault_key.axe-ots ]
}

resource "azurerm_automation_account" "axe-ots" {
  name                = "axe-aa-ots"
  location            = azurerm_resource_group.axe-ots.location
  resource_group_name = azurerm_resource_group.axe-ots.name
  sku_name = "Basic"
  identity {
    type = "SystemAssigned"
  }
}

resource "azurerm_role_assignment" "axe-ots-automation" {
  scope                = azurerm_container_group.axe-ots.id
  role_definition_name = "Contributor"
  principal_id         = azurerm_automation_account.axe-ots.identity.0.principal_id
}


resource "azurerm_automation_runbook" "axe-ots" {
  name                    = "Restart-ContainerGroup"
  location                = azurerm_resource_group.axe-ots.location
  resource_group_name     = azurerm_resource_group.axe-ots.name
  automation_account_name = azurerm_automation_account.axe-ots.name
  log_verbose             = "true"
  log_progress            = "true"
  runbook_type            = "PowerShell"
  content             = <<-EOT
    param (
      [Parameter(Mandatory=$true)]
      [String]$containergroupname,
      [Parameter(Mandatory=$true)]
      [String]$resourcegroupname
    )
    
      $currentTime = Get-Date -Format "dd.MM.yyyy HH:mm:ss"
      Write-Host "[$currentTime] Connecting to Azure account..."
      Connect-AzAccount -Identity

      $currentTime = Get-Date -Format "dd.MM.yyyy HH:mm:ss"
      Write-Host "[$currentTime] Restarting Azure Container Group '$containerGroupName' in Resource Group '$resourceGroupName'..."
      Restart-AzContainerGroup -Name $containerGroupName -ResourceGroupName $resourceGroupName
    EOT
}

resource "azurerm_automation_schedule" "axe-ots" {
  name                    = "weekly-saturday-3am"
  resource_group_name     = azurerm_resource_group.axe-ots.name
  automation_account_name = azurerm_automation_account.axe-ots.name
  frequency               = "Week"
  interval                = 1
  timezone                = "Europe/Zurich"
  start_time              = replace(timeadd(timestamp(), "24h"), "/T\\d{2}:\\d{2}:\\d{2}/", "T03:00:00")
  week_days               = ["Saturday"]
  lifecycle {
    ignore_changes = [
      start_time
    ]
  }
}

resource "azurerm_automation_job_schedule" "axe-ots" {
  resource_group_name     = azurerm_resource_group.axe-ots.name
  automation_account_name = azurerm_automation_account.axe-ots.name
  schedule_name           = azurerm_automation_schedule.axe-ots.name
  runbook_name            = azurerm_automation_runbook.axe-ots.name
  parameters = {
    "resourcegroupname" = azurerm_resource_group.axe-ots.name
    "containergroupname" = azurerm_container_group.axe-ots.name
  }
}

variables.tf

variable "location" {
  type = string
  default = "switzerlandnorth"
}

variable "ots_vault_token" {}
variable "ots_vault_cert" {}
variable "ots_vault_cert_key" {}

.gitlab-ci.yml

image: registry.gitlab.com/gitlab-org/terraform-images/stable:latest
variables:
  CA_CERTIFICATE: "$AXE_CA_01"
  TF_ROOT: "$CI_PROJECT_DIR"
  TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_PROJECT_NAME}
  ARM_CLIENT_ID: "$SP_CLIENT_ID"
  ARM_CLIENT_SECRET: "$SP_CLIENT_SECRET"
  ARM_SUBSCRIPTION_ID: "$SUBSCRIPTION_ID"
  ARM_TENANT_ID: "$TENANT_ID"

cache:
  key: production
  paths:
    - ${TF_ROOT}/.terraform

before_script:
  - cd ${TF_ROOT}

stages:
  - prepare
  - validate
  - build
  - deploy

init:
  stage: prepare
  script:
    - echo "$CA_CERTIFICATE" > /usr/local/share/ca-certificates/AXE-CA-01.crt
    - update-ca-certificates
    - gitlab-terraform init

validate:
  stage: validate
  script:
    - echo "$CA_CERTIFICATE" > /usr/local/share/ca-certificates/AXE-CA-01.crt
    - update-ca-certificates
    - gitlab-terraform init
    - gitlab-terraform validate

plan:
  stage: build
  script:
    - echo "$CA_CERTIFICATE" > /usr/local/share/ca-certificates/AXE-CA-01.crt
    - update-ca-certificates
    - gitlab-terraform plan
    - gitlab-terraform plan-json
  artifacts:
    name: plan
    paths:
      - ${TF_ROOT}/plan.cache
    reports:
      terraform: ${TF_ROOT}/plan.json

apply:
  stage: deploy
  environment:
    name: production
  script:
    - echo "$CA_CERTIFICATE" > /usr/local/share/ca-certificates/AXE-CA-01.crt
    - update-ca-certificates
    - gitlab-terraform apply
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: manual
  dependencies:
    - plan
  
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant