Author Tim Cartwright
Will synchronize sql logins between servers. Synchronizes both the HASHED password and the SID. Very useful for synchronizing logins between Availability Group Servers. SA and DBO are not synchronized.
Will synchronize sql logins between servers. Synchronizes:
- HASHED password
- SID
- Default Database
- Default Language
The password hashes are backwards compatible, but they are not forward compatible. That means that if you script out the login on an older server it will NOT be deployable against a newer server. If you have a mix of older and newer servers, it is advised you use the AuthorityServer method, and have your logins synchronize out from there.
There are two methods of determining what logins will be scripted out:
- Default method: By default all logins will be compared with the same login name, and the login with the latest modified date will be chosen to be deployed to all the servers.
- Authority Server method: This method will only use a single server as the source for all logins to be deployed out.
- Example: if an authorityServer of "server1" is passed in, then only logins from "server1" will be scripted, and then deployed to all the other servers.
- Extra logins that are not on the AuthorityServer are ignored when scripted out, and not dropped. Only logins available on the AuthorityServer are synchronized.
Invoke-DBSynchronizeSQLLogins
[-Servers] <DBServer[]>
[[-IgnoreRegex] <String>]
[[-AuthorityServer] <String>]
[-DoNotAddAutoFix ]
[-DropIfExists ]
[-DoNotInvoke ]
[[-CreateAlterLoginSqlOutputVar] <PSReference>]
[<CommonParameters>]
-Servers <DBServer[]>
Collection of server / database names to run the query against. An array of type TCDbTools.DbServer.
NOTE: The ctor has this signature:
public DBServer(string serverInstance, string database = "master", PSCredential credentials = null)
Required? true
Position? 1
Default value
Accept pipeline input? false
Accept wildcard characters? false
-IgnoreRegex <String>
A regex that will be applied to all of the login names. Any name that matches the regex will be ignored and not scripted out.
Required? false
Position? 2
Default value
Accept pipeline input? false
Accept wildcard characters? false
-AuthorityServer <String>
The authority server to use for scripting logins. When null all logins from all servers will be compared using the latest modified
date. When a valid value is passed in, then only logins from the AuthorityServer will be scripted and deployed.
Required? false
Position? 3
Default value
Accept pipeline input? false
Accept wildcard characters? false
-DoNotAddAutoFix <SwitchParameter>
By default a script is added to the end of each login to auto fix all of the database users. Uses sp_MSForeachdb to
loop all of the databases. Passing this switch in will disable that part of the script generation.
Required? false
Position? named
Default value False
Accept pipeline input? false
Accept wildcard characters? false
-DropIfExists <SwitchParameter>
Normally a login is only dropped if found, but the SID is different. If this switch is present, then the login is always
dropped and recreated.
Required? false
Position? named
Default value False
Accept pipeline input? false
Accept wildcard characters? false
-DoNotInvoke <SwitchParameter>
When this switch is enabled then the script that is generated is NOT executed against the servers.
Required? false
Position? named
Default value False
Accept pipeline input? false
Accept wildcard characters? false
-CreateAlterLoginSqlOutputVar <PSReference>
Allows for capture of the sql that is run by use of a [ref] parameter. See examples.
Required? false
Position? 4
Default value
Accept pipeline input? false
Accept wildcard characters? false
A table of the servers updated, and the logins synched to each and whether deploying to that server was successful.
Optionally a reference parameter can be utilized to grab the login alter / create sql.
Uses the authority server to synchronize logins to the other servers
$authorityServer = "server1"
$serverList = @()
$serverList += [TCDbTools.DBServer]::new("server1")
$serverList += [TCDbTools.DBServer]::new("server2")
$serverList += [TCDbTools.DBServer]::new("server3")
$retVal = Invoke-DBSynchronizeSQLLogins -Servers $serverList -AuthorityServer $authorityServer -InformationAction Continue
$retValGrabs the latest logins from each server to synchronize to the other servers. Ignores any logins with an underscore in the name.
$serverList = @()
$serverList += [TCDbTools.DBServer]::new("server1")
$serverList += [TCDbTools.DBServer]::new("server2")
$serverList += [TCDbTools.DBServer]::new("server3")
$retVal = Invoke-DBSynchronizeSQLLogins -Servers $serverList -InformationAction Continue -IgnoreRegex "^[^_]*?_.*"
$retValGenerates the change sql, but does not run it against the server list. Grabs the sql into a variable.
$serverList = @()
$serverList += [TCDbTools.DBServer]::new("server1")
$serverList += [TCDbTools.DBServer]::new("server2")
$serverList += [TCDbTools.DBServer]::new("server3")
[string]$loginSql = ""
$retVal = Invoke-DBSynchronizeSQLLogins -Servers $serverList -DoNotInvoke -InformationAction Continue -CreateAlterLoginSqlOutputVar ([ref]$loginSql)
$retVal
$loginSqlAn example of the script that is generated for a single login when DoNotAddAutoFix is not enabled, and DropIfExists is not enabled.
The password has been truncated.
DECLARE @checked BIT = 0
/******************************************************************************************************************************************/
/******************************* Login: FOO generated from: SERVER1 on: 2023-05-01 15:49:32 ***********************************************/
/******************************************************************************************************************************************/
SET @checked = (SELECT [is_policy_checked] FROM sys.sql_logins WHERE name = 'FOO')
IF @checked = 1 BEGIN ALTER LOGIN [FOO] WITH CHECK_POLICY = OFF END
IF NOT EXISTS(SELECT 1 FROM sys.server_principals sp WHERE sp.name = 'FOO' AND sp.sid = 0xA5D09CAA8D73364BBEBC172F0C033C64) BEGIN
RAISERROR('********************************[FOO]********************************', 0, 1) WITH NOWAIT;
IF EXISTS(SELECT 1 FROM sys.server_principals sp WHERE sp.name = 'FOO') BEGIN;
RAISERROR('DROPPING LOGIN [FOO]', 0, 1) WITH NOWAIT;
DROP LOGIN [FOO];
END;
RAISERROR('CREATING LOGIN [FOO]', 0, 1) WITH NOWAIT;
CREATE LOGIN [FOO]
WITH PASSWORD = 0x02001867BC72D30F... HASHED,
DEFAULT_DATABASE = [master],
DEFAULT_LANGUAGE = [us_english],
SID = 0xA5D09CAA8D73364BBEBC172F0C033C64;
END ELSE BEGIN
RAISERROR('ALTERING LOGIN [FOO]', 0, 1) WITH NOWAIT;
ALTER LOGIN [FOO]
WITH PASSWORD = 0x02001867BC72D30F... HASHED,
DEFAULT_DATABASE = [master],
DEFAULT_LANGUAGE = [us_english];
END
RAISERROR('AUTO FIXING LOGIN [FOO]', 0, 1) WITH NOWAIT;
EXEC sys.sp_MSForeachdb N'
USE [?];
IF EXISTS (SELECT 1 FROM sys.databases d WHERE d.name = ''?'' AND d.is_read_only = 0) BEGIN
IF EXISTS (SELECT 1 FROM sys.database_principals dp WHERE dp.name = ''FOO'') BEGIN
EXEC sys.sp_change_users_login @Action = ''Update_One'', @UserNamePattern = ''FOO'', @LoginName = ''FOO''
END
END
'
IF @checked = 1 BEGIN ALTER LOGIN [FOO] WITH CHECK_POLICY = ON END;An example of the script that is generated for a single login when DoNotAddAutoFix is enabled, and DropIfExists is enabled.
The password has been truncated.
DECLARE @checked BIT = 0;
/******************************************************************************************************************************************/
/******************************* Login: FOO generated from: SERVER1 on: 2023-05-01 15:49:32 ***********************************************/
/******************************************************************************************************************************************/
SET @checked = (SELECT [is_policy_checked] FROM sys.sql_logins WHERE name = 'FOO')
IF @checked = 1 BEGIN ALTER LOGIN [FOO] WITH CHECK_POLICY = OFF END
RAISERROR('********************************[FOO]********************************', 0, 1) WITH NOWAIT;
IF EXISTS(SELECT 1 FROM sys.server_principals sp WHERE sp.name = 'FOO') BEGIN;
RAISERROR('DROPPING LOGIN [FOO]', 0, 1) WITH NOWAIT;
DROP LOGIN [FOO];
END;
RAISERROR('CREATING LOGIN [FOO]', 0, 1) WITH NOWAIT;
CREATE LOGIN [FOO]
WITH PASSWORD = 0x02001867BC72D30F... HASHED,
DEFAULT_DATABASE = [master],
DEFAULT_LANGUAGE = [us_english],
SID = 0xA5D09CAA8D73364BBEBC172F0C033C64;
IF @checked = 1 BEGIN ALTER LOGIN [FOO] WITH CHECK_POLICY = ON END;