Run any PnP-PowerShell in one AzureFunction from Microsoft Flow
/Are you missing a SharePoint connector in Flow? Do you need that one extra thing that's only in PnP-PowerShell (because there are 300 cmdlets in PnP-PowerShell)? Frustrated with having to write another AzureFunction just for this one thing?
(Trumpet please) Behold! I present the last AzureFunction (for PnP-PowerShell) you'll ever need to write.
Run-Any-PnP-PowerShell
And then, we'll call it from Flow. Hmm wait, I think we can do better with the title...
(Trumpet please) Behold!
Run-Any-PowerShell in Flow
Wait, wait, no, why stop there with the title...
(Trumpet please) Behold!
Run Any Compute You Want in Flow
Plan
- Create our Run-Any-PnP-PowerShell script
- Call it from Flow
- Call it to do some random compute for us
Create our Run-Any-PnP-PowerShell script
First - create an AzureFunctions app. Install PnP-PowerShell in it.
I have an old post in 2016 that has similar steps, but the ones from SharePoint PnP Team is much more current.
If you have one already - great, just add another method.
Select PowerShell from Experimental. I call it Run-Any-PnP-PowerShell
Write the method.
# we need to import PnP-PowerShell cmdlets Import-Module "D:\home\site\wwwroot\modules\SharePointPnPPowerShellOnline.psd1" # POST method: $req $script = Get-Content $req -Raw # prepare credentials from environment USER/PW $securepw = ConvertTo-SecureString $env:PW -AsPlainText -Force $credentials = New-Object System.Management.Automation.PSCredential ($env:USER, $securepw) # run the script from POST body Invoke-Expression $script -OutVariable out | Tee-Object -Variable out # catch output and send it back Out-File -Encoding Ascii -FilePath $res -inputObject $out
6 lines of magic.
Call this from Flow
This works because Azure Functions is pretty clever about guessing the output of the Function, and changes the content type to JSON
Call it to do some random compute
Here's the same Function doing complex maths. It'll also handle XML, JSON, .NET Framework and run any of the existing cmdlets or any extra ones we decide to import.
I think this ASCII module isn't very good.
Security [updated]
The feedback I got immediately was we'll need to secure that. That's fair. Here's a bunch of pretty important links to secure such a crazy "weapon of mass scripting".
Guard your AzureFunction activation key
Because you are calling AzureFunction from within a Flow - technically, only people that use the Flow can see the URL.
IP Filtering
Turn on IP Address Filtering for Azure Functions https://docs.microsoft.com/en-us/azure/app-service/app-service-ip-restrictions
And the list of IP Addresses for your Flow runs from https://docs.microsoft.com/en-us/flow/limits-and-config#ip-address-configuration
Secure by Azure AD
You can secure the Function with Azure AD https://docs.microsoft.com/en-us/azure/app-service/app-service-mobile-how-to-configure-active-directory-authentication
Your Flow HTTP request will need to authenticate as well before it can call your AzureFunction. https://twitter.com/johnnliu/status/943761628671090688
Implementing Azure Functions Managed Service Identity
https://docs.microsoft.com/en-us/azure/app-service/app-service-managed-service-identity
This prevents the username/password being stored in the environment which can be read by the script. With MSI, the credentials are stored in KeyVault.
Switch to Azure Automation
Azure Automation's native integration with KeyVault may offer the extra security you want. I personally find it slower to start up, and the connector to retrieve the output is one extra step. But this is definitely possible.
Add Persistent Logging
Because all the scripts has to come through your Flow - add a logging step that ensures scripts that are run are stored away with a log.
Finally
I do apologize I published this initially without talking about some security options. Over the next day it became pretty clear that we need to secure such a crazy technique.
On the other hand, there was some pretty good discussions - so may be leaving out the security section for one day was a good idea after all.