This post is a continuation of Multiple DB refresh automation using PowerShell post . The OP requested me to automate the login transfer process as well. The script should generate an outfile and execute the file on the target server.
<#
.ProblemStatement
Copying SQL server logins to file is little bit tricky though we are able to generate the script the password need to handled in a proper way to make it work.
Usually we’ll make use of scripts like sp_help_revlogin to manage copying logins from one instance to another but the requirement won’t suffice our purpose it won’t generate the script for server permissions and role assignments.
When you are automating the entire refresh process,PowerShell provides a flexibility to run SQL and generate the scripts in the said path. The SQL file can be invoked using invoke-sqlcmd to to execute it across any target server
The below code loads the snapins
SqlServerProviderSnapin100
SqlServerCmdletSnapin100
.Example1
To generate the script
PS:\>Copy-Logins -SourceServer <SourceServer> -outpath E:\OutPut
.Example2
Generate and apply script on to the target server
PS P:\> Copy-Logins -SourceServer <SourceServer> -outpath E:\OutPut -TargetServer <ServerName>
.Reference Link
#>
function Copy-Logins{
[cmdletbinding()]
Param
(
[parameter(Mandatory=$true)][string] $Sourceserver,
[String] $TargetServer,
[string] $outpath)
##Create a new outfile at query function call
[string]$FileDS = Get-Date -Format "yyyyMMdd"
[string]$outFile = $outpath +'\'+ $Sourceserver+'_'+$FileTS + "_login.sql"
New-Item -ItemType file $outfile -Force
##Prepare the connection string
$SqlConnection = New-Object System.Data.SQLClient.SQLConnection("Server=$Sourceserver;Integrated Security=sspi;");
$SqlCommand = New-Object System.Data.SQLClient.SqlCommand;
$SqlCommand.Connection = $SqlConnection;
Try
{
##SQL Account - Executing a SQL is better than working with Hashed value
$SQL= "
-- Scripting Out the Logins To Be Created
SELECT 'IF (SUSER_ID('+QUOTENAME(SP.name,'''')+') IS NULL) BEGIN CREATE LOGIN ' +QUOTENAME(SP.name)+
CASE
WHEN SP.type_desc = 'SQL_LOGIN' THEN ' WITH PASSWORD = ' +CONVERT(NVARCHAR(MAX),SL.password_hash,1)+ ' HASHED, CHECK_EXPIRATION = '
+ CASE WHEN SL.is_expiration_checked = 1 THEN 'ON' ELSE 'OFF' END +', CHECK_POLICY = ' +CASE WHEN SL.is_policy_checked = 1 THEN 'ON,' ELSE 'OFF,' END
ELSE ' FROM WINDOWS WITH'
END
+' DEFAULT_DATABASE=[' +SP.default_database_name+ '], DEFAULT_LANGUAGE=[' +SP.default_language_name+ '] END;' COLLATE SQL_Latin1_General_CP1_CI_AS AS [-- Logins To Be Created --]
FROM sys.server_principals AS SP LEFT JOIN sys.sql_logins AS SL
ON SP.principal_id = SL.principal_id
WHERE SP.type IN ('S','G','U')
AND SP.name NOT LIKE '##%##'
AND SP.name NOT LIKE 'NT AUTHORITY%'
AND SP.name NOT LIKE 'NT SERVICE%'
AND SP.name <> ('sa')"
$SQL2="SELECT
'EXEC master..sp_addsrvrolemember @loginame = N''' + SL.name + ''', @rolename = N''' + SR.name + '''
' AS [-- server Roles the Logins Need to be Added --]
FROM master.sys.server_role_members SRM
JOIN master.sys.server_principals SR ON SR.principal_id = SRM.role_principal_id
JOIN master.sys.server_principals SL ON SL.principal_id = SRM.member_principal_id
WHERE SL.type IN ('S','G','U')
AND SL.name NOT LIKE '##%##'
AND SL.name NOT LIKE 'NT AUTHORITY%'
AND SL.name NOT LIKE 'NT SERVICE%'
AND SL.name <> ('sa')"
$SQL3="
SELECT
CASE WHEN SrvPerm.state_desc <> 'GRANT_WITH_GRANT_OPTION'
THEN SrvPerm.state_desc
ELSE 'GRANT'
END
+ ' ' + SrvPerm.permission_name + ' TO [' + SP.name + ']' +
CASE WHEN SrvPerm.state_desc <> 'GRANT_WITH_GRANT_OPTION'
THEN ''
ELSE ' WITH GRANT OPTION'
END collate database_default AS [-- server Level Permissions to Be Granted --]
FROM sys.server_permissions AS SrvPerm
JOIN sys.server_principals AS SP ON SrvPerm.grantee_principal_id = SP.principal_id
WHERE SP.type IN ( 'S', 'U', 'G' )
AND SP.name NOT LIKE '##%##'
AND SP.name NOT LIKE 'NT AUTHORITY%'
AND SP.name NOT LIKE 'NT SERVICE%'
AND SP.name <> ('sa')"
$SqlCommand.CommandText = $SQL;
$SqlConnection.Open();
$table = $SqlCommand.ExecuteReader();
While ($table.Read())
{
$table[0]+' '+"`r`n"+"GO"|Out-File -Append -FilePath $outfile
}
$SqlConnection.Close();
$SqlConnection.Open();
$SqlCommand.CommandText = $SQL2;
$table = $SqlCommand.ExecuteReader();
While ($table.Read())
{
$table[0]+' '+"`r`n"+"GO"|Out-File -Append -FilePath $outfile
}
$SqlConnection.Close();
$SqlConnection.Open();
$SqlCommand.CommandText = $SQL3;
$table = $SqlCommand.ExecuteReader();
While ($table.Read())
{
$table[0]+' '+"`r`n"+"GO"|Out-File -Append -FilePath $outfile
}
$SqlConnection.Close();
}
catch
{
$ErrorMessage = $_.Exception.Message
Write-host "$Sourceserver does't exist or Invalid SQL instance Name $ErrorMessage"
}
if (!(Get-PSSnapin | ?{$_.name -eq 'SqlServerProviderSnapin100'}))
{
add-pssnapin SqlServerProviderSnapin100
write-host "Loading SqlServerProviderSnapin100 in session"
}
else
{
write-host "SqlServerProviderSnapin100 is already loaded"
}
if (!(Get-PSSnapin | ?{$_.name -eq 'SqlServerCmdletSnapin100'}))
{
add-pssnapin SqlServerCmdletSnapin100
write-host "Loading SqlServerCmdletSnapin100 in session"
}
else
{
write-host "SqlServerCmdletSnapin100 is already loaded"
}
If($TargetServer){
try
{
Invoke-Sqlcmd -ServerInstance $TargetServer -InputFile $outFile -ErrorAction Stop
}
catch
{
Write-host "$TargetServer not reachable "
}
}
}
Please refer the below link which gives a full list of required details