#!/usr/bin/env python3 """Klassifiziert einen Terraform-Plan als ITIL-Change-Typ. Aufruf: classify_change.py Erzeugt: change-record.md und change.env (für GitLab-dotenv-Reports) """ import datetime import json import os import sys # Änderungen an diesen Typen sind immer ein Normal Change CRITICAL_TYPES = { "azurerm_resource_group", "azurerm_virtual_network", "azurerm_subnet", "azurerm_network_security_group", "azurerm_subnet_network_security_group_association", "azurerm_shared_image_gallery", "azurerm_shared_image", } def classify(plan): changes, reasons = [], [] for rc in plan.get("resource_changes", []): actions = rc["change"]["actions"] if actions in (["no-op"], ["read"]): continue changes.append((rc["address"], rc["type"], actions)) if "delete" in actions: art = "wird ersetzt" if "create" in actions else "wird gelöscht" reasons.append(f"{rc['address']} {art}") elif rc["type"] in CRITICAL_TYPES: reasons.append(f"{rc['address']}: kritischer Ressourcentyp") if not changes: return "none", changes, reasons return ("normal" if reasons else "standard"), changes, reasons def main(): plan_file, stack = sys.argv[1], sys.argv[2] with open(plan_file, encoding="utf-8") as fh: plan = json.load(fh) change_type, changes, reasons = classify(plan) lines = [ f"# Change-Datensatz – Schicht {stack}", "", f"- Zeitpunkt: {datetime.datetime.now(datetime.timezone.utc):%Y-%m-%d %H:%M} UTC", f"- Change-Typ: **{change_type}**", f"- Commit: {os.environ.get('CI_COMMIT_SHA', 'lokal')}", f"- Merge Request: {os.environ.get('CI_MERGE_REQUEST_IID', '-')}", f"- Ausgelöst von: {os.environ.get('GITLAB_USER_LOGIN', 'lokal')}", "", "## Geplante Änderungen", ] lines += [f"- `{addr}`: {'/'.join(act)}" for addr, _, act in changes] or ["- keine"] if reasons: lines += ["", "## Gründe für Normal Change"] + [f"- {r}" for r in reasons] with open("change-record.md", "w", encoding="utf-8") as fh: fh.write("\n".join(lines) + "\n") with open("change.env", "w", encoding="utf-8") as fh: fh.write(f"CHANGE_TYPE_{stack.upper()}={change_type}\n") print(f"[{stack}] Change-Typ: {change_type}") for r in reasons: print(f" - {r}") if __name__ == "__main__": main()