Pulse Secure and the bells around it

So you have implemented Pulse Secure as a VPN-solution? Well, here are somethings that just about every consulting firm or department eventually will ask you to implement along-side it – so the Pulse Secure client works. The details aren’t here – but more the general mindset, what todo when – but not exactly how every scheduled task / script looks like. Hopefully this will guide you – and potentially arm you with possibility to fix more problems in the future. 

Triggers

First things first – a great way to trigger activity is based on Pulse Secure events – described more in detailed on their admin-guide. Common event-ids that might be useful are;

Disconnect – 302 / 106

Connect – 312 (not documented)

To create a scheduled task for any event – simply find a sample event a select to create a Scheduled Task;

image

In addition to Application and Services log / Pulse Secure, you can always find Microsoft-Windows-NetworkProfile/Operational. This contains great entries for what network you connect to, what network profile type of profile you connect to. Event-id 1000 within NetworkProfile is simply that you are connected to a new network as a sample.

Actions

What actions will be asked to run when these events happen?

Re-register DNS

Name-resolution for the helpdesk not quick-enough to connect to your laptop once you are connected? On event Connect (detected by Pulse Secure/Operational and the event id 312) you can simply have the scheduled task run the command-line – and this will mitigate some stuff. 

ipconfig /registerdns

DNS Suffix search list

Pulse normally appends the searchlist of the DNS-suffixes that are set on your client. It also fails to clean this up properly – so name resolution after a disconnect can be challenging. A tidy way todo this would be to trigger on a disconnect (primarily event id 302 within Pulse Secure, but also 106 might be applicable) and then do a sweep. Sample code;

$dnsCGSetting = Get-DnsClientGlobalSetting
$SList = $dnsCGSetting.SuffixSearchList
If ([string]::IsNullOrEmpty($SList) -or $SList -eq "OK.suffix")
{
#donothing
}
Else{
Set-DnsClientGlobalSetting -SuffixSearchList @('')
}

SuperUser has some options if you have a longer list of suffixes to handle..

Host-checker antivirus check

Did someone implement a host-checker and decided something needs to be up-2-date? Like antivirus definitions? Use trigger NetworkProfile with EventID to identify that a device has successfully connected to a network, and then do a check which network – and if not the corporate one – start up the processes to ensure users can avoid having a failed connect.

Sample functions (to check what network and do a validation of Defender AV Signature) – in VBscript as this was to be firing of quite heavily on all endpoints.

Private Function NetConnectionProfileName(Network)
	Dim objWMIService
	Dim colItems

	NetConnectionProfileName = False
	
	On Error Resume Next
	Set objWMIService = GetObject("winmgmts:\\.\root\StandardCimv2")

	Set colItems = objWMIService.ExecQuery("Select * From MSFT_NetConnectionProfile")

	For Each objItem in colItems
		if objItem.Name = Network Then 
			NetConnectionProfileName= True
		End if 
	Next

	Set objWMIService = Nothing

End Function

Private Function DefenderSignatureUpdate
	Dim objWMIService
	Dim colItems
	
	DefenderSignatureUpdate = False

	On Error Resume Next
	Set objWMIService = GetObject("winmgmts:\\.\root\Microsoft\Windows\Defender")

	Set colItems = objWMIService.ExecQuery("Select * From MSFT_MPComputerStatus")
	Wscript.echo "Definition-update: " & Left(objItem.AntivirusSignatureLastUpdated,8) & "Today: " & year(now())&right("00" & month(now),2)&right("00" & Day(now),2)
	For Each objItem in colItems
		if Left(objItem.AntivirusSignatureLastUpdated,8) = year(now())&right("00" & month(now),2)&right("00" & Day(now),2) Then 
			
			DefenderSignatureUpdate = True
		End if 
	Next



	Set objWMIService = Nothing

End Function

A similiar way within Powershell to detect a Domain-profile is posted on Twitter:

Windows Firewall profile doesn’t always switch to Domain when you use a third-party VPN client

This isn’t really an action. You could most likely trigger something off the event ids – however, Microsoft has documented a great article about this problem. Pulse allegedly fixed this in a really old-version of the client, but to this day noone is happy about it. Set the two workarounds as part of your baseline for devices connecting via VPN – and you should be good. Pulse (now owned by Ivanti) will not be fixing this it seems. As far as I can tell Pulse adds routes, and then doesn’t notify Windows in anyway that triggers a rediscovery for Domain-connections.

Microsoft states:

  • First, disable Domain Discovery negative cache by adding the NegativeCachePeriod registry key to following subkey: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters Name: NegativeCachePeriod
    Type: REG_DWORD
    Value Data: 0 (default value: 45 seconds; set to 0 to disable caching)
  • If issue doesn’t resolve, further disable DNS negative cache by adding the MaxNegativeCacheTtl registry key to the following subkey: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters Name: MaxNegativeCacheTtl
    Type: REG_DWORD
    Value Data: 0 (default value: 5 seconds; set to 0 to disable caching)

Force a new network-profile

Your client still skipping around and not getting stuff right? Well, you can switch over to a Private Profile. Public is by default, and Domain requires that a Active Directory is reachable (process documented in previous article from Microsoft) – but Private you can switch to.

See this one-liner to detect a specific network and switch the Network Profile

Get-NetConnectionProfile -Name 'networkname' | Set-NetConnectionProfile -NetworkCategory Private

Bitlocker and howto make your users love you

This is how you deploy Bitlocker and make everyone fall in love with you.

1. Don’t understand Bitlocker – and read some generic Microsoft guideline which provides you with a generic approach and doesn’t compensate for your lack of understanding

2. Don’t read Adams detailed walk-through, and in particular skip the section regarding PCR Settings if you are deploying this in a pre-Windows 10 / SecureBoot era.
(or the gist: enable PCR validation: 0, 1, 8, 9, 10, & 11 only  for legacy BIOS)

3. Really don’t make an effort to push this forward to a SecureBoot era where the annoyance for all users are minimal.

4. Don’t validate any hardware – any BIOS-versions, TPM versions or anything that could potentially have an impact on the experience of Bitlocker.

5. Don’t test anything and just assume that it works as all the guidelines that say you “must” do this will never have any negative impact (and is there a section which says impact? don’t read it)
(Interactive logon: Machine account lockout threshold should match your account lockout setting and also not be as low any given user will force the machine into recovery mode every single day)

6. If the a user is forced to provide a Bitlocker Recovery Key – don’t reset the platform validation data. Most likely the Bitlocker Recovery key will not show up during the next reboot.

7.  Make sure you configure stuff – especially things that contradict the initial setup state of Bitlocker. Future assumptions made by Microsoft will surely not impact you.

8. Sighs. Why? This seems to be required if using the Machine Account lockout threshold….

“Windows Settings” – “Security Settings” – “Local Policies/User Rights Assigment”

“Access this computer from the network” – “BUILTIN\Administrators” – append: “NT AUTHORITY\Authenticated Users”

Windows 10 20H2 and Edge

To start of this blog-post we have to set a few basics…

Image result for edge chromium

Windows 10 20H2 includes Edge Chromium. Specifically – Edge Chromium v84 (something something)

If you deploy Edge Chromium later version (like 87? 88?) to your 1909/2004 devices – and then upgrade to Windows 10 20H2, you will effectively downgrade Edge Chromium to 84.

As far as I can understand – the details, the workarounds and apparently a promise that this will be better is all published on Borncity.

The issue at hand though is that Edge Chromium is a moving target, so including even the latest version today in whatever upgrade process you have – will most likely in a months time have an older version than what you have installed on your endpoint. This is installer will bomb-out with “there is a newer version already installed”. Yet, the users will be stuck with Edge Chromium 84. I don’t quite get why this is the case. The previous version is installed, but version 84 always starts and you can’t upgrade because of this and for some reason Edge Chromium is special software that just doesn’t tag along?

Now, to workaround this you can read the information regarding the installed Edge Chromium (it is installed, just not running) and then perform an over-ride (REINSTALL=ALL REINSTALLMODE=A according to the comments on Borncity).

Let’s gather what we need to create something simplistic.

