Deploy any application from Azure Storage with Intune, PowerShell and BITS!

Deploy any application from Azure Storage with Intune, PowerShell and BITS!

Update September 24, 2018

The posted solution has been superseded today, as Brad Anderson from Microsoft has announced Win32 app support for Microsoft Intune. You can now repackage executables (.exe) to Windows Apps (.intunewin) with the Microsoft Intune Win32 App Packaging Tool available at https://github.com/Microsoft/Intune-Win32-App-Packaging-Tool.

Check out the blog post at http://www.scconfigmgr.com/2018/09/24/deploy-win32-applications-with-microsoft-intune/ for more information how to deploy these apps.

Original blog post:

If you are using Microsoft Intune to manage your organization’s devices, you might find it challenging that deploying Line-of-business apps only supports (single) .msi, .msix, .msixbundle, .appx and .appxbundle application packages for being deployed.

I’ve read an article from Peter Selch Dahl, about deploying EXE files from Microsoft Intune using Azure Blob Storage, in which Peter explains how to deploy applications with PowerShell Scripts from Microsft Intune, where the source files are located on Azure Blob Storage.

Peter provides a very basic script how to install an application. Before I went ahead and deployed certain applications using PowerShell, I’ve set a few requirements that I wanted to meet first, namely:

  • Add support for multiple file downloads from a given Azure Blob Storage Container;
  • Prevent that files downloaded are buffered in memory;
  • Provide easy adoption of the script for other applications;

In this blog post I will be providing a more advanced script to deploy any application with Microsoft Intune.

Did it work out?

I believe I managed to meet my requirements quite well! To meet these requirements, I’ve written a PowerShell script that:

  1. Installs the NuGet Package Provider in the CurrentUser scope;
    # Install the NuGet Package Provider, preventing that trusting the PSGallery with the Set-PSRepository cmdlet would hang on user input.
    try {
        Install-PackageProvider -Name NuGet -Scope CurrentUser -Force
        Write-Output 'Installed the NuGet Package Provider'
    }
    catch {
        Write-Error "Failed to install NuGet Package Provider. Exception: $($_.Exception.Message)"
    }
  2. Trusts PSGallery Repository;
    # Trust the PSGallery Repository to install required modules
    if((Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue).InstallationPolicy -ne 'Trusted') {
        try {
            Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
            Write-Output 'Trusted the PSGallery Repository'
        }
        catch {
            Write-Error "Failed to trust PSGallery Repository. Exception: $($_.Exception.Message)"
        }
    } else {
        Write-Output 'PSGallery repository is already trusted.'
    }
  3. Installs AzureRM.Storage Module in the CurrentUser scope;
    Used to retrieve all installer files (Blobs) from a given Azure Blob Storage Container. So I don’t need to specify any URLs, I just provided the Storage Account Name and Container.

    # Install required AzureRM Storage module. Used to retrieve all installer files (Blobs) from a given Azure Blob Storage Container.
    if(-not (Get-Module -Name AzureRM.Storage -ListAvailable)) {
        try {
            Install-Module -Name AzureRM.Storage -Scope CurrentUser
            Write-Output 'Installed AzureRM.Storage Module'
        }
        catch {
            Write-Error "Failed to install required AzureRM.Storage module. Exception: $($_.Exception.Message)"
        }
    } else {
        Write-Output 'AzureRM.Storage Module is already present.'
    }
  4. Creates a temp directory for storing the installation files;
    # Create directory for container in $Env:Temp
    if (!(Test-Path -Path "$Env:Temp\$Container")) { 
        Write-Output "Creating '$Env:Temp\$Container' directory"
        New-Item -ItemType Directory -Path "$Env:Temp\$Container"
    }
  5. Downloads installation files from Azure Blob Storage to a temp directory using BITS;
    Using BITS instead of Invoke-WebRequest, because the latter buffers downloads in memory.

    # All files contained inside the given $Container will be downloaded from Azure Blob Storage (Requires SAS Token with Read + List access rights)
    try {
        Write-Output "Trying to download installer files ..."
        $AzureStorageContext = New-AzureStorageContext -StorageAccountName $StorageAccountName -SasToken $SasToken
        $Blobs = Get-AzureStorageBlob -Container $Container -Context $AzureStorageContext
        foreach ($Blob in $Blobs) {
            # Save file to $Env:Temp\$Container
            Start-BitsTransfer -Source ($($Blob.ICloudBlob.StorageUri.PrimaryUri.AbsoluteUri) + $SasToken) -Destination "$Env:Temp\$Container\$($Blob.Name)"     
        }
        Write-Output "Downloaded all installer files"
    }
    catch {
        Write-Error "Failed to download installation files from Azure Blob Storage. Exception: $($_.Exception.Message)"
    }
  6. Silently installs the application;
    # Install application using specified arguments passed with the installer file as configured in the configuration section
    # Waits for installation to finish before continuing
    try {
        Start-Process -FilePath "$Env:Temp\$Container\$ApplicationSetupFile" -Args $ApplicationArguments -Wait
        Write-Output 'Application installation completed.'
    }
    catch {
        Write-Error "Failed to install application. Exception: $($_.Exception.Message)"
    }
  7. Cleans up installation files.
    Removes the temp directory and contents when the installation has completed.

    # Clean-up installation files
    Remove-Item "$Env:Temp\$Container" -Force -Recurse

You can download the full Install-ApplicationName.ps1 PowerShell script from the TechNet Gallery here.

Configuration

The PowerShell script has a configuration region. I believe that for most, if not all, all you need to edit is the configuration section to install ‘any’ (e.g. executable or App-V) application.

For example, let’s go ahead and deploy Notepad++!

  1. Download Notepad++ Installer 64-bit x64;
  2. Upload the .exe file to a separate, private container in your Azure Storage account. In my case I named the container notepadplusplus and uploaded npp.7.5.8.Installer.x64.exe;
  3. Download the template Install-ApplicationName.ps1 script from the TechNet Gallery;
  4. Copy or rename it to Install-Notepad++.ps1;
  5. Modify the configuration section, for example:
    #region Configuration
    # The Azure Blob Storage Account and Container where all required installer files are available at.
    $StorageAccountName = 'mystorageaccountname'
    $Container          = 'notepadplusplus'
    
    # SasToken to access the Azure Blob Storage account. Requires at least Read + List permissions
    $SasToken = '?sv=2018-08-25&ss=b&srt=sco&sp=rl&se=2028-03-03T22:17:41Z&st=2018-03-03T14:17:41Z&spr=https&sig=RandomSignatureString'
    
    # Installer file and arguments passed to install the application.
    $ApplicationSetupFile = 'npp.7.5.8.Installer.x64.exe'
    $ApplicationArguments = '/S'
    #endregion Configuration<br data-mce-bogus="1">
  6. Upload the PowerShell Script to Microsoft Intune;
    1. Do not run the script using the logged on credentials, since Notepad++ requires administrative permissions to install. Otherwise users will be prompted with a UAC prompt.
  7. Assign the script to a group of users;
  8. Wait for the PowerShell script to be deployed (or restart the Microsoft Intune Mananagement Service to force a sync).

Troubleshooting

Is the script not working as expected? You can review the log file in C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\IntuneManagementExtension.log, which contains the logging for PowerShell Scripts.

Interested in more information how PowerShell Scripts in Microsoft Intune work under the hood? Check out this amazing blog post by Oliver Kieselbach: Deep dive Microsoft Intune Management Extension – PowerShell Scripts.

Feel free to post any comments! I’d be happy to answer any questions you have.

3 thoughts on “Deploy any application from Azure Storage with Intune, PowerShell and BITS!

  1. Hi! Great blog post and very relevant for AAD.

    Got a quick question, can this script install multiple applications from the Storage? And how so? Currently dealing with onboarding and need to install multiple applications at the same time

    1. Hi Nikolai,

      Thanks for your question. You can create multiple copies of this script and modify each to include the details per application. You can then upload the per-application PowerShell scripts to Intune and assign them to your users.

      Best regards,

      John

    2. Hi Nikolai,

      The posted solution has been superseded today, as Brad Anderson from Microsoft has announced Win32 app support for Microsoft Intune. You can now repackage executables (.exe) to Windows Apps (.intunewin) with the Microsoft Intune Win32 App Packaging Tool available at https://github.com/Microsoft/Intune-Win32-App-Packaging-Tool.

      Check out the blog post at http://www.scconfigmgr.com/2018/09/24/deploy-win32-applications-with-microsoft-intune/ for more information.

      Best regards,

      John

Leave a Reply

Your email address will not be published. Required fields are marked *