I’m proud and I feel so honored to be awarded as an MVP – Enterprise Mobility today on the 1th of August 2018. It’s such a great community and I’m really looking forward to participate in the MVP program, interact with the product groups to get clarification on product features and to provide feedback. Despite this very big honor, all this wouldn’t be possible without all my followers and blog readers. A big thank you to you all! It’s a pleasure to share my knowledge and getting great feedback and all the discussions about various problems with you all.
I want to give my family a big thank you ❤ as they are giving me the chance to do all this work (often in the after hours). They are supporting me during my night shifts when I try new things or writing articles.
Also I need to thank my employer as he fully supports me on this journey. Without his support it wouldn’t be possible to do some parts of my contribution, thank you!
Sharing is everything, that’s how our society generates knowledge and value. I can truly encourage everyone to participate and start sharing in the IT community. Even a small post or answer in a forum about a simple fact, which you think might not be worth to publish, can help others to accomplish their goals or even drive new solutions which often find their way back to the community as well. You never know, maybe you who is currently reading this will be the next MVP :-).
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:
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.
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:
Finally, we can implement the complete Runbook which can be found on my GitHub account here:
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.
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.
Additional variables needed for Azure Blob Storage access:
StorageAccountName: <your-blob-storage-account> StorageKey: <your-blob-storage-secret-key> * as encrypted variable
Additional variables needed for Microsoft Teams Channel notification:
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):
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:
Look for Incoming Webhook and click Configure
Type in the details and upload a icon and click Create
Finally copy the Webhook URL:
Paste it into the Azure Automation variable TeamsWebHookUrl and set encrypted value to Yes
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:
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.
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:
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.
Sample output of the Runbook
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.
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.
Just define a schedule (max once per hour) and monitor the recent job overview if it works:
The schedule can even be enhanced by using a simple Logic App and not using the Azure Automation Runbook schedule at all:
Please see Stefan Strangers post for detailed instructions how to implement a simple Logic App to trigger an Azure Automation Runbook:
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:
Here is a sample output of the Cleanup Runbook:
UPDATE: There is a way to gather AutoPilot profiles for existing devices in the meantime (see screenshot below), but this would not give you the AutoPilot Experience during initial setup.
I enjoyed some vacation and did a lot of customer work recently but I had the honor to speak at our SCConfigMgr.com Modern Management Summit 2018 event in London at the Microsoft Reactor. It was an amazing day and I had a lot of discussions around a cloud first modern management approach. If you like to read something about it my colleague Maurice Daly wrote a very good wrap-up blog post about it. You can even find the presentation slides online there. It was great to meet you all and I’m happy to speak at the next one sometime.
Recently Microsoft enhanced the Intune Managed Browser experience with Mobile Application Management (MAM) and app-based Conditional Access (CA) a lot. It is integrated into the Conditional Access story as an approved app and supports the Azure AD Application Proxy very well now.
What does this allow us to do now?
We are now able to design a solution to publish our internal websites externally with minimal effort and then allow access to it from our mobile devices only by the Intune Managed Browser protected by Intune app protection policy. This ensures the information is safeguarded in our containerized Intune MAM solution. This gives most companies enough trust to actually do the publishing of internal resources for usage on mobile devices and support the bring your own device (BYOD) solution.
Please read the How does Application Proxy work? documentation from Microsoft to get a better understanding what we are going to do in the next section with the Azure AD Application Proxy. The Azure AD Application Proxy architecture is shown in the figure below:
One of the nice things is it will not require us to open up any inbound firewall ports. As long as we are allowed to make outbound connections we can publish internal websites easily to external. The solution even supports various authentication scenarios inclusive Single Sign-On (SSO).
Here is a walkthrough of a demo setup to show it in action
The walkthrough of the demo scenario should get you a deeper understanding of the new possibility. Assuming we have some internal websites e.g. intranet and expenses and they are available in the internal network only. To simulate that, I have setup an IIS server hosting the two simple websites, intranet and expenses within a private network. They are reachable on the IIS server via http://localhost for intranet and http://localhost:81 for expenses. In addition I have a link from intranet pointing to expenses website (link target is: http://localhost:81, compare screenshot with html source code). I built the two demo sites to also demonstrate link translation with Azure AD Application Proxy later on.
How do we get the internal websites published now?
First of all we need to switch off the IE Enhanced Security Configuration on the Windows Server otherwise we are not able to complete the login prompt of the Azure AD Application Proxy during setup procedure. Then we are downloading the Azure AD Application Proxy on our demo IIS server and run the msi installer. It’s a very lightweight installer and the only thing we need to provide is the Global Administrator credential during setup to finish the process.
The next step after installing the connector is to enable it by clicking Enable application proxy. After it is enabled the UI switches to “Disable application proxy” (shown in screenshot as step 3). Once enabled we have the Connector group default and our server listed there. It is possible to install more then one connector and build connector groups to support better reliability of the publishing (in fact this is recommended). The connector does not need to be installed on the IIS as I have done it in my demo setup, it should be on a dedicated Windows Server 2016 for example. I needed to run it on the IIS for simplicity of my setup and to use the internal address of http://localhost during publishing later on.
Now I can open up my published intranet from external and the intranet link originally pointing to http://localhost:81 was replaced by the application proxy because we enabled link translation on the application body (compare screenshot below). This works only if we publish both websites as the application proxy must find a published website for http://localhost:81 to do the translation.
In a real world implementation I would recommend to use a custom domain for publishing to maintain your links. For example if we have mydomain.com as Active Directory (AD) and I publish via Azure AD Application Proxy with the custom domain mydomain.com I can reach the website internally and externally with the same URL. To set this up follow the instructions here:
Securing our Intune mobile apps with Intune application protection policies
Now we need to add a MAM policy – app protection policy to secure the Intune Managed Browser and Mobile Outlook. To do that we open Intune > Mobile apps > App protection policies > Add a policy
After adding the policy we make sure Outlook and the Managed Browser is in the targeted apps and of course we adjust the individual Policy setting to meet our corporate standard and to realize the containerization (e.g. let apps only transfer data to other managed apps, encrypt data and so on…).
For the policy setting we need to make sure the setting Restrict web content to display in the Managed Browser is set to Yes. This makes sure internal links in emails are opened in the Intune Managed Browser. Even better because of the Azure AD Application Proxy publishing we make sure that internal links get translated and opened successful in Intune Managed Browser. We will do that by assigning an additional app configuration policy in the next step.
As last configuration we assign the app protection policy to our AAD user group we want to target.
To configure the Intune Managed Browser to work hand in hand with the Azure AD Application Proxy and translate internal URLs to the published URLs we need to configure an app configuration policy for the managed browser.
Now the important piece of configuration is to configure:
The screenshot below does not display the complete string!
Again as last configuration we assign the app configuration policy to our AAD user group we want to target.
Controlling access to the internal websites with app-based Conditional Access
Now we need to make sure our internal published website can only be accessed by Intune approved apps which are protected by app protection policy.
To do that we create the following Conditional Access policy in Intune or in the Azure AD portal. We assign our AAD user group, target All cloud apps, and include iOS and Android devices, and select Browser and Mobile apps desktop clients
As access control we grant access for approved client apps by choosing the option Require approved client app
How about the user experience?
Everything is in place and we assume someone in the company sent us an internal link to the new intranet site http://localhost. We open up mobile Outlook on iOS in this example:
If we now click on the internal link, Outlook is configured to Restrict web content to display in the Managed Browser and will open the link in the Intune Managed Browser for us. The Intune Managed Browser is then instructed for AppProxyRedirection = true. This will redirect us to the external published URL instead of the internal URL as shown below and shows us the demo intranet site:
Even the link within the demo intranet site is translated and will open the published demo expenses website:
To make sure that the published intranet site is only accessible by the Intune Managed Browser we open up Safari and open the published intranet site by typing in the external URL and we will check if access if blocked:
As we see the access is blocked and we get a nice feedback to use the Intune Managed Browser instead and we can directly use the blue link button to open the Intune Managed Browser.
We have seen how to publish internal websites via Azure AD Application Proxy easily. Then we configured our mobile apps to use an Intune app protection policy and instructed the Intune Managed Browser to use Azure AD proxy redirection to translate internal links and open them successfully. We achieve protection of the published internal website to prevent data leakage.
My advice to all, give it a try and start to play with MAM and app-based Conditional Access as it might be a quick win for your company and finally allow the usage of BYOD as company data can be protected very well in this scenario.
Recently I got the opportunity to join the team of awesome bloggers at SCConfigMgr.com.
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:
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!