Created
November 2, 2023 21:46
-
-
Save AlexanderHolmeset/01d909d21948bee9db0446a4ec5fa239 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using namespace System.Net | |
using namespace System.IO | |
# Input bindings are passed in via param block. | |
param($Request, $TriggerMetadata) | |
# Write to the Azure Functions log stream. | |
Write-Host "PowerShell HTTP trigger function processed a request." | |
function Push-HttpBinding { | |
<# | |
.SYNOPSIS | |
Simplified Output for HTTP Trigger Responses with intelligent defaults | |
.DESCRIPTION | |
Push-OutputBinding takes a lot of syntax for a simple response. Since the examples use Response as an HTTP output, we can safely assume that as a default and standard practice, plus there is error checking to warn if this isn't accurate. | |
We can also assume the user wants to issue an "OK" response unless something otherwise was provided. | |
We can also "help" users by assuming string arrays and strings were meant as a single item, and detect if some text formats like XML/HTML/JSON were used and set the content type appropriately for easier consumption. | |
.EXAMPLE | |
Get-Variable | Push-HTTPBinding | |
Show all the variables in the current environment. Defaults to JSON output | |
.EXAMPLE | |
Get-ChildItem . | ConvertTo-HTML | Push-HTTPBinding | |
Shows the items in the current folder in HTML table format. Push-HTTPBinding detects XHTML and sets the type accordingly | |
.EXAMPLE | |
"<html>test</html>" | Push-HTTPBinding -ContentType "text/plain" -StatusCode Accepted -Header @{"X-MyCoolHeader"="its so cool"} | |
Outputs html text but overrides the content type to text/plain, statuscode to Accepted, and adds a custom header | |
#> | |
[CmdletBinding()] | |
param ( | |
#The body of the message | |
[Parameter(Mandatory,ValueFromPipeline)]$Body, | |
#The output binding to send the HTTP response to. Default is Response | |
[String]$Name = 'Response', | |
#The response code for the message. Default is 200 OK | |
[HttpStatusCode]$StatusCode = 'OK', | |
#Specify the Content Type of the message. If this is specified, it will be assumed you want the raw object to be output | |
[string]$ContentType, | |
#Specify custom headers to include in the HTTP response. | |
[HashTable]$Headers | |
) | |
$Body = $input | |
#If a single object in a collection was provided, unwrap that object | |
if ($Body.count -eq 1) {$Body = $Body[0]} | |
#If a collection of strings was provided, consolidate them to a single string | |
if (-not $Body.Where{$PSItem -isnot [String]}) {$Body = $Body -join [Environment]::NewLine} | |
#region TypeDetection | |
#This really should be done in https://github.com/Azure/azure-functions-powershell-worker/blob/9f3179923deb5bb95702da1baaf1dab67fbcea7d/src/Utility/TypeExtensions.cs | |
#Passthru if it was any of the already handled types in TypeExtensions.cs | |
$typeIsDetected = $false | |
([double],[long],[int],[byte[]],[Stream]).foreach{if ($body -is $psitem) {$typeIsDetected=$true;break}} | |
function DetectStringType { | |
[CmdletBinding()] | |
param ( | |
[string]$String, | |
[regex]$Regex, | |
$chars=1000 | |
) | |
#Do a "magic numbers"-style file type detection. Designed to guess file types of large strings to maintain performance at the risk of a potential mismatch | |
$buffer = [Char[]]::New($chars) | |
([IO.StringReader]$String).Read($buffer,0,$chars) > $null | |
$headerToMatch = $buffer -join '' | |
if ($headerToMatch -match $Regex) {$true} else {$false} | |
} | |
if (-not $ContentType -and -not $typeIsDetected) { | |
#XHTML Hint Detection | |
#This is primarily assuming someone used ConvertTo-Html to create an HTML document and tried to send it out. | |
if (-not $typeIsDetected) { | |
if (DetectStringType -String ([String]$Body) -Regex ([Regex]::Escape('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'))) { | |
write-warning "Detected XHTML String, setting content type to application/html+xml" | |
$typeIsDetected = $true | |
$ContentType = 'application/xhtml+xml' | |
} | |
} | |
#HTML Hint Detection | |
if (-not $typeIsDetected) { | |
if (DetectStringType [String]$Body -regex '(?s)<html.*?>') { | |
write-warning "Detected HTML String, setting content type to text/html" | |
$typeIsDetected = $true | |
$ContentType = 'text/html' | |
} | |
} | |
#XML Hint Detection | |
#Try XML. This was tested against large XML and non-XML objects to ensure it both "failed fast and succeeded fast" and didn't try to process the entire file. | |
[switch]$isXml = $false | |
if ($Body -is [xml.xmlnode]) { | |
$isXml = $true | |
[String]$Body = $Body.OuterXml | |
} else { | |
#Very basic XML detection that looks for the first non-whitespace character to be a < then two sets of <> with a little extra criteria, this could certainly be a better regex maybe | |
$xmlDetectRegex = '(?s)^\s*?<\w+.+?>.*?<[\w\/]+.+?>.*?' | |
if (DetectStringType [String]$Body -regex $xmlDetectRegex) { | |
$isXml = $true | |
} | |
} | |
if ($isXml) { | |
write-warning "Detected XML String, setting content type to application/xml" | |
$typeIsDetected = $true | |
$ContentType = 'application/xml' | |
[String]$Body = [String]$Body | |
} | |
#JSON Hint Detection | |
if (-not $typeIsDetected) { | |
if ($Body -is [newtonsoft.json.linq.jtoken]) { | |
$isJson = $true | |
[String]$Body = [String]$Body | |
} else { | |
#Very basic XML detection that looks for the first non-whitespace character to be a < then two sets of <> with a little extra criteria, this could certainly be a better regex maybe | |
#Tested against a 180MB json string to make sure it "fails fast" | |
$jsonDetectRegex = '(?s)^\s*?[\{\[]+\s*?"\w.+?"\:' | |
if (DetectStringType [String]$Body -regex $jsonDetectRegex) { | |
$isJson = $true | |
[String]$Body = [String]$Body | |
} | |
} | |
if ($isJson) { | |
write-warning "Detected JSON String, setting content type to application/json" | |
$typeIsDetected = $true | |
$ContentType = 'application/json' | |
[String]$Body = [String]$Body | |
} | |
} | |
#For everything else set the type to application/json because downstream the parser will serialize any remaining objects to json | |
if (-not $typeIsDetected) { | |
write-warning "Did not detect any types, setting content type to application/json by default" | |
$ContentType = 'application/json' | |
} | |
} | |
Push-OutputBinding -Name $Name -Value ([HttpResponseContext]@{ | |
Body = $Body | |
Headers = $Headers | |
ContentType = $ContentType | |
StatusCode = $StatusCode | |
}) | |
} | |
#import-module Az.Accounts | |
# Write to the Azure Functions log stream. | |
Write-Host "PowerShell HTTP trigger function processed a request." | |
$accessToken = $Request.Headers["X-MS-TOKEN-AAD-ACCESS-TOKEN"] | |
$Header = @{"Authorization" = "Bearer $accessToken" } | |
$uri = "https://graph.microsoft.com/v1.0/me/" | |
$me = Invoke-RestMethod -Uri $uri -Headers $Header -Method get | |
Write-Host $me.mail | |
$body = @" | |
<html> | |
<head> | |
<title>$($me.displayname)</title> | |
</head> | |
<body> | |
<div style="text-align: center;"> | |
<h1>This is your email address: $($me.mail)</h1> | |
<h1>This is your AzureAD ObjectID: $($me.id)</h1> | |
<p>This is awesome!</p> | |
</div> | |
</body> | |
</html> | |
"@ | |
$body | Push-HTTPBinding | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment