New laptop chassis type is not recognized by MDT

I recently ran into an issue where MDT could not determine if our new Dell XPS 13 9365 was a laptop or desktop. After running wmic path win32_systemenclosure get chassistypes, I was able to determine that the chassis type 31 was not listed in MDT’s ZTIGather.wsf. Adding 31 to ZTIGather.wsf (Line 417) as shown below, fixed the issue and MDT was now able to determine that the XPS 13 9365 was a laptop!

Example:

Select Case objInstance.ChassisTypes(0)
Case "8", "9", "10", "11", "12", "14", "18", "21", "31"
	bIsLaptop = true
Case "3", "4", "5", "6", "7", "15", "16"
	bIsDesktop = true
Case "23"
	bIsServer = true
Case Else
	' Do nothing
End Select

Feel free to leave any questions in the comment section!

How to silently install LexisNexis InterAction 6.21.21

I recently had some issues specifying server data for my LexisNexis Interaction silent install. For some reason, the server information that I used was not applying during the install. After a few failed attempts, I decided to review the MSIEXEC logs and found the following lines:

Action start 15:13:52: SetDataDirDef.
MSI (s) (F0:68) [15:13:52:542]: Doing action: SetDBServerSRCH
Action ended 15:13:52: SetDataDirDef. Return value 1.
MSI (s) (F0:68) [15:13:52:542]: Transforming table CustomAction.

MSI (s) (F0:68) [15:13:52:542]: PROPERTY CHANGE: Deleting IADBSERVER property. Its current value is ‘SQLSERVER’.

According to the logs, it looks like the server information that I added was getting deleted because of the SetDBServerSRCH action.

Now that I found what was causing the server information to be removed, I decide to review the InterAction MSI table and search for the SetDBServerSRCH action. Once I found the action, I was able to locate the condition that was forcing the action to run.
Condtion:
IANETINSTALL=0 and UILevel<5
Conditions

After a few tests, I verified that you can specify server information if you make the condition false. So all you have to do is add the following to your MSIEXEC command:
IANETINSTALL=2

And for anyone lazy enough to not read everything, here is the entire silent install command for InterAction.
MSIEXEC /I InterAction Desktop Applications.msi IADBSERVER=”DB SERVER” IADBNAME=”DB NAME” IAPORT=”PORT HERE” NETAPPSERVER=”APPSERVER” IANETINSTALL=2 /qn

I hope this helps someone!

“The universal unique identifier (UUID) type is not supported” MDT Fix

During our Windows 10 testing, we noticed that some users would randomly come across the universal unique identifier (UUID) type is not supported error when they logged onto their computer for the first time. In order to get around this error, Microsoft provided a work around that would work with SCCM. Click here for the article.

Unfortunately this does not work well with MDT because the administrator account does not have permission to add a value to the “HKLM\SYSTEM\CurrentControlSet\Services\gpsvc” registry key.
The following Powershell script will fix this by changing the owner of the key to the Administrators group and also providing full access to the Administrators group. This will be temporary since sysprep seems to revert the permissions after it has processed. Fortunately the value stays with the registry key!

Note: In order to have this fix work successfully with MDT, we will need to configure the script to run before the sysprep step in your capture task sequence.

$definition = @"
using System;
using System.Runtime.InteropServices;
 
namespace Win32Api
{
 
public class NtDll
{
[DllImport("ntdll.dll", EntryPoint="RtlAdjustPrivilege")]
public static extern int RtlAdjustPrivilege(ulong Privilege, bool Enable, bool CurrentThread, ref bool Enabled);
}
}
"@
 
Add-Type -TypeDefinition $definition -PassThru
 
$bEnabled = $false
$res = [Win32Api.NtDll]::RtlAdjustPrivilege(9, $true, $false, [ref]$bEnabled)

# Change Owner to the local Administrators group
$regKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SYSTEM\CurrentControlSet\Services\gpsvc",[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,[System.Security.AccessControl.RegistryRights]::TakeOwnership)
$regACL = $regKey.GetAccessControl()
$regACL.SetOwner([System.Security.Principal.NTAccount]"Administrators")
$regKey.SetAccessControl($regACL)

# Change Permissions for the local Administrators group
$regKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SYSTEM\CurrentControlSet\Services\gpsvc",[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,[System.Security.AccessControl.RegistryRights]::ChangePermissions)
$regACL = $regKey.GetAccessControl()
$regRule = New-Object System.Security.AccessControl.RegistryAccessRule ("Administrators","FullControl","ContainerInherit","None","Allow")
$regACL.SetAccessRule($regRule)
$regKey.SetAccessControl($regACL)

# Add registry key fix
cmd /c reg add "HKLM\SYSTEM\CurrentControlSet\Services\gpsvc" /v Type /t REG_DWORD /d 0x10 /f

Feel free to leave any questions in the comments!

Test-RegValue Function

Powershell has a great CMDLET called Test-Path that can check to see if a registry key exists but unfortunately it does not have the ability to check for registry values. In order to get around this, I created the following function to check to see if a registry value exists. This can be extremely useful when you are using a registry key to verify if a script has already executed in the past.

Examples:
Test-RegValue -Key “HKCU:\Control Panel\Desktop” -Value WallPaper
Test-RegValue -Key “HKLM:\SOFTWARE\Custom” -Value Test

Function Test-RegValue {
    <#
    .SYNOPSIS
    Determine if a registry value exists

    .PARAMETER Key
    Provide registry key path

    .PARAMETER Value
    Provide registry value that you would like to test

    .EXAMPLE
    Test-RegValue -Key "HKCU:\Control Panel\Desktop" -Value WallPaper

    .EXAMPLE
    Test-RegValue -Key "HKLM:\SOFTWARE\Custom" -Value Test
    #>
    param (
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$Value,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$Key
    )

    
    Try {
        Get-ItemProperty "$Key" -ErrorAction Stop| Select-Object -ExpandProperty $Value -ErrorAction Stop | Out-Null
        Return $True
    }
    Catch [System.Management.Automation.ItemNotFoundException] {
        Write-Host "Please verify the registry key exists" -ForegroundColor Red 
        Return $False
    }
    Catch {
        Return $False
    }
}

Read more

Remote Outlook 2016 Caching Report

I was recently asked to figure out a way to determine which users had cache mode enabled in Outlook 2016 and the end result is the script below.

In order to run the script, you will need to specify the following variables:
$Directory = Insert the exact path of where you would like to save your report ( EX: C:\Reports)
$File = Insert the file name of your report (EX: MyReport.csv)
$ComputerList = Insert the full path of your computer list (EX: C:\Computerlist.txt)


<#  

.SYNOPSIS  
    Run a report to find who is in Outlook Cache Mode

.NOTES  
    File Name  : OutlookCachingReport.ps1  
    Author     : Jose Espitia
    Requires   : PowerShell V5
    Version    : Version 1.00

#>

# Specify where to save the report
$Directory = "C:\Reports"
$File = "MyReport.csv"

# Computer list
$ComputerList = "C:\ComputerList"
$Computers = Get-Content "$ComputerList"

ForEach($Computer in $Computers) {
    
    Try { 
        # Test connection with computer
        Test-Connection -ComputerName $Computer -ErrorAction Stop
        # Query remote machines
        $HKEY_Users = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("Users",$Computer)
        # Get list of SIDs
        $SIDs = $HKEY_Users.GetSubKeyNames() | Where-Object { ($_ -like "S-1-5-21*") -and ($_ -notlike "*_Classes") }

        # Associate SID with Username
        $TotalSIDs = ForEach ($SID in $SIDS) {
            Try {
                $SID = [system.security.principal.securityidentIfier]$SID
                $user = $SID.Translate([System.Security.Principal.NTAccount])
                New-Object PSObject -Property @{
                    Name = $User.value
                    SID = $SID.value
                }                 
            } 
            Catch {
                Write-Warning ("Unable to translate {0}.`n{1}" -f $UserName,$_.Exception.Message)
            }
        }
        $UserList = $TotalSIDs 

        # Loop through users to determine If they are in cache mode
        ForEach($User in $UserList) {
            # Get SID
            $UserSID = $User.SID
    
            # Get list of Outlook profiles
            $OutlookProfiles = $HKEY_Users.OpenSubKey("$UserSID\SOFTWARE\Microsoft\Office\16.0\Outlook\Profiles\Outlook\")
    
            # Loop through Outlook profiles to find caching key
            ForEach($Profile in ($OutlookProfiles.GetSubKeyNames())) {
        
                $ProfileKey = $HKEY_Users.OpenSubKey("$UserSID\SOFTWARE\Microsoft\Office\16.0\Outlook\Profiles\Outlook\$Profile")
        
                # Locate cache key
                If(($ProfileKey.GetValueNames() -contains "00036601") -eq $True) {
                    $Result = $ProfileKey.GetValue("00036601")
                    # Convert value to HEX
                    $Result = [System.BitConverter]::ToString($Result)
            
                    # Determine if cache mode is enabled
                    If($Result -like "8*") {
                        $CacheMode = "Enabled"
                    }
                    Else {
                        $CacheMode = "Disabled"
                    }
                    # Create custom table
                    $Table = New-Object PSObject -Property @{
                        Username = $User.Name
                        SID = $User.SID
                        "Computer Name" = $Computer
                        "Cache Mode" = $CacheMode
                        "Registry Key Value" = $Result

                    } | Select-Object Username, SID, "Computer Name", "Cache mode", "Registry Key Value"
                    # Export table to CSV
                    $Table | Export-Csv -NoTypeInformation -Append -Path "$directory\$file"
                }
            
            }
        }
    }
    Catch {
        # Create custom table
        $Table = New-Object PSObject -Property @{
            Username = "N/A"
            SID = "N/A"
            "Computer Name" = $Computer
            "Cache Mode" = "N/A"
            "Registry Key Value" = "N/A"

        } | Select-Object Username, SID, "Computer Name", "Cache mode", "Registry Key Value"
        # Export table to CSV
        $Table | Export-Csv -NoTypeInformation -Append -Path "$directory\$file"
    }
}

Feel free to leave questions in the comments!

Add a wireless profile to your MDT task sequence

First you will need to export the configuration for the wireless profile that you would like to add to your task sequence.
Note: You will need to connect to the wireless profile before you can export it.

:: ADD YOUR WIRELESS PROFILE NAME
SET WIFI-PROFILE="YOUR WIRELESS PROFILE"
:: EXPORT WIRELESS CONFIGURATION
NETSH WLAN EXPORT PROFILE "%WIFI-PROFILE%" FOLDER="%USERPROFILE%\Desktop" KEY=Clear

The export should have copied an XML file to your desktop. In order to keep this simple, go ahead and rename the XML file WirelessProfile.xml. Now, copy WirelessProfile.xml and place it inside of your Deployment Share.
For this example, I will be copying the XML file into a folder called Custom, inside of your scripts folder.

Now go ahead and open up your task sequence and add a “Run Command Line” task inside of the State Restore group.

You can name the task anything you would like but in this example I have named it “Add Wireless Profile”.

Last but not least, you will need to add the following in the Command Line field:

NETSH WLAN ADD PROFILE FILENAME="%SCRIPTROOT%\Custom\WirelessProfile.xml" USER=All

Now you will have a pre-configured Wireless profile!

Enable or Disable Outlook caching based on the user’s location

The following script will use the user’s AD city and the default gateway to determine if a user is in their home office or visiting an office. If the script determines that the user is not in their home office, it will automatically disable Outlook caching and vice versa for a user that is in their home office.

In order for the script to work, you will need to use the cities listed in AD for your $CurrentLocation switch. Similar to the example below, you will also have to copy your default gateways inside of the switch.

<#  

.SYNOPSIS  
    Caches a user's mailbox based on machine type or location  

.NOTES  
    File Name  : OutlookCaching.ps1  
    Author     : Jose Espitia
    Requires   : PowerShell V5
    Version    : Version 1.00

#>
if($env:COMPUTERNAME -like "*-D") {    
    # Get Default Gateway
    $CurrentDefaultGateway = (Get-WmiObject Win32_NetworkAdapterConfiguration) | foreach { $_.DefaultIPGateway }

    # Find Machine's Location
    $CurrentLocation = switch ($CurrentDefaultGateway) { 
            10.101.1.1 {"Orlando"} 
            10.102.1.1 {"Miami"} 
            10.103.1.1 {"Atlanta"} 
            10.104.1.1 {"New York City"}
            10.105.1.1 {"Denver"}
            10.106.1.1 {"Austin"}
            10.107.1.1 {"Los Angeles"}
            10.108.1.1 {"Las Vegas"}
            10.109.1.1 {"Seattle"} 
    }

    # Get User's Info
    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    $objSearcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry
    $objSearcher.Filter = "(&(objectCategory=User)(SAMAccountName=$env:USERNAME))"
    $objSearcher.SearchScope = "Subtree"
    $obj = $objSearcher.FindOne()
    $UsersHome = $obj.Properties["l"]

    # Determine if user is in their home office
    if($UsersHome -match $CurrentLocation) {
        cmd /c REG ADD "HKEY_CURRENT_USER\Software\Policies\Microsoft\Office\16.0\Outlook\Cached Mode" /v Enable /t REG_DWORD /d 1 /f
    }
    else {
        cmd /c REG ADD "HKEY_CURRENT_USER\Software\Policies\Microsoft\Office\16.0\Outlook\Cached Mode" /v Enable /t REG_DWORD /d 0 /f

    }
}

Registry Keys for Windows 10 Privacy Settings

The following registry keys in this post control the privacy settings in Windows 10 1607. These settings can be found in the GUI by going to SETTINGS\PRIVACY.
Read more

Pin shortcuts to a user in a specific Active Directory group.

The following Powershell script will pin the Chrome shortcut to the Windows 10 start menu for anyone inside of a specific Active Directory group. In order to pin to the start menu, you will need to verify if your shortcut can be pinned through the GUI. To check this, you can right click your shortcut and see if you have “Pin to Start” available in your context menu. If you do not see this, then you may want to try copying the shortcut to:
%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs

In order to configure the script, you will need to provide values for the following variables:
$Group = YOUR AD GROUP
$Shortcut = “THE SHORTCUT NAME WITH THE EXTENSION”
$Location = “THE SHORTCUT’S LOCATION”

# Variables that need to be set
$Group = "YOUR AD GROUP HERE"
$Shortcut = "Google Chrome.lnk"
$Location = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs"


# Get User's Info
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher.Filter = "(&(objectCategory=User)(SAMAccountName=$env:USERNAME))"
$objSearcher.SearchScope = "Subtree"
$obj = $objSearcher.FindOne()
$User = $obj.Properties["distinguishedname"]

# Get Group Info
$objSearcher.Filter = "(&(objectCategory=group)(SamAccountname=$Group))"
$objSearcher.SearchScope = "Subtree"
$obj = $objSearcher.FindOne()
[String[]]$Members = $obj.Properties["member"]

If ($Members -contains $User) { 
    $object= New-Object -ComObject shell.application
    $folder = $object.Namespace("$Location")
    $file= $folder.parsename("$Shortcut")
    $file.InvokeVerb('pintostartscreen')  
}

How to remove “Scan with Windows Defender” from the Context Menu

The following short batch script will automatically remove “Scan with Windows Defender” from the context menu for files, folders and drives in Windows 10.

:: Removes Windows Defender from the Context Menu for Files
REG DELETE HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\EPP /F
:: Removes Windows Defender from the Context Menu for Folders
REG DELETE HKEY_CLASSES_ROOT\Directory\shellex\ContextMenuHandlers\EPP /F
:: Removes Windows Defender from the Context Menu for Drives
REG DELETE HKEY_CLASSES_ROOT\Drive\shellex\ContextMenuHandlers\EPP /F
Older Posts »
Page 1 of 6