Configuring the Windows 10 Pro Lock Screen using MEM

Windows 10 Enterprise supports a specific MEM policy to configure the Windows 10 Lock screen for End-users. If you’re unlucky enough to be on a lesser Windows 10 version, you’ll need to trick the OS into thinking the lock screen is modified by the user instead of through a policy.

Here’s a simple ARM template for blob storage and a PS script to deploy through MEM in user context to configure the lock screen of your users:

1-click ARM template

And the script itself (don’t forget to configure the image URL):

<#
    .SYNOPSIS
    Sets custom lock screen based on file in an Azure Storage Blob container
    See blob template to automatically configure a blob container: https://gitlab.com/Lieben/assortedFunctions/-/blob/master/ARM%20templates/blob%20storage%20with%20container%20for%20Teams%20Backgrounds%20and%20public%20access.json
   
    .NOTES
    filename: set-windows10LockScreen.ps1
    author: Jos Lieben
    blog: www.lieben.nu
    created: 13/05/2021
#>

$changedDate = "2021-05-13"
$lockscreenFileURL = "https://tasdsadgsadsad.blob.core.windows.net/teamsbackgrounds/figure-a.jpg" #this is the full URL to the desired lock screen image

Start-Transcript -Path (Join-Path -Path $Env:TEMP -ChildPath "set-windows10LockScreen.log")

$tempFile = (Join-Path $Env:TEMP -ChildPath "img100.jpg")

try{
    Write-Output "downloading lock screen file from $lockscreenFileURL"
    Invoke-WebRequest -Uri $lockscreenFileURL -UseBasicParsing -Method GET -OutFile $tempFile
    Write-Output "file downloaded to $tempFile"
}catch{
    Write-Output "Failed to download file, aborting"
    Write-Error $_ -ErrorAction SilentlyContinue
    Exit
}

[Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,ContentType=WindowsRuntime] | Out-Null
Add-Type -AssemblyName System.Runtime.WindowsRuntime

$asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | ? { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' })[0]
Function Await($WinRtTask, $ResultType) {
    $asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
    $netTask = $asTask.Invoke($null, @($WinRtTask))
    $netTask.Wait(-1) | Out-Null
    $netTask.Result
}

Function AwaitAction($WinRtAction) {
    $asTask = ([System.WindowsRuntimeSystemExtensions].GetMethods() | ? { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and !$_.IsGenericMethod })[0]
    $netTask = $asTask.Invoke($null, @($WinRtAction))
    $netTask.Wait(-1) | Out-Null
}

[Windows.Storage.StorageFile,Windows.Storage,ContentType=WindowsRuntime] | Out-Null
		
try{
	$image = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($tempFile)) ([Windows.Storage.StorageFile])
    Write-Output "Image loaded from $tempFile"
}catch {
    Write-Output "Failed to load image from $tempFile"
    Write-Error $_ -ErrorAction SilentlyContinue
    Exit
} 
       
try{ 
    Write-Output "Setting image as lock screen image"
    AwaitAction ([Windows.System.UserProfile.LockScreen]::SetImageFileAsync($image))
    Write-Output "$tempFile configured as lock screen image"
    Remove-Item -Path $tempFile -Force -Confirm:$False
}catch{
    Write-Output "Failed to set lock screen image"
    Write-Error $_ -ErrorAction SilentlyContinue
} 

Write-Output "Script complete"
Stop-Transcript

Source: https://gitlab.com/Lieben/assortedFunctions/-/blob/master/set-windows10LockScreen.ps1

Deploying in user context:

Copying filesystem permissions for long paths using AlphaFS nad Powershell

AlphaFS is my go-to library when working with Long Paths, since PowerShell’s built in functions do not support long paths and error out.

As I couldn’t find good PowerShell examples using GetAccessControl and SetAccessControl with the AlphaFS library, I wanted to post my script here for those googling an example 🙂

Param(
    [String]$sourcePath,
    [String]$targetPath
)

[System.Reflection.Assembly]::UnsafeLoadFrom('https://gitlab.com/Lieben/assortedFunctions/-/raw/master/lib/AlphaFS.dll?inline=false')

$sourcePath = [Alphaleonis.Win32.Filesystem.Path]::GetLongPath($sourcePath)
$targetPath = [Alphaleonis.Win32.Filesystem.Path]::GetLongPath($targetPath)

if([Alphaleonis.Win32.Filesystem.Directory]::Exists($sourcePath)){
    Write-Verbose "Detected sourcePath as FOLDER"
    $sourceObject = New-Object Alphaleonis.Win32.Filesystem.DirectoryInfo($sourcePath)
}elseif([Alphaleonis.Win32.Filesystem.File]::Exists($sourcePath)){
    Write-Verbose "Detected sourcePath as FILE"
    $sourceObject = New-Object Alphaleonis.Win32.Filesystem.FileInfo($sourcePath)
}else{
    Throw "sourcePath not found"
}

if([Alphaleonis.Win32.Filesystem.Directory]::Exists($targetPath)){
    Write-Verbose "Detected targetPath as FOLDER"   
    $targetObject = New-Object Alphaleonis.Win32.Filesystem.DirectoryInfo($targetPath)
}elseif([Alphaleonis.Win32.Filesystem.File]::Exists($targetPath)){
    Write-Verbose "Detected targetPath as FILE"
    $targetObject = New-Object Alphaleonis.Win32.Filesystem.FileInfo($targetPath)
}else{
    Throw "targetPath not found"
}

$sourceACL = $sourceObject.GetAccessControl("Access")
$targetObject.SetAccessControl($sourceACL)

Git: https://gitlab.com/Lieben/assortedFunctions/-/blob/master/copy-longPathACL.ps1

Distributing Teams Backgrounds to all users using MEM

I’ve seen a few examples of distributing a set of teams backgrounds to users in MEM, mostly they seemed a little overly complex, especially in targetting all local users and/or packaging the script into an application. I wanted to:

  • Deploy using MEM PS script in user context
  • Avoid access controls on the storage location of the backgrounds
  • Simplify the creation of required Azure resources
  • Have a simple update procedure

Here’s my take, first autocreate a blob storage location using my template:

https://gitlab.com/Lieben/assortedFunctions/-/blob/master/ARM%20templates/blob%20storage%20with%20container%20for%20Teams%20Backgrounds%20and%20public%20access.json

Note down the name you used for the storage account (which was the only parameter to the template), and use it to configure this PowerShell script:

https://gitlab.com/Lieben/assortedFunctions/-/blob/master/add-teamsBackgroundsFromBlobContainer.ps1

Finally, deploy the script to a group of your users in MEM, in their own context: