How to enumerate available properties of member - powershell

In the code below there are a number of properties of "member" I've added "description" property successfully but I can't find out if the account is enabled. I've tried "status" or "enabled" or "disabled" all to no avail. I realize it's a member of a group of an ADSI call but, I really need to know if the account is enabled or not.
Thanks in advance!
Full script available at https://github.com/JDogHerman/Powershell_Scripts/blob/master/get-localgroupmembers.ps1
Process {
ForEach($Computer in $ComputerName) {
Write-host "Working on $Computer"
If(!(Test-Connection -ComputerName $Computer -Count 1 -Quiet)) {
Write-Verbose "$Computer is offline. Proceeding with next computer"
Add-Content -Path $OutputFile -Value "$Computer,$LocalGroupName,Offline"
Continue
} else {
Write-Verbose "Working on $computer"
try {
$group = [ADSI]"WinNT://$Computer/$LocalGroupName"
$members = #($group.Invoke("Members"))
Write-Verbose "Successfully queries the members of $computer"
if(!$members) {
Add-Content -Path $OutputFile -Value "$Computer,$LocalGroupName,NoMembersFound"
Write-Verbose "No members found in the group"
continue
}
}
catch {
Write-Verbose "Failed to query the members of $computer"
Add-Content -Path $OutputFile -Value "$Computer,,FailedToQuery"
Continue
}
foreach($member in $members) {
try {
$MemberName = $member.GetType().Invokemember("Name","GetProperty",$null,$member,$null)
$MemberType = $member.GetType().Invokemember("Class","GetProperty",$null,$member,$null)
$MemberPath = $member.GetType().Invokemember("ADSPath","GetProperty",$null,$member,$null)
$MemberDomain = $null

Based on this answer, you can change this part of your code:
foreach($member in $members) {
try {
$MemberName = $member.GetType().Invokemember("Name","GetProperty",$null,$member,$null)
$MemberType = $member.GetType().Invokemember("Class","GetProperty",$null,$member,$null)
....
For this:
$members.ForEach([adsi]).ForEach({
$enabled = switch ($_.class) {
User { ('Enabled', 'Disabled')[[bool]($_.UserFlags.Value -band 2)] }
Default { 'Not Applicable'}
}
[pscustomobject]#{
Name = $_.Name.Value
Class = $_.Class
ADSPath = $_.ADSPath
Enabled = $enabled
}
})
You can add a try / catch logic if you believe it's needed. As also stated in comments, the built-in cmdlet Get-LocalUser already does this for you.

Related

PowerShell get output in CSV format

I am seeking help to get output in csv format. I have written powershell code and would want to tweak the output to get in csv format as shown in below pic.
$servers = Get-Content 'C:\Temp\listofservers.txt'
foreach ($server in $servers)
{
#DHCP
if (((Get-Service -ComputerName $server -ServiceName 'DHCPServer' -ErrorAction SilentlyContinue).Status) -eq 'Running')
{
if ((((Get-DhcpServerv4Scope -ComputerName $server | Get-DhcpServerv4Lease -ComputerName $server) | Measure-Object).Count) -ge 1)
{
Write-Host "DHCP present on $server and in use"
}
else
{
Write-Host "DHCP present on $server and not in use"
}
}
else
{
Write-Host("DHCP is not present on $server")
}
#Certificate authority
if (((Get-Service -ComputerName $server -ServiceName 'CertSvc' -ErrorAction SilentlyContinue).Status) -eq 'Running')
{
Write-Host "Certificate Authority is present on $server"
}
else
{
Write-Host "Certificate Authority is not present on $server"
}
}
Send all outputs to a custom object then export them:
$servers = get-content "C:\Temp\listofservers.txt"
$ExportPath = 'c:\temp\results.csv'
$Servers | ForEach-Object {
Write-Host "Checking $_"
# DHCP status
# Use Try....Catch to trap the errors - it's more robust than
# If...Then and prevents a wall of red text if something goes wrong
Try {
$DHCPStatus = Get-Service -ComputerName $_ -Name 'DHCPServer' -ErrorAction Stop
If ($DHCPStatus.Status -eq "Running") {
Try {
If ((((Get-DhcpServerv4Scope -ComputerName $_ -ErrorAction Stop | Get-DhcpServerv4Lease -ComputerName $_ -ErrorAction Stop) | Measure-Object).Count) -ge 1) {
$DHCPResult = "DHCP present on, in use"
}
Else {
$DHCPResult = "DHCP present, not in use"
}
}
Catch {
$DHCPResult = "DHCP present - error obtaining details: $_"
}
}
}
Catch {
$DHCPResult = "DHCP is not present"
}
#Certificate authority
Try {
If (((Get-Service -ComputerName $_ -ServiceName 'CertSvc' -ErrorAction Stop).Status) -eq "Running") {
$CAResult = "Certificate Authority is present"
}
}
Catch {
$CAResult = "Certificate Authority is not present"
}
[pscustomobject]#{ComputerName = $_;DHCP = $DHCPResult;CA=$CAResult}
} | Export-Csv -Path $ExportPath -NoTypeInformation
Use a hashtable to build your object, adding key/values for each property along the way. Then convert the hashtable to a [PSCustomObject] and output it capturing in a variable ($results). Finally, export it to csv using Export-Csv
$servers = Get-Content 'C:\Temp\listofservers.txt'
$results = foreach ($server in $servers) {
# Create hashtable to build object and add ComputerName property
$output = [ordered]#{ComputerName = $server }
#DHCP
if (((Get-Service -ComputerName $server -ServiceName 'DHCPServer' -ErrorAction SilentlyContinue).Status) -eq 'Running') {
if ((((Get-DhcpServerv4Scope -ComputerName $server |
Get-DhcpServerv4Lease -ComputerName $server) |
Measure-Object).Count) -ge 1) {
# add Dhcp property if present, in use
$output.Add('Dhcp', 'Present, in use')
}
else {
# add Dhcp property if present, not in use
$output.Add('Dhcp', 'Present, not in use')
}
}
else {
# add Dhcp property if not present
$output.Add('Dhcp', 'Not present')
}
#Certificate authority
if (((Get-Service -ComputerName $server -ServiceName 'CertSvc' -ErrorAction SilentlyContinue).Status) -eq 'Running') {
# add CA property if present
$output.Add('CA', 'Present')
}
else {
# add CA property if not present
$output.Add('CA', 'Not present')
}
# Convert hashtable to pscustomobject and output it
[PSCustomObject]$output
}
# output results to screen
$results
# export results to csv file
$results | Export-Csv -Path c:\temp\server_dhcp_ca_check.csv -NoTypeInformation

Script is not picking all servers from the text file

$Servers = Get-Content "D:\Server\Servers.txt"
#Gathering Vars
$OSVersion = (Get-CimInstance Win32_OperatingSystem).version
$WarningPreference = 'SilentlyContinue'
$key = 'HKLM:\Software\Company\SCCMMetaData'
$StampCheck = (Get-ItemProperty -Path $key).PatchRelease
$data = foreach ($server in $Servers) {
#Gathering OS Version & Normalization of data
If ($OSVersion -like "10.*") { $OSName = "WINS2016-"; $KB = "KB4540670", "KB4538461", "KB4550929", "KB4556813", "KB4549949", "KB4550994", "KB4494175", "KB4540723" }
Elseif ($OSVersion -like "6.0.*") { $OSName = "WINS08R1-"; $KB = "KB4534303", "KB4534312" }
Elseif ($OSVersion -like "6.1.*") { $OSName = "WINS08R2-"; $KB = "KB4534314", "KB4539602", "KB4534310" }
Elseif ($OSVersion -like "6.2.*") { $OSName = "WINS12R1-"; $KB = "KB4541510", "KB4550917", "KB4556840", "KB4540726" }
Elseif ($OSVersion -like "6.3.*") { $OSName = "WINS12R2-"; $KB = "KB4541509", "KB4550961", "KB4556846", "KB4540725" }
#Check to see if KB is installed & build the stamp
Try {
$KBSearch = Get-HotFix -id $KB -ErrorAction Stop
$Stamp = $OSName
If ($StampCheck -eq "2020Q2") {
Return "$Server Already Compliant"
}
Else {
Set-ItemProperty -path 'HKLM:\Software\Company\SCCMMetaData' -Name 'PatchRelease' -Value $Stamp
Restart-Service -DisplayName 'Agent' -ErrorAction Ignore
Start-Sleep 30
Return "$Server Stamped"
}
}
Catch { Return "Missing Patch $KB on server $Server" }
}
Restart-Service -DisplayName ' Agent'
$data | Export-Csv "D:\Scripts\KB.csv" -NoTypeInformation
This is my code and it is not iterating for all the servers in the .txt file. It is only taking 1st server in the list .
It is not checking for each server in the txt file . Where i am doing the mistake ? Could any one help me ?
return will cause PowerShell to... well, return control to the caller :)
Simply omit the return keyword and leave the expressions following them as-is - they'll automatically "bubble up" to the caller anyway:
#Check to see if KB is installed & build the stamp
Try {
$KBSearch = Get-HotFix -id $KB -ErrorAction Stop
$Stamp = $OSName
If ($StampCheck -eq "2020Q2") {
"$Server Already Compliant"
}
Else {
Set-ItemProperty -path 'HKLM:\Software\Company\SCCMMetaData' -Name 'PatchRelease' -Value $Stamp
Restart-Service -DisplayName 'Agent' -ErrorAction Ignore
Start-Sleep 30
"$Server Stamped"
}
}
Catch { "Missing Patch $KB on server $Server" }
You can read more about the return keyword in the about_Return helpfile

Disable multiple computer account from txt

$Computers = Get-Content c:\temp\computers.txt
foreach ($Computer in $Computers) {
$ADComputer = $null
$ADComputer = Get-ADComputer $Computer -Properties Description
if ($ADComputer) {
Add-Content C:\temp\computers.log -Value "Found $Computer, disabling"
Set-ADComputer $ADComputer -Description "Computer Disabled on $(Get-Date)" -Enabled $false
} else {
Add-Content C:\temp\computers.log -Value "$Computer not in Active Directory"
}
}
Can someone verify whether above code is correct?
Above code suppose to:
disable multiple computers, read from txt file.
Edit description of the disabled computers.
create log file for the result.
Customer don't have any testing environment that we can test. So I just don't want to risk anything.
The original code is working fine. So i am posting this as answer.
$Computers = Get-Content c:\temp\computers.txt
foreach ($Computer in $Computers) {
$ADComputer = $null
$ADComputer = Get-ADComputer $Computer -Properties Description
if ($ADComputer) {
Add-Content C:\temp\computers.log -Value "Found $Computer, disabling"
Set-ADComputer $ADComputer -Description "Computer Disabled on $(Get-Date)" -Enabled $false
} else {
Add-Content C:\temp\computers.log -Value "$Computer not in Active Directory"
}
}

Changing CSV report entry if duplicates exist with powershell

I'm pretty new to powershell, so I'll do my best to explain myself. I'm currently working with a script which creates a csv report of access levels for all users and user groups on a server. Breaking it down between Admin and User privileges. As is, it currently out puts two entries for groups which have both Admin and User access. Resembling the following image. (posted as an image due to some trouble with creating a table on stackoverflow)
I was hoping for some suggestions about how to consolidate users/groups which repeat in the report into one entry with an X in both fields. Kind of like the following:
Here is my current script:
[CmdletBinding()]
Param(
[Parameter( ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true
)]
[string[]]
$ComputerName = $env:ComputerName,
[Parameter()]
[array] $LocalGroupNames = #("Administrators", "Remote Desktop Users"),
[Parameter()]
[string]
$LocalGroupName,
[Parameter()]
[string]
$OutputDir = "c:\temp"
)
Begin {
$OutputFile = Join-Path $OutputDir "OSUsers $(get-date -f yyyy-MM-dd).csv"
Add-Content -Path $OutPutFile -Value "ServerName, User\Group, Administrator, User"
}
Process {
ForEach($Computer in $ComputerName) {
foreach ($LocalGroupName in $LocalGroupNames) {
Write-host "Working on $Computer"
If(!(Test-Connection -ComputerName $Computer -Count 1 -Quiet)) {
Add-Content -Path $OutputFile -Value "$Computer,Offline,Offline,Offline"
Continue
} else {
Write-Verbose "Working on $computer"
try {
$group = [ADSI]"WinNT://$Computer/$LocalGroupName"
$members = #($group.Invoke("Members"))
Write-Verbose "Successfully queries the members of $computer"
if(!$members) {
Add-Content -Path $OutputFile -Value "$Computer,NoMembersFound"
Write-Verbose "No members found in the group"
continue
}
}
catch {
Add-Content -Path $OutputFile -Value "$Computer,FailedToQuery"
Continue
}
foreach($member in $members) {
try {
$MemberName = $member.GetType().Invokemember("Name","GetProperty",$null,$member,$null)
$MemberType = $member.GetType().Invokemember("Class","GetProperty",$null,$member,$null)
$MemberPath = $member.GetType().Invokemember("ADSPath","GetProperty",$null,$member,$null)
$MemberDomain = $null
if($MemberPath -match "^Winnt\:\/\/(?<domainName>\S+)\/(?<CompName>\S+)\/") {
if($MemberType -eq "User") {
$MemberType = "LocalUser"
} elseif($MemberType -eq "Group"){
$MemberType = "LocalGroup"
}
$MemberDomain = $matches["CompName"]
} elseif($MemberPath -match "^WinNT\:\/\/(?<domainname>\S+)/") {
if($MemberType -eq "User") {
$MemberType = "DomainUser"
} elseif($MemberType -eq "Group"){
$MemberType = "DomainGroup"
}
$MemberDomain = $matches["domainname"]
} else {
$MemberType = "Unknown"
$MemberDomain = "Unknown"
}
if ($MemberType -ne "Unknown") ##Exclude unresolved users
{
if ($LocalGroupName -eq "Administrators")
{Add-Content -Path $OutPutFile -Value "$Computer, $MemberName, X, ,"}
if ($LocalGroupName -eq "Remote Desktop Users")
{Add-Content -Path $OutPutFile -Value "$Computer, $MemberName, , X,"}
}
} catch
{
Add-Content -Path $OutputFile -Value "$Computer, ,FailedQueryMember"
}
}
}
}
}
}
End {}
What you may want to do here is get the members of both the Administrators and Remote Desktop Users, and then with a foreach statement, check the results for each user's presence in both groups or not.
Compare-Object might be your friend here as well, as you can determine how to handle what you write to your file. That command will output "<=", "=>", or "==" if you use the -IncludeEqual parameter.
Ex:
Powershell - find items which are in array1 but NOT in array2

PSObject bug with specific parameter

I have a powershell script that does some simple auditing of group membership on remote servers. The output is as expected except in the case of one group.
There are two parameters two this script, an OU to check in AD and a Group name to check. The OU parameter returns a list of server names, the Group name is the group to return members on. This all works fine except in one case, Backup Operators.
param([parameter(mandatory=$true)][string]$region,[string]$group)
### Debug flag for viewing output when running the script.
$DEBUG = 1
$self = $myinvocation.mycommand.name
function cmdopts {
if ($DEBUG) {
write-host "$self running with options"
write-host "Region: $region"
write-host "Group: $group"
}
}
### Function to handle custom messages to the user.
function usageRegion {
# Removed for brevity
}
function usageGroups {
# Removed for brevity
}
### Cleanup from previous run of the script.
function cleanup {
# Removed for brevity
}
#### Function to load powershell modules at runtime
function loadmod {
param([string]$name)
if ( -not(get-module -name $name)) {
if (get-module -listavailable| where-object { $_.name -eq $name}) {
import-module -name $name
$true
} else {
$false
}
} else {
$true
}
}
### Main()
cmdopts
#### Validate commandline options
if ( "cnr","nwr","swr","ner","ser","emr","lar","apr" -notcontains $region ) {
usageRegion
exit
}
if ( "Administrators","Backup Operators","Event Log Readers","Hyper-V Administrators","Power Users",
"Print Operators","Remote Desktop Users" -notcontains $group) {
usageGroups
exit
} else {
### We are creating three files for each run, previous runs need to be cleaned up before we start.
cleanup
### The ActiveDirectory module is a dependency for this script, we use it to get a list of machine names from AD for the OU.
if ( loadmod -name "activedirectory" ) {
write-host "Loading ActiveDirectory powershell module..." -foregroundcolor green
} else {
write-host "Sorry, you do not have the ActiveDirectory powershell module installed." -foregroundcolor yellow
write-host "The script cannot contnue." -foregroundcolor yellow
exit
}
### Get the list of servers from AD for the OU specified by the user.
get-adcomputer -f * -searchbase "ou=$region,ou=servers,dc=domain,dc=com" | select name | out-file "c:\scripts\ps\$region.srvtmp.txt" -append
### We need to fix some format issues with the file before continuing
# Removed for brevity, cleans up the file output from get-adcomputer and sets variable $srvlist
$srvlist = gc "c:\scripts\ps\$region.srvlist.txt"
# Store for the return
$store = #()
# Fix the group string for the filename
$filestring = $group
$filestring = $filestring.replace(' ', '')
$filestring = $filestring.tolower()
foreach ( $srv in $srvlist ) {
if ( $srv -eq "bustedserver" ) {
# This box hangs and does not tear down WMI when it can't complete, timeout does not work
write-host "skipping $srv"
} else {
$response = test-connection $srv -count 1 -quiet
### This does not work super well, might have to try a custom function
if ($response -eq $false ) {
write-host "$srv was offline during test" -foregroundcolor darkmagenta
} else {
write-host "Checking $group on " -nonewline; write-host $srv -foregroundcolor cyan
$groupinfo = new-object PSObject
$members = gwmi -computer $srv -query "SELECT * FROM Win32_GroupUser WHERE GroupComponent=`"Win32_Group.Domain='$srv',Name='$group'`""
$members = $members | sort-object -unique
$count = 0
if ($members -ne $null) {
add-member -inputobject $groupinfo -membertype noteproperty -name "Server" -value $srv
add-member -inputobject $groupinfo -membertype noteproperty -name "Group" -value $group
foreach ($member in $members) {
$count += 1
$data = $member.partcomponent -split "\,"
$domain = ($data[0] -split "=")[1]
$name = ($data[1] -split "=")[1]
$line = ("$domain\$name").replace("""","")
add-member -inputobject $groupinfo -membertype noteproperty -name "Member $count" -value $line
}
}
if ($DEBUG) {
write-host $groupinfo
}
$store += $groupinfo
}
}
}
}
#$store | export-csv -path "$HOME\desktop\$region-$filestring-audit.csv" -notype
$store
If I run this script against a group like Administrators, or Remote Desktop Users the output looks like the following.
Server: SERVER1
Group: Remote Desktop Users
Member1: GroupName1
Member2: GroupName2
Member3: GroupName3
If I run this script against the group Backup Operators, I only get the first group even if there are many. In the debug write-host statement, it will show all of the groups. When printing the store, it only shows the first one. Even if there are two or more, it will alsways print...
Server: SERVER1
Group: Backup Operators
Member1: GroupName1
Any ideas on why this is broken specifically for 'Backup Operators' and not others would be appreciated.