centos:ansible:first

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
Nächste ÜberarbeitungBeide Seiten der Revision
centos:ansible:first [14.01.2020 16:20. ] – [Zielverzeichnis anlegen und öffentlichen Schlüssel kopieren] djangocentos:ansible:first [20.06.2020 19:07. ] – [Links] django
Zeile 2: Zeile 2:
 {{:centos:ansible:ansible_logo.png?nolink&125|Bild: Ansible Logo}} \\ \\ {{:centos:ansible:ansible_logo.png?nolink&125|Bild: Ansible Logo}} \\ \\
  
-Nachdem wir uns bereits eingehend mit den **[[centos:ansible:start#grundlagen|Grundlagen]]** und auch schon das benötigte Programmpaket auf unserer Admin-Workstation zur orchestrierung **[[centos:ansible:start#installation|installiert haben]]**, machen wir uns nun an die Konfguration von Ansible und wagen uns an die ersten Playbooks heran. +Nachdem wir uns bereits eingehend mit den **[[centos:ansible:start#grundlagen|Grundlagen]]** und auch schon das benötigte Programmpaket auf unserer Admin-Workstation zur Orchestrierung **[[centos:ansible:start#installation|installiert haben]]**, machen wir uns nun an die Konfiguration von Ansible und wagen uns an die ersten Playbooks heran. 
  
 ===== Voraussetzung: SSH-Schlüssel ===== ===== Voraussetzung: SSH-Schlüssel =====
-Einer der wesentlichen Vorteil von Ansible ist, dass hierzu kein separater eigener Server aufgesetzt werden muss, sondern dass im Grunde ein (Client-/Admin-)Workstation ausreicht, auf dem die Playbooks zur Verfügung stehen. Dieser Rechner muss dann lediglich die Server, die automatisiert verwaltet und konfiguriert werden sollen, mit Hilfe der SSH erreichen können, denn Ansible arbeitet im Push-Verfahren und benötigt neben **[[centos:ssh_c7|SSH]]** und **[[https://python.org|Python]]** keine weitere Installation auf den einzelnen Systemen. +Einer der wesentlichen Vorteil von Ansible ist, dass hierzu kein separater eigener Server von Nöten ist, sondern dass im Grunde ein (Client-/Admin-)Workstation ausreicht, auf dem die Playbooks zur Verfügung stehen. Gleichwohl wird man in größeren INstallationsumgebungen in aller Regel einen dedizierten Server zur Orchestrierung mit Ansible vorhalten. Dieser Rechner muss dann lediglich die Server, die automatisiert verwaltet und konfiguriert werden sollen, mit Hilfe der SSH erreichen können, denn Ansible arbeitet im Push-Verfahren und benötigt neben **[[centos:ssh_c7|SSH]]** und **[[https://python.org|Python]]** keine weitere Installation auf den einzelnen Systemen. 
  
 ==== Dokumentation ==== ==== Dokumentation ====
Zeile 46: Zeile 46:
 </WRAP> </WRAP>
  
-Auf diese RSA-Schlüssel muss man aber nicht mehr zwingend zurückgreifen, stehen doch aktuellere und zeitgemäßere Cipher, MACs, Schlüssel Typen und Key Exchange Algorithmen zur Verfügung. Als Alternative zu einem RSA-Keys wollen wir nun nun einen [[http://ed25519.cr.yp.to/|ed25519]] Schlüssel erzeugen. [[https://de.wikipedia.org/wiki/Curve25519|Ed25519]] ist ein Elliptic Curve Signature Schema, welches beste Sicherheit bei vertretbaren Aufwand verspricht, als ECDSA oder DSA dies der Fall ist. Zur Auswahl sicherer kryptografischer Kurven bei der //Elliptic-Curve Cryptography// findet man auf der Seite [[https://safecurves.cr.yp.to/|hier]] hilfreiche Erklärungen und eine Gegenüberstellung der möglichen verschiedenen Alternativen.+Auf diese RSA-Schlüssel muss man aber nicht mehr zwingend zurückgreifen, stehen doch aktuellere und zeitgemässere Cipher, MACs, Schlüssel Typen und Key Exchange Algorithmen zur Verfügung. Als Alternative zu einem RSA-Keys wollen wir nun nun einen [[http://ed25519.cr.yp.to/|ed25519]] Schlüssel erzeugen. [[https://de.wikipedia.org/wiki/Curve25519|Ed25519]] ist ein Elliptic Curve Signature Schema, welches beste Sicherheit bei vertretbaren Aufwand verspricht, als ECDSA oder DSA dies der Fall ist. Zur Auswahl sicherer kryptografischer Kurven bei der //Elliptic-Curve Cryptography// findet man auf der Seite [[https://safecurves.cr.yp.to/|hier]] hilfreiche Erklärungen und eine Gegenüberstellung der möglichen verschiedenen Alternativen.
  
 Im zweiten Fall erstellen wir uns nun einen **ED25519**-Schlüssel (**''-t''**), mit einer festen Schlüssellänge. Der Parameter (**''-a''**) beschreibt dabei die Anzahl der KDF-Schlüsselableitfunktion (siehe manpage von ssh-keygen). Wir  verwenden wieder als Beschreibung **Ansible Systemuser** (**''-C''**) und als Ziel-/Speicherort **~/.ssh/id_ed25519_ansible** (**''-f''**). Im zweiten Fall erstellen wir uns nun einen **ED25519**-Schlüssel (**''-t''**), mit einer festen Schlüssellänge. Der Parameter (**''-a''**) beschreibt dabei die Anzahl der KDF-Schlüsselableitfunktion (siehe manpage von ssh-keygen). Wir  verwenden wieder als Beschreibung **Ansible Systemuser** (**''-C''**) und als Ziel-/Speicherort **~/.ssh/id_ed25519_ansible** (**''-f''**).
Zeile 83: Zeile 83:
     scp /home/django/.ssh/id_ed25519_ansible.pub zielhost:/home/django/.ssh/key.pub     scp /home/django/.ssh/id_ed25519_ansible.pub zielhost:/home/django/.ssh/key.pub
  
-Anschließend wird der Schlüssel in die Datei authorized_keys kopiert. Diese Datei kann mehrere Schlüssel enthalten, daher ist das doppelte Umleitungszeichen wichtig, um eine evt. existierende Datei nicht versehentlich zu überschreiben. Somit wird der neue Schlüssel in die Datei hinzugefügt:+Anschliessend wird der Schlüssel in die Datei authorized_keys kopiert. Diese Datei kann mehrere Schlüssel enthalten, daher ist das doppelte Umleitungszeichen wichtig, um eine evt. existierende Datei nicht versehentlich zu überschreiben. Somit wird der neue Schlüssel in die Datei hinzugefügt:
    $ cat key.pub >> authorized_keys    $ cat key.pub >> authorized_keys
  
Zeile 91: Zeile 91:
  
  
-<WRAP round info>~~codedoc:xref:anchor_sshcopyid~~ **ssh-copy-id**+<WRAP round info>**ssh-copy-id** ~~codedoc:xref:anchor_sshcopyid~~ \\
 Das Kopieren des Public-Keys auf unseren Zielhost mit Anpassen der Dateiberechtigungen kann man natürlich auch einfacher vornehmen. Man benutzt hierzu einfach den Befehl **ssh-copy-id** aus dem Paket **//openssh-clients//**. Das Kopieren des Public-Keys auf unseren Zielhost mit Anpassen der Dateiberechtigungen kann man natürlich auch einfacher vornehmen. Man benutzt hierzu einfach den Befehl **ssh-copy-id** aus dem Paket **//openssh-clients//**.
  
Zeile 97: Zeile 97:
   * ed25519-Key <code> $ ssh-copy-id -i ~/.ssh/id_ed25519_ansible.pub zielhost.intra.nausch.org</code>   * ed25519-Key <code> $ ssh-copy-id -i ~/.ssh/id_ed25519_ansible.pub zielhost.intra.nausch.org</code>
  
-Mit der Angabe ''~/.ssh/id_rsa4096_dmz'' bzw. ''~/.ssh/id_ed25519_dmz'' kopiert dann der Befehl **ssh-copy-id** den zugehörigen öffentlichen Schlüssel auf den Zielhost ''testhost.intra.nausch.org''.+Mit der Angabe ''~/.ssh/id_rsa4096_dmz'' bzw. ''~/.ssh/id_ed25519_dmz'' kopiert dann der Befehl **ssh-copy-id** den zugehörigen öffentlichen Schlüssel auf den Zielhost ''zielhost.intra.nausch.org''.
 </WRAP> </WRAP>
  
 ==== Verbindungstest via SSH ==== ==== Verbindungstest via SSH ====
 Nun können wir eine erste Testverbindung zu unserem Zielsystem aufbauen. Nun können wir eine erste Testverbindung zu unserem Zielsystem aufbauen.
-   $ ssh -l ansible -i ~/.ssh/id_ed25519_ansible 10.0.0.90+   $ ssh -l ansible -i ~/.ssh/id_ed25519_ansible 10.0.0.190
 <code>############################################################################## <code>##############################################################################
 #                                                                            # #                                                                            #
-       ╭∩╮( ͡° ل͟ ͡° )╭∩╮   This is not your server!   ╭∩╮( ͡° ل͟ ͡° )╭∩╮        #+        ╭∩╮( ͡° ل͟ ͡° )╭∩╮   This is not your server!   ╭∩╮( ͡° ل͟ ͡° )╭∩╮       #
 #                                                                            # #                                                                            #
 #             Unauthorized access to this system is prohibited !             # #             Unauthorized access to this system is prohibited !             #
Zeile 115: Zeile 115:
 Activate the web console with: systemctl enable --now cockpit.socket Activate the web console with: systemctl enable --now cockpit.socket
  
-Last login: Mon Dec 30 21:43:35 2019</code>+Last login: Mon Feb 30 21:43:35 2019</code>
  
 ===== (Grund-)Konfiguration ===== ===== (Grund-)Konfiguration =====
Zeile 123: Zeile 123:
 ├── hosts ├── hosts
 └── roles └── roles
 +
 +1 directory, 2 files
 </code> </code>
  
 ==== /etc/ansible/ansible.cfg ==== ==== /etc/ansible/ansible.cfg ====
-Fast alle Parameter können in einem Ansible-playbook oder mit Kommandozeilenflags überschrieben werden. Ansible liest ANSIBLE_CONFIG, ansible.cfg im aktuellen Arbeitsverzeichnis, .ansible.cfg im Home-Verzeichnis des Admin-Users oder /etc/ansible/ansible.cfg, je nachdem was zu erst gefunden und gelesen wird.+Fast alle Parameter können in einem Ansible-playbook oder mit Kommandozeilenflags überschrieben werden.  
 + 
 +Bei der Ermittlung der Konfigurationsdatei - **''ansible.cfg''** sucht [[https://www.ansible.com/|Ansible]] die **[[https://docs.ansible.com/ansible/latest/reference_appendices/config.html|Ansible Konfigurationseinstellungen]]** nacheinander in folgenden Verzeichnissen: 
 +  - Erst durch die Umgebungsvariable ''ANSIBLE_CONFIG'' definierten individuellen Konfigurationsdatei 
 +  - dann die Datei **''./ansible.cfg''** im aktuellen Verzeichnis und  
 +  - anschließend **''~/.ansible.cfg''** im $HOME Verzeichnis des verwendeten Benutzers und 
 +  zum Schluss in der Konfigurationsdatei **''/etc/ansible/ansible.cfg''**. 
    # vim /etc/ansible/ansible.cfg    # vim /etc/ansible/ansible.cfg
  
Zeile 621: Zeile 630:
 </file> </file>
  
-{{page>centos:ansible:hosts&nofooter&showheader}}+==== /etc/ansible/hosts ==== 
 +In der Konfigurationsdatei **''/etc/ansible/hosts''** werden alle Hosts und Hostgruppen definiert, über die Ansible bei der Orchestrierung mit Hilfe der **SSH** zentral administriert werden sollen. 
 + 
 +In dieser Konfigurationsdatei gelten folgende Rahmenparameter: 
 +  * Kommentare beginnen mit dem Zeichen '#'
 +  * Leere Zeilen werden ignoriert 
 +  * Gruppen von Hosts werden durch [header]-Elemente abgegrenzt 
 +  * Es können Hostnamen oder IP-Adressen verwendet werden 
 +  * Ein Hostname/IP-Adresse kann Mitglied mehrerer Gruppen sein 
 + 
 +   # vim /etc/ansible/hosts 
 +<file bash /etc/ansible/hosts># 
 +# It should live in /etc/ansible/hosts 
 +
 +#   - Comments begin with the '#' character 
 +#   - Blank lines are ignored 
 +#   - Groups of hosts are delimited by [header] elements 
 +#   - You can enter hostnames or ip addresses 
 +#   - A hostname/ip can be a member of multiple groups 
 + 
 +# Ex 1: Ungrouped hosts, specify before any group headers. 
 + 
 +## green.example.com 
 +## blue.example.com 
 +## 192.168.100.1 
 +## 192.168.100.10 
 + 
 +# Ex 2: A collection of hosts belonging to the 'webservers' group 
 + 
 +## [webservers] 
 +## alpha.example.org 
 +## beta.example.org 
 +## 192.168.1.100 
 +## 192.168.1.110 
 + 
 +# If you have multiple hosts following a pattern you can specify 
 +# them like this: 
 + 
 +## www[001:006].example.com 
 + 
 +# Ex 3: A collection of database servers in the 'dbservers' group 
 + 
 +## [dbservers] 
 +##  
 +## db01.intranet.mydomain.net 
 +## db02.intranet.mydomain.net 
 +## 10.25.1.56 
 +## 10.25.1.57 
 + 
 +# Here's another example of host ranges, this time there are no 
 +# leading 0s: 
 + 
 +## db-[99:101]-node.example.com 
 + 
 +</file> 
 + 
 +==== Ansible: Directory Layout ===== 
 +Gemäß **[[http://docs.ansible.com/ansible/latest/playbooks_best_practices.html|Ansible's Best Practices]]** gibt es durchaus einige Möglichkeiten wie man seine Playbooks organisiert. In aller Regel wird man hier die eigenen individuellen Bedürfnisse (seiner Unternehmung) vornan stellen.  
 + 
 +<WRAP center round tip 90%> 
 +Jedoch empfiehlt es sich durchaus auf Empfohlenes zurückzugreifen! So empfiehlt es sich zum Beispiel auch, Rollen anstelle von Aufgaben zu verwenden, da dies wesentlich bei der Flexibilität und besseren Organisation der eigenen Playbooks/Codes helfen! 
 +</WRAP> 
 + 
 +Ansible bietet **[[https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html#content-organization|zwei Beispiele]]** für Verzeichnis-Layouts. Wir werden im folgen Beispiel uns eine Umgebung aufbauen, in der unser Inventory, also unsere Hosts und Nodes, in mehreren **''groups''** und **''childs''** aufteilen. 
 + 
 +Mit dieser Struktur sind wir dann in der Lage jede Inventardatei mit ihrer **''group_vars''** und **''host_vars''** in ein separates Verzeichnis zu packen. Fernen können wir so einfach neue Rollen **''roles''**erzeugen, in dem wir dann einfach die bereits vorgefertigte Rollenvorlage **''common''** kopieren. 
 + 
 +Werfen wir also einfach mal auf die beschrieben Verzeichnisstruktur einen genaueren Blick. Die entsprechende Verwendung der einzelnen Verzeichnisse und DAteien ist in der Aufstellung entsprechend angegeben. 
 +<code>ansible/ 
 +├── filter_plugins                 # (optionales) Verzeichnis für individuelle filter plugins 
 +├── library                        # (optionales) Verzeichnis für benutzerdefinierte Module 
 +├── module_utils                   # (optionales) Verzeichnis für benutzerdefinierte module_utils zur Unterstützung von Modulen 
 +│ 
 +├── inventories                    # Verzeichnis für die einzelnen (unterschiedlichen) Invenory 
 +│   ├── production                 # Verzeichnis für die Hosts aus der Gruppe production 
 +│   │   ├── hosts.yml              # YML-Datei mit den Host-Definitionen aus der Gruppe production 
 +│   │   ├── group_vars             # Verzeichnis für die Gruppenspezifischen Variablen der Gruppe production 
 +│   │   └── host_vars              # Verzeichnis für die Hostspezifischen Variablen der Gruppe production 
 +│   │     
 +│   └── staging                    # Verzeichnis für die Hosts aus der Gruppe staging 
 +│       ├── hosts.yml              # YML-Datei mit den Host-Definitionen aus der Gruppe production 
 +│       ├── group_vars             # Verzeichnis für die Gruppenspezifischen Variablen der Gruppe  
 +│       └── host_vars              # Verzeichnis für die Hostspezifischen Variablen der Gruppe production 
 +│ 
 +├── roles                          # Verzeichnis für die einzelnen (unterschiedlichen) Rollen 
 +│   └── common                     # Verzeichnis "role" common mit seinen entsprechenden Definitionen - Vorlage zum Kopieren 
 +│       ├── defaults               # Verzeichnis "defaults" 
 +│       │   └── main.yml           # Standardvariablen mit niedrigerer Priorität für diese Rolle 
 +│       ├── files                  # Verzeichnis "files" 
 +│       │   └── main.yml           # (Skript-)Dateien zur Verwendung als Kopier- bzw. Script-Rressource 
 +│       ├── handlers               # Verzeichnis "handlers" 
 +│       │   └── main.yml           # Datei mit den Definitionen zu den rollenspezifischen "handlers" 
 +│       ├── library                # Verzeichnis für benutzerdefinierte Module einer der Rolle (role) "common" 
 +│       ├── lookup_plugin          # Verzeichnis für weitere Arten von Plugins, wie in diesem Fall "lookup" 
 +│       ├── meta                   # Verzeichnis "meta" für Rollenspezifische Definitionen/Abhängigkeiten 
 +│       │   └── main.yml           # Datei mit Rollenspezifischen Definitionen 
 +│       ├── module_utils           # Verzeichnis "module_utils", die benutzerdefinierte module_utils der Rollen enthalten könnte 
 +│       ├── tasks                  # Verzeichnis "tasks" für kleinere Aufgabendateien 
 +│       │   └── main.yml           # Datei für kleinere Aufgaben, falls diese benötigt werden würden  
 +│       ├── templates              # Verzeichnis mit den Templates 
 +│       │   └── main.j2            # Template-Datei mit dem Dateisuffix/-Ende .j2 
 +│       └── vars                   # Verzeichnis "vars", mit den zu dieser Rolle zugeordneten Variablen 
 +│           └── main.yml           # Datei mit den Rollenspezifischen Variablen 
 +│ 
 +└── site.yml                       # master playbook 
 +</code> 
 + 
 +Um dieses Verzeichnis-Layout einfach und schnell auf den Weg zu bringen, verwenden wir die nachfolgend gezeigten zwei Befehle bzw. genauer gesagt die beiden Befehlskette: 
 +   $ mkdir -p ~/ansible/inventories/{production,staging}/{group_vars,host_vars} \ 
 +              ~/ansible/{library,module_utils,filter_plugins} \ 
 +              ~/ansible/roles/common/{tasks,handlers,templates,files,vars,defaults,meta,library,module_utils,lookup_plugin} 
 + 
 +   $ touch    ~/ansible/inventories/{production,staging}/hosts.yml \ 
 +              ~/ansible/site.yml \ 
 +              ~/ansible/roles/common/{tasks,handlers,templates,files,vars,defaults,meta}/main.yml 
 + 
 +==== Musterkonfiguration ===== 
 +=== ansible.cfg === 
 +Wie schon zuvor angemerkt sucht [[https://www.ansible.com/|Ansible]] die **[[https://docs.ansible.com/ansible/latest/reference_appendices/config.html|Ansible Konfigurationseinstellungen]]** bei der Ermittlung der Konfigurationseinstellungen nacheinander in folgenden Verzeichnissen: 
 +  - Erst durch die Umgebungsvariable ''ANSIBLE_CONFIG'' definierten individuellen Konfigurationsdatei,  
 +  - dann die Datei **''./ansible.cfg''** im aktuellen Verzeichnis und  
 +  - anschließend **''~/.ansible.cfg''** im $HOME Verzeichnis des verwendeten Benutzers und 
 +  - zum Schluss in der Konfigurationsdatei **''/etc/ansible/ansible.cfg''**. 
 + 
 +Wir werden nun unsere Host- und Admin-spezifische Konfiguration also in der datei **''.ansible.cfg''** im **''~''**-/Homeverzeichnis unseres Adminbenutzers vorhalten. Dazu kopieren wir uns die aus dem RPM stammende Musterdatei in unser home-Verzeichnis. 
 +   # cp /etc/ansible/ansible.cfg ~/.ansible.cfg 
 +Zuvor haten wir bereits die Datei **''~/ansible/inventories/production/hosts.yml''** für die Definition der Hosts (Inventory), bei der Verzeichnisanlage unserer **[[#ansibledirectory_layout|Musterinstallation]]** angelegt. Damit diese auch verwendet werden kann werden wir als erstes in der Konfigurationsdatei **''.ansible.cfg''** hierzu die Betreffende Option **''inventory''** in der Sektion **''[defaults]''** setzen. 
 +   # vim /etc/ansible/ansible.cfg 
 + 
 +<file bash /etc/ansible/ansible.cfg># config file for ansible -- https://ansible.com/ 
 +# =============================================== 
 + 
 +# nearly all parameters can be overridden in ansible-playbook 
 +# or with command line flags. ansible will read ANSIBLE_CONFIG, 
 +# ansible.cfg in the current working directory, .ansible.cfg in 
 +# the home directory or /etc/ansible/ansible.cfg, whichever it 
 +# finds first 
 + 
 +[defaults] 
 + 
 +# some basic default values... 
 + 
 +# Django 2020-06-19 
 +# default: #inventory      = /etc/ansible/hosts 
 +inventory       = ~/ansible/inventories/production/hosts.yml 
 +#library        = /usr/share/my_modules/ 
 +#module_utils   = /usr/share/my_module_utils/ 
 +#remote_tmp     = ~/.ansible/tmp 
 +#local_tmp      = ~/.ansible/tmp 
 +#plugin_filters_cfg = /etc/ansible/plugin_filters.yml 
 +#forks          = 5 
 +#poll_interval  = 15 
 +#sudo_user      = root 
 +#ask_sudo_pass = True 
 +#ask_pass      = True 
 +#transport      = smart 
 +#remote_port    = 22 
 +#module_lang    = C 
 +#module_set_locale = False 
 + 
 +# plays will gather facts by default, which contain information about 
 +# the remote system. 
 +
 +# smart - gather by default, but don't regather if already gathered 
 +# implicit - gather by default, turn off with gather_facts: False 
 +# explicit - do not gather by default, must say gather_facts: True 
 +#gathering = implicit 
 + 
 +# This only affects the gathering done by a play's gather_facts directive, 
 +# by default gathering retrieves all facts subsets 
 +# all - gather all subsets 
 +# network - gather min and network facts 
 +# hardware - gather hardware facts (longest facts to retrieve) 
 +# virtual - gather min and virtual facts 
 +# facter - import facts from facter 
 +# ohai - import facts from ohai 
 +# You can combine them using comma (ex: network,virtual) 
 +# You can negate them using ! (ex: !hardware,!facter,!ohai) 
 +# A minimal set of facts is always gathered. 
 +#gather_subset = all 
 + 
 +# some hardware related facts are collected 
 +# with a maximum timeout of 10 seconds. This 
 +# option lets you increase or decrease that 
 +# timeout to something more suitable for the 
 +# environment. 
 +# gather_timeout = 10 
 + 
 +# Ansible facts are available inside the ansible_facts.* dictionary 
 +# namespace. This setting maintains the behaviour which was the default prior 
 +# to 2.5, duplicating these variables into the main namespace, each with a 
 +# prefix of 'ansible_'
 +# This variable is set to True by default for backwards compatibility. It 
 +# will be changed to a default of 'False' in a future release. 
 +# ansible_facts. 
 +# inject_facts_as_vars = True 
 + 
 +# additional paths to search for roles in, colon separated 
 +#roles_path    = /etc/ansible/roles 
 + 
 +# uncomment this to disable SSH key host checking 
 +#host_key_checking = False 
 + 
 +# change the default callback, you can only have one 'stdout' type  enabled at a time. 
 +#stdout_callback = skippy 
 + 
 + 
 +## Ansible ships with some plugins that require whitelisting, 
 +## this is done to avoid running all of a type by default. 
 +## These setting lists those that you want enabled for your system. 
 +## Custom plugins should not need this unless plugin author specifies it. 
 + 
 +# enable callback plugins, they can output to stdout but cannot be 'stdout' type. 
 +#callback_whitelist = timer, mail 
 + 
 +# Determine whether includes in tasks and handlers are "static" by 
 +# default. As of 2.0, includes are dynamic by default. Setting these 
 +# values to True will make includes behave more like they did in the 
 +# 1.x versions. 
 +#task_includes_static = False 
 +#handler_includes_static = False 
 + 
 +# Controls if a missing handler for a notification event is an error or a warning 
 +#error_on_missing_handler = True 
 + 
 +# change this for alternative sudo implementations 
 +#sudo_exe = sudo 
 + 
 +# What flags to pass to sudo 
 +# WARNING: leaving out the defaults might create unexpected behaviours 
 +#sudo_flags = -H -S -n 
 + 
 +# SSH timeout 
 +#timeout = 10 
 + 
 +# default user to use for playbooks if user is not specified 
 +# (/usr/bin/ansible will use current user as default) 
 +#remote_user = root 
 + 
 +# logging is off by default unless this path is defined 
 +# if so defined, consider logrotate 
 +#log_path = /var/log/ansible.log 
 + 
 +# default module name for /usr/bin/ansible 
 +#module_name = command 
 + 
 +# use this shell for commands executed under sudo 
 +# you may need to change this to bin/bash in rare instances 
 +# if sudo is constrained 
 +#executable = /bin/sh 
 + 
 +# if inventory variables overlap, does the higher precedence one win 
 +# or are hash values merged together?  The default is 'replace' but 
 +# this can also be set to 'merge'
 +#hash_behaviour = replace 
 + 
 +# by default, variables from roles will be visible in the global variable 
 +# scope. To prevent this, the following option can be enabled, and only 
 +# tasks and handlers within the role will see the variables there 
 +#private_role_vars = yes 
 + 
 +# list any Jinja2 extensions to enable here: 
 +#jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n 
 + 
 +# if set, always use this private key file for authentication, same as 
 +# if passing --private-key to ansible or ansible-playbook 
 +#private_key_file = /path/to/file 
 + 
 +# If set, configures the path to the Vault password file as an alternative to 
 +# specifying --vault-password-file on the command line. 
 +#vault_password_file = /path/to/vault_password_file 
 + 
 +# format of string {{ ansible_managed }} available within Jinja2 
 +# templates indicates to users editing templates files will be replaced. 
 +# replacing {file}, {host} and {uid} and strftime codes with proper values. 
 +#ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host} 
 +# {file}, {host}, {uid}, and the timestamp can all interfere with idempotence 
 +# in some situations so the default is a static string: 
 +#ansible_managed = Ansible managed 
 + 
 +# by default, ansible-playbook will display "Skipping [host]" if it determines a task 
 +# should not be run on a host.  Set this to "False" if you don't want to see these "Skipping" 
 +# messages. NOTE: the task header will still be shown regardless of whether or not the 
 +# task is skipped. 
 +#display_skipped_hosts = True 
 + 
 +# by default, if a task in a playbook does not include a name: field then 
 +# ansible-playbook will construct a header that includes the task's action but 
 +# not the task's args.  This is a security feature because ansible cannot know 
 +# if the *module* considers an argument to be no_log at the time that the 
 +# header is printed.  If your environment doesn't have a problem securing 
 +# stdout from ansible-playbook (or you have manually specified no_log in your 
 +# playbook on all of the tasks where you have secret information) then you can 
 +# safely set this to True to get more informative messages. 
 +#display_args_to_stdout = False 
 + 
 +# by default (as of 1.3), Ansible will raise errors when attempting to dereference 
 +# Jinja2 variables that are not set in templates or action lines. Uncomment this line 
 +# to revert the behavior to pre-1.3. 
 +#error_on_undefined_vars = False 
 + 
 +# by default (as of 1.6), Ansible may display warnings based on the configuration of the 
 +# system running ansible itself. This may include warnings about 3rd party packages or 
 +# other conditions that should be resolved if possible. 
 +# to disable these warnings, set the following value to False: 
 +#system_warnings = True 
 + 
 +# by default (as of 1.4), Ansible may display deprecation warnings for language 
 +# features that should no longer be used and will be removed in future versions. 
 +# to disable these warnings, set the following value to False: 
 +#deprecation_warnings = True 
 + 
 +# (as of 1.8), Ansible can optionally warn when usage of the shell and 
 +# command module appear to be simplified by using a default Ansible module 
 +# instead.  These warnings can be silenced by adjusting the following 
 +# setting or adding warn=yes or warn=no to the end of the command line 
 +# parameter string.  This will for example suggest using the git module 
 +# instead of shelling out to the git command. 
 +# command_warnings = False 
 + 
 + 
 +# set plugin path directories here, separate with colons 
 +#action_plugins     = /usr/share/ansible/plugins/action 
 +#become_plugins     = /usr/share/ansible/plugins/become 
 +#cache_plugins      = /usr/share/ansible/plugins/cache 
 +#callback_plugins   = /usr/share/ansible/plugins/callback 
 +#connection_plugins = /usr/share/ansible/plugins/connection 
 +#lookup_plugins     = /usr/share/ansible/plugins/lookup 
 +#inventory_plugins  = /usr/share/ansible/plugins/inventory 
 +#vars_plugins       = /usr/share/ansible/plugins/vars 
 +#filter_plugins     = /usr/share/ansible/plugins/filter 
 +#test_plugins       = /usr/share/ansible/plugins/test 
 +#terminal_plugins   = /usr/share/ansible/plugins/terminal 
 +#strategy_plugins   = /usr/share/ansible/plugins/strategy 
 + 
 + 
 +# by default, ansible will use the 'linear' strategy but you may want to try 
 +# another one 
 +#strategy = free 
 + 
 +# by default callbacks are not loaded for /bin/ansible, enable this if you 
 +# want, for example, a notification or logging callback to also apply to 
 +# /bin/ansible runs 
 +#bin_ansible_callbacks = False 
 + 
 + 
 +# don't like cows?  that's unfortunate. 
 +# set to 1 if you don't want cowsay support or export ANSIBLE_NOCOWS=1 
 +#nocows = 1 
 + 
 +# set which cowsay stencil you'd like to use by default. When set to 'random', 
 +# a random stencil will be selected for each task. The selection will be filtered 
 +# against the `cow_whitelist` option below. 
 +#cow_selection = default 
 +#cow_selection = random 
 + 
 +# when using the 'random' option for cowsay, stencils will be restricted to this list. 
 +# it should be formatted as a comma-separated list with no spaces between names. 
 +# NOTE: line continuations here are for formatting purposes only, as the INI parser 
 +#       in python does not support them. 
 +#cow_whitelist=bud-frogs,bunny,cheese,daemon,default,dragon,elephant-in-snake,elephant,eyes,
 +#              hellokitty,kitty,luke-koala,meow,milk,moofasa,moose,ren,sheep,small,stegosaurus,
 +#              stimpy,supermilker,three-eyes,turkey,turtle,tux,udder,vader-koala,vader,www 
 + 
 +# don't like colors either? 
 +# set to 1 if you don't want colors, or export ANSIBLE_NOCOLOR=1 
 +#nocolor = 1 
 + 
 +# if set to a persistent type (not 'memory', for example 'redis') fact values 
 +# from previous runs in Ansible will be stored.  This may be useful when 
 +# wanting to use, for example, IP information from one group of servers 
 +# without having to talk to them in the same playbook run to get their 
 +# current IP information. 
 +#fact_caching = memory 
 + 
 +#This option tells Ansible where to cache facts. The value is plugin dependent. 
 +#For the jsonfile plugin, it should be a path to a local directory. 
 +#For the redis plugin, the value is a host:port:database triplet: fact_caching_connection = localhost:6379:
 + 
 +#fact_caching_connection=/tmp 
 + 
 + 
 + 
 +# retry files 
 +# When a playbook fails a .retry file can be created that will be placed in ~/ 
 +# You can enable this feature by setting retry_files_enabled to True 
 +# and you can change the location of the files by setting retry_files_save_path 
 + 
 +#retry_files_enabled = False 
 +#retry_files_save_path = ~/.ansible-retry 
 + 
 +# squash actions 
 +# Ansible can optimise actions that call modules with list parameters 
 +# when looping. Instead of calling the module once per with_ item, the 
 +# module is called once with all items at once. Currently this only works 
 +# under limited circumstances, and only with parameters named 'name'
 +#squash_actions = apk,apt,dnf,homebrew,pacman,pkgng,yum,zypper 
 + 
 +# prevents logging of task data, off by default 
 +#no_log = False 
 + 
 +# prevents logging of tasks, but only on the targets, data is still logged on the master/controller 
 +#no_target_syslog = False 
 + 
 +# controls whether Ansible will raise an error or warning if a task has no 
 +# choice but to create world readable temporary files to execute a module on 
 +# the remote machine.  This option is False by default for security.  Users may 
 +# turn this on to have behaviour more like Ansible prior to 2.1.x.  See 
 +# https://docs.ansible.com/ansible/become.html#becoming-an-unprivileged-user 
 +# for more secure ways to fix this than enabling this option. 
 +#allow_world_readable_tmpfiles = False 
 + 
 +# controls the compression level of variables sent to 
 +# worker processes. At the default of 0, no compression 
 +# is used. This value must be an integer from 0 to 9. 
 +#var_compression_level = 9 
 + 
 +# controls what compression method is used for new-style ansible modules when 
 +# they are sent to the remote system.  The compression types depend on having 
 +# support compiled into both the controller's python and the client's python. 
 +# The names should match with the python Zipfile compression types: 
 +# * ZIP_STORED (no compression. available everywhere) 
 +# * ZIP_DEFLATED (uses zlib, the default) 
 +# These values may be set per host via the ansible_module_compression inventory 
 +# variable 
 +#module_compression = 'ZIP_DEFLATED' 
 + 
 +# This controls the cutoff point (in bytes) on --diff for files 
 +# set to 0 for unlimited (RAM may suffer!). 
 +#max_diff_size = 1048576 
 + 
 +# This controls how ansible handles multiple --tags and --skip-tags arguments 
 +# on the CLI.  If this is True then multiple arguments are merged together.  If 
 +# it is False, then the last specified argument is used and the others are ignored. 
 +# This option will be removed in 2.8. 
 +#merge_multiple_cli_flags = True 
 + 
 +# Controls showing custom stats at the end, off by default 
 +#show_custom_stats = True 
 + 
 +# Controls which files to ignore when using a directory as inventory with 
 +# possibly multiple sources (both static and dynamic) 
 +#inventory_ignore_extensions = ~, .orig, .bak, .ini, .cfg, .retry, .pyc, .pyo 
 + 
 +# This family of modules use an alternative execution path optimized for network appliances 
 +# only update this setting if you know how this works, otherwise it can break module execution 
 +#network_group_modules=eos, nxos, ios, iosxr, junos, vyos 
 + 
 +# When enabled, this option allows lookups (via variables like {{lookup('foo')}} or when used as 
 +# a loop with `with_foo`) to return data that is not marked "unsafe". This means the data may contain 
 +# jinja2 templating language which will be run through the templating engine. 
 +# ENABLING THIS COULD BE A SECURITY RISK 
 +#allow_unsafe_lookups = False 
 + 
 +# set default errors for all plays 
 +#any_errors_fatal = False 
 + 
 +[inventory] 
 +# enable inventory plugins, default: 'host_list', 'script', 'auto', 'yaml', 'ini', 'toml' 
 +#enable_plugins = host_list, virtualbox, yaml, constructed 
 + 
 +# ignore these extensions when parsing a directory as inventory source 
 +#ignore_extensions = .pyc, .pyo, .swp, .bak, ~, .rpm, .md, .txt, ~, .orig, .ini, .cfg, .retry 
 + 
 +# ignore files matching these patterns when parsing a directory as inventory source 
 +#ignore_patterns= 
 + 
 +# If 'true' unparsed inventory sources become fatal errors, they are warnings otherwise. 
 +#unparsed_is_failed=False 
 + 
 +[privilege_escalation] 
 +#become=True 
 +#become_method=sudo 
 +#become_user=root 
 +#become_ask_pass=False 
 + 
 +[paramiko_connection] 
 + 
 +# uncomment this line to cause the paramiko connection plugin to not record new host 
 +# keys encountered.  Increases performance on new host additions.  Setting works independently of the 
 +# host key checking setting above. 
 +#record_host_keys=False 
 + 
 +# by default, Ansible requests a pseudo-terminal for commands executed under sudo. Uncomment this 
 +# line to disable this behaviour. 
 +#pty=False 
 + 
 +# paramiko will default to looking for SSH keys initially when trying to 
 +# authenticate to remote devices.  This is a problem for some network devices 
 +# that close the connection after a key failure.  Uncomment this line to 
 +# disable the Paramiko look for keys function 
 +#look_for_keys = False 
 + 
 +# When using persistent connections with Paramiko, the connection runs in a 
 +# background process.  If the host doesn't already have a valid SSH key, by 
 +# default Ansible will prompt to add the host key.  This will cause connections 
 +# running in background processes to fail.  Uncomment this line to have 
 +# Paramiko automatically add host keys. 
 +#host_key_auto_add = True 
 + 
 +[ssh_connection] 
 + 
 +# ssh arguments to use 
 +# Leaving off ControlPersist will result in poor performance, so use 
 +# paramiko on older platforms rather than removing it, -C controls compression use 
 +#ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s 
 + 
 +# The base directory for the ControlPath sockets. 
 +# This is the "%(directory)s" in the control_path option 
 +
 +# Example: 
 +# control_path_dir = /tmp/.ansible/cp 
 +#control_path_dir = ~/.ansible/cp 
 + 
 +# The path to use for the ControlPath sockets. This defaults to a hashed string of the hostname, 
 +# port and username (empty string in the config). The hash mitigates a common problem users 
 +# found with long hostnames and the conventional %(directory)s/ansible-ssh-%%h-%%p-%%r format. 
 +# In those cases, a "too long for Unix domain socket" ssh error would occur. 
 +
 +# Example: 
 +# control_path = %(directory)s/%%h-%%r 
 +#control_path = 
 + 
 +# Enabling pipelining reduces the number of SSH operations required to 
 +# execute a module on the remote server. This can result in a significant 
 +# performance improvement when enabled, however when using "sudo:" you must 
 +# first disable 'requiretty' in /etc/sudoers 
 +
 +# By default, this option is disabled to preserve compatibility with 
 +# sudoers configurations that have requiretty (the default on many distros). 
 +
 +#pipelining = False 
 + 
 +# Control the mechanism for transferring files (old) 
 +#   * smart = try sftp and then try scp [default] 
 +#   * True = use scp only 
 +#   * False = use sftp only 
 +#scp_if_ssh = smart 
 + 
 +# Control the mechanism for transferring files (new) 
 +# If set, this will override the scp_if_ssh option 
 +#   * sftp  = use sftp to transfer files 
 +#   * scp   = use scp to transfer files 
 +#   * piped = use 'dd' over SSH to transfer files 
 +#   * smart = try sftp, scp, and piped, in that order [default] 
 +#transfer_method = smart 
 + 
 +# if False, sftp will not use batch mode to transfer files. This may cause some 
 +# types of file transfer failures impossible to catch however, and should 
 +# only be disabled if your sftp version has problems with batch mode 
 +#sftp_batch_mode = False 
 + 
 +# The -tt argument is passed to ssh when pipelining is not enabled because sudo  
 +# requires a tty by default.  
 +#usetty = True 
 + 
 +# Number of times to retry an SSH connection to a host, in case of UNREACHABLE. 
 +# For each retry attempt, there is an exponential backoff, 
 +# so after the first attempt there is 1s wait, then 2s, 4s etc. up to 30s (max). 
 +#retries = 3 
 + 
 +[persistent_connection] 
 + 
 +# Configures the persistent connection timeout value in seconds.  This value is 
 +# how long the persistent connection will remain idle before it is destroyed. 
 +# If the connection doesn't receive a request before the timeout value 
 +# expires, the connection is shutdown. The default value is 30 seconds. 
 +#connect_timeout = 30 
 + 
 +# The command timeout value defines the amount of time to wait for a command 
 +# or RPC call before timing out. The value for the command timeout must 
 +# be less than the value of the persistent connection idle timeout (connect_timeout) 
 +# The default value is 30 second. 
 +#command_timeout = 30 
 + 
 +[accelerate] 
 +#accelerate_port = 5099 
 +#accelerate_timeout = 30 
 +#accelerate_connect_timeout = 5.0 
 + 
 +# The daemon timeout is measured in minutes. This time is measured 
 +# from the last activity to the accelerate daemon. 
 +#accelerate_daemon_timeout = 30 
 + 
 +# If set to yes, accelerate_multi_key will allow multiple 
 +# private keys to be uploaded to it, though each user must 
 +# have access to the system via SSH to add a new key. The default 
 +# is "no"
 +#accelerate_multi_key = yes 
 + 
 +[selinux] 
 +# file systems that require special treatment when dealing with security context 
 +# the default behaviour that copies the existing context or uses the user default 
 +# needs to be changed to use the file system dependent context. 
 +#special_context_filesystems=nfs,vboxsf,fuse,ramfs,9p,vfat 
 + 
 +# Set this to yes to allow libvirt_lxc connections to work without SELinux. 
 +#libvirt_lxc_noseclabel = yes 
 + 
 +[colors] 
 +#highlight = white 
 +#verbose = blue 
 +#warn = bright purple 
 +#error = red 
 +#debug = dark gray 
 +#deprecate = purple 
 +#skip = cyan 
 +#unreachable = red 
 +#ok = green 
 +#changed = yellow 
 +#diff_add = green 
 +#diff_remove = red 
 +#diff_lines = cyan 
 + 
 + 
 +[diff] 
 +# Always print diff when running ( same as always running with -D/--diff ) 
 +# always = no 
 + 
 +# Set how many context lines to show in diff 
 +# context = 3 
 +</file> 
 + 
 +=== hosts.yml === 
 +Im Abschnitt **[[#etc_ansible_hosts|/etc/ansible/hosts]]** hatten wir bereits einen ersten Blick auf die Inventory-Definitionsvorlage aus dem **RPM** geworfen. In der Ansible-Konfigurationsdatei **''[[#ansiblecfg|.ansible.cfg]]''** hatten wir definiert, dass unsere Hosts aus dem Produktionsumfeld in der Datei **''~/ansible/inventories/production/hosts.yml''** definiert werden. 
 + 
 +Wir befüllen nun also die entsprechende Datei mit den Daten unserer Produktions-/INstallationsumgebung. 
 +   $ vim ~/ansible/inventories/production/hosts.yml 
 +<file yml ~/ansible/inventories/production/hosts.yml>--- #YAML start syntax (optional)  
 +centos7: 
 +  hosts: 
 +    bh7:  
 +      ansible_ssh_host: bh7.dmz.nausch.org 
 +         
 +centos8: 
 +  hosts: 
 +    ansible: 
 +      ansible_ssh_host: 10.0.0.40 
 +    demo: 
 +      ansible_ssh_host: 10.0.0.190 
 +      ansible_ssh_port: 10022 
 +... #YAML ende syntax (optional)</file> 
 + 
 + 
 +Wir definieren damit folgende Hostgruppen: 
 +  * **''centos7''** mit dem Host **''bh7''**, definiert über dessen Hostname **''bh7.dmz.nausch.org''** 
 +  * **''centos8''**  
 +    * mit dem Host **''ansible''**, definiert über dessen IP-Adresse **''10.0.0.40''** und  
 +    * mit dem Host **''demo''**, definiert über dessen IP-Adresse **''10.0.0.190''** und einem anderen SSH-Port, nämlich  
 + 
 +Mit dem Befehl **''ansible''** und der Option **''--list-hosts''** können wir nun abfragen welche Hoste in den entsprechendne Hostgruppen enthalten sind. 
 +   # ansible --list-hosts centos7 
 +<code>  hosts (1): 
 +    bh7</code> 
 +   # ansible --list-hosts centos8 
 +<code>  hosts (2): 
 +    ansible 
 +    demo</code> 
 + 
 + 
 +===== erste Verbindungstests ===== 
 +Nachdem wir im Abschnitt **[[#voraussetzungssh-schluessel|Voraussetzung: SSH-Schlüssel]]** bereits unsere Schlüssel erstellt und verteilt hatten, können wir nun ein paar kleine Test fahren. Im ersten Schritt wollen wir uns das HOME-Verzeichnis unseres Ansibe-Systemusers **''ansible''** anzeigen lassen. 
  
-==== Verbindungstest ==== 
-Nachdem wir im Abschnitt **[#voraussetzungssh-schluessel|Voraussetzung: SSH-Schlüssel]]** bereits unsere Schlüssel erstellt und verteilt hatten, können wir nun ein paar kleine Test fahren.Im ersten Schritt wollen wir das HOME-Verzeichnis unseres Ansibe-Systemusers **''ansible''** uns anzeigen lassen.  
 Dies können wir mit Hilfe des Folgenden SSH-Aufrufes bewerkstelligen: Dies können wir mit Hilfe des Folgenden SSH-Aufrufes bewerkstelligen:
-   $ ssh -l ansible -i ~/.ssh/id_ed25519_ansible 10.0.0.90 "ls -alF ~/"+   $ ssh -l ansible -i ~/.ssh/id_ed25519_ansible 10.0.0.190 "ls -alF ~/"
 <code>############################################################################## <code>##############################################################################
 #                                                                            # #                                                                            #
Zeile 646: Zeile 1314:
 -rw-------. 1 ansible ansible 793 Dec 30 21:47 .viminfo</code> -rw-------. 1 ansible ansible 793 Dec 30 21:47 .viminfo</code>
  
-Im Abschnitt **[[centos:ansible:start#adhoc_-_befehle|adhoc - Befehle]]** des Kapitels **[[centos:ansible:start|Ansible - Grundlagen]]** sind wir bereits schon mal kurz in die Mögölichkeit eingestiegen, mit der wir Befehele auf einem Ziesystem ausführen können, in dem wir den Befehl **''ansible''** hierzu verwenden. +Im Abschnitt **[[centos:ansible:start#adhoc_-_befehle|adhoc - Befehle]]** des Kapitels **[[centos:ansible:start|Ansible - Grundlagen]]** sind wir bereits schon mal kurz in die Möglichkeit eingestiegen, mit der wir Befehle auf einem Zielsystem ausführen können, in dem wir den Befehl **''ansible''** hierzu verwenden. 
-   $ ansible 10.0.0.90 -u ansible --private-key /home/django/.ssh/id_ed25519_ansible -m shell -a "/usr/bin/ls -alF" +   $ ansible 10.0.0.190 -u ansible --private-key /home/django/.ssh/id_ed25519_ansible -m shell -a "/usr/bin/ls -alF" 
  
 <html><pre class="code"> <html><pre class="code">
-<font style="color: rgb(196, 160, 0)"><b>10.0.0.90 | CHANGED | rc=0 >>+<font style="color: rgb(196, 160, 0)"><b>10.0.0.190 | CHANGED | rc=0 >>
 total 20 total 20
 drwx------. 4 ansible ansible 127 Dec 30 22:26 ./ drwx------. 4 ansible ansible 127 Dec 30 22:26 ./
Zeile 663: Zeile 1331:
 </html> </html>
  
-Alternative dazu können wir natürlich auch den **Host** oder auch die definierten **Hostgruppen** aus unserer Konfigurationsdatei **''/etc/ansible/hosts''** ansprechen. In nachfolgendem Beispiel wollen wir unsere Anfrage zur Hostgruppe **''centos_8''** schicken. +Alternative dazu können wir natürlich auch den **Host** oder auch die definierten **Hostgruppen** aus unserer Inventory-Konfigurationsdatei **''~/ansible/inventories/production/hosts.yml''** ansprechen. In nachfolgendem Beispiel wollen wir unsere Anfrage zur Hostgruppe **''centos8''** schicken. 
-   $ ansible centos_8 -u ansible --private-key /home/django/.ssh/id_ed25519_ansible -m shell -a "/usr/bin/ls -alF" +   $ ansible centos8 -u ansible --private-key /home/django/.ssh/id_ed25519_ansible -m shell -a "/usr/bin/ls -alF" 
 <html><pre class="code"> <html><pre class="code">
 <font style="color: rgb(196, 160, 0)"><b>10.0.0.90 | CHANGED | rc=0 >> <font style="color: rgb(196, 160, 0)"><b>10.0.0.90 | CHANGED | rc=0 >>
Zeile 688: Zeile 1356:
     "unreachable": true     "unreachable": true
 }</font> }</font>
-<font style="color: rgb(25, 100, 5)">10.0.0.90 | SUCCESS => {+<font style="color: rgb(25, 100, 5)">10.0.0.190 | SUCCESS => {
     "ansible_facts": {     "ansible_facts": {
         "discovered_interpreter_python": "/usr/libexec/platform-python"         "discovered_interpreter_python": "/usr/libexec/platform-python"
Zeile 697: Zeile 1365:
 </html> </html>
  
-In dem gezeigten Fall ist der Host **bh7.dmz.nausch.org** aktuell nicht erreichbar und entsprechend **rot** gekennzeichnet. Der zweite **grün** markierte Host mit der IP-Adresse **10.0.0.90** hingegen ist erreichbar uns mit dem Zusatz **SUCCESS** versehen.+Im gezeigten Fall ist der Host **bh7.dmz.nausch.org** aktuell nicht erreichbar und entsprechend **rot** gekennzeichnet. Der zweite **grün** markierte Host mit der IP-Adresse **10.0.0.90** hingegen ist erreichbar uns mit dem Zusatz **SUCCESS** versehen.
  
 ===== erweiterte Konfiguration - Anpassungen ===== ===== erweiterte Konfiguration - Anpassungen =====
 ==== remote User-Anpassung via ansible.conf ==== ==== remote User-Anpassung via ansible.conf ====
-Um nun nicht bei jedem Aufruf den Remouteuser **''ansible''** und dessen Keyfile **''/home/django/.ssh/id_ed25519_ansible''** explizit angeben zu müssen, erweitern wir unser Konfigurationsdatei **''/etc/ansible/ansible.cfg''** nun ein wenig.+Um nun nicht bei jedem Aufruf den Remouteuser **''ansible''** und dessen Keyfile **''~/.ssh/id_ed25519_ansible''** explizit angeben zu müssen, erweitern wir unser Konfigurationsdatei **''~/.ansible.cfg''** nun entsprechend.
  
 Wie öffnen also unsere Konfigurationsdatei und ergänzen nachfolgende Abschnitte. Wie öffnen also unsere Konfigurationsdatei und ergänzen nachfolgende Abschnitte.
-   # vim /etc/ansible/ansible.cfg+   # vim ~/.ansible.cfg
 === remote_user === === remote_user ===
 <code bash># default user to use for playbooks if user is not specified <code bash># default user to use for playbooks if user is not specified
Zeile 718: Zeile 1386:
 #private_key_file = /path/to/file #private_key_file = /path/to/file
 # Django : 2019-12-30 # Django : 2019-12-30
-private_key_file = /home/django/.ssh/id_ed25519_ansible+private_key_file = ~/.ssh/id_ed25519_ansible
 </code> </code>
  
 === (Verbindungs-)Tests === === (Verbindungs-)Tests ===
-Nun können wir schon mal viel einfacher ein [[centos:ansible:start#adhoc_-_befehle|adhoc-Kommando]] absetzen, um z.B. den im Einsatz befindlichen CentOS-Relase abzufragen. +Nun können wir schon mal viel einfacher ein [[centos:ansible:start#adhoc_-_befehle|adhoc-Kommando]] absetzen, um z.B. den im Einsatz befindlichen CentOS-Release abzufragen. 
-   $ ansible centos_8 -m shell -a "/usr/bin/cat /etc/redhat-release"+   $ ansible centos7 -m shell -a "/usr/bin/cat /etc/redhat-release"
 <html><pre class="code"> <html><pre class="code">
 <font style="color: rgb(196, 160, 0)"><b>10.0.0.90 | CHANGED | rc=0 >> <font style="color: rgb(196, 160, 0)"><b>10.0.0.90 | CHANGED | rc=0 >>
-CentOS Linux release 8.0.1905 (Core)</font></b></pre>+CentOS Linux release 7.8.2003 (Core)</font></b></pre>
 </html> </html>
  
Zeile 732: Zeile 1400:
 Das vorgenannte Beispiel funktioniert natürlich nur, wenn wir uns in einem homogenen Systemumfeld befinden, in dem alle Zielsysteme z.B. den gleichen Schlüsseltyp verwenden. Haben wir aber Systeme, die z.B. nur RSA-Schlüssel verwenden wie z.B. CentOS 6 Systeme oder Knoten eines Freifunk Netzes, so müssen wir natürlich eine Möglichkeit schaffen, für unterschiedliche Hosts auch unterschiedliche Schlüsseldateien und/oder sogar unterschiedliche User angeben zu können.  Das vorgenannte Beispiel funktioniert natürlich nur, wenn wir uns in einem homogenen Systemumfeld befinden, in dem alle Zielsysteme z.B. den gleichen Schlüsseltyp verwenden. Haben wir aber Systeme, die z.B. nur RSA-Schlüssel verwenden wie z.B. CentOS 6 Systeme oder Knoten eines Freifunk Netzes, so müssen wir natürlich eine Möglichkeit schaffen, für unterschiedliche Hosts auch unterschiedliche Schlüsseldateien und/oder sogar unterschiedliche User angeben zu können. 
  
-Wir verändern nun also unser Konfigurationsdatei **''/etc/ansible/ansible.cfg''** entsprechend.+Wir verändern nun also unser Konfigurationsdatei **''~/.ansible.cfg''** entsprechend.
  
 Wie öffnen also unsere Konfigurationsdatei und ergänzen nachfolgende Abschnitte. Wie öffnen also unsere Konfigurationsdatei und ergänzen nachfolgende Abschnitte.
-   # vim /etc/ansible/ansible.cfg+   # vim ~/.ansible.cfg
  
 === remote_user === === remote_user ===
Zeile 756: Zeile 1424:
  
 === inventory === === inventory ===
-In der Konfigurationsdatei **''ansible.cfg''** finden wir ganz am Anfang ein paar Angaben zu default Werten. So liest Ansible von Haus aus die Datei **''/etc/ansible/hosts''** und erwartet dort die Definitionen zu den einzelnen Hosts+Zuvor hatten wir bereits die Datei **''~/ansible/inventories/production/hosts.yml''** für die Definition der Hosts (Inventory)bei der Verzeichnisanlage unserer **[[#ansibledirectory_layout|Musterinstallation]]** angelegt
- +
-Wir hinterlegen also dortdass zukünftig die Inventory-Datei **''hosts.yml''** eingelesen und ausgewertet werden soll. +
-<code>[defaults] +
- +
-# some basic default values... +
- +
-# Django : 2020-01-01  +
-# default: #inventory      = /etc/ansible/hosts +
-inventory      = /etc/ansible/hosts.yml +
-</code>+
  
 +In der Konfigurationsdatei **''~/.ansible.cfg''** finden wir ganz am Anfang ein paar Angaben zu default Werten. Wir hatten bereits **[[#ansiblecfg|zuvor]]** in der Sektion **''[defaults]''** definiert, dass unsere Inventory-/Host-Definitionen aus der Datei **''~/ansible/inventories/production/hosts.yml''**geladen werden sollen.
 === hosts.yml === === hosts.yml ===
-Unsere Hosts packen wir nun in der **[[centos:ansible:start#yaml_-_was_ist_das|YAML]]**-Notation in die **Inventory**-Konfigurationsdatei **''vim /etc/ansible/hosts.yml +Unsere Hosts packen wir nun in der **[[centos:ansible:start#yaml_-_was_ist_das|YAML]]**-Notation in die **Inventory**-Konfigurationsdatei **''vim ~/ansible/inventories/production/hosts.yml''** 
-''** +   # vim ~/ansible/inventories/production/hosts.yml
-   # vim /etc/ansible/hosts.yml+
  
-<file bash /etc/ansible/hosts.yml>--- #YAML start syntax (optional) +<file YML ~/ansible/inventories/production/hosts.yml>--- #YAML start syntax (optional) 
  
 centos8: centos8:
Zeile 875: Zeile 1533:
  
 === mit Abfrage eines Passwortes === === mit Abfrage eines Passwortes ===
-Dass eine Rechteerweiterung ohne Abfrage eines Passwortes nicht unbedingt erstrebenswert ist, wollen wir uns nun daran setzen unsere Konfiguration etwas optimieren. Aus Sicherheitsgründen wird ein user, sobald er mittels **''sudo su -''** versucht **root**-Rechte zu erlangen gebeten sein Userpasswort einzugeben. Genau diesen Mechanismus wollen wir nun auch bei unseren Ansible-Aufgaben nutzen.+Da eine Rechteerweiterung ohne Abfrage eines Passwortes nicht unbedingt erstrebenswert ist, wollen wir uns nun daran setzen unsere Konfiguration etwas optimieren. Aus Sicherheitsgründen wird ein user, sobald er mittels **''sudo su -''** versucht **root**-Rechte zu erlangen gebeten sein Userpasswort einzugeben. Genau diesen Mechanismus wollen wir nun auch bei unseren Ansible-Aufgaben nutzen.
  
 Unser Nutzer Ansible, den wir benutzen um per SSH auf die Zielsysteme zu gelangen, haben wir ja bereits in die Gruppe **''wheel''** gepackt. Unser Nutzer Ansible, den wir benutzen um per SSH auf die Zielsysteme zu gelangen, haben wir ja bereits in die Gruppe **''wheel''** gepackt.
Zeile 936: Zeile 1594:
 \\ \\ \\ \\
 <WRAP center round tip 80%> <WRAP center round tip 80%>
-Neben der Ansicherung unseres SSH-Schlüssels mittel Passphrase habne wir auch unser Zielsystem nicht unnötig aufgeweicht. Und die einmalige Eingabe eines Passworts beim Befehl **''ansible-playbook''** ist auch soweit gerechtfertigt und eine unnötige Rechteerweiterung auf den Zielsystemen ist somit **__nicht__** notwendig! :OK:+Neben der Absicherung unseres SSH-Schlüssels mittel Passphrase haben wir auch unser Zielsystem nicht unnötig aufgeweicht. Und die einmalige Eingabe eines Passworts beim Befehl **''ansible-playbook''** ist auch soweit gerechtfertigt und eine unnötige Rechteerweiterung auf den Zielsystemen ist somit **__nicht__** notwendig! :OK:
 </WRAP> </WRAP>
- 
-===== Playbook - Beispiele ===== 
-In den beiden Kapiteln **[[centos:ansible:start#playbooks|Playbooks]]** und **[[centos:ansible:start#yaml_-_was_ist_das|YAML - was ist das?]]** hatten wir uns schon eingehend mit den Hintergrundinformationen zu diesen beiden Themenblöcken beschäftigt, so dass wir uns nun direkt mit unserem ersten Playbook beschäftigen können. 
- 
-==== Verzeichnis-Struktur ==== 
-Für die weitere programmatische Arbeit legen wir uns am besten im Home-Verzeichnis unseres Admins ein Verzeichnis **''ansible''** an. Somit haben wir es später einfacher einen Überblick über unsere Administrations- und Orchestrierungsaufgaben zu  behalten. 
-   # mkdir -p ~/ansible/files 
-   # mkdir -p ~/ansible/authkeys 
- 
-Somit haben wir folgende Struktur angelehgt: 
-<code>/home/django/ansible/ 
-├── authkeys 
-└── files</code> 
- 
-==== 01: Benutzer anlegen ==== 
-Bei unserem ersten Playbook-Beispiel wollen wir mit Hilfe von Ansible einen Benutzer auf unserem Zielsystem automatisiert anlegen. 
-Bevor wir unser erstes Script schreiben, wechseln wir in unser zuvor angelegtes Zielverzeichnis: 
-   $ cd ~/ansible 
- 
-=== Script anlegen === 
-Hier legen wir nun unser erstes Script ab. 
-   $ vim 01_create-user.yml 
- 
-<file bash 01_create-user.yml>--- 
-- hosts: centos8 
-  become: true 
-  vars: 
-    sudoers: ansible 
-    createguid: '1010' 
-    createuser: 'ruben' 
-    createusername: 'Ruben Nausch' 
-    createpassword: 'M31nP4p4157d3r4113r83573!' 
- 
-  tasks: 
-    - name: Make sure we have a group 'ruben' for our new admin-user 'ruben' 
-      group: 
-        name: '{{ createuser }}' 
-        gid: '{{ createguid }}' 
-        state: present 
- 
-    - name: Add the user 'ruben' with a specific uid and a primary group of 'ruben' 
-      user: 
-        name: '{{ createuser }}' 
-        comment: '{{ createusername }}' 
-        uid: '{{ createguid }}' 
-        group: '{{ createuser }}' 
-        state: present 
- 
-    - name: Initial password generation for user 'ruben' 
-      shell: usermod -p $(echo '{{ createpassword }}' | openssl passwd -1 -stdin) {{ createuser }}</file> 
- 
-=== Script Beschreibung === 
-Die einzelnen Zeilen haben dabei folgende Funktionen und Aufgaben. 
-Zeile: 
-  * Zeile **1**: **''%%---%%''** : Start-Ziele einer jeden YAML-Datei 
-  * Zeile **2**: **''- hosts: centos8''** : Das Script soll auf allen Systemen, die in unserer **[[#remote_user-anpassung_via_inventory_hostsyml|Inventory]]**-Datei die Eigenschaft **//CentOS 8//** zugewiesen bekommen haben. 
-  * Zeile **3**: **''become: true''** : Wir benötigen eine **[[#mit_abfrage_eines_passwortes|Rechteerweiterung]]** zu **root**-Rechten, damit wir einen neuen Nutzer auf dem Zielsystem anlegen können. 
-  * Zeile **4**: **''vars:''** Wir möchten unser Script so gestalten, dass dies später für ggf. weiter Nutzer möglichst einfach zu verwenden ist. Daher verwenden wir hier Variablen. 
-  * Zeile **5**: **''sudoers: ansible''** Nutzer auf dem Zielsystem, der für die Administrationsaufgaben benutzt werden soll. 
-  * Zeile **6**: **''createguid: '1010'''** Variable die der **UID** bzw. der **GID** unseres Nutzers 'ruben' zugewiesen werden soll. 
-  * Zeile **7**: **''createuser: 'ruben'''** Variable mit dem Nutzernamen 'ruben' 
-  * Zeile **8**: **''createusername: 'Ruben Nausch'''** Variable mit dem vollen Namen des Benutzers 'ruben' 
-  * Zeile **9**: **''createpassword: 'M31nP4p4157d3r4113r8357'''** Variable mit dem initialen Klartextpasswortes der Benutzers 'ruben' 
-  * Zeile **10**: 
-  * Zeile **11**: **''tasks''** Schlüsselwort mit den nachfolgenden Aufgaben, die mit dem Playbook dann abgearbeitet werden sollen. 
-  * Zeile **12**: **''name''**: Beschreibender Text(//Make sure we have a group 'ruben' for our new admin-user 'ruben'//), der später beim Aufruf von **''ansible-playbooks''** ausgegeben werden soll. 
-  * Zeile **13 - 16**: **''group:''** Ansible Module **[[https://docs.ansible.com/ansible/latest/modules/group_module.html|group]]** welches zum Anlegen, verändern und auch Löschen von Gruppen herangezogen werden kann.  
-  * Zeile **17**: 
-  * Zeile **18**: **''name''**: Beschreibender Text(//Add the user 'ruben' with a specific uid and a primary group of 'ruben'//), der später beim Aufruf von **''ansible-playbooks''** ausgegeben werden soll. 
-  * Zeile **19 - 24**: **''user:''** Ansible Module **[[https://docs.ansible.com/ansible/latest/modules/user_module.html|user]]** welches zum Anlegen, verändern und auch Löschen von Benutzern herangezogen werden kann.  
-  * Zeile **25**: 
-  * Zeile **26**: **''name''**: Beschreibender Text(//Initial password generation for user 'ruben'//), der später beim Aufruf von **''ansible-playbooks''** ausgegeben werden soll. 
-  * Zeile **27**: **''shell:''** Ansible Module **[[https://docs.ansible.com/ansible/latest/modules/shell_module.html|shell]]** welches zum Ausführen von Shell-Befehlen auf dem Zielsystem verwendet werden kann. Hiermit setzen wir das Passwort unseres gerade angelegten Users 'ruben'. 
- 
-=== Script ausführen === 
-Nun wollen wir unser ersten Playbook ausführen, um auf dem Zielhost den gewünschten Benutzer anzulegen; hierzu rufen wir unser Script wie folgt auf: 
-   $ ansible-playbook -v 01_create-user.yml 
- 
-<html><pre class="code"> 
-<font style="color: rgb(43, 100, 164)">Using /etc/ansible/ansible.cfg as config file</font> 
-<font style="color: rgb(0, 0, 0)">BECOME password:  
- 
-PLAY [centos8] **************************************************************************************************************************************** 
- 
-TASK [Gathering Facts] ****************************************************************************************************************************************</font> 
-<font style="color: rgb(25, 100, 5)">ok: [www8.dmz.nausch.org]</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [Make sure we have a group 'ruben' for our new admin-user 'ruben'] ****************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">changed: [www8.dmz.nausch.org] => {"changed": true, "gid": 1010, "name": "ruben", "state": "present", "system": false}</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [Add the user 'ruben' with a specific uid and a primary group of 'ruben'] *********************************************************</font> 
-<font style="color: rgb(196, 160, 0)">changed: [www8.dmz.nausch.org] => {"changed": true, "comment": "Ruben Nausch", "create_home": true, "group": 1010, "home": "/home/ruben", "name": "ruben", "shell": "/bin/bash", "state": "present", "stderr": "Creating mailbox file: File exists\n", "stderr_lines": ["Creating mailbox file: File exists"], "system": false, "uid": 1010}</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [Initial password generation for user 'ruben'] ************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">changed: [www8.dmz.nausch.org] => {"changed": true, "cmd": "usermod -p $(echo 'M31nP4p4157d3r4113r83573!' | openssl passwd -1 -stdin) ruben", "delta": "0:00:00.375778", "end": "2020-01-04 20:00:16.435753", "rc": 0, "start": "2020-01-04 20:00:16.059975", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}</font></br> 
-<font style="color: rgb(0, 0, 0)">PLAY RECAP ***************************************************************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">www8.dmz.nausch.org        </font><font style="color: rgb(0, 0, 0)">: </font><font style="color: rgb(25, 100, 5)">ok=4    </font><font style="color: rgb(196, 160, 0)">changed=3    </font><font style="color: rgb(0, 0, 0)">unreachable=0    failed=0    skipped=0    rescued=0    ignored=0</font><br></pre> 
-</html> 
- 
-=== Script Ablaufbeschreibung === 
-Bei jedem Ansible-Playbook werden beim Ausführen zu aller erst die sog. Facts durch Ansible gesammelt.  
- 
-Anschließend werden die einzelnen Tasks (Aufgaben) der Reihe nach abgearbeitet. In der Zeile nach **TASK** wird dann die Beschreibung ausgegeben, die wir im YAML-Script nach dem Schlüsselwort **''name:''** angegeben hatten. 
- 
-Bei der Ausgabe werden Stati mit einer entsprechneden Farbe hinterlegt: 
-  * <html><b><font style="color: rgb(25, 100, 5)">grün</font></b></html> : Werden lediglich von Ansible Daten erhoben, oder wenn Aufgaben keine Änderungen nach sich ziehen, werden die entsprechenden Zeilen mit der Farbe <html><b><font style="color: rgb(25, 100, 5)">grün</font></b></html> dargestellt. 
-  * <html><b><font style="color: rgb(196, 160, 0)">ockergelb</font></b></html> : Werden Änderungen durch das Script verursacht erfolgt die Anzeige in der Farbe <html><b><font style="color: rgb(196, 160, 0)">ockergelb</font></b></html> angezeigt. 
-  * <html><b><font style="color: rgb(99, 0, 0)">rot</font></b></html> : Fehler werden in der Farbe <html><b><font style="color: rgb(99, 0, 0)">rot</font></b></html> dargestellt. 
- 
-Eine Zusammenfasssung für jeden Host wir am ende unter dem Kennzeichen **PLAY RECAP** nochmals zusammengefasst. 
- 
-=== Protokollierung === 
-Auf dem Zielhost werden die Aktivitäten des Ansible-Playbooks entsprechend protokolliert. 
-   # less /var/log/secure 
- 
-<code>Jan  4 19:59:51 vml000090 sshd[11916]: Postponed publickey for ansible from 10.0.0.27 port 50940 ssh2 [preauth] 
-Jan  4 19:59:51 vml000090 sshd[11916]: Accepted publickey for ansible from 10.0.0.27 port 50940 ssh2: ED25519 SHA256:jTZQUDbCqZaV648fKVBfx3L4+tBMWL+z+iUCBY3kKMQ 
-Jan  4 19:59:51 vml000090 systemd[11920]: pam_unix(systemd-user:session): session opened for user ansible by (uid=0) 
-Jan  4 19:59:51 vml000090 sshd[11916]: pam_unix(sshd:session): session opened for user ansible by (uid=0) 
-Jan  4 19:59:58 vml000090 sudo[12053]: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-lxiprplnioypvfmjjylwxvdjapibaahs ; /usr/libexec/platform-python /home/ansible/.ansible/tmp/ansible-tmp-1578164696.339052-252470145168513/AnsiballZ_setup.py 
-Jan  4 19:59:58 vml000090 sudo[12053]: pam_systemd(sudo:session): Cannot create session: Already running in a session or user slice 
-Jan  4 19:59:58 vml000090 sudo[12053]: pam_unix(sudo:session): session opened for user root by ansible(uid=0) 
-Jan  4 20:00:06 vml000090 sudo[12053]: pam_unix(sudo:session): session closed for user root 
-Jan  4 20:00:12 vml000090 sudo[12213]: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-dkoativjxmvilrtomekhdjnqzyyyuqqr ; /usr/libexec/platform-python /home/ansible/.ansible/tmp/ansible-tmp-1578164713.323935-211226042197181/AnsiballZ_group.py 
-Jan  4 20:00:12 vml000090 sudo[12213]: pam_systemd(sudo:session): Cannot create session: Already running in a session or user slice 
-Jan  4 20:00:12 vml000090 sudo[12213]: pam_unix(sudo:session): session opened for user root by ansible(uid=0) 
-Jan  4 20:00:12 vml000090 groupadd[12222]: group added to /etc/group: name=ruben, GID=1010 
-Jan  4 20:00:12 vml000090 groupadd[12222]: group added to /etc/gshadow: name=ruben 
-Jan  4 20:00:12 vml000090 groupadd[12222]: new group: name=ruben, GID=1010 
-Jan  4 20:00:12 vml000090 sudo[12213]: pam_unix(sudo:session): session closed for user root 
-Jan  4 20:00:14 vml000090 sudo[12330]: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-qrempbbpxxpgsigefdvnuctemlnerdpu ; /usr/libexec/platform-python /home/ansible/.ansible/tmp/ansible-tmp-1578164719.7395723-99484507541654/AnsiballZ_user.py 
-Jan  4 20:00:14 vml000090 sudo[12330]: pam_systemd(sudo:session): Cannot create session: Already running in a session or user slice 
-Jan  4 20:00:14 vml000090 sudo[12330]: pam_unix(sudo:session): session opened for user root by ansible(uid=0) 
-Jan  4 20:00:14 vml000090 useradd[12339]: new user: name=ruben, UID=1010, GID=1010, home=/home/ruben, shell=/bin/bash 
-Jan  4 20:00:14 vml000090 sudo[12330]: pam_unix(sudo:session): session closed for user root 
-Jan  4 20:00:15 vml000090 sudo[12448]: ansible : TTY=pts/1 ; PWD=/home/ansible ; USER=root ; COMMAND=/bin/sh -c echo BECOME-SUCCESS-guzlsexqnsjzxvmqkxlzyfmeywwkmtxf ; /usr/libexec/platform-python /home/ansible/.ansible/tmp/ansible-tmp-1578164721.6724513-118758141371611/AnsiballZ_command.py 
-Jan  4 20:00:15 vml000090 sudo[12448]: pam_systemd(sudo:session): Cannot create session: Already running in a session or user slice 
-Jan  4 20:00:15 vml000090 sudo[12448]: pam_unix(sudo:session): session opened for user root by ansible(uid=0) 
-Jan  4 20:00:16 vml000090 usermod[12455]: change user 'ruben' password 
-Jan  4 20:00:16 vml000090 sudo[12448]: pam_unix(sudo:session): session closed for user root</code> 
- 
-==== 02: sudoers anpassen ==== 
-Bei unserem zweiten Beispiel wollen wir lediglich dafür sorgen, dass die Nutzer der Gruppe **''wheel''** beim Ausführen von Befehlen, die **root**-Berechtigungen erfordern, ihr Passwort eingeben müssen. Hierzu werden wir im Verzeichnis **''/etc/sudoers.d/''** eine Datei mit dem Namen **''10_passwd_sudo_wheel''** ablegen, die nachfolgenden Inhalt aufweist. 
-<code># Allows people in group wheel to run all command 
-%wheel ALL=(ALL) ALL</code> 
- 
-=== Script anlegen === 
-Hier legen wir nun unser erstes Script ab. 
-   $ vim 02_passwd_sudo_wheel.yml 
- 
-<file bash 02_passwd_sudo_wheel.yml>--- 
-- hosts: centos8 
-  become: true 
-  vars: 
-    sudoers: ansible 
- 
-  tasks: 
-    - name: All users from groub 'wheel' are allowed sudo users 
-      copy: 
-        content: "# Allows people in group wheel to run all command\n%wheel    ALL=(ALL)       ALL\n" 
-        dest: /etc/sudoers.d/10_passwd_sudo_wheel 
-        owner: root 
-        group: root 
-        mode: "0440" 
-        validate: visudo -cf %s</file> 
- 
-=== Script Beschreibung === 
-Die einzelnen Zeilen/Blöcke haben dabei folgende Funktionen und Aufgaben. 
-Zeile: 
-  * Zeile **1 - 5**: Der bereits bekannte Block aus dem **[[#script_beschreibung|ersten Beispiel]]**, der Aussagen trifft, wo das Script laufen soll, unter welchen User-Rechten das passiern soll etc. pp.. 
-  * https://docs.ansible.com/ansible/latest/modules/copy_module.html 
-  * Zeile **6**: 
-  * Zeile **7**: **''tasks''** Schlüsselwort mit den nachfolgenden Aufgaben, die mit dem Playbook dann abgearbeitet werden sollen. 
-  * Zeile **8**: **''name''**: Beschreibender Text (//All users from groub 'wheel' are allowed sudo users//), der später beim Aufruf von **''ansible-playbooks''** ausgegeben werden soll. 
-  * Zeile **9 - 15**: **''copy:''** Ansible Module **[[https://docs.ansible.com/ansible/latest/modules/copy_module.html|copy]]** welches zum Kopieren, verändern und auch Löschen von Dateien herangezogen werden kann.  
- 
-=== Script ausführen === 
-Nun wollen wir unser ersten Playbook ausführen, um auf dem Zielhost eine Datei mit dem gewünschten Inhalt ablegen; hierzu rufen wir unser Script wie folgt auf: 
-   $ ansible-playbook -v 02_passwd_sudo_wheel.yml 
- 
-<html><pre class="code"> 
-<font style="color: rgb(43, 100, 164)">Using /etc/ansible/ansible.cfg as config file</font> 
-<font style="color: rgb(0, 0, 0)">BECOME password:  
- 
-PLAY [centos8] **************************************************************************************************************************************** 
- 
-TASK [Gathering Facts] ****************************************************************************************************************************************</font> 
-<font style="color: rgb(25, 100, 5)">ok: [www8.dmz.nausch.org]</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [All users from groub 'wheel' are allowed sudo users] *****************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">changed: [www8.dmz.nausch.org] => {"changed": true, "checksum": "b51f017f799aca0d0aef9fa29b7da87006ea5c29", "dest": "/etc/sudoers.d/10_passwd_sudo_wheel", "gid": 0, "group": "root", "md5sum": "a7c4cc84eb0dbbf844d2a8d4fbe64164", "mode": "0440", "owner": "root", "secontext": "system_u:object_r:etc_t:s0", "size": 80, "src": "/home/ansible/.ansible/tmp/ansible-tmp-1578174240.9800038-82649856412743/source", "state": "file", "uid": 0}</font><br> 
-<font style="color: rgb(0, 0, 0)">PLAY RECAP ***************************************************************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">www8.dmz.nausch.org        </font><font style="color: rgb(0, 0, 0)">: </font><font style="color: rgb(25, 100, 5)">ok=2    </font><font style="color: rgb(196, 160, 0)">changed=1    </font><font style="color: rgb(0, 0, 0)">unreachable=0    failed=0    skipped=0    rescued=0    ignored=0</font><br></pre> 
-</html> 
- 
-=== Ergebnis === 
-Auf dem Zielhost findet sich nun unsere gewünschte Datei mit dem zugehörigen Inhalt.  
-   # cat /etc/sudoers.d/10_passwd_sudo_wheel 
- 
-<file bash /etc/sudoers.d/10_passwd_sudo_wheel># Allows people in group wheel to run all command 
-%wheel    ALL=(ALL)       ALL</file> 
- 
- 
-==== 03: mehrere Benutzer anlegen und SSH-Schlüssel kopieren ==== 
-Beim drittem Playbook-Beispiel wollen wir mit Hilfe von Ansible nicht nur ein Admin-Konto sondern gleich mehrere anlegen. Dabei kopieren wir dann auch noch gleich die zugehörigen öffentlichen SSH-Schlüssel an Ort und Stelle. Zu guter Letzt stellen wir noch sicher dass der Eintrag **''%wheel    ALL=(ALL)       ALL''** gesetzt ist, hierzu binden wird das YAML-Playbook von [[#sudoers_anpassen|Beispiel 02]] ein. 
- 
-Bevor wir unser erstes Script schreiben, wechseln wir in unser zuvor angelegtes Zielverzeichnis: 
-   $ cd ~/ansible 
- 
-=== Script anlegen === 
-Nun legen wir nun unser erstes Script ab. 
-   $ vim 03_create-admins.yml 
- 
-<file bash 03_create-admins.yml>--- 
-- hosts: centos8 
-  become: true 
-  vars: 
-    sudoers: ansible 
- 
-  tasks: 
-    - name: add several users to the system 
-      user: 
-        name: "{{ item.name }}" 
-        comment: "{{ item.fullname }}" 
-        uid: "{{ item.uid }}" 
-        groups: "{{ item.groups }}" 
-        state: present 
-      with_items: 
-         - { name: bofh, fullname: "Bastard Operator from Hell", uid: 1020, groups: "wheel, users" } 
-         - { name: ruben, fullname: "Ruben Nausch", uid: 1010, groups: wheel } 
-         - { name: ansible, fullname: "Ansible Systemuser", uid: 2003, groups: wheel } 
- 
-    - name: Initial password generation for each user 
-      shell: usermod -p $(echo '{{ item.secret }}' | openssl passwd -1 -stdin) {{ item.name }} 
-      with_items: 
-         - { name: bofh, secret: "/ImTAxBwi++W2Y26195+Q72GbH73i/zQyaq12wsx" } 
-         - { name: ruben, secret: "lop5YtypT+E6qhOjpZEoAlnyiLH7HlIF1k212qyo" } 
-         - { name: ansible, secret: "X4z3AEx6WZ2+DDzvuzjx0mBERQ-o03f12qwPOSyx" } 
- 
-    - name: Set authorized keys for each user 
-      authorized_key: 
-        user: "{{ item.name }}" 
-        state: present 
-        key: "{{ lookup('file', '/home/django/ansible/authkeys/{{ item.name }}.pub') }}" 
-      with_items: 
-         - {name: bofh } 
-         - {name: ruben } 
-         - {name: ansible } 
- 
-    - include_tasks: 02_passwd_sudo_wheel.yml 
-</file> 
- 
-=== Script Beschreibung === 
-Das Script ist soweit selbsterklärend, werden doch der Reihe nach vier Tasks abgearbeitet: 
-  * **Task 1** : Drei Userkonten anlegen - die betreffenden Daten für unsere User holen wir uns dabei aus einer Liste (array) 
-  * **Task 2** : Setzen der initialen Passwörter je Userkonto 
-  * **Task 3** : Kopieren der jeweiligen öffentlichen SSH-Schlüssel der User. In diesem Konfigurationsbeispiel liegen diese im Verzeichnis **''/home/django/ansible/authkeys/''**. <code> $ ~/ansible/authkeys/ </code><code>insgesamt 12 
--rw-r--r--. 1 django django 100  4. Jan 22:12 ansible.pub 
--rw-r--r--. 1 django django 108  4. Jan 22:13 bofh.pub 
--rw-r--r--. 1 django django  98  4. Jan 22:16 ruben.pub</code> 
-  * **Task 4** : Das Anpassen der **sudoers**-Eigenschaften haben wir in diesem Konfigurationsbeispiel in eine separate YAML-Datei ausgelagert. Hierzu nutzen wir das YAML-Modul **[[https://docs.ansible.com/ansible/latest/modules/include_module.html|include]]**. Das entsprechende Include-Verzeichnis legen wir gleich mal an. <code> $ mkdir ~/ansible/includes</code> Hier legen wir uns eine YAML-Datei an, die **__nur__** den entsprechenden **''task''** beinhaltet. <code> $ vim  includes/sudoers.yml</code> <file bash includes/sudoers.yml>--- 
-    - name: All users from groub 'wheel' are allowed sudo users 
-      copy: 
-        content: "# Allows people in group wheel to run all command\n%wheel    ALL=(ALL)       ALL\n" 
-        dest: /etc/sudoers.d/10_passwd_sudo_wheel 
-        owner: root 
-        group: root 
-        mode: "0440" 
-        validate: visudo -cf %s 
-</file> 
- 
- 
-=== Script ausführen === 
-Nun wollen wir unser Playbook ausführen, um auf dem Zielhost den gewünschten Benutzer anzulegen; hierzu rufen wir unser Script wie folgt auf: 
-   $ ansible-playbook -v 03_create-admins.yml03_create-admins.yml 
- 
-<html><pre class="code"> 
-<font style="color: rgb(43, 100, 164)">Using /etc/ansible/ansible.cfg as config file</font> 
-<font style="color: rgb(0, 0, 0)">BECOME password:  
- 
-PLAY [centos8] **************************************************************************************************************************************** 
- 
-TASK [Gathering Facts] ****************************************************************************************************************************************</font> 
-<font style="color: rgb(25, 100, 5)">ok: [www8.dmz.nausch.org]</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [add several users to the system] *************************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">changed: [www8.dmz.nausch.org] => (item={'name': 'bofh', 'fullname': 'Bastard Operator from Hell', 'uid': 1020, 'groups': 'wheel, users'}) => {"ansible_loop_var": "item", "changed": true, "comment": "Bastard Operator from Hell", "create_home": true, "group": 1020, "groups": "wheel, users", "home": "/home/bofh", "item": {"fullname": "Bastard Operator from Hell", "groups": "wheel, users", "name": "bofh", "uid": 1020}, "name": "bofh", "shell": "/bin/bash", "state": "present", "system": false, "uid": 1020} 
-changed: [www8.dmz.nausch.org] => (item={'name': 'ruben', 'fullname': 'Ruben Nausch', 'uid': 1010, 'groups': 'wheel'}) => {"ansible_loop_var": "item", "changed": true, "comment": "Ruben Nausch", "create_home": true, "group": 1010, "groups": "wheel", "home": "/home/ruben", "item": {"fullname": "Ruben Nausch", "groups": "wheel", "name": "ruben", "uid": 1010}, "name": "ruben", "shell": "/bin/bash", "state": "present", "system": false, "uid": 1010}</font> 
-<font style="color: rgb(25, 100, 5)">ok: [www8.dmz.nausch.org] => (item={'name': 'ansible', 'fullname': 'Ansible Systemuser', 'uid': 500, 'groups': 'wheel'}) => {"ansible_loop_var": "item", "append": false, "changed": false, "comment": "Ansible Systemuser", "group": 500, "groups": "wheel", "home": "/home/ansible", "item": {"fullname": "Ansible Systemuser", "groups": "wheel", "name": "ansible", "uid": 500}, "move_home": false, "name": "ansible", "shell": "/bin/bash", "state": "present", "uid": 500}</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [Initial password generation for each user] ***************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">changed: [www8.dmz.nausch.org] => (item={'name': 'bofh', 'secret': '/ImTAxBwi++W2Y26195+Q72GbH73i/zQyaq12wsx'}) => {"ansible_loop_var": "item", "changed": true, "cmd": "usermod -p $(echo '/ImTAxBwi++W2Y26195+Q72GbH73i/zQyaq12wsx' | openssl passwd -1 -stdin) bofh", "delta": "0:00:00.389066", "end": "2020-01-05 17:04:51.991293", "item": {"name": "bofh", "secret": "/ImTAxBwi++W2Y26195+Q72GbH73i/zQyaq12wsx"}, "rc": 0, "start": "2020-01-05 17:04:51.602227", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []} 
-changed: [www8.dmz.nausch.org] => (item={'name': 'ruben', 'secret': 'lop5YtypT+E6qhOjpZEoAlnyiLH7HlIF1k212qyo'}) => {"ansible_loop_var": "item", "changed": true, "cmd": "usermod -p $(echo 'lop5YtypT+E6qhOjpZEoAlnyiLH7HlIF1k212qyo' | openssl passwd -1 -stdin) ruben", "delta": "0:00:00.382204", "end": "2020-01-05 17:04:53.167841", "item": {"name": "ruben", "secret": "lop5YtypT+E6qhOjpZEoAlnyiLH7HlIF1k212qyo"}, "rc": 0, "start": "2020-01-05 17:04:52.785637", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []} 
-changed: [www8.dmz.nausch.org] => (item={'name': 'ansible', 'secret': 'X4z3AEx6WZ2+DDzvuzjx0mBERQ-o03f12qwPOSyx'}) => {"ansible_loop_var": "item", "changed": true, "cmd": "usermod -p $(echo 'P1r473np4r731' | openssl passwd -1 -stdin) ansible", "delta": "0:00:00.386751", "end": "2020-01-05 17:04:54.313829", "item": {"name": "ansible", "secret": "X4z3AEx6WZ2+DDzvuzjx0mBERQ-o03f12qwPOSyx"}, "rc": 0, "start": "2020-01-05 17:04:53.927078", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [Set authorized keys for each user] ***********************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">hanged: [www8.dmz.nausch.org] => (item={'name': 'bofh'}) => {"ansible_loop_var": "item", "changed": true, "comment": null, "exclusive": false, "follow": false, "item": {"name": "bofh"}, "key": "ssh-ed25519 AAAAC3NzaC2lZDI1NTE5AAAAILxi47aZOS3tfvNFxVVqkJAfSKXjpvemB3kRZEQ5q/kf Bastard Operator from Hell", "key_options": null, "keyfile": "/home/bofh/.ssh/authorized_keys", "manage_dir": true, "path": null, "state": "present", "user": "bofh", "validate_certs": true} 
-changed: [www8.dmz.nausch.org] => (item={'name': 'ruben'}) => {"ansible_loop_var": "item", "changed": true, "comment": null, "exclusive": false, "follow": false, "item": {"name": "ruben"}, "key": "ssh-ed25519 AAAAC3TzaC2lZ60DI1NTE5AAILxi47aZOS3tfvNFxq16293SKXjp4tsB3kRZffQ5q/kf ruben@nausch.org", "key_options": null, "keyfile": "/home/ruben/.ssh/authorized_keys", "manage_dir": true, "path": null, "state": "present", "user": "ruben", "validate_certs": true}</font> 
-<font style="color: rgb(25, 100, 5)">ok: [www8.dmz.nausch.org] => (item={'name': 'ansible'}) => {"ansible_loop_var": "item", "changed": false, "comment": null, "exclusive": false, "follow": false, "item": {"name": "ansible"}, "key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILti47aZOSetfvNFxVVqkJAfSKXjyvemB3kRZEQ5q/kf Ansible Systemuser", "key_options": null, "keyfile": "/home/ansible/.ssh/authorized_keys", "manage_dir": true, "path": null, "state": "present", "user": "ansible", "validate_certs": true}</font></br> 
-<font style="color: rgb(0, 0, 0)">TASK [All users from groub 'wheel' are allowed sudo users] *****************************************************************************</font> 
-<font style="color: rgb(4, 138, 145)">included: /home/django/ansible/includes/sudoers.yml for www8.dmz.nausch.org</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [All users from groub 'wheel' are allowed sudo users] *****************************************************************************</font> 
-<font style="color: rgb(25, 100, 5)">ok: [www8.dmz.nausch.org] => {"changed": false, "checksum": "b51f017f799aca0d0aef9fa29b7da87006ea5c29", "dest": "/etc/sudoers.d/10_passwd_sudo_wheel", "gid": 0, "group": "root", "mode": "0440", "owner": "root", "path": "/etc/sudoers.d/10_passwd_sudo_wheel", "secontext": "system_u:object_r:etc_t:s0", "size": 80, "state": "file", "uid": 0}</font></br> 
-<font style="color: rgb(0, 0, 0)">PLAY RECAP ***************************************************************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">www8.dmz.nausch.org        </font><font style="color: rgb(0, 0, 0)">: </font><font style="color: rgb(25, 100, 5)">ok=6    </font><font style="color: rgb(196, 160, 0)">changed=3    </font><font style="color: rgb(0, 0, 0)">unreachable=0    failed=0    skipped=0    rescued=0    ignored=0</font><br></pre> 
-</html> 
- 
-=== Ergebnis === 
-Auf dem Zielhost findet sich nun unsere gewünschte Datei mit dem zugehörigen Inhalt.  
-   # cat /etc/sudoers.d/10_passwd_sudo_wheel 
- 
-<file bash /etc/sudoers.d/10_passwd_sudo_wheel># Allows people in group wheel to run all command 
-%wheel    ALL=(ALL)       ALL</file> 
- 
-Auch unsere drei Nutzer-/Admin-Konten sind angelet und deren öffentlicher SSH-Schlüssel in diue Datei **''.ssh/authorized_keys''** im jeweiligen Homeverzeichnis der User kopiert. 
-<code>/home/ 
-├── ansible 
-│   ├── .bash_logout 
-│   ├── .bash_profile 
-│   ├── .bashrc 
-│   ├── .ssh 
-│   │   └── authorized_keys 
-├── bofh 
-│   ├── .bash_logout 
-│   ├── .bash_profile 
-│   ├── .bashrc 
-│   └── .ssh 
-│       └── authorized_keys 
-└── ruben 
-    ├── .bash_logout 
-    ├── .bash_profile 
-    ├── .bashrc 
-    └── .ssh 
-        └── authorized_keys</code> 
- 
-==== 04: Bedingtes Kopieren ==== 
-Im vierten Beispiel wollen wir die Konfigurationsdatei **''mailserver.guru.repo''** für das Repository mailserver.guru auf alle unsere definierten CentOS-Hosts kopieren. Dabei müssen wir natürlich beachten, dass sich die Datei zwischen den Versionen **CentOS 7** und **CentOS 8** unterscheiden, wenn auch nur geringfügig! 
- 
-=== Script anlegen === 
-Das Script legen wir wie auch schon bei den anderen Beispielen zuvor im Verzeichnis **''~/ansible''** an 
-   $ vim 04_repro.yml 
- 
-<file bash 04_repro.yml>--- 
-- hosts: all 
-  become: true 
-  vars: 
-    sudoers: ansible 
- 
-  tasks: 
-    - name: Place repo-file mailserver.guru for CentOS 8 right in place 
-      copy: 
-        src: /home/django/ansible/files/CentOS8/mailserver.guru.repo  
-        dest: /etc/yum.repos.d/ 
-      when: 
-        - ansible_facts['distribution'] == "CentOS" 
-        - ansible_facts['distribution_major_version'] == "8" 
- 
-    - name: Place repo-file mailserver.guru for CentOS 7 right in place  
-      copy: 
-        src: files/CentOS7/mailserver.guru.repo  
-        dest: /etc/yum.repos.d/ 
-      when: 
-        - ansible_facts['distribution'] == "CentOS" 
-        - ansible_facts['distribution_major_version'] == "7" 
- 
-</file> 
- 
-Im Arbeitsverzeichnis für unsere Ansible hatten wir **[[#verzeichnis-struktur|zu Beginn]]** bereits ein Verzeichnis für lokale (Konfigurationsdateien) angelegt. Zur besseren Unterscheidung legen wir uns dort noch zwei Verzeichnisse an, in denen wir die Releaseversionsabhängigen Dateien vorhalten werden. 
-   $ mkdir ~/ansible/files/CentOS7 
-   $ mkdir ~/ansible/files/CentOS8 
- 
-\\ 
-Die **CentOS 7** spezifische Repo-Datei **''mailserver.guru.repo''** speichern wir nun im Verzeichnis **''~/ansible/files/CentOS7''**. 
-   $ vim ~/ansible/files/CentOS7/mailserver.guru.repo 
- 
-<file bash ~/ansible/files/CentOS7/mailserver.guru.repo># Repository mailserver.guru 
-[mailserver.guru-os] 
-name=Extra (Mailserver-)Packages for Enterprise Linux 7 - $basearch 
-baseurl=http://repo.mailserver.guru/7/os/$basearch 
-priority=5 
-enabled=1 
-gpgcheck=1 
-gpgkey=file:///etc/pki/rpm-gpg/MAILSERVER.GURU-RPM-GPG-KEY-CentOS-7 
- 
- 
-[mailserver.guru-testing] 
-name=Testing (Mailserver-)Packages for Enterprise Linux 7 - $basearch 
-baseurl=http://repo.mailserver.guru/7/testing/$basearch/ 
-#priority=5 
-enabled=0 
-gpgcheck=1 
-gpgkey=file:///etc/pki/rpm-gpg/MAILSERVER.GURU-RPM-GPG-KEY-CentOS-7</file> 
- 
-Die **CentOS 8** spezifische Repo-Datei **''mailserver.guru.repo''** speichern wir entsprechend im Verzeichnis **''~/ansible/files/CentOS8''** ab. 
-   $ vim ~/ansible/files/CentOS8/mailserver.guru.repo 
- 
-<file bash ~/ansible/files/CentOS8/mailserver.guru.repo># Repository mailserver.guru 
-[mailserver.guru-os] 
-name=Extra (Mailserver-)Packages for Enterprise Linux 8 - $basearch 
-baseurl=http://repo.mailserver.guru/8/os/$basearch 
-priority=5 
-enabled=1 
-gpgcheck=1 
-gpgkey=file:///etc/pki/rpm-gpg/MAILSERVER.GURU-RPM-GPG-KEY-CentOS-8 
- 
- 
-[mailserver.guru-testing] 
-name=Testing (Mailserver-)Packages for Enterprise Linux 8 - $basearch 
-baseurl=http://repo.mailserver.guru/8/testing/$basearch/ 
-#priority=5 
-enabled=0 
-gpgcheck=1 
-gpgkey=file:///etc/pki/rpm-gpg/MAILSERVER.GURU-RPM-GPG-KEY-CentOS-8</file> 
- 
-=== Script Beschreibung === 
-Unser Playbook, welches alle Hosts anspricht besteht im Wesentlichen aus zwei Tasks/Aufgaben. Abhängig von den beiden Ansible Facts **''distribution''** und **''distribution_major_version''** wird die Repository-Konfigurations-Datei **''mailserver.guru.repo''** auf den entsprechenden Zielhost kopiert.   
- 
-Zum Kopieren wird dann das Ansible Modul **[[https://docs.ansible.com/ansible/latest/modules/copy_module.html|copy]]** verwendet. Da wir das RPM-Paket **[[centos:ansible:start#dokumentation_rpm|ansible-doc]]** installiert hatten, könne wir auch auf der Konsole direkt Informationen zu dem Modul beziehen. Allgemeine Infos zu dem Modul erhalten wir mit folgendem Befehl: 
-   $ ansible-doc copy 
- 
-<code>> COPY    (/usr/lib/python2.7/site-packages/ansible/modules/files/copy.py) 
- 
-        The `copy' module copies a file from the local or remote machine to a location on the remote 
-        machine. Use the [fetch] module to copy files from remote locations to the local box. If you need 
-        variable interpolation in copied files, use the [template] module. For Windows targets, use the 
-        [win_copy] module instead. 
- 
-  * note: This module has a corresponding action plugin. 
- 
-OPTIONS (= is mandatory): 
- 
-- attributes 
-        Attributes the file or directory should have. To get supported flags look at the man page for 
-        `chattr' on the target system. This string should contain the attributes in the same order as the 
-        one displayed by `lsattr'. 
-        (Aliases: attr)[Default: None] 
-        version_added: 2.3 
- 
-- backup 
-        Create a backup file including the timestamp information so you can get the original file back if 
-        you somehow clobbered it incorrectly. 
-        [Default: no] 
-        type: bool 
-        version_added: 0.7 
- 
-- content 
-        When used instead of `src', sets the contents of a file directly to the specified value. For 
-        anything advanced or with formatting also look at the template module. 
-        [Default: (null)] 
-        version_added: 1.1 
- 
-- decrypt 
-        This option controls the autodecryption of source files using vault. 
-        [Default: Yes] 
-        type: bool 
-        version_added: 2.4 
- 
-= dest 
-        Remote absolute path where the file should be copied to. If `src' is a directory, this must be a 
-        directory too. If `dest' is a nonexistent path and if either `dest' ends with "/" or `src' is a 
-        directory, `dest' is created. If `src' and `dest' are files, the parent directory of `dest' isn't 
-        created: the task fails if it doesn't already exist. 
- 
- 
-- directory_mode 
-        When doing a recursive copy set the mode for the directories. If this is not set we will use the 
-        system defaults. The mode is only set on directories which are newly created, and will not affect 
-        those that already existed. 
-        [Default: (null)] 
-        version_added: 1.5 
- 
-- follow 
-        This flag indicates that filesystem links in the destination, if they exist, should be followed. 
-        [Default: no] 
-        type: bool 
-        version_added: 1.8 
- 
-- force 
-        the default is `yes', which will replace the remote file when contents are different than the 
-        source. If `no', the file will only be transferred if the destination does not exist. 
-        (Aliases: thirsty)[Default: yes] 
-        type: bool 
-        version_added: 1.1 
- 
-- group 
-        Name of the group that should own the file/directory, as would be fed to `chown'. 
-        [Default: None] 
- 
-- local_follow 
-        This flag indicates that filesystem links in the source tree, if they exist, should be followed. 
-        [Default: yes] 
-        type: bool 
-        version_added: 2.4 
- 
-- mode 
-        Mode the file or directory should be. For those used to `/usr/bin/chmod' remember that modes are 
-        actually octal numbers (like 0644). Leaving off the leading zero will likely have unexpected 
-        results. As of version 1.8, the mode may be specified as a symbolic mode (for example, `u+rwx' or 
-        `u=rw,g=r,o=r'). 
-        [Default: None] 
- 
-- owner 
-        Name of the user that should own the file/directory, as would be fed to `chown'. 
-        [Default: None] 
- 
-- remote_src 
-        If `no', it will search for `src' at originating/master machine. 
-        If `yes' it will go to the remote/target machine for the `src'. Default is `no'. 
-        Currently `remote_src' does not support recursive copying. 
-        [Default: no] 
-        type: bool 
-        version_added: 2.0 
- 
-- selevel 
-        Level part of the SELinux file context. This is the MLS/MCS attribute, sometimes known as the 
-        `range'. `_default' feature works as for `seuser'. 
-        [Default: s0] 
- 
-- serole 
-        Role part of SELinux file context, `_default' feature works as for `seuser'. 
-        [Default: None] 
- 
-- setype 
-        Type part of SELinux file context, `_default' feature works as for `seuser'. 
-        [Default: None] 
- 
-- seuser 
-        User part of SELinux file context. Will default to system policy, if applicable. If set to 
-        `_default', it will use the `user' portion of the policy if available. 
-        [Default: None] 
- 
-- src 
-        Local path to a file to copy to the remote server; can be absolute or relative. If path is a 
-        directory, it is copied recursively. In this case, if path ends with "/", only inside contents of 
-        that directory are copied to destination. Otherwise, if it does not end with "/", the directory 
-        itself with all contents is copied. This behavior is similar to Rsync. 
-        [Default: (null)] 
- 
-- unsafe_writes 
-        Normally this module uses atomic operations to prevent data corruption or inconsistent reads from 
-        the target files, sometimes systems are configured or just broken in ways that prevent this. One 
-        example are docker mounted files, they cannot be updated atomically and can only be done in an 
-        unsafe manner. 
-        This boolean option allows ansible to fall back to unsafe methods of updating files for those 
-        cases in which you do not have any other choice. Be aware that this is subject to race conditions 
-        and can lead to data corruption. 
-        [Default: False] 
-        type: bool 
-        version_added: 2.2 
- 
-- validate 
-        The validation command to run before copying into place. The path to the file to validate is 
-        passed in via '%s' which must be present as in the example below. The command is passed securely 
-        so shell features like expansion and pipes won't work. 
-        [Default: None] 
- 
- 
-NOTES: 
-      * The [copy] module recursively copy facility does not scale to lots (>hundreds) of files. 
-        For alternative, see [synchronize] module, which is a wrapper around `rsync'. 
-      * For Windows targets, use the [win_copy] module instead. 
- 
-AUTHOR: Ansible Core Team, Michael DeHaan 
-EXTENDS_DOCUMENTATION_FRAGMENT: files, validate, decrypt 
-        METADATA: 
-          status: 
-          - stableinterface 
-          supported_by: core 
-         
- 
-EXAMPLES: 
-# Example from Ansible Playbooks 
-- copy: 
-    src: /srv/myfiles/foo.conf 
-    dest: /etc/foo.conf 
-    owner: foo 
-    group: foo 
-    mode: 0644 
- 
-# The same example as above, but using a symbolic mode equivalent to 0644 
-- copy: 
-    src: /srv/myfiles/foo.conf 
-    dest: /etc/foo.conf 
-    owner: foo 
-    group: foo 
-    mode: u=rw,g=r,o=r 
- 
-# Another symbolic mode example, adding some permissions and removing others 
-- copy: 
-    src: /srv/myfiles/foo.conf 
-    dest: /etc/foo.conf 
-    owner: foo 
-    group: foo 
-    mode: u+rw,g-wx,o-rwx 
- 
-# Copy a new "ntp.conf file into place, backing up the original if it differs from the copied version 
-- copy: 
-    src: /mine/ntp.conf 
-    dest: /etc/ntp.conf 
-    owner: root 
-    group: root 
-    mode: 0644 
-    backup: yes 
- 
-# Copy a new "sudoers" file into place, after passing validation with visudo 
-- copy: 
-    src: /mine/sudoers 
-    dest: /etc/sudoers 
-    validate: /usr/sbin/visudo -cf %s 
- 
-# Copy a "sudoers" file on the remote machine for editing 
-- copy: 
-    src: /etc/sudoers 
-    dest: /etc/sudoers.edit 
-    remote_src: yes 
-    validate: /usr/sbin/visudo -cf %s 
- 
-# Create a CSV file from your complete inventory using an inline template 
-- hosts: all 
-  tasks: 
-  - copy: 
-      content: | 
-        HOSTNAME;IPADDRESS;FQDN;OSNAME;OSVERSION;PROCESSOR;ARCHITECTURE;MEMORY; 
-        {% for host in hostvars %} 
-        {%   set vars = hostvars[host|string] %} 
-        {{ vars.ansible_hostname }};{{ vars.remote_host }};{{ vars.ansible_fqdn }};{{ vars.ansible_distribution }};{{ vars.ansible_d 
-        {% endfor %} 
-      dest: /some/path/systems.csv 
-      backup: yes 
-    run_once: yes 
-    delegate_to: localhost 
- 
-RETURN VALUES: 
- 
- 
-dest: 
-    description: destination file/path 
-    returned: success 
-    type: string 
-    sample: /path/to/file.txt 
-src: 
-    description: source file used for the copy on the target machine 
-    returned: changed 
-    type: string 
-    sample: /home/httpd/.ansible/tmp/ansible-tmp-1423796390.97-147729857856000/source 
-md5sum: 
-    description: md5 checksum of the file after running copy 
-    returned: when supported 
-    type: string 
-    sample: 2a5aeecc61dc98c4d780b14b330e3282 
-checksum: 
-    description: sha1 checksum of the file after running copy 
-    returned: success 
-    type: string 
-    sample: 6e642bb8dd5c2e027bf21dd923337cbb4214f827 
-backup_file: 
-    description: name of backup file created 
-    returned: changed and if backup=yes 
-    type: string 
-    sample: /path/to/file.txt.2015-02-12@22:09~ 
-gid: 
-    description: group id of the file, after execution 
-    returned: success 
-    type: int 
-    sample: 100 
-group: 
-    description: group of the file, after execution 
-    returned: success 
-    type: string 
-    sample: httpd 
-owner: 
-    description: owner of the file, after execution 
-    returned: success 
-    type: string 
-    sample: httpd 
-uid: 
-    description: owner id of the file, after execution 
-    returned: success 
-    type: int 
-    sample: 100 
-mode: 
-    description: permissions of the target, after execution 
-    returned: success 
-    type: string 
-    sample: 0644 
-size: 
-    description: size of the target, after execution 
-    returned: success 
-    type: int 
-    sample: 1220 
-state: 
-    description: state of the target, after execution 
-    returned: success 
-    type: string 
-    sample: file 
- 
-</code> 
-Wollen wir direkt ein **snippet** für unser Playbook zum Kopieren haben, geben wir bei dem Befehl **''ansible-doc copy''** noch die Option **''-s''** an. 
-   $ ansible-doc copy -s 
-<code>- name: Copies files to remote locations 
-  copy: 
-      attributes:            # Attributes the file or directory should have. To get supported flags look at the man page for 
-                               `chattr' on the target system. This string should contain the 
-                               attributes in the same order as the one displayed by `lsattr'. 
-      backup:                # Create a backup file including the timestamp information so you can get the original file back if 
-                               you somehow clobbered it incorrectly. 
-      content:               # When used instead of `src', sets the contents of a file directly to the specified value. For 
-                               anything advanced or with formatting also look at the template 
-                               module. 
-      decrypt:               # This option controls the autodecryption of source files using vault. 
-      dest:                  # (required) Remote absolute path where the file should be copied to. If `src' is a directory, this 
-                               must be a directory too. If `dest' is a nonexistent path and if 
-                               either `dest' ends with "/" or `src' is a directory, `dest' is 
-                               created. If `src' and `dest' are files, the parent directory of 
-                               `dest' isn't created: the task fails if it doesn't already exist. 
-      directory_mode:        # When doing a recursive copy set the mode for the directories. If this is not set we will use the 
-                               system defaults. The mode is only set on directories which are newly 
-                               created, and will not affect those that already existed. 
-      follow:                # This flag indicates that filesystem links in the destination, if they exist, should be followed. 
-      force:                 # the default is `yes', which will replace the remote file when contents are different than the 
-                               source. If `no', the file will only be transferred if the destination 
-                               does not exist. 
-      group:                 # Name of the group that should own the file/directory, as would be fed to `chown'. 
-      local_follow:          # This flag indicates that filesystem links in the source tree, if they exist, should be followed. 
-      mode:                  # Mode the file or directory should be. For those used to `/usr/bin/chmod' remember that modes are 
-                               actually octal numbers (like 0644). Leaving off the leading zero will 
-                               likely have unexpected results. As of version 1.8, the mode may be 
-                               specified as a symbolic mode (for example, `u+rwx' or 
-                               `u=rw,g=r,o=r'). 
-      owner:                 # Name of the user that should own the file/directory, as would be fed to `chown'. 
-      remote_src:            # If `no', it will search for `src' at originating/master machine. If `yes' it will go to the 
-                               remote/target machine for the `src'. Default is `no'. Currently 
-                               `remote_src' does not support recursive copying. 
-      selevel:               # Level part of the SELinux file context. This is the MLS/MCS attribute, sometimes known as the 
-                               `range'. `_default' feature works as for `seuser'. 
-      serole:                # Role part of SELinux file context, `_default' feature works as for `seuser'. 
-      setype:                # Type part of SELinux file context, `_default' feature works as for `seuser'. 
-      seuser:                # User part of SELinux file context. Will default to system policy, if applicable. If set to 
-                               `_default', it will use the `user' portion of the policy if 
-                               available. 
-      src:                   # Local path to a file to copy to the remote server; can be absolute or relative. If path is a 
-                               directory, it is copied recursively. In this case, if path ends with 
-                               "/", only inside contents of that directory are copied to 
-                               destination. Otherwise, if it does not end with "/", the directory 
-                               itself with all contents is copied. This behavior is similar to 
-                               Rsync. 
-      unsafe_writes:         # Normally this module uses atomic operations to prevent data corruption or inconsistent reads from 
-                               the target files, sometimes systems are configured or just broken in 
-                               ways that prevent this. One example are docker mounted files, they 
-                               cannot be updated atomically and can only be done in an unsafe 
-                               manner. This boolean option allows ansible to fall back to unsafe 
-                               methods of updating files for those cases in which you do not have 
-                               any other choice. Be aware that this is subject to race conditions 
-                               and can lead to data corruption. 
-      validate:              # The validation command to run before copying into place. The path to the file to validate is passed 
-                               in via '%s' which must be present as in the example below. The 
-                               command is passed securely so shell features like expansion and pipes 
-                               won't work. 
-</code> 
- 
-=== Script ausführen === 
-Zum Kopieren der unterschiedlichen Dateien rufen wir nun unser Playbook wie folgt auf: 
-   $ ansible-playbook -v 04_repro.yml  
- 
-<html><pre class="code"> 
-<font style="color: rgb(43, 100, 164)">Using /etc/ansible/ansible.cfg as config file</font> 
-<font style="color: rgb(0, 0, 0)">BECOME password:  
- 
-PLAY [all] ***************************************************************************************************************************** 
- 
-TASK [Gathering Facts] *****************************************************************************************************************</font> 
-<font style="color: rgb(25, 100, 5)">ok: [www7.dmz.nausch.org] 
-ok: [www8.dmz.nausch.org]</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [Place repo-file mailserver.guru for CentOS 8 right in place] *********************************************************************</font> 
-<font style="color: rgb(4, 138, 145)">skipping: [www7.dmz.nausch.org] => {"changed": false, "skip_reason": "Conditional result was False"}</font> 
-<font style="color: rgb(196, 160, 0)">changed: [www8.dmz.nausch.org] => {"changed": true, "checksum": "e9ab494c29df71d4e869c5b0bf68caf865ef74c9", "dest": "/etc/yum.repos.d/mailserver.guru.repo", "gid": 0, "group": "root", "md5sum": "f21be9d7340e512c004747204b54a2b5", "mode": "0644", "owner": "root", "secontext": "system_u:object_r:system_conf_t:s0", "size": 614, "src": "/home/ansible/.ansible/tmp/ansible-tmp-1578253275.4362092-25566445227282/source", "state": "file", "uid": 0}</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [Place repo-file mailserver.guru for CentOS 7 right in place] *********************************************************************</font> 
-<font style="color: rgb(4, 138, 145)">skipping: [www8.dmz.nausch.org] => {"changed": false, "skip_reason": "Conditional result was False"}</font> 
-<font style="color: rgb(196, 160, 0)">changed: [www7.dmz.nausch.org] => {"changed": true, "checksum": "0fac1360785f0e02e074b4cc4f785e181f6620b9", "dest": "/etc/yum.repos.d/mailserver.guru.repo", "gid": 0, "group": "root", "md5sum": "90d8ca6369ff514b3c3dc3ddfac4ebdb", "mode": "0644", "owner": "root", "secontext": "system_u:object_r:system_conf_t:s0", "size": 614, "src": "/home/ansible/.ansible/tmp/ansible-tmp-1578253277.3541374-16074731401428/source", "state": "file", "uid": 0}</font><br> 
-<font style="color: rgb(0, 0, 0)">PLAY RECAP ***************************************************************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">www7.dmz.nausch.org        </font><font style="color: rgb(0, 0, 0)">: </font><font style="color: rgb(25, 100, 5)">ok=2    </font><font style="color: rgb(196, 160, 0)">changed=1    </font><font style="color: rgb(0, 0, 0)">unreachable=0    failed=0    <font style="color: rgb(4, 138, 145)">skipped=1</font><font style="color: rgb(0, 0, 0)">    rescued=0    ignored=0</font> 
-<font style="color: rgb(196, 160, 0)">www8.dmz.nausch.org        </font><font style="color: rgb(0, 0, 0)">: </font><font style="color: rgb(25, 100, 5)">ok=2    </font><font style="color: rgb(196, 160, 0)">changed=1    </font><font style="color: rgb(0, 0, 0)">unreachable=0    failed=0    <font style="color: rgb(4, 138, 145)">skipped=1</font><font style="color: rgb(0, 0, 0)">    rescued=0    ignored=0</font><br></pre> 
-</html> 
- 
-\\ 
-Die <html><b><font style="color: rgb(4, 138, 145)">blau</font></b></html> markierten Zeilen bzw. in der Zusammenfassung genannten <html><b><font style="color: rgb(4, 138, 145)">skipped=1</font></b></html> brauchen uns nicht beunruhigen. \\ 
-Die Ursache hierzu ist einfach erklärt: Da wir die Prüfung auf die CentOS-Version **7** und **8** auf beiden Hosts ausführen, führt natürlich immer nur zu einem Treffer, der jeweils "falsche" wird einfach übersprungen. 
- 
-=== Ergebnis === 
-Auf dem Zielhost findet sich nun unsere gewünschte Datei mit dem zugehörigen Inhalt an Ort und Stelle.  
-   # ll /etc/yum.repos.d/mailserver.guru.repo 
- 
-<code>-rw-r--r--. 1 root root 614 Jan  5 20:41 /etc/yum.repos.d/mailserver.guru.repo</code> 
- 
-==== 05: NTP-Daemon chrony installieren und konfigurieren ==== 
-In folgendem Beispiel Nummer fünf wollen wir auf unseren **CentOS 8**-Hosts den NTP-Deamon **[[https://chrony.tuxfamily.org/|chrony]]** installieren und auch entsprechend als Client konfigurieren. 
- 
-=== Script anlegen === 
-Das Script legen wir wie auch schon bei den anderen Beispielen zuvor im Verzeichnis **''~/ansible''** an 
-   $ vim 05_chrony.yml 
- 
-<file bash 05_chrony.yml>--- 
-- hosts: centos8 
-  become: true 
-  vars: 
-    sudoers: ansible 
-    config_file: /etc/chrony.conf 
-    # chronyd client config-options 
-    chrony_pool: "server time.dmz.nausch.org iburst" 
-    chrony_stratumweight: "stratumweight 0" 
-    chrony_makestep: "makestep 10 3" 
- 
-  tasks: 
-    - name: Install chrony ntp Deamon 
-      dnf:  
-      #https://docs.ansible.com/ansible/latest/modules/dnf_module.html 
-        name: chrony 
-        state: latest 
- 
-    - name: Check if /etc/chrony.orig does exists 
-      stat: 
-      #https://docs.ansible.com/ansible/latest/modules/stat_module.html 
-        path: /etc/chrony.conf.orig 
-      register: stat_result 
- 
-    - name: Make a copy of /etc/chrony.conf as /etc/chrony.conf.orig   
-      copy: 
-      #https://docs.ansible.com/ansible/latest/modules/copy_module.html 
-        remote_src: yes 
-        src: /etc/chrony.conf 
-        dest: /etc/chrony.conf.orig 
-      when: stat_result.stat.exists == False 
- 
-    - name: Copy template config-file in place 
-      template: 
-      #https://docs.ansible.com/ansible/latest/modules/template_module.html         
-        src: templates/CentOS8/chrony-client.conf.j2  
-        dest: "{{ config_file }}" 
- 
-    - name: Make sure Chrony is started up 
-      service:  
-      #https://docs.ansible.com/ansible/latest/modules/service_module.html 
-        name: chronyd  
-        state: started  
-        enabled: yes 
- 
-</file> 
- 
-Die Konfigurationsdatei unseres chrony-Daemon werden wir im Arbeitsbereich unserer ansible-Umgebung auf dem Admin-Rechner/-Server in einem eigenen Verzeichnis vorhalten. Diese Verzeichnis erstellen wir uns nun noch. 
-   $ mkdir -p ~/ansible/templates/CentOS8/ 
- 
-Ansible nutzt die **[[https://jinja.palletsprojects.com/en/2.10.x/templates/|Jinja2 Template Engine]]** zum abgleich der verwendeten Variablen in einem Playbook. Wir werden also unsere Konfigurationsdatei entsprechend präparieren und dort ablegen. Als Datei-Extension verwenden wir hier **''.j2''**, um dies optisch abzutrennen. Wir könnten auch andere Datei-Extension verwenden, da Ansible selbst nur den Inhalt bzw. die Formatierung der Variablen interprätiert. 
- 
- 
-   $ vim ~/ansible/templates/CentOS8/chrony-client.conf.j2 
- 
-<file bash ~/ansible/templates/CentOS8/chrony-client.conf.j2># Use public servers from the pool.ntp.org project. 
-# Please consider joining the pool (http://www.pool.ntp.org/join.html). 
-{{ chrony_pool }} 
- 
-# Ignore stratum in source selection 
-{{ chrony_stratumweight }} 
- 
-# Record the rate at which the system clock gains/losses time. 
-driftfile /var/lib/chrony/drift 
- 
-# Allow the system clock to be stepped in the first three updates 
-# if its offset is larger than 1 second. 
-makestep 1.0 3 
- 
-# Enable kernel synchronization of the real-time clock (RTC). 
-rtcsync 
- 
-# In first three updates step the system clock instead of slew 
-# if the adjustment is larger than 10 seconds. 
-{{ chrony_makestep }} 
- 
-# Enable hardware timestamping on all interfaces that support it. 
-#hwtimestamp * 
- 
-# Increase the minimum number of selectable sources required to adjust 
-# the system clock. 
-#minsources 2 
- 
-# Allow NTP client access from local network. 
-#allow 192.168.0.0/16 
- 
-# Serve time even if not synchronized to a time source. 
-#local stratum 10 
- 
-# Specify file containing keys for NTP authentication. 
-keyfile /etc/chrony.keys 
- 
-# Get TAI-UTC offset and leap seconds from the system tz database. 
-leapsectz right/UTC 
- 
-# Specify directory for log files. 
-logdir /var/log/chrony 
- 
-# Select which information is logged. 
-#log measurements statistics tracking 
- 
-</file> 
- 
-Die drei Konfigurationsoptionen, die wir für unsere chrony-client Konfiguration später setzen und ggf. verändern wollen haben wir hier mit einer Variable belegt: 
-  * **''%%{{%% chrony_pool %%}}%%''** : Server von dem bzw. denen wir später die Zeit beziehen wollen. 
-  * **''%%{{%% chrony_stratumweight %%}}%%''** : Ignorieren der **stratum** Bewertung, da wir hier nur einen Zielhost zur Zeit befragen 
-  * **''%%{{%% chrony_makestep %%}}%%''** : Definition wie bei den ersten Aktualisierungsschritten zu verfahren ist. 
- 
-=== Script Beschreibung === 
-Im Playbook greifen wir auf folgende Ansible-Module zurück: 
-  * **[[https://docs.ansible.com/ansible/latest/modules/dnf_module.html|dnf]]** zum Installieren des Paketes 
-  * **[[https://docs.ansible.com/ansible/latest/modules/stat_module.html|stat]]** und **[[https://docs.ansible.com/ansible/latest/modules/copy_module.html|copy]]** zum Sichern der originalen Konfigurationsdatei. 
-  * **[[https://docs.ansible.com/ansible/latest/modules/template_module.html|template]]** zur Konfiguration unseres Daemon 
-  * **[[https://docs.ansible.com/ansible/latest/modules/service_module.html|service]]** zum (automatischen) Starten (beim Systemstart des Hosts)). 
- 
-In unserem Playbook werden am Anfang den entsprechenden Variablen ihre werte zugewiesen. Im Anschluss daran werden fünft **''tasks''** definiert: 
-  - Aufgabe: Installation des chrony NTP-Daemon 
-  - Aufgabe: Überprüfen ob von der Konfigurationsdatei, die das RPM-Paket mitbrachte schon eine Sicherungskopie erstellt wurde. 
-  - Aufgabe: Sofern bei der Prüfung in Aufgabe **2** noch keine Sicherungskopie erstellt wurde, wird eine Sicherungsopie erstellt. 
-  - Aufgabe: Konfigurieren unseres chrony-Daemon 
-  - Aufgabe: Starten des chrony-Daemon und aktivieren des automatischen Starts beim Starten des Hosts 
- 
-=== Script ausführen === 
-Zum Kopieren der unterschiedlichen Dateien rufen wir nun unser Playbook wie folgt auf: 
-   $ ansible-playbook -v 05_chrony.yml  
- 
-<html><pre class="code"> 
-<font style="color: rgb(43, 100, 164)">Using /etc/ansible/ansible.cfg as config file</font> 
-<font style="color: rgb(0, 0, 0)">BECOME password:  
- 
-PLAY [centos8] ************************************************************************************************************************* 
- 
-TASK [Gathering Facts] *****************************************************************************************************************</font> 
-<font style="color: rgb(25, 100, 5)">ok: [www7.dmz.nausch.org] 
-ok: [www8.dmz.nausch.org]</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [Install chrony ntp Deamon] *******************************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">changed: [www8.dmz.nausch.org] => {"changed": true, "msg": "", "rc": 0, "results": ["Installed: chrony", "Installed: chrony-3.3-3.el8.x86_64"]}</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [Check if /etc/chrony.orig does exists] *******************************************************************************************</font> 
-<font style="color: rgb(25, 100, 5)">ok: [www8.dmz.nausch.org] => {"changed": false, "stat": {"exists": false}}</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [Make a copy of /etc/chrony.conf as /etc/chrony.conf.orig] ************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">changed: [www8.dmz.nausch.org] => {"changed": true, "checksum": "89175e7c294dedf12bd473a952014e2cefd5766d", "dest": "/etc/chrony.conf.orig", "gid": 0, "group": "root", "md5sum": "97078948a9e2c1b99ab3e38d26a3311d", "mode": "0644", "owner": "root", "secontext": "system_u:object_r:etc_t:s0", "size": 1085, "src": "/etc/chrony.conf", "state": "file", "uid": 0}</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [Copy template config-file in place] **********************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">changed: [www8.dmz.nausch.org] => {"changed": true, "checksum": "37539ecdd11393937e5596894db41a02c6121c5f", "dest": "/etc/chrony.conf", "gid": 0, "group": "root", "md5sum": "adde7eeb1766f7f83bd3fba6cc30ec23", "mode": "0644", "owner": "root", "secontext": "system_u:object_r:etc_t:s0", "size": 1265, "src": "/home/ansible/.ansible/tmp/ansible-tmp-1578323551.4891849-132640554634531/source", "state": "file", "uid": 0}</font><br> 
-<font style="color: rgb(0, 0, 0)">TASK [Make sure Chrony is started up] **************************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">changed: [www8.dmz.nausch.org] => {"changed": true, "enabled": true, "name": "chronyd", "state": "started", "status": {"ActiveEnterTimestampMonotonic": "0", "ActiveExitTimestampMonotonic": "0", "ActiveState": "inactive", "After": "sysinit.target system.slice -.mount systemd-tmpfiles-setup.service sntp.service ntpdate.service tmp.mount systemd-journald.socket ntpd.service basic.target", "AllowIsolate": "no", "AmbientCapabilities": "", "AssertResult": "no", "AssertTimestampMonotonic": "0", "Before": "multi-user.target shutdown.target", "BlockIOAccounting": "no", "BlockIOWeight": "[not set]", "CPUAccounting": "no", "CPUQuotaPerSecUSec": "infinity", "CPUSchedulingPolicy": "0", "CPUSchedulingPriority": "0", "CPUSchedulingResetOnFork": "no", "CPUShares": "[not set]", "CPUUsageNSec": "[not set]", "CPUWeight": "[not set]", "CacheDirectoryMode": "0755", "CanIsolate": "no", "CanReload": "no", "CanStart": "yes", "CanStop": "yes", "CapabilityBoundingSet": "cap_chown cap_dac_override cap_dac_read_search cap_fowner cap_fsetid cap_kill cap_setgid cap_setuid cap_setpcap cap_linux_immutable cap_net_bind_service cap_net_broadcast cap_net_admin cap_net_raw cap_ipc_lock cap_ipc_owner cap_sys_module cap_sys_rawio cap_sys_chroot cap_sys_ptrace cap_sys_pacct cap_sys_admin cap_sys_boot cap_sys_nice cap_sys_resource cap_sys_time cap_sys_tty_config cap_mknod cap_lease cap_audit_write cap_audit_control cap_setfcap cap_mac_override cap_mac_admin cap_syslog cap_wake_alarm cap_block_suspend", "CollectMode": "inactive", "ConditionResult": "no", "ConditionTimestampMonotonic": "0", "ConfigurationDirectoryMode": "0755", "Conflicts": "systemd-timesyncd.service shutdown.target ntpd.service", "ControlPID": "0", "DefaultDependencies": "yes", "Delegate": "no", "Description": "NTP client/server", "DevicePolicy": "auto", "Documentation": "man:chronyd(8) man:chrony.conf(5)", "DynamicUser": "no", "EnvironmentFiles": "/etc/sysconfig/chronyd (ignore_errors=yes)", "ExecMainCode": "0", "ExecMainExitTimestampMonotonic": "0", "ExecMainPID": "0", "ExecMainStartTimestampMonotonic": "0", "ExecMainStatus": "0", "ExecStart": "{ path=/usr/sbin/chronyd ; argv[]=/usr/sbin/chronyd $OPTIONS ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", "ExecStartPost": "{ path=/usr/libexec/chrony-helper ; argv[]=/usr/libexec/chrony-helper update-daemon ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", "FailureAction": "none", "FileDescriptorStoreMax": "0", "FragmentPath": "/usr/lib/systemd/system/chronyd.service", "GID": "[not set]", "GuessMainPID": "yes", "IOAccounting": "no", "IOSchedulingClass": "0", "IOSchedulingPriority": "0", "IOWeight": "[not set]", "IPAccounting": "no", "IPEgressBytes": "18446744073709551615", "IPEgressPackets": "18446744073709551615", "IPIngressBytes": "18446744073709551615", "IPIngressPackets": "18446744073709551615", "Id": "chronyd.service", "IgnoreOnIsolate": "no", "IgnoreSIGPIPE": "yes", "InactiveEnterTimestampMonotonic": "0", "InactiveExitTimestampMonotonic": "0", "JobRunningTimeoutUSec": "infinity", "JobTimeoutAction": "none", "JobTimeoutUSec": "infinity", "KeyringMode": "private", "KillMode": "control-group", "KillSignal": "15", "LimitAS": "infinity", "LimitASSoft": "infinity", "LimitCORE": "infinity", "LimitCORESoft": "infinity", "LimitCPU": "infinity", "LimitCPUSoft": "infinity", "LimitDATA": "infinity", "LimitDATASoft": "infinity", "LimitFSIZE": "infinity", "LimitFSIZESoft": "infinity", "LimitLOCKS": "infinity", "LimitLOCKSSoft": "infinity", "LimitMEMLOCK": "16777216", "LimitMEMLOCKSoft": "16777216", "LimitMSGQUEUE": "819200", "LimitMSGQUEUESoft": "819200", "LimitNICE": "0", "LimitNICESoft": "0", "LimitNOFILE": "4096", "LimitNOFILESoft": "1024", "LimitNPROC": "31132", "LimitNPROCSoft": "31132", "LimitRSS": "infinity", "LimitRSSSoft": "infinity", "LimitRTPRIO": "0", "LimitRTPRIOSoft": "0", "LimitRTTIME": "infinity", "LimitRTTIMESoft": "infinity", "LimitSIGPENDING": "31132", "LimitSIGPENDINGSoft": "31132", "LimitSTACK": "infinity", "LimitSTACKSoft": "8388608", "LoadState": "loaded", "LockPersonality": "no", "LogLevelMax": "-1", "LogsDirectoryMode": "0755", "MainPID": "0", "MemoryAccounting": "yes", "MemoryCurrent": "[not set]", "MemoryDenyWriteExecute": "no", "MemoryHigh": "infinity", "MemoryLimit": "infinity", "MemoryLow": "0", "MemoryMax": "infinity", "MemorySwapMax": "infinity", "MountAPIVFS": "no", "MountFlags": "", "NFileDescriptorStore": "0", "NRestarts": "0", "Names": "chronyd.service", "NeedDaemonReload": "no", "Nice": "0", "NoNewPrivileges": "no", "NonBlocking": "no", "NotifyAccess": "none", "OOMScoreAdjust": "0", "OnFailureJobMode": "replace", "PIDFile": "/var/run/chrony/chronyd.pid", "PermissionsStartOnly": "no", "Perpetual": "no", "PrivateDevices": "no", "PrivateMounts": "no", "PrivateNetwork": "no", "PrivateTmp": "yes", "PrivateUsers": "no", "ProtectControlGroups": "no", "ProtectHome": "yes", "ProtectKernelModules": "no", "ProtectKernelTunables": "no", "ProtectSystem": "full", "RefuseManualStart": "no", "RefuseManualStop": "no", "RemainAfterExit": "no", "RemoveIPC": "no", "Requires": "sysinit.target -.mount system.slice", "RequiresMountsFor": "/var/tmp", "Restart": "no", "RestartUSec": "100ms", "RestrictNamespaces": "no", "RestrictRealtime": "no", "Result": "success", "RootDirectoryStartOnly": "no", "RuntimeDirectoryMode": "0755", "RuntimeDirectoryPreserve": "no", "RuntimeMaxUSec": "infinity", "SameProcessGroup": "no", "SecureBits": "0", "SendSIGHUP": "no", "SendSIGKILL": "yes", "Slice": "system.slice", "StandardError": "inherit", "StandardInput": "null", "StandardInputData": "", "StandardOutput": "journal", "StartLimitAction": "none", "StartLimitBurst": "5", "StartLimitIntervalUSec": "10s", "StartupBlockIOWeight": "[not set]", "StartupCPUShares": "[not set]", "StartupCPUWeight": "[not set]", "StartupIOWeight": "[not set]", "StateChangeTimestampMonotonic": "0", "StateDirectoryMode": "0755", "StatusErrno": "0", "StopWhenUnneeded": "no", "SubState": "dead", "SuccessAction": "none", "SyslogFacility": "3", "SyslogLevel": "6", "SyslogLevelPrefix": "yes", "SyslogPriority": "30", "SystemCallErrorNumber": "0", "TTYReset": "no", "TTYVHangup": "no", "TTYVTDisallocate": "no", "TasksAccounting": "yes", "TasksCurrent": "[not set]", "TasksMax": "26213", "TimeoutStartUSec": "1min 30s", "TimeoutStopUSec": "1min 30s", "TimerSlackNSec": "50000", "Transient": "no", "Type": "forking", "UID": "[not set]", "UMask": "0022", "UnitFilePreset": "enabled", "UnitFileState": "enabled", "UtmpMode": "init", "WantedBy": "multi-user.target", "WatchdogTimestampMonotonic": "0", "WatchdogUSec": "0"}}</font><br> 
-<font style="color: rgb(0, 0, 0)">PLAY RECAP ***************************************************************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">www8.dmz.nausch.org        </font><font style="color: rgb(0, 0, 0)">: </font><font style="color: rgb(25, 100, 5)">ok=6    </font><font style="color: rgb(196, 160, 0)">changed=0    </font><font style="color: rgb(0, 0, 0)">unreachable=0    failed=0    skipped=0    rescued=0    ignored=0</font><br></pre> 
-</html> 
- 
-=== Ergebnis === 
-Auf dem Zielhost finden wir nun die Sicherungskopie der originalen chrony.conf aus dem RPM-Paket. 
-   # ll /etc/chrony.conf* 
-<code>-rw-r--r--. 1 root root 1265 Jan  6 16:12 /etc/chrony.conf 
--rw-r--r--. 1 root root 1085 Apr  4  2018 /etc/chrony.conf.orig</code> 
- 
-Der Dienst **chrony.conf** wurde entsprechend gestartet und läuft: 
-   # systemctl status chronyd.service 
- 
-<html><pre class="code"> 
-<font style="color: rgb(0, 255, 0)"><b>● </b></font><font style="color: rgb(0, 0, 0)">chronyd.service - NTP client/server 
-   Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled) 
-   Active: <font style="color: rgb(0, 255, 0)"><b>active (running) </b></font><font style="color: rgb(0, 0, 0)">since Mon 2020-01-06 16:12:34 CET; 47s ago 
-     Docs: man:chronyd(8) 
-           man:chrony.conf(5) 
-  Process: 10697 ExecStartPost=/usr/libexec/chrony-helper update-daemon (code=exited, status=0/SUCCESS) 
-  Process: 10693 ExecStart=/usr/sbin/chronyd $OPTIONS (code=exited, status=0/SUCCESS) 
- Main PID: 10695 (chronyd) 
-    Tasks: 1 (limit: 26213) 
-   Memory: 1.1M 
-   CGroup: /system.slice/chronyd.service 
-           └─10695 /usr/sbin/chronyd 
- 
-Jan 06 16:12:34 vml000090.dmz.nausch.org systemd[1]: Starting NTP client/server... 
-Jan 06 16:12:34 vml000090.dmz.nausch.org chronyd[10695]: chronyd version 3.3 starting (+CMDMON +NTP +REFCLOCK +RTC +PRIVDROP +SCFILTER +SIGND +ASYNCDNS +SECHASH +IPV6 +DEBUG) 
-Jan 06 16:12:34 vml000090.dmz.nausch.org chronyd[10695]: Initial frequency 10.454 ppm 
-Jan 06 16:12:34 vml000090.dmz.nausch.org chronyd[10695]: Using right/UTC timezone to obtain leap second data</font> 
-</pre></html> 
- 
-Beim Starten des Servers/Hosts wird der **chronyd**-Daemon auch automatisch gestartet. Dies können wir wie folgt überprüfen: 
-   # systemctl is-enabled chronyd.service 
- 
-  enabled 
- 
-==== 06: Ansible und Zielhosts ohne Python ==== 
-In diesem Konfigurationsbeispiel befassen wir uns nun mit einer Besonderheit in Sachen Zielhosts. Nicht immer hat man auf einem Zielsystem **[[https://python.org|Python]]** zur Verfügung, wie z.B. bei **[[https://freifunk.net/|Freifunk]]**-Hardware, die auf **[[https://gluon.readthedocs.io/en/v2019.1.x/|gluon]]** und **[[https://openwrt.org/|OpenWrt]]** basieren. Aber auch hier gibt es eine Lösungsmöglichkeit, in dem man für diesen speziellen Fall auf das Modul **[[https://docs.ansible.com/ansible/latest/modules/dnf_module.html|raw]]** zurückgreift. 
- 
-=== Script anlegen === 
-Auch dieses Beispiel-Script speichern wir im Verzeichnis **''~/ansible''** ab. 
-   $ vim 06_change_contact.yml 
- 
-<file bash 06_change_contact.yml>--- 
-- hosts: ffmuc_gluon 
-  gather_facts: False 
- 
-  tasks: 
-    - name: "Update new contact-address on own ffmuc-nodes" 
-      #https://docs.ansible.com/ansible/latest/modules/raw_module.html 
-      raw: uci set gluon-node-info.@owner[0].contact=' Django [BOfH] django@nausch.org | chat -> @django' ; uci commit 
- 
-</file> 
- 
-=== Script Beschreibung === 
-Dieses Playbook besteht nur aus einer Aufgabe, nämlich dem Ändern der Kontakt-Adresse unserer Freifunk-WLAN-Knoten, die allesamt auf **Gluon/OpenWRT** basieren. Daher macht es auch keinen Sinn, dass Ansible versuchen wird, die **Facts** der Zielhosts zu ermitteln, da dort **Python** nicht zur Verfügung steht, welches für die Ermittlung der systemdaten benötigt wird. 
- 
-Wir deaktivieren also mit **''gather_facts: False''** diese Funktion zu Beginn. 
- 
-Zum Ausführen der Kommandos auf den Freifunk-Knoten verwenden wird das Ansible Modul **[[https://docs.ansible.com/ansible/latest/modules/raw_module.html|raw]]**. Zum Ändern der **[[https://ffmuc.net/wiki/doku.php?id=knb:ssh#kontaktdaten|Kontaktdaten]]** benutzen wir die beiden Befehle: 
-  uci set gluon-node-info.@owner[0].contact='Django [BOfH] django@nausch.org | chat -> @django' 
-  uci commit 
- 
-=== Script ausführen === 
-Nun wollen wir unser ersten Playbook ausführen, um auf den Freifunk-Knoten die Kontaktdaten zu aktualisieren; hierzu rufen wir unser Script wie folgt auf: 
-   $ ansible-playbook -v 06_change_contact.yml 
- 
-<html><pre class="code"> 
-<font style="color: rgb(43, 100, 164)">Using /etc/ansible/ansible.cfg as config file</font> 
-<font style="color: rgb(0, 0, 0)">BECOME password:  
- 
-PLAY [ffmuc_gluon] ********************************************************************************************************************* 
- 
-<font style="color: rgb(0, 0, 0)">TASK [Update new contact-address on own ffmuc-nodes] ***********************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">changed: [ff_pliening_gbw_ug] => {"changed": true, "rc": 0, "stderr": "Shared connection to 2001:608:a01:102:32b5:c2ff:fe56:62b1 closed.\r\n", "stderr_lines": ["Shared connection to 2001:608:a01:102:32b5:c2ff:fe56:62b1 closed."], "stdout": "", "stdout_lines": []} 
-changed: [ff_pliening_gbw_egod] => {"changed": true, "rc": 0, "stderr": "Shared connection to 2001:608:a01:102:1ae8:29ff:fea9:22ed closed.\r\n", "stderr_lines": ["Shared connection to 2001:608:a01:102:1ae8:29ff:fea9:22ed closed."], "stdout": "", "stdout_lines": []} 
-changed: [ff_pliening_gbw_ogod] => {"changed": true, "rc": 0, "stderr": "Shared connection to 2001:608:a01:102:1ae8:29ff:fec0:aaae closed.\r\n", "stderr_lines": ["Shared connection to 2001:608:a01:102:1ae8:29ff:fec0:aaae closed."], "stdout": "", "stdout_lines": []} 
-changed: [ff_pliening_gbw_dgod] => {"changed": true, "rc": 0, "stderr": "Shared connection to 2001:608:a01:102:1ae8:29ff:fec6:c8eb closed.\r\n", "stderr_lines": ["Shared connection to 2001:608:a01:102:1ae8:29ff:fec6:c8eb closed."], "stdout": "", "stdout_lines": []} 
-changed: [ff_pliening_gbw_cpod] => {"changed": true, "rc": 0, "stderr": "Shared connection to 2001:608:a01:102:1ae8:29ff:fec6:c8dd closed.\r\n", "stderr_lines": ["Shared connection to 2001:608:a01:102:1ae8:29ff:fec6:c8dd closed."], "stdout": "", "stdout_lines": []} 
-changed: [ff_pliening_gbw_kvm_ol] => {"changed": true, "rc": 0, "stderr": "Shared connection to 2001:608:a01:102:5054:ff:fe9e:b358 closed.\r\n", "stderr_lines": ["Shared connection to 2001:608:a01:102:5054:ff:fe9e:b358 closed."], "stdout": "", "stdout_lines": []}</font><br> 
-<font style="color: rgb(0, 0, 0)">PLAY RECAP *****************************************************************************************************************************</font> 
-<font style="color: rgb(196, 160, 0)">ff_pliening_gbw_cpod       </font><font style="color: rgb(0, 0, 0)">: </font><font style="color: rgb(25, 100, 5)">ok=1    </font><font style="color: rgb(196, 160, 0)">changed=1    </font><font style="color: rgb(0, 0, 0)">unreachable=0    failed=0    skipped=0    rescued=0    ignored=0</font> 
-<font style="color: rgb(196, 160, 0)">ff_pliening_gbw_dgod       </font><font style="color: rgb(0, 0, 0)">: </font><font style="color: rgb(25, 100, 5)">ok=1    </font><font style="color: rgb(196, 160, 0)">changed=1    </font><font style="color: rgb(0, 0, 0)">unreachable=0    failed=0    skipped=0    rescued=0    ignored=0</font> 
-<font style="color: rgb(196, 160, 0)">ff_pliening_gbw_egod       </font><font style="color: rgb(0, 0, 0)">: </font><font style="color: rgb(25, 100, 5)">ok=1    </font><font style="color: rgb(196, 160, 0)">changed=1    </font><font style="color: rgb(0, 0, 0)">unreachable=0    failed=0    skipped=0    rescued=0    ignored=0</font> 
-<font style="color: rgb(196, 160, 0)">ff_pliening_gbw_kvm_ol     </font><font style="color: rgb(0, 0, 0)">: </font><font style="color: rgb(25, 100, 5)">ok=1    </font><font style="color: rgb(196, 160, 0)">changed=1    </font><font style="color: rgb(0, 0, 0)">unreachable=0    failed=0    skipped=0    rescued=0    ignored=0</font> 
-<font style="color: rgb(196, 160, 0)">ff_pliening_gbw_ogod       </font><font style="color: rgb(0, 0, 0)">: </font><font style="color: rgb(25, 100, 5)">ok=1    </font><font style="color: rgb(196, 160, 0)">changed=1    </font><font style="color: rgb(0, 0, 0)">unreachable=0    failed=0    skipped=0    rescued=0    ignored=0</font> 
-<font style="color: rgb(196, 160, 0)">ff_pliening_gbw_ug         </font><font style="color: rgb(0, 0, 0)">: </font><font style="color: rgb(25, 100, 5)">ok=1    </font><font style="color: rgb(196, 160, 0)">changed=1    </font><font style="color: rgb(0, 0, 0)">unreachable=0    failed=0    skipped=0    rescued=0    ignored=0</font> 
-<br></pre> 
-</html> 
- 
-=== Ergebnis === 
-Alle unsere eigenen definierten Freifunk Knoten haben nun auf der **[[https://map.ffmuc.net/#!/de/map/18e829a922ed|Freifunk München Karte]]** unseren aktualisierte Kontaktdaten. 
- 
  
 ====== Links ====== ====== Links ======
   * **[[centos:ansible:start|zurück zum Kapitel "Ansible - Start"]] <= **   * **[[centos:ansible:start|zurück zum Kapitel "Ansible - Start"]] <= **
-  * **=> [[centos:ansible:detail|weiter zum Kapitel "Ansible - Erweiterte Konfigurationsbeispiele]]**+  * **=> [[centos:ansible:playbooks1|weiter zum Kapitel "Ansible - Playbookbeispiele]]** 
 +  * **[[centos:ansible:start|Zurück zur "Ansible"-Übersicht]]**
   * **[[wiki:start|Zurück zu >>Projekte und Themenkapitel<<]]**   * **[[wiki:start|Zurück zu >>Projekte und Themenkapitel<<]]**
   * **[[http://dokuwiki.nausch.org/doku.php/|Zurück zur Startseite]]**   * **[[http://dokuwiki.nausch.org/doku.php/|Zurück zur Startseite]]**
  
 /* https://docs.ansible.com/ansible/latest/modules/modules_by_category.html */ /* https://docs.ansible.com/ansible/latest/modules/modules_by_category.html */