Tag Archives: environment

Powershell Impersonation – the easy way

Have you ever had a script which runs great against multiple SQL instances but then you find it failing on a new instance?

I have a script which backs up all my SQL Server databases and then copies the local backups to warehouse storage. The script runs as a Windows Scheduled task on our storage server for each SQL Instance.

Usually when I get a new SQL Server, I just add a new Windows Scheduled Task on our storage server, adjust the param to point to the new server, and the script backs up all the databases on the server\instance. Great! However, I recently found a job in which the backups were failing consistently. Not good! . It was a fairly new “test” server so I probably did not pay as close attention to it as I should have when I first set it up.

The bottom line causing the problem The only reason I knew of the failures was because of having I set up the script to write and event log for any error. However,, it was one of those “catch-all” errors with little significant information. If you don’t know already, debugging a Powershell script executed via a scheduled task is no easy task. Error codes are not easy to get. Traces are not easy to get. All in all, this type of debugging is a real PITA.

What made this even more confusing is the script ran perfectly when run from the console. No problems! BTW, I have at least 40 of these tasks running each evening without error. After a few hours of putting in numerous traps and write event logs I finally found the reason it would only fail when run as a Windows Task. The real cause of the problem was the “Log on as” user for the scheduled task did not have access to the SQL Instance! My normal testing used MY Windows credentials which has complete access rights.

Say hello to my little friend, RUNAS!

When starting a Powershell session, the run-time system sets up the environment with your Windows credentials and uses them for access to all resources. In order to have an environment whereby you don’t have to login to Windows as another user, you can use the RUNAS command (in most circumstances). Using RUNAS, I was able to start a PowerGUI session in which my credentials were the same as those being used as the logon for the Windows Task. Now, I was able to thoroughly debug the script using breaks, traces, etc. I now have a desktop Icon which starts up PowerGUI as that user for whenever I need it. You can find out more about RUNAS here, and the command I am using looks similar to this:

C:\Windows\System32\runas.exe /user:Domain\Login "C:\Program Files (x86)\PowerGUI\ScriptEditor.exe"

Failover SQL Server with Powershell

To failover an SQL mirrored database is fairly easy using SSMS or T-SQL. However, in a DR situation when things are happening quickly and time is of the essence, there is not always the luxury of having the resources available to accommodate failing over a large number of databases. If at all possible it is most desirable to fail over all over your databases while the principal server is still available.

In preparation for our next DR test I developed a Powershell script which would identify all mirrored databases and failover those databases. In the event the principal server is unavailable, the script can also be used for a forced failover on the mirrored server. Our environment consists of mirroring databases on one server to another like server at our DR site. Although you could mirror various databases from one instance to a multitude of instances, we adhere to a one-to-one instance environment. The script assumes this one-to-one placement but it could be easily changed to accommodate a one-to-many or even a many-to-many environment. That’s the beauty of Powershell scripting.

The script is developed on a Windows 7 platform with SQL 2008 R2 Tools. It was tested on Windows Server 2008 and 2008 R2 with SQL 2008 R2 EE. You are free to use the script as is or to modify it as needed. However, I do ask you maintain the attribution to the author


Param(
	[CmdletBinding()]
	[Parameter(Mandatory=$true,Position=0)]
	[string]$PrincipalServer,
	[Parameter(Mandatory=$true,Position=1)]
	[string]$MirrorServer,
	[Parameter(Mandatory=$false,Position=2)]
	[String[]]$Database,
	[Parameter(Mandatory=$false,Position=3)]
	[switch]$Force
)

Function New-SMOconnection {
	[CmdletBinding()]
    Param (
		[Parameter(Mandatory=$true)]
		[string]$server,
		[int]$StatementTimeout=0
	)
	Process {
	If(!(Test-Connection -ComputerName ($server.Split('\')[0]) -Quiet -Count 1)) {
		Throw "Could not connect to Server Machine $($server.Split('\')[0])."
	}
	$conn = New-Object Microsoft.SqlServer.Management.Common.ServerConnection($server)
	$conn.applicationName = "PowerShell SMO"
	$conn.StatementTimeout = $StatementTimeout
	Try {$conn.Connect()}
	Catch {
		Write-Exception $_.Exception
	}
	if ($conn.IsOpen -eq $false) {
		Throw "Could not connect to $server SQL Instance."
	}
	$smo = New-Object Microsoft.SqlServer.Management.Smo.Server($conn)
	$smo
	}
}

Function Write-Exception {
	Param($exception)
	Write-Host $exception.Message
	While ($exception.InnerException) {
		$exception = $exception.InnerException
		Write-Host $exception.Message
	}
}

Function Force-FailOver {
Param(
	[CmdletBinding()]
	[Parameter(Mandatory=$true,Position=0,ValueFromPipeLine=$true)]
	$database
)
	Process {
		Try{
			$database.ChangeMirroringState([Microsoft.SqlServer.Management.Smo.MirroringOption]::ForceFailoverAndAllowDataLoss)
		}
		Catch {
			Write-Exception $_.Exception
			Return "`nUnsuccessful Failover for $($database.Name)"
		}
		Return "Database mirror $($database.Name) is failed over with possible data loss"
	}
}

Function Normal-FailOver {
<#
	Normal failover executes on the Principal database. Once the failover completes,
	we then need to adjust our Mirroring Safety Level back to OFF. We have to switch
	to the Principal database which is now on the mirror server and alter the Saftey
	level there.
#>
Param(
	[CmdletBinding()]
	[Parameter(Mandatory=$true,Position=0)]
	$smo,
	[Parameter(Mandatory=$true,Position=0,ValueFromPipeLine=$true)]
	$database
)
	Process {
		Try{
			$database.MirroringSafetyLevel = [Microsoft.SqlServer.Management.Smo.MirroringSafetyLevel]::Full
			$database.Alter()
			# We have to wait to do the actual failover. The database needs to
			# catch up after changing the safety level and altering the database
			Start-Sleep -Seconds 3
			$database.ChangeMirroringState([Microsoft.SqlServer.Management.Smo.MirroringOption]::Failover)

			# Switching to server where the Principal now resides
			$db = $smo.Databases[$($Database.Name)]
			$db.MirroringSafetyLevel = [Microsoft.SqlServer.Management.Smo.MirroringSafetyLevel]::Off
			$db.Alter()
		}
		Catch {
			Write-Exception $_.Exception
			Return "`nUnsuccessfull failover for $($database.Name)"
		}
		Return "Database mirror $($database.Name) is failed over"
	}
}
# Initialize process
Set-StrictMode -Version Latest
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo")
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO")
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended")
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SqlEnum")

# Get a connection to the Mirror server
Try {
	$smoMirror = New-SMOconnection -server $MirrorServer
}
Catch {
	Write-Exception $_
	Throw "`nMirror server unavailable. Failover can not be run for $MirrorServer"
}

If($Force -eq $false) {
	# Get a connection to the Principal Server
	Try {
		$smoPrincipal = New-SMOconnection -server $PrincipalServer
	}
	Catch {
		Write-Exception $_.Exception
		Throw "Principal server $PrincipalServer is unavailable. Try running with the -Force parameter"
	}
	# Get a collection of mirrored databases from the principal server
	$databases = @($smoPrincipal.databases | Where-Object {$_.IsMirroringEnabled -and
		$_.Status -eq 'Normal' -and
		$_.MirroringStatus -eq [Microsoft.SqlServer.Management.Smo.MirroringStatus]::Synchronized})
}
Else {
	# Get a collection of mirrored databases from the mirrored server
	$databases = @($smoMirror.databases | Where-Object {$_.IsMirroringEnabled -AND ($_.Status -eq 'Restoring')})
}

# Use the database parameter list (if supplied) to limit the databases to the list
If($database) {
	$databases = @($databases | Where-Object {$database -contains $_.Name})
}

# Use a function to fail over the databases depending on the type of failover
If($Force -eq $true) {
	$databases | Force-FailOver
}
Else {
	$databases | Normal-FailOver -smo $smoMirror
}

If($smoPrincipal) {$smoPrincipal.ConnectionContext.Disconnect()}
$smoMirror.ConnectionContext.Disconnect()
Write-Host "`nScript completed for $($databases.Count) database(s)"