Chef Interactive
This content is more than 4 years old and the cloud moves fast so some information may be slightly out of date.
Es ist vermutlich bekannt, dass Chef ein Tool für die automatische Provisionierung und Konfiguration von Systemen ist. Sobald man also ein Problem hat, dass diese beiden Bereiche verlässt, werden Internetposts und Supportanfragen jeder Art wahrscheinlich in eine von zwei Antworten münden: „Das ist nicht möglich“ oder „So macht man das aber nicht“.
Aber – was wenn man einen echten, exotischen Anwendungsfall hat. Und das möglicherweise nur als Übergangslösung?
Use Cases
Für den Inhalt dieses Posts sind uns bisher zwei Use Cases untergekommen: Die Modernisierung eines Provisionierungsworkflows oder der Einsatz von Tools, die nur interaktiv installiert werden können.
Beispiel hier wäre eine Firma, in der IT Systeme als Teil einer größeren Lösung vorinstalliert werden. Diese Systeme sind also nur ein kleiner Teil des Produktes und wahrscheinlich sogar einer, der als nicht-zentral für das Endresultat gesehen wird. Insbesondere dann, wenn dieser Workflow ausgelagert ist zu einem Dienstleister, müssen einige Bedingungen beachtet werden bis fortgeschrittene Lösungen ausgerollt werden können. Natürlich würde ein „Big Bang“ besser sein und direkt alles auf die neuen Tools optimieren. Aber wie oft hat dieser Ansatz in der Vergangenheit wirklich funktioniert?
In unserem nicht-ganz-so-hypothetischen Fall werden wir uns einen Workflow vorstellen, in dem Mitarbeiter daran gewohnt sind, eine frische Windowsinstanz zu starten und dann diverse Aufgaben zur Einrichtung durchzuführen. Viele dieser Aufgaben können wir schon mit Chef-basierten Lösungen ersetzen. Aber wie handhaben wir, wenn Server mit der falschen Anzahl von Festplatten geliefert werden? Oder wenn wir Konfigurationsabweichungen haben, die nur manuell gelöst werden können? Normalerweise sollten diese Fälle in der Fertigung nicht auftreten – jeder, der die Lean Bewegung verfolgt hat, kennt vermutlich das Jidoka-Prinzip. Aber gehen wir davon aus, dass wir dieses Thema nicht beeinflussen können und einen Umweg brauchen.
Einige dieser Punkte klingen vielleicht bekannt. Setups die aus unverständlichen Gründen keine automatisierte Installation beherrschen. Keine Parameter für den Installer – oder schlimmer, ein Popupfenster in das Aktivierungsschlüssel eingegeben werden müssen. Eine pragmatische Lösung ist es, Tools wie AutoIt oder AutoHotkey zu nutzen um auf diese Events zu warten und dann Tastatureingaben/Mausklicks zu simulieren.
Das Kernproblem ist dann, dass Chef als Hintergrundprozess arbeitet und keinen UI-Zugriff hat. Nicht einmal eine simulierte UI – damit kommt auch AutoIt nicht klar.
Schritt 1: Interaktiv werden
Zwei Ansätze sind hier möglich: der eine mit einem Tool aus der Sysinternals Suite von Microsoft mit Namen „psexec“ und einer, der rein mit Windows-Bordmitteln funktioniert. Beides erfordert eine interaktive Windows-Session, also ist dies unsere erste Aufgabe.
Windowssysteme haben die Möglichkeit, einen Benutzer automatisch beim Systemstart anzumelden. Ganz offensichtlich ist dies sicherheitstechnisch bedenklich, in unserem Use Case sind wir aber in einem einmaligen Workflow – wir können diese temporäre Sicherheitslücke also später wieder entfernen.
Aktuell besitzt Chef keine Ressource, um Autologin zu aktivieren oder deaktivieren. Aber wir können uns eine simple Custom Resource schreiben die mit der registry_key resource die passenden Werte setzt:
resource_name :autologin
property :user, String, name_property: true
property :password, String
default_action :enable
action :enable do
registry_key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' do
values [
{ name: 'DefaultUserName', type: :string, data: new_resource.user },
{ name: 'DefaultPassword', type: :string, data: new_resource.password },
{ name: 'AutoAdminLogon', type: :dword, data: 1 },
]
sensitive true
action :create
notifies :reboot_now, 'reboot[now]', :immediate
end
reboot 'now' do
action :nothing
end
end
action :disable do
registry_key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' do
values [
{ name: 'DefaultUserName', type: :string, data: '' },
{ name: 'DefaultPassword', type: :string, data: '' },
{ name: 'AutoAdminLogon', type: :dword, data: 0 },
]
action :create
end
end
Wie man sieht, enthält diese Lösung einen bedingten Reboot und wir brauchen tatsächlich einen Neustart zu diesem Zeitpunkt. Darüber hinaus: Benutzer und Passwort stehen im Klartext in der Registry! Leider gibt es aber keinen alternativen Weg, ich bin für Kommentare natürlich dankbar.
Bei der Verwendung dieser Ressource müssen zwei Punkte bedacht werden:
- autologin muss nach dem Abschluss wieder deaktiviert werden (hier ist allerdings kein Reboot notwendig)
- dieser Task wird bei jeder Aktivierung einen Neustart bewirken, wir brauchen also einen Guard um die Schritte nur einmal zu starten (z.B. ein Registry Key ob das Programm schon installiert ist)
Schritt 2: Arbeitsplanung
Wie können wir jetzt eine Ressource ausführen, nachdem der Server gestartet ist? Einen Hinweis gibt der Titel – wir werden nämlich den Windows Task Scheduler nutzen, der eine Konfiguration bietet die uns hilft:
- den Zeitpunkt des Tasks als „on logon” definieren
- die Eigenschaft „Start when a user is logged on” (dafür brauchten wir die Autologin Ressource)
- Nutzer/Passwort für den Task
Da der Task in der gleichen Session starten soll, die per Autologin eingeloggt wurde, können wir uns die Zugangsdaten direkt aus der Registry besorgen. Keine Duplikation und sogar ein Vorteil von der Speicherung in Klartext. Die Chef windows_task resource macht die Einrichtung einfach.
resource_name :execute_interactive
property :command, String, name_property: true
default_action :enable
action :enable do
require 'securerandom'
# Extract from autologin settings
autologin = registry_get_values('HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon')
user_auto = autologin.select { |data| data[:name] == 'DefaultUserName' }.first[:data]
password_auto = autologin.select { |data| data[:name] == 'DefaultPassword' }.first[:data]
taskname = format('execute-interactive-%s', SecureRandom.hex(4))
windows_task 'Create task ' + taskname do
action [:create, :run]
task_name taskname
command new_resource.command
interactive_enabled true
user user_auto
password password_auto
sensitive true
run_level :highest
frequency :once
start_time Time.now.strftime('%H:%M')
end
powershell_script 'Wait for completion of ' + taskname do
action :run
code <<~PS1
Do {
Start-Sleep -Seconds 1
} while ((Get-ScheduledTaskInfo -TaskName #{taskname}).LastTaskResult -ge 0x41301)
PS1
end
windows_task 'Remove task ' + taskname do
task_name taskname
action :delete
end
end
Diese Lösung ist natürlich ein absolutes Minimum und eine produktive Implementation hat noch ein paar Zusatzpunkte. Aber es funktioniert! Für alle fortgeschrittenen Chef User sei dazugesagt, dass diese Lösung immer konvergierte Ressourcen produziert – falls darauf also in einer Pipeline getestet wird, um „idempotenten“ Code zu erzwingen, sollte das berücksichtigt werden.
Falls AutoIt/AutoHotkey genutzt wird, empfehle ich eine Custom Ressource in Chef die nur den Code des Scripts annimmt. Damit enthält das Chef-Recipe alle Punkte der Infrastruktur und die Lösung ist nicht auf mehrere Dateien verteilt. Für einen fortgeschrittenen Chef-Practitioner sollte das kein Problem sein.
Alternative: PSExec
Der zweite Weg funktioniert mit der Sysinternals Suite von Microsoft, die ein Tool zur Taskausführung enthält. PSExec hat Parameter für interaktiven Start und auch die Nutzung des System Accounts. Falls also aus diversen Gründen die Windows Task-Lösung nicht gewünscht ist und die Nutzung externer Ressourcen akzeptabel ist, geht auch diese Variante. Auch hier wird allerdings die Autologin Ressource notwendig, nur die execute_interactive Custom Resource muss anders geschrieben werden.
Zusammenfassung
Auch wenn diverse Leute (meist zu recht) davon abraten, Programme mit Chef interaktiv zu starten – es ist möglich. Die Anwendung dieser Ideen sollte aber immer ein letztes Mittel oder eine Übergangslösung sein. In unserem Beispiel der Fertigung wäre es ohnehin besser, wenn immer gleiche Server geliefert würden oder unterliegende Schritte angepasst werden. Die Virtualisierung von Servern zusammen mit automatischem Deployment von VMware ESX wäre hier wesentlich besser.