Ansible idempotent MySQL installation Playbook

je veux configurer un serveur MySQL sur AWS, en utilisant Ansible pour la gestion de la configuration. J'utilise L'AMI par défaut D'Amazon ( ami-3275ee5b), qui utilise yum pour la gestion des paquets.

quand le Playbook ci-dessous est exécuté, tout va bien. Mais quand je l'exécute pour la deuxième fois, la tâche Configure the root credentials échoue, parce que L'ancien mot de passe de MySQL ne correspond plus, puisqu'il a été mis à jour la dernière fois que j'ai lancé ce Playbook.

Ce qui rend la Playbook non-idempotent, ce que je n'aime pas. Je veux être capable d'exécuter le Playbook autant de fois que je veux.

- hosts: staging_mysql
  user: ec2-user
  sudo: yes

  tasks:
    - name: Install MySQL
      action: yum name=$item
      with_items:
        - MySQL-python
        - mysql
        - mysql-server

    - name: Start the MySQL service
      action: service name=mysqld state=started

    - name: Configure the root credentials
      action: command mysqladmin -u root -p $mysql_root_password

quelle serait la meilleure façon de résoudre cela, ce qui signifie faire le Playbook idempotent? Merci à l'avance!

40
demandé sur Mxx 2013-05-08 19:15:27

10 réponses

j'ai posté à ce sujet sur coderwall, mais je vais reproduire l'amélioration de dennisjac dans les commentaires de mon billet original.

le truc pour le faire de façon idempo est de savoir que le module mysql_user chargera un ~/.mon Dieu.fichier cnf s'il en trouve un.

je change d'abord le mot de passe, puis je copie A. mon Dieu.fichier cnf avec le mot de passe. Lorsque vous tentez de l'exécuter une deuxième fois, le module myqsl_user ansible le trouvera .mon Dieu.cnf et utiliser le nouveau le mot de passe.

- hosts: staging_mysql
  user: ec2-user
  sudo: yes

  tasks:
    - name: Install MySQL
      action: yum name={{ item }}
      with_items:
        - MySQL-python
        - mysql
        - mysql-server

    - name: Start the MySQL service
      action: service name=mysqld state=started

    # 'localhost' needs to be the last item for idempotency, see
    # http://ansible.cc/docs/modules.html#mysql-user
    - name: update mysql root password for all root accounts
      mysql_user: name=root host={{ item }} password={{ mysql_root_password }} priv=*.*:ALL,GRANT
      with_items:
        - "{{ ansible_hostname }}"
        - 127.0.0.1
        - ::1
        - localhost

    - name: copy .my.cnf file with root password credentials
      template: src=templates/root/.my.cnf dest=/root/.my.cnf owner=root mode=0600

le .mon Dieu.le modèle cnf ressemble à ceci:

[client]
user=root
password={{ mysql_root_password }}

Edit: ajout de privilèges tel que recommandé par Dhananjay Nene dans les commentaires, et modification de l'interpolation des variables pour utiliser des accolades au lieu du signe dollar.

35
répondu Lorin Hochstein 2013-12-10 17:36:37

version Ansible pour une installation MySQL sécurisée.

mysql_secure_installation.yml

- hosts: staging_mysql
  user: ec2-user
  sudo: yes

  tasks:
    - name: Install MySQL
      action: yum name={{ item }}
      with_items:
        - MySQL-python
        - mysql
        - mysql-server

    - name: Start the MySQL service
      action: service name=mysqld state=started

    # 'localhost' needs to be the last item for idempotency, see
    # http://ansible.cc/docs/modules.html#mysql-user
    - name: update mysql root password for all root accounts
      mysql_user: name=root host={{ item }} password={{ mysql_root_password }}
      with_items:
        - "{{ ansible_hostname }}"
        - 127.0.0.1
        - ::1
        - localhost

    - name: copy .my.cnf file with root password credentials
      template: src=templates/root/my.cnf.j2 dest=/root/.my.cnf owner=root mode=0600

    - name: delete anonymous MySQL server user for $server_hostname
      action: mysql_user user="" host="{{ server_hostname }}" state="absent"

    - name: delete anonymous MySQL server user for localhost
      action: mysql_user user="" state="absent"

    - name: remove the MySQL test database
      action: mysql_db db=test state=absent

