X-Git-Url: https://vilimpoc.org/repos/dotfiles/blobdiff_plain/ea83999a6597ec98e54525a0b832967c4308978c..HEAD:/setup-windows-with-uac.ps1?ds=inline diff --git a/setup-windows-with-uac.ps1 b/setup-windows-with-uac.ps1 index 17d02ee..9f3d8ae 100644 --- a/setup-windows-with-uac.ps1 +++ b/setup-windows-with-uac.ps1 @@ -17,36 +17,12 @@ - Windows Driver Kit 10.0.26100 - Windows Performance Toolkit - xperf, wpr and Windows Performance Analyzer (wpa.exe) - on the machine PATH - - Performance Log Users membership for one ordinary account, so it can run - user-mode ETW sessions (xperf -start ... -on ) without elevation. - Kernel traces are NOT covered - the NT Kernel Logger is admin-only; see the - step for what was measured. Change $VsInstallerUrl below to the Professional or Enterprise bootstrapper if needed: Professional : https://aka.ms/vs/17/release/vs_professional.exe Enterprise : https://aka.ms/vs/17/release/vs_enterprise.exe #> -param( - # Account to put in Performance Log Users (see the "ETW session control" - # step, which runs first). Defaults to the interactive console user, but - # setup-windows.bat passes it explicitly: with over-the-shoulder elevation - # THIS script runs as the administrator whose credentials went into the UAC - # prompt, not as the user who started the batch file, so $env:USERNAME here - # is the wrong answer. - # - # Pass an empty string to skip it; adding an account later is one - # `net localgroup` away. - [string] $TraceUser = '', - - # Do the ETW step and nothing else. It is a group membership and no - # downloads, where a full run is dominated by the three Visual Studio - # passes, which take minutes even when they have nothing to do. It is why - # that step runs FIRST: -EtwRightsOnly is then just an early exit rather - # than a set of guards down the rest of the script. - [switch] $EtwRightsOnly -) - $ErrorActionPreference = 'Stop' function Write-Step([string]$Msg) { @@ -132,121 +108,6 @@ try { Start-Transcript -Path $LogFile -Force | Out-Null } catch {} try { -# --------------------------------------------------------------------------- -# ETW session control for an ordinary account -# -# Creating or controlling an event tracing session - even a user-mode one naming -# a single provider - is checked against the security descriptor ETW keeps per -# provider GUID under HKLM\SYSTEM\CurrentControlSet\Control\WMI\Security. The -# default grants the session-control rights (TRACELOG_CREATE_ONDISK, -# TRACELOG_CREATE_REALTIME, TRACELOG_GUID_ENABLE, TRACELOG_LOG_EVENT) to SYSTEM, -# Administrators, the service accounts and BUILTIN\Performance Log Users, and to -# nobody else. Its own description says members "may ... enable trace providers, -# and collect event traces", and that is what membership buys: -# -# xperf -start MySession -on Microsoft-Windows-Kernel-Process -f trace.etl -# xperf -stop MySession -# -# runs unelevated for a member and is "Access is denied. (0x5)" for everyone -# else. Enough to trace your own application's providers without a UAC prompt. -# -# Membership is read into the access token at LOGON, so the account has to sign -# out and back in. Any NEW logon does it - an ssh login into this box is one, -# which is the quick way to check without dropping the desktop. -# -# WHAT THIS DOES NOT BUY: system-wide kernel traces. `xperf -on base` and -# `wpr -start` drive the NT Kernel Logger, which is reserved for Administrators -# and LocalSystem - Microsoft documents Performance Log Users access as -# explicitly NOT extending to it. Measured here, so that nobody repeats it: with -# the account in the group, SeSystemProfilePrivilege ("Profile system -# performance") granted to that group, and an explicit ACE giving the group -# TRACELOG_ACCESS_KERNEL_LOGGER on SystemTraceControlGuid - all three in place, -# across a reboot - xperf still answered -# -# xperf: error: NT Kernel Logger: Access is denied. (0x5). -# -# It is not a check an ACE overrides. Those two grants were dropped again rather -# than left on the box earning nothing, and CPU sampling and whole-system traces -# are elevated work: run xperf, wpr or VTune from an Administrator prompt. -# -# Analysis needs none of this either way - wpa.exe opens an existing .etl as a -# plain user. -# --------------------------------------------------------------------------- -Write-Step 'ETW session control (non-elevated user-mode tracing)' -$PerfLogUsersSid = 'S-1-5-32-559' # BUILTIN\Performance Log Users -try { - # Fall back to the console user when the caller did not name one: with - # over-the-shoulder elevation that is the person who started - # setup-windows.bat, which is who wants to trace. - $target = $TraceUser - if (-not $target) { - $target = (Get-CimInstance Win32_ComputerSystem -ErrorAction SilentlyContinue).UserName - if ($target) { Write-Host " No -TraceUser given; using the console user $target" } - } - - if (-not $target) { - Write-Warning 'No account to add to Performance Log Users (pass -TraceUser DOMAIN\user).' - Write-Warning 'To do it later:' - Write-Warning ' net localgroup "Performance Log Users" DOMAIN\user /add' - } else { - # Resolve to a SID first: it validates the name, and it is what the - # membership check compares, so a member spelled ".\claude" in one place - # and "LATISLAB\claude" in another is still recognised as the same account. - $targetSid = (New-Object System.Security.Principal.NTAccount($target)).Translate( - [System.Security.Principal.SecurityIdentifier]) - - # By SID, never by name: "Performance Log Users" is localised, and - # Get-LocalGroup -SID is how this stays correct on a non-English box. - $group = Get-LocalGroup -SID $PerfLogUsersSid - - # Get-LocalGroupMember throws on a group holding a SID that no longer - # resolves (a known Windows 10 bug), so a failure to READ the membership - # must not stop us from writing it - fall through and let the add report. - $already = $false - try { - $already = @(Get-LocalGroupMember -SID $PerfLogUsersSid | - Where-Object { $_.SID.Value -eq $targetSid.Value }).Count -gt 0 - } catch { - Write-Host " (could not enumerate $($group.Name) members: $($_.Exception.Message))" -ForegroundColor DarkGray - } - - if ($already) { - Write-Host " OK: $target is already in $($group.Name)" - } else { - try { - Add-LocalGroupMember -SID $PerfLogUsersSid -Member $targetSid.Value - } catch { - # "already a member" is only reachable when the enumeration above - # failed, and is not an error. Matched on the type NAME rather - # than in a typed catch clause: catch types are resolved when the - # script is PARSED, before the LocalAccounts module has been - # autoloaded, so naming the type there is a parse error that - # would take the whole script down. - if ($_.Exception.GetType().Name -ne 'MemberExistsException') { throw } - } - Write-Host " Added $target to $($group.Name)" - } - - Write-Host '' - Write-Host " $target must sign out and back in before this takes effect." -ForegroundColor Yellow - Write-Host ' Then, from that account (NOT elevated):' -ForegroundColor Yellow - Write-Host ' xperf -start T -on Microsoft-Windows-Kernel-Process -f trace.etl' -ForegroundColor Yellow - Write-Host ' xperf -stop T' -ForegroundColor Yellow - } -} catch { - Write-Warning "Performance Log Users membership failed: $($_.Exception.Message)" - Write-Warning 'Do it by hand with:' - Write-Warning ' net localgroup "Performance Log Users" /add' -} - -if ($EtwRightsOnly) { - # `exit` inside the try still runs the finally below, so the transcript is - # stopped and the log is left readable by the non-elevated caller. - Write-Host "`n-EtwRightsOnly: skipping the installs." -ForegroundColor Green - exit 0 -} - - # --------------------------------------------------------------------------- # Base tools via winget # ---------------------------------------------------------------------------