-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtoggle-guest-local-administrators.ps1
More file actions
159 lines (131 loc) · 5.07 KB
/
toggle-guest-local-administrators.ps1
File metadata and controls
159 lines (131 loc) · 5.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<#
.SYNOPSIS
Removes a specified principal (default: Guest) from a specified local group (default: Administrators).
.DESCRIPTION
Defensive remediation helper. This script:
- Verifies it is running with Administrator privileges
- Captures group membership before/after
- Removes the specified principal from the target local group (SupportsShouldProcess => -WhatIf / -Confirm)
- Optionally disables the account (e.g., Guest)
- Logs actions to a timestamped file
NOTE:
- Test in a non-production environment first.
- Confirm business impact before removing group membership or disabling accounts.
.PARAMETER PrincipalName
The local principal to remove (default: Guest). You can also pass COMPUTERNAME\Guest.
.PARAMETER LocalGroup
The local group to remove the principal from (default: Administrators).
.PARAMETER DisableAccount
If set, disables the local account after group removal (where supported).
.PARAMETER LogPath
Optional log file path. If not provided, a timestamped log is created under scripts/logs/.
.EXAMPLE
.\toggle-guest-local-administrators.ps1 -WhatIf
.EXAMPLE
.\toggle-guest-local-administrators.ps1 -PrincipalName "Guest" -LocalGroup "Administrators" -Confirm
.EXAMPLE
.\toggle-guest-local-administrators.ps1 -PrincipalName "Guest" -DisableAccount -Confirm
#>
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
param(
[Parameter(Mandatory = $false)]
[string]$PrincipalName = "Guest",
[Parameter(Mandatory = $false)]
[string]$LocalGroup = "Administrators",
[Parameter(Mandatory = $false)]
[switch]$DisableAccount,
[Parameter(Mandatory = $false)]
[string]$LogPath
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
function Test-IsAdministrator {
$currentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($currentIdentity)
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
function New-DefaultLogPath {
$logsDir = Join-Path -Path $PSScriptRoot -ChildPath "logs"
if (-not (Test-Path $logsDir)) { New-Item -ItemType Directory -Path $logsDir | Out-Null }
$ts = Get-Date -Format "yyyyMMdd-HHmmss"
return Join-Path -Path $logsDir -ChildPath "toggle-guest-local-administrators-$ts.log"
}
function Write-Log {
param([string]$Message)
$line = "$(Get-Date -Format o) $Message"
Write-Output $line
Add-Content -Path $script:LogPath -Value $line
}
function Get-GroupMembersSafe {
param([string]$Group)
try {
return Get-LocalGroupMember -Group $Group
} catch {
# Fallback: parse net localgroup output
$raw = & cmd.exe /c "net localgroup ""$Group"""
$members = @()
$capture = $false
foreach ($ln in $raw) {
if ($ln -match "^-{3,}$") { $capture = $true; continue }
if ($capture -and $ln -match "The command completed successfully") { break }
if ($capture) {
$name = $ln.Trim()
if ($name) {
$members += [pscustomobject]@{ Name = $name; ObjectClass = "Unknown" }
}
}
}
return $members
}
}
if (-not (Test-IsAdministrator)) {
throw "This script must be run as Administrator."
}
if (-not $LogPath) { $LogPath = New-DefaultLogPath }
$script:LogPath = $LogPath
Write-Log "Starting remediation"
Write-Log "Target group: '$LocalGroup'"
Write-Log "Target principal: '$PrincipalName'"
Write-Log "DisableAccount: $DisableAccount"
Write-Log "LogPath: $LogPath"
$before = Get-GroupMembersSafe -Group $LocalGroup
Write-Log "Group membership BEFORE:"
$before | ForEach-Object { Write-Log " - $($_.Name)" }
# Accept "Guest" or "COMPUTER\Guest"
$principalPattern = "(^|\\)$([Regex]::Escape($PrincipalName))$"
$matches = @($before | Where-Object { $_.Name -match $principalPattern })
if ($matches.Count -eq 0) {
Write-Log "No matching member found for '$PrincipalName' in '$LocalGroup'. Nothing to do."
} else {
foreach ($m in $matches) {
$memberToRemove = $m.Name
if ($PSCmdlet.ShouldProcess("$LocalGroup", "Remove member '$memberToRemove'")) {
try {
Remove-LocalGroupMember -Group $LocalGroup -Member $memberToRemove -ErrorAction Stop
Write-Log "Removed '$memberToRemove' from '$LocalGroup'."
} catch {
& cmd.exe /c "net localgroup ""$LocalGroup"" ""$memberToRemove"" /delete" | Out-Null
Write-Log "Removed '$memberToRemove' from '$LocalGroup' via net localgroup fallback."
}
} else {
Write-Log "WhatIf/Confirm prevented removal of '$memberToRemove' from '$LocalGroup'."
}
}
}
if ($DisableAccount) {
if ($PSCmdlet.ShouldProcess("$PrincipalName", "Disable local user account")) {
try {
Disable-LocalUser -Name $PrincipalName -ErrorAction Stop
Write-Log "Disabled local user '$PrincipalName'."
} catch {
Write-Log "Could not disable '$PrincipalName' (cmdlet unavailable or account missing). Details: $($_.Exception.Message)"
}
} else {
Write-Log "WhatIf/Confirm prevented disabling '$PrincipalName'."
}
}
$after = Get-GroupMembersSafe -Group $LocalGroup
Write-Log "Group membership AFTER:"
$after | ForEach-Object { Write-Log " - $($_.Name)" }
Write-Log "Completed remediation"
exit 0