templates/root / my.cnf.j2

[client]
user=root
password={{ mysql_root_password }}

Références

29
répondu Voles 2017-05-23 12:33:58

C'est une solution alternative à celle proposée par @LorinHochStein

une de mes contraintes était de m'assurer qu'aucun mot de passe ne soit stocké dans des fichiers texte en clair n'importe où sur le serveur. Ainsi .mon Dieu.le fcen n'était pas une proposition pratique

Solution:

- name: update mysql root password for all root accounts from local servers
  mysql_user: login_user=root 
              login_password={{ current_password }} 
              name=root 
              host=$item 
              password={{ new_password }} 
              priv=*.*:ALL,GRANT
  with_items:
      - $ansible_hostname
      - 127.0.0.1
      - ::1
      - localhost

Et dans le fichier vars

current_password: foobar
new_password: "{{ current_password }}"

si vous ne changez pas le mot de passe mysql, lancez un playbook accessible en ligne de commande comme d'habitude.

en changeant le mot de passe mysql, ajoutez le suivez la ligne de commande. Le spécifier sur la ligne de commande permet au paramètre défini sur la ligne de commande d'avoir la priorité sur celui qui est par défaut dans le fichier vars.

$ ansible-playbook ........ --extra-vars "new_password=buzzz"

après avoir exécuté la commande, changez le fichier vars comme suit

current_password=buzzz
new_password={{ current_password }}
5
répondu Dhananjay Nene 2014-03-03 14:22:41

en ajoutant aux réponses précédentes, Je ne voulais pas d'étape manuelle avant d'exécuter la commande, c'est à dire que je veux lancer un nouveau serveur et juste exécuter le playbook sans avoir à modifier manuellement le mot de passe racine pour la première fois. Je ne crois pas que {{ mysql_password} fonctionne la première fois, quand le mot de passe root est nul, parce que mysql_password doit encore être défini quelque part (à moins que vous ne vouliez l'annuler avec-e).

alors j'ai ajouté une règle pour faire ça, qui est ignorée si elle échoue. C'est dans plus, et apparaît avant, les autres commandes ici.

- name: Change root user password on first run
  mysql_user: login_user=root
              login_password=''
              name=root
              password={{ mysql_root_password }}
              priv=*.*:ALL,GRANT
              host={{ item }}
      with_items:
        - $ansible_hostname
        - 127.0.0.1
        - ::1
        - localhost
      ignore_errors: true
5
répondu mahemoff 2014-03-14 18:31:14

Pour ansible 1.3+ :

- name: ensure mysql local root password is zwx123
  mysql_user: check_implicit_admin=True login_user=root login_password="zwx123" name=root password="zwx123" state=present
4
répondu anneb 2014-08-29 22:48:13

Eh bien, c'est venu un peu compliqué. J'ai passé une journée entière sur ce et est venu avec la solution indiquée ci-dessous. Le point clé est la façon dont Ansible installe MySQL server. À partir de la documentation de mysql_user module (dernière remarque de la page):

MySQL server installs with default login_user of ‘root’ and no password. To secure this user as part of an idempotent playbook, you must create at least two tasks: the first must change the root user’s password, without providing any login_user/login_password details. The second must drop a ~/.my.cnf file containing the new root credentials. Subsequent runs of the playbook will then succeed by reading the new credentials from the file.

ce problème avec le mot de passe vide ou nul était un gros surprise.

Rôle:

---

- name: Install MySQL packages
  sudo: yes
  yum: name={{ item }} state=present
  with_items:
    - mysql
    - mysql-server
    - MySQL-python


- name: Start MySQL service
  sudo: yes
  service: name=mysqld state=started enabled=true


- name: Update MySQL root password for root account
  sudo: yes
  mysql_user: name=root password={{ db_root_password }} priv=*.*:ALL,GRANT


- name: Create .my.cnf file with root password credentials
  sudo: yes
  template: src=.my.cnf.j2 dest=/root/.my.cnf owner=root group=root mode=0600
  notify:
  - restart mysql


- name: Create a database
  sudo: yes
  mysql_db: name={{ db_name }}
            collation=utf8_general_ci
            encoding=utf8
            state=present


- name: Create a database user
  sudo: yes
  mysql_user: name={{ db_user }}
              password={{ db_user_password }}
              priv="{{ db_name }}.*:ALL"
              host=localhost
              state=present

Gestionnaire:

---

- name: restart mysql
  service: name=mysqld state=restarted

.mon Dieu.cnf.j2:

[client]
user=root
password={{ db_root_password }}
3
répondu oblalex 2014-09-23 16:45:00

La suite de Travailler (Insérer mon.cnf entre 2 mysql_user appels)


- name: 'Install MySQL'
    yum: name={{ item }} state=present
    with_items:
    - MySQL-python
    - mysql
    - mysql-server
    notify:
     - restart-mysql
- name: 'Start Mysql Service'
  action: service name=mysqld state=started enabled=yes
- name: 'Update Mysql Root Password'
  mysql_user: name=root host=localhost password={{ mysql_root_password }} state=present
- name: 'Copy Conf file with root password credentials'
  template: src=../templates/my.cnf.j2 dest=/root/.my.cnf owner=root mode=0600
- name: 'Update Rest-Mysql Root Password'
  mysql_user: name=root host={{ item }} password={{ mysql_root_password }} state=present
    with_items:
    - "{{ ansible_hostname }}"
    - "{{ ansible_eth0.ipv4.address }}"
    - 127.0.0.1
    - ::1
- name: 'Delete anonymous MySQL server user from server'
  mysql_user: name="" host={{ ansible_hostname }} state="absent"
1
répondu smuniyappa 2014-01-29 23:58:07

je sais que c'est une vieille question, mais je partage mon manuel de travail pour ceux qui la cherchent:

mysql.yml

---
 - name: Install the MySQL packages
   apt: name={{ item }} state=installed update_cache=yes
   with_items:
     - mysql-server-5.6
     - mysql-client-5.6
     - python-mysqldb
     - libmysqlclient-dev

 - name: Copy the configuration file (my.cnf)
   template: src=my.cnf.j2 dest=/etc/mysql/my.cnf
   notify:
     - Restart MySQL

 - name: Update MySQL root password for all root accounts
   mysql_user: name=root host={{ item }} password={{ mysql_root_pass }} state=present
   with_items:
     - "{{ ansible_hostname }}"
     - 127.0.0.1
     - ::1
     - localhost

 - name: Copy the root credentials as .my.cnf file
   template: src=root.cnf.j2 dest=~/.my.cnf mode=0600

 - name: Ensure Anonymous user(s) are not in the database
   mysql_user: name='' host={{ item }} state=absent
   with_items:
     - localhost
     - "{{ ansible_hostname }}"

 - name: Remove the test database
   mysql_db: name=test state=absent
   notify:
     - Restart MySQL

vars.yml

---
 mysql_port: 3306 #Default is 3306, please change it if you are using non-standard
 mysql_bind_address: "127.0.0.1" #Change it to "0.0.0.0",if you want to listen everywhere
 mysql_root_pass: mypassword #MySQL Root Password

mon.cnf.j2

[client]
port            = 3306
socket          = /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket          = /var/run/mysqld/mysqld.sock
nice            = 0

[mysqld]
user            = mysql
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
port            = {{ mysql_port }}
basedir         = /usr
datadir         = /var/lib/mysql
tmpdir          = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
bind-address            = {{ mysql_bind_address }}
key_buffer              = 16M
max_allowed_packet      = 64M
thread_stack            = 192K
thread_cache_size       = 8
myisam-recover         = BACKUP
query_cache_limit       = 1M
query_cache_size        = 16M
log_error = /var/log/mysql/error.log
expire_logs_days        = 10
max_binlog_size         = 100M

[mysqldump]
quick
quote-names
max_allowed_packet      = 64M

[mysql]

