<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://blog.aabech.no/rss/xslt"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>Lars-Erik's blog</title>
    <link>https://blog.aabech.no/</link>
    <description>Ramblings about Umbraco, .net and JavaScript development. With a sprinkle of other stuff.</description>
    <generator>Articulate, blogging built on Umbraco</generator>
    <item>
      <guid isPermaLink="false">1136</guid>
      <link>https://blog.aabech.no/archive/automating-umbraco-with-powershell/</link>
      <category>umbraco</category>
      <category>automation</category>
      <title>Automating Umbraco with PowerShell</title>
      <description>&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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. &lt;/p&gt;
&lt;h2&gt;Resolve&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Param(
    [string]$password
)

$ws = New-Object Microsoft.PowerShell.Commands.WebRequestSession

$body = @{
    &amp;quot;username&amp;quot;=&amp;quot;admin@admin.com&amp;quot;
    &amp;quot;password&amp;quot;=$password
}

$json = $body | ConvertTo-Json

Invoke-RestMethod `
    -Method Post `
    -ContentType &amp;quot;application/json&amp;quot; `
    -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/backoffice/UmbracoApi/Authentication/PostLogin&amp;quot; `
    -WebSession $ws `
    -Body $json

Write-Host &amp;quot;20%&amp;quot;

Invoke-RestMethod -Method Get -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/ucommerce/catalog/editcategory.aspx?id=718&amp;quot; -WebSession $ws

Write-Host &amp;quot;40%&amp;quot;

Invoke-RestMethod -Method Get -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/ucommerce/catalog/editproduct.aspx?id=465&amp;amp;parentcategoryId=718&amp;quot; -WebSession $ws

Write-Host &amp;quot;60%&amp;quot;
Invoke-RestMethod -Method Get -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/ucommerce/settings/orders/editpaymentmethod.aspx?id=8&amp;quot; -WebSession $ws

Write-Host &amp;quot;80%&amp;quot;

Invoke-RestMethod -Method Get -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/ucommerce/settings/orders/editshippingmethod.aspx?id=10&amp;quot; -WebSession $ws
Write-Host &amp;quot;100%&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;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 &lt;code&gt;Invoke-RestMethod&lt;/code&gt; cmdlet, I can do regular web calls. The cmdlet has a sibling called &lt;code&gt;Invoke-WebRequest&lt;/code&gt;, but the rest version is better for posting commands. It's mostly a matter of mental context, but they have a few differences.&lt;/p&gt;
&lt;h2&gt;Log into Umbraco&lt;/h2&gt;
&lt;p&gt;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 &lt;code&gt;Invoke-RestMethod&lt;/code&gt; to pass this cookie, we can create a &lt;code&gt;WebRequestSession&lt;/code&gt; we pass to each call:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ws = New-Object Microsoft.PowerShell.Commands.WebRequestSession

Invoke-RestMethod -WebSession $ws -Uri &amp;quot;...&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If the response brings a cookie, it's kept in the &lt;code&gt;WebRequestSession&lt;/code&gt;, and subsequently passed back with each new request. Just like a browser.&lt;/p&gt;
&lt;p&gt;Then we need some JSON to pass our username and password. You can declare dictionaries of sorts in PowerShell like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$body = @{
    &amp;quot;username&amp;quot;=&amp;quot;admin@admin.com&amp;quot;
    &amp;quot;password&amp;quot;=$password
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then convert it to JSON by piping it to the &lt;code&gt;ConvertTo-Json&lt;/code&gt; cmdlet:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$json = $body | ConvertTo-Json
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally we're ready to fire the request off to Umbraco, adding config for HTTP method, ContentType etc.:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Invoke-RestMethod `
    -Method Post `
    -ContentType &amp;quot;application/json&amp;quot; `
    -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/backoffice/UmbracoApi/Authentication/PostLogin&amp;quot; `
    -WebSession $ws `
    -Body $json
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Automate Umbraco&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;It takes a while, so I added a little status message. I'm sure PowerShell wizards would pack this stuff into better reusable parts.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Write-Host &amp;quot;20%&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now reuse that &lt;code&gt;WebRequestSession&lt;/code&gt; object to fire off new &lt;em&gt;authenticated&lt;/em&gt; requests:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Invoke-RestMethod -WebSession $ws -Method Get -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/ucommerce/catalog/editcategory.aspx?id=718&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Do it everywhere&lt;/h2&gt;
&lt;p&gt;With all the love I can give to UCommerce, I ended up naming the script &lt;code&gt;kick-ucommerce.ps1&lt;/code&gt;. It's like kicking your old belowed car to get it started, after you've polished it. Really!&lt;br /&gt;
Adding my source folder to the path environment variable makes the script available from any shell. Even the Package Manager console in Visual Studio. &lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1034/warmup-ucommerce.png" alt="Warm up UCommerce" /&gt;&lt;/p&gt;
&lt;p&gt;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? ;)&lt;/p&gt;
&lt;h2&gt;Smoke test and swap the Azure slot&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;When everything looks good, I can just go:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;swap-slot -from staging -to production -site customer-x
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And here's the &amp;quot;simpleness&amp;quot; of that one. There are fairly good docs on all the Azure cmdlets over on Microsoft's sites. (Ask Google. ;) )&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Param(
    [string]$site,
    [string]$from,
    [string]$to
)

$subscriptionId = &amp;quot;333112F4-4483-449C-A2DA-727E8D2E428D&amp;quot;
$resourcegroupname = &amp;quot;Common-Group&amp;quot;     # Might need to be param

Login-AzureRmAccount -SubscriptionId $subscriptionId

Swap-AzureRmWebAppSlot `
    -SourceSlotName $from `
    -DestinationSlotName $to `
    -Name $site `
    -ResourceGroupName $resourcegroupname
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That's it! Now go automate something so you get more time to do fun stuff! :)&lt;/p&gt;
</description>
      <pubDate>Tue, 07 Nov 2017 19:46:14 Z</pubDate>
      <a10:updated>2017-11-07T19:46:14Z</a10:updated>
    </item>
  </channel>
</rss>