Powershell

Removing Duplicate Windows Boot Manager Entries in your MDT Task Sequence

We recently purchased quite a few Dell XPS 13 9365 laptops and while testing our image; I noticed that the list of Windows Boot Manager entries in the BIOS was growing each time I would image the laptop. In order to get around this, I had to create the following script that would automatically scan through the Boot Configuration Data (BCD) store and remove all of the duplicate GUID’s that appear after running the image.

# CONFIGURE MDT LOGGING
$TSenv = New-Object -COMObject Microsoft.SMS.TSEnvironment 
$LogPath = $TSenv.Value("LogPath")  
$LogFile = "$LogPath\WindowsBootManagerRemoval.log"

Start-Transcript $LogFile

# LOCATE GUID
$Identifiers = BCDEDIT /ENUM FIRMWARE | Select-String "identifier" | ForEach-Object { $_ -replace "identifier" }

If($Identifiers -ne $Null) {
    
    $IdentifierList = $Identifiers.Replace(" ","") | Where-Object {$_ -notcontains "{fwbootmgr}" -and $_ -notcontains "{bootmgr}"}
    
    Write-Host "" 
    Write-Host "--------------------------------------------------------------------------------" 
    Write-Host "PREPARING TO REMOVE DUPLICATE WINDOWS BOOT MANAGER ENTRIES"
    Write-Host "--------------------------------------------------------------------------------" 
    Write-Host "" 

    # REMOVE GUIDS
    ForEach($Identifier in $IdentifierList) {
        BCDEDIT /Delete $Identifier
        Write-Host "DELETED $Identifier"
    }
}
Else {
    Write-Host "SKIPPING - COULD NOT LOCATE DUPLICATE WINDOWS BOOT MANAGER ENTRIES"
}

Once you are ready to use the script, go ahead and copy it over to your Deployment Share and add it to your State Restore group in your MDT Task Sequence.
MDT Task Sequence

Since this was not happening to all of our computer models, I made sure to add a Task Sequence condition that forced this step to only run for the Dell XPS 13 9365 laptops.

Feel free to leave any questions below!

Set-Wallpaper Powershell Function

The following Powershell function will change the current user’s desktop wallpaper automatically using the SystemParametersInfo function that can be located in the User32.dll.

Function Set-WallPaper($Image) {
<#
.SYNOPSIS
Applies a specified wallpaper to the current user's desktop
  
.PARAMETER Image
Provide the exact path to the image

.EXAMPLE
Set-WallPaper -Image "C:\Wallpaper\Default.jpg"

#>

Add-Type -TypeDefinition @" 
using System; 
using System.Runtime.InteropServices;

public class Params
{ 
    [DllImport("User32.dll",CharSet=CharSet.Unicode)] 
    public static extern int SystemParametersInfo (Int32 uAction, 
                                                   Int32 uParam, 
                                                   String lpvParam, 
                                                   Int32 fuWinIni);
}
"@ 

$SPI_SETDESKWALLPAPER = 0x0014
$UpdateIniFile = 0x01
$SendChangeEvent = 0x02

$fWinIni = $UpdateIniFile -bor $SendChangeEvent 

$ret = [Params]::SystemParametersInfo($SPI_SETDESKWALLPAPER, 0, $Image, $fWinIni)
}

Example:
Set-WallPaper -Image “C:\Wallpaper\Default.jpg”

For more information about the SystemParametersInfo function, please see this link to MSDN.

Reboot computer if up time is greater than 5 days

The following script will reboot a computer if it has an up time greater than 5 days and does not currently have a user logged on.

Function Get-TimeStamp {
     
    Return "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date)   
      
}

$LastBootUpTime = Get-WmiObject win32_operatingsystem
$Uptime = ((Get-Date) - ($LastBootUpTime.ConvertToDateTime($LastBootUpTime.LastBootUpTime))).Days

If($Uptime -gt 5) {
    $Explorer = Get-WmiObject Win32_Process -Filter "Name = 'Explorer.exe'"

    If($Explorer -eq $null) {
        Write-Output "$(Get-TimeStamp) - Uptime = $Uptime Days" >> C:\TEMP\ForcedReboot.log
        Write-Output "$(Get-TimeStamp) - Reboot Initiated" >> C:\TEMP\ForcedReboot.log
        Restart-Computer
    
    }
    Else {
        Write-Output "$(Get-TimeStamp) - Uptime = $Uptime Days" >> C:\TEMP\ForcedReboot.log
        Write-Output "$(Get-TimeStamp) - User is currently logged on.  Reboot has been postponed" >> C:\TEMP\ForcedReboot.log
    }
}

Windows 10 Creators Update 1703 Cleanup Script

The following script is intended to run after an in place upgrade (Ex: 1607 to 1703). If you do not know how to run a post script after an upgrade, please refer to my previous post here.