Function to retrieve installed software in Powershell. There are a bunch out there. You can use PSADT. I just found one that was small and did the trick. I can see so many problems with it – but it works. Unfortunately I have no idea where I stole this from. If you want todo properly – use PSADT. If I stole this from you – post a comment and I will remove it and post a link instead.

function Get-InstalledSoftware {
    <#
    .SYNOPSIS
        Retrieves a list of all software installed
    .EXAMPLE
        Get-InstalledSoftware
        
        This example retrieves all software installed on the local computer
    .PARAMETER Name
        The software title you'd like to limit the query to.
    #>
    [OutputType([System.Management.Automation.PSObject])]
    [CmdletBinding()]
    param (
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )
 
    $UninstallKeys = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
    $null = New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS
    $UninstallKeys += Get-ChildItem HKU: -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'S-\d-\d+-(\d+-){1,14}\d+$' } | ForEach-Object { "HKU:\$($_.PSChildName)\Software\Microsoft\Windows\CurrentVersion\Uninstall" }
    if (-not $UninstallKeys) {
        Write-Verbose -Message 'No software registry keys found'
    } else {
        foreach ($UninstallKey in $UninstallKeys) {
            if ($PSBoundParameters.ContainsKey('Name')) {
                $WhereBlock = { ($_.PSChildName -match '^{[A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}}$') -and ($_.GetValue('DisplayName') -like "$Name*") }
            } else {
                $WhereBlock = { ($_.PSChildName -match '^{[A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}}$') -and ($_.GetValue('DisplayName')) }
            }
            $gciParams = @{
                Path        = $UninstallKey
                ErrorAction = 'SilentlyContinue'
            }
            $selectProperties = @(
                @{n='GUID'; e={$_.PSChildName}}, 
                @{n='Name'; e={$_.GetValue('DisplayName')}}
            )
            Get-ChildItem @gciParams | Where $WhereBlock | Select-Object -Property $selectProperties
        }
    }
}

Second you need something to validate that MSIExec isn’t busy. Like the function Test-IsMutexAvailable from (you guessed it) PSADT. Come to think of it – you really should just throw everything I did in a garbage bin and rewrite it using the PSADT framework.

Third – here is the crude and basic logic of what we want to run after the Windows 10 20H2 upgrade is completed.

Consists of – arguments to run the re-install of Edge. Oddly – the latest version should have a ProductCode, but not version 84 which comes in the box. Then wait until MSIExec had its coffee. Once coffee is up – run the install…. Logging optional.

$args = "/i `"$((Get-InstalledSoftware -Name "Microsoft Edge").guid)`" /qn REINSTALL=ALL REINSTALLMODE=A"
Test-IsMutexAvailable -MutexName 'Global\_MSIExecute' -MutexWaitTimeInMilliseconds (New-TimeSpan -Minutes 5).TotalMilliseconds
$process = Start-Process -FilePath "msiexec" -ArgumentList  $args -wait -PassThru

Set-Content -Path c:\windows\temp\edge.txt -Value "Edge Exit code: $($process.exitcode) - Argument: $($args)"

This should run if the upgrade is successful. Not entirely sure how this works in reality – but Microsoft offers a success.cmd since 2004 so that would be a good idea to use.

NTUser.dat and last updated

Regardless what type of estate of Windows-devices, there always seems to be a need of clearing out unused profiles from a computer to save diskspace, increase performance and what not.

In Windows 7 there was an issue (resolved by a hotfix) that simply loading up a ntuser.dat file would change the timestamp of when it was last written to. It seems that this has now been the defacto default behaviour for Windows 10, and a long-running thread disusses different ways of adressing the issue – how can you identify if a profile was recently used on a device? Nirsoft tools (aren’t they great?) provide a great and easy to read overview if logon history based on security event logs. 

That seems tedious. Using the written time for the folder doesn’t seem to be accurate – and the risk of removing active user profiles is high. However, if one could track the last-write time for the registry entry for the profile – we should be good, right? Unfortunately – last write time for the registry entry isn’t there out of the box using Powershell (or VBScript etc). Seems to be a few things posted on Technet Gallery (to be gone soon) that will provide the missing piecies. 

Where are we looking? Right here;

image

Use the function Add-RegKeyMember, loop through all profiles and then filter any potential things you want to leave behind – and we should be able to clear out not so active profiles. A few dangerous lines commented out so you can copy and paste at will. 

function Add-RegKeyMember {
 <#
 .SYNOPSIS
Adds note properties containing the last modified time and class name of a 
registry key.

.DESCRIPTION
 The Add-RegKeyMember function uses the unmanged RegQueryInfoKey Win32 function
 to get a key's last modified time and class name. It can take a RegistryKey 
object (which Get-Item and Get-ChildItem output) or a path to a registry key.

.EXAMPLE
 PS> Get-Item HKLM:\SOFTWARE | Add-RegKeyMember | Select Name, LastWriteTime

Show the name and last write time of HKLM:\SOFTWARE

.EXAMPLE
 PS> Add-RegKeyMember HKLM:\SOFTWARE | Select Name, LastWriteTime

Show the name and last write time of HKLM:\SOFTWARE

.EXAMPLE
 PS> Get-ChildItem HKLM:\SOFTWARE | Add-RegKeyMember | Select Name, LastWriteTime

Show the name and last write time of HKLM:\SOFTWARE's child keys

.EXAMPLE
 PS> Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Control\Lsa | Add-RegKeyMember | where classname | select name, classname

Show the name and class name of child keys under Lsa that have a class name defined.

.EXAMPLE
 PS> Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall | Add-RegKeyMember | where lastwritetime -gt (Get-Date).AddDays(-30) | 
 >> select PSChildName, @{ N="DisplayName"; E={gp $_.PSPath | select -exp DisplayName }}, @{ N="Version"; E={gp $_.PSPath | select -exp DisplayVersion }}, lastwritetime |
 >> sort lastwritetime

Show applications that have had their registry key updated in the last 30 days (sorted by the last time the key was updated).
 NOTE: On a 64-bit machine, you will get different results depending on whether or not the command was executed from a 32-bit
       or 64-bit PowerShell prompt.

#>

    [CmdletBinding()]
     param(
         [Parameter(Mandatory, ParameterSetName="ByKey", Position=0, ValueFromPipeline)]
         # Registry key object returned from Get-ChildItem or Get-Item
         [Microsoft.Win32.RegistryKey] $RegistryKey,
         [Parameter(Mandatory, ParameterSetName="ByPath", Position=0)]
         # Path to a registry key
         [string] $Path
     )

    begin {
         # Define the namespace (string array creates nested namespace):
         $Namespace = "CustomNamespace", "SubNamespace"

        # Make sure type is loaded (this will only get loaded on first run):
         Add-Type @"
             using System; 
             using System.Text;
             using System.Runtime.InteropServices; 

            $($Namespace | ForEach-Object {
                 "namespace $_ {"
             })

                public class advapi32 {
                     [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
                     public static extern Int32 RegQueryInfoKey(
                         Microsoft.Win32.SafeHandles.SafeRegistryHandle hKey,
                         StringBuilder lpClass,
                         [In, Out] ref UInt32 lpcbClass,
                         UInt32 lpReserved,
                         out UInt32 lpcSubKeys,
                         out UInt32 lpcbMaxSubKeyLen,
                         out UInt32 lpcbMaxClassLen,
                         out UInt32 lpcValues,
                         out UInt32 lpcbMaxValueNameLen,
                         out UInt32 lpcbMaxValueLen,
                         out UInt32 lpcbSecurityDescriptor,
                         out Int64 lpftLastWriteTime
                     );
                 }
             $($Namespace | ForEach-Object { "}" })
 "@
     
         # Get a shortcut to the type:    
         $RegTools = ("{0}.advapi32" -f ($Namespace -join ".")) -as [type]
     }

    process {
         switch ($PSCmdlet.ParameterSetName) {
             "ByKey" {
                 # Already have the key, no more work to be done :)
             }

            "ByPath" {
                 # We need a RegistryKey object (Get-Item should return that)
                 $Item = Get-Item -Path $Path -ErrorAction Stop

                # Make sure this is of type [Microsoft.Win32.RegistryKey]
                 if ($Item -isnot [Microsoft.Win32.RegistryKey]) {
                     throw "'$Path' is not a path to a registry key!"
                 }
                 $RegistryKey = $Item
             }
         }

        # Initialize variables that will be populated:
         $ClassLength = 255 # Buffer size (class name is rarely used, and when it is, I've never seen 
                             # it more than 8 characters. Buffer can be increased here, though. 
         $ClassName = New-Object System.Text.StringBuilder $ClassLength  # Will hold the class name
         $LastWriteTime = $null
             
         switch ($RegTools::RegQueryInfoKey($RegistryKey.Handle,
                                     $ClassName, 
                                     [ref] $ClassLength, 
                                     $null,  # Reserved
                                     [ref] $null, # SubKeyCount
                                     [ref] $null, # MaxSubKeyNameLength
                                     [ref] $null, # MaxClassLength
                                     [ref] $null, # ValueCount
                                     [ref] $null, # MaxValueNameLength 
                                     [ref] $null, # MaxValueValueLength 
                                     [ref] $null, # SecurityDescriptorSize
                                     [ref] $LastWriteTime
                                     )) {

            0 { # Success
                 $LastWriteTime = [datetime]::FromFileTime($LastWriteTime)

                # Add properties to object and output them to pipeline
                 $RegistryKey | Add-Member -NotePropertyMembers @{
                     LastWriteTime = $LastWriteTime
                     ClassName = $ClassName.ToString()
                 } -PassThru -Force
             }

            122  { # ERROR_INSUFFICIENT_BUFFER (0x7a)
                 throw "Class name buffer too small"
                 # function could be recalled with a larger buffer, but for
                 # now, just exit
             }

            default {
                 throw "Unknown error encountered (error code $_)"
             }
         }
     }
 }


             $profiles = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Add-RegKeyMember | Select Name, LastWriteTime
             foreach ($p in $profiles) {
             
             if ($p.lastwritetime -lt $(Get-Date).Date.AddDays(-90)) {
                     
                     $key = $($p.name) -replace "HKEY_LOCAL_MACHINE","HKLM:"
                     $path = (Get-ItemProperty  -Path $key -Name ProfileImagePath).ProfileImagePath
                     $path.tolower()
                     if ($path.ToLower() -notlike 'c:\windows\*' -and $path.ToLower() -notlike 'c:\users\adm*') {
                         write-host "delete " $path
                         #Get-CimInstance -Class Win32_UserProfile | Where-Object { $path.split('\')[-1] -eq $User } | Remove-CimInstance #-ErrorAction SilentlyContinue
                         #Add-Content c:\windows\temp\DeleteProfiles.log -Value "$User was deleted from this computer."
                     }

                }
             }

EnableFastFirstSignin – howto set it up

EnableFastFirstSignin seems to be a semi-announced feature that is only possible to configure using a Provisioning Package. I think the official documentation states that it is in preview.

But, let’s not care about that

What is it?

This:

Is there  any documentation for this seemingly awesome black magic?

Yes, at Docs@Microsoft

How do I set it up?

Prerequisites

Windows 10 – 1809

Windows ADK – 1809 – Windows Imaging and Design Configuration

Brief overview of what you need todo;

· Configure Provisioning Package

· Generate Provisioning Package

· Install Provisioning Package

Configure / Generate package

Start Windows Imaging and Configuration Designer
Press to create Advanced Provisioning
Define the following settings:
Runtime Settings -> Policies -> Authentication ->

EnableFastFirstSignIn

Select Enabled

Runtime Settings -> SharedPC -> EnabledSharedPCMode

Select TRUE

Runtime Settings -> SharedPC -> AccountManagement -> AccountModel

Select Domain-joined only

Select Export -> Provisioning Package
Enter information regarding name of package, version. Information is arbitrarily set.

Owner is: IT Admin

Rank: 0 or 1

Press Next

Press Next
Select where to save the Provisioing Package

Press Next

Press Build
Press Finish

Install Package

Ensure you are using Windows 10 – 1809
Open an elevated Powershell prompt, using a local administrative account
NOTE: PackagePath is unique to the package name and environment you are working

Execute the following command to install:

Install-ProvisioningPackage -PackagePath “sample-name.ppkg” -QuietInstall

After this the awesome experience should be on whatever endpoint you installed this on. As far as I can tell all that remains is Group Policy Object processing.

What happens in the background?

What does this magical black box of awesomeness actually do in the background? Microsoft has little to reveal, however quite a few people have posted findings on Twitter so far

Trenteye seem to be digging into this further and this is what has been shared so far;

Windows 10–1709 and OneDrive UX

OneDrive receives some well deserved attention in every upgrade of Windows 10. The road to an exciting user experience is paved with some hiccups, but once 1709 came around – most of the quirks were sorted out.

OneDrive is still seen in the system tray as two clouds (blue for OneDrive for Business and white for the personal edition). Spotting them in the task manager reveals a different confusion still.

image

image

One of the advocated new improvements is the ability to maintain sparse-files, or pointers, or – well, the point beeing; the file is not on disk and will only be retrieved from the cloud when needed (or requrested). The ability needs toggling under the Settings for each cloud (business or personal) and named Files On-Demand (or this can be enabled via group policy per machine)

image

Technically Files On-Demand is an attribute set for each file. The state is called Pinned or Unpinned and can be toggled via the “attrib” command. No idea why they chose to maintain an older command rather than enabling this via Powershell only.
To make files available offline; attrib -U +P /s

image

The end-user isn’t required to know this and can toggle the options from  right-click option when selecting any file or folder. The names aren’t the same, but rather offer a more end-user friendly name.

image

Applying the Free up space option will clearly show the progress of altering the attribute by saying Applying properties….

image

As a way to educate the user and offer a clear view of the state of any folder (offline, online, issues or syncing) there is a new column added (as opposed to the previous overlay on the icons) named Status. Personally this seems to be an improvement (allows for sorting for example).

image

Remember the system tray icons? A bit more useful this time around – if you left-click them once the following status will be shown.

OneDrive for Business

image

OneDrive

image

The reason for this write-up is based on the experience of migrating all OneDrive content to OneDrive for Business. Odd thing once I was completed. (1) is the OneDrive for Business and (2) is OneDrive. For some reason the consumption of data differs with just above 100gb. Where did this go? The below is from settings of the OneDrive-client.

image

Comparing the folder on disk looks like this shows that both are roughly 237gb – so this seems odd. Verifying online via the Manage Storage button shows some correct numbers and verifiying basic folder structure from the web-interface provides some additional confidence. Accessing the OneDrive for Business via the admin-center for Office 365 and spinning up the Reports for usage – shows that 8mb is currently consumed?

Are there any more ways to confirm how much data I got?

image

As OneDrive for Business is essentially a glorified interface for Sharepoint – there are certain limitations defined. There are restrictions on files both in OneDrive and OneDrive for Business, however OneDrive for Business has far more annoying limitations. Sample output to resolve the 181 conflicts – a number far lower than expected to be honest.

image

Note to self: WMI does not adhere to RPC-standards

Every single project…. every single firewall guy, and every single requirement list that I had to dig into…

Windows Management Traffic leverages the same ports as RPC traffic (TCP 135 for initial connection, and after that a random port within a defined port-range), however it does not adhere to the RPC specification and will therefore not be correctly identified by any firewall (yes, any firewall) as RPC traffic. Most firewalls tries to dynamically identify the specific port for the session within the dynamic range, however this requires that lots of things are RPC and not MSRPC.

Cisco wrote it pretty clearly;

As Microsoft switched from using pure RPC to use DCOM (ORPC) calls, those non-epm calls will be used more and more. Windows RPC/DCOM services use the RPC Endpoint Mapper to accept initial communications on port 135 and then dynamically transition to ports for the service.

Just open all the high-ports.

Checkpoint statement

Cisco statement

Troubleshoot:

Testing RPC ports with PowerShell (and yes, it’s as much fun as it sounds)

Wireshark-article if you ever need to troubleshoot

 

Office 365, App-V and Windows 10 – 1607

Oh, the joy of reusing technology. Office 365 leverages the App-V technology, however slightly modified and primarily used as a delivery method with no isolation. App-V in comparision is used more for its isolation and to be honest – not so much for its delivery mechanism.

In addition – since Windows 10 – 1607 the App-V client is now a Windows Feature.

Now, for fun – lets try these steps;

  1. Install Windows 10 – 1607
  2. Install any version of Office 365 ProPlus

Find a Visio drawing and open it. Most likely it will be opened with the Visio Viewer – which is a component part of Office 365 ProPlus. Visio Viewer is just an ActiveX component and will therefore load the drawing inside Internet Explorer 11.

image

let’s add a third-step to this process….

3. Open an elevated Powershell prompt and type Enable-AppV. Press the enter-key
image

Once you attempt to re-open the Visio-drawing you will most likely see a blank Internet Explorer-page instead of the Visio Viewer ActiveX component. The below is from a minimal window.

image

All Microsoft components and suddenly we have a break scenario? The details are actually clarified in a knowledgebase-article relating to Skype for Business, Internet Explorer and App-V;

By default, Explorer.exe and Internet Explorer are listed there. Therefore, when the iexplore.exe process runs, it has the APPVEntsubsystem32.dllloaded in Internet Explorer. When Office C2R detects that APPVEntsubsystem32.dll is loaded into Internet Explorer, it does not load jitv.dll or APPVISVSubsystem32.dll into Internet Explorer. As a result, there is no registry redirection support.
When the JavaScript that’s running in Internet Explorer tries to create any ActiveX objects that are part of the Office C2R package, that operation fails and the Office C2R and SharePoint integration are broken. Office C2R and App-V dynamic virtualization aren’t designed to co-exist; therefore, Office C2R disables Dynamic Virtualization when it detects that AppV client binaries are loaded in Internet Explorer to prevent double hooking.

The article unfortunately provides no fix for a Windows 10 – 1607 unless someone is wanting to decrease the feature set for App-V. Browsing the web allows a little gem to be spotted in the comment section of Dan Gough’s amazing blog – the comment is from an Andrew G:

and also there is a registry key suggested by MS:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ClickToRun\OverRide
keyname=AllowJitvInAppvVirtualizedProcess (reg_dword)
keyvalue=1

Searching for this registry key on google presents only a few results (one spiceworks thread) are found. A hidden gem, I presume?

Add the registry key and immediately the Visio drawing can be opened without issues

image

2017-08-22 Update

According to Roy Essers – these registry keys will be automatically created on Windows 10 – 1703

Windows 10–1607–LinkedConnections

So, lots of things have to fall into place for this to take effect, but here we go….

If you have UAC enabled (in some form or another) and have users that are able to run in two different contexts (elevated and non-elevated) with the same user account – it means that they are local administrators on the device. It also means…

.. that if they map a network drive in one context it will not be visible in the other context by default.

Microsoft created the great Linked Connection which will automatically allow the mapping of a network drive to be visible in both contexts.

Well, since roughly October 2016 this stopped working if you were running Windows 10 – 1607 with the October 2016 patch, and the user was local administrator on the device and mapped a drive in one context and also expected the EnableLinkedConnections to provide a smooth user experience – it simply stopped working.

To add insult to injury – if you map a drive within a login-script; the default context is elevated so the non-elevated process (such as explorer.exe) will not show the mapped drive.

As far as I can tell this is not listed as a known defect. Therefore its not listed as a fixed defect in the March 2017 patch for Windows (so, october.. november… december.. 6 months in total in a non-working state). According to Google (oh, what a reliable source for anything named Microsoft) Windows 10 – 1607 was noted as Business Ready (Current Business Branch) since 29 November 2016.

image

Ready for business, eh? Well – at least one step further….

Group Policy Preference and Scheduled Tasks

For some reason it’s always the details in the basics that are the longest hurdle to get over. This particular topic is something that always needs to be re-googled before the last details are sorted out.

Purpose

To create a schedule task to either run as the system-account or the interactive user via Group Policy Preference.

The detail:

When resolving SYSTEM the normally resolved identity is BUILTIN\SYSTEM. Interactive is normally not able to resolve at all. This normally results in the following error client side when attempting to apply the Group Policy

‘0x80070534 No mapping between account names and security IDs was done.’

What should be done?

Click the Change User or Group and select the domain of your environment, and proceed to select the Builtin-container. This will resolve both Interactive (running in the user context of the logged on user) and system to NT Authority.

image

End-result;

image

or

image