This avoids using get-localgroupmembers, which gives errors when there are ‘bad’ records in the group
# Purpose : Add and/or remove members from local administrators group
# Application: Add a domain group to the local admins group - REQ3084421 CHG0268600
# Author : Roger C 3-Jun-2018 / 2022-12-28
# Usage: use this in a baseline
# Edits: Define the $ShouldRemediate variable:
# For Discovery script, use $False
# For Remediation script, use $True
# Edits: Define the members that you want to see in Local Administrators
$ScriptName = "Add_to_Local_Admins"
$Version = "1.4.3.21"
$ShouldRemediate = $True # set this when you setup the baseline
$Compliant = $True # Assume compliance unless we find otherwise
$Remediated = $False # Assume not remediated unless we find otherwise
$IssuesFound = 0 # How many correctable issues were found
# $ExtraMembers = [System.Collections.ArrayList]@() # Members to be removed
# $MissingMembers = [System.Collections.ArrayList]@() # Members to added
$RequiredMembers = [System.Collections.ArrayList]@() # Members that we want in the group
$DisallowedMembers = [System.Collections.ArrayList]@() # Members that we do not want in the group
#===========================================================
# Define accounts that should be in the administrators group
#===========================================================
$Null = $RequiredMembers.Add([PSCustomObject][Ordered]@{ ObjectClass = 'Group'; Name = 'CORP\DTRA-AST-LOCAL-ADMIN-W'})
#==================================================================
# Define accounts that should be not be in the administrators group
#==================================================================
$Null = $DisallowedMembers.Add([PSCustomObject][Ordered]@{ ObjectClass = 'Group'; Name = 'CORP\DTRA-AST-LOCAL-ADMIN'})
#===============================
# Setup logging function
#===============================
$LogPath = "$env:windir\ccmcache\DCM_Logs"
#$LogPath = "c:\temp"
$Logfilename = "$LogPath\$ScriptName.log"
Function Writeln-Log( $s ) {
$TempWhatIf = $WhatIfPreference; $WhatIfPreference = $False
$s = (Get-Date -Format "HH:mm:ss.ff") + " $s"
#write-Host $s
$s | Add-Content -Path ($LogFilename) -ErrorAction SilentlyContinue | Out-Null
$WhatIfPreference = $TempWhatIf
}
$LogFolder = split-path $Logfilename -Parent
# Housekeeping: Remove lines if needed to reduce size and preserve the latest
if (!(Get-Item -Path $LogFolder -ErrorAction SilentlyContinue)) {New-Item -Path $LogFolder -ItemType Directory | Out-Null}
$MaxFilesize = 500 * 1KB # 500KB
$ResizeTo = 8000 # 8000 lines (about 350K)
if (Get-ChildItem $Logfilename -ErrorAction SilentlyContinue) {
$LogFileSize = (Get-ChildItem $Logfilename).Length
if ($LogFileSize -gt $MaxFilesize) { # Setup a header for the start of the file
$LogResetStamp = @("****************************************************************")
$LogResetStamp += @("$((Get-Date).tostring()) - Log size exceeded $($MaxFilesize / 1kb)KB.")
$LogResetStamp += @("$((Get-Date).tostring()) - Reduced to last $ResizeTo lines to reduce size")
$LogResetStamp += @("****************************************************************")
$LogResetStamp + (get-content $Logfilename | # Overwrite with headers plus the last desired lines
select -last $ResizeTo) |
Set-Content $Logfilename | Out-Null
}
}
# INITIALIZE - write the header line
("===[ " + (get-date -uformat "%m/%d/%Y" ) `
+ " ]===[ Computer: " + (hostname) `
+ " ]===[ User: " + [System.Security.Principal.WindowsIdentity]::GetCurrent().Name `
+ " ]===[ $ScriptName V.$Version" `
+ " ]=======" ) |
Add-Content -Path ($LogFilename)
#==============================================================================
#============================== end of logfile setup ==========================
#==============================================================================
#==============================================================================
#===[ Functions ]==============================================================
#==============================================================================
Function My-Get-LocalGroupMembers {
param(
[Parameter(ValuefromPipeline=$true)][array]$server = $env:computername,
$GroupName = "Administrators"
)
PROCESS {
$finalresult = @()
$computer = [ADSI]"WinNT://$server"
if (!($groupName)) {
$Groups = $computer.psbase.Children | Where {$_.psbase.schemaClassName -eq "group"} | select -expand name
} else {
$groups = $groupName
}
$CurrentDomain = ((Get-WMIObject Win32_ComputerSystem | Select-Object -ExpandProperty Domain) -split '\.')[0]
foreach ($group in $groups) {
$gmembers = $null
$LocalGroup = [ADSI]("WinNT://$server/$group,group")
$GMembers = $LocalGroup.psbase.invoke("Members")
$GMemberProps = @{ObjectClass="";Name="";PrincipalSource=""}
$MemberResult = @()
if ($gmembers) {
foreach ($gmember in $gmembers) {
$membertable = new-object psobject -Property $GMemberProps
$name = $gmember.GetType().InvokeMember("Name",'GetProperty', $null, $gmember, $null)
#$sid = $gmember.GetType().InvokeMember("objectsid",'GetProperty', $null, $gmember, $null)
$class = $gmember.GetType().InvokeMember("Class",'GetProperty', $null, $gmember, $null)
$ads = $gmember.GetType().InvokeMember("adspath",'GetProperty', $null, $gmember, $null) -replace 'WinNT://',''
$Name = ''
$Domain = ''
if (($ads -split '/').count -eq 2) { # (eg CORP/Domain Admins)
$Domain = ($ads -split '/')[0]
$Name = ($ads -split '/')[1]
$PrincipalSource = "ActiveDirectory"
} else { # Domain and local hostname (eg CORP/computername/localadmin)
$Domain = ($ads -split '/')[1]
$Name = ($ads -split '/')[2]
$PrincipalSource = "Local"
}
if ($Name) {
$MemberTable.ObjectClass= "$class"
$MemberTable.name= "$Domain\$name"
$membertable.PrincipalSource = $PrincipalSource
$MemberResult += $MemberTable
}
}
}
$finalresult += $MemberResult
}
$finalresult | select ObjectClass,Name,PrincipalSource
} # end Process section
}
Function My-Test-LocalAdmin ($TestMember) {
# Looks for the given name, returns $True if found in local admins
$Found = $False
foreach ($GroupMember in (My-Get-LocalGroupMembers)) {
if ( ($GroupMember.Name -eq $TestMember.Name ) -and
($GroupMember.ObjectClass -eq $TestMember.ObjectClass ) ) {
$Found = $true
}
}
Return $Found
}
#==============================================================================
#===[ Main ]===================================================================
#==============================================================================
Writeln-Log "Should Remediate: $ShouldRemediate"
Writeln-Log "Processing Begins"
$GroupMembers = My-Get-LocalGroupMembers
if (-not $GroupMembers) {
Writeln-Log 'Noncompliant - Powershell Error'
Writeln-Log 'Error getting members of administrators'
write-output 'Noncompliant - Powershell Error'
} else {
Writeln-Log "Found $($GroupMembers.Count) Local Administrators group members"
#-----------------------------------------------------------------
# Check each disallowed member to make sure it is not in the group
#-----------------------------------------------------------------
foreach ($DisallowedMember in $DisallowedMembers) {
Writeln-Log "Look for disallowed name: $($DisallowedMember.Name)"
If (-not (My-Test-LocalAdmin -TestMember $DisallowedMember)) {
Writeln-Log "$($DisallowedMember.Name) was not found in Local Administrators"
} else { # We found a disallowed member in the group
Writeln-Log "$($DisallowedMember.Name) does not belong in Local Administrators"
$Compliant = $false
$IssuesFound += 1
if ($ShouldRemediate) {
Remove-LocalGroupMember -Name 'Administrators' -Member $DisallowedMember.Name -ErrorAction SilentlyContinue
if (My-Test-LocalAdmin -TestMember $DisallowedMember) {
Writeln-Log "Failed to remove $($DisallowedMember.Name)"
} else {
Writeln-Log "Successfully removed $($DisallowedMember.Name)"
$Remediated = $True
}
}
}
}
#---------------------------------------------------------------
# Check each required member to make sure it exists in the group
#---------------------------------------------------------------
foreach ($RequiredMember in $RequiredMembers) {
Writeln-Log "Look for required name: $($RequiredMember.Name)"
If (My-Test-LocalAdmin -TestMember $RequiredMember) {
Writeln-Log "$($RequiredMember.Name) was found in Local Administrators"
} else {
Writeln-Log "$($RequiredMember.Name) is missing from Local Administrators"
$Compliant = $false
$IssuesFound += 1
if ($ShouldRemediate) {
writeln-log "Add: $($RequiredMember.Name)"
Add-LocalGroupMember -Name 'Administrators' -Member $RequiredMember.Name
if (My-Test-LocalAdmin -TestMember $RequiredMember) {
Writeln-Log "Successfully added $($RequiredMember.Name)"
$Remediated = $True
} else {
Writeln-Log "Failed to add $($RequiredMember.Name)"
}
}
}
}
#---------------------------------------------------------------
# Finalize the results
#---------------------------------------------------------------
if ($IssuesFound -eq 1) {$es = ''} else {$es='s'}
If (-not $ShouldRemediate) {
If ($Compliant) {
Writeln-Log "No issues found - Compliant"
'Compliant'
} else {
Writeln-Log "$IssuesFound issue$es found - Noncompliant"
'Noncompliant'
}
} else {
if ($Remediated ) {
Writeln-Log "$IssuesFound issue$es found - Remediated"
} else {
Writeln-Log "$IssuesFound issue$es found - Not Remediated"
}
}
}
Writeln-Log "Processing Ends"