The Powershell script will copy your old wallpapers from C:\Windows.old\windows\Web\Wallpaper\Windows\ and C:\Windows.old\windows\Web\4K\Wallpaper\Windows\ to their appropriate folders. It will also uninstall OneDrive, and prevent OneDriveSetup.exe and Windows Defender from running at logon. As well as remove the Contact Support application, move Office 2016 applications to their appropriate folder in the Start Menu (During my testing, these shortcuts moved around randomly), and attempt to remove any new apps that have reappeared with the upgrade.

Logging is enabled in the script and the entire cleanup log can be located in C:\Logs\1703-Upgrade.log

<#  

.FUNCTIONS
    1 - Set-FilePermissions
        Configures file permissions

    2 - Set-FileOwnership
        Configures ownership of files

    3 - Get-TimeStamp
        Configures timestamp for logs

    4 - Write-Log
        Creates a log for the script

#>

# Configure Functions

Function Set-FilePermissions {
    param (
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$File,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$User,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$Control,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$Access
    )

    $ACL = Get-ACL "$File"
    Set-Acl -Path "$File" -AclObject $ACL
    $Permission = New-Object  system.security.accesscontrol.filesystemaccessrule("$User","$Control","$Access")
    $Acl.SetAccessRule($Permission)
    Set-Acl -Path "$File" -AclObject $ACL

}

Function Set-FileOwnership {
    param (
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$File,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$User
    )

    $ACL = Get-ACL "$File"
    $Group = New-Object System.Security.Principal.NTAccount("$User")
    $ACL.SetOwner($Group)
    Set-Acl -Path "$File" -AclObject $ACL

}

function Get-TimeStamp {
    
    return "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date)
    
}

function Write-Log {
    param (
    [parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]$Passed,
    [parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]$Failed
    )
    
    If ($ProcessError.Count -eq 0) {
        Write-Output "$(Get-TimeStamp) $Passed" >> C:\Logs\1703-Upgrade.log
    }
    Else {
        Write-Output "$(Get-TimeStamp) $Failed" >> C:\Logs\1703-Upgrade.log
        $Global:Errors++
        $ProcessError.Clear()
    }
}

<# -- Script begins below --  #>

$Errors = 0

Write-Output "$(Get-TimeStamp) CLEANUP SCRIPT STARTED" > C:\Logs\1703-Upgrade.log
Write-Output " " >> C:\Logs\1703-Upgrade.log
Write-Output "$(Get-TimeStamp) START COPYING WALLPAPERS" >> C:\Logs\1703-Upgrade.log

# Update Wallpaper
Set-FileOwnership -File "C:\windows\web\Wallpaper\Windows\img0.jpg" -User Users
Set-FilePermissions -File "C:\windows\web\Wallpaper\Windows\img0.jpg" -User Users -Control FullControl -Access Allow
Copy-Item "C:\Windows.old\windows\Web\Wallpaper\Windows\img0.jpg" -Destination "C:\windows\web\Wallpaper\Windows\img0.jpg" -Force -ErrorVariable +ProcessError
Write-Log -Passed "Copied C:\windows\web\Wallpaper\Windows\img0.jpg to C:\windows\web\Wallpaper\Windows\img0.jpg" -Failed "Failed to copy C:\windows\web\Wallpaper\Windows\img0.jpg to C:\windows\web\Wallpaper\Windows\img0.jpg"

# Update 4k Wallpapers
$Wallpapers = Get-ChildItem C:\Windows\Web\4K\Wallpaper\Windows
ForEach($Wallpaper in $Wallpapers) {
    Set-FileOwnership -File $Wallpaper.FullName -User Users
    Set-FilePermissions -File $Wallpaper.FullName -User Users -Control FullControl -Access Allow

    $FileName = $Wallpaper.Name
    $FilePath = $Wallpaper.FullName

    Copy-Item C:\Windows.old\windows\Web\4K\Wallpaper\Windows\$FileName -Destination $Wallpaper.FullName -Force -ErrorVariable +ProcessError
    Write-Log -Passed "Copied C:\Windows.old\windows\Web\4K\Wallpaper\Windows\$FileName to $FilePath" -Failed "Failed to copy C:\Windows.old\windows\Web\4K\Wallpaper\Windows\$FileName to $FilePath"
}   

# Uninstall OneDrive
Write-Output " " >> C:\Logs\1703-Upgrade.log
Write-Output "$(Get-TimeStamp) UNINSTALLING ONEDRIVE" >> C:\Logs\1703-Upgrade.log
Start-Process C:\Windows\SysWOW64\OneDriveSetup.exe /uninstall -Wait -ErrorVariable +ProcessError
Write-Log -Passed "Uninstalled OneDrive successfully" -Failed "Failed to uninstall OneDrive"

# Rename OneDriveSetup.exe (This is to prevent OneDrive First Run)
Write-Output " " >> C:\Logs\1703-Upgrade.log
Write-Output "$(Get-TimeStamp) RENAMING ONEDRIVESETUP.EXE TO PREVENT ONEDRIVE FROM RUNNING AT LOGON" >> C:\Logs\1703-Upgrade.log
Set-FileOwnership -File C:\Windows\SysWOW64\OneDriveSetup.exe -User Users
Set-FilePermissions -File C:\Windows\SysWOW64\OneDriveSetup.exe -User Users -Control FullControl -Access Allow
Rename-Item C:\Windows\SysWOW64\OneDriveSetup.exe C:\Windows\SysWOW64\OneDriveSetup.exe.old -ErrorVariable +ProcessError
Write-Log -Passed "Renamed C:\Windows\SysWOW64\OneDriveSetup.exe to C:\Windows\SysWOW64\OneDriveSetup.exe.old" -Failed "Failed to rename C:\Windows\SysWOW64\OneDriveSetup.exe to C:\Windows\SysWOW64\OneDriveSetup.exe.old"

# Remove OneDrive run key
Write-Output " " >> C:\Logs\1703-Upgrade.log
Write-Output "$(Get-TimeStamp) REMOVING ONEDRIVE RUN KEY FROM THE DEFAULT'S NTUSER.DAT FILE" >> C:\Logs\1703-Upgrade.log
cmd /c REG LOAD "HKLM\DEFAULT_USER" "C:\Users\Default\NTUSER.DAT" 
Remove-ItemProperty -Path "HKLM:\DEFAULT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "OneDriveSetup" -ErrorVariable +ProcessError
Write-Log -Passed "Removed OneDriveSetup from the default profile's run key" -Failed "Failed to remove OneDriveSetup from the default profile's run key"
cmd /c REG UNLOAD "HKLM\DEFAULT_USER"

# Delete run key for Windows Defender
Write-Output " " >> C:\Logs\1703-Upgrade.log
Write-Output "$(Get-TimeStamp) REMOVING WINDOWS DEFENDER FROM THE CURRENT USER RUN KEY" >> C:\Logs\1703-Upgrade.log
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "SecurityHealth" -ErrorVariable +ProcessError
Write-Log -Passed "Removed SecurityHealth from the current user's run key" -Failed "Failed to remove SecurityHealth from the current user's run key"

# Remove Contact Support
Write-Output " " >> C:\Logs\1703-Upgrade.log
Write-Output "$(Get-TimeStamp) REMOVING THE CONTACT SUPPORT APPLICATION" >> C:\Logs\1703-Upgrade.log
Get-WindowsCapability -online | ? {$_.Name -like ‘*ContactSupport*’} | Remove-WindowsCapability –online -ErrorVariable +ProcessError
Write-Log -Passed "Removed the Contact Support application" -Failed "Failed to remove the Contact Support application"

# Move Office 2016 Applications to correct folder in the Start Menu if needed
Write-Output " " >> C:\Logs\1703-Upgrade.log
Write-Output "$(Get-TimeStamp) MOVING OFFICE 2016 APPLICATIONS TO CORRECT FOLDER IN START MENU" >> C:\Logs\1703-Upgrade.log
$OfficePrograms = GCI "C:\ProgramData\Microsoft\Windows\Start Menu\Programs" | Where-Object name -like "*2016.lnk"

If($OfficePrograms.count -gt 0) {
    ForEach($Program in $OfficePrograms) {
    $OfficeFilePath = $Program.FullName
    $OfficeFileName = $Program.Name
    Copy-Item "$OfficeFilePath" -Destination "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Office 2016\$OfficeFileName"  -Force -ErrorVariable +ProcessError
    Remove-Item "$OfficeFilePath"
    Write-Log -Passed "Copied $OfficeFilePath to C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Office 2016\$OfficeFileName" -Failed "Failed to copy $OfficeFilePath to C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Office 2016\$OfficeFileName"

    }
}
Else {
    Write-Output "$(Get-TimeStamp) Did not find any Office 2016 programs outside of C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Office 2016" >> C:\Logs\1703-Upgrade.log
}


# Remove 1703 apps
Write-Output " " >> C:\Logs\1703-Upgrade.log
Write-Output "$(Get-TimeStamp) PREPARING TO REMOVE APPS" >> C:\Logs\1703-Upgrade.log
$AppsList = "Microsoft.WindowsFeedbackHub", "Microsoft.XboxIdentityProvider", "Microsoft.Windows.HolographicFirstRun", "Windows.ContactSupport", "Microsoft.XboxGameCallableUI", "HoloShell", "HoloItemPlayerApp", "HoloCamera", "Microsoft.OneConnect", "Microsoft.People", "Microsoft.XboxSpeechToTextOverlay", "Microsoft.XboxGameOverlay", "Microsoft.SkypeApp", "Microsoft.MicrosoftSolitaireCollection", "Microsoft.MicrosoftOfficeHub", "Microsoft.3DBuilder", "Microsoft.Getstarted", "Microsoft.Microsoft3DViewer", "Microsoft.Office.OneNote", "Microsoft.XboxApp", "Microsoft.ZuneMusic", "Microsoft.ZuneVideo", "Microsoft.MSPaint"
ForEach ($App in $AppsList)
{
  $PackageFullName = (Get-AppxPackage $App).PackageFullName
  $ProPackageFullName = (Get-AppxProvisionedPackage -online | where {$_.Displayname -eq $App}).PackageName
  Write-Host $PackageFullName
  Write-Host $ProPackageFullName

  If ($PackageFullName)
  {
    Write-Host “Removing Package: $App”
    Remove-AppxPackage -package $PackageFullName -ErrorVariable +ProcessError
    Write-Log -Passed "Removed $App" -Failed "Failed to remove $App"

  }
  else
  {
    Write-Output "$(Get-TimeStamp) Unable to find package: $App” >> C:\Logs\1703-Upgrade.log
  }

  if ($ProPackageFullName)
  {
    Write-Host “Removing Provisioned Package: $ProPackageFullName”
    Remove-AppxProvisionedPackage -online -packagename $ProPackageFullName -ErrorVariable +ProcessError
    Write-Log -Passed "Removed $ProPackageFullName" -Failed "Failed to remove $ProPackageFullName"
  }
  else
  {
    Write-Output "$(Get-TimeStamp) Unable to find provisioned package: $App” >> C:\Logs\1703-Upgrade.log
  }
}
$ErrorCount = $Errors
Write-Output " " >> C:\Logs\1703-Upgrade.log
Write-Output "$(Get-TimeStamp) CLEANUP COMPLETE - FOUND $ErrorCount ERRORS" >> C:\Logs\1703-Upgrade.log

Set-FileOwnership and Set-FilePermissions Powershell function

Here are two separate Powershell functions that will configure file ownership and file permissions. I had to create them, since I was having to reuse the same code multiple times in my upcoming Creators Update 1703 cleanup script. Enjoy!

Set-FileOwnership

Function Set-FileOwnership {
    <#
    .SYNOPSIS
    Sets File Ownership
 
    .PARAMETER File
    Provide file path
 
    .PARAMETER User
    Provide a username or group that requires file ownership
 
    .EXAMPLE
    Set-FileOwnership -File "C:\windows\web\Wallpaper\Windows\img0.jpg" -User Users
 
    .EXAMPLE
    Set-FileOwnership -File "C:\windows\web\Wallpaper\Windows\img0.jpg" -User Administrators
    #>
    param (
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$File,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$User
    )

    $ACL = Get-ACL "$File"
    $Group = New-Object System.Security.Principal.NTAccount("$User")
    $ACL.SetOwner($Group)
    Set-Acl -Path "$File" -AclObject $ACL

}

Set-FilePermissions

Function Set-FilePermissions {
    <#
    .SYNOPSIS
    Sets File Permissions
 
    .PARAMETER File
    Provide file path
 
    .PARAMETER User
    Provide a username or group that requires permissions configured

    .PARAMETER Control
    Provide file system rights (Ex: FullControl, Modify, ReadAndExecute, etc)
 
    .PARAMETER Access
    Provide file system access rule (Ex: Allow or Deny)
 
    .EXAMPLE
    Set-FilePermissions -File "C:\windows\web\Wallpaper\Windows\img0.jpg" -User Users -Control FullControl -Access Allow
 
    .EXAMPLE
    Set-FilePermissions -File "C:\windows\web\Wallpaper\Windows\img0.jpg" -User Administrators -Control ReadAndExecute -Access Allow
    #>
    param (
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$File,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$User,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$Control,
     [parameter(Mandatory=$true)]
     [ValidateNotNullOrEmpty()]$Access
    )

    $ACL = Get-ACL "$File"
    Set-Acl -Path "$File" -AclObject $ACL
    $Permission = New-Object  system.security.accesscontrol.filesystemaccessrule("$User","$Control","$Access")
    $Acl.SetAccessRule($Permission)
    Set-Acl -Path "$File" -AclObject $ACL

}

How to remove the Contact Support app in Windows 10 1607 and above

The Contact Support app can now officially be removed but the process is a little different than how you would normally remove a Windows app with Powershell. Luckily it isn’t too difficult and it can be done with a one liner!

Get-WindowsCapability -online | ? {$_.Name -like ‘*ContactSupport*’} | Remove-WindowsCapability –online

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!

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

    }
}

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')  
}
Older Posts »
Page 1 of 4