WSUS Offline und Ansible

Ansible ist schon sehr nice. Benutzt man es mit Windows, ist es auch noch nice aber manchmal fühlt man sich dabei wie ein Ritt auf einem Vulkan, durch die Hölle... oder sowas. Meist funktioniert es aber was im Hintergrund passiert gleicht Feen-Magie.

Da wir es wagen einen Proxy zu benutzen scheint es völlig unmöglich zu sein an Windows Updates zu kommen. Und in letzter Zeit scheinen auch meine Workarounds nicht zu fruchten. Immer wieder hängen die Updates tagelang in der Pipeline und können nicht runtergeladen oder installiert werden. Also schaute ich mir mal wieder einen alten Bekannten an: WSUS Offline Update. Ein Paket aus Scripten das Updates zentral runterlädt und dann offline installiert werden kann. Das schöne daran: Das Download-Script liegt auch in Shell vor und kann so direkt auf dem Linux Samba Server ausgeführt werden. Nur wie kommen die Updates auf die Clients? Wie immer lautet die Antwort: Per Ansible Playbook:

---
- name: mount updates share and run update
  raw: "net use U: \\\\meinsambaserver\\updates /persistent:no; cmd /c U:\\wsusoffline\\client\\cmd\\DoUpdate.cmd"
  register: command_result
  changed_when: "'Installation successful' in command_result.stdout"
  failed_when:
    - "'Nothing to do!' not in command_result.stdout"
    - "'Installation successful.' not in command_result.stdout"

WSUS Offline liegt dabei auf einem Updates-Share. Die zwei schwierigsten Sachen war das Escaping der Orte für den Kommandoaufruf und den richtigen Status erkennen. Aus irgendeinem Grund hat WSUS Offline immer den Return Code 1 raus wenn er was installiert hat. Mit der Hilfe von changed_when und failed_when suche ich in dem STDOUT-Output nach bestimmten Stichworten um den richtigen Status zu bekommen. Bestimmt decke ich nicht alles ab, aber in meinen ersten Tests funktioniert es.

Ansible, win_package und Upgrades

source: http://childofmoonlight.tumblr.com/post/44755113585/im-a-grumpy-guss-enjoy-this-gif-set-of-grumpy

Das Ansible Modul win_package beruht auf die Annahme das die product_id in der Registry vorhanden ist oder eben nicht. Davon macht es abhängig ob ein Paket installiert werden soll oder ob es schon vorhanden ist. Nun kann es ja auch vorkommen das man ein Paket, obwohl es laut Registry schon installiert ist, es noch einmal installieren möchte. Quasi ein Upgrade machen. Schön wäre es wenn er nicht nur schaut ob das Paket installiert ist, sondern auch die installierte Version. Daran könnte man Task Entscheidungen treffen. Dies mache ich nun manuell. Ein Beispiel für VLC:

---

# Der ganz normale Install-Task. Es wird nach der product_id gesucht gegebenenfalls installiert
- name: install
  win_package:
    product_id="VLC media player"
    path="//myserver/updates/software/vlc/vlc-2.2.2-win32.exe"
    arguments="/L=1031 /S"

# Ein Powershell Snippet das die Version des installierten Pakets in der Variabel "version" speichert
- name: check version
  raw: (Get-ItemProperty "HKLM:\SOFTWARE\wow6432node\Microsoft\Windows\CurrentVersion\Uninstall\VLC media player").DisplayVersion
  register: version

- name: upgrade
  win_package:
    product_id="VLC media player upgrade" # Muss anders sein damit das Paket nochmal installiert wird
    path="//myserver/updates/software/vlc/vlc-2.2.2-win32.exe"
    arguments="/L=1031 /S"
  register: upgrade_results # Speichert das Ergebnis des Tasks in die Variabel "upgrade_results"
  changed_when: '"was installed" in upgrade_results.msg' # Ändert den Status auf "changed" wenn der String "was installed" im Ergebnis ist
  failed_when: '"was installed" not in upgrade_results.msg' # Status "failed" wenn "was installed" nicht im Ergebnis ist
  ignore_errors: True # Sonst bricht er ab bevor der Task überhaupt die Variabel "upgrade_results" befüllt
  when: '"2.2.2" not in version.stdout_lines' # Den Task nur ausführen wenn die Version eine andere ist

Ansible, win_package und die product_id

Gerade läuft das letzte Windows 7 auf Windows 10 Update. Diese Möglichkeit habe ich genutzt um für die Administration der Clients von SaltStack zu Ansible zu wechseln. Windows Administration ist für mich eher so ein leidiges Thema, was mich ziemlich oft auf die Palme bringt. Ansible benutze ich auf den Linux Servern für fast alles. Mal schnell ein paar Update einspielen oder auch für das ausrollen meines batcaves.

Master vorbereiten

Neben Ansible sollte man noch das Modul pywinrm installieren:

pip install "pywinrm>=0.1.1"

Clients vorbereiten

Um Ansible unter Windows zu benutzen muss ein PowerShell Script ausgeführt werden. Damit dies funktioniert muss erst eine PowerShell Policy angepasst werden. Dies tut man mit set-executionpolicy remotesigned. Danach Script ausführen und alles sollte eingerichtet sein.

Inventory

Auch die hosts-Datei muss ein wenig angepasst werden. Es werden ein paar zusätzliche Daten benötigt. Wenn das hosts-File so aussieht:

[windows]
192.168.1.5
192.168.1.6
192.168.1.7

habe ich eine group_vars/windows.yml angelegt die so aussieht:

ansible_user: winadminuser
ansible_password: winadminpassword
ansible_port: 5986
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore

Dieses File kann man mit ansible-vault encrypt group_vars/windows.yml verschlüßeln.

Der erste Test

ansible windows -m win_ping --ask-vault-pass sollte für einen ersten Versuch reichen.

Pakete installieren

Hier ein einfaches Beispiel um ein ein Paket zu installieren:

---
- name: Install the vc thingy
  win_package:
    name="Microsoft Visual C thingy"
    path="http://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe"
    Product_Id="{CF2BEA3C-26EA-32F8-AA9B-331F7E34BA97}"
    Arguments="/install /passive /norestart"

Ich hatte mit fast allen Paketen Probleme die product_id herauszufinden. Erst dachte ich es handelt sich hierbei um den Titel der Software in den Systemeinstellungen unter Windows. Pustekuchen. Man muss sich diese ID aus den Registry popeln. Diese befindet sich unter HKLM:\Software\microsoft\windows\currentversion\uninstall. Aber die sah bei mir ziemlich dünn aus und war bei weiten nicht mit allen Sachen gefüllt die ich installiert hatte. Stellt sich herraus das ein Teil der Registry sich unter HKLM:\Software\wow6432node\microsoft\windows\currentversion\uninstall versteckt. Und zwar wenn es sich um 32bit Pakete handelt.

Windows Updates mit SaltStack verteilen

Und weiter geht die wilde Reise um SaltStack Windows Hosts zu administrieren. Mein ewiges Projekt um um Windows Servern herumzukommen. Mittlerweise installier ich per Salt Software und update diese auch von einem Linux Server aus.

Als Basis setze ich den WSUS Offline Updater ein. Ein ganz tolles Stück Software, welches der Heise Verlag mal veröffentlicht hat und seitdem weiter entwickelt wird. Es ist eigentlich dazu gedacht Windows Update herunterzuladen und dann auf eine CD zu brennen mit der man dann Turnschuhadmin-mäßig durch das Büro rennen kann und offline die Updates einspielt. Nachdem die Update runtergeladen wurden, wird unter anderem ein Batch-Script erstellt das Massenupdate anschubst. Meine Herangehensweise ist vielleicht noch nicht 100% ausgereift aber erstmal ein Anfang.

Den WSUS Offline Updater auf den Salt-Master runterladen, entpacken und die Pakete laden. In dem Verzeichnis sh/ liegt das Script DownloadUpdates.sh. Dies ist unser Freund. Es wird aufgerufen und abgefragt welche Updates man gerne haben möchte. Ist alles auf der Festplatte, legt man sich sein state-file an. Ich habe es einfach als init.sls in den entpackten wsusoffline-Ordner geschmissen.

c:/windows/temp/wsusoffline:
  file.recurse:
    - source: salt://win/wsusoffline/client

c:/windows/temp/wsusoffline/cmd/DoUpdate.cmd:
  cmd.run

Da passiert kein Hexenwerk. Ich kopiere die Windows-Updates, in ein temporäres Verzeichnis, auf die Clients. Ganz praktisch das quasi ein Sync stattfindet. Er bringt die beiden Verzeichnisse auf einen Stand. Danach wird lokal das Update-Script ausgeführt.

Also los geht es mit sudo salt 'testwindowsclient' state.sls win/wsusoffline:

testwindowsclient:
----------
          ID: c:/windows/temp/wsusoffline
    Function: file.recurse
      Result: True
     Comment: The directory c:/windows/temp/wsusoffline is in the correct state
     Changes:   
----------
          ID: c:/windows/temp/wsusoffline/cmd/DoUpdate.cmd
    Function: cmd.run
      Result: False
     Comment: Command "c:/windows/temp/wsusoffline/cmd/DoUpdate.cmd" run
     Changes:   
              ----------
              pid:
                  4252
              retcode:
                  3010
              stderr:
                  Der Befehl "chcp" ist entweder falsch geschrieben oder
                  konnte nicht gefunden werden.
              stdout:
                  Starting WSUS Offline Update (v. 9.4.2) at 14:33:59,78...
                  Checking user's privileges...
                  Determining system's properties...
                  Determining Windows licensing info...
                  Found Microsoft Windows version: 6.1.7601.18700 (w61 x64 deu sp1)
                  Found Microsoft Windows Software Licensing Management Tool info...
                  Name: Windows(R) 7, Ultimate edition
                  Beschreibung: Windows Operating System - Windows(R) 7, OEM_COA_SLP channel
                  Teil-Product Key: JX37D
                  Lizenzstatus: Lizenziert
                  Found Microsoft Office 2007 Word version: 12.0.4518.1014 (o2k7 deu sp0)
                  Checking medium content...
                  Medium build date: 11.02.2015
                  Medium supports Microsoft Windows (w61 x64 glb).
                  Medium does not support Microsoft Office (ofc glb).
                  Checking Windows Service Pack version...
                  Checking Windows Update Agent version...
                  Checking Windows Installer version...
                  Checking Windows Script Host version...
                  Checking Internet Explorer version...
                  Checking Windows Defender installation state...
                  Checking Office Service Pack versions...
                  Warning: Update kb2526086 not found.
                  Warning: Office Service Pack installation file(s) not found.
                  Checking state of service 'Windows Update'...
                  14:34:02,93 - Listing ids of missing updates (please be patient, this will take a while)...
                  14:37:42,05 - Done.
                  Listing ids of installed updates...
                  Listing update files...
                  Info: Skipping update kb982861 (Internet Explorer 9) due to matching black list entry.
                  Warning: Update kb2538243 (id: 729a0dcb-df9e-4d02-b603-ed1aee074428) not found.
                  Info: Skipping update kb2526086 (Office 2007 Service Pack 3) due to matching black list entry.
                  Info: Skipping update kb2917500 (Revoked Root Certificates) due to matching black list entry.
                  Info: Skipping update kb2841134 (Internet Explorer 11) due to matching black list entry.
                  Info: Skipping update kb890830 (Malicious Software Removal Tool) due to matching black list entry.
                  Installing updates...
                  Installing update 1 of 1...
                  Installing ..\w61-x64\glb\windows6.1-kb2532531-x64_4625e47e04e8708b66895de5439ec7221cccd41b.cab...
                  Checking Microsoft Security Essentials installation state...

                  Installation successful. Please reboot your system now.


                  Ending WSUS Offline Update at 14:37:49,32...

Summary
------------
Succeeded: 1
Failed:    1
------------
Total:     2

Was auffällt:

  • Es meint ein Job wäre gefailed. Irgendein komischer Fehler mit chcp. Danach laufen die Updates trotzdem an.
  • Natürlich laufe ich in die Falle mit den Reboots. Ich muss noch rausfinden ob es nur heißt das er einen Reboot braucht damit die Änderungen in Kraft treten oder ob ich nach dem Reboot nochmal das Script anschmeißen muss.

Es bleibt spannend. Aber es ist ein Anfang...

Windows Clients administrieren mit SaltStack

Das Leben ist nicht immer schön. Manchmal muss man sich auch um andere Systeme kümmern. Softwareupdates einspielen oder zum Debuggen sich kurz die Netzwerk-Konfiguration anzeigen lassen. Während meiner Ausbildung war es üblich das ich als Azubi 60-70 Computer ablaufen musste und überall zum Beispiel den Adobe Acrobat Reader updaten musste. Selbst ein WSUS wurde zu diesem Zeitpunkt noch nicht eingesetzt. Zur Zeit administriere ich eine Handvoll Linux Server und mehrere Windows 7 Clients. Um den Einsatz eines WSUS zu umgehen hatte ich eine Weile auf WPKG gesetzt. Das besteht aus einen Client der unter Windows beim Start und Runterfahren auf einer SMB-Freigabe lauscht auf der ein VBS-Script liegt. Und dann passiert irgendeine Art von "Magic". Die einzellnen Programme werden in ein, für mich, cryptischen XML-Files gepflegt. Meistens ist es ein kopfloses copy-pasten aus dem WPKG-Wiki. Meistens funktionierte es... manchmal aber auch nicht.

In letzter Zeit las ich mehr über Ansible. Ein Tool mit dem man viele Server gleichzeitig bespielen und administrieren kann. Gerade in Zeiten des Heartbleed-Bugs ein Segen für viele Systemadministratoren. Die Anzahl der Server die ich administriere ist überschaubar. Die Anzahl der Windows-Clients ist doch an einem Punkt wo es nervt die ganzen Sicherheitsupdates einzupflegen. Problem: Ansible kann kein Windows. SaltStack schon. Auf SaltStack stieß ich duch meine Suche Ansible auf Windows Client laufen zu bekommen. Also schnell aufgesetzt... und siehe da... es läuft. Auf Windows wird ein kleiner Client installiert und den Rest machen Python-Scripte auf einem Linux-Server. Und da bin ich schon bei einem der Vorteile für mich: Es ist in Python geschrieben. Für die Templates wird Jinja2 eingesetzt. Ich bin alles andere als ein Wahnnsinns-Programmierer aber trotzdem bin ich nicht ganz abgeneigt wenn es in einer Sprache geschrieben ist die ich ansatzweise, minimal verstehe. Für meine Windows-Files habe ich ein Repo angelegt. Dort habe ich die Software die ich so jeden Tag auf meinem Windows Client brauche oder die gut ist um sie vorzuhalten. Man weiß ja nie. In den jeweiligen Verzeichnissen liegt eine init.sls und files. In init.sls sind die Anweisungen der Pakete zum installieren und deinstallieren. In files liegen die Setup-Files. Sehr praktisch da wir hinter einem Proxy sind und Windows da relativ oft Probleme hat aus dem Internet Files zu laden. Also kein hassle mit Samba oder dem Internet. Die init.sls für Firefox sieht bei mir wie folgt aus:

firefox:
  32.0.3:
    installer: 'salt://win/repo/salt-winrepo.git/firefox/files/Firefox Setup 32.0.3.exe'
    full_name: 'Mozilla Firefox 32.0.3 (x86 de)'
    reboot: False
    install_flags: ' /s '
    uninstaller: 'C:\Program Files (x86)\Mozilla Firefox\uninstall\helper.exe'
    uninstall_flags: ' /S'
  32.0.2:
    installer: 'salt://win/repo/salt-winrepo.git/firefox/files/Firefox Setup 32.0.2.exe'
    full_name: 'Mozilla Firefox 32.0.2 (x86 de)'
    reboot: False
    install_flags: ' /s '
    uninstaller: 'C:\Program Files (x86)\Mozilla Firefox\uninstall\helper.exe'
    uninstall_flags: ' /S'

Eigentlich selbsterklärend. Für jede Version die ich anbiete lege ich einen neuen Abschnitt an. Darin wird festgelegt wo das Setup-File liegt und wie es "silent" installiert werden kann. Was natürlich auch praktisch ist: Wie es wieder deinstalliert werden kann. Das salt:// spiegelt

file_roots:
  base:
    - /srv/salt

in der /etc/salt/master wieder. In dem gleichen File gibt es auch noch einen Bereich für die Windows Software Repo settings:

#####     Windows Software Repo settings #####
##############################################
# Location of the repo on the master
win_repo: '/srv/salt/win/repo'

# Location of the master's repo cache file
win_repo_mastercachefile: '/srv/salt/win/repo/winrepo.p'

# List of git repositories to include with the local repo
win_gitrepos:
  - 'https://github.com/xsteadfastx/salt-winrepo.git'

Im unteren Abschnitt kann man noch externe Git-Repos hinzufügen die mit salt-run winrepo.update_git_repos geupdatet werden können. Der Windows Client ist relativ flott eingerichtet. Der Installationsdialog fragt nach dem Master. Ich habe in dem Firmen-DNS einfach den Host "salt" angelegt. Dieser zeigt auf den Master. Dann muss in Zukunft auch nichts an der Config geschraubt werden, sollte der Master mal seine Location ändern. Dann werden Keys zwischen Master und Minion (so nennt man die Clients) ausgetauscht. Diese müssen auf den Master mit salt -A akzeptiert werden. Los gehts:

Mit salt-run winrepo.genrepo wird das Cache-File aktualisiert. Nun muss es mit salt '*' pkg.refresh_db auf die Minions überspielt werden. Spricht man so zum Beispiel Debian/Ubuntu Systeme an, wird auf den Minions ein apt-get update ausgeführt. Da da Minions nun alle Informationen haben kann man Firefox mit sudo salt '*' pkg.install firefox installieren. Soll es eine bestimmte Version sein sudo salt '*' pkg.install firefox version=32.0.3. Und fertig ist die laube. Natürlich kann man auch States (wininstall.sls) anlegen. In diesem Fall eine Liste von Software die jeder Windows-Minion haben soll:

7zip:
  pkg.installed

adobeflash:
  pkg.installed

adobereader:
  pkg.installed

jre:
  pkg.installed

vlc:
  pkg.installed

firefox:
  pkg.installed

thunderbird:
  pkg.installed

jitsi:
  pkg.installed

keepass:
  pkg.installed

Dies kann ich mit salt '*' state.sls wininstall ausrollen.

Ein paar Beispiele was man noch so alles machen kann:

# Netzwerk Informationen der Minions
salt '*' network.interfaces

# Liste der installierten Software auf den Minions
salt '*' pkg.list_pkgs

# Liste der lokalen User
salt '*' ps.get_users

Also ich bin und bleibe Fan.