Enabling BitLocker on non-HSTI devices with Intune

BitLocker-DriveThis is a post about enabling BitLocker on non-HSTI devices with Windows 10 version 1809 and standard user permissions.

First of all a little background on HSTI. HSTI is a Hardware Security Testability Interface. It is an interface to report the results of security-related self-tests. Its purpose is to provide high assurance validation of proper security configuration.

The enhancement with Windows 10 version 1809 is that we are able to activate BitLocker with a MDM policy (Intune), even for non-HSTI devices and on Windows 10 Pro Edition. This was not working with Windows 10 version 1803 or lower and the community came up with custom solutions to handle this like custom PowerShell scripts deployed via Intune Management Extension. If we wanted to use Intune native MDM policies via the BitLocker CSP we needed HSTI compliant devices like the Surface devices or newer hardware devices which are mostly delivered as HSTI compliant devices now. To successful start the encryption as a standard user, a Windows 10 version 1803 was the minimum as the feature was introduced with this version.

The prerequisites for Intune BitLocker configuration are

  • Windows 10 version 1809
  • Microsoft Intune
  • non-HSTI device

Older devices can be protected by Intune BitLocker policy now?

Yes, as long as they are running Windows 10 version 1809. The most common problem is that we do not replace all devices in every Windows 10 project to have only latest HSTI compliant devices in the environment. We have to support older devices purchased maybe not long ago but not HSTI compliant. These devices can now be managed by an Intune device configuration policy to turn on BitLocker silently without administrative permissions as long as the device is a Windows 10 version 1809 device.

 

What do we need to do?

Currently at the time of writing we need two configuration policies. One endpoint protection profile and a custom profile.

The endpoint protection profile configures the silent BitLocker enforcement and other parameters like encryption strength. Go to Microsoft Intune > Device configuration – Profiles > yourpolicyname – Properties > Endpoint protection > Windows Encryption

Set Encrypt devices to Require
Set Warning for other disk encryption to Block

Sure you can set other parameters like encryption methods as well, but for a functional test this is enough.

These two settings make sure the encryption starts and it starts silently as we block the warning dialog for other disk encryption software.

Example shown below:

BitLockerIntuneEndpointProtection

The second profile is a custom profile (at time of writing it was not available in the UI) and it configures the ability to enforce the BitLocker encryption even when standard users are logging in. For example when the Windows 10 device is enrolled with an Autopilot profile where the user account type is set to standard user. AllowStandardUserEncryption is a new setting introduced with Windows 10 version 1809 BitLocker CSP and must be used in conjunction with the setting “Warning for other disk encryption set to Block” otherwise it is not functional!

The custom OMA-URI configuration must be configured like this:

OMA-URI: ./Vendor/MSFT/BitLocker/AllowStandardUserEncryption
Data type: integer
Value: 1

Example shown below:

BitLockerIntuneCustom

The two policies must be assigned to a user group or device group to test the new policies. To force the user type to a standard user after enrollment we need an Autopilot profile and assign it to our device.

AutopilotStandardUser

If we now enroll a new Windows 10 version 1809 non-HSTI device it must be encrypted silently and the recovery key must be backed up to Azure AD.

 

How can I easily verify this?

I used a Hyper-V VM Generation 2 with an enabled TPM module:

BitLockerHyperVVMTPM

To test if the VM is reporting as a non-HSTI compliant device I downloaded the Device Guard and Credential Guard hardware readiness tool and verified the HSTI status with the following PowerShell command:

.\DG_Readiness_Tool_v3.5.ps1 -Capable

The result is displayed like this:

HyperVVMHSTICheck

During my test I had to make sure that after the first restart, the Windows 10 version 1809 ISO is ejected, otherwise silent BitLocker encryption will fail. This is because the system does not have the normal start parameters during the BitLocker and TPM provisioning. The platform would take into account the additional media as the normal platform verification parameter. Which means after ejecting the ISO it would have prompted us for the recovery key. Microsoft takes care of this situation and does not start the BitLocker provisioning process at all. So, the generation of the platform default configuration parameters for later verification to unlock the TPM is prevented, as long as a removable media is inserted. See the failure event here:

BitLockerSilentEncryptionErrorIso

Without an ISO it will successfully starts the encryption and key backup to Azure AD. A success event is shown below:

BitLockerVMeventlog

The BitLocker state can be verified with the PowerShell command on the client:

Get-BitLockerVolume | fl

BitLockerVMPSStatus

In the Intune portal we can see the recovery key appended to the AAD device object:

BitLockerIntuneAADDeviceRK

 

Further information

What’s new in Windows 10, version 1809 for IT Pros
https://docs.microsoft.com/en-us/windows/whats-new/whats-new-windows-10-version-1809#bitlocker

Device Guard and Credential Guard hardware readiness tool
https://www.microsoft.com/en-us/download/details.aspx?id=53337

Hardware Security Testability Specification
https://docs.microsoft.com/en-us/windows-hardware/test/hlk/testref/hardware-security-testability-specification

 

Go ahead and start encrypting all your Windows 10 devices to strengthen your security level 👍

Part 3, Deep dive Microsoft Intune Management Extension – Win32 Apps

IntuneWin32ComboLogoMicrosoft made it finally happen and provides an integrated way to deploy Win32 Apps via the Intune Management Extension. This is by far the biggest step forward in the Modern Management field. Until now the community came up with lots of ways to utilize PowerShell scripts to finally install some Win32 Apps. By doing this you need to take care of the content location, availability, distribution, the app install, and verification logic. All this is now available in the enhanced Intune Management Extension. As a short refresh regarding Intune Management Extension, I recommend to read my blog articles about it, where you find a lot of information regarding the architecture, the inner workings, and troubleshooting advises:

Part 1 Deep dive Microsoft Intune Management Extension – PowerShell Scripts
Part 2 Deep dive Microsoft Intune Management Extension – PowerShell Scripts

Both articles are very relevant to this new feature as they all describe the architecture and technical details regarding the Intune Management Extension itself. Exactly this Intune Management Extension is used now for deploying Win32 apps packaged as .intunewin file format. It’s even better, the new functionality can be used to basically transport everything in a package and trigger a certain command line for execution. This is not a end to end walk-through how to use the feature in regards of the Intune portal or on the client itself. For this check out other great articles found in my last section End to end usage walk-throughs?. This is an article all about the inner workings of this new capability, a deep dive into the new technology itself.

Table of content for easy navigation

How do we get packaged apps now?

To get a Win32 apps deployed via the Intune Management Extension we first need to package the content we want to distribute. Microsoft decided to use the same approach like they did for the macOS world, where they provide a small tool to create the packages – Microsoft Intune App Wrapping Tool for macOS. A similar tool to create these packages is available for the Windows world now. This tool is called:

Microsoft Intune Win32 App Packaging Tool

For all of you with ConfigMgr experience, the new feature to deploy Win32 apps with Intune can be compared a little bit with the Packages and Programs functionality within ConfigMgr, but with some additions.

The usage of the Microsoft Intune Win32 App Packaging Tool (IntuneWinAppUtil.exe) is quite simple. We specify a folder and the executable and create then an yourappname.intunewin file. This is our packaged app which we then can upload to the Intune service. The command line of the tool is this:

IntuneWinAppUtil -c  -s  -o  <-q>

To dig a little deeper we inspect the resulting package in detail now. After successful execution it will generate the .intunewin file from the specified source folder and setup file. For MSI setup files, this tool will retrieve required information for later usage in the Intune portal. To explore this a little bit I captured the output and highlighted some important facts there, to gain understanding how the tool is working.

IntuneWin32AppToolOutput

As seen the tool is executed with the necessary parameters to create a package for Google Chrome Enterprise Edition Browser, which is provided as a .msi file. A sub folder called Chrome is in the same folder as the IntuneWinAppUtil. The command line then is:

IntuneWinAppUtil.exe -c .\Chrome -s GoogleChromeStandaloneEnterprise64.msi -o .\ -q

If we look at the green highlighted output now we find some very important information appearing which is exactly describing the workflow:

  1. Compressing the source folder and its files to a sub folder ‘content’ with the new extension .intunewin
  2. Encrypting the compressed file
  3. Computing SHA265 hash
  4. Generating a detection.xml file in a sub folder ‘metadata’
  5. Compressing complete working folder and create again an .intunewin file

As we can see a normal zip compression is used and therefore it is easy to verify all this by opening the generated package with our favorite archive application:

IntuneWinContentRoot

We can see the content folder and metadata folder. Where the content folder stores the encrypted .intunewin file:

IntuneWinContentSubContent

and the metadata folder contains the detection.xml file:

IntuneWinContentSubMetadata

If we now have a look at the detection.xml file we can see the information gathered about the package and msi file:

<ApplicationInfo xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ToolVersion="1.0.0.0">
  <Name>Google Chrome</Name>
 <UnencryptedContentSize>52284458</UnencryptedContentSize>
  <FileName>GoogleChromeStandaloneEnterprise64.intunewin</FileName>
  <SetupFile>GoogleChromeStandaloneEnterprise64.msi</SetupFile>
  <EncryptionInfo>
    <EncryptionKey>Rj9EtHeHGYMv5HYBpyobrmp0rVg0pzIy0TwO9PnSRg8=</EncryptionKey>
    <MacKey>5kxZeuk6xfeMak+r/9hzbwS0vBUW1jBMhgN10Jy7XBQ=</MacKey>
    <InitializationVector>2AK05qM3tm/5ijDnlFPISA==</InitializationVector>
    <Mac>zI8vragiGb1VtbVRiKo3lPkJldZGZB4juX50MCNrmDw=</Mac>
    <ProfileIdentifier>ProfileVersion1</ProfileIdentifier>
    <FileDigest>Nzl3O6e70W1P0kJDJiWh3eU6gCVOhODNNy7mspfplQA=</FileDigest>
    <FileDigestAlgorithm>SHA256</FileDigestAlgorithm>
  </EncryptionInfo>
  <MsiInfo>
    <MsiProductCode>{A4BC9C54-4589-3A4C-8217-9FA00262F471}</MsiProductCode>
    <MsiProductVersion>67.92.84</MsiProductVersion>
    <MsiUpgradeCode>{C1DFDF69-5945-32F2-A35E-EE94C99C7CF4}</MsiUpgradeCode>
    <MsiExecutionContext>System</MsiExecutionContext>
    <MsiRequiresLogon>false</MsiRequiresLogon>
    <MsiRequiresReboot>false</MsiRequiresReboot>
    <MsiIsMachineInstall>true</MsiIsMachineInstall>
    <MsiIsUserInstall>false</MsiIsUserInstall>
    <MsiIncludesServices>false</MsiIncludesServices>
    <MsiIncludesODBCDataSource>false</MsiIncludesODBCDataSource>
    <MsiContainsSystemRegistryKeys>true</MsiContainsSystemRegistryKeys>
    <MsiContainsSystemFolders>false</MsiContainsSystemFolders>
  </MsiInfo>
</ApplicationInfo>

A detailed review shows us the file and encryption information and in addition the MSI file information. This information is used by the Intune portal to pre-populate some Intune app definitions for us.

The MSI information is only available when a msi file is packaged. If we package a setup.exe for example you will not see the entire section in the detection.xml file.

Why is the .intunewin packaged like this?

This is pretty simple to explain. The portal and the Intune service gets all necessary information to deal with the app (in this case Google Chrome). The msi information listed in the detection.xml is used to simplify the setup within the Intune UI by pre-filling the app form during creation. For example by adding the install and uninstall commands for the msi automatically:

IntuneWin32AppsMSIInstallCommand

The EncryptionInfo is used to store it with your Intune tenant to gain access to the uploaded .intunewin package. The encrtypted .intunewin (located in the content folder) can be distributed safely to the Intune back-end services responsible for content distribution without getting exposed to others, only the tenant who uploaded the file has the EncryptionInfo and can decrypt the file.

Where can I find details of the Win32 App deployment?

The Intune Management Extension tracks some details of the Win32 apps in the registry at: HKLM\SOFTWARE\Microsoft\IntuneManagementExtension\Apps\

IntuneWin32AppDetailsRegistry

The yellow highlighted names provide us the execution commands for install and uninstall and the attempts and results of it.

If we look at the green highlighted GUID at HKLM\SOFTWARE\Microsoft\IntuneManagementExtension\Win32Apps\73d664e4-0886-4a… we can see it matches my Azure AD user object ID. To compare it I got the details via Azure AD PowerShell:

GetAzureADUser

The red highlighted GUID (b0f62b79-e464-4f95-afe2-ed99eb612fe5) is the application GUID which is assigned by the Intune service back-end. This can be easily traced by looking at the graph data with the Graph Explorer from Microsoft:

https://developer.microsoft.com/en-us/graph/graph-explorer

In my example I used this to retrieve the details of my uploaded app:

https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/b0f62b79-e464-4f95-afe2-ed99eb612fe5

This is the detailed information about the uploaded Win32 .intunewin app:

{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#deviceAppManagement/mobileApps/$entity",
    "@odata.type": "#microsoft.graph.win32LobApp",
    "id": "b0f62b79-e464-4f95-afe2-ed99eb612fe5",
    "displayName": "Google Chrome (new)",
    "description": "Google Browser",
    "publisher": "Google",
    "largeIcon": null,
    "createdDateTime": "2018-09-21T09:38:11.4343585Z",
    "lastModifiedDateTime": "2018-09-21T09:39:16.2037636Z",
    "isFeatured": true,
    "privacyInformationUrl": null,
    "informationUrl": null,
    "owner": "",
    "developer": "",
    "notes": "",
    "uploadState": 1,
    "publishingState": "published",
    "committedContentVersion": "1",
    "fileName": null,
    "size": 52284512,
    "installCommandLine": "msiexec /i \"GoogleChromeStandaloneEnterprise64.msi\" /q",
    "uninstallCommandLine": "msiexec /x {A4BC9C54-4589-3A4C-8217-9FA00262F471} /q",
    "applicableArchitectures": "x64",
    "minimumFreeDiskSpaceInMB": null,
    "minimumMemoryInMB": null,
    "minimumNumberOfProcessors": null,
    "minimumCpuSpeedInMHz": null,
    "msiInformation": null,
    "setupFilePath": null,
    "minimumSupportedOperatingSystem": {
        "v8_0": false,
        "v8_1": false,
        "v10_0": false,
        "v10_1607": true,
        "v10_1703": false,
        "v10_1709": false,
        "v10_1803": false
    },
    "detectionRules": [
        {
            "@odata.type": "#microsoft.graph.win32LobAppProductCodeDetection",
            "productCode": "{A4BC9C54-4589-3A4C-8217-9FA00262F471}",
            "productVersionOperator": "notConfigured",
            "productVersion": null
        }
    ],
    "installExperience": {
        "runAsAccount": "system"
    },
    "returnCodes": [
        {
            "returnCode": 0,
            "type": "success"
        },
        {
            "returnCode": 1707,
            "type": "success"
        },
        {
            "returnCode": 3010,
            "type": "softReboot"
        },
        {
            "returnCode": 1641,
            "type": "hardReboot"
        },
        {
            "returnCode": 1618,
            "type": "retry"
        }
    ]
}

Intune Management Extension Agent working folders?

If we have a look at the file system at:

C:\Program Files (x86)\Microsoft Intune Management Extension\Content

we can find some interesting folders where the agent is downloading the content and detection scripts and stores the actual MSI log files:

ItnuneWin32AppsFolders

Agent execution context when processing .intunewin files?

The agent is bound to the same restrictions during execution like we know them for PowerShell scripts, meaning the calling process (the Intune Management Extension agent) is a 32-bit process. This can result in some unexpected behavior. For example when trying to package a simple .ps1 file within a .intunewin and trying to execute cmdlets in this scripts which are only available in a 64-bit environment. We then have to use a technique to restart the PowerShell script as a 64-bit process. This is described in my previous articles and I have built a Intune PowerShell script template to simplify the start for this, which can be found on my GitHub here https://github.com/okieselbach/Intune/blob/master/ManagementExtension-Samples/IntunePSTemplate.ps1. This can also affect installers packaged as .intunewin when they assume a 64-bit environment!

If no restart technique is used keep in mind that environment variables must be handled with care from 32-bit processes. For example if you like to target the %windir%\System32 environment from a 32-bit process you have to use %windir%\Sysnative otherwise you will land in %windir%\SysWOW64. Visit the linkt to read more about the file system redirector: https://docs.microsoft.com/en-us/windows/desktop/winprog64/file-system-redirector

What are the retry and execution intervals?

The agent has default values for retry and execution:

  • max. of 3 retries
  • interval between the retries is 5 min.
  • max. execution run time is 60 min.

these limits are not adjustable at the moment.

Current Limitations (as per date Oct-2018)?

  • No user context app installation (added 22th of Oct)
  • No system context installation without user logged on (added 22th of Oct)
  • No dependency support
  • No supersedence
  • No support for Delivery Optimization
  • No integration into Enrollment Status Page, no wait for Win32 app installs
  • No notification suppression
  • No ordered installations, sequencing like .NET Framework install before App X
  • Current application size is capped at 2GB 8GB per app (changed 22th of Oct)

End to end usage walk-throughs?

Good walk-throughs on how to use the feature with the Intune portal and the end user experience can be found here:

Deploy Win32 Applications with Microsoft Intune
http://www.scconfigmgr.com/2018/09/24/deploy-win32-applications-with-microsoft-intune/

Deploy customized Win32 apps via Microsoft Intune
https://www.petervanderwoude.nl/post/deploy-customized-win32-apps-via-microsoft-intune/

Intune – sidecar for Win32 apps revealed
http://gerryhampsoncm.blogspot.com/2018/09/intune-sidecar-for-win32-apps-revealed.html

Intune Win32 App deployment – A gamechanger
https://ccmexec.com/2018/10/intune-win32-app-deployment-a-gamechanger/

Official documentation can be seen here

Intune Standalone – Win32 app management
https://docs.microsoft.com/en-us/intune/apps-win32-app-management

Remember to check back frequently as Intune progresses and the feature will be enhanced in short cycles. I will update the article accordingly.

Happy packaging, deploying and troubleshooting Win32 apps with Intune!

Automation of gathering and importing Windows Autopilot information

Complete process automation of gathering and upload of a device Autopilot information to the Windows Autopilot service with an Azure Automation Runbook.

AzureAutomation
On one of my previous blog post Gather Windows 10 Autopilot info in azure blob storage during wipe and reload, I described the gathering of Autopilot information during operating system deployment in a wipe and reload scenario with MDT. Just a short recap of the problem and my initial solution:

If we purchase a new device, the OEM vendor takes care of installing Windows 10 with a signature edition or provisioning ready installation including all necessary drivers. If we buy new hardware the information for Autopilot can be synced into our tenant from the OEM vendor (Lenovo is already capable of doing that and others will follow). We will get the device information in Intune and we can start to assign an Autopilot Deployment Profile and start to enroll the devices.

What if we have a bunch of Windows 7 devices in the environment?

A way to handle this is that we are playing the role of the OEM vendor and do the install of a Windows 10 signature edition on the existing Windows 7 devices, gathering Autopilot information, and let Windows 10 start in the Out of Box Experience (OOBE) again for user enrollment. Depending what is available we can use ConfigMgr or MDT for this. My example uses MDT.

Now imagine a situation where a rollout team is preparing a lot of machines. We would end up in a lot of .csv files on different devices. To make this a little easier for IT to import the hardware information of new devices into the Autopilot service, we build up the following logic:

  1. Gather hardware information via PowerShell Script Get-WindowsAutoPilotInfo during wipe and reload
  2. Upload .csv file via AzCopy to an Azure Blob Storage
  3. Gather .csv files from Azure Blob Storage and combine them into a single combined.csv file
    This was a manual step in my previous solution
  4. Upload combined .csv file to Autopilot and assign Deployment Profiles
    This was a manual step in my previous solution
  5. Device can be delivered to the end user like it where shipped by the OEM vendor

You can read more about the initial solution here: Gather Windows 10 Autopilot info in azure blob storage during wipe and reload

This blog post is all about automating these two steps – gathering and upload of Autopilot information to Intune.

Architecture

First, I will explain the architecture and how it works and then I’m going to describe the way to implement it. The overall architecture is based on an Azure Automation Runbook and looks like this:

AutoPilotImportArchitectureOverview

The new procedure including the enhanced logic for a complete automation of the import is now as follows (modified steps for complete automation):

  1. Gather hardware information via PowerShell Script Get-WindowsAutoPilotInfo during wipe and reload scenario
  2. Upload .csv file via AzCopy to an Azure Blob Storage
  3. Gather .csv files from Azure Blob Storage and combine them into a single .csv file with the help of a scheduled Azure Runbook
  4. Upload combined .csv file information to Windows Autopilot Service via PowerShell Script WindowsAutoPilotIntune running in an Azure Automation Runbook 
  5. Cleanup Azure Blob Storage (delete all .csv files of successfully imported devices and delete all .csv files of already imported devices)
  6. Generate import notification and summary and post it to a Microsoft Teams channel
  7. Autopilot information is available for the OOBE user enrollment scenario with Autopilot. The Autopilot profile gets automatically assigned by a dynamic AzureAD device group membership.
  8. Device can be delivered to the end user like it where shipped by the OEM vendor

I’ve still chosen the copy via AzCopy of individual .csv files to the Azure Blob Storage approach as we can then limit the access quite well via shared access signature and we can easily limit permission to write blob objects only. No need to provide credentials or Blob Storage keys on the client side. Sure, we could build up a more advanced HTTP endpoint to gather device information, but this approach is quick and simple. I’m pretty sure that the complete solution of this automation task is something which we do not need in future when devices are Windows 10 migrated and we then buy Autopilot ready hardware only.

 

Guide to build the new solution

The Autopilot Graph API is an API with focus on batch processing. This means we import new device information into a kind of staging area and Windows Autopilot service will pick up the new device information and starts importing it. This process varies in the amount of time it takes and we have to check the status of all devices to get the import result. As soon as the devices are processed we can clean up the staging area and the import is done. Normally we would do this by wrapping the Graph API calls (REST) into some PowerShell functions and build the logic for the described process. Luckily Microsoft released a new PowerShell Module WindowsAutoPilotIntune (thx to @mniehaus) based on the Graph API to import new AutoPilot information into Intune.

In my previous blog post about Process automation for Intune and Azure AD with Azure Automation, I created a Runbook to delete old devices from Intune via Graph API and demonstrated how to do an unattended authentication within the Runbook. All the details how this can be achieved are explained there. Please follow the guide to setup the Azure Automation account. I use the same unattended authentication technique to utilize the PowerShell Module WindowsAutoPilotIntune to import the device information into Autopilot service in the following Runbook. Additionally, the Runbook is built to protect concurrent execution (thx to Tao Yang, I used his implementation for it) to ensure a sequential processing and to keep track of current running imports. If we would design this as a concurrent solution it would get much harder in terms of monitoring and reporting in the end. In addition, there is a max import of 175 devices into the staging area of the API which we are taking care of by limiting the Runbook import to 175 devices during one run.

If the requirements are implemented based on the previous blog post (especially the Intune native app and the automation service account) we simply need to extend the permissions to “Read and write Microsoft Intune configuration” of the automation account which was created during the setup of Process automation for Intune and Azure AD with Azure Automation in section Building the solution.

IntuneNativeAppPermission

For the concurrent execution protection, we need our automation credential to have Reader permission and for Blob Storage access we need Contributor permissions on the subscription. As the result we grant Contributor permission to the automation account:

SubscriptionPermissions

Finally, we can implement the complete Runbook which can be found on my GitHub account here:

https://github.com/okieselbach/Azure-Automation/blob/master/Import-AutoPilotInfo.ps1

The Runbook is written in PowerShell and follows the logic described in the beginning of this post – section architecture.

Create a PowerShell Runbook and paste-in the code.

CreateNewRunbook

PowerShellRunbook

To make sure the Runbook successfully runs we need to define some additional variables. I assume that the IntuneClientId and Tenant variable are defined as described in the previous blog post.

AzureAutomationVariables

Additional variables needed for Azure Blob Storage access:

ContainerName: <your-blob-storage-containername>
StorageAccountName:
<your-blob-storage-account>
StorageKey: <your-blob-storage-secret-key> * as encrypted variable

Additional variables needed for Microsoft Teams Channel notification:

SubscriptionUrl: <your-subscription-url>

The subscription URL can be found as shown below. Please do not copy the /overview end of the URL. The URL should end with the subscription GUID only (like highlighted):

AzureSubscriptionsUrl

TeamsWebHookUrl: <your-ms-teams-webhook-url>

Open MS Teams and choose a Channel where the Autopilot notification from the Azure Runbook should be displayed. Click on the three dots and choose Connectors:

TeamsConnectors

Look for Incoming Webhook and click Configure

incomingwebhook.png

Type in the details and upload a icon and click Create

ConnectorDetails

Finally copy the Webhook URL:

ConnectorWebHookUrl

Paste it into the Azure Automation variable TeamsWebHookUrl and set encrypted value to Yes

TeamsWebHookVarEncrypted

This is necessary to get Microsoft Teams notifications with some statistics and information to troubleshoot errors. Below is an example of an import notification in Microsoft Teams:

AutoPiloImportTeamsNotification

We have some statistics, detailed error list with device information and a link to the Runbook itself in Azure. All based on Adaptive Cards JSON code. This can be easily modified to fulfill personal needs. Have a look at the Adaptive Cards Designer (https://acdesignerbeta.azurewebsites.net) for experimenting with layouts and adjust the Runbook code.

 

Enhanced client-side script part

I my previous blog post about Gather Windows 10 Autopilot info in azure blob storage during wipe and reload I have described how to setup the Azure Blob Storage to gather the “<hostname>.csv” files during MDT operating system installation. Please follow the previous guide to setup the Azure Blob Storage and the integration in MDT.

I have an enhanced version of the gather script now which can be found on my GitHub account and is also shown below. The enhanced version does not have the dependency on AzCopy.exe (incl. dependency files) and Get-WindowsAutoPilotInfo.ps1 in the script directory. If they are not available, they are downloaded from an additional Blob Storage container named resources. The additional container resources must be created and the AzCopy.zip and Get-WindowsAutoPilotInfo.ps1 must be uploaded there to successfully run the script:

BlobStorageResources

BlobStorageResourcesContent

The enhanced Get-WindowsAutoPilotInfoAndUpload.ps1 version:

Replace ZZZZ with your Blob Storage account name and ZZZZ with your SAS signature in the script above. See here Delegating Access with a Shared Access Signature for more SAS signature details.

This version can also be used to be executed via Microsoft Intune Management Extension to run it on existing Windows 10 devices. It is possible to collect all targeted device information and the Runbook will import the device information. Already imported devices will be skipped. This way we can make sure every device is imported to the Autopilot service.

SideCarAutoPilotScript

 

Sample output of the Runbook

AutoPiloImportRunbookOutput

If a device information <hostname>.csv is successfully imported the .csv files will be deleted from the Azure Blob Storage. In case of an error it will be left there untouched but reported via Runbook output and Teams notification. There is one case where the Runbook will delete the .csv file from the Azure Blob Storage also. This is if the Runbook detects an error 806 – ZtdDeviceAlreadyAssigned. In that case we can delete the .csv as it has no consequences. In every other error situation someone needs to resolve the error manually. The Teams notification is only generated if there is some device information in the Azure Blob Storage. The normal procedure would be if operations gets an import notification it should check and in case of errors they should be resolved manually.

AutoPiloImportTeamsNotification

 

Important observations during testing

The Autopilot import via Graph API takes some time and it may also timeout. So, it is not a typical request response REST API in this case. Remember all device information is staged and then the devices are monitored for their success or error state. This led to situations where I had to wait up to 1 hour to get a successful import, even when the UI in Intune tells us it takes up to 15 min. So be prepared that it might take longer or fails during the run. I chose this approach as it is the same procedure as the Azure Portal does the import. In fact, we really automated the import process in Intune but did not invent a complete different process which might cause different problems of device tracking or concurrency and so on. Depending on the use case you can run the Runbook on a recurring schedule. My normal use case for this scenario is to support people who are preparing older devices when they are getting reinstalled from Windows 7 to Windows 10. If someone expects immediate availability of Autopilot information after such a reinstall this might be problematic as the API is not designed for this. Another fact is that the Autopilot Deployment Profile assignment using Intune does take some time also at the moment. I observed that it took several hours sometimes. I suggest to re-arrange the operational processes and hand out reinstalled Windows 10 devices after some additional time and not directly after reinstalling, as this increases the possibility that the Autopilot information is not imported and profile assigned yet.

To run the Runbook on a recurring schedule just go to the Runbook and add a schedule. The max recurrence is limited to every hour.

AddRunbookSchedule

Just define a schedule (max once per hour) and monitor the recent job overview if it works:

AzureRunbookSchedule

The schedule can even be enhanced by using a simple Logic App and not using the Azure Automation Runbook schedule at all:

LogicAppTriggerRunbook

Please see Stefan Strangers post for detailed instructions how to implement a simple Logic App to trigger an Azure Automation Runbook:

https://blogs.technet.microsoft.com/stefan_stranger/2017/06/23/azur-logic-apps-schedule-your-runbooks-more-often-than-every-hour/

In case something goes wrong I have created a second Runbook to clean up the staging area of the Autopilot Graph API. Get it from my GitHub account and run it in case of fatal errors where you might want to clean up the staging area:

https://github.com/okieselbach/Azure-Automation/blob/master/Cleanup-AutoPilotImportedDevices.ps1

Here is a sample output of the Cleanup Runbook:

CleanupRunbookOutput

Further information

Azure Logic Apps – Schedule your Runbooks more often than every hour
https://blogs.technet.microsoft.com/stefan_stranger/2017/06/23/azur-logic-apps-schedule-your-runbooks-more-often-than-every-hour

Preventing Azure Automation Concurrent Jobs In the Runbook
https://blog.tyang.org/2017/07/03/preventing-azure-automation-concurrent-jobs-in-the-runbook

Post notifications to Microsoft Teams using PowerShell
https://blogs.technet.microsoft.com/privatecloud/2016/11/02/post-notifications-to-microsoft-teams-using-powershell

importedWindowsAutopilotDeviceIdentity resource type
https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/resources/intune_enrollment_importedwindowsautopilotdeviceidentity

Autopilot profile assignment using Intune
https://blogs.technet.microsoft.com/mniehaus/2018/06/13/autopilot-profile-assignment-using-intune

Adaptive Cards Designer
https://acdesignerbeta.azurewebsites.net

I published the same article on SCConfigMgr in a more step-by-step guide version, meaning there are not so many cross references to my other articles:

Automation of gathering and importing Windows Autopilot information
http://www.scconfigmgr.com/2018/07/23/automation-of-gathering-and-importing-windows-autopilot-information

 

I hope this can increase your throughput on the way to an Autopilot Windows 10 modern management environment.

When someone finds bugs or problems with the solution let me know and leave a comment. I will do my best to fix them as it should be a reliable part during preparing old devices with Windows 10.

Blogger for SCConfigMgr.com

Recently I got the opportunity to join the team of awesome bloggers at SCConfigMgr.com.

SCMainLogoSmall

I’m  really honored as the team is really great, in fact they are highly respected in the community 🙂 and it’s a real pleasure for me to join the team. I’ve written my first blog post there about WinPE and wireless support, check it out:

Build a WinPE with wireless support
http://www.scconfigmgr.com/2018/03/06/build-a-winpe-with-wireless-support

Blog post summary:
The post describes the usage of WinRE as a replacement for the normal WinPE with some additional enhancements to get full wireless support. To provide user driven connection handling I built a .NET program (can be found on my GitHub site) to support the wireless setup process.

For the time being I do not plan to retire my newly started blog here at oliverkieselbach.com. I will need to figure out how to handle this new situation with two blogs but I’m sure I will find a good way to do that.

I can truly encourage everyone to check out SCConfigMgr.com as we have great articles around everything in the Enterprise Mobility and Security space.

Feel free to leave a comment and stay tuned for my next blog articles, thanks!

Use Delivery Optimization with DHCP Option on Pre-Windows 10 version 1803

The new Windows 10 Peer 2 Peer feature Delivery Optimization was enhanced by the setting to query DHCP option ID 234 to get a Group ID (DOGroupIdSource). It was implemented into the latest Windows 10 version 1803 based on my feedback. I’m a little proud that the idea was well received and my product feedback was implemented as new option within six month.

It was officially announced with Windows Insider Preview 17063. It can be read here: https://blogs.windows.com/windowsexperience/2017/12/19/announcing-windows-10-insider-preview-build-17063-pc/#zluZyU1FMlLMzU6h.97 in the section New features for IT Pros in Delivery Optimization.

I’ve written a blog post how Delivery Optimization works and how to use this feature with new Windows versions 1803+, see here: Configure Delivery Optimization with Intune for Windows Update for Business.  It’s really great to have it with Windows 10 version 1803+ but I need to provide this functionality to older Windows 10 versions (1703 and 1709) also. I want to use this very good and flexible grouping capability from now on for all devices even pre-version 1803.

Therefore I have designed a solution to provide this functionality to older versions of Windows 10 and switch over to the native Windows 10 implemented solution as soon as the device gets upgraded to 1803.

First problem is to get the DHCP option from the DHCP server. Luckily I’ve written a small C++ program to do that in the past. I’ve written it to even work with a WinPE environment. The small binary can be used to send the DHCP option ID x request. When executed the binary expects a result as a string value. Below an example:

DhcpOption.exe <OptionID>

DhcpOptionExeExample

It’s even possible to specify a debug switch

DhcpOption.exe <OptionID> [debug]

to get debug output for troubleshooting:

DhcpOptionExeDebugExample

The C++ solution can be found on my GitHub here:
https://github.com/okieselbach/Helpers/tree/master/DhcpOption_VC2017

The compiled x64 binary DhcpOption.exe can be found here:
https://github.com/okieselbach/Intune/tree/master/ManagementExtension-Samples/DOScript

With this little helper we can use the Intune Management Extension and design a PowerShell script to:

  1. query DHCP server for the Option ID 234
  2. write result (Group ID) to the registry

Prerequisite is an available Option ID 234 on the DHCP server. See Configure Delivery Optimization with Intune for Windows Update for Business how to configure the DHCP server for this.

To support travelling users we need to make sure the device will query the DHCP server from time to time to get the group ID belonging to the current DHCP scope the client is using. For this we schedule the script as a scheduled task and run it at logon and on every unlock of the workstation.

The Delivery Optimization service will query the registry value for every new request and this makes sure a client uses the Group ID delivered by DHCP from the particular site.

To make sure we do not interfere with the native implementation starting with 1803 I implemented a logic to disable the scheduled task and remove the registry key as soon as Windows 10 version 1803, a build greater than 16299 is found.

The DhcpOption.exe will be provided from an Azure Blob Storage account. We need to create a Storage account as type Blob storage and a container to store the DhcpOption.exe. Then we use the provided download link in the script to get the binary during install. A guide how to create the Azure storage account is included in my article Gather Windows 10 AutoPilot info in Azure Blob Storage during wipe and reload. You don’t need the SAS token, just the container and upload DhcpOption.exe. The SAS token is only needed to support POSTS methods for uploading new files to the blob storage. The DhcpOption.exe is uploaded once and then provided for download only.

With this solution we can use the DHCP option for all Windows 10 version until 1803 and as soon as we get upgraded we disable and remove our self and the native implementation takes over. To make sure the native implementation is used as soon 1803 is found we need to implement the new DO settings (DOGroupIdSource). Follow my guide in my previous post for this Configure Delivery Optimization with Intune for Windows Update for Business.

Source Code of the full custom DO install script Register-DOScript.ps1 can be found on my GitHub Gist here:

Make sure to replace the Azure blob storage URL with your own one!

The uninstall script Unregister-DOScript.ps1 is also available via my GitHub Gist:

If we combine the script with Intune Management Extension we can easily deploy this solution to our Azure AD joined modern managed Windows 10 pre-version 1803 devices.

We will get the files for scheduling at C:\ProgramData\CustomScripts

DOScriptProgramDataFiles

and the scheduled task RunCustomDOscript

DOScriptScheduledTask

with task trigger logon or unlock of workstation

DOScriptScheduledTaskTrigger

when executed we will get the Group ID from the DHCP server

MD DHCP Group ID Option in Scope

and the received string is written to the registry

DOGroupIdRegistry

for me this bridges the time until all devices are upgraded to Windows 10 version 1803 and gives me enough flexibility to group my devices to achieve maximum Delivery Optimization benefits.

Happy caching!