Tuesday, October 8, 2024
HomePowershellYour Go-To PowerShell Template for HTTP-Triggered Azure Capabilities

Your Go-To PowerShell Template for HTTP-Triggered Azure Capabilities


Stop reinventing the wheel and crafting snowflake PowerShell scripts to execute your PowerShell Azure Capabilities. Be a part of me as I cowl the right way to create a easy framework run script to incorporate a an ordinary in all your HTTP-triggered PowerShell capabilities!

The Anatomy of a PowerShell Script

Within the PowerShell world, a script or a operate has all the time has three areas of labor; enter, processing and output. Every space serves a definite function:

  • To customise script conduct – enter
  • To do thew work – processing
  • To let you know what occurred – output

When writing a typical PowerShell script, your enter will sometimes be parameters. Parameters outline variables you may move to capabilities at runtime.

Get-Factor -Identify 'thing1' -Worth 'value1'

Processing will be almost something from formatting strings, restarting companies, messing with fil, no matter..

Output all the time returns objects whether or not that’s a easy string or some complicated object.

PS> Get-Factor -Identify 'thing1' -Worth 'value1'
thingvalue

PS> Get-Factor -Identify 'thing1' -Worth 'value1'
[pscustomobject]@{
	Identify="thing1"
	Worth="value1"
}

PowerShell scripts/capabilities all the time have these main constructs and customarily comply with the identical sample each time of receiving the identical type of enter and returning the identical type output.

The “Earlier than” PowerShell Method

If you’re “changing” PowerShell scripts/capabilities for the net as in remodeling them for Azure Capabilities, that anatomy modifications. You continue to have the identical enter, processing and output areas of labor however what your script takes as enter and outputs is totally different.

On the internet, all the pieces is HTTP and your scripts should settle for and converse HTTP.

For instance, you’ll have a script you run regionally that restarts a Home windows service. That script may need a parameter, restart the requested service and return the service object.

[CmdletBinding()]
param(
	[Parameter(Mandatory)]
	[string]$ServiceName
)


## Returns the service object output from Restart-Service
Restart-Service -Identify $ServiceName

The script might even carry out the service restart over PowerShell remoting to restart a service on a distant pc.

[CmdletBinding()]
param(
	[Parameter(Mandatory)]
	[string]$ComputerName,
	
	[Parameter(Mandatory)]
	[string]$ServiceName
)


## Returns the service object output from Restart-Service
Invoke-Command -ComputerName $ComputerName -ScriptBlock { Restart-Service -Identify $utilizing:ServiceName }

You’d then name the script per the same old:

.restartmyservice.ps1 -ComputerName 'REMOTEPC' -ServiceName 'someservice'

PowerShell alleviates lots of overhead for you. You may merely move values to a parameter, have the script course of and built-in instructions like Restart-Service or Invoke-Command return some type of object that’s then returned to you.

However now you must restart a service on an Azure digital machine, make a change to an Azure web site or question an Azure SQL database; the placement modifications and so should your PowerShell script.

Serverless and Native: A Completely Totally different Setting

If you construct a PowerShell script and intend to execute it on a machine in your community, you naturally make some assumptions:

  • It can run on a bit of {hardware} you management
  • The script can have entry to inner community sources
  • It can execute below some type of context like a website consumer, native consumer, and so forth.
  • It can run so long as it’s wanted to finish the job

No matter what that script does, you may assume lots of environmental attributes. You’ve full management over it’s execution and it’s state. The script might additionally run for days when you wished it to. Complete management is yours!

Within the Azure Capabilities/serverless world, the stakes are a bit totally different. If you execute PowerShell within the cloud by way of HTTP webhooks/triggers, you relinquish some management to alleviate the pains of working your individual compute.

You surrender the liberty to execute PowerShell nevertheless you need in change to forego the fear of host reliability.

HTTP is stateless and is usually for short-lived executions.

Out with the Outdated Parameters

Taking the brand new setting your script is working in, let’s change up that restart service script you’ve been counting on and begin remodeling it into an Azure Operate.

First, the parameters gotta go. No, you don’t need to eliminate enter fully however you do need to take away PowerShell parameters and introduce “HTTP parameters”.

Beforehand, you had a few parameters; ComputerName and ServiceName. In Azure Capabilities, a operate triggered by way of HTTP should solely have two PowerShell parameters; $Request and $TriggerMetadata.

The $Request parameter incorporates all the incoming HTTP stuff just like the physique, headers, supply, and so forth. The $TriggerMetadata parameter incorporates varied different metadata in regards to the operate invocation similar to how the operate was triggered, queue messages, and so forth.

That’s it. Two PowerShell parameters.

Now, how, you could ask, do you inform your script now which service you’d prefer to restart? You learn to “embed” that parameter within the HTTP physique.

Similar to net APIs, the HTTP physique incorporates the info payload; mainly the stuff you wish to move to the endpoint. Sounds acquainted? The HTTP physique passes data to the script similar to your parameters do.

Let’s modify the script now to simply accept the required parameters.

[CmdletBinding()]
param(
	[Parameter(Mandatory)]
	$Request,
	
	[Parameter(Mandatory)]
	$TriggerMetadata
)

💡 You don’t even must make the parameters obligatory as a result of when the script is invoked, these will all the time be the 2 parameters handed to it.

Invoking the Azure Capabilities Script

Now we have the $Request parameter outlined within the new script however what does it even seem like? How do you even invoke this script to start with to inform? HTTP!

Utilizing Invoke-WebRequest or Invoke-RestMethod, you may invoke this script that’s embedded in an Azure Operate. The URI will comply with the format https://<operate app identify>.azurewebsites.web/<operate identify>?code=optionalcodehereforprivateauthentication.

Let’s say we have now an Azure Operate App created referred to as MyApp and a operate within it referred to as RestartService. I might name the operate like this:

Invoke-RestMethod -Uri 'https://myapp.azurewebsites.web/restartservice?code=.....'

💡 You may get any Azure operate’s base URL with PowerShell by working:

PS> $functionApp = Get-AzFunctionApp -ResourceGroupName $resourceGroupName -Identify $functionAppName
PS> $functionApp.HostName | ForEach-Object { "https://$_/$functionName }

I wish to see what the Request parameter holds after I name this operate so I’ll add a Write-Host reference like I often do within the script and deploy the app (with the script) to Azure.

[CmdletBinding()]
param(
	[Parameter(Mandatory)]
	$Request,
	
	[Parameter(Mandatory)]
	$TriggerMetadata
)

Write-Host $Request
Deployment in Visible Studio Code

As soon as the app has been zipped up, despatched as much as Azure and is prepared, I’ll invoke the script with Invoke-RestMethod and see what occurs….

Completely nothing.

Why? As a result of all your widespread “Write” cmdlets don’t work the way you’re used to within the net world. There’s no verbose, data, error, output stream anymore. Your communication should go over the net; not over PowerShell.

Let’s Get Some Output with HTTP Bindings

To get some type of output again, you need to “bind” that output to what Azure Capabilities calls an HTTP binding. That basically means you must connect any output out of your script to the HTTP response going out as soon as the script is completed executing.

To bind your script output to HTTP, you need to use the Push-OutputBinding cmdlet. This cmdlet references a pre-existing output binding and provides a worth to it.

Operate bindings are all the time saved within the operate.json file that sits within your Azure operate.

A single enter binding referred to as Request and an output binding referred to as Response.

To “bind” PowerShell output to operate output, you need to use Push-OutputBinding to connect the PowerShell output to the output binding identify. On this case, it’s Response.

However, you may’t simply assign the output on to the Worth parameter of Push-OutputBinding.

[CmdletBinding()]
param(
	[Parameter(Mandatory)]
	$Request,
	
	[Parameter(Mandatory)]
	$TriggerMetadata
)


Push-OutputBinding -Identify Response -Worth $Request

When you do, you’ll obtain a pleasant HTTP/500 error indicating failure. Push-OutputBinding can’t full translate the PowerShell object to HTTP-speak.

To make sure your output comes again accurately, you need to use the System.Web.HttpResponseContext object. You may create that by making a hashtable with a Physique key and assign your output to the Physique key. Then, casting that hashtable to the HttpResponseContext and assigning that because the Worth.

utilizing namespace System.Web

[CmdletBinding()]
param(
[Parameter(Mandatory)]
$Request,

[Parameter(Mandatory)]
$TriggerMetadata
)

$response = @{
Physique = $Request
}

Push-OutputBinding -Identify Response -Worth ([HttpResponseContext]$response)

💡 Don’t neglect the utilizing namespace System.Web line. This masses the .NET meeting that the HTTPResponseContext sort wants.

Deploy modifications, invoke the URI once more and also you’ll see that the request returns the contents of the Request parameter! That is beginning to seem like a PowerShell operate once more!

Output from PowerShell Azure operate

Including “PowerShellEsque” Parameters

In a PowerShell script, we have now the posh of defining parameters and including options like obligatory, parameter sorts, parameter validation, dynamic parameters and so forth. We are able to do rather a lot with parameters however in an Azure Operate, when your enter is an HTTP request represented by a single $request variable, your choices are restricted. All “parameters” to the script are held within the $request hashtable. However, with some ingenuity, you may mimic that very same performance. By enumerating the important thing/worth pairs within the $request hashtable, you may course of the parameters nevertheless you’d like.

For instance, I do know I solely wish to permit sure parameters on this script. Since my official PowerShell parameters are simply $request and $TriggerMetadata, I can enumerate the important thing/worth pairs within the $request hashtable to imitate these parameters.

Creating Variables from HTTP Question Parameters

Possibly I would like this Azure Operate to have two parameters; Identify and Worth. In a typical PowerShell setting, you possibly can reference a $Identify and $Worth variable within the script.

[CmdletBinding()]
param(
	[Parameter()]
	$Identify,
	
	[Parameter()]
	$Worth
)

Write-Host "The Identify parameter is $Identify"
Write-Host "The Worth parameter is $Worth"

In an Azure Operate, you need to “convert” hashtable keys to variables to reference them the identical approach by enumerating the hashtable and creating new variables with the New-Variable cmdlet.

$Request.Question.GetEnumerator() | ForEach-Object {
	New-Variable -Identify $_.Key -Worth $_.Worth
}

Then, you’re free to reference $Identify and $Worth anyplace within the script.

utilizing namespace System.Web

[CmdletBinding()]
param(
	[Parameter(Mandatory)]
	$Request,
	
	[Parameter(Mandatory)]
	$TriggerMetadata
)

$Request.Question.GetEnumerator() | ForEach-Object {
	New-Variable -Identify $_.Key -Worth $_.Worth
}

$response = @{
	Physique = "The identify handed was [$Name] with worth of [$Value]
}

Push-OutputBinding -Identify Response -Worth ([HttpResponseContext]$response)

However, you don’t wish to settle for all HTTP question parameters. Similar to in a typical PowerShell script, you solely wish to permit sure parameters and mimic obligatory parameters. In that case, you may carry out some checks towards recognized parameters and throw an error if you would like any of them to be obligatory.


utilizing namespace System.Web

[CmdletBinding()]
param(
	[Parameter(Mandatory)]
	$Request,
	
	[Parameter(Mandatory)]
	$TriggerMetadata
)

$response = @{
	Physique = $null
}

## Outline all parameters (obligatory or elective)
$allowedQueryParams = @('Identify','Worth')

## Outline all the parameters that should have a worth
$requiredParams = @('Identify')

## If any HTTP question params handed are usually not acknowledged, cease
[array]$notRecognizedParams = Examine-Object $allowedQueryParams @($request.Question.Keys) | Choose-Object -ExpandProperty InputObject
if ($notRecognizedParams.Depend -gt 0) {
	throw "The parameter(s) [$($notRecognizedParams -join ',')] weren't acknowledged!"
}

## Guarantee all obligatory parameters have been handed
[array]$missingMandatoryParams = $requiredParams | The place-Object { $_ -notin @($request.Question.Keys) }
if ($missingMandatoryParams.Depend -gt 0) {
	throw "Lacking obligatory parameter(s) [$($missingMandatoryParams -join ',')]!"
}

## If all parametere have been acknowledged and all obligatory parameters have been used, now create variables from them
$Request.Question.GetEnumerator() | ForEach-Object {
	New-Variable -Identify $_.Key -Worth $_.Worth
}

$response = @{
	Physique = "The identify handed was [$Name] with worth of [$Value]"
}

Push-OutputBinding -Identify Response -Worth ([HttpResponseContext]$response)

If you execute the operate now, you’ll see the operate will solely settle for the parameters you outlined and even has “obligatory” parameters.

Validating Azure Capabilities parameter validation

The Remaining Contact: Creating Descriptive HTTP Errors

Now that we have now an amazing script constructed out so as to add to an Azure Operate, let’s add a bit little bit of error dealing with to it. You noticed from the screenshot above that when the script doesn’t have the best parameters, it returns an HTTP/500 error however simply says Inner Server Error. We are able to make this extra descriptive by sending our personal HTTP standing code.

The ultimate script beneath:

  • Wraps all the code within a strive/catch block.
  • Defines a customized HTTP standing code within the response
  • Defines the exception message because the response physique
  • Returns the HTTP response within the lastly block to return the response whether or not or not an exception was thrown

utilizing namespace System.Web

[CmdletBinding()]
param(
	[Parameter(Mandatory)]
	$Request,
	
	[Parameter(Mandatory)]
	$TriggerMetadata
)

$response = @{}

strive {

	## Outline all parameters (obligatory or elective)
	$allowedQueryParams = @('Identify','Worth')
	
	## Outline all the parameters that should have a worth
	$requiredParams = @('Identify')
	
	## If any HTTP question params handed are usually not acknowledged, cease
	[array]$notRecognizedParams = Examine-Object $allowedQueryParams @($request.Question.Keys) | Choose-Object -ExpandProperty InputObject
	if ($notRecognizedParams.Depend -gt 0) {
		throw "The parameter(s) [$($notRecognizedParams -join ',')] weren't acknowledged!"
	}
	
	## Guarantee all obligatory parameters have been handed
	[array]$missingMandatoryParams = $requiredParams | The place-Object { $_ -notin @($request.Question.Keys) }
	if ($missingMandatoryParams.Depend -gt 0) {
		throw "Lacking obligatory parameter(s) [$($missingMandatoryParams -join ',')]!"
	}
	
	## If all parametere have been acknowledged and all obligatory parameters have been used, now create variables from them
	$Request.Question.GetEnumerator() | ForEach-Object {
		New-Variable -Identify $_.Key -Worth $_.Worth
	}
	
	$response.Physique = "The identify handed was [$Name] with worth of [$Value]"

} catch  Out-String)
 lastly {
	Push-OutputBinding -Identify Response -Worth ([HttpResponseContext]$response)
}
Returning HTTP standing code and messages

It’s best to now have an amazing template to make use of along with your Azure Capabilities PowerShell capabilities!

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments