Azure Virtual Network, VPN and Azure Virtual Desktop Setup

I recently had to completely rebuild my home lab and this time decided to extend it into Azure as well. I wanted to take a moment to document what my lab in Azure currently looks like.

It’s not an extensive setup, with only a virtual network and a site-to-site VPN connection with my home lab and also the Azure Virtual Desktop side of things too. I’ve built, tore down and rebuilt the AVD host pools several times and will continue to do so.

Since all my resources in Azure are currently in one Resource Group the ‘Resource visualizer’ is perfect for this:

image

Of course, this doesn’t show my Intune and AutoPilot policies and profiles which I will document separately.

My lab at home is a super simple setup with a AD DC which has DNS, DHCP, RRAS and CA, a ConfigMgr/SCCM primary site. I also have a separate RRAS server with OpenVPN Cloud connector to simulate WAN/off-site access which is primarily used to test AutoPilot with AAD Join and domain access using WHfB.

Intune: Reproducing Windows Update Experience

With the integration of SCCM and Intune into Microsoft Endpoint Manager, Microsoft is trying to get us to adopt modern management practices for our Windows endpoints. The advent of hybrid working amid the global pandemic has validated this push for this modern management powered by the Cloud.

Microsoft have done an awesome job in helping with our Cloud journey from providing fantastic documentation to the likes of Settings Catalog in Intune. However, I feel they’ve dropped the ball in other aspects and could be doing better to help us with this transition.

Consider moving the Windows updates workload from on-premises SCCM to Intune. Consider the following screenshots which shows our deployment deadline and reboot behaviour configurations in SCCM.

image

image

Now if I were to move this workload to Intune, I am presented with this configuration experience:

image

Why couldn’t Microsoft provide with identical options in Intune so we can just get a quick win, safe in the knowledge that the end user experience is going to be the same? Wouldn’t that make it easier for us to switch to Intune?

The ‘admin experience’ of moving from SCCM to Intune in this case requires one to decipher what all these options mean from the documentation and trying to come up with something that follows the existing policies.

Also, consider the end user experience. Why do we have to follow the existing policies and behaviour? We have previously run a campaign of emails and articles on how the update rings and reboot behaviours work to educate our users so they’re not caught out by a reboot unexpectedly, for example. Ideally, I’d like the user experience to remain exactly the same as it was with SCCM.

So in the above example, what options have I chosen that will allow the user experience to remain something similar?

I’ve set the Automatic update behaviour to Notify download. Microsoft says “If the user takes no action, the update will not install until the deadline you have configured is reached”. The key bit here is “until the deadline”.

image

So I’ve enabled deadline settings and set the deadline for quality updates to 2 days and the Grace period to 1 day. This means the user has 2 days to install the updates after being notified and has 1 day to restart their device before it is forced.

image

As for the reminders, we have no options for recurring reminders every x hours. Instead what we have is the “Remind user prior to required auto-restart with dismissible reminder (hours)” option which is a one-time reminder that the user can dismiss. I’ve set this to 4 hours prior to restart.

And I’ve set the user to be notified 60 minutes before the restart which they cannot dismiss.

image

Windows 365 Research

I’ve missed the boat on signing up for a free trial of Windows 365. Microsoft says they have seen significant demand for Windows 365 and have had to pause the free trial program until they can provision additional capacity.  That’s probably a good thing as my focus is entirely on learning and implementing Azure Virtual Desktop (AVD).

image

I signed up to receive notification when trials resume and have also been saving links to reading and research materials from various sources, primarily from Twitter, which has an awesome technical community.

As always, my approach to learning a new technology is to research and read as much as I can on the technology to try and learn from the experience of community members to prepare myself in advance and only then do I start hands-on experimenting in labs and/or trials.

Since AVD will be taking up a lot of my time in the coming weeks I don’t foresee having the capacity to look into Windows 365 much. So for the time being I wanted to collate all the reading materials here in this post for easy access as and when I’m ready to do dive into the subject.

Choosing Between Windows 365 & Azure Virtual Desktop (AVD) | Nerdio (getnerdio.com)

Windows 365 Plans and Pricing | Microsoft

How to configure Windows 365 Enterprise in Microsoft Endpoint Manager (mindcore.dk)

Windows 365 planning guide | Microsoft Docs

Get started with Windows 365 Enterprise – Microsoft Tech Community

Step by step: How to deploy a Cloud PC with Windows 365 | 4sysops

Windows 365, your Cloud PC. What it is, how it works, and how to… | by Mechanics Team | Jul, 2021 | Medium

Get started with Universal Print and Windows 365 Cloud PC – Microsoft Tech Community

Create A Filter Rule For Windows 365 Cloud PCs Using Intune HTMD Blog (anoopcnair.com)

Troubleshoot Windows 365 Business Cloud PC setup issues – Microsoft 365 admin | Microsoft Docs

Troubleshooting hybrid Azure AD errors during Windows 365 Cloud PC provisioning – Microsoft Tech Community

ConfigMgrDogs on Twitter: “For those trying out #Windows365 for Business, you need to sign-in to https://t.co/ALOoLgxd8h for cloud PC provisioning to complete. Signing up is not enough – login to kick off the provisioning!”

I will update this post with more links that I find interesting and worth reading.

Enable Azure Active Directory SSO for OpenVPN Cloud

This is the third post in the OpenVPN Cloud series aimed at lab use. In the previous post we created user accounts in the portal but in this step we are going to enable SSO with Azure so we don’t have to create users manually in the OVPN portal.

1) Log onto the OpenVPN Cloud by browsing to your portal URL. This is set to “YourOpenVPNID.openvpn.com”. Log in with the account you signed up with (owner account).

2) Go to Settings section and click on the User Authentication tab and click Edit on the top right corner.

image

Click on the Configure button under the SAML option

image

This will open a new window with the SAML configuration details. Keep this window open as you’ll need these details to setup SSO in Azure. 

image

Now to create an enterprise application in Azure.

3) Log onto Azure Active Directory and create a Group with members who you want to have access to OpenVPN Cloud.

4) Back on Azure AD click on the “Enterprise applications” blade on the left

image

Click on New Application

image

And then click on Create your own application.

image

5) Give you app a name and choose the option “Integrate any other application you don’t find in the gallery (Non-gallery)”

image

Click Create

6) Under Getting Started click on “Assign users and groups” and go ahead and add the group of users you created earlier

image

7) Click on “Set up single sign on” on the left and then click on SAML.

image

8) Click on Edit next to Basic SAML Configuration

image

9) Refer back to the SAML details provided in the window opened in the OpenVPN Cloud portal and enter the following details.

image

For Identifier enter the Issuer name.

For Reply URL enter the SSO URL provided.

image

10) Scroll down to SAML Signing Certificate and copy the App Federation Metadata URL. You’ll need this in a bit.

image

11) Now back on the OpenVPN Cloud page with the SAML details, click on Next

image

12) Enter a name for the identity provider and paste in the App Federation Metadata URL from the SAML Signing Certificate section of the enterprise app in Azure.

image

Click Next and Finish.

13) Head back into Settings and then click on User Authentication and click on Edit on the top right

image

Select SAML and click on the Update button and the Confirm.

That’s it. The next time you browse to your OpenVPN Cloud portal url you will see a Sign in button which will redirect you to the Azure sign in screen.

image

image

OpenVPN Cloud Endpoint Testing

So in the last post we stopped at the point of creating additional users who you want to give VPN access to. I also explained that the connection from the RRAS server to the OpenVPN Cloud counts as 1 out of 3 connections so you are left with two connections for your endpoints/users.

Now let’s get the connector installed on our endpoint device and our users connected.

Just a quick reminder that the portal address is “youropenvpncloudId.openvpn.com”

1) Log into your OpenVPN web portal and add your end users. Note that the email address here must be valid for this to work. Since this is intended for lab use just enter your own for testing.

image_thumb[10]

2) Build a VM with Windows 10 installed. The virtual network adapter must be connected to the Internal switch to join the domain and verify you can ping the domain and maybe access a file share or something.

3) Now change the virtual network adapter so it’s bound to the External switch instead to simulate offsite access. Verify that you cannot ping the domain.

4) On this VM log into the email address you provided for the user which has instructions on how to install the connector app. It’s just a standard MSI so go ahead and install it.

image

5) Open the connector app and enter your OpenVPN Cloud portal URL.

image

Sign in with the user account ID you created earlier.

Choose your Region and click Connect. It should only take a few seconds to establish the VPN connection.

image

Verify that you can ping your domain and access file shares.

You can, of course, install the endpoint connector on a physical domain-joined laptop and connect to your Wi-Fi (assuming your lab is on a Internal/Private network) to test this.

In the next post I will go through instructions on how to enable Azure SSO so you don’t have to manually create the user accounts in the portal and better simulate a production environment.

Split Tunnel VPN with OpenVPN Cloud

I wanted to set up VPN split tunnelling for my home lab to simulate my workplace (which uses a commercial VPN solution ) to build a proof of concept of Windows AutoPilot with on-premise domain access from Azure AD joined devices.

I came across OpenVPN Cloud which provides you with 3 connections for free/personal use.

A couple of notes on my setup:

My host server has one Internal switch and one External switch. I installed RRAS on a Windows server VM in my lab running Windows Server 2016 with two virtual network adapters, one connected to the Internal switch and the other on the External switch. Both have DHCP enabled. Now for the instructions:

1) Install RRAS on a Windows server box (mine is on Windows Server 2016) with two virtual network adapters as mentioned above.

2) Enable routing on the server and follow the rest of these instructions on this RRAS server

3) Sign up for a free personal account and create your OpenVPN ID.

image

3) Once logged in, click on Networks on the left and and select “Remote Access” and follow the rest of the wizard.

image

Give your network a name.

Give the connector a name and select your region. This is the RRAS server on which you will have a OpenVPN connector (agent) running.

Add your lab subnet under Private Subnets.

4) Next select where to deploy the connector. I chose Windows, downloaded the connector and installed it on my RRAS server mentioned above.

5) Open the OpenVPN Connector on the RRAS server, sign in with the user account/email address you signed up with and you should see a connection established.

image

Back on the OpenVPN web portal click Next and wait for the connection to be shown as established on that end too.

6) Next add your internal DNS to the web portal.

image

7) Click on Users and add a second user. This is the user who will be signing into VPN from an endpoint device over WAN/off-site. You can, of course, create more users here if you wish.

image

This completes the setup of the OpenVPN Cloud on the server side. I will soon write a follow up post on how to get endpoint devices connected via VPN.

Note that the connector which you installed on the RRAS server and the connection that it established with OpenVPN Cloud is counted as 1 of the 3 free connections you get with the free account. So you actually have only two connections for your endpoint devices. However, for quick POCs and lab use this is more than enough.

Windows 10 Upgrade: End User Experience

I worked really hard in designing the Windows-as-a-Service at my workplace. It’s been a great learning experience full of engineering and automation experience. A lot of superstars in the tech community have posted their upgrade task sequences and scripts and I wanted to write a post of my own specifically to demonstrate the user experience in the upgrade process.

What I have here is basically the guidance we’ve provided to our own staff which is perfect for the purpose of this post.

Introduction

Following our previous update, we’ve had a very successful run of the upgrade during our pilot phase and are now ready to roll this out to the rest of the organisation. 

The Windows 10 1909 Upgrade should be available to your device shortly; this will appear in the Software Centre. You will get a prompt advising you when the installation is ready. This is what it looks like:

When the upgrade is available for your device you will initially be notified by this prompt:

Install prompt

Install Reminders

You can either upgrade straight away if it is convenient for you or you can snooze the upgrade.

You will also be prompted every 2-3 hours reminding you that the upgrade is available for you to install. This will include instructions for you to follow and the deadline for the upgrade.

An example of this prompt is provided below:

image

Once you see this you can either go directly to Software Center from this window or follow the instructions below.

Deadline date: Please note that you will be given a deadline date to run this upgrade; if you fail to upgrade by this time, your system will perform a forced upgrade, which may cause interruption. Therefore, to prevent this we recommend you run this upgrade at your earliest convenience.  

Before you upgrade: make sure your laptop charger is plugged in and Global Protect is connected.

Instructions on how to install the upgrade:

1. Open Software Center from the Start menu (or type it into search bar once you click start).

Software center from Start Menu

2. Click on the “Operating Systems” tab. You will see “Windows 10 1909 Upgrade” listed here.

selecting upgrade option

3. Click on the ‘Install’’ button and confirm installation by clicking Install again.

Click to install

Confirm installation

Confirm installation

4. Software Center will then close automatically – do not be concerned by this, rest assured the upgrade will continue in the background.

Please do not unplug the charger and remain connected via Global Protect.

Note: It is recommended to start the upgrade at the end of the day or during lunch break and walk away to allow the upgrade to continue without any interruptions.

You will see a message on the lock screen advising you that an upgrade is in progress. Although not recommended. you can log back in again if you wish but please do not restart or shut down the computer until you are prompted to do so as mentioned below.

This is what the upgrade process will entail:

  • Installation will take place in the background and will take just over 90 minutes.
  • If working from home it is recommended to install the upgrade at the end of the day and walk away so the upgrade can continue without interruptions.
  • Once install has completed you will be prompted to restart your computer with a 15-minute countdown.
  • When prompted please close all open applications and click on the ‘Restart Now’ button.
  • If you do not click on “Restart Now” within the 15 minute window the computer will forcefully restart. Please DO NOT restart the computer using the power button or from the Start menu but instead click on ‘Restart Now’ in the prompt that will be displayed.
  • Your computer will restart and finish installing the upgrade. This part will take roughly 30 minutes during which you will not be able to log on. It is normal for the computer to restart a couple of times during this stage.
  • Once the lock screen displays a message that the upgrade was successful you can go ahead and log on. The first log on after the upgrade will take a couple of minutes longer than usual.

If you sign out or lock your computer:

You will see a message on the lock screen advising you that an upgrade is in progress which will look like this:

sign or lock your computer

You can log back in again and continue working. You’ll see this message when logging on while the upgrade is in progress:

Upgrade in progress

You can continue working until you see a prompt to restart as mentioned earlier.

When the upgrade is complete:

The message on the lock screen will change to say the upgrade finished successfully as shown below:

Upgrade complete

And when logging on you will see a message to say that your original lock screen wallpaper will be restored:

original lock screen wallpaper will be restored

Further support & information:

  • If the computer failed to upgrade, the lock screen wallpaper will display a message. You will still be able to login in and continue to work, despite the upgrade failure.
  • If this happens, please raise a ticket with Service Desk with ‘Windows10 upgrade fail’ in the subject line along with your device service tag in the description.
  • A member of the D&T team will be in touch, advising you of the failure and how to re-start the installation.

AppLocker Basics

I wrote a set of quick documentation on on AppLocker and it’s configuration for my workplace which I thought I’d post here. I’ve taken out anything specific to my workplace and here is the rest.

AppLocker Introduction

  • Define rules based on file path and file hash as well as rules based on file attributes such as the publisher, product name and file version.
  • Target these rules to specific security groups or individual users. You can also Exclude specific groups or users from the rule.
  • Create exceptions to rules such as blocking all applications exept winword.exe.
  • Use audit-only mode for what-if scenarios which are logged in EventViewer to analyse the impact should the rules be enforced

GPOs With AppLocker Rules

AppLocker rules are are configured in standard Group Policy Objects. This is where you would configure the rules in a GPO:

clip_image002

The following rules can be set using AppLocker:

Rule collection Note:
Executable Rules This is for allowing or denying .exe programs
Windows installer rules This is for MSI installers
Script rules This is for .vbs, .ps1, .cmd, .bat, etc
Packaged app rules This is for Universal Windows apps
DLL rules* This is for specific .dll files

*Note that the DLL rules node is not visible by default (as shown in the previous screenshot). To use DLL rules you have to enable it by right-clicking on “AppLocker” > Advanced tab > check “Enable the DLL rule collection”.

*If your script, exe or installer require the use of DLL files then you must also create rules for the DLL files in addition to the script/exe/installer.

Creating a AppLocker Rule

AppLocker rules are only configured in the Computer Configuration of a GPO but you can apply any rule to a specific group of users or set it to apply to the “Everyone” group.

It is recommended to create a set of default rules for each of the collection of rules. This is already done in the two GPOs that currently have AppLocker policies.

To create a rule for a executable right-click on “Executable Rules” under AppLocker and select “Create New Rule…”.

clip_image004

At this point you choose whether your rule is to Allow or Deny an executable from running. In other words, Allow is whitelisting an app and Deny is blacklisting an app.

You can also choose to apply this rule to a specific group of users by choosing an Active Directory security group or leave the default which is applied to the “Everyone” group.

clip_image006

You then have the option to choose a condition to meet to be able to apply this rule to an executable. Another way of looking at this is to work out how to identify the said executable. You have the following three options:

Condition/Option Notes
Publisher This will only work if the executable is signed by a software publisher. Alternatively you can sign the item using a certificate.
Path This tells AppLocker to expect to find the executable in a specific location. You can use wildcards for folder paths and filenames.

If the executable is moved a location which is not covered by a rule then the application will be allowed to run (since the executable was not in the path specified in this rule).

File hash If the application is not signed by a publisher then you can select the executable and AppLocker will generate a hash which uniquely identifies this executable.

If the executable is updated at any time in the future, the hash that was originally generated will not identify the updated executable. In this case the hash will need to be regenerated in AppLocker or else the rule will no longer apply to the application (since it has a different hash now).

For further information on understanding rule conditions consult the following Microsoft guide:

https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/applocker/understanding-applocker-rule-condition-types

Enforce Rule vs Audit Only

By default, the rules you create are set to “Audit only” which means the executables, scripts, etc will be allowed to run on client devices but everything will be logged in the Event Viewer so you can monitor what the behaviour will be like on a client device. The logs will say if the executables, scripts, etc was allowed to run or if it would have been blocked if the rules were enforced.

To enforce the rules you have to right-click AppLocker, select Properties and choose “Enforce rules” from the drop down under each rule collection:

clip_image008

Furthermore, it is necessary to understand how AppLocker rule collections are inherited in Group Policy. The following article can help understand inheritance:

https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/applocker/understand-applocker-rules-and-enforcement-setting-inheritance-in-group-policy

AppLocker Logs on Clients

You can examine the logs in the Event Viewer to troubleshoot AppLocker. The location of the logs are shown in the following screenshot:

clip_image010

To monitor the AppLocker logs on a remote computer you can use the following PowerShell code:

$Computer = “MNI-Win10PC”

Get-WinEvent -LogName “Microsoft-Windows-AppLocker/EXE and DLL” -ComputerName $Computer  # | Where-Object {$_.Id -eq 8004}

This will give you an output like the following:

image

Change the log name as appropriate (look this up in the Event View as shown in the above screenshot). This script will need to be run from your PAW device with your workstation admin account.

You can remove the # and filter the events to a specific event id. For example, event id 8004 is when something is blocked by AppLocker. For more information on event ids relating to AppLocker consult the following article:

https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/ee844150(v%3Dws.11)

Process for whitelisting an application

  • Ensure you understand what the application does and that it does not present any security concerns

  • Ask the customer if this is a one-time use application or meant for only one or two computers. If so then consider moving the application to one of the locations mentioned above

  • Ask if the computer and the app in question is only used by a group of users

  • Install the application on a test computer and try and run it

  • Check the AppLocker event log and see if the app requires additional dll/files which are also blocked

  • Make note of the exact path mentioned in the AppLocker logs

  • Whitelist the app as per the instructions above under the heading “Creating a AppLocker rule”. Make sure you use the “Allow” option to whitelist the application.

  • To only allow the application to a select number of users only then create a group of AD users and filter the AppLocker rule to the group.

  • Decide whether to Allow the application based on its location (path), publisher, or hash. Consider the pros and cons set out in the table above.

Windows AutoPilot Feasibility

I recently carried out a feasibility study on Windows AutoPilot with a view to replicate as much as we can from my current Windows 10 task sequence for our staff devices. This is a quick post with my findings and recommendations.

Requirements for AutoPilot:

Windows 10 1703 or higher

All of our Windows 10 computers are 1809 or higher.

Microsoft Intune license

Covered by our EM+S E5 license

Azure Active Directory Premium

Covered by our EM+S E5 license

Device registration in Intune

Devices will need to be registered by Dell (OEM) or CDW (Reseller). Dell will register devices for free but will charge £30 fee per device to remove bloatware.
We will need to find out from CDW if they provide a service to register devices to Intune/AutoPilot and what the associated costs are.

Azure Active Directory custom branding

Custom branding has already been done in our tenant

Azure Active Directory automatic enrolment

This will need to be configured. This allows users to enroll devices to Intune (the enrolment takes place as part of the device set up process). However, enabling this raises the question “can we enable this and stop users from enrolling their personal devices into Intune as well?

Configure Autopilot profiles

This is a collection of rules and configurations to set up the computer during the device set up process.

Source: https://docs.microsoft.com/en-us/windows/deployment/windows-autopilot/windows-autopilot-requirements

Replicating the Task Sequence in AutoPilot and Intune

AutoPilot will only make sense for the standard Staff build since it is designed to be handed over to the user who goes through a few simple steps and then Intune kicks in.

Step

Notes

Clean Windows image

Possible options include:

BIOS Configurations (password, secure boot, TPM, etc)

BIOS configuration will need to be set either from the OEM or will need to be done after the computer is handed over to the user.

All computers come with UEFI, Secure Boot and TPM activated. The only exception is password and UEFI Network Stack. UEFI Network Stack is only required for PXE-boot which we don’t need for AutoPilot. However, password will need to be set using ‘Dell Command Configure’ post-deployment.

https://ccmexec.com/2019/09/configuring-dell-bios-settings-using-intune-win32app-and-powershell/

BIOS Updates

Although the BIOS updates can be packaged as an application and deployed via Intune it would be easier to manage this using SCCM (to be updated after deployed)

Set computer name

Naming ‘patterns’ can be set in the AutoPilot configuration profile. The %SERIAL% macro but unsure how flexible this is (for example truncating Surface Pro serial numbers and adding WT).

https://blog.basevision.ch/2019/06/ultimate-guide-to-define-device-names-in-windows-autopilot-hybrid-join-scenario/

Join on-premise domain

By default AutoPilot computers join Azure Active Directory. To join on-premise AD as well then additional configuration need to be done to enable Hybrid Azure AD Join.

https://docs.microsoft.com/en-us/intune/windows-autopilot-hybrid

Also requires TPM 2.0:

https://docs.microsoft.com/en-us/azure/active-directory/devices/hybrid-azuread-join-plan

Move to correct OU

You can select an OU to move all computers to as part of the AutoPilot configuration profile.

Currently in our environment we move laptops and desktops to separate OUs, which we cannot do using AutoPilot.

Our options are:

  • Setup AutoPilot for laptops only (and continue using SCCM for desktops)
  • Move both laptops and computers to the same OU but maintain separate AD groups to identify computers by hardware

Add to AD security groups

Computers can be added to Azure Groups but not to on-premise AD groups. Since this functionality is not provided natively by AutoPilot a solution will need to be engineered.

Driver updates

Similar to BIOS updates this will need to be managed using SCCM post deployment.

SCCM Client

Options are:

  • Deploy as a win32 app from Intune
  • Rely on SCCM client push installation (AD object gets discovered and client is deployed to the machine

Local admin account

https://docs.microsoft.com/en-us/azure/active-directory/devices/assign-local-admin

https://www.risual.com/2019/02/05/managing-local-admins-with-intune-azure-ad-join-devices/

Install staff build applications

Win32 applications can now be deployed through Intune

https://docs.microsoft.com/en-us/intune/apps-win32-app-management

https://www.robinhobo.com/how-to-deploy-win32-applications-with-microsoft-intune/

Install Office 365

Can be packaged as a package and deployed through Intune or Office 365 apps can be “assigned” to devices/users using Intune

  • If we deploy this as a package then it will continue to be patched through SCCM
  • If assigned to devices/users then it will be patched directly from Microsoft thus will need more work to understand how this works

https://docs.microsoft.com/en-us/intune/apps-add-office365

OneDrive auto sign in

This is possible using Azure AD Join.

Redirect user folders to OneDrive

Options are:

  • OneDrive Known Folder Move
  • Group Policy redirect

(will rely on Hybrid Azure AD Join)

Install Symantec

Although this is possible to be deployed using a package moving to AutoPilot might be an opportunity to trial Windows Defender?

This will require more discussions and input form the of Information Security.

Applications chosen by service desk

This feature will NOT be available through AutoPilot.

Alternative options are:

  • We enable applications to be deployed to users so Service Desk deploys the applications to users in advance and when the computer is in SCCM the application will be deployed to the user. However, we will need to make sure the application can only be installed on one device when deployed to the user.

Apply Start Menu Layout

A custom Start menu layout can be applied using Intune

https://docs.microsoft.com/en-us/windows/configuration/customize-windows-10-start-screens-by-using-mobile-device-management

Enable BitLocker

BitLocker can be enabled using Intune

https://www.sepago.de/blog/bitlocker-how-to-configure-bitlocker-drive-encryption-on-windows-10-clients-in-microsoft-intune/

Further research is required to make sure BitLocker uses TPM and the recovery key can be stored in the computer object in on-premise AD.

Run PowerShell scripts (Corporate font, registry tweaks, etc.

PowerShell scripts can be run from Intune

https://matthewjwhite.co.uk/2019/06/13/deploy-custom-fonts-to-intune-managed-devices/

https://docs.microsoft.com/en-us/intune/intune-management-extension

https://albertneef.wordpress.com/2018/06/01/part-16-configure-microsoft-intune-powershell-scripts/

Launch TrustNet at log on

TrustNet shortcut can be deployed using a PowerShell script in a package and deployed to the device using Intune.

Recommendations

With the ‘work from home’ scenario amid the current pandemic there is a real need to provision end user devices for new staff and making the onboarding process as simple as possible without access to the office. It is therefore absolutely necessary to take this work a step forward and carry out a proof of concept of Windows Autopilot which has the potential of simplifying the end user device provisioning and onboarding process.

References:

https://docs.microsoft.com/en-us/windows/deployment/windows-autopilot/user-driven
https://www.scconfigmgr.com/2019/04/01/replicating-task-sequences-in-autopilot-part-1-bare-metal/

Windows 10 Upgrade – Write Metrics from Registry to WMI

Like many SCCM Engineers in the End User Computing field I am working (bent on backwards, might I add) on creating a process to managing the bi-annual Windows 10 upgrades which can be repeated at least once a year. In my current environment we’re still in the early stages of testing our first upgrade where we’re looking to upgrade our estate from 1703 to 1803.

There are many ‘community champions’ (as I like to call them in my head) who have been very generous in sharing their knowledge and skills with the rest of us and thus taking out a lot of the guesswork and helping us along the way. Personally I am often inspired by their ideas which ignites a passion to do better and improve my own processes.

Sometimes I take a solution shared by a community champion and I use it as it is. Other times I have to make modifications or adapt the solution or script to suit my requirements. One such script is the SetOSDInfo.ps1 script (now renamed to Set-ComboInfo.ps1) from Gary Blok over at Garytown.com.

Recently Gary asked on Twitter if anybody’s modified or extended his script and I happened to mention my changes when he said he’d like to see it. Therefore this blog post is for Gary and anybody else who may find the script useful.

Before I post the script I’d like to take a moment to note down my reasons for modifying it. The original script from Gary is intended to be run in an OSD, Compatibility Scan and In-Place Upgrade task sequences which writes metrics to the registry and WMI. As Gary mentions in his blog post the functions which writes the data to the WMI is originally from Jason Sandy’s Set-OSDInfo script. I know the script works for many people as it is but I just couldn’t get it to work for myself, specifically the New-WMIClass function which kept throwing errors which I could not get past. Therefore I had to look elsewhere for inspiration to replace the New-WMIClass function and ended up also removing the New-WMIClassInstane function altogether. Apart from this there were a few other reasons why I had to modify the script to suit my requirements.

Firstly, I use the tattoo script from Jorgen Nilsson to write metrics to the registry during OSD, which I am very happy with, so I had no need for the OSD metrics from the script.

As for writing metrics during Compatibility Scan and IPU task sequences I prefer to write my metrics to the registry using Run Command Line steps within the Task Sequences as shown below:

thumbnail

I prefer this because I can visually see what metrics are being written in the task sequence editor without referring to the script. Also, when the time comes for me to hand over the process to my successor (whenever that time comes) I think this will help reduce some of the complexities in the task sequence. (Side note: I always endeavour to leave an environment in a better state than how I inherited it in the first place.) If I’m already writing the metrics to the registry in the task sequence then I did not need any portion of the script which writes the metrics to the registry again. Instead, what I needed was to collate the (already written) metrics from the registry and write these to WMI.

Lastly, the script requires you to create new variables with a prefix (New-Variable -Name “$($AttributePrefix)$Value” -Value $ID) which allows you to call the Get-Variable cmdlet (like $customAttributes = Get-Variable -Name “$AttributePrefix*”) that gives you an array object which you can loop through and write the data to registry and/or WMI. In my case my variables (or metrics) already start with a prefix – “CompatScan_” or “IPU_” hence I got rid of the need for the $AttributePrefix variable from the script.

I could probably expand a bit more on the script but I’m in a bit of a rush, especially since I promised Gary I’d get the script out there days ago.

Taking inspiration from Gary and Martin Bengtsson I plan on sharing my Compatibility Scan and In-Place Upgrade task sequence with the tech community as soon as I have refined my processes.

For now, here’s the scrip. You can also download it from this link.

[cmdletBinding()]

Param(

        [Parameter(Mandatory=$false)][String]$Namespace=emeneye,

        [Parameter(Mandatory=$false)][String]$Class=“IPU”,

        [Parameter(Mandatory=$false)][String]$Key=“Build”,

        [Parameter(Mandatory=$false)][String]$Build=“1803”,

        [Parameter(Mandatory=$false)][String]$RegPath=“HKLM:\Software\emeneye\Win10_Upgrade\1803″

)

 

# References:

# https://home.configmgrftw.com/configmgr-osd-information-script/

# https://garytown.com/collect-osd-ipu-info-with-hardware-inventory

# https://social.technet.microsoft.com/Forums/office/en-US/233a5cb3-ed1c-47f8-a495-a271f225a24e/writing-a-custom-wmi-class?forum=winserverpowershell

 

Clear-Host

 

######################################################################################################################################################

## Functions ##

######################################################################################################################################################

 

Function Get-WMINamespace {

[CmdletBinding()]

       Param(

        [Parameter(Mandatory=$false,valueFromPipeLine=$true)][string]$Namespace

       ) 

    Begin {

              Write-Host “Getting WMI namespace $Namespace

    }

    Process {

        If ($Namespace) {

            $filter = “Name = ‘$Namespace‘”

            $return = Get-WmiObject -Namespace “root” -Class “__namespace” -filter $filter

        }

        Else {

            $return = Get-WmiObject -Namespace root -Class __namespace

        }

    }

    End {

        Return $return

    }

}

 

Function New-WMINamespace {

[CmdletBinding()]

       Param(

        [Parameter(Mandatory=$true,valueFromPipeLine=$true)][string]$Namespace

       )

 

       If (!(Get-WMINamespace -Namespace $Namespace)) {

              $newNamespace = “”

              $rootNamespace = [wmiclass]root:__namespace

        $newNamespace = $rootNamespace.CreateInstance()

              $newNamespace.Name = $Namespace

              $newNamespace.Put(| out-null

            

              Write-Host “Namespace $($Namespace) created.”

       } Else {

              Write-Host “Namespace $($Namespace) is already present. Skipping..”

       }

}

 

Function Get-WMIClass {

[CmdletBinding()]

       Param(

              [Parameter(Mandatory=$false,valueFromPipeLine=$true)][string]$Class,

        [Parameter(Mandatory=$false)][string]$Namespace = “cimv2”

       ) 

    Begin {

              Write-Host “Getting WMI class $Class

    }

    Process {

              If (Get-WMINamespace -Namespace $Namespace) {

                     $namespaceFullName = “root\$Namespace

 

            Write-Host $namespaceFullName

            

                     If (!$Class) {

                           $return = Get-WmiObject -Namespace $namespaceFullName -Class * -list

                     } Else {

                           $return = Get-WmiObject -Namespace $namespaceFullName -Class $Class -list

                     }

              }

              Else {

                     Write-Host “WMI namespace $Namespace does not exist.”

                     $return = $null

              }

    }

    End {

        Return $return

    }

}

 

Function New-WMI-Class {

[CmdletBinding()]

       Param(

              [Parameter(Mandatory=$false,valueFromPipeLine=$true)][string]$Class,

        [Parameter(Mandatory=$false)][string]$Namespace,

        [Parameter(Mandatory=$false)][System.Management.Automation.PSVariable[]]$Attributes,

        [Parameter(Mandatory=$false)][string[]]$Key

       )

  

    # Create the Class

    $newClass = New-Object System.Management.ManagementClass(“root\$Namespace, [String]::Empty, $null);

    $newClass[“__CLASS”] = $Class;

 

    # Add the Key property to the WMI Class

    $newClass.Properties.Add($($Key), [System.Management.CimType]::String, $false)

    $newClass.Properties[$($Key)].Qualifiers.Add(“key”, $true)

    $newClass.Properties[$($Key)].Qualifiers.Add(“read”, $true)

 

    ForEach ($Attr in $Attributes) {

 

        Set the property value to the attribute name

        $Property = $Attr.Name

 

        # Remove the attribute prefix and underscores from the property name

        $Property = $Property -replace “_”,“”

      

        If ($Property eq “(default)”) {

            # Do nothing

        } Else {

            # Add the property to the WMI class, but not the Key which is already added above

            If (-not (($Property eq $Key))) {

                # Add the property to the WMI Class

                $newClass.Properties.Add($Property, [System.Management.CimType]::String, $false)

 

                Set the Read qualifier to the property

                $newClass.Properties[$Property].Qualifiers.Add(“read”, $true)

            }

        }

    }

 

    $newClass.Put()

}

 

Function Get-PropertiesAndValuesFromRegistry {

[CmdletBinding()]

       Param

    (

        [Parameter(Mandatory=$true)][string]$RegPath

       )

 

    Begin { 

        Push-Location

    }

 

    Process {

        Set-Location $RegPath

 

        $AllPropertiesInRegPath = Get-Item $RegPath | Select-Object ExpandProperty Property

 

        $Object = New-Object TypeName PSObject

 

        ForEach ($Property in $AllPropertiesInRegPath) {

            Add-Member InputObject $Object MemberType NoteProperty -Name $Property -Value (Get-ItemProperty -Path $RegPath -Name $Property).$Property

        }

 

        return $Object.PSObject.Properties

      

    }

    End {

        Pop-Location

    }

}

 

######################################################################################################################################################

## Write CompatScan Return Code and Error Message to Registry ##

######################################################################################################################################################

 

If ($Class eq CompatScan) {

    $TSEnv = New-Object ComObject Microsoft.SMS.TSEnvironment

 

    [int64]$decimalreturncode = $TSEnv.Value(“_SMSTSOSUpgradeActionReturnCode)

    $hexreturncode = “{0:X0}” -f [int64]$decimalreturncode

    #[int64] $hexreturncode = 0xC1900210

 

    $WinIPURet = @(

    @{ Err = “C1900210”Msg = ‘No compatibility issues.’}

    @{ Err = “C1900208”Msg = ‘Incompatible apps or drivers.’ }

    @{ Err = “C1900204”Msg = ‘Selected migration choice is not available.’ }

    @{ Err = “C1900200”Msg = ‘Not eligible for Windows 10.’ }

    @{ Err = “C190020E”Msg = ‘Not enough free disk space.’ }

    @{ Err = “C1900107”Msg = ‘Unsupported Operating System.’ }

    @{ Err = “80070652”Msg = ‘Previous Install Pending, Reboot.’ }

    @{ Err = “8024200D”Msg = ‘Update Needs to be Downloaded Again.’ }

    @{ Err = “0”Msg = ‘Windows Setup completed successfully.’ }

    )

 

    $ErrorMsg = $winipuret | ? err eq $hexreturncode  | % Msg

 

    # Write hexreturncode and errormsg to registry

    New-ItemProperty -Path $RegPath -Name CompatScan_HexReturnCode -Value $hexreturncode PropertyType String -Force

    Set-ItemProperty -Path $RegPath -Name CompatScan_ErrorMessage -Value $ErrorMsg

}

 

######################################################################################################################################################

## Write Username to Registry ## – This is the username of the person who kicks off the installation from Software Center

                              ## – See Gary Blok’s post at # https://garytown.com/gather-user-account-name-during-ipu

######################################################################################################################################################

 

If ($Class eq “IPU”) {

    If ($TSEnv.Value(“_SMSTSUserStartedeq $true) {

        # Get the username from the task sequence variable, which is set earlier in the task sequence

        # $UserAccount =$env:USERNAME

        $UserAccount = $TSEnv.Value(IPU_UserAccount)

 

        # Write the username to the registry

        New-ItemProperty -Path $RegPath -Name IPU_UserAccount -Value $UserAccount PropertyType String -Force

    }

}

 

######################################################################################################################################################

## Collate metrics from registry and set variables ##

######################################################################################################################################################

 

$PropertiesAndValuesFromRegistry = Get-PropertiesAndValuesFromRegistry RegPath $RegPath

 

$PropertiesAndValuesFromRegistry | Where-Object {$_.Name -like $Class*”| ForEach-Object {

    Set the variable name and value

    $Name = $_.Name

    $Value = $_.Value

 

    # Remove the variable if it already exists

    Remove-Variable -Name $Name ErrorAction SilentlyContinue

  

    # Create new variable

    New-Variable -Name $Name -Value $Value

}

 

######################################################################################################################################################

## Create the WMI Namespace and Class if they do not already exist ##

######################################################################################################################################################

 

# Create WMI Namespace

If (-not (Get-WMINamespace -Namespace $Namespace)) {

    # Namespace does not exist, proceed to create Namespace

    Try {

        New-WMINamespace -Namespace $Namespace

    } Catch {

        Write-Host “Error creating WMI namespace $Namespace

    }

}

 

# Create WMI Class

If (-not (Get-WMIClass -Class $Class -Namespace $Namespace)) {

    # Class does not exist

  

    # Collate all attributes

    $Attributes = Get-Variable -Name $Class*”

 

    Now create the WMI class

    New-WMI-Class -Class $Class -Namespace $Namespace -Attributes $Attributes -Key “Build”

Else {

    # Class does exist

  

    If an instance already exists with the same Build then delete the instance

    $Instance = Get-CimInstance ClassName $Class -Namespace root/$Namespace | Select Build ErrorAction SilentlyContinue

    If ($Instance.Build eq $Build) {

        Get-CimInstance ClassName $Class -Namespace root/$Namespace | Remove-CimInstance

    }

}

 

######################################################################################################################################################

## Collate all variables and create a WMI Class Instance

######################################################################################################################################################

 

# Get all attributes

$Attributes = Get-Variable -Name $Class*”

 

# Create empty hash table

$PropertyHash = @{}

 

# Loop through each attribute and populate the hash table

ForEach ($Attr in $Attributes) {

    Set the attribute name and clean it up – underscores and dashes causes the Set-WMIInstance cmdlet to fail

    $AttrName = $Attr.Name

    $AttrName = $AttrName -replace “_”,“”

    $AttrName = $AttrName -replace “-“,“”

 

  

    Set the attribute value

    $AttrValue = $Attr.Value

 

    # Add the attribute name and value to the hash table

    $PropertyHash.Add($($AttrName),$($AttrValue))

}

 

# Add the key value

$PropertyHash.Add($($Key),$($Build))

 

# Remove the (default) property – this errors out otherwise

$PropertyHash.Remove(“(default)”)

 

Create new WMI Class Instance, i.e. populate the class

Set-WmiInstance -Path \\.\root\$($Namespace):$Class -Arguments $PropertyHash