Using Intune to remotely install Powershell modules on enrolled devices

A few weeks ago I shared a post detailing how you could write the resultant output of an Intune pushed Powershell script to Azure Tables, you can read that post here, the use case that drove that post was a customer asking for explicit evidence that a particular Microsoft hotfix had been installed on all devices in their estate.

The main function of that script used the Az module to connect to the Azure table and write the data, however, in that script I made what is in hindsight a pretty significant oversight in that I assumed and therefore didn’t check that the Az module had been installed and imported, this meant it failed when running on the majority of users’ devices as they didn’t have the module installed.

Thank you to Nathan Cook who commented on that post asking that very question and making me realise the mistake, I’ve now added an addendum to that post advising as such.

Intune-InstallPSModules-Comment

To that end see the below script from Nickolaj Andersen’s Github repo that I’ve adapted to suit being deployed from Intune.

This script can either be used at the start of an individual script to check for the presence of any required modules or deployed separately, say as part of an Autopilot post-deployment sequence to push out commonly used modules.

#Start logging
Start-Transcript -Path "C:\Logs\InstallAzNew - $(((get-date).ToUniversalTime()).ToString("yyyyMMddThhmmssZ")).log" -Force

# Determine if the Az module needs to be installed
try {
Write-Host "Attempting to locate Az module"
$AzModule = Get-InstalledModule -Name Az -ErrorAction Stop -Verbose:$false
if ($AzModule -ne $null) {
Write-Host "Authentication module detected, checking for latest version"
$LatestModuleVersion = (Find-Module -Name Az -ErrorAction Stop -Verbose:$false).Version
if ($LatestModuleVersion -gt $AzModule.Version) {
Write-Host "Latest version of Az module is not installed, attempting to install: $($LatestModuleVersion.ToString())"
$UpdateModuleInvocation = Update-Module -Name Az -Scope CurrentUser -Force -ErrorAction Stop -Confirm:$false -Verbose:$false
}
}
}
catch [System.Exception] {
Write-Host "Unable to detect Az module, attempting to install from PSGallery"
try {
# Install NuGet package provider
$PackageProvider = Install-PackageProvider -Name NuGet -Force -Verbose:$false

# Install Az module
Install-Module -Name Az -Scope AllUsers -Force -ErrorAction Stop -Confirm:$false -Verbose:$false
Write-Host "Successfully installed Az"
}
catch [System.Exception] {
Write-Host "An error occurred while attempting to install Az module. Error message: $($_.Exception.Message)" ; break
}
}

# Stop Logging
Stop-Transcript

This script should be run as the logged-on user, ensure this is set when creating the task in Intune, as below.

Intune-InstallPSModules-RunAsUser

Note, if you do use this script to deploy the entire Az module and not a subset such as Az.Network, be aware that it is pretty big and may take a while to download depending on environmental factors, such as available bandwidth etc.

The below screenshot shows the output of the transcript log written to the local device, note it shows that neither the Az module or NuGet package provider was installed so they were both pulled from the PSGallery and installed.

Intune-InstallPSModules-Log

For confidence during testing, you can see the script installing the many Az modules into C:\Program Files\WindowsPowershell\Modules

Intune-InstallPSModules-ModulesDir

A few quick tips for troubleshooting, not just for this script but for any you deploy via Intune.

1 > Don’t Wait

The default refresh and pull cycle of Intune (think GP refresh time for AD GPO’s) is 60 minutes but during development you’re going to want to push that script out fast. There are several ways to force a sync between Windows 10 and Intune, the quickest is definitely to restart the Microsoft Intune Management Extension service, this will force an immediate sync.

Intune-InstallPSModules-Service

2 > Run Script Locally

This may sound like an obvious one, but say you’re running a script like the one above to install a certain module and you’ll want to keep the testing environment that same, that is, you don’t want to run it on a different build of Windows 10, where you have full admin rights etc as this will increase the likelihood of false-positives.

To that end, Intune caches and executes a local copy of the script in C:\Program Files (x86)\Microsoft Intune Management Extension\Policies\Scripts – run that as the locally logged on user, maybe add the -WhatIf switch to simulate the results.

Note, the scripts won’t have the same friendly and informative name you saved them as, instead, they’re given the GUID name of the task in Azure.

Intune-InstallPSModules-SavedDir

3 > In The Registry We Trust

If you don’t subscribe to the practice of using Start and Stop-Transcript for logging you can use the registry to get the results of the script.

The key is HKLM\Software\Microsoft\MicrosoftIntuneManagementExtension\Policies

Again, as with the locally cached script, the key adopts the name of the task GUID, from there you can view the Result and ResultDetails values.

This is handy for development, however, I strongly suggest writing the out to verbose logs for Production.

Intune-InstallPSModules-Registry

3 thoughts on “Using Intune to remotely install Powershell modules on enrolled devices”

  1. Thanks Dean.

    I was just coming back as i’m using Intune now and also want to install some PS modules.

    Great blog post!. Worked well

    Liked by 1 person

  2. Hi,

    Any thoughts on how to tackle this if your users dont have local admin rights? As the above has to be run in the context of the user, if they dont have admin rights the script to install any modules doesn’t work!

    Any thoughts appreciated!!

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: