Automating Umbraco with PowerShell

Books I read
See all recommendations

Background

This particular example is for warming up a UCommerce site in staging, but the technique can be used for anything you can do in the Umbraco backoffice. Especially with the new REST APIs coming out.

In this particular case, I've sinned and not created a good test environment for the last few integration bits of a project. It was hard to tune the production behavior of some code without actually running it in production. However, it's running Umbraco 7.5.13 and UCommerce 7.7. It's probably missing the other performance fix too, but the result is that it takes quite a while to warm up everything. So we warm it up in staging and then swap slots to get it fresh, awake and blazing fast into production.

Resolve

After having done this a few times, I figured I wanted something to do while waiting. What better activity than automating the whole routine so I could do something else instead? (Like automating automation...) Here's a powershell script I ended up with to warm up everything in the backoffice. I'll go through the pieces below.

Param(
    [string]$password
)

$ws = New-Object Microsoft.PowerShell.Commands.WebRequestSession

$body = @{
    "username"="admin@admin.com"
    "password"=$password
}

$json = $body | ConvertTo-Json

Invoke-RestMethod `
    -Method Post `
    -ContentType "application/json" `
    -Uri "https://customer-staging.azurewebsites.net/umbraco/backoffice/UmbracoApi/Authentication/PostLogin" `
    -WebSession $ws `
    -Body $json

Write-Host "20%"

Invoke-RestMethod -Method Get -Uri "https://customer-staging.azurewebsites.net/umbraco/ucommerce/catalog/editcategory.aspx?id=718" -WebSession $ws

Write-Host "40%"

Invoke-RestMethod -Method Get -Uri "https://customer-staging.azurewebsites.net/umbraco/ucommerce/catalog/editproduct.aspx?id=465&parentcategoryId=718" -WebSession $ws

Write-Host "60%"
Invoke-RestMethod -Method Get -Uri "https://customer-staging.azurewebsites.net/umbraco/ucommerce/settings/orders/editpaymentmethod.aspx?id=8" -WebSession $ws

Write-Host "80%"

Invoke-RestMethod -Method Get -Uri "https://customer-staging.azurewebsites.net/umbraco/ucommerce/settings/orders/editshippingmethod.aspx?id=10" -WebSession $ws
Write-Host "100%"

The effect is that we log into Umbraco using a provided password, and instead of navigating and clicking everything, we fire a request triggering all the caching and JIT compilation for us. Even though I'm using the Invoke-RestMethod cmdlet, I can do regular web calls. The cmdlet has a sibling called Invoke-WebRequest, but the rest version is better for posting commands. It's mostly a matter of mental context, but they have a few differences.

Log into Umbraco

To set up an authorized session with Umbraco, we can call the PostLogin action. It's the same endpoint that is used from the login screen. An authorized session means that we need to get a cookie and pass it with all our requests. In order for each Invoke-RestMethod to pass this cookie, we can create a WebRequestSession we pass to each call:

$ws = New-Object Microsoft.PowerShell.Commands.WebRequestSession

Invoke-RestMethod -WebSession $ws -Uri "..."

If the response brings a cookie, it's kept in the WebRequestSession, and subsequently passed back with each new request. Just like a browser.

Then we need some JSON to pass our username and password. You can declare dictionaries of sorts in PowerShell like so:

$body = @{
    "username"="admin@admin.com"
    "password"=$password
}

And then convert it to JSON by piping it to the ConvertTo-Json cmdlet:

$json = $body | ConvertTo-Json

Finally we're ready to fire the request off to Umbraco, adding config for HTTP method, ContentType etc.:

Invoke-RestMethod `
    -Method Post `
    -ContentType "application/json" `
    -Uri "https://customer-staging.azurewebsites.net/umbraco/backoffice/UmbracoApi/Authentication/PostLogin" `
    -WebSession $ws `
    -Body $json

Automate Umbraco

For this I just needed to kick off a request to some pages, but posting messages around like rebuilding a grumpy index, running an ad hoc task, even publishing should be just as simple.

It takes a while, so I added a little status message. I'm sure PowerShell wizards would pack this stuff into better reusable parts.

Write-Host "20%"

Now reuse that WebRequestSession object to fire off new authenticated requests:

Invoke-RestMethod -WebSession $ws -Method Get -Uri "https://customer-staging.azurewebsites.net/umbraco/ucommerce/catalog/editcategory.aspx?id=718"

Do it everywhere

With all the love I can give to UCommerce, I ended up naming the script kick-ucommerce.ps1. It's like kicking your old belowed car to get it started, after you've polished it. Really!
Adding my source folder to the path environment variable makes the script available from any shell. Even the Package Manager console in Visual Studio.

Warm up UCommerce

Make a note that the password is a parameter. You do change it more often than you'd like to update the code right? How 'bout automating the process? ;)

Smoke test and swap the Azure slot

I'll leave the swap-slot script I run after warming up the site here too. The cool thing about warming up with the script is that it'll fail almost immediately on the Umbraco login if anything isn't like it should. So it doubles as a smoke test.

When everything looks good, I can just go:

swap-slot -from staging -to production -site customer-x

And here's the "simpleness" of that one. There are fairly good docs on all the Azure cmdlets over on Microsoft's sites. (Ask Google. ;) )

Param(
    [string]$site,
    [string]$from,
    [string]$to
)

$subscriptionId = "333112F4-4483-449C-A2DA-727E8D2E428D"
$resourcegroupname = "Common-Group"     # Might need to be param

Login-AzureRmAccount -SubscriptionId $subscriptionId

Swap-AzureRmWebAppSlot `
    -SourceSlotName $from `
    -DestinationSlotName $to `
    -Name $site `
    -ResourceGroupName $resourcegroupname

That's it! Now go automate something so you get more time to do fun stuff! :)

Author

comments powered by Disqus