How can I run a deployment or a script as a different user?

In production, many Octopus customers use service accounts to run the Tentacle service on their deployment targets. This is good practice and helps us follow the Principle of least privilege. But what if your Tentacle service accounts lack access to other network or filesystem resources needed for your deployment? For example, when running a SQL Server database deployment you may want to use Windows Authentication to connect to your database, and the user needed for SQL Server may be different from the user running Tentacle.

Customers have asked us is it possible to run steps as a different user? While there is not built in support for this in Octopus, there are a few different methods available with a bit of scripting. PowerShell has a few cmdlets that seem related. See: Invoke-Command, New-PSSession, or Start-Process. I struggled with these myself and ran into a few hurdles with which my limited Windows Administration skills could not overcome. Luckily, my colleague and Senior Solutions Architect James Chatmas came to the rescue with a script that uses some lower level Windows APIs to impersonate a user.

The script can be found here:

The PowerShell module Use-Impersonation.psm1 imports some lower level Windows DLLs and uses native APIs to mimic the Windows user login. Help for the script also came from: c# - How to use LogonUser properly to impersonate domain user from workgroup client - Stack Overflow

Now we’ll try using this in Octopus…
First, since the function in the gist is packaged as a PowerShell module (.psm1), we can use Octopus’es Script Modules feature to import the code we need:

Next, I’ll create a new Octopus Runbook with a Run a Script step called Run Script as Impersonated User. I want to prompt the user to enter their credentials, so this is a great opportunity to use Prompted Variables:

I created three prompted variables to correspond with the parameters exposed in the Use-Impersonation module:

  • Impersonate.Username
  • Impersonate.Password
  • Impersonate.Domain

Next, we need to connect our Script Module to this project:

And finally, we just need to call the module within our script step!

Here is what the invocation looks like in my example:

$securePass = ConvertTo-SecureString $OctopusParameters["Impersonate.Password"] -AsPlainText -Force
$scriptBlock = { 
    Write-Host "Hello World from $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)" 
    # Add more code here you'd like to run as the impersonated user.
}

Use-Impersonation -Username $OctopusParameters["Impersonate.Username"] `
    -Password $securePass `
    -Domain $OctopusParameters["Impersonate.Domain"] `
    -ScriptBlock $scriptBlock

Obviously, your own $scriptBlock will look different from mine :slight_smile:

But when I run this, it works!

Note: This method will only work using Windows PowerShell, not PowerShell core. The same APIs are not available in PSCore.