Ansible idempotent MySQL installation Playbook

后端 未结 11 1380
后悔当初
后悔当初 2021-01-30 04:29

I want to setup a MySQL server on AWS, using Ansible for the configuration management. I am using the default AMI from Amazon (ami-3275ee5b), which uses yum

相关标签:
11条回答
  • 2021-01-30 04:32

    Ansible version for a secure MySQL installation.

    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 }}
    

    References

    • The original answer by Lorin Hochstein
    • https://github.com/gaspaio/ansible-devbox/blob/master/roles/mysql/tasks/server.yml
    0 讨论(0)
  • 2021-01-30 04:37

    I know this is an old question, but I am sharing my working playbook for those, who are looking for it:

    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
    

    my.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/
    

    root.cnf.j2

    [client]
    user=root
    password={{ mysql_root_pass }}
    
    0 讨论(0)
  • 2021-01-30 04:42

    I posted about this on coderwall, but I'll reproduce dennisjac's improvement in the comments of my original post.

    The trick to doing it idempotently is knowing that the mysql_user module will load a ~/.my.cnf file if it finds one.

    I first change the password, then copy a .my.cnf file with the password credentials. When you try to run it a second time, the myqsl_user ansible module will find the .my.cnf and use the new password.

    - 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
    

    The .my.cnf template looks like this:

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

    Edit: Added privileges as recommended by Dhananjay Nene in the comments, and changed variable interpolation to use braces instead of dollar sign.

    0 讨论(0)
  • 2021-01-30 04:43

    It is important to start/re-start the mysql server prior to setting the root password. Also, I had tried everything posted up to this post [date] and discovered it is imperative to pass login_password and login_user.

    (i.e.) Any Plays after setting the mysql_user user:root and password= {{ SOMEPASSWORD }}, you must connect using login_password and login_user for any subsequent play.

    Note: The with_items below is based on what Ansible &/ MariaDB default hosts created.

    Example for Securing a MariaDB Server:

    ---
    # '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 讨论(0)
  • 2021-01-30 04:43

    I'm adding my own take on the various approaches (centos 7).

    The variable mysql_root_password should be stored in an ansible-vault (better) or passed on the command-line (worse)

    - 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 讨论(0)
  • 2021-01-30 04:45

    Well, this came a bit complicated. I've spent a whole day on this and came up with the solution listed below. The key point is how Ansible installs MySQL server. From the docs of mysql_user module (last note on 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.
    

    That issue with blank or null password was a big surprise.

    Role:

    ---
    
    - 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
    

    Handler:

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

    .my.cnf.j2:

    [client]
    user=root
    password={{ db_root_password }}
    
    0 讨论(0)
提交回复
热议问题