91

I have an existing MySQL instance (test), containing 2 databases and a few users each having different access privileges to each database.

I now need to duplicate one of the databases (into production) and the users associated with it.

Duplicating the database was easy:

Export:

mysqldump --no-data --tables -u root -p secondb >> secondb_schema.sql

Import:

mysql -u root -p -h localhost secondb < secondb_schema.sql

I didn't find, however, a straightforward way to export and import users, from the command line (either inside or outside mysql).

How do I export and import a user, from the command line?


Update: So far, I have found manual (and thus error prone) steps for accomplishing this:

-- lists all users
select user,host from mysql.user;

Then find its grants:

-- find privilege granted to a particular user
show grants for 'root'@'localhost'; 

Then manually create user with the grants listed in the result of the 'show grants' command above.

I prefer a safer, more automated way. Is there one?

Community
  • 1
  • 1
Withheld
  • 4,603
  • 10
  • 45
  • 76

12 Answers12

71

One of the easiest ways I've found to export users is using Percona's tool pt-show-grants. The Percona tool kit is free, easy to install, and easy to use, with lots of documentation. It's an easy way to show all users, or specific users. It lists all of their grants and outputs in SQL format. I'll give an example of how I would show all grants for test_user:

shell> pt-show-grants --only test_user

Example output of that command:

GRANT USAGE ON *.* TO 'test_user'@'%' IDENTIFIED BY PASSWORD '*06406C868B12689643D7E55E8EB2FE82B4A6F5F4';
GRANT ALTER, INSERT, LOCK TABLES, SELECT, UPDATE ON `test`.* TO 'test_user'@'%';

I usually rederict the output into a file so I can edit what I need, or load it into mysql.

Alternatively, if you don't want to use the Percona tool and want to do a dump of all users, you could use mysqldump in this fashion:

shell> mysqldump mysql --tables user db > users.sql

Note: --flush-privileges won't work with this, as the entire db isn't being dumped. this means you need to run it manually.

shell> mysql -e "FLUSH PRIVILEGES"
Matthew Carey
  • 738
  • 6
  • 7
  • 1
    Download tools containing pt-show-grants from [here](https://www.percona.com/downloads/percona-toolkit/LATEST/), Doc is [here](https://www.percona.com/doc/percona-toolkit/LATEST/pt-show-grants.html) – robsch Mar 29 '17 at 12:23
  • Note that for MySQL 5, you will need to use the 2.x version of this tool, rather than 3.x – trey-jones Jun 14 '18 at 13:45
  • @threeve I assume you suggest that because `pt-show-grants` no longer provides the password with that combination? – danemacmillan Feb 07 '19 at 20:53
  • @danemacmillan My memory is hazy - I believe 3.x is compatible with mysql 8+. Probably my comment extends from trying the latest release with our server (5.x) and finding that "it didn't work". – trey-jones Feb 07 '19 at 21:38
31
mysql -u<user> -p<password> -h<host> -e"select concat('show grants for ','\'',user,'\'@\'',host,'\'') from mysql.user" > user_list_with_header.txt
sed '1d' user_list_with_header.txt > ./user.txt
while read user; do  mysql -u<user> -p<password> -h<host> -e"$user" > user_grant.txt; sed '1d' user_grant.txt >> user_privileges.txt; echo "flush privileges" >> user_privileges.txt; done < user.txt
awk '{print $0";"}'  user_privileges.txt >user_privileges_final.sql
rm user.txt user_list_with_header.txt user_grant.txt user_privileges.txt

Above script will run in linux environment and output will be user_privileges_final.sql that you can import in new mysql server where you want to copy user privileges.

UPDATE: There was a missing - for the user of the 2nd mysql statement.

JamesDeHart
  • 245
  • 2
  • 9
Vardan Gupta
  • 3,505
  • 5
  • 31
  • 40
  • 1
    There is a lot of `flush privileges;` empty lines but it works. Thanks! – Hugo H Oct 12 '16 at 11:54
  • 1
    i was really anxious to run this (hard to read script), but it worked, kudos! –  Jul 08 '17 at 20:45
  • How to add backticks? some columns use keywords so backticks are needed. GRANT SELECT, UPDATE **(ssl_key, ssl, ssl_request, ssl_action, ssl_letsencrypt, ssl_cert)** ON `database`.`table` TO 'user'@'hostname'; ssl is a reserved work, I cant change the column its not my project but it breaks this script, as will any reserved word. – CodingInTheUK Oct 02 '19 at 18:46
  • @Chris ```GRANT SELECT, UPDATE (`ssl_key`, `ssl`, `ssl_request`, `ssl_action`, `ssl_letsencrypt`, `ssl_cert`) ON `database`.`table` TO 'user'@'hostname';``` – Toothbrush Nov 27 '19 at 14:15
16

In mysql 5.7 and later you can use this.

mysqlpump -uroot -p${yourpasswd} --exclude-databases=% --users

This will generate a sql format output that you can redirect to mysql_users.sql.

Note that it is mysqlpump not mysqldump.

Pablo Luna
  • 369
  • 3
  • 5
  • 1
    NOT mysqldump but mysqlpump.... pump not dump – Pablo Luna Dec 22 '21 at 15:05
  • 4
    This is the best solution. mysqlpump is a native mysql tool, no extra shell scripting is required, and it emits both CREATE USER and GRANT statements, which you need in mysql 8. However you probably want to add `--exclude-users=mysql.%,root`. – Andrew Schulman Jul 27 '22 at 16:08
  • This just generates lots of: `mysqlpump: [ERROR] (1193) Unknown system variable 'information_schema_stats_expiry'` – AlexPi Dec 12 '22 at 23:17
  • I love this approach, but the apline linux on which my docker images are based, comes with mariadb, it sadly doesn;t have the mysqlpump :( is there a mariadb alternative? – St. Jan Jun 11 '23 at 18:53
8

Yet another bash one-liner for linux that you can use instead of the Percona tool:

mysql -u<user> -p<password> -h<host> -N mysql -e "select concat(\"'\", user, \"'@'\", host, \"'\"), coalesce(password, authentication_string) from user where not user like 'mysql.%'" | while read usr pw ; do echo "GRANT USAGE ON *.* TO $usr IDENTIFIED BY PASSWORD '$pw';" ; mysql -u<user> -p<password> -h<host> -N -e "SHOW GRANTS FOR $usr" | grep -v 'GRANT USAGE' | sed 's/\(\S\)$/\1;/' ; done
Community
  • 1
  • 1
  • 4
    Why would you force this into a one-line?? The answer is barely readable – Diggi55 Oct 11 '21 at 07:13
  • 1
    @Diggi55 just because I can and want to and just because it's convenient when you work in command line and retrieve the whole command from the history as one line rather than set of commands. You can put this one-liner into file, break it to multiple line and save as script if one-liner doesn't fit your needs. – Sergey Podushkin Oct 11 '21 at 18:31
6

In complement of @Sergey-Podushkin 's answer, this shell script code is workin for me:

mysql -u<user> -p<password> -N mysql -e "select concat(\"'\", user, \"'@'\", host, \"'\"), authentication_string from user where not user like 'root'" | while read usr pw ; do mysql -u<user> -p<password> -N -e "SHOW GRANTS FOR $usr" | sed 's/\(\S\)$/\1;/'; done 
shgnInc
  • 2,054
  • 1
  • 23
  • 34
  • 3
    Thanks, this is perfect for MySql 8, Sergey's won't work in version 8 because there isn't field password in mysql.user table – amicoderozer Jun 11 '21 at 08:07
3

PhpMyAdminYou can use phpMyAdmin.

Login and Go to your database or a table where the user has access.

Select privileges

All users with access are there.

Select Export. And a little window with all the GRANTS are there ready to copy and paste.

2

Here's what I'm using these days as part of my daily backup scripts (requires root shell and MySQL access, linux shell, and uses the mysql built-in schema:

First, I create a file /var/backup/mysqlroot.cnf containing the root password so I can automate my scripts and not hardcode any passwords in them:

[client]
password=(put your password here)

Then I create an export script which dumps create user commands and grants like this:

touch /var/backup/backup_sql.sh
chmod 700 /var/backup/backup_sql.sh
vi /var/backup/backup_sql.sh

And then write the following contents:

#!/bin/bash

mysql --defaults-extra-file=/var/backup/mysqlroot.cnf -sNe " \
  SELECT \
    CONCAT( 'CREATE USER \'', User, '\'@\'', Host, '\' IDENTIFIED BY \'', authentication_string, '\'\;' ) AS User \
  FROM mysql.user \
  WHERE \
    User NOT LIKE 'mysql.%' AND CONCAT( User, Host ) <> 'rootlocalhost' AND User <> 'debian-sys-maint' \
"

mysql --defaults-extra-file=/var/backup/mysqlroot.cnf -sNe " \
  SELECT \
    CONCAT( '\'', User, '\'@\'', Host, '\'' ) as User FROM mysql.user \
  WHERE \
    User NOT LIKE 'mysql.%' \
    AND CONCAT( User, Host ) <> 'rootlocalhost' \
    AND User <> 'debian-sys-maint' \
" | sort | while read u ; 
 do echo "-- $u"; mysql --defaults-extra-file=/var/backup/mysqlroot.cnf -sNe "show grants for $u" | sed 's/$/;/'
done

Then I just have to run it like this: /var/backup/backup_sql.sh > /tmp/exportusers.sql

RedScourge
  • 976
  • 1
  • 8
  • 12
  • Your `IDENTIFIED BY ', authentication_string,` doesn't work on the import as it's missing enclosing commas around `authentication_string`. – TommyPeanuts Jan 02 '21 at 13:56
  • authentication_string is not a string, rather it is a column name of a field we want in the table mysql.user, so it should indeed work without additional quotation marks. – RedScourge Jan 04 '21 at 05:59
  • when I try running the SQL, the hashes don't turn up in that column but the literal string does unless I put quotes around it. Also, the resulting grants don't seem to work anyway. I have had to use the percona tool instead. – TommyPeanuts Jan 04 '21 at 08:32
  • Oh, I see what you mean now. Seems I do have quotes there actually in the code I'm running. I will update the answer. As for the grants not working, I have run into problems with certain versions of MySQL, particularly lower ones, or where your hashes were originally generated in a much older version. In the latter case I had to re-set all the passwords when migrating very old credentials, then the hashes output as expected. Other than that, it works fine for me in 5.7.32 as I use it as part of my automated backups for recreating everything. – RedScourge Jan 05 '21 at 12:48
  • interesting - I'm using 5.7.32 but some of the hashes were generated in an older version so perhaps that's it. – TommyPeanuts Jan 05 '21 at 15:42
  • I think the hashes that gave me problems were from a pretty old version, like 5.5.30 or something. – RedScourge Jan 06 '21 at 16:28
  • It's possible they could have come from that early a version. BTW when I tried the SQL output from your script (once I'd fixed the quote thing) I didn't get any errors, just that the users couldn't log in. – TommyPeanuts Jan 06 '21 at 18:30
1

I tackled this with a small C# program. There is code here to generate a script or apply the grants directly from source to destination. If porting from a Windows -> *nix environment you may have to consider case sensitivity issues.

using System;
using MySql.Data.MySqlClient;
using System.Configuration;
using System.IO;
using System.Collections.Generic;

namespace GenerateUsersScript
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> grantsQueries = new List<string>();

            // Get A Show Grants query for each user
            using (MySqlConnection sourceConn = OpenConnection("sourceDatabase"))
            {
                using (MySqlDataReader usersReader = GetUsersReader(sourceConn))
                {
                    while (usersReader.Read())
                    {
                        grantsQueries.Add(String.Format("SHOW GRANTS FOR '{0}'@'{1}'", usersReader[0], usersReader[1]));
                    }
                }

                Console.WriteLine("Exporting Grants For {0} Users", grantsQueries.Count);

                using (StreamWriter writer = File.CreateText(@".\UserPermissions.Sql"))
                {
                    // Then Execute each in turn 
                    foreach (string grantsSql in grantsQueries)
                    {
                        WritePermissionsScript(sourceConn, grantsSql, writer);
                    }

                    //using (MySqlConnection destConn = OpenConnection("targetDatabase"))
                    //{
                    //    MySqlCommand command = destConn.CreateCommand();

                    //    foreach (string grantsSql in grantsQueries)
                    //    {
                    //        WritePermissionsDirect(sourceConn, grantsSql, command);
                    //    }
                    //}
                }
            }

            Console.WriteLine("Done - Press A Key to Continue");

            Console.ReadKey();
        }

        private static void WritePermissionsDirect(MySqlConnection sourceConn, string grantsSql, MySqlCommand writeCommand)
        {
            MySqlCommand cmd = new MySqlCommand(grantsSql, sourceConn);

            using (MySqlDataReader grantsReader = cmd.ExecuteReader())
            {
                while (grantsReader.Read())
                {
                    try
                    {
                        writeCommand.CommandText = grantsReader[0].ToString(); 

                        writeCommand.ExecuteNonQuery();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(grantsReader[0].ToString());

                        Console.WriteLine(ex.Message);
                    }
                }
            } 
        }

        private static void WritePermissionsScript(MySqlConnection conn, string grantsSql, StreamWriter writer)
        {
            MySqlCommand command = new MySqlCommand(grantsSql, conn);

            using (MySqlDataReader grantsReader = command.ExecuteReader())
            {
                while (grantsReader.Read())
                {
                    writer.WriteLine(grantsReader[0] + ";");
                }
            }

            writer.WriteLine();
        }

        private static MySqlDataReader GetUsersReader(MySqlConnection conn)
        {
            string queryString = String.Format("SELECT User, Host FROM USER");
            MySqlCommand command = new MySqlCommand(queryString, conn);
            MySqlDataReader reader = command.ExecuteReader();
            return reader;
        }

        private static MySqlConnection OpenConnection(string connName)
        {
            string connectionString = ConfigurationManager.ConnectionStrings[connName].ConnectionString;
            MySqlConnection connection = new MySqlConnection(connectionString);
            connection.Open();
            return connection; 

        }
    }
}

with an app.config containing ...

    <connectionStrings>
        <add name="sourceDatabase" connectionString="server=localhost;user id=hugh;password=xxxxxxxx;persistsecurityinfo=True;database=MySql" />
        <add name="targetDatabase" connectionString="server=queeg;user id=hugh;password=xxxxxxxx;persistsecurityinfo=True;database=MySql" />
    </connectionStrings> 
Hugh Jones
  • 2,706
  • 19
  • 30
1

A PHP script to loop over your users to get the grant commands would be as such:

// Set up database root credentials
$host = 'localhost';
$user = 'root';
$pass = 'YOUR PASSWORD';
// ---- Do not edit below this ----
// Misc settings
header('Content-type: text/plain; Charset=UTF-8');
// Final import queries goes here
$export = array();
// Connect to database
try {
    $link = new PDO("mysql:host=$host;dbname=mysql", $user, $pass);
} catch (PDOException $e) {
    printf('Connect failed: %s', $e->getMessage());
    die();
}

// Get users from database
$statement = $link->prepare("select `user`, `host`, `password` FROM `user`");
$statement->execute();
while ($row = $statement->fetch())
{
    $user   = $row[0];
    $host   = $row[1];
    $pass   = $row[2];
    $export[] = "CREATE USER '{$user}'@'{$host}' IDENTIFIED BY '{$pass}'";
    // Fetch any permissions found in database
    $statement2 = $link->prepare("SHOW GRANTS FOR '{$user}'@'{$host}'");
    $statement2->execute();
    while ($row2 = $statement2->fetch())
    {
        $export[] = $row2[0];
    }
}

$link = null;
echo implode(";\n", $export);

Gist: https://gist.github.com/zaiddabaeen/e88a2d10528e31cd6692

pbarney
  • 2,529
  • 4
  • 35
  • 49
zed
  • 3,180
  • 3
  • 27
  • 38
  • Really handy script, the comments in the gist for code tweaks are necessary, after which it does exactly what I need. – Novocaine Oct 05 '20 at 15:37
0
    pass=your_password_here; \ 
    MYSQL_PWD=$pass mysql -B -N -uroot -e "SELECT CONCAT('\'', user,'\' ','\'', host,'\' ','\'', authentication_string,'\' ','\'', plugin,'\'') FROM mysql.user WHERE user != 'debian-sys-maint' AND user != 'root' AND user != 'mysql.sys' AND user != 'mysql.session' AND user != ''" > mysql_all_users.txt; \
    while read line; do linearray=(${line}); \
    MYSQL_PWD=$pass mysql -B -N -uroot -e "SELECT CONCAT('CREATE USER \'',${linearray[0]},'\'@\'',${linearray[1]},'\' IDENTIFIED WITH \'',${linearray[3]},'\' AS \'',${linearray[2]},'\'')"; \
        done < mysql_all_users.txt > mysql_all_users_sql.sql; \
    while read line; do linearray=(${line}); \
    MYSQL_PWD=$pass mysql -B -N -uroot -e "SHOW GRANTS FOR ${linearray[0]}@${linearray[1]}"; \
        done < mysql_all_users.txt >> mysql_all_users_sql.sql; \
    sed -e 's/$/;/' -i mysql_all_users_sql.sql; \
    echo 'FLUSH PRIVILEGES;' >> mysql_all_users_sql.sql; \
    unset pass

First mysql command : export all users to file and exclude some.
Second mysql command : loop users from file to write a sql command 'create user' to an exported file (with authentication credentials).
Third mysql command : loop users from file to append their privileges to the exported file.
sed command to append a ";" to end of lines and flush privileges to finish.
To import : MYSQL_PWD=$pass mysql -u root < mysql_all_users_sql.sql

0
SELECT CONCAT('\create user ', user,'\'@\'', host, '\' identified by ', "'", authentication_string, "'"'\;') FROM user WHERE user != 'mysql.session' AND user !='mysql.sys'  AND user != 'root' AND user != '';
Dave
  • 3,073
  • 7
  • 20
  • 33
  • 1
    See "[Explaining entirely code-based answers](https://meta.stackoverflow.com/q/392712/128421)". While this might be technically correct, it doesn't explain why it solves the problem or should be the selected answer. We should educate along with helping solve the problem. – the Tin Man Mar 22 '22 at 05:00
-1

I had the same problem. The solution is that after the import of the backup you need to do a "flush privileges;". Then the privileges of the users will be active as in the original database.

So execute:

mysql -u root -p -h localhost secondb < secondb_schema.sql
mysql -u root

then in mysql: flush privileges;

SwissCodeMen
  • 4,222
  • 8
  • 24
  • 34