I've recently upgraded my vagrant from ubuntu/trusty-64 to bento/ubuntu-16.04. With that MySQL was updated to 5.7. I've made several updates to my playbook, but I keep getting stuck when setting the root user's password.
In the past (before 5.7) the following was sufficient:
- name: MySQL | Set the root password.
mysql_user:
name=root
host=localhost
password={{ mysql_root_password }}
become: true
In my playbook this is tested by attempting to delete an anonymous user.
- name: MySQL | Delete anonymous MySQL server user for {{ server_hostname }}
mysql_user:
name=""
host="{{ server_hostname }}"
state="absent"
login_user=root
login_password={{ mysql_root_password }}
However, now my playbook fails at this step, returning:
"Access denied for user 'root'#'localhost'"
TASK [mysql : MySQL | Delete anonymous MySQL server user for vagrant] **********
task path: /Users/jonrobinson/vagrant/survey/playbooks/roles/mysql/tasks/mysql.yml:51
fatal: [vagrant]: FAILED! => {"changed": false, "failed": true, "msg": "unable to connect to database, check login_user and login_password are correct or /home/vagrant/.my.cnf has the credentials. Exception message: (1698, \"Access denied for user 'root'#'localhost'\")"}
I've tried several things:
Setting the password blank for root user mysql_root_password=""
Attempting to delete the root user then recreate it with Ansible. I get same error probably because it's trying to act at the root user.
Manually updating the root password in mysql. - This also doesn't appear to work (password isn't recognized) unless I delete the root user and recreate it with all the permissions. Just updating the root user password appears to have no change.
My Full MySQL YAML:
---
- name: MySQL | install mysql packages
apt: pkg={{ item }} state=installed
become: true
with_items:
- mysql-client
- mysql-common
- mysql-server
- python-mysqldb
- name: MySQL | create MySQL configuration file
template:
src=my.cnf.j2
dest=/etc/mysql/my.cnf
backup=yes
owner=root
group=root
mode=0644
become: true
- name: MySQL | create MySQLD configuration file
template:
src=mysqld.cnf.j2
dest=/etc/mysql/conf.d/mysqld.cnf
backup=yes
owner=root
group=root
mode=0644
become: true
- name: MySQL | restart mysql
service: name=mysql state=restarted
become: true
- name: MySQL | Set the root password.
mysql_user:
name=root
host=localhost
password={{ mysql_root_password }}
become: true
- name: MySQL | Config for easy access as root user
template: src=mysql_root.my.cnf.j2 dest=/root/.my.cnf
become: true
- name: MySQL | Config for easy access as root user
template: src=mysql_root.my.cnf.j2 dest={{ home_dir }}/.my.cnf
when: "'{{ user }}' != 'root'"
- name: MySQL | Delete anonymous MySQL server user for {{ server_hostname }}
mysql_user: name="" host="{{ server_hostname }}" state="absent" login_user=root login_password={{ mysql_root_password }}
- name: MySQL | Delete anonymous MySQL server user for localhost
mysql_user: name="" state="absent" host=localhost login_user=root login_password={{ mysql_root_password }}
- name: MySQL | Secure the MySQL root user for IPV6 localhost (::1)
mysql_user: name="root" password="{{ mysql_root_password }}" host="::1" login_user=root login_password={{ mysql_root_password }}
- name: MySQL | Secure the MySQL root user for IPV4 localhost (127.0.0.1)
mysql_user: name="root" password="{{ mysql_root_password }}" host="127.0.0.1" login_user=root login_password={{ mysql_root_password }}
- name: MySQL | Secure the MySQL root user for localhost domain (localhost)
mysql_user: name="root" password="{{ mysql_root_password }}" host="localhost" login_user=root login_password={{ mysql_root_password }}
- name: MySQL | Secure the MySQL root user for {{ server_hostname }} domain
mysql_user: name="root" password="{{ mysql_root_password }}" host="{{ server_hostname }}" login_user=root login_password={{ mysql_root_password }}
- name: MySQL | Remove the MySQL test database
mysql_db: db=test state=absent login_user=root login_password={{ mysql_root_password }}
- name: MySQL | create application database user
mysql_user: name={{ dbuser }} password={{ dbpass }} priv=*.*:ALL host='%' state=present login_password={{ mysql_root_password }} login_user=root
- name: MySQL | restart mysql
service: name=mysql state=restarted
become: true
I was able to figure it out. The gist of the problem had to do with mysql 5.7 using auth_socket for the root user when no password is provided. See the following: "That plugin doesn’t care and doesn’t need a password. It just checks if the user is connecting using a UNIX socket and then compares the username. "
When this is the case you cannot update the password using:
SET PASSWORD FOR 'root'#'localhost' = PASSWORD('test');
And instead must use:
ALTER USER 'root'#'localhost' IDENTIFIED WITH mysql_native_password='test';
Solution 1: However, Ansible, as of version 2.0.2 didn't account for this. I was able to get around this by setting the password before MySql is installed
- name: Specify MySQL root password before installing
debconf: name='mysql-server' question='mysql-server/root_password' value='{{mysql_root_password | quote}}' vtype='password'
become: true
- name: Confirm MySQL root password before installing
debconf: name='mysql-server' question='mysql-server/root_password_again' value='{{mysql_root_password | quote}}' vtype='password'
become: true
- name: MySQL | install mysql packages
apt: pkg={{ item }} state=installed
become: true
with_items:
- mysql-client
- mysql-common
- mysql-server
- python-mysqldb
...
However, this has also since been addressed by Ansible
Solution 2: The easiest solution is just to upgrade Ansible to 2.2.1
From what I understand, in MySQL, changing the root password needs to be done for localhost, the server's hostname and 127.0.0.1 and also needs full privileges. Something along the these lines may help (Note: I've only tested this on MariaDB, and not MySQL):
tasks:
- name: Set a new root password
mysql_user: check_implicit_admin=yes
login_user=root
login_password={{ mysql_root_password }}
user=root
password={{ NEW_mysql_root_password }}
host={{ item }}
priv='*.*:ALL,GRANT'
with_items:
- localhost
- 127.0.0.1
- {{ server_hostname }}
notify:
- restart_mariadb
handlers:
- name: restart_mariadb
service: name=mariadb
state=restarted
Related
I am using Ansible to replicate a given shopware installation guide with an Ansible project. Therefore I need to create a database and edit privileges.
I added my credentials to local my.cnf file and copy it to the server via
- name: Create Database
become: yes
community.mysql.mysql_db:
name: shopware
state: present
config_file: /etc/mysql/my.cnf
[client-server]
# Import all .cnf files from configuration directory
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mariadb.conf.d/
[client]
user="root"
password=""
The next step
- name: Create Database
become: yes
community.mysql.mysql_db:
name: shopware
state: present
config_file: /etc/mysql/my.cnf
- name: Create user with password, all database privileges and 'WITH GRANT OPTION' shopware database
become: yes
community.mysql.mysql_user:
state: present
name: "{{shopware_db_username}}"
password: "{{shopware_db_pass}}"
priv:
'shopware.*': 'ALL,GRANT'
is throwing the error:
TASK [Create Database] ******************************************************************************************************************************************************************************************************
fatal: [swserver]: FAILED! => {"changed": false, "msg": "unable to connect to database, check login_user and login_password are correct or /etc/mysql/my.cnf has the credentials. Exception message: (1698, \"Access denied for user 'root'#'localhost'\")"}
Additional info
login via mysql -u root -p with empty password does not work
Server
OS: Ubuntu 20.04.3 LTS x86_64
Kernel: 5.4.0-94-generic
Host
OS: Ubuntu 20.04.3 LTS on Windows 10 x86_64
Ansible 2.9.6
Kernel: 5.10.16.3-microsoft-standard-WSL2
Source:
https://git.rosibert.de/rpolito/ansible-shopware
Since I had a similar question in the past I like to share my solution approach here.
After MySQL/MariaDB server installation
- name: Install from EPEL-7 and/or IUS-7 repositories
yum:
name: ['mariadb103-server', 'mariadb103', 'python2-PyMySQL']
state: latest
and copying the config to /etc/my.cnf.d/my.cnf
[server]
[mysqld]
datadir=/var/data
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mariadb/mariadb.log
pid-file=/run/mariadb/mariadb.pid
it was possible to start the service
- name: Make sure service is started and enabled
systemd:
name: mariadb
state: started
enabled: yes
and connect to via socket to perform the next steps
# mysql_secure_installation
- name: Delete all anonymous SQL user accounts
mysql_user:
user: ""
host_all: yes
state: absent
- name: Remove the SQL test database
mysql_db:
db: "test"
state: absent
- name: Change root user password on first run
mysql_user:
login_user: root
login_password: ''
name: root
password: "{{ SQL_ROOT_PASSWORD }}"
priv: "*.*:ALL,GRANT"
host: "{{ item }}"
with_items:
- "{{ ansible_hostname }}"
- "127.0.0.1"
- "::1"
- "localhost"
After that, tasks like
- name: Make sure user exists
shell:
cmd: "mysql -uroot -p{{ SQL_ROOT_PASSWORD }} -e\"CREATE USER IF NOT EXISTS '{{ SQL_USER }}'#'{{ SQL_IP }}' IDENTIFIED BY '{{ SQL_USER_PASSWORD }}' REQUIRE SSL;\" -N -B"
register: result
were be possible.
I am trying to create a mysql database through Ansible playbook. After reading several threads & questions over here, I have created my playbook like this -
---
- name: "Install Packages"
hosts: localhost
connection: local
become: yes
vars:
mysql_root_password: Test#1123
tasks:
- name: Install basic packages
apt:
name:
- vim
- curl
- python3-pip
- mysql-server
- libmysqlclient-dev
- python-pymysql
state: latest
cache_valid_time: 3600
- name: start & enable mysql Server
service:
name=mysql
state=started
enabled=yes
- name: checking RabbitMQ & mysql Server status
command: systemctl status "{{ item }}"
with_items:
- mysql
register: result
ignore_errors: yes
- name: showing mysql Server status
debug:
var: result
- name: Install pip modules
pip:
name:
- pymysql
- virtualenv
state: present
executable: /usr/bin/pip3
- name: create /root/.my.cnf with password credentials
blockinfile:
path: /root/.my.cnf
block: |
[client]
user=root
password={{ mysql_root_password }}
[mysql]
user=root
password={{ mysql_root_password }}
[mysqldump]
user=root
password={{ mysql_root_password }}
[mysqldiff]
user=root
password={{ mysql_root_password }}
create: yes
- name: "Mysql Configuration - Resetting RootPassword"
mysql_user:
login_user: root
login_password: ''
name: root
host_all: yes
password: "{{mysql_root_password}}"
- name: Create a new database with name 'test'
mysql_db:
login_user: root
login_password: "{{mysql_root_password}}"
login_unix_socket: /run/mysqld/mysqld.sock
name: test
state: present
- name: Create database user with name 'test' and password 'Test#1123' with all database privileges
mysql_user:
name: test
password: Test#1123
priv: '*.test:ALL'
state: present
While running this playbook, I am getting below error -
TASK [Mysql Configuration - Resetting RootPassword] *****************************************************************************************************************************************
[WARNING]: Module did not set no_log for update_password
fatal: [localhost]: FAILED! => {"changed": false, "msg": "unable to connect to database, check login_user and login_password are correct or /root/.my.cnf has the credentials. Exception message: (1698, u\"Access denied for user 'root'#'localhost'\")"}
I have checked /root/.my.cnf file has been updated with correct username & password.
I tried to run mysql -u root -p. This has been successful without any password. But while trying to run mysql -u 'root'#'localhost' -p, it is not allowing to get into mysql with or without password.
I know this can be fixed several ways manually. But how to fix this issue with Ansible. May be I am doing some silly mistake. But unable to figure it out. I have also tried without /root/.my.cnf file and without socket file also. But facing same issue.
UPDATE:
I fixed the issue by following this
please take a look at the https://docs.ansible.com/ansible/latest/modules/mysql_user_module.html#examples mysql_user_module documentation.
Here's probably what you want to achieve:
- name: "Mysql Configuration - Resetting RootPassword"
mysql_user:
login_user: root
login_password: actual_root_mysql_password
host: ''
name: root
password: "{{mysql_root_password}}"
To make sure that host A can connect to the database of the host B, I try to use mysql_db on a remote host
- name: Make sure A can connect to B database
mysql_db:
login_user=root
login_password=password
login_host=B_address
login_port=B_port
name=B_database
state=present
and I get that error message even if the login/pass is right
msg: unable to connect, check login_user and login_password are correct,
or alternatively check ~/.my.cnf contains credentials
Am i missing something? can I set login_host with a specific ansible host?
Did you configure the mysql to accept the connection from Host A because
by default mysql only accept connection from localhost.
If you have configured that the mysql accept the connection from Host A then you can verify that the database exist
- name: check if DB exists
shell: mysql -e 'SHOW DATABASES;' | grep {{ B_database }}
register: dbstatus
failed_when: dbstatus.rc == 2
Then you can run your task, if the B_database exist
- name: Make sure A can connect to B database
mysql_db:
login_user=root
login_password=password
login_host=B_address
login_port=B_port
name=B_database
state=present
when: dbstatus.rc == 0
no_log: yes # You can disable this, if you want to print the stdout
If you are sure that the above cases are true and you are still getting error, then please do this:
Add this task inside your task/main.yml
- name: Copy the root credentials as .my.cnf file
template:
src: root.cnf.j2
dest: "~/.my.cnf"
mode: 0600
and this will be your root.cnf.j2
[client]
user=root
password={{ password }}
What it will do is, to connect the mysql from the root user without password and perform these task. You can remove it after running all the task or leave it like this because it is under root and have tight permission.
In my situation I only wanted to import an SQL file once, when the database gets created. Here's how to achieve that, the Ansible way:
---
- name: Test playbook
hosts: localhost
connection: local
tasks:
- name: Database exists
mysql_db:
name: some_db
state: present
register: database_exists
- name: Import database schema
mysql_db:
name: some_db
state: import
target: database.sql.gz
when: database_exists.changed
Using combination of above answers
- name: check if DB exists
shell: mysql --host={{ db_host }} --user={{ db_username }} --password={{ db_password }} -e 'SHOW DATABASES;' | grep -c {{ db_name }}
register: dbstatus
failed_when: dbstatus.rc == 2
- name: Create database
mysql_db: name={{db_name}} collation=utf8mb4_unicode_ci state=present login_host={{ db_host }} login_user={{ db_root_username }} login_password={{ db_root_password }}
when: dbstatus.stdout == "0"
- name: Create application user in the database
mysql_user: name={{ db_username }} password={{ db_password }} host={{ db_connection_host }} append_privs=true priv={{ db_name }}.*:ALL state=present login_host={{ db_host }} login_user={{ db_root_username }} login_password={{ db_root_password }}
when: dbstatus.stdout == "0"
useful to note you can get the output of dbstatus by adding -vvv when you run the playbook
Because ansible was failing on the grep exit code when it was not found, I found a different way to filter the databases:
> mysql -e "SHOW DATABASES LIKE '<database>'" -sN
and then you can do the following:
when: dbstatus.stdout_lines
because python will treat an empty array as false
keep in mind that there's actually 3 hosts involved here:
the "ansible execution host", the host on which you're running ansible;
the "ansible target host", the host(s) your task target;
the mysql host, that is the databse server.
In this case, the target host needs to be able to connect to the mysql host as the login_user with the login_password.
Check :
that the provider user and password are granted connectivity to the mysql host from the target host
that the firewalls between the target and mysql hosts allow mysql connections (typically port 3306). If these things work, so should the ansible module.
Use config_file: to connect, and check mode to figure out if database exists or not:
- name: Make sure A can connect to B database
mysql_db:
config_file: /etc/mysql/debian.cnf
name=B_database
state=present
check_mode: yes
register: database_exists
- debug: var=database_exists.changed
I'm working with postgresql and a much later version of ansible, so using the above answers I pulled together this solution.
- name: find out if db exists
postgresql_query:
query: |
SELECT datname FROM pg_database where datname = '{{ dbname }}'
register: result
become: yes
become_user: postgres
- name: look at results {{ result.rowcount }}.....
debug:
var: result
{{ dbname }} is a variable which can be replaced with a literal. The second task shows that you can look at the result.rowcount and if it is 0 then the database does not exist. If it is 1, the database exist. Now you can use result.rowcount with when: to determine if a task is executed or not. For example, in my case I'm trying to determine if I should create the database or not, such as:
- name: message if db exists....
debug:
msg: "yes it exist"
when: "{{ result.rowcount }} == 1"
I'm trying to write a single page ansible script for mysql installation and setup a new user and create a empty DB. what I've tried so far -
hosts file
[mysql]
webapp ansible_ssh_host=xxx.xxx.xxx.xxx
mysql.yml (The single file for all tasks/vars/handelers) (both hosts and mysql.yml in same directory) and I can login in remote system using ssh
---
- hosts: mysql
vars:
- system_packages:
- build-essential
- python-dev
- python-pip
- libmysqlclient-dev
- mysql-server
- python-mysqldb
- root_password: root
tasks:
- name: Install MySQL
apt: pkg={{ item }} state=installed update-cache=yes
with_items: system_packages
tags:
- setup
- name: Start MySQL service
action: service name=mysql state=started enabled=yes
- name: Update mysql password for root account
mysql_user: name=root host={{ item }} password={{root_password}}
with_items:
- 127.0.0.1 #In case of distributed system how should I place Ip addr of this system
- localhost
- name: create db 'mydb'
action: mysql_db db=mydb state=present
- name: Creates database user 'eric' with password 'eric' for 'mydb' and grant all priveleges
action: mysql_user state=present name=eric password=eric priv=mydb.*:ALL
handlers:
- name: start mysql
service: name=mysql state=started
when I run this script I get this output(+error)
failed: [webapp] => (item=127.0.0.1) => {"failed": true, "item": "127.0.0.1"}
msg: unable to connect to database, check login_user and login_password are correct or ~/.my.cnf has the credentials
You can't connect to mysql server before you will set root password. To do this use the following:
- name: Set password for root user
shell:
mysqladmin password "{{ root_password }}" -u root
After a default installation on ubuntu, you only can login to your mysql as root with password "root". So to change your password you will have to set your mysql login user to root (as you did in the next task)
- name: Update mysql password for root account
mysql_user:
name=root host={{ item }}
password={{root_password}}
login_user=root
login_password="root"
sudo: yes
sudo_user: root
There is also a ready-to-go solution in ansible galaxy
Please add this task inside your task/main.yml
- name: Copy the root credentials as .my.cnf file
template: src=root.cnf.j2 dest=~/.my.cnf mode=0600
and this will be your root.cnf.j2
[client]
user=root
password={{ root_password }}
What it will do is, to connect the mysql from the root user without password and perform these task. You can remove it after running all the task or leave it like this because it is under root and have tight permission.
I have a problem installing MySQL with ansible on a vagrant ubuntu,
This is my MySQL part
---
- name: Install MySQL
apt:
name: "{{ item }}"
with_items:
- python-mysqldb
- mysql-server
- name: copy .my.cnf file with root password credentials
template:
src: templates/root/.my.cnf
dest: ~/.my.cnf
owner: root
mode: 0600
- name: Start the MySQL service
service:
name: mysql
state: started
enabled: true
# '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
And I have this error
failed: [default] => (item=vagrant-ubuntu-trusty-64) => {"failed": true, "item": "vagrant-ubuntu-trusty-64"}
msg: unable to connect to database, check login_user and login_password are correct or ~/.my.cnf has the credentials
failed: [default] => (item=127.0.0.1) => {"failed": true, "item": "127.0.0.1"}
msg: unable to connect to database, check login_user and login_password are correct or ~/.my.cnf has the credentials
failed: [default] => (item=::1) => {"failed": true, "item": "::1"}
msg: unable to connect to database, check login_user and login_password are correct or ~/.my.cnf has the credentials
failed: [default] => (item=localhost) => {"failed": true, "item": "localhost"}
msg: unable to connect to database, check login_user and login_password are correct or ~/.my.cnf has the credentials
my .my.cnf is
[client]
user=root
password={{ mysql_root_password }}
and when copied on the server
[client]
user=root
password=root
I don't understand why, ~/.my.cnf is created
Project Github
Thanks
When mysql-server is installed headlessly, there's no password. Therefore to make .my.cnf work, it should have a blank password line. Here's what I tested with for a .my.cnf:
[client]
user=root
password=
It's also slightly strange to put .my.cnf in your vagrant user directory as owned by root and only readable as root.
After ensuring the password was blank in .my.cnf, I was able to properly set the password for root in those four contexts. Note that it fails to run after that, since .my.cnf would need to be updated, so it fails the idempotency test.
There's a note on the ansible mysql_user module page that suggests writing the password and then writing the .my.cnf file. If you do that, you need a where clause to the mysql_user action (probably with a file stat before that).
Even more elegant is to use check_implicit_admin along with login_user and login_password. That's beautifully idempotent.
As a third way, perhaps check_implicit_admin makes it even easier.
Here's my successful playbook showing the above, tested with a few fresh servers. Kinda proud of this. Note .my.cnf is unnecessary for all of this.
---
- hosts: mysql
vars:
mysql_root_password: fart
tasks:
- name: Install MySQL
apt: name={{ item }} update_cache=yes cache_valid_time=3600 state=present
sudo: yes
with_items:
- python-mysqldb
- mysql-server
#- name: copy cnf
# copy: src=.my.cnf dest=~/.my.cnf owner=ubuntu mode=0644
# sudo: yes
- name: Start the MySQL service
sudo: yes
service:
name: mysql
state: started
enabled: true
- name: update mysql root password for all root accounts
sudo: yes
mysql_user:
name: root
host: "{{ item }}"
password: "{{ mysql_root_password }}"
login_user: root
login_password: "{{ mysql_root_password }}"
check_implicit_admin: yes
priv: "*.*:ALL,GRANT"
with_items:
- "{{ ansible_hostname }}"
- 127.0.0.1
- ::1
- localhost
(edit- removed my.cnf)
Here is my complete working MySQL role, that might help you.
vars/main.yml :
mysql_root_pass: mypassword #MySQL Root Password
asks/main.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: 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
templates/root.cnf.j2
[client]
user=root
password={{ mysql_root_pass }}
handlers/main.yml
---
- name: Restart MySQL
service: name=mysql state=restarted
site.yml
---
- hosts: all
become: yes
gather_facts: yes
roles:
- mysql
If you need any help, please check this github link. Thanks
On ubuntu 20.04, it requires login_unix_socket now:
- name: setting default root password
mysql_user:
name: root
password: "{{ mysql_root_password }}"
check_implicit_admin: true
login_unix_socket: /var/run/mysqld/mysqld.sock
Cheers