Files
kestra-scripts/hetzner-vm-provision.yaml
T
2026-06-26 17:59:52 +02:00

241 lines
7.2 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
id: hetzner-vm-provision
namespace: hetzner
inputs:
- id: vm_name
type: STRING
displayName: "VM Name"
description: "Eindeutiger Name der VM, z.B. dev-server-01."
- id: server_type
type: SELECT
displayName: "Server-Typ"
description: "cx23 = 2 vCPU 4 GB ~4 €/Mon | cx33 = 4 vCPU 8 GB ~9 €/Mon | cpx42 = 8 vCPU 16 GB ~30 €/Mon"
values:
- cx23
- cx33
- cx43
- cx53
defaults: cx23
- id: location
type: SELECT
displayName: "Standort"
description: "Hetzner-Datacenter"
values:
- nbg1
- fsn1
- hel1
defaults: nbg1
- id: team
type: STRING
displayName: "Team"
description: "Wird als Label auf der VM gesetzt."
defaults: demo
tasks:
- id: terraform-plan
type: io.kestra.plugin.terraform.cli.TerraformCLI
containerImage: hashicorp/terraform:latest
env:
AWS_ACCESS_KEY_ID: "{{ secret('AWS_ACCESS_KEY_ID') }}"
AWS_SECRET_ACCESS_KEY: "{{ secret('AWS_SECRET_ACCESS_KEY') }}"
TF_BACKEND_BUCKET: "{{ secret('TF_BACKEND_BUCKET') }}"
TF_BACKEND_ENDPOINT: "{{ secret('TF_BACKEND_ENDPOINT') }}"
TF_VAR_hcloud_token: "{{ secret('HCLOUD_TOKEN') }}"
TF_VAR_ssh_key_name: "{{ secret('SSH_KEY_NAME') }}"
inputFiles:
main.tf: |
terraform {
required_version = ">= 1.5"
required_providers {
hcloud = {
source = "hetznercloud/hcloud"
version = "~> 1.49"
}
}
backend "s3" {}
}
provider "hcloud" {
token = var.hcloud_token
}
data "hcloud_ssh_key" "default" {
name = var.ssh_key_name
}
resource "hcloud_server" "vm" {
name = var.vm_name
image = "ubuntu-24.04"
server_type = var.server_type
location = var.location
ssh_keys = [data.hcloud_ssh_key.default.id]
public_net {
ipv4_enabled = true
ipv6_enabled = false
}
labels = {
managed_by = "terraform"
team = var.team
}
}
output "vm_ipv4" {
value = hcloud_server.vm.ipv4_address
}
variables.tf: |
variable "hcloud_token" {
type = string
sensitive = true
}
variable "ssh_key_name" {
type = string
}
variable "vm_name" {
type = string
}
variable "server_type" {
type = string
default = "cx23"
}
variable "location" {
type = string
default = "nbg1"
}
variable "team" {
type = string
default = "demo"
}
beforeCommands:
- |
terraform init -reconfigure \
-backend-config="bucket=$TF_BACKEND_BUCKET" \
-backend-config="endpoints={s3=\"$TF_BACKEND_ENDPOINT\"}" \
-backend-config="key=vms/{{ inputs.vm_name }}/terraform.tfstate" \
-backend-config="region=us-east-1" \
-backend-config="skip_requesting_account_id=true" \
-backend-config="skip_credentials_validation=true" \
-backend-config="skip_metadata_api_check=true" \
-backend-config="skip_region_validation=true" \
-backend-config="use_path_style=true"
commands:
- |
set -eo pipefail
terraform plan -no-color -out=tfplan \
-var="vm_name={{ inputs.vm_name }}" \
-var="server_type={{ inputs.server_type }}" \
-var="location={{ inputs.location }}" \
-var="team={{ inputs.team }}" \
| tee plan-output.txt
outputFiles:
- tfplan
- main.tf
- variables.tf
- plan-output.txt
- id: llm-summary
type: io.kestra.plugin.openai.ChatCompletion
apiKey: "{{ secret('OPENAI_API_KEY') }}"
model: gpt-4o-mini
messages:
- role: system
content: "Du bist ein DevOps-Assistent. Fasse den folgenden Terraform Plan auf Deutsch in 3-5 Sätzen zusammen. Erkläre, was erstellt oder geändert wird und ob der Plan sicher aussieht."
- role: user
content: "{{ read(outputs['terraform-plan'].outputFiles['plan-output.txt']) }}"
- id: log-summary
type: io.kestra.plugin.core.log.Log
message: |
📋 KI-Analyse des Terraform Plans:
{{ outputs['llm-summary'].choices[0].message.content }}
──────────────────────────────────────────
→ Bitte prüfen und Execution fortsetzen oder ablehnen.
- id: approval-gate
type: io.kestra.plugin.core.flow.Pause
onResume:
- id: approved
type: BOOLEAN
displayName: "Deployment genehmigen?"
description: "Ja = VM wird erstellt. Nein = Execution wird abgebrochen."
defaults: false
- id: deploy-decision
type: io.kestra.plugin.core.flow.If
condition: "{{ outputs['approval-gate'].onResume.approved == true }}"
then:
- id: terraform-apply
type: io.kestra.plugin.terraform.cli.TerraformCLI
containerImage: hashicorp/terraform:latest
env:
AWS_ACCESS_KEY_ID: "{{ secret('AWS_ACCESS_KEY_ID') }}"
AWS_SECRET_ACCESS_KEY: "{{ secret('AWS_SECRET_ACCESS_KEY') }}"
TF_BACKEND_BUCKET: "{{ secret('TF_BACKEND_BUCKET') }}"
TF_BACKEND_ENDPOINT: "{{ secret('TF_BACKEND_ENDPOINT') }}"
TF_VAR_hcloud_token: "{{ secret('HCLOUD_TOKEN') }}"
TF_VAR_ssh_key_name: "{{ secret('SSH_KEY_NAME') }}"
inputFiles:
main.tf: "{{ outputs['terraform-plan'].outputFiles['main.tf'] }}"
variables.tf: "{{ outputs['terraform-plan'].outputFiles['variables.tf'] }}"
tfplan: "{{ outputs['terraform-plan'].outputFiles['tfplan'] }}"
beforeCommands:
- |
terraform init -reconfigure \
-backend-config="bucket=$TF_BACKEND_BUCKET" \
-backend-config="endpoints={s3=\"$TF_BACKEND_ENDPOINT\"}" \
-backend-config="key=vms/{{ inputs.vm_name }}/terraform.tfstate" \
-backend-config="region=us-east-1" \
-backend-config="skip_requesting_account_id=true" \
-backend-config="skip_credentials_validation=true" \
-backend-config="skip_metadata_api_check=true" \
-backend-config="skip_region_validation=true" \
-backend-config="use_path_style=true"
commands:
- terraform apply -auto-approve -no-color tfplan
- terraform output -raw vm_ipv4 | tr -d '\n' > vm_ipv4.txt
outputFiles:
- vm_ipv4.txt
- id: log-result
type: io.kestra.plugin.core.log.Log
message: |
✅ VM erfolgreich bereitgestellt!
Name: {{ inputs.vm_name }}
Typ: {{ inputs.server_type }} @ {{ inputs.location }}
Team: {{ inputs.team }}
IPv4: {{ read(outputs['terraform-apply'].outputFiles['vm_ipv4.txt']) }}
SSH: ssh root@{{ read(outputs['terraform-apply'].outputFiles['vm_ipv4.txt']) }}
else:
- id: log-aborted
type: io.kestra.plugin.core.log.Log
message: "❌ Deployment abgebrochen Approval wurde verweigert."