[isamchk]
key_buffer              = 16M

!includedir /etc/mysql/conf.d/

racine.cnf.j2

[client]
user=root
password={{ mysql_root_pass }}
0
répondu Arbab Nazar 2015-05-26 01:19:02

il est important de démarrer/redémarrer le serveur mysql avant de définir le mot de passe racine. Aussi, j'avais tout essayé affiché jusqu'à ce post [date] et a découvert qu'il est impératif de passer login_password et login_user.

(i.e.) N'importe quel jeu après avoir mis le mysql_useruser:root et password= {{ SOMEPASSWORD }}, vous devez vous connecter en utilisant login_password et login_user pour tous les jouer.

Remarque:with_items ci-dessous est basé sur ce que les hôtes par défaut Ansible &/ MariaDB créé.

exemple de sécurisation D'un serveur MariaDB:

---
# 'secure_mariadb.yml'

- name: 'Ensure MariaDB server is started and enabled on boot'
  service: name={{ mariadb_service_name }} state=started enabled=yes

# localhost needs to be the last item for idempotency, see
# http://ansible.cc/docs/modules.html#mysql-user
- name: 'Update Mysql Root Password'
  mysql_user: name=root
              host={{ item }}
              password={{ root_db_password }}
              priv=*.*:ALL,GRANT
              state=present
  with_items:
    - 127.0.0.1
    - ::1
    - instance-1 # Created by MariaDB to prevent conflicts between port and sockets if multi-instances running on the same computer.
    - localhost

- name: 'Create MariaDB main configuration file'
  template: >
    src=my.cnf.j2
    dest=/etc/mysql/my.cnf
    owner=root
    group=root
    mode=0600

- name: 'Ensure anonymous users are not in the database'
  mysql_user: login_user=root 
              login_password={{ root_db_password }}
              name=''
              host={{ item }}
              state=absent
  with_items:
    - 127.0.0.1
    - localhost

- name: 'Remove the test database'
  mysql_db: login_user=root 
            login_password={{ root_db_password }}
            name=test
            state=absent

- name: 'Reload privilege tables'
  command: 'mysql -ne "{{ item }}"'
  with_items:
    - FLUSH PRIVILEGES
  changed_when: False

- name: 'Ensure MariaDB server is started and enabled on boot'
  service: name={{ mariadb_service_name }} state=started enabled=yes


# 'End Of File'
0
répondu CelticParser 2015-06-24 22:05:07

j'ajoute mon propre point de vue sur les différentes approches (centos 7).

la variable mysql_root_password doit être stockée dans une voûte possible (meilleure) ou transmise sur la ligne de commande (pire)

- name: "Ensure mariadb packages are installed"
  yum: name={{ item }} state="present"
  with_items:
    - mariadb
    - mariadb-server

- name: "Ensure mariadb is running and configured to start at boot"
  service: name=mariadb state=started enabled=yes

# idempotently ensure secure mariadb installation --
# - attempts to connect as root user with no password and then set the root@ mysql password for each mysql root user mode.
# - ignore_errors is true because this task will always fail on subsequent runs (as the root user password has been changed from "")
- name: Change root user password on first run, this will only succeed (and only needs to succeed) on first playbook run
  mysql_user: login_user=root
              login_password=''
              name=root
              password={{ mysql_root_password }}
              priv=*.*:ALL,GRANT
              host={{ item }}
  with_items:
    - "{{ ansible_hostname }}"
    - 127.0.0.1
    - ::1
    - localhost
  ignore_errors: true

- name: Ensure the anonymous mysql user ""@{{ansible_hostname}} is deleted
  action: mysql_user user="" host="{{ ansible_hostname }}" state="absent" login_user=root login_password={{ mysql_root_password }}

- name: Ensure the anonymous mysql user ""@localhost is deleted
  action: mysql_user user="" state="absent" login_user=root login_password={{ sts_ad_password }}

- name: Ensure the mysql test database is deleted
  action: mysql_db db=test state=absent login_user=root login_password={{ mysql_root_password }}
0
répondu Ben 2015-09-04 18:27:36