Compare commits

...
2 Commits
Author SHA1 Message Date
alexrg b3e40fabb5 Deploy branded default account picture 2026-09-04 15:09:19 -06:00
alexrg a850b56a02 Enforce logon presentation through domain policy 2026-09-04 14:53:51 -06:00
5 changed files with 102 additions and 13 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

+4 -1
View File
@@ -85,7 +85,10 @@ The generic SGU credential is rendered as a dedicated branded tile instead of
being grouped below the anonymous **Other user** tile. Machine policy assigns being grouped below the anonymous **Other user** tile. Machine policy assigns
the SGU CLSID as the default provider, hides the last signed-in identity, and the SGU CLSID as the default provider, hides the last signed-in identity, and
disables local-user enumeration while retaining the built-in Microsoft password disables local-user enumeration while retaining the built-in Microsoft password
provider and its **Other user** recovery path. It enumerates one provider and its **Other user** recovery path. The computer GPO also applies
Windows' native default account picture to named Windows accounts; client
enrollment installs the La Salle mascot bitmap in Windows' standard account-picture
location before that GPO takes effect. It enumerates one
`CPFT_TILE_IMAGE` and places the `CPFT_LARGE_TEXT` heading immediately after it `CPFT_TILE_IMAGE` and places the `CPFT_LARGE_TEXT` heading immediately after it
with `CPFS_DISPLAY_IN_SELECTED_TILE`. LogonUI owns field typography and vertical with `CPFS_DISPLAY_IN_SELECTED_TILE`. LogonUI owns field typography and vertical
tile order: on Windows 10 and 11, the account-name title used by **Other user** tile order: on Windows 10 and 11, the account-name title used by **Other user**
+70
View File
@@ -35,6 +35,8 @@ $providerRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authent
$classRegistryPath = "HKLM:\SOFTWARE\Classes\CLSID\$providerClassId\InprocServer32" $classRegistryPath = "HKLM:\SOFTWARE\Classes\CLSID\$providerClassId\InprocServer32"
$defaultProviderPolicyPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System' $defaultProviderPolicyPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System'
$interactiveLogonPolicyPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' $interactiveLogonPolicyPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
$accountPictureSourcePath = Join-Path $PublishPath 'branding\user.png'
$accountPictureDirectory = Join-Path $env:ProgramData 'Microsoft\User Account Pictures'
$identity = [Security.Principal.WindowsIdentity]::GetCurrent() $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]::new($identity) $principal = [Security.Principal.WindowsPrincipal]::new($identity)
@@ -56,6 +58,70 @@ function Test-DotNet10Runtime {
return $false return $false
} }
function Install-DefaultAccountPicture {
param([Parameter(Mandatory)][string]$SourcePath)
if (-not (Test-Path -LiteralPath $SourcePath -PathType Leaf)) {
return $false
}
Add-Type -AssemblyName System.Drawing
New-Item -ItemType Directory -Path $accountPictureDirectory -Force | Out-Null
function Save-AccountPicture {
param(
[Parameter(Mandatory)][Drawing.Image]$Image,
[Parameter(Mandatory)][string]$Path,
[Parameter(Mandatory)][Drawing.Imaging.ImageFormat]$Format
)
$stream = [IO.MemoryStream]::new()
try {
$Image.Save($stream, $Format)
[IO.File]::WriteAllBytes($Path, $stream.ToArray())
}
finally {
$stream.Dispose()
}
}
$source = [Drawing.Image]::FromFile($SourcePath)
try {
foreach ($size in @(192, 48, 40, 32)) {
$bitmap = [Drawing.Bitmap]::new($size, $size)
try {
$graphics = [Drawing.Graphics]::FromImage($bitmap)
try {
$graphics.Clear([Drawing.Color]::Transparent)
$graphics.InterpolationMode = [Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$graphics.DrawImage($source, [Drawing.Rectangle]::new(0, 0, $size, $size))
Save-AccountPicture -Image $bitmap `
-Path (Join-Path $accountPictureDirectory "user-$size.png") `
-Format ([Drawing.Imaging.ImageFormat]::Png)
}
finally {
$graphics.Dispose()
}
}
finally {
$bitmap.Dispose()
}
}
Save-AccountPicture -Image $source `
-Path (Join-Path $accountPictureDirectory 'user.png') `
-Format ([Drawing.Imaging.ImageFormat]::Png)
Save-AccountPicture -Image $source `
-Path (Join-Path $accountPictureDirectory 'user.bmp') `
-Format ([Drawing.Imaging.ImageFormat]::Bmp)
}
finally {
$source.Dispose()
}
return $true
}
if (-not (Test-DotNet10Runtime)) { if (-not (Test-DotNet10Runtime)) {
if (-not $InstallDotNetRuntime) { if (-not $InstallDotNetRuntime) {
throw 'Microsoft .NET 10 x64 runtime is required. Re-run with -InstallDotNetRuntime or install it first.' throw 'Microsoft .NET 10 x64 runtime is required. Re-run with -InstallDotNetRuntime or install it first.'
@@ -166,6 +232,10 @@ if ($PSCmdlet.ShouldProcess($installPath, 'Install and register the SGU Credenti
[IO.File]::WriteAllText($completeMarker, $packageHash, [Text.UTF8Encoding]::new($false)) [IO.File]::WriteAllText($completeMarker, $packageHash, [Text.UTF8Encoding]::new($false))
} }
# The domain GPO selects the Windows default account picture. Install its
# branded bitmap during enrollment so no per-machine manual setup is needed.
Install-DefaultAccountPicture -SourcePath $accountPictureSourcePath | Out-Null
New-Item -ItemType Directory -Path (Split-Path $settingsPath -Parent) -Force | Out-Null New-Item -ItemType Directory -Path (Split-Path $settingsPath -Parent) -Force | Out-Null
$settingsJson = @{ $settingsJson = @{
BrokerEndpoint = $BrokerEndpoint BrokerEndpoint = $BrokerEndpoint
+2
View File
@@ -111,6 +111,8 @@ Copy-Item -Path (Join-Path $providerOutput '*') `
-Destination (New-Item -ItemType Directory ` -Destination (New-Item -ItemType Directory `
-Path (Join-Path $clientRoot 'payload\credential-provider') -Force).FullName ` -Path (Join-Path $clientRoot 'payload\credential-provider') -Force).FullName `
-Recurse -Force -Recurse -Force
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'assets\branding\lasalle-mascot-account.png') `
-Destination (Join-Path $clientRoot 'payload\credential-provider\branding\user.png')
Copy-RequiredFile -Source $runtimeInstaller.FullName ` Copy-RequiredFile -Source $runtimeInstaller.FullName `
-Destination (Join-Path $clientRoot "payload\prerequisites\$($runtimeInstaller.Name)") -Destination (Join-Path $clientRoot "payload\prerequisites\$($runtimeInstaller.Name)")
Write-PackageManifest -PackageRoot $clientRoot -PackageVersion $Version -PackageKind Client Write-PackageManifest -PackageRoot $clientRoot -PackageVersion $Version -PackageKind Client
+26 -12
View File
@@ -61,15 +61,29 @@ elseif (-not $existingLinkEnabled -and
$dataCollectionKey = 'HKLM\Software\Policies\Microsoft\Windows\DataCollection' $dataCollectionKey = 'HKLM\Software\Policies\Microsoft\Windows\DataCollection'
$powerPolicyRoot = 'HKLM\Software\Policies\Microsoft\Power\PowerSettings' $powerPolicyRoot = 'HKLM\Software\Policies\Microsoft\Power\PowerSettings'
$credentialProviderPolicyKey = 'HKLM\Software\Policies\Microsoft\Windows\System'
$interactiveLogonPolicyKey = 'HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System'
$accountPicturePolicyKey = 'HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer'
$providerClassId = '{D789CFD8-5AD4-489F-9B83-7EB5D9D09335}'
$policies = @( $policies = @(
@{ Key = $dataCollectionKey; Name = 'AllowTelemetry'; Value = 0 }, @{ Key = $dataCollectionKey; Name = 'AllowTelemetry'; Type = 'DWord'; Value = 0 },
@{ Key = $dataCollectionKey; Name = 'DisableTelemetryOptInSettingsUx'; Value = 1 }, @{ Key = $dataCollectionKey; Name = 'DisableTelemetryOptInSettingsUx'; Type = 'DWord'; Value = 1 },
@{ Key = $dataCollectionKey; Name = 'DisableTelemetryOptInChangeNotification'; Value = 1 }, @{ Key = $dataCollectionKey; Name = 'DisableTelemetryOptInChangeNotification'; Type = 'DWord'; Value = 1 },
@{ Key = $dataCollectionKey; Name = 'DisableDiagnosticDataViewer'; Value = 1 }, @{ Key = $dataCollectionKey; Name = 'DisableDiagnosticDataViewer'; Type = 'DWord'; Value = 1 },
@{ Key = 'HKLM\Software\Policies\Microsoft\Windows\OOBE'; Name = 'DisablePrivacyExperience'; Value = 1 }, @{ Key = 'HKLM\Software\Policies\Microsoft\Windows\OOBE'; Name = 'DisablePrivacyExperience'; Type = 'DWord'; Value = 1 },
@{ Key = 'HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System'; Name = 'EnableFirstLogonAnimation'; Value = 0 }, @{ Key = $interactiveLogonPolicyKey; Name = 'EnableFirstLogonAnimation'; Type = 'DWord'; Value = 0 },
@{ Key = 'HKLM\Software\Policies\Microsoft\Windows\LocationAndSensors'; Name = 'DisableLocation'; Value = 1 }, @{ Key = 'HKLM\Software\Policies\Microsoft\Windows\LocationAndSensors'; Name = 'DisableLocation'; Type = 'DWord'; Value = 1 },
@{ Key = 'HKLM\Software\Policies\Microsoft\Windows\AppPrivacy'; Name = 'LetAppsAccessLocation'; Value = 2 } @{ Key = 'HKLM\Software\Policies\Microsoft\Windows\AppPrivacy'; Name = 'LetAppsAccessLocation'; Type = 'DWord'; Value = 2 },
# Enrollment selects the provider before domain join; this computer GPO
# becomes the authoritative, self-healing configuration afterwards.
@{ Key = $credentialProviderPolicyKey; Name = 'DefaultCredentialProvider'; Type = 'String'; Value = $providerClassId },
@{ Key = $credentialProviderPolicyKey; Name = 'EnumerateLocalUsers'; Type = 'DWord'; Value = 0 },
@{ Key = $interactiveLogonPolicyKey; Name = 'DontDisplayLastUserName'; Type = 'DWord'; Value = 1 },
# Use Windows' native default account image for named user tiles. LogonUI
# retains ownership of the anonymous Other user tile and its circular mask.
@{ Key = $accountPicturePolicyKey; Name = 'UseDefaultTile'; Type = 'DWord'; Value = 1 }
) )
$powerSettingIds = @( $powerSettingIds = @(
@@ -80,8 +94,8 @@ $powerSettingIds = @(
) )
foreach ($settingId in $powerSettingIds) { foreach ($settingId in $powerSettingIds) {
$settingKey = "$powerPolicyRoot\$settingId" $settingKey = "$powerPolicyRoot\$settingId"
$policies += @{ Key = $settingKey; Name = 'ACSettingIndex'; Value = 0 } $policies += @{ Key = $settingKey; Name = 'ACSettingIndex'; Type = 'DWord'; Value = 0 }
$policies += @{ Key = $settingKey; Name = 'DCSettingIndex'; Value = 0 } $policies += @{ Key = $settingKey; Name = 'DCSettingIndex'; Type = 'DWord'; Value = 0 }
} }
foreach ($policy in $policies) { foreach ($policy in $policies) {
@@ -92,7 +106,7 @@ foreach ($policy in $policies) {
-Server $DomainController ` -Server $DomainController `
-Key $policy.Key ` -Key $policy.Key `
-ValueName $policy.Name ` -ValueName $policy.Name `
-Type DWord ` -Type $policy.Type `
-Value $policy.Value | Out-Null -Value $policy.Value | Out-Null
} }
} }
@@ -105,7 +119,7 @@ foreach ($policy in $policies) {
-Server $DomainController ` -Server $DomainController `
-Key $policy.Key ` -Key $policy.Key `
-ValueName $policy.Name -ValueName $policy.Name
$configuredPolicies[$policy.Name + '@' + $policy.Key] = [int]$configured.Value $configuredPolicies[$policy.Name + '@' + $policy.Key] = $configured.Value
} }
$link = @(Get-GPInheritance -Target $TargetOuDn -Domain $domainName -Server $DomainController).GpoLinks | $link = @(Get-GPInheritance -Target $TargetOuDn -Domain $domainName -Server $DomainController).GpoLinks |
Where-Object DisplayName -eq $GpoName | Where-Object DisplayName -eq $GpoName |