4 Praktische Implementierung - Ein Webserver-Szenario im Vergleich

4.1 Einführung und Szenario-Definition

Um die theoretischen Unterschiede zwischen Ansible, Puppet, Chef und ZENworks greifbar zu machen, betrachten wir nun ein konkretes, praxisnahes Szenario. Nichts verdeutlicht die Philosophien und Herangehensweisen der verschiedenen Tools besser als die praktische Umsetzung einer realen Aufgabenstellung.

Unser Beispielszenario umfasst die Bereitstellung und Konfiguration eines Nginx-Webservers auf einem Linux-System. Diese Aufgabe ist bewusst gewählt, da sie verschiedene Aspekte des Configuration Managements abdeckt: Package-Installation, Service-Management, Dateikonfiguration und Sicherheitsaspekte. Gleichzeitig ist sie komplex genug, um die charakteristischen Unterschiede der Tools zu verdeutlichen, aber einfach genug, um nachvollziehbar zu bleiben.

In diesem Szenario wird folgendes Setup virtuell implementiert:

Das Szenario ermöglicht es, die praktischen Auswirkungen der verschiedenen Architekturprinzipien zu erleben. Sie werden sehen, wie sich die agentlose versus agentenbasierte Architektur, der prozedurale versus deklarative Ansatz und die unterschiedlichen Abstraktionsebenen in der täglichen Arbeit manifestieren.

4.2 Ansible: Der pragmatische Ansatz

Ansible verfolgt einen direkten, pragmatischen Ansatz, der sich in der Einfachheit seiner Werkzeuge und Dateien widerspiegelt. Die wichtigsten Komponenten sind das ansible-playbook Kommando und YAML-basierte Playbooks, die menschenlesbar und intuitiv verständlich sind.

4.2.1 Typische Werkzeuge und Dateien

Die Ansible-Implementierung organisiert sich um wenige, aber mächtige Werkzeuge. Das ansible-playbook Kommando ist der zentrale Einstiegspunkt für die Ausführung von Automatisierungsaufgaben. Für Ad-hoc-Aufgaben verwenden wir ansible, für die Verwaltung von Roles nutzen wir ansible-galaxy. Die Konfiguration erfolgt hauptsächlich über die ansible.cfg Datei und das Inventory.

webserver-project/
├── ansible.cfg                 # Grundkonfiguration für Ansible
├── inventory/
│   └── hosts.yml              # Definition der Zielsysteme
├── playbooks/
│   └── webserver.yml          # Hauptplaybook für Webserver-Setup
├── roles/
│   └── nginx/                 # Nginx-spezifische Role
│       ├── tasks/main.yml     # Hauptaufgaben der Role
│       ├── handlers/main.yml  # Event-Handler (z.B. Service-Restart)
│       ├── templates/         # Jinja2-Templates für Konfigurationsdateien
│       │   └── nginx.conf.j2
│       ├── files/             # Statische Dateien
│       │   └── index.html
│       └── vars/main.yml      # Variablen für die Role
└── group_vars/
    └── webservers.yml         # Gruppenspezifische Variablen

4.2.2 Ansible-Konfiguration und Inventory

Die ansible.cfg definiert grundlegende Einstellungen, die das Verhalten von Ansible steuern. Diese Datei zeigt bereits Ansibles Philosophie der einfachen, transparenten Konfiguration.

# ansible.cfg - Grundkonfiguration für unser Webserver-Projekt
[defaults]
# Definiert das Standard-Inventory
inventory = inventory/hosts.yml

# Deaktiviert die Überprüfung von SSH-Host-Keys für Lab-Umgebungen
host_key_checking = False

# Aktiviert die Ausgabe von Timing-Informationen
callback_whitelist = timer

# Definiert die Standard-Remote-User
remote_user = ansible

# Aktiviert SSH-Pipelining für bessere Performance
pipelining = True

[privilege_escalation]
# Definiert Standard-Privilege-Escalation
become = True
become_method = sudo
become_user = root

Das Inventory definiert, auf welchen Systemen die Automatisierung ausgeführt werden soll. Ansible unterstützt sowohl statische als auch dynamische Inventories.

# inventory/hosts.yml - Definition der Zielsysteme
all:
  children:
    webservers:
      hosts:
        web01.example.com:
          ansible_host: 192.168.1.10
        web02.example.com:
          ansible_host: 192.168.1.11
      vars:
        # Gruppenspezifische Variablen für alle Webserver
        nginx_port: 80
        nginx_user: www-data
        firewall_enabled: true

4.2.3 Das Hauptplaybook

Das Hauptplaybook orchestriert die gesamte Webserver-Konfiguration. Hier zeigt sich Ansibles prozeduraler Ansatz - wir definieren explizit, welche Schritte in welcher Reihenfolge ausgeführt werden sollen.

# playbooks/webserver.yml - Hauptplaybook für Webserver-Setup
---
- name: Configure Nginx Webserver
  hosts: webservers
  become: yes
  
  vars:
    # Playbook-spezifische Variablen
    custom_index_message: "Welcome to our automated Nginx server!"
    
  pre_tasks:
    # Vorbereitende Aufgaben - werden vor den Roles ausgeführt
    - name: Update package cache (Ubuntu/Debian)
      apt:
        update_cache: yes
        cache_valid_time: 3600
      when: ansible_os_family == "Debian"
      
    - name: Update package cache (CentOS/RHEL)
      yum:
        update_cache: yes
      when: ansible_os_family == "RedHat"
      
  roles:
    # Einbindung der Nginx-Role
    - nginx
    
  post_tasks:
    # Abschließende Aufgaben nach den Roles
    - name: Verify nginx is responding
      uri:
        url: "http://{{ inventory_hostname }}:{{ nginx_port }}"
        method: GET
        status_code: 200
      delegate_to: localhost
      run_once: true
      
    - name: Display completion message
      debug:
        msg: "Webserver setup completed successfully on {{ inventory_hostname }}"

4.2.4 Die Nginx-Role im Detail

Die Role kapselt die gesamte Nginx-spezifische Logik und macht sie wiederverwendbar. Die tasks/main.yml definiert die einzelnen Konfigurationsschritte.

# roles/nginx/tasks/main.yml - Hauptaufgaben der Nginx-Role
---
# Installation des Nginx-Pakets
- name: Install nginx package
  package:
    name: nginx
    state: present
  notify: restart nginx  # Triggert Handler bei Änderungen

# Sicherstellung, dass das www-data Benutzer existiert
- name: Ensure nginx user exists
  user:
    name: "{{ nginx_user }}"
    system: yes
    shell: /bin/false
    home: /var/www
    create_home: no

# Deployment der Hauptkonfigurationsdatei
- name: Deploy nginx main configuration
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
    backup: yes  # Erstellt Backup der vorherigen Konfiguration
  notify: restart nginx

# Entfernung der Standard-Site-Konfiguration
- name: Remove default nginx site
  file:
    path: /etc/nginx/sites-enabled/default
    state: absent
  notify: reload nginx

# Deployment unserer benutzerdefinierten Startseite
- name: Deploy custom index.html
  template:
    src: index.html.j2
    dest: /var/www/html/index.html
    owner: "{{ nginx_user }}"
    group: "{{ nginx_user }}"
    mode: '0644'

# Firewall-Konfiguration (nur wenn aktiviert)
- name: Allow HTTP traffic through firewall
  ufw:
    rule: allow
    port: "{{ nginx_port }}"
    proto: tcp
  when: firewall_enabled | default(false)

# Sicherstellung, dass der Service läuft und beim Boot startet
- name: Ensure nginx is started and enabled
  service:
    name: nginx
    state: started
    enabled: yes

Die Handler-Datei definiert Aktionen, die nur bei Bedarf ausgeführt werden - ein wichtiges Konzept für effiziente Systemverwaltung.

# roles/nginx/handlers/main.yml - Event-Handler für Nginx
---
# Handler werden nur ausgeführt, wenn sie durch 'notify' getriggert werden
- name: restart nginx
  service:
    name: nginx
    state: restarted
  
- name: reload nginx
  service:
    name: nginx
    state: reloaded  # Sanfterer Neustart, behält bestehende Verbindungen

Das Nginx-Konfigurationstemplate zeigt Ansibles Template-Engine in Aktion, die auf Jinja2 basiert.

# roles/nginx/templates/nginx.conf.j2 - Nginx-Hauptkonfiguration
# Generiert durch Ansible - Änderungen werden überschrieben
user {{ nginx_user }};
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 1024;
    use epoll;
    multi_accept on;
}

http {
    # Basic Settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    
    # MIME Types
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    # Logging Configuration
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    
    # Gzip Compression
    gzip on;
    gzip_vary on;
    gzip_types text/plain text/css application/json application/javascript;
    
    # Default Server Block
    server {
        listen {{ nginx_port }} default_server;
        listen [::]:{{ nginx_port }} default_server;
        
        root /var/www/html;
        index index.html index.htm;
        
        server_name _;
        
        location / {
            try_files $uri $uri/ =404;
        }
        
        # Security Headers
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header X-Content-Type-Options "nosniff" always;
        
        # Error Pages
        error_page 404 /404.html;
        error_page 500 502 503 504 /50x.html;
    }
}

4.2.5 Ausführung und Debugging

Die Ausführung erfolgt über einfache Kommandozeilenbefehle, die Ansibles Fokus auf Einfachheit und Transparenz widerspiegeln.

# Syntax-Prüfung des Playbooks
ansible-playbook playbooks/webserver.yml --syntax-check

# Dry-Run (Check-Mode) - zeigt, was geändert würde ohne Ausführung
ansible-playbook playbooks/webserver.yml --check --diff

# Vollständige Ausführung mit erhöhter Verbosity
ansible-playbook playbooks/webserver.yml -v

# Ausführung nur für spezifische Hosts
ansible-playbook playbooks/webserver.yml --limit web01.example.com

# Ausführung nur spezifischer Tags (falls definiert)
ansible-playbook playbooks/webserver.yml --tags nginx-config

4.3 Puppet: Der deklarative Enterprise-Ansatz

Puppet verfolgt einen fundamental anderen Ansatz als Ansible. Anstatt Schritte zu definieren, beschreiben wir den gewünschten Zustand des Systems. Diese deklarative Herangehensweise spiegelt sich in allen Aspekten von Puppet wider, von der Dateiorganisation bis zur Ausführung.

4.3.1 Typische Werkzeuge und Dateien

Die Puppet-Infrastruktur ist komplexer als die von Ansible, da sie eine kontinuierliche Client-Server-Architektur unterstützt. Die wichtigsten Kommandos sind puppet apply für lokale Ausführung, puppet agent für die Client-Server-Kommunikation und puppet module für die Modulverwaltung.

puppet-webserver/
├── manifests/
│   └── site.pp                # Hauptkonfigurationsdatei
├── modules/
│   └── nginx/                 # Nginx-Modul
│       ├── manifests/
│       │   ├── init.pp        # Hauptklasse des Moduls
│       │   ├── install.pp     # Installation-spezifische Klasse
│       │   ├── config.pp      # Konfiguration-spezifische Klasse
│       │   └── service.pp     # Service-spezifische Klasse
│       ├── templates/
│       │   └── nginx.conf.erb # ERB-Template für Nginx-Config
│       ├── files/
│       │   └── index.html     # Statische Dateien
│       └── metadata.json      # Modul-Metadaten
├── hieradata/                 # Hiera-Datenstore
│   ├── common.yaml
│   └── nodes/
│       ├── web01.example.com.yaml
│       └── web02.example.com.yaml
└── hiera.yaml                 # Hiera-Konfiguration

4.3.2 Hiera-Konfiguration und Datenorganisation

Hiera ist Puppets Datenabstraktionsschicht, die es ermöglicht, Daten von Code zu trennen. Diese Trennung ist fundamental für Puppets Skalierbarkeit in großen Umgebungen.

# hiera.yaml - Hiera-Konfiguration
---
version: 5
defaults:
  datadir: hieradata
  data_hash: yaml_data

hierarchy:
  # Node-spezifische Daten haben höchste Priorität
  - name: "Per-node data"
    path: "nodes/%{trusted.certname}.yaml"
    
  # Umgebungs-spezifische Daten
  - name: "Per-environment data"
    path: "environments/%{server_facts.environment}.yaml"
    
  # Operating System Familie
  - name: "Per-OS family"
    path: "os_family/%{facts.os.family}.yaml"
    
  # Globale Defaults
  - name: "Common data"
    path: "common.yaml"
# hieradata/common.yaml - Globale Standardwerte
---
# Nginx-spezifische Konfiguration
nginx::package_name: 'nginx'
nginx::service_name: 'nginx'
nginx::config_dir: '/etc/nginx'
nginx::user: 'www-data'
nginx::group: 'www-data'
nginx::worker_processes: 'auto'
nginx::worker_connections: 1024

# Firewall-Konfiguration
firewall_rules:
  '100 allow http':
    dport: 80
    proto: tcp
    action: accept
# hieradata/nodes/web01.example.com.yaml - Node-spezifische Werte
---
nginx::server_name: 'web01.example.com'
nginx::custom_config:
  client_max_body_size: '50M'
  proxy_read_timeout: '300'

4.3.3 Die Hauptmanifest-Datei

Die site.pp ist der Einstiegspunkt für Puppet und definiert, welche Klassen auf welche Nodes angewendet werden. Hier zeigt sich Puppets deklarative Natur.

# manifests/site.pp - Hauptkonfigurationsdatei
# Definiert Node-Klassifikationen und globale Einstellungen

# Globale Defaults für alle Nodes
node default {
  # Grundlegende Systemkonfiguration
  include stdlib  # Puppet Standard Library
  
  # Firewall-Grundkonfiguration
  class { 'firewall':
    ensure => running,
  }
  
  # Sammle und exportiere alle Firewall-Regeln
  Firewall {
    before  => Class['firewall::post'],
    require => Class['firewall::pre'],
  }
  
  class { 'firewall::pre': }
  class { 'firewall::post': }
}

# Spezifische Klassifikation für Webserver
node /^web\d+\.example\.com$/ {
  # Erbt von default node
  include default
  
  # Nginx-Webserver Konfiguration
  class { 'nginx':
    # Parameter werden automatisch aus Hiera gelesen
    manage_repo => true,
    confd_purge => true,
  }
  
  # Firewall-Regel für HTTP-Traffic
  firewall { '100 allow http':
    dport  => 80,
    proto  => tcp,
    action => accept,
  }
  
  # Custom fact für Monitoring
  $server_role = 'webserver'
}

4.3.4 Das Nginx-Modul im Detail

Puppet-Module folgen einer standardisierten Struktur, die Wiederverwendbarkeit und Wartbarkeit fördert. Das Nginx-Modul zeigt, wie komplexe Abhängigkeiten deklarativ verwaltet werden.

# modules/nginx/manifests/init.pp - Hauptklasse des Nginx-Moduls
class nginx (
  String $package_name = 'nginx',
  String $service_name = 'nginx',
  String $config_dir   = '/etc/nginx',
  String $user         = 'www-data',
  String $group        = 'www-data',
  String $worker_processes = 'auto',
  Integer $worker_connections = 1024,
  Boolean $manage_repo = false,
  Boolean $confd_purge = true,
  Hash $custom_config  = {},
) {
  
  # Abhängigkeiten zwischen Klassen definieren
  contain nginx::install
  contain nginx::config
  contain nginx::service
  
  # Reihenfolge der Ausführung
  Class['nginx::install']
  -> Class['nginx::config']
  ~> Class['nginx::service']  # ~> bedeutet "notify" (Neustart bei Änderung)
}
# modules/nginx/manifests/install.pp - Installation des Nginx-Pakets
class nginx::install {
  # Repository-Management (optional)
  if $nginx::manage_repo {
    case $facts['os']['family'] {
      'Debian': {
        apt::source { 'nginx':
          location => 'http://nginx.org/packages/ubuntu/',
          repos    => 'nginx',
          key      => {
            'id'     => '573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62',
            'source' => 'http://nginx.org/keys/nginx_signing.key',
          },
        }
      }
      'RedHat': {
        yumrepo { 'nginx':
          descr    => 'nginx repo',
          baseurl  => 'http://nginx.org/packages/centos/$releasever/$basearch/',
          gpgcheck => 1,
          enabled  => 1,
          gpgkey   => 'http://nginx.org/keys/nginx_signing.key',
        }
      }
      default: {
        fail("Unsupported OS family: ${facts['os']['family']}")
      }
    }
  }
  
  # Paket-Installation
  package { $nginx::package_name:
    ensure => installed,
  }
  
  # Benutzer-Management
  user { $nginx::user:
    ensure  => present,
    system  => true,
    shell   => '/bin/false',
    home    => '/var/www',
    require => Package[$nginx::package_name],
  }
}
# modules/nginx/manifests/config.pp - Konfigurationsverwaltung
class nginx::config {
  # Hauptkonfigurationsdatei
  file { "${nginx::config_dir}/nginx.conf":
    ensure  => file,
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template('nginx/nginx.conf.erb'),
    require => Package[$nginx::package_name],
    notify  => Service[$nginx::service_name],
  }
  
  # Entfernung der Standard-Site
  file { "${nginx::config_dir}/sites-enabled/default":
    ensure => absent,
    notify => Service[$nginx::service_name],
  }
  
  # Verzeichnisse sicherstellen
  file { ['/var/www', '/var/www/html']:
    ensure => directory,
    owner  => $nginx::user,
    group  => $nginx::group,
    mode   => '0755',
  }
  
  # Custom Index-Seite
  file { '/var/www/html/index.html':
    ensure  => file,
    owner   => $nginx::user,
    group   => $nginx::group,
    mode    => '0644',
    content => file('nginx/index.html'),
    require => File['/var/www/html'],
  }
  
  # Konfigurationsverzeichnis bereinigen (optional)
  if $nginx::confd_purge {
    file { "${nginx::config_dir}/conf.d":
      ensure  => directory,
      purge   => true,
      recurse => true,
      notify  => Service[$nginx::service_name],
    }
  }
}
# modules/nginx/manifests/service.pp - Service-Management
class nginx::service {
  service { $nginx::service_name:
    ensure     => running,
    enable     => true,
    hasrestart => true,
    hasstatus  => true,
    require    => [
      Package[$nginx::package_name],
      File["${nginx::config_dir}/nginx.conf"],
    ],
  }
  
  # Health Check (optional)
  exec { 'nginx-syntax-check':
    command     => '/usr/sbin/nginx -t',
    refreshonly => true,
    subscribe   => File["${nginx::config_dir}/nginx.conf"],
    before      => Service[$nginx::service_name],
  }
}

4.3.5 ERB-Template und Ausführung

Das ERB-Template zeigt Puppets Template-Engine, die auf Ruby basiert.

<%# modules/nginx/templates/nginx.conf.erb - Nginx Konfigurationstemplate %>
# Generiert durch Puppet - Änderungen werden überschrieben
user <%= @user %>;
worker_processes <%= @worker_processes %>;
pid /run/nginx.pid;

events {
    worker_connections <%= @worker_connections %>;
    use epoll;
    multi_accept on;
}

http {
    # Basic Settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    
<% if @custom_config.key?('client_max_body_size') %>
    client_max_body_size <%= @custom_config['client_max_body_size'] %>;
<% end %>

<% if @custom_config.key?('proxy_read_timeout') %>
    proxy_read_timeout <%= @custom_config['proxy_read_timeout'] %>;
<% end %>
    
    # Include mime types
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    # Default Server
    server {
        listen 80 default_server;
        root /var/www/html;
        index index.html;
        server_name _;
        
        location / {
            try_files $uri $uri/ =404;
        }
    }
}

Die Ausführung von Puppet zeigt die verschiedenen Modi der Anwendung.

# Syntax-Prüfung
puppet parser validate manifests/site.pp

# Simulation (Noop-Mode)
puppet apply --noop --verbose manifests/site.pp

# Vollständige Anwendung
puppet apply --verbose manifests/site.pp

# Mit Hiera-Debug
puppet apply --verbose --debug --hiera_debug manifests/site.pp

# Agent-Mode (in produktiven Umgebungen)
puppet agent --test --verbose

4.4 Chef: Der entwicklerorientierte API-Ansatz

Chef unterscheidet sich von Ansible und Puppet durch seine starke Ruby-Basis und API-zentrierte Architektur. Dieser Ansatz macht Chef besonders mächtig für Entwicklungsumgebungen, erfordert aber auch eine andere Denkweise.

4.4.1 Typische Werkzeuge und Dateien

Chef organisiert sich um das Konzept von Cookbooks und Recipes. Die wichtigsten Werkzeuge sind knife für das Management der Chef-Infrastruktur, chef-client für die Ausführung auf Nodes und berkshelf für das Dependency-Management.

chef-webserver/
├── Berksfile                   # Berkshelf Dependency Management
├── metadata.rb                 # Cookbook-Metadaten
├── Policyfile.rb              # Policy-basierte Konfiguration (Chef 12+)
├── recipes/
│   ├── default.rb             # Standard-Recipe
│   ├── install.rb             # Installation-Recipe
│   ├── configure.rb           # Konfiguration-Recipe
│   └── service.rb             # Service-Recipe
├── attributes/
│   └── default.rb             # Standard-Attribute
├── templates/
│   └── nginx.conf.erb         # ERB-Template für Nginx
├── files/
│   └── index.html             # Statische Dateien
├── libraries/
│   └── helpers.rb             # Ruby-Hilfsmethoden
├── spec/
│   └── unit/                  # ChefSpec Unit-Tests
├── test/
│   └── integration/           # Test-Kitchen Integration-Tests
└── .kitchen.yml               # Test-Kitchen Konfiguration

4.4.2 Cookbook-Metadaten und Dependencies

Chef verwendet detaillierte Metadaten zur Beschreibung von Cookbooks und ihren Abhängigkeiten.

# metadata.rb - Cookbook-Metadaten
name 'nginx-webserver'
maintainer 'DevOps Team'
maintainer_email 'devops@example.com'
license 'Apache-2.0'
description 'Installs and configures Nginx web server'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version '1.2.0'
chef_version '>= 15.0'

# Plattform-Unterstützung
supports 'ubuntu', '>= 18.04'
supports 'centos', '>= 7.0'
supports 'debian', '>= 9.0'

# Cookbook-Abhängigkeiten
depends 'apt', '~> 7.4'
depends 'yum', '~> 6.1'
depends 'firewall', '~> 2.7'

# Issues und Source-Code
issues_url 'https://github.com/example/chef-nginx/issues'
source_url 'https://github.com/example/chef-nginx'

# Attribute-Definition für Dokumentation
attribute 'nginx/port',
   'Nginx Port',
   'Port for Nginx to listen on',
   'string',
   '80'

attribute 'nginx/user',
   'Nginx User',
   'User account for Nginx worker processes',
   'string',
   'www-data'
# Berksfile - Dependency Management mit Berkshelf
source 'https://supermarket.chef.io'

metadata

# Externe Cookbook-Dependencies
cookbook 'apt', '~> 7.4'
cookbook 'yum', '~> 6.1'
cookbook 'firewall', '~> 2.7'

# Cookbooks aus Git-Repositories
cookbook 'custom-firewall',
   'https://github.com/example/chef-custom-firewall.git',
   'master'

# Lokale Cookbook-Entwicklung
# cookbook 'nginx-webserver', path: '.'

group  do
  cookbook 'test-helper',  'https://github.com/example/test-helper.git'
end

4.4.3 Attribute und Konfigurationsdaten

Chef verwendet Attribute für die Konfigurationsdatenabstraktion, die eine flexible Hierarchie unterstützen.

# attributes/default.rb - Standard-Attribute für das Cookbook
# Grundlegende Nginx-Konfiguration
default['nginx']['port'] = 80
default['nginx']['user'] = value_for_platform_family(
  'debian' => 'www-data',
  'rhel' => 'nginx',
  'amazon' => 'nginx'
)
default['nginx']['group'] = node['nginx']['user']

# Package-Namen per Platform
default['nginx']['package'] = value_for_platform_family(
  'debian' => 'nginx',
  'rhel' => 'nginx'
)

# Service-Namen per Platform
default['nginx']['service'] = value_for_platform_family(
  'debian' => 'nginx',
  'rhel' => 'nginx'
)

# Pfad-Konfiguration
default['nginx']['config_dir'] = '/etc/nginx'
default['nginx']['html_dir'] = '/var/www/html'
default['nginx']['log_dir'] = '/var/log/nginx'

# Nginx-Performance-Einstellungen
default['nginx']['worker_processes'] = 'auto'
default['nginx']['worker_connections'] = 1024
default['nginx']['keepalive_timeout'] = 65

# Feature-Flags
default['nginx']['enable_gzip'] = true
default['nginx']['enable_ssl'] = false
default['nginx']['purge_default_site'] = true

# Custom-Konfiguration (Hash für Flexibilität)
default['nginx']['custom_config'] = {}

# Firewall-Konfiguration
default['firewall']['enabled'] = true
default['firewall']['rules'] = [
  {
    'port' => 80,
    'protocol' => 'tcp',
    'action' => 'allow',
    'comment' => 'Allow HTTP traffic'
  }
]

# Monitoring und Logging
default['nginx']['access_log'] = "#{node['nginx']['log_dir']}/access.log"
default['nginx']['error_log'] = "#{node['nginx']['log_dir']}/error.log"
default['nginx']['log_level'] = 'warn'

4.4.4 Recipes: Die Hauptlogik

Das Standard-Recipe orchestriert die gesamte Nginx-Installation und zeigt Chefs Ruby-basierte DSL.

# recipes/default.rb - Haupt-Recipe für Nginx-Installation
#
# Cookbook:: nginx-webserver
# Recipe:: default
#
# Copyright:: 2024, DevOps Team
#
# Licensed under the Apache License, Version 2.0

# Include benötigter Recipes in der richtigen Reihenfolge
include_recipe 'nginx-webserver::install'
include_recipe 'nginx-webserver::configure' 
include_recipe 'nginx-webserver::service'

# Firewall-Konfiguration (wenn aktiviert)
if node['firewall']['enabled']
  include_recipe 'firewall::default'
  
  node['firewall']['rules'].each do |rule|
    firewall_rule "allow-#{rule['port']}-#{rule['protocol']}" do
      port rule['port']
      protocol rule['protocol'].to_sym
      action 
      command 
      notifies , 'service[ufw]', 
    end
  end
end

# Custom Ruby-Code für komplexe Logik
ruby_block 'verify-nginx-installation' do
  block do
    require 'net/http'
    require 'uri'
    
    # Warte auf Service-Start
    sleep 5
    
    begin
      uri = URI("http://localhost:#{node['nginx']['port']}")
      response = Net::HTTP.get_response(uri)
      
      if response.code == '200'
        Chef::Log.info('Nginx is responding correctly')
      else
        Chef::Log.warn("Nginx responded with status: #{response.code}")
      end
    rescue => e
      Chef::Log.error("Failed to verify Nginx: #{e.message}")
    end
  end
  action 
  only_if { node['platform_family'] == 'debian' }
end

# Export von Node-Daten für andere Systeme (Search-Feature)
node.default['nginx']['fqdn'] = node['fqdn']
node.default['nginx']['ip_address'] = node['ipaddress']
node.save unless Chef:[]
# recipes/install.rb - Installation-spezifisches Recipe
#
# Installiert Nginx und abhängige Pakete

# Platform-spezifische Repository-Konfiguration
case node['platform_family']
when 'debian'
  # APT-Repository für neueste Nginx-Version
  apt_repository 'nginx' do
    uri 'http://nginx.org/packages/ubuntu/'
    distribution node['lsb']['codename']
    components ['nginx']
    key 'http://nginx.org/keys/nginx_signing.key'
    action 
    only_if { node['nginx']['use_official_repo'] }
  end
  
  # Update Package-Cache
  apt_update 'update' do
    action 
    only_if { node['nginx']['use_official_repo'] }
  end

when 'rhel'
  # YUM-Repository für RHEL/CentOS
  yum_repository 'nginx' do
    description 'nginx repo'
    baseurl "http://nginx.org/packages/centos/#{node['platform_version'].to_i}/$basearch/"
    gpgkey 'http://nginx.org/keys/nginx_signing.key'
    action 
    only_if { node['nginx']['use_official_repo'] }
  end
end

# Nginx-Package-Installation
package node['nginx']['package'] do
  action 
  version node['nginx']['version'] if node['nginx']['version']
  notifies , 'service[nginx]', 
end

# Zusätzliche Tools installieren
package %w[curl wget] do
  action 
end

# Nginx-User und -Group erstellen (falls nicht vorhanden)
group node['nginx']['group'] do
  action 
  system true
end

user node['nginx']['user'] do
  group node['nginx']['group']
  system true
  shell '/bin/false'
  home '/var/www'
  action 
end

# Verzeichnisstruktur erstellen
[
  node['nginx']['config_dir'],
  node['nginx']['html_dir'],
  node['nginx']['log_dir'],
  "#{node['nginx']['config_dir']}/conf.d",
  "#{node['nginx']['config_dir']}/sites-available",
  "#{node['nginx']['config_dir']}/sites-enabled"
].each do |dir|
  directory dir do
    owner 'root'
    group 'root'
    mode '0755'
    recursive true
    action 
  end
end
# recipes/configure.rb - Konfiguration-spezifisches Recipe
#
# Konfiguriert Nginx mit Templates und Dateien

# Haupt-Konfigurationsdatei mit Template
template "#{node['nginx']['config_dir']}/nginx.conf" do
  source 'nginx.conf.erb'
  owner 'root'
  group 'root'
  mode '0644'
  variables(
     node['nginx']['worker_processes'],
     node['nginx']['worker_connections'],
     node['nginx']['user'],
     node['nginx']['keepalive_timeout'],
     node['nginx']['enable_gzip'],
     node['nginx']['custom_config'],
     node['nginx']['access_log'],
     node['nginx']['error_log'],
     node['nginx']['log_level']
  )
  notifies , 'service[nginx]', 
  action 
end

# Standard-Site entfernen (wenn gewünscht)
if node['nginx']['purge_default_site']
  %w[
    /etc/nginx/sites-enabled/default
    /etc/nginx/conf.d/default.conf
  ].each do |default_site|
    file default_site do
      action 
      notifies , 'service[nginx]', 
      only_if { ::File.exist?(default_site) }
    end
  end
end

# Custom Index-Seite
cookbook_file "#{node['nginx']['html_dir']}/index.html" do
  source 'index.html'
  owner node['nginx']['user']
  group node['nginx']['group']
  mode '0644'
  action 
end

# Custom Nginx-Konfiguration für spezielle Anforderungen
if node['nginx']['custom_config'].any?
  node['nginx']['custom_config'].each do |config_name, config_content|
    file "#{node['nginx']['config_dir']}/conf.d/#{config_name}.conf" do
      content config_content
      owner 'root'
      group 'root'
      mode '0644'
      notifies , 'service[nginx]', 
      action 
    end
  end
end

# Log-Rotation konfigurieren
logrotate_app 'nginx' do
  cookbook 'logrotate'
  path [
    node['nginx']['access_log'],
    node['nginx']['error_log']
  ]
  options %w[missingok notifempty compress delaycompress sharedscripts]
  frequency 'daily'
  rotate 52
  postrotate '/bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true'
  create "644 #{node['nginx']['user']} #{node['nginx']['group']}"
end

4.4.5 Ruby-Libraries und Test-Integration

Chef ermöglicht die Erweiterung mit Ruby-Code und bietet umfassende Test-Unterstützung.

# libraries/helpers.rb - Ruby-Hilfsmethoden für das Cookbook
#
# Erweitert Chef mit benutzerdefinierten Methoden

class Chef
  class Recipe
    # Hilfsmethode zur Nginx-Versions-Erkennung
    def nginx_version
      cmd = Mixlib::ShellOut.new('nginx -v 2>&1')
      cmd.run_command
      if cmd.exitstatus == 0
        version_match = cmd.stdout.match(/nginx\/(\d+\.\d+\.\d+)/)
        return version_match[1] if version_match
      end
      'unknown'
    end
    
    # Prüft ob Nginx läuft und antwortet
    def nginx_responding?(port = 80)
      require 'net/http'
      require 'timeout'
      
      begin
        Timeout::timeout(5) do
          response = Net::HTTP.get_response('localhost', '/', port)
          return response.code.to_i == 200
        end
      rescue
        return false
      end
    end
    
    # Generiert SSL-Konfiguration basierend auf verfügbaren Zertifikaten
    def ssl_config_for_domain(domain)
      cert_path = "/etc/ssl/certs/#{domain}.crt"
      key_path = "/etc/ssl/private/#{domain}.key"
      
      if ::File.exist?(cert_path) && ::File.exist?(key_path)
        return {
          'ssl_certificate' => cert_path,
          'ssl_certificate_key' => key_path,
          'ssl_protocols' => 'TLSv1.2 TLSv1.3',
          'ssl_ciphers' => 'ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512'
        }
      end
      
      {}
    end
  end
end

# Resource-Erweiterung für Custom-Nginx-Sites
class Chef
  class Resource
    class NginxSite < Chef:
      resource_name 
      provides 
      
      property , String,  true
      property , String,  'site.conf.erb'
      property , Hash,  {}
      property , [true, false],  true
      
      action  do
        template "#{node['nginx']['config_dir']}/sites-available/#{new_resource.site_name}" do
          source new_resource.template_source
          variables new_resource.variables
          notifies , 'service[nginx]', 
        end
        
        if new_resource.enable
          link "#{node['nginx']['config_dir']}/sites-enabled/#{new_resource.site_name}" do
            to "#{node['nginx']['config_dir']}/sites-available/#{new_resource.site_name}"
            notifies , 'service[nginx]', 
          end
        end
      end
      
      action  do
        file "#{node['nginx']['config_dir']}/sites-available/#{new_resource.site_name}" do
          action 
          notifies , 'service[nginx]', 
        end
        
        link "#{node['nginx']['config_dir']}/sites-enabled/#{new_resource.site_name}" do
          action 
          notifies , 'service[nginx]', 
        end
      end
    end
  end
end

4.4.6 Test-Kitchen Integration

Chef bietet umfassende Test-Möglichkeiten mit Test-Kitchen, ChefSpec und InSpec.

# .kitchen.yml - Test-Kitchen Konfiguration für lokale Tests
---
driver:
  name: vagrant
  
provisioner:
  name: chef_zero
  product_name: chef
  product_version: 17
  
verifier:
  name: inspec

platforms:
  - name: ubuntu-20.04
    driver:
      box: bento/ubuntu-20.04
  - name: centos-8
    driver:
      box: bento/centos-8

suites:
  - name: default
    run_list:
      - recipe[nginx-webserver::default]
    attributes:
      nginx:
        port: 80
        enable_gzip: true
    verifier:
      inspec_tests:
        - test/integration/default

Die Ausführung von Chef zeigt die verschiedenen Deployment-Modi.

# Lokale Ausführung mit chef-client
sudo chef-client --local-mode --runlist 'recipe[nginx-webserver::default]'

# Test-Kitchen für lokale Tests
kitchen test

# ChefSpec Unit-Tests
rspec spec/

# Berkshelf für Dependency-Management
berks install
berks upload

# Knife für Server-Management
knife cookbook upload nginx-webserver
knife node run_list add web01.example.com 'recipe[nginx-webserver::default]'

4.5 ZENworks: Der integrierte Enterprise-Ansatz

ZENworks von Micro Focus verfolgt einen grundlegend anderen Ansatz als die anderen drei Tools. Es ist primär GUI-basiert und bietet eine integrierte Plattform für verschiedene Aspekte des IT-Managements. Der Fokus liegt auf einfacher Bedienung für traditionelle IT-Administratoren.

4.5.1 Typische Werkzeuge und Komponenten

ZENworks organisiert sich um Bundles, Policies und die zentrale Management-Konsole. Die wichtigsten Komponenten sind die ZENworks Control Center (ZCC) Web-Konsole, der zman-Kommandozeilenwerkzeug und das Bundle-Konzept für Software-Deployment.

zenworks-webserver/
├── Bundles/
│   ├── Nginx-Installation.xml      # Bundle-Definition für Installation
│   ├── Nginx-Konfiguration.xml     # Bundle für Konfiguration
│   └── Firewall-HTTP.xml          # Bundle für Firewall-Regeln
├── Policies/
│   ├── Linux-Management.xml       # Linux-spezifische Policies
│   └── Security-Baseline.xml      # Sicherheits-Policies
├── Scripts/
│   ├── nginx-install.sh           # Bash-Script für Installation
│   ├── nginx-config.ps1           # PowerShell für Windows-Integration
│   └── health-check.py           # Python für Health-Checks
├── Files/
│   ├── nginx.conf                 # Statische Konfigurationsdateien
│   └── index.html                # Web-Content
└── Reports/
    ├── deployment-status.xml      # Deployment-Status-Berichte
    └── compliance-check.xml       # Compliance-Berichte

4.5.2 Bundle-Konzept und GUI-Konfiguration

ZENworks verwendet Bundles als zentrale Einheit für Software-Deployment und Konfiguration. Diese werden primär über die grafische Oberfläche erstellt und verwaltet.

Das Nginx-Installation-Bundle würde typischerweise folgende Struktur haben:

<!-- Beispiel einer Bundle-Definition - vereinfacht dargestellt -->
<Bundle Name="Nginx-Webserver-Installation" Type="Linux">
  <Summary>Installiert und konfiguriert Nginx Webserver</Summary>
  <Requirements>
    <Platform>Linux</Platform>
    <Distribution>Ubuntu,CentOS,SLES</Distribution>
    <MinimumVersion>18.04</MinimumVersion>
  </Requirements>
  
  <Actions>
    <PreInstall>
      <Script Type="Bash" Name="pre-install-check.sh"/>
      <Log Message="Starting Nginx installation..."/>
    </PreInstall>
    
    <Install>
      <CommandLine>
        <Command>/usr/bin/apt-get update</Command>
        <WorkingDirectory>/tmp</WorkingDirectory>
        <RunAs>root</RunAs>
      </CommandLine>
      <CommandLine>
        <Command>/usr/bin/apt-get install -y nginx</Command>
        <SuccessReturnCodes>0</SuccessReturnCodes>
      </CommandLine>
    </Install>
    
    <PostInstall>
      <FileOperation Type="Copy">
        <Source>Files/nginx.conf</Source>
        <Destination>/etc/nginx/nginx.conf</Destination>
        <Owner>root</Owner>
        <Permissions>644</Permissions>
      </FileOperation>
      <FileOperation Type="Copy">
        <Source>Files/index.html</Source>
        <Destination>/var/www/html/index.html</Destination>
        <Owner>www-data</Owner>
        <Permissions>644</Permissions>
      </FileOperation>
      <Service Name="nginx" Action="Start"/>
      <Service Name="nginx" Action="Enable"/>
    </PostInstall>
  </Actions>
  
  <Verification>
    <Process Name="nginx" MustBeRunning="true"/>
    <Port Number="80" MustBeListening="true"/>
    <HTTPCheck URL="http://localhost" ExpectedStatus="200"/>
  </Verification>
</Bundle>

4.5.3 Script-Integration und Automatisierung

Obwohl ZENworks primär GUI-basiert ist, können Scripts für komplexere Aufgaben integriert werden.

#!/bin/bash
# Scripts/nginx-install.sh - Installation-Script für ZENworks

# ZENworks-spezifische Logging-Funktionen
ZENLOG="/opt/novell/zenworks/bin/zenlog"

log_message() {
    echo "$1"
    $ZENLOG -l INFO -m "$1" 2>/dev/null || true
}

error_exit() {
    echo "ERROR: $1" >&2
    $ZENLOG -l ERROR -m "$1" 2>/dev/null || true
    exit 1
}

log_message "Starting Nginx installation process"

# Betriebssystem-Erkennung
if [ -f /etc/os-release ]; then
    . /etc/os-release
    OS=$NAME
    VERSION=$VERSION_ID
    log_message "Detected OS: $OS $VERSION"
else
    error_exit "Cannot determine operating system"
fi

# Package-Installation basierend auf Betriebssystem
case "$OS" in
    "Ubuntu"*)
        log_message "Installing Nginx on Ubuntu"
        apt-get update || error_exit "Failed to update package cache"
        apt-get install -y nginx || error_exit "Failed to install nginx"
        NGINX_USER="www-data"
        ;;
    "CentOS"* | "Red Hat"*)
        log_message "Installing Nginx on RHEL/CentOS"
        yum install -y epel-release || error_exit "Failed to install EPEL"
        yum install -y nginx || error_exit "Failed to install nginx"
        NGINX_USER="nginx"
        ;;
    *)
        error_exit "Unsupported operating system: $OS"
        ;;
esac

# Service-Konfiguration
log_message "Configuring Nginx service"
systemctl enable nginx || error_exit "Failed to enable nginx service"
systemctl start nginx || error_exit "Failed to start nginx service"

# Firewall-Konfiguration
log_message "Configuring firewall"
if command -v ufw >/dev/null; then
    ufw allow 80/tcp
elif command -v firewall-cmd >/dev/null; then
    firewall-cmd --permanent --add-service=http
    firewall-cmd --reload
else
    log_message "No supported firewall found, skipping firewall configuration"
fi

# Verification
log_message "Verifying installation"
sleep 5  # Warten bis Service vollständig gestartet ist

if systemctl is-active --quiet nginx; then
    log_message "Nginx service is running"
else
    error_exit "Nginx service is not running"
fi

if curl -s -o /dev/null -w "%{http_code}" http://localhost | grep -q "200"; then
    log_message "Nginx is responding on port 80"
else
    error_exit "Nginx is not responding correctly"
fi

log_message "Nginx installation completed successfully"

# ZENworks-spezifische Rückgabe für Bundle-Status
exit 0
# Scripts/nginx-config.ps1 - PowerShell-Script für Windows-Integration
# Dieses Script würde auf Windows-Systemen für ähnliche Aufgaben verwendet

param(
    [string]$ConfigSource = "",
    [string]$LogLevel = "INFO"
)

# ZENworks-spezifische PowerShell-Module laden
try {
    Import-Module ZENworks -ErrorAction SilentlyContinue
} catch {
    Write-Host "ZENworks PowerShell module not available"
}

function Write-ZenLog {
    param([string]$Message, [string]$Level = "INFO")
    
    Write-Host "[$Level] $Message"
    
    # ZENworks-Logging falls verfügbar
    try {
        if (Get-Command "Write-ZenworksLog" -ErrorAction SilentlyContinue) {
            Write-ZenworksLog -Level $Level -Message $Message
        }
    } catch {
        # Fallback zu Standard-Logging
    }
}

Write-ZenLog "Starting Windows Nginx proxy configuration" $LogLevel

# Beispiel: Nginx als Reverse Proxy für Windows-IIS konfigurieren
try {
    # Prüfen ob Nginx auf Linux-Backend erreichbar ist
    $LinuxBackend = "192.168.1.10"
    $TestConnection = Test-NetConnection -ComputerName $LinuxBackend -Port 80
    
    if ($TestConnection.TcpTestSucceeded) {
        Write-ZenLog "Linux backend is reachable" $LogLevel
        
        # Windows-spezifische Konfiguration
        # z.B. Load Balancer-Einträge aktualisieren
        
    } else {
        Write-ZenLog "Linux backend not reachable" "ERROR"
        exit 1
    }
    
} catch {
    Write-ZenLog "Configuration failed: $($_.Exception.Message)" "ERROR"
    exit 1
}

Write-ZenLog "Windows configuration completed" $LogLevel
exit 0

4.5.4 Policy-Management und Compliance

ZENworks bietet umfassende Policy-Management-Funktionen, die über einfache Software-Installation hinausgehen.

<!-- Linux-Management-Policy für Nginx-Server -->
<Policy Name="Linux-Nginx-Security-Baseline" Type="LinuxManagement">
  <Description>Sicherheits-Baseline für Nginx-Server</Description>
  
  <Settings>
    <!-- Benutzer- und Gruppenverwaltung -->
    <UserManagement>
      <User Name="nginx-admin" Action="Create">
        <Groups>nginx-admins,sudo</Groups>
        <Shell>/bin/bash</Shell>
        <HomeDirectory>/home/nginx-admin</HomeDirectory>
      </User>
      <Group Name="nginx-admins" Action="Create"/>
    </UserManagement>
    
    <!-- Dateisystem-Berechtigungen -->
    <FileSystemSettings>
      <Directory Path="/var/log/nginx">
        <Owner>root</Owner>
        <Group>nginx-admins</Group>
        <Permissions>750</Permissions>
      </Directory>
      <File Path="/etc/nginx/nginx.conf">
        <Owner>root</Owner>
        <Group>root</Group>
        <Permissions>644</Permissions>
      </File>
    </FileSystemSettings>
    
    <!-- Service-Konfiguration -->
    <ServiceManagement>
      <Service Name="nginx">
        <StartupType>Automatic</StartupType>
        <State>Running</State>
      </Service>
      <Service Name="ssh">
        <StartupType>Automatic</StartupType>
        <State>Running</State>
      </Service>
    </ServiceManagement>
    
    <!-- Firewall-Regeln -->
    <FirewallSettings>
      <Rule Name="Allow-HTTP" Action="Allow">
        <Port>80</Port>
        <Protocol>TCP</Protocol>
        <Direction>Inbound</Direction>
      </Rule>
      <Rule Name="Allow-SSH" Action="Allow">
        <Port>22</Port>
        <Protocol>TCP</Protocol>
        <Direction>Inbound</Direction>
        <SourceRange>192.168.1.0/24</SourceRange>
      </Rule>
    </FirewallSettings>
    
    <!-- Compliance-Checks -->
    <ComplianceSettings>
      <Check Name="Nginx-Version" Type="Command">
        <Command>nginx -v 2>&1 | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+'</Command>
        <ExpectedResult MinVersion="1.18.0"/>
      </Check>
      <Check Name="SSL-Configuration" Type="FileContent">
        <FilePath>/etc/nginx/nginx.conf</FilePath>
        <SearchPattern>ssl_protocols TLSv1.2 TLSv1.3</SearchPattern>
        <MustExist>true</MustExist>
      </Check>
    </ComplianceSettings>
  </Settings>
  
  <Schedule>
    <Frequency>Daily</Frequency>
    <Time>02:00</Time>
    <Remediation>Automatic</Remediation>
  </Schedule>
</Policy>

4.5.5 Reporting und Monitoring

ZENworks bietet umfassende Reporting-Funktionen, die über die grafische Oberfläche konfiguriert werden.

#!/usr/bin/env python3
# Scripts/health-check.py - Python-basierter Health-Check für ZENworks

import subprocess
import json
import urllib.request
import sys
import socket
from datetime import datetime

def run_command(command):
    """Führt ein Shell-Kommando aus und gibt das Ergebnis zurück"""
    try:
        result = subprocess.run(command, shell=True, capture_output=True, text=True)
        return result.returncode, result.stdout.strip(), result.stderr.strip()
    except Exception as e:
        return 1, "", str(e)

def check_service_status():
    """Prüft den Status des Nginx-Services"""
    returncode, stdout, stderr = run_command("systemctl is-active nginx")
    return returncode == 0, stdout

def check_port_listening():
    """Prüft ob Nginx auf Port 80 lauscht"""
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        result = sock.connect_ex(('localhost', 80))
        sock.close()
        return result == 0
    except:
        return False

def check_http_response():
    """Prüft HTTP-Response von Nginx"""
    try:
        with urllib.request.urlopen('http://localhost', timeout=10) as response:
            return response.getcode() == 200, response.getcode()
    except Exception as e:
        return False, str(e)

def get_nginx_version():
    """Ermittelt die Nginx-Version"""
    returncode, stdout, stderr = run_command("nginx -v 2>&1")
    if returncode == 0:
        # nginx version: nginx/1.18.0 (Ubuntu)
        try:
            version = stdout.split('/')[1].split(' ')[0]
            return version
        except:
            return "unknown"
    return "error"

def generate_zenworks_report():
    """Generiert einen Bericht im ZENworks-kompatiblen Format"""
    
    report = {
        "timestamp": datetime.now().isoformat(),
        "hostname": socket.gethostname(),
        "checks": {}
    }
    
    # Service-Status prüfen
    service_running, service_status = check_service_status()
    report["checks"]["service_status"] = {
        "status": "PASS" if service_running else "FAIL",
        "message": f"Nginx service is {'running' if service_running else 'not running'}",
        "details": service_status
    }
    
    # Port-Check
    port_listening = check_port_listening()
    report["checks"]["port_check"] = {
        "status": "PASS" if port_listening else "FAIL",
        "message": f"Port 80 is {'open' if port_listening else 'closed'}",
        "details": "nginx listening on port 80" if port_listening else "port 80 not accessible"
    }
    
    # HTTP-Response-Check
    http_ok, http_details = check_http_response()
    report["checks"]["http_response"] = {
        "status": "PASS" if http_ok else "FAIL",
        "message": f"HTTP response {'OK' if http_ok else 'ERROR'}",
        "details": http_details
    }
    
    # Version-Check
    version = get_nginx_version()
    report["checks"]["version_check"] = {
        "status": "INFO",
        "message": f"Nginx version: {version}",
        "details": version
    }
    
    # Gesamtstatus
    all_critical_passed = all(
        check["status"] == "PASS" 
        for key, check in report["checks"].items() 
        if key in ["service_status", "port_check", "http_response"]
    )
    
    report["overall_status"] = "HEALTHY" if all_critical_passed else "UNHEALTHY"
    
    return report

def main():
    """Hauptfunktion"""
    try:
        report = generate_zenworks_report()
        
        # Ausgabe als JSON für ZENworks-Parsing
        print(json.dumps(report, indent=2))
        
        # Exit-Code basierend auf Gesamtstatus
        exit_code = 0 if report["overall_status"] == "HEALTHY" else 1
        
        # ZENworks-spezifisches Logging
        if report["overall_status"] == "HEALTHY":
            run_command("zenlog -l INFO -m 'Nginx health check passed'")
        else:
            run_command("zenlog -l WARNING -m 'Nginx health check failed'")
        
        sys.exit(exit_code)
        
    except Exception as e:
        error_report = {
            "timestamp": datetime.now().isoformat(),
            "hostname": socket.gethostname(),
            "overall_status": "ERROR",
            "error": str(e)
        }
        print(json.dumps(error_report, indent=2))
        run_command(f"zenlog -l ERROR -m 'Health check script error: {str(e)}'")
        sys.exit(2)

if __name__ == "__main__":
    main()

4.5.6 ZENworks-spezifische Kommandozeilen-Tools

ZENworks bietet auch Kommandozeilen-Tools für Automatisierung und Scripting.

#!/bin/bash
# Beispiel-Script für ZENworks-Kommandozeilen-Management

# ZENworks-Umgebungsvariablen setzen
export ZENWORKS_SERVER="https://zenworks.example.com:443"
export ZENWORKS_USER="admin"

# zman-Kommandos für Bundle-Management
echo "Deploying Nginx Bundle to Linux devices..."

# Bundle zu Device-Gruppe zuweisen
zman bundle-assign "Nginx-Webserver-Installation" "/Devices/Linux-Servers/Webservers"

# Deployment-Status überwachen
zman bundle-list-assignments "Nginx-Webserver-Installation" --status

# Policy anwenden
zman policy-assign "Linux-Nginx-Security-Baseline" "/Devices/Linux-Servers"

# Berichte generieren
zman report-generate "Bundle Deployment Status" --format CSV --output /tmp/deployment-report.csv

# Device-Status abfragen
zman device-info web01.example.com --detailed

echo "ZENworks deployment completed"

4.6 Vergleichende Betrachtung der Implementierungsansätze

Nachdem wir die praktische Umsetzung unseres Nginx-Webserver-Szenarios in allen vier Tools betrachtet haben, werden die fundamentalen Unterschiede in den Philosophien und Herangehensweisen deutlich sichtbar. Diese Unterschiede gehen weit über die reine Syntax hinaus und betreffen die gesamte Denkweise bei der Infrastruktur-Automatisierung.

4.6.1 Komplexität und Lernkurve

Die Implementierungsbeispiele zeigen deutlich die unterschiedlichen Komplexitätsniveaus der vier Ansätze. Ansible besticht durch seine intuitive YAML-Syntax, die auch ohne tiefere Programmierkenntnisse verständlich ist. Ein neuer Teamkollege kann die Ansible-Playbooks lesen und verstehen, ohne spezielle Schulungen zu benötigen. Die prozedurale Natur macht die Ablauflogik transparent und nachvollziehbar.

Puppet erfordert hingegen ein tieferes Verständnis seiner deklarativen Konzepte und der spezifischen DSL-Syntax. Die Trennung von Code und Daten durch Hiera, die Abhängigkeitsverwaltung zwischen Ressourcen und die Klassenstruktur erfordern eine systematische Herangehensweise. Dafür bietet Puppet eine sehr mächtige Abstraktion für komplexe Infrastrukturen.

Chef repräsentiert den komplexesten Ansatz mit seiner vollwertigen Ruby-Basis. Die Möglichkeit, beliebigen Ruby-Code zu verwenden, macht Chef extrem flexibel, erfordert aber auch entsprechende Programmierkenntnisse. Die Integration von Test-Frameworks wie ChefSpec und Test-Kitchen zeigt Chefs Fokus auf entwicklungsorientierte Arbeitsweisen.

ZENworks verfolgt den GUI-zentrierten Ansatz und reduziert die Komplexität für traditionelle IT-Administratoren. Die Bundle-Struktur und Policy-Verwaltung sind intuitiv über die grafische Oberfläche bedienbar, auch wenn die XML-basierten Konfigurationsdateien im Hintergrund komplex werden können.

4.6.2 Wartbarkeit und Skalierbarkeit

Die Wartbarkeit der verschiedenen Ansätze zeigt interessante Charakteristika. Ansible-Playbooks können bei wachsender Komplexität schwer überschaubar werden, da die prozedurale Natur dazu führt, dass alle Schritte explizit definiert werden müssen. Die Role-Struktur hilft dabei, aber bei sehr komplexen Abhängigkeiten zwischen Systemen kann die Übersichtlichkeit leiden.

Puppet zeigt hier seine Stärken durch die deklarative Natur und das ausgefeilte Modulsystem. Einmal korrekt implementierte Puppet-Module sind hochgradig wiederverwendbar und skalieren gut über verschiedene Umgebungen hinweg. Die automatische Abhängigkeitsauflösung und die kontinuierliche Zustandsprüfung machen Puppet-basierte Infrastrukturen sehr wartungsarm.

Chef bietet durch seine Ruby-Basis theoretisch die höchste Flexibilität, erfordert aber auch die meiste Disziplin bei der Strukturierung. Gut geschriebene Chef-Cookbooks mit umfassenden Tests sind sehr wartbar, schlecht strukturierter Chef-Code kann jedoch schnell zu einem Wartungsalptraum werden.

ZENworks punktet durch seine integrierte Benutzeroberfläche und die visuellen Workflows. Die Wartung erfolgt primär über die GUI, was für traditionelle IT-Teams vertraut ist. Allerdings kann die Versionskontrolle und das Change-Management schwieriger werden, da die Konfiguration nicht in Textdateien vorliegt.

4.6.3 Integration und Ökosystem

Die Integration in moderne DevOps-Workflows zeigt deutliche Unterschiede. Ansible integriert sich nahtlos in CI/CD-Pipelines und Git-basierte Workflows. Die Tatsache, dass alle Konfigurationen in versionskontrollierten Textdateien vorliegen, macht Infrastructure-as-Code-Ansätze sehr natürlich.

Puppet bietet ähnliche Vorteile, erfordert aber mehr Planungsaufwand für die Integration in automatisierte Deployment-Pipelines. Die kontinuierlich laufenden Agenten passen nicht immer optimal zu event-getriebenen CI/CD-Prozessen.

Chef zeigt seine Stärken bei der API-Integration und ermöglicht sehr flexible Workflows. Die umfassenden Test-Frameworks und die Ruby-Basis machen Chef zu einer natürlichen Wahl für entwicklungsorientierte Teams.

ZENworks fokussiert sich auf die Integration in traditionelle Enterprise-Management-Prozesse und bietet weniger native Unterstützung für moderne DevOps-Workflows.

4.6.4 Transparenz und Debugging

Die Debugging-Erfahrung unterscheidet sich erheblich zwischen den Tools. Ansible bietet mit seinen Check- und Diff-Modi eine transparente Vorschau auf geplante Änderungen. Die YAML-Ausgabe ist menschenlesbar und macht Fehleranalysen relativ einfach.

Puppet bietet sehr detaillierte Logs und Berichte, die Abhängigkeitsgraphen und Ausführungszeiten enthalten. Die deklarative Natur kann aber bei komplexen Fehlern die Ursachenfindung erschweren.

Chef erfordert oft Ruby-spezifische Debugging-Kenntnisse, bietet aber mit Test-Kitchen sehr gute Test- und Entwicklungsmöglichkeiten.

ZENworks bietet grafische Logs und Status-Berichte, die für traditionelle IT-Teams sehr zugänglich sind, aber weniger detaillierte technische Einblicke ermöglichen.

4.7 Die richtige Wahl für Ihr Szenario

Die praktischen Beispiele verdeutlichen, dass die Wahl des richtigen Configuration Management-Tools stark von Ihren spezifischen Anforderungen und Rahmenbedingungen abhängt. Jedes Tool hat seine Berechtigung in bestimmten Kontexten und es gibt keine universal “beste” Lösung.

Ansible ist die ideale Wahl, wenn Sie schnell produktiv werden möchten, ein gemischtes Team aus Entwicklern und Administratoren haben und Wert auf einfache Integration in moderne DevOps-Workflows legen. Die niedrige Einstiegshürde und die agentlose Architektur machen es besonders attraktiv für Cloud-native Umgebungen und Teams, die Infrastructure-as-Code-Prinzipien umsetzen möchten.

Puppet empfiehlt sich für Enterprise-Umgebungen mit stabilen, langlebigen Infrastrukturen und strikten Compliance-Anforderungen. Die kontinuierliche Überwachung und automatische Korrektur von Konfigurationsabweichungen machen es zu einer ausgezeichneten Wahl für Organisationen, die “set and forget”-Mentalität bevorzugen.

Chef ist optimal für entwicklungsorientierte Teams, die maximale Flexibilität benötigen und bereit sind, in Ruby-Expertise zu investieren. Die API-zentrierte Architektur und die umfassenden Test-Frameworks machen Chef zu einer mächtigen Plattform für maßgeschneiderte Automatisierungslösungen.

ZENworks eignet sich hervorragend für traditionelle Enterprise-IT-Umgebungen, die eine integrierte Management-Plattform mit grafischer Benutzeroberfläche bevorzugen. Besonders in heterogenen Umgebungen mit starkem Windows-Anteil und umfassenden Asset-Management-Anforderungen kann ZENworks seine Stärken ausspielen.

Die Implementierungsbeispiele zeigen auch, dass hybride Ansätze durchaus sinnvoll sein können. Große Organisationen verwenden oft verschiedene Tools für verschiedene Aufgaben – etwa Ansible für Cloud-Deployments und Puppet für die Verwaltung kritischer On-Premise-Systeme. Das Verständnis der Stärken und Eigenarten aller verfügbaren Tools ermöglicht es Ihnen, fundierte Entscheidungen zu treffen und das jeweils optimale Werkzeug für Ihre spezifischen Anforderungen zu wählen.