Running Azure startup tasks as a real user

In my post about enabling PowerShell. I mentioned I got blocked for a while and would explain later why and what happened.

Problem: Windows Azure runs startup tasks as localsystem. Some startup tasks need to be running in the context of a user.

Solution: Use the task scheduler in Windows Server to execute the command.

A few people have already asked how to apply the technique to other things so here goes.

Lets take a look at the original startup task I was trying to execute.

netsh advfirewall firewall add rule name="Windows Remote Management (HTTP-In)" dir=in action=allow service=any enable=yes profile=any localport=5985 protocol=tcp

Powershell -Command Enable-PSRemoting -Force

This doesn’t work because the powershell command “Enable-PSRemoting” doesn’t work unless it runs as an elevated user that belongs to the administrators group. Localsystem doesn’t belong to this group.

In my original blog post, I showed how you could enable this using winrm. But sometimes you want this something to happen when the role starts.

To make a task run as a user account as a startup task simply create a new task for the Windows Scheduler to execute as shown below.

netsh advfirewall firewall add rule name="Windows Remote Management (HTTP-In)" dir=in action=allow service=any enable=yes profile=any localport=5985 protocol=tcp

 

net user scheduser Secr3tC0de /add

net localgroup Administrators scheduser /add

 

schtasks /CREATE /TN "EnablePS-Remoting" /SC ONCE /SD 01/01/2020 /ST 00:00:00 /RL HIGHEST /RU scheduser /RP Secr3tC0de /TR "powershell -Command Enable-PSRemoting -Force" /F

schtasks /RUN /TN "EnablePS-Remoting"

Works like a charm. Hopefully you can figure out how to run your own commands instead of PowerShell.

It is safe to say that you probably want to secure the username and password that is created. I think ideally a wrapper that can read a username and encrypted password from config, create all the scheduled tasks and then execute them. Maybe that is a job for next time.

THIS POSTING IS PROVIDED “AS IS” WITH NO WARRANTIES, AND CONFERS NO RIGHTS

Enable PowerShell Remoting on Windows Azure

If it takes you more than 1 line of code, you aren’t doing it right!

I started writing this post a week ago in response to a customer request, so excited I was I even tweeted about writing a post on PowerShell. My Bad – because right after I tweeted I hit a snag – which is worthy of a whole post on its own. Ignoring the snag right now – let me tell you how to do the above.

First – a BIG shout out to the PowerShell team at Microsoft who answered my endless questions. Also a big shout out to Lee Holmes – who once again saved my bacon.

Anyway…

At long last I’ve had a chance to “play” around with the new Windows Azure features we announced at PDC 2010. I thought it would be fun to enable PowerShell Remoting in Windows Azure Roles. (Note I’m talking about Web and Worker roles here – not VM Role).

With new features such as remote desktop, startup tasks and Azure Connect – setting up PowerShell should be easy.

First, I’m going to assume you have worked through the notes/tutorials/stuff to enable Azure Connect & Remote Desktop – that way this post stays within the realms of being relatively small.

Here is our checklist:

  • Make sure the OS Family in the ServiceConfiguration.cscfg is set to “2” to enable R2. 
  • Create a user account so you can connect to the server.
  • Add a startup task to open the firewall port.
  • Add the Role to Azure Connect.
  • Execute Lee’s magic script to enable PowerShell Remoting

PowerShell v2 is the version you need to do remoting. Server 2008 R2 contains PowerShell v2 in the box. We can tell Windows Azure to use an R2 server by changing the OSFamily in the ServiceCOnfiguration.cscfg to 2 as shown below:

<ServiceConfiguration serviceName="AzureMemcachedTest" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="2" osVersion="*">

Next step is to create a user account so that you can actually connect to the server. The easiest way to do this is to enable remote desktop, which creates a user on the host.

Now to make sure the firewall port is open we will need. I created a .cmd file containing the following 2 commands. The first opens the firewall for WINRM, the second for ping. This was added in the root folder of my role project.

netsh advfirewall firewall add rule name="Windows Remote Management (HTTP-In)" dir=in action=allow service=any enable=yes profile=any localport=5985 protocol=tcp

netsh advfirewall firewall add rule name="ICMPv6 echo" dir=in action=allow enable=yes protocol=icmpv6:128,any

Then I added a startup task to ServiceDefinition.csdef:

<Startup>

  <Task commandLine="EnablePowershellRemoting.cmd" executionContext="elevated" taskType="foreground"/>

</Startup>

Next add the Role to Azure Connect. I’ll assume you know how to do this.

Now deploy your service. Once deployed, do the final step to connect the Azure connect network, and make sure the agent is installed on your computer.

The final step is to “turn on” PowerShell Remoting. For numerous reasons, you cannot just run a startup task with “Enable-PSRemoting” as the command. The biggest reason is that startup tasks run as local system and thus cannot actually complete the Enable-PSRemoting command.

This is where I got stuck for 3 days until Lee shared his script. The script is below and is fairly easy to follow. Basically it will connect to the VM and create a scheduled task to enable-psremoting.

##############################################################################

##

## Enable-RemotePsRemoting

##

## From Windows PowerShell Cookbook (O'Reilly)

## by Lee Holmes (http://www.leeholmes.com/guide)

##

##############################################################################

 

<#

.SYNOPSIS

 

Enables PowerShell Remoting on a remote computer. Requires that the machine

responds to WMI requests, and that its operating system is Windows Vista or

later.

 

.EXAMPLE

 

Enable-RemotePsRemoting <Computer>

 

#>

 

param(

    ## The computer on which to enable remoting

    $Computername,

 

    ## The credential to use when connecting

    $Credential = (Get-Credential)

)

 

Set-StrictMode -Version Latest

$VerbosePreference = "Continue"

 

$credential = Get-Credential $credential

$username = $credential.Username

$password = $credential.GetNetworkCredential().Password

 

$script = @"

 

`$log = Join-Path `$env:TEMP Enable-RemotePsRemoting.output.txt

Remove-Item -Force `$log -ErrorAction SilentlyContinue

Start-Transcript -Path `$log

 

## Create a task that will run with full network privileges.

## In this task, we call Enable-PsRemoting

schtasks /CREATE /TN 'Enable Remoting' /SC WEEKLY /RL HIGHEST ``

    /RU $username /RP $password ``

    /TR "powershell -noprofile -command Enable-PsRemoting -Force" /F |

    Out-String

schtasks /RUN /TN 'Enable Remoting' | Out-String

 

`$securePass = ConvertTo-SecureString $password -AsPlainText -Force

`$credential =

    New-Object Management.Automation.PsCredential $username,`$securepass

 

## Wait for the remoting changes to come into effect

for(`$count = 1; `$count -le 10; `$count++)

{

    `$output = Invoke-Command localhost { 1 } -Cred `$credential ``

        -ErrorAction SilentlyContinue

    if(`$output -eq 1) { break; }

 

    "Attempt `$count : Not ready yet."

    Sleep 5

}

 

## Delete the temporary task

schtasks /DELETE /TN 'Enable Remoting' /F | Out-String

Stop-Transcript

 

"@

 

$commandBytes = [System.Text.Encoding]::Unicode.GetBytes($script)

$encoded = [Convert]::ToBase64String($commandBytes)

 

Write-Verbose "Configuring $computername"

$command = "powershell -NoProfile -EncodedCommand $encoded"

$null = Invoke-WmiMethod -Computer $computername -Credential $credential `

    Win32_Process Create -Args $command

 

Write-Verbose "Testing connection"

Invoke-Command $computername {

    Get-WmiObject Win32_ComputerSystem } -Credential $credential

 

Piece of cake really.

Once you have the script saved, you can execute it like

PS> $computername = "WhateverTheComputerNameOfTheInstanceIs"
PS> .\Enable-RemotePSRemoting.ps1 $computername

Once it finishes executing, you should be able to connect using:

PS> Enter-PSSession –ComputerName $computername –Credential RemoteDesktopUsername

Then you can work interactively – try get-process as an example.

Pretty neat, and great for debugging!

THIS POSTING IS PROVIDED “AS IS” WITH NO WARRANTIES, AND CONFERS NO RIGHTS