Tuesday, March 9, 2021

Automatically Logoff Idle Users

Google around and you'll find various ways of automatically logging off idle users.  None of them suited my requirements so I created a task scheduler coupled to a powershell script to manage it.  The advantages of this approach are

  • Can't be intercepted by users
  • Only uses existing OS tools and scripting
  • Manages user logons individually
  • Works if Switch User is used to logon other accounts
  • Simple, quick, effective with no significant processing requirement
  • Any background management tasks such as OS updates are not affected.
The exact requirements were to logoff "idle" user logon sessions from Windows 10 computers.  The computers were open access type computers.  None of the work in the sessions would have any state to save and no background processing tasks in the session would exist.  Forcing a logoff for unused logons would not result in any lost work.  In reality this does not check for "Idle" sessions or an idle computer state.  It assumes sessions are unused and not needed and forces them to logoff.

This method also requires security event auditing to be turned on so that the events are stored in the security log.

The scheduled task is set to run as SYSTEM.  Check the tick box Run with highest privileges

The scheduled task is triggered by a custom event filter. The XML of which is thus:

<QueryList>
  <Query Id="0" Path="Security">
    <Select Path="Security">*[System[(EventID=4800 or EventID=4802)]]</Select>
  </Query>
</QueryList>

The program called is the powershell.exe with the argument -file <PathToScript>

There are no conditions.

Settings - Select "Run a new instance in parallel"  This setting enables multiple user sessions to be tracked if neccessary.

The PowerShell script is as follows:

#This script has just been triggered by a user 4800(lock) or 4802(screensaver) security log event
#get user id and event time
$trigger = get-eventlog -logname security | where {$_.InstanceId -like "4800" -or $_.InstanceId -like "4802"} | select -first 1

#Get event log time
#We'll look forward from this point later.
$trigtime = get-date -date $trigger.TimeGenerated

#Get trigger SessionId
$trigMsg = $trigger.Message
$trigIDtemp = $trigMsg.split("Session ID: ") | select -last 1
$trigID = $trigIDtemp.trim()

# Every 15 minutes check to see if the user has returned
# Do this in total 11 times (during 3 hours) if the user is still not back
# log them out.

#first wait 15 mins  wait period 1
start-sleep -s 900
#wait test
#start-sleep -s 120

#count from 0 while less that 10 causes 10 checks
$count = 0
While ($count -le 10)
    {
    #Current Time
    if ($count -eq 0)
        {$TimeSpec = $trigtime}
    else
        {$TimeSpec = $CheckTime}
           
    #Has user returned in the last 15 mins?
    $CheckEvLog = $null
    $CheckEvLog = get-eventlog -logname security -After $TimeSpec | where {$_.InstanceID -eq 4801 -or $_.InstanceId -eq 4803}
    
    #Check any return events 4801(unlock) or 4803(screensaver dismissed) for user id
    if ($CheckEvLog -notlike $null) 
        {
        #were any of the events related to the trigger sessionid ?
        foreach ($logitem in $CheckEvLog)
            {
            $logitemMsg = $logitem.Message
            $entryIDtemp = $logitemMsg.split("Session ID: ") | select -last 1
            $entryID = $entryIDtemp.trim()
        
            #if sessionIDs match then user has returned so exit
            if ($entryID -eq $trigID){exit}
            }
        }
    
    #there are no return events or they are not for current session id.
    $CheckTime = Get-Date
    
    #wait 15 mins
    start-sleep -s 900
    #wait test
    #start-sleep -s 15
       
    $Count; $Count +=1
    }

#if you're here, that user/sessionID ain't back. Time periods configured above have expired.
#Time to log them off
& logoff $trigID

Create a URI File to call

 Amazon Workspace Client can use Universal Resource Identifiers (URIs) to launch with predefined parameters such as the username and region code.

This code creates a URI file and makes a call to it.  In practice addtional code would be used to determine the SAMAccountName and RegionCode

$SAMAccountName=mysamaccountname
$RegionCode=myRegionCode

$targetpath = 'workspaces://'+$SAMAccountName+'@'+$RegionCode+'?MFACode=push'

$line1 = '[InternetShortcut]' | Out-File $URIFile
$line2 = 'URL='+$targetpath | Out-File $URIFile -Append
$line3 = 'IconIndex = 0' | Out-File $URIFile -Append
$line4 = 'IconFile=C:\Program Files (x86)\Amazon Web Services, Inc\Amazon\WorkSpaces\workspaces.exe' | Out-File $URIFile -Append

Invoke-Item $URIFile


Creating an SCCM Task Sequence variable

 

During a task sequence run, it is useful to set task sequence variables to set conditions for tasks.

If using the Microsoft Deployment Tookit (MDT) task sequence variables are set for example when a virtual machine or laptop is detected.

$SMSTSEnv.Value("IsVM") will return True for virtual machines
$SMSTSEnv.Value("IsLaptop") will return True for laptops

These items can then be queried to control if laptop orientated tasks should activate.

To set a custom task sequence variable in a powershell script first create the object then set the variable value.

$SMSTSEnv = New-Object -ComObject Microsoft.SMS.TSEnvironment -ErrorAction SilentlyContinue
$SMSTSEnv.Value("TestVar")="TestVarValue"

Task sequence task options can then be conditioned on the task sequence variable value

Thursday, February 18, 2021

Removing Windows 10 Provisioned Universal Windows Platform (UWP) (AppX) apps

 

During a first logon to Windows 10 some time is taken to setup UWP apps for the user.  During the build phase you may wish to remove some of the ones you do not need.

Microsoft.549981C3F5F10
Microsoft.BingWeather
Microsoft.DesktopAppInstaller
Microsoft.GetHelp
Microsoft.Getstarted
Microsoft.HEIFImageExtension
Microsoft.Microsoft3DViewer
Microsoft.MicrosoftEdge.Stable
Microsoft.MicrosoftOfficeHub
Microsoft.MicrosoftSolitaireCollection
Microsoft.MicrosoftStickyNotes
Microsoft.MixedReality.Portal
Microsoft.MSPaint
Microsoft.Office.OneNote
Microsoft.People
Microsoft.ScreenSketch
Microsoft.SkypeApp
Microsoft.StorePurchaseApp
Microsoft.VCLibs.140.00
Microsoft.VP9VideoExtensions
Microsoft.Wallet
Microsoft.WebMediaExtensions
Microsoft.WebpImageExtension
Microsoft.Windows.Photos
Microsoft.WindowsAlarms
Microsoft.WindowsCalculator
Microsoft.WindowsCamera
microsoft.windowscommunicationsapps
Microsoft.WindowsFeedbackHub
Microsoft.WindowsMaps
Microsoft.WindowsSoundRecorder
Microsoft.WindowsStore
Microsoft.Xbox.TCUI
Microsoft.XboxApp
Microsoft.XboxGameOverlay
Microsoft.XboxGamingOverlay
Microsoft.XboxIdentityProvider
Microsoft.XboxSpeechToTextOverlay
Microsoft.YourPhone
Microsoft.ZuneMusic
Microsoft.ZuneVideo

Run this script logged in as admin and elevated.

#get provisioned apps
$AppxProv = Get-AppxProvisionedPackage -Online

#Enter the Apps you want to keep into this array

$AppsToKeep = @("Microsoft.MicrosoftOfficeHub","Microsoft.WindowsCalculator","Microsoft.Office.OneNote","Microsoft.WindowsCamera")

#loop through them

foreach ($AppxProvItem in $AppxProv)

    {
    
    $AppxprvDN = $AppxProvItem | Select -expand DisplayName

         if ($AppxprvDN -notin $AppsToKeep)

            {
            #Get the package full name
            $AppxTargetPFN = Get-AppxPackage -Name $AppxPrvDN | Select -expand PackageFullName

            #first remove the package from the logged on session
            Remove-AppxPackage -Package $AppxTargetPFN

            #then target the provisioned Appx version
            $AppxPrvTarget = Get-AppxProvisionedPackage -Online | where{$_.DisplayName -like $AppxPrvDN}

            $AppxPrvTargetPN = $AppxPrvTarget | Select -expand PackageName

            Remove-AppxProvisionedPackage -Online -PackageName $AppxPrvTargetPN

            }

    }

Monday, February 8, 2021

Suppress screen output errors

Insert this at the top of a script to stop error information appearing in the output that you are trying to read!

$ErrorActionPreference= 'silentlycontinue'

Tuesday, January 12, 2021

Change Permissions and overwrite img0.jpg

 C:\Windows\Web\Wallpaper\Windows\img0.jpg is typically the default wallpaper for Windows 10.  As part of the OS can be tricky to change unless you adjust the permissions.  This script was designed to run in an SCCM task sequence

$ScriptsPath = Split-Path - Parent $MyInvocation.MyCommand.Path

$img0new = $ScriptsPath+"\img0.jpg
$img0 = "C:\Windows\Web\Wallpaper\Windows\img0.jpg

#modify default permissions on img0.jpg
takeown /f $img0
icacls $img0 /Grant 'System:(F)'

Copy-Item -Path $img0new -Destination $img0 -Force