Marvin Preuss xsteadfastx photo

github twitter mastodon flickr

windows

WSUS Offline und Ansible

/// d794cd8 /// ansible windows

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.

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

/// d794cd8 /// ansible windows linux

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.

https://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

/// d794cd8 /// ansible windows

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:

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

/// d794cd8 /// saltstack windows

{% notebook notebooks/windows-updates-mit-saltstack-verteilen.ipynb %}

{% notebook notebooks/windows-updates-mit-saltstack-verteilen.ipynb %}



Windows Clients administrieren mit SaltStack

/// d794cd8 /// saltstack windows

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.

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.



1 of 1