Get-WmiObject deprecated now what? - powershell

I am trying to get the following function to work within PowerShell 6.0.2, however apparently Get-WmiObject has been deprecated. Can anyone help me figure out how to replace it with Get-CimInstance which has replaced it?
Get-WmiObject is within the PROCESS area of the code below.
Complete function code provided in case someone is interested.
function Get-DiskFree
{
[CmdletBinding()]
param
(
[Parameter(Position=0,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[Alias('hostname')]
[Alias('cn')]
[string[]]$ComputerName = $env:COMPUTERNAME,
[Parameter(Position=1,
Mandatory=$false)]
[Alias('runas')]
[System.Management.Automation.Credential()]$Credential =
[System.Management.Automation.PSCredential]::Empty,
[Parameter(Position=2)]
[switch]$Format
)
BEGIN
{
function Format-HumanReadable
{
param ($size)
switch ($size)
{
{$_ -ge 1PB}{"{0:#.#'P'}" -f ($size / 1PB); break}
{$_ -ge 1TB}{"{0:#.#'T'}" -f ($size / 1TB); break}
{$_ -ge 1GB}{"{0:#.#'G'}" -f ($size / 1GB); break}
{$_ -ge 1MB}{"{0:#.#'M'}" -f ($size / 1MB); break}
{$_ -ge 1KB}{"{0:#'K'}" -f ($size / 1KB); break}
default {"{0}" -f ($size) + "B"}
}
}
$wmiq = 'SELECT * FROM Win32_LogicalDisk WHERE Size != Null AND DriveType >= 2'
}
PROCESS
{
foreach ($computer in $ComputerName)
{
try
{
if ($computer -eq $env:COMPUTERNAME)
{
$disks = Get-WmiObject -Query $wmiq `
-ComputerName $computer -ErrorAction Stop
}
else
{
$disks = Get-WmiObject -Query $wmiq `
-ComputerName $computer -Credential $Credential `
-ErrorAction Stop
}
if ($Format)
{
# Create array for $disk objects and then populate
$diskarray = #()
$disks | ForEach-Object { $diskarray += $_ }
$diskarray | Select-Object #{n='Name';e={$_.SystemName}},
#{n='Vol';e={$_.DeviceID}},
#{n='Size';e={Format-HumanReadable $_.Size}},
#{n='Used';e={Format-HumanReadable `
(($_.Size)-($_.FreeSpace))}},
#{n='Avail';e={Format-HumanReadable $_.FreeSpace}},
#{n='Use%';e={[int](((($_.Size)-($_.FreeSpace))`
/($_.Size) * 100))}},
#{n='FS';e={$_.FileSystem}},
#{n='Type';e={$_.Description}}
}
else
{
foreach ($disk in $disks)
{
$diskprops = #{'Volume'=$disk.DeviceID;
'Size'=$disk.Size;
'Used'=($disk.Size - $disk.FreeSpace);
'Available'=$disk.FreeSpace;
'FileSystem'=$disk.FileSystem;
'Type'=$disk.Description
'Computer'=$disk.SystemName;}
# Create custom PS object and apply type
$diskobj = New-Object -TypeName PSObject `
-Property $diskprops
$diskobj.PSObject.TypeNames.Insert(0,'BinaryNature.DiskFree')
Write-Output $diskobj
}
}
}
catch
{
# Check for common DCOM errors and display "friendly" output
switch ($_)
{
{ $_.Exception.ErrorCode -eq 0x800706ba } `
{ $err = 'Unavailable (Host Offline or Firewall)';
break; }
{ $_.CategoryInfo.Reason -eq 'UnauthorizedAccessException' } `
{ $err = 'Access denied (Check User Permissions)';
break; }
default { $err = $_.Exception.Message }
}
Write-Warning "$computer - $err"
}
}
}
END {}
}
Below is the PowerShell commands that I will run after loading the function, taken from this site: http://binarynature.blogspot.com/2010/04/powershell-version-of-df-command.html
$cred = Get-Credential 'example\administrator'
$servers = 'dc01','db01','exch01','sp01'
Get-DiskFree -Credential $cred -cn $servers -Format |
? { $_.Type -like '*fixed*' } |
select * -ExcludeProperty Type |
Out-GridView -Title 'Windows Servers Storage Statistics'

As EBGreen stated, this can be resolved by changing Get-WmiObject to Get-CimInstance. There are only two lines in that function that need rewriting:
Current (using Get-WmiObject)
$disks = Get-WmiObject -Query $wmiq -ComputerName $computer -ErrorAction Stop
$disks = Get-WmiObject -Query $wmiq -ComputerName $computer -Credential $Credential -ErrorAction Stop
Changed (using Get-CimInstance)*
$disks = Get-CimInstance -Query $wmiq -ComputerName $computer -ErrorAction Stop
$disks = Invoke-Command -ArgumentList $wmiq { param($wmiq) Get-CimInstance -Query $wmiq } -ComputerName $computer -Credential $Credential -ErrorAction Stop | Select-Object DeviceID, DriveType, ProviderName, FreeSpace, Size, VolumeName
Here is the full function with these changes already made (and cleaned up a bit to my liking). I can confirm that it is working on PowerShell Core v6.1.2
function Get-DiskFree {
[CmdletBinding()]
param (
[Parameter(Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[Alias('cn')]
[string[]]$ComputerName = $env:COMPUTERNAME,
[Parameter(Position=1, Mandatory=$false)]
[Alias('cr')]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Position=2)]
[Alias('f')]
[switch]$Format
)
begin {
$ErrorActionPreference = "Stop"
function Format-HumanReadable {
param (
$size
)
switch ($size) {
{$_ -ge 1PB}
{"{0:#.#'P'}" -f ($size / 1PB); break}
{$_ -ge 1TB}
{"{0:#.#'T'}" -f ($size / 1TB); break}
{$_ -ge 1GB}
{"{0:#.#'G'}" -f ($size / 1GB); break}
{$_ -ge 1MB}
{"{0:#.#'M'}" -f ($size / 1MB); break}
{$_ -ge 1KB}
{"{0:#'K'}" -f ($size / 1KB); break}
default
{"{0}" -f ($size) + "B"}
}
}
$wmiq = 'SELECT * FROM Win32_LogicalDisk WHERE Size != Null AND DriveType >= 2'
}
process {
foreach ($computer in $ComputerName) {
try {
if ($computer -eq $env:COMPUTERNAME) {
$disks = Get-CimInstance -Query $wmiq -ComputerName $computer
}
else {
$disks = Invoke-Command -ArgumentList $wmiq { param($wmiq) Get-CimInstance -Query $wmiq } -ComputerName $computer -Credential $Credential `
| Select-Object DeviceID, DriveType, ProviderName, FreeSpace, Size, VolumeName
}
if ($Format) {
# Create array for $disk objects and then populate
$diskarray = #()
$disks | ForEach-Object { $diskarray += $_ }
$diskarray | Select-Object
#{Name='Name'; Expression={$_.SystemName}},
#{Name='Vol'; Expression={$_.DeviceID}},
#{Name='Size'; Expression={Format-HumanReadable $_.Size}},
#{Name='Used'; Expression={Format-HumanReadable (($_.Size)-($_.FreeSpace))}},
#{Name='Avail'; Expression={Format-HumanReadable $_.FreeSpace}},
#{Name='Use%'; Expression={[int](((($_.Size)-($_.FreeSpace))/($_.Size) * 100))}},
#{Name='FS'; Expression={$_.FileSystem}},
#{Name='Type'; Expression={$_.Description}}
}
else {
foreach ($disk in $disks) {
$diskprops = #{
'Volume'=$disk.DeviceID;
'Size'=$disk.Size;
'Used'=($disk.Size - $disk.FreeSpace);
'Available'=$disk.FreeSpace;
'FileSystem'=$disk.FileSystem;
'Type'=$disk.Description
'Computer'=$disk.SystemName;
}
# Create custom PS object and apply type
$diskobj = New-Object -TypeName PSObject -Property $diskprops
$diskobj.PSObject.TypeNames.Insert(0,'BinaryNature.DiskFree')
Write-Output $diskobj
}
}
}
catch {
# Check for common DCOM errors and display "friendly" output
switch ($_) {
{ $_.Exception.ErrorCode -eq 0x800706ba }
{$err = 'Unavailable (Host Offline or Firewall)'; break}
{ $_.CategoryInfo.Reason -eq 'UnauthorizedAccessException' }
{$err = 'Access denied (Check User Permissions)'; break}
default
{$err = $_.Exception.Message}
}
Write-Warning "$computer - $err"
}
}
}
end {
}
}

Related

Powershell, I do input a list gather data and output that whole list into one CSV

I am creating a script that reads a list of computer names and collects data from security event logs about who is on the computer, how long they have been on for, and how long it has been since the computer has restarted. I have it working except that it does not output all the data into one CSV. I just receive one CSV file with one computer name.
function Get-KioskInfo {
param (
[parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True,Position=0)]
[Alias('PSComputerName','DNSHostName','CN','Hostname')]
[string]
$ComputerName = $env:COMPUTERNAME
)
#PARAM
$User = try {(Get-WmiObject -ComputerName $ComputerName Win32_ComputerSystem | Select-Object -ExpandProperty username).trimstart("NG\")} catch {Write-Output "User not detected";break}
$BootStart = ((get-date) - (Get-CimInstance win32_operatingsystem -ComputerName $ComputerName).LastBootUpTime).Days
#These variables are for the DATE & Time calculation
If ($user -NE $null)
{ Write-Verbose 1
# Do something
$Date1 = Get-date
Write-Verbose 2
$SP = Get-WinEvent -ComputerName $ComputerName -FilterHashTable #{LogName = "Security";ID="5379";Data=$User; StartTime=((Get-Date).AddDays(-1))}
Write-Verbose 3
$Date2 =($SP | select -first 1).timecreated
Write-Verbose 4
$USERLOGTIME = ($Date1-$Date2).hours.tostring("N2")
Write-Verbose 5
}
else{Write-Output "No user";break}
Write-Verbose 6
#Rename-Computer -ComputerName "Srv01" -NewName "Server001" -DomainCredential Domain01\Admin01 -Force ------ Rename script for computers if it is needed.
#$computers = Get-Content C:\Users\jaycbee\Desktop\kiosknames.txt ------ To load kiosk list
#foreach ($c in $computers) {start-job -Name $c -ScriptBlock ${Function:get-kioskinfo} -ArgumentList $c} for learning how to do a foreach script
Write "Computer Name: $Computername"
Write "---USER---"
Write "Name: $User"
Write "Log in Time $USERLOGTIME"
Write "Boot start $BootStart days ago"
$ComputerName | ForEach-Object {
if (Test-Connection -ComputerName $ComputerName -Count 1 -Quiet)
{
Invoke-Command -ComputerName $ComputerName {
}
} # Offline Check
else
{
Write-Host "Computer is Unreachable or Offline" -ForegroundColor Gray
}
} # Foreach
$Continue = Read-Host "WARNING! This will READ LIST of computers in \\ou\ouor-groups\Desktop_Support\SD\Kiosks\kiosknames.txt Type CONTINUE to proceed."
if ($Continue -eq "CONTINUE")
{
$Computers = Get-Content '\\ou\ouor-groups\Desktop Support\SD\Kiosks\kiosknames.txt'
foreach ($C in $Computers) {start-job -Name $c -ScriptBlock ${Function:get-kioskinfo} -ArgumentList $c
}
}
[pscustomobject]#{ Name = $ComputerName ; User = $User ; "User Log in time in hours" = $USERLOGTIME;"BootStart days ago" = $BootStart} | export-csv -path "\\ou\ouor-groups\Desktop Support\SD\Kiosks\test45$ComputerName.csv" -Append
} #Function
#For each-computer | do this at this location,
Continuing from my comment. I too wonder why the use of jobs for this use case. Unless you are doing this on hundreds of computers, thus needing parallel processing.
This refactor/formatting is just my way of making sense of what you posted. I'm old, and crowded code just really hurts my eyes. ;-} Yet, code the way you like of course. ;-}
I do not have an environment to test this, but give it a shot.
function Get-KioskInfo
{
param
(
[parameter(ValueFromPipeline = $True,ValueFromPipelineByPropertyName = $True,Position = 0)]
[Alias(
'PSComputerName',
'DNSHostName',
'CN',
'Hostname'
)]
[string]
$ComputerName = $env:COMPUTERNAME
)
($User = try
{
(Get-WmiObject -ComputerName $ComputerName Win32_ComputerSystem |
Select-Object -ExpandProperty username).trimstart("NG\")
}
catch
{
'User not detected'
break
}
)
($BootStart = ((get-date) - (Get-CimInstance win32_operatingsystem -ComputerName $ComputerName).LastBootUpTime).Days)
If ($user -NE $null)
{
($Date1 = Get-date)
($SP = Get-WinEvent -ComputerName $ComputerName -FilterHashTable #{
LogName = 'Security'
ID = '5379'
Data = $User
StartTime = ((Get-Date).AddDays(-1))
})
($Date2 = (
$SP |
select -first 1
).timecreated)
($USERLOGTIME = ($Date1-$Date2).hours.tostring('N2'))
}
else
{
'No user'
break
}
"Computer Name: $Computername
---USER---
Name: $User
Log in Time $USERLOGTIME
Boot start $BootStart days ago"
$ComputerName |
ForEach-Object {
if (Test-Connection -ComputerName $ComputerName -Count 1 -Quiet)
{Invoke-Command -ComputerName $ComputerName}
else
{Write-Warning -Message 'Computer is Unreachable or Offline'}
}
$UserMessage = '
WARNING!
This will READ LIST of computers in:
\\ou\ouor-groups\Desktop_Support\SD\Kiosks\kiosknames.txt
Type CONTINUE to proceed'
$Continue = Read-Host $UserMessage
if ($Continue -eq 'CONTINUE')
{
Get-Content '\\ou\ouor-groups\Desktop Support\SD\Kiosks\kiosknames.txt' |
foreach {
{start-job -Name $PSItem -ScriptBlock ${Function:get-kioskinfo} -ArgumentList $PSItem}
[pscustomobject]#{
Name = $ComputerName
User = $User
'User Log in time in hours' = $USERLOGTIME
'BootStart days ago' = $BootStart
}
} |
Export-Csv -path "$PWD\$ComputerName.csv" -Append
}
}
These didn't help me with my solution, but you were right about the start-jobs. I have to rework the entire script in order to get the correct info.

Why do I get different output in Powershell from start-job vs just running the code?

The script runs correctly when outside of Start-Job but when in a scriptblock I get incorrect results. Where am I going wrong?
I need the Start-Job functionality since I have servers where the remote commands will hang (separate problem - WMI is borked) and I need to timeout and move to the next server.
I've tried every variation I can find in Google and still don't have the results I'm looking for.
I am really at my wits end with this as I don't understand what is happening... Help?
Thanks!
$timeoutSeconds = 90
ForEach($server in $servers) {
#$ErrorActionPreference = "inquire"
#$WarningPreference = "inquire"
$ErrorActionPreference = "silentlycontinue"
$WarningPreference = "silentlycontinue"
write-host $SERVER
$code = {
param($SERVER,$LOGto,$outputPath)
$ping = (Test-Connection -ComputerName $SERVER -Count 2 -Quiet )
if($ping -eq $true)
{
$pingVerbose = (Test-Connection -ComputerName $SERVER -Count 1)
$IP = $pingVerbose.IPV4Address.IPAddressToString
$osname2 = (Get-WMIObject -computerName $SERVER win32_operatingsystem).name
if($osname2 -match "|")
{
$osname,$osname1 = $osname2.Split('|')
} else {
$osname = $osname2
}
$lastinstalled = (Get-HotFix -computerName $SERVER | where -property InstalledOn -ne $null)
if($lastinstalled.InstalledOn)
{
$lastinstalledOn1 = ($lastinstalled.InstalledOn | Sort-Object -Property InstalledOn )[-1]
$lastinstalledOn = $lastinstalledOn1
}
$lastQFE = (get-wmiobject -class win32_quickfixengineering -computerName $SERVER | where -property InstalledOn -ne $null)
if($lastQFE.InstalledOn -ne $null)
{
$lastQFEon = ($lastQFE.InstalledOn | Sort-Object -Property InstalledOn)[-1]
$lastQFEon = $lastQFEon
}
if(($lastinstalledOn) -or ($lastQFEon))
{
if(($lastinstalledOn) -and ($lastinstalledOn -gt $lastQFEon))
{
$installedOn = $lastinstalledOn.tostring("MM/dd/yyyy")
$HotFixNumber = ($lastinstalled.HotFixID | Sort-Object -Property HotFixID)[-1]
} else {
$installedOn = $lastQFEon.tostring("MM/dd/yyyy")
$HotFixNumber = ($lastQFE.HotFixID | Sort-Object -Property HotFixID)[-1]
}
} else {
$installedOn = ''
$HotFixNumber = ''
}
}
#add entries to the log file
ac $outputPath\$LOGto "$Server,$ip,$installedOn,$HotFixNumber,$ping,$osname "
Write-Host "$Server,$ip,$installedOn,$HotFixNumber,$ping,$osname "
}
$runCode = Start-Job -ScriptBlock $code -ArgumentList $server,$LOGto,$outputPath
if(Wait-Job $runCode -Timeout $timeoutSeconds)
{
Receive-Job $runCode
} else {
Remove-Job -Force $runCode
ac $($outputPath + "\error.txt") "$Server"
}
}
When running in the scriptblock I receive the wrong date and KB.
SERVERNAME
SERVERNAME,10.1.XX.XX,03/13/2015,KB3022777,True,Microsoft Windows Server 2012 R2 Standard
vs.
SERVERNAME
SERVERNAME,10.1.XX.XX,05/15/2017,KB4012213,True,Microsoft Windows Server 2012 R2 Standard
For posterity more than anything else...
The error I was making was using sort-object against a string instead of a numerical value.
The code below will work correctly once you replace the domain name and file path information.
Thanks,
-Luke
#change this to a directory of your choice
$outputPath = "C:\Users\username\Desktop"
cd $outputPath
#create a default file name
$LOGto = "AllWinHotFixDateDC_$((Get-Date).ToString('yyyyMMdd')).csv"
#create the headers
sc .\$LOGto "Server,IPAddress,InstalledOn,HotFixNumber,Ping,OS_Name"
#get the server names from AD
Get-ADComputer -Filter {(Enabled -eq "True") -and (OperatingSystem -like "*Windows*") } -SearchBase "OU=Servers,DC=mydomain,DC=net" -server 'mydomain.net' -SearchScope Subtree | Select Name -ExpandProperty name | Sort-Object | Out-File .\servers.txt
$servers = get-content .\servers.txt
$timeoutSeconds = 90
ForEach($server in $servers) {
$ErrorActionPreference = "inquire"
$WarningPreference = "inquire"
#$ErrorActionPreference = "silentlycontinue"
#$WarningPreference = "silentlycontinue"
write-host $SERVER
$code = {
param($SERVER,$LOGto,$outputPath)
$ping = (Test-Connection -ComputerName $SERVER -Count 2 -Quiet )
if($ping -eq $true)
{
$pingVerbose = (Test-Connection -ComputerName $SERVER -Count 1)
$IP = $pingVerbose.IPV4Address.IPAddressToString
$osname2 = (Get-WMIObject -computerName $SERVER win32_operatingsystem).name
if($osname2 -match "|")
{
$osname,$osname1 = $osname2.Split('|')
} else {
$osname = $osname2
}
$getinstalled = (Get-HotFix -computerName $SERVER)
if($getinstalled)
{
if($getinstalled.HotFixID -ne $null)
{
$validinstalled = ($getinstalled.HotFixID -match "KB*")
$KB = (($validinstalled -replace 'KB','') | Sort-Object {[int]($_ -replace '(\d+).*', '$1')})[-1]
$lastinstalledOnHotFix = ("KB$KB")
}
if ($getinstalled.InstalledOn -ne $null)
{
$lastInstalled = ($getinstalled | Sort-Object -Property InstalledOn)[-1]
$lastInstalledlist = $lastInstalled.InstalledOn | Sort-Object {[int]($_ -replace '(\d+).*', '$1')}
$lastInstalledon = $lastInstalledlist.tostring("MM/dd/yyyy")
} else {
$lastinstalledOn = "0"
}
}
Write-Host $lastinstalledOn
Write-Host $lastinstalledOnHotFix
$getQFE = (get-wmiobject -class win32_quickfixengineering -computerName $SERVER )
if($getQFE)
{
if($getQFE.HotFixID -ne $null)
{
$validQFE = ($getQFE.HotFixID -match 'KB')
$KB = (($validQFE -replace 'KB','') | Sort-Object {[int]($_ -replace '(\d+).*', '$1')})[-1]
$lastQFEonHotFix = ("KB$KB")
}
if($getQFE.InstalledOn -ne $null)
{
$lastQFE = ($getQFE | Sort-Object -Property InstalledOn)[-1]
$lastQFElist = $lastQFE.InstalledOn | Sort-Object {[int]($_ -replace '(\d+).*', '$1')}
$lastQFEon = $lastQFElist.tostring("MM/dd/yyyy")
} else {
$lastQFEon = "0"
}
}
Write-Host $lastQFEon
Write-Host $lastQFEonHotFix
if(($lastinstalledOn -ne $null) -or ($lastQFEon -ne $null))
{
if(($lastInstalledlist -ne $null) -and ($lastInstalledlist -gt $lastQFElist))
{
$installedOn = $lastinstalledOn
$HotFixNumber = $lastinstalledOnHotFix
} elseif($lastQFEon -ne $null)
{
$installedOn = $lastQFEon
$HotFixNumber = $lastQFEonHotFix
}
} else {
$installedOn = '0'
$HotFixNumber = $lastQFEonHotFix
}
}
#add entries to the log file
ac $outputPath\$LOGto "$Server,$ip,$installedOn,$HotFixNumber,$ping,$osname "
Write-Host "$Server,$ip,$installedOn,$HotFixNumber,$ping,$osname "
}
$runCode = Start-Job -ScriptBlock $code -ArgumentList $server,$LOGto,$outputPath
if(Wait-Job $runCode -Timeout $timeoutSeconds)
{
Receive-Job $runCode
} else {
Remove-Job -Force $runCode
ac $($outputPath + "\error.txt") "$Server"
}
}

get local admin users with password age

I am working on once assignment where want to get a list of local Windows admin users with X password age. Got below function for local admin users and other one for age. Please help me integrate these.
I have below command can work with users list to fetch details from specific groups and hostnames.
Get-Content -Path "D:\Groups.txt" | ForEach-Object {
Get-GroupMember -ComputerName (Get-Content -Path "D:\servers.txt") -LocalGroup $_
} | Export-Csv -Path D:\Getgroupmembers_$(Get-Date -Format ddMMyyyy).csv -NoTypeInformation
List of users:
function Get-GroupMember {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[Alias('Group')]
[string]$LocalGroup,
[Alias('CN','Computer')]
[string[]]$ComputerName = '.'
)
foreach ($Computer in $ComputerName) {
Write-Verbose "Checking membership of localgroup: '$LocalGroup' on $Computer"
try {
([adsi]"WinNT://$Computer/$LocalGroup,group").psbase.Invoke('Members') | ForEach-Object {
New-Object -TypeName PSCustomObject -Property #{
ComputerName = $Computer
LocalGroup = $LocalGroup
Member = $_.GetType().InvokeMember('Name', 'GetProperty', $null, $_, $null)
}
}
Write-Verbose "Successfully checked membership of localgroup: '$LocalGroup' on $Computer"
} catch {
Write-Warning $_
}
}
}
TO check Password age we can use below code and we need to integrate these two using one command:
function Get-PwdAge {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false,
Position=1,
ValueFromPipeline=$false,
ValueFromPipelineByPropertyName=$false)]
[String]$Usr,
[Switch]$All
)
$filter = "(&(objectCategory=person)(objectClass=user)(name=$Usr))"
if ($All) {
$filter = '(&(objectCategory=person)(objectClass=user))'
}
$root = New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")
$searcher = New-Object System.DirectoryServices.DirectorySearcher $filter
$SearchRoot = $root.defaultNamingContext
$searcher.SearchRoot = "LDAP://CN=Users,$SearchRoot"
$searcher.SearchScope = 'SubTree'
$searcher.SizeLimit = 0
$searcher.PageSize = 1000
$searcher.FindAll() | ForEach-Object {
$account = $_.GetDirectoryEntry()
$pwdset = [DateTime]::FromFileTime($_.Properties.Item("pwdLastSet")[0])
$age = (New-TimeSpan $pwdset).Days
$info = 1 | Select-Object Name, Login, AgeInDays, LastSet
$info.Name = $account.DisplayName[0]
$info.Login = $account.SamAccountName[0]
$info.AgeInDays = $age
$info.LastSet = $pwdset
$info
}
}
Param
(
[Parameter(Position=0,Mandatory=$false)]
[ValidateNotNullorEmpty()]
[Alias('cn')][String[]]$ComputerName=$Env:COMPUTERNAME,
[Parameter(Position=1,Mandatory=$false)]
[Alias('un')][String[]]$AccountName,
[Parameter(Position=2,Mandatory=$false)]
[Alias('cred')][System.Management.Automation.PsCredential]$Credential
)
$Obj = #()
$now = Get-Date
Foreach($Computer in $ComputerName)
{
If($Credential)
{
$AllLocalAccounts = Get-WmiObject -Class Win32_UserAccount -Namespace "root\cimv2" `
-Filter "LocalAccount='$True'" -ComputerName $Computer -Credential $Credential -ErrorAction Stop
}
else
{
$AllLocalAccounts = Get-WmiObject -Class Win32_UserAccount -Namespace "root\cimv2" `
-Filter "LocalAccount='$True'" -ComputerName $Computer -ErrorAction Stop
}
$Obj = $AllLocalAccounts | ForEach-Object {
$user = ([adsi]"WinNT://$computer/$($_.Name),user")
$pwAge = $user.PasswordAge.Value
$maxPwAge = $user.MaxPasswordAge.Value
$pwLastSet = $now.AddSeconds(-$pwAge)
New-Object -TypeName PSObject -Property #{
'Account Name' = $_.Name
'Disabled' = $_.Disabled
'Password Expires' = $_.PasswordExpires
'Password Last Set' = $pwLastSet
'Password Expiry Date' = $now.AddSeconds($maxPwAge - $pwAge)
'Password Required' = $_.PasswordRequired
'Domain' = $_.Domain
'Password Age' = ($now - $pwLastSet).Days
}
}
If($AccountName)
{
Foreach($Account in $AccountName)
{
$Obj|Where-Object{$_.Name -like "$Account"}
}
}
else
{
$Obj
}
}

get last user logon time without AD

I'm trying to create a script that can get the user profiles that haven't logged on a specific computer within 30 days NOT using active directory but my script didn't work. I am using Powershell version 3. This is my code:
netsh advfirewall firewall set rule group="Windows Management Instrumentation (WMI)" new enable=yes
$ComputerList = Get-Content C:\temp\Computers1.txt
$myDomain = Get-Content C:\temp\Domain.txt
$csvFile = 'C:\temp\Profiles.csv'
# Create new .csv output file
New-Item $csvFile -type file -force
# Output the field header-line to the CSV file
"HOST,PROFILE" | Add-Content $csvFile
# Loop over the list of computers from the input file
foreach ($Computer in $ComputerList) {
# see if ping test succeeds for this computer
if (Test-Connection $Computer -Count 3 -ErrorAction SilentlyContinue) {
$ComputerFQDN = $Computer + $myDomain
$Profiles = Get-WmiObject -Class Win32_UserProfile -Computer $ComputerFQDN | Where{$_.LocalPath -notlike "*$env:SystemRoot*"}
foreach ($profile in $profiles) {
try {
$objSID = New-Object System.Security.Principal.SecurityIdentifier($profile.LocalPath) | Where {((Get-Date)-$_.lastwritetime).days -ge 30}
#| Where-Object {$_.LastLogonDate -le $CurrentDate.AddDays(-60)}
$objuser = $objsid.Translate([System.Security.Principal.NTAccount])
$objusername = $objuser.value
} catch {
$objusername = $profile.LocalPath
}
switch($profile.status){
1 { $profileType="Temporary" }
2 { $profileType="Roaming" }
4 { $profileType="Mandatory" }
8 { $profileType="Corrupted" }
default { $profileType = "LOCAL" }
}
$User = $objUser.Value
#output profile detail for this host
"$($Computer.toUpper()), $($objusername)" | Add-Content $csvFile
}
} else {
#output failure message for this host
"$($Computer.toUpper()), PING TEST FAILED" | Add-Content $csvFile
}
#LOOP
}
I tried to change the -ge to -le in the line $objSID = New-Object System.Security.Principal.SecurityIdentifier($profile.LocalPath) | Where {((Get-Date)-$_.lastwritetime).days -ge 30}, as well as changing the range after it but it still gave me the same list of computers regardless of my changes.
There are a few problems with the script, most notable is that your use of Where-Object is testing an object (SID) that doesn't know anything about dates.
I would break it down a little differently. I would write a function to catch all the stuff I need to do to attempt to figure out the last logon. That's my goes in my stack of utility functions in case I need it again.
Then I have something to use that function which deals with implementing the logic for the immediate requirement.
So you end up with this. It's a bit long, see what you think.
function Get-LastLogon {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline = $true)]
[String]$ComputerName = $env:COMPUTERNAME
)
process {
Get-WmiObject Win32_UserProfile -ComputerName $ComputerName -Filter "Special='FALSE'" | ForEach-Object {
# Attempt to get the UserAccount using WMI
$userAccount = Get-WmiObject Win32_UserAccount -Filter "SID='$($_.SID)'" -ComputerName $ComputerName
# To satisfy WMI all single \ in a path must be escaped.
# Prefer to use NTUser.dat for last modification
$path = (Join-Path $_.LocalPath 'ntuser.dat') -replace '\\', '\\'
$cimObject = Get-WmiObject CIM_DataFile -Filter "Name='$path'" -ComputerName $ComputerName
if ($null -eq $cimObject) {
# Fall back to the directory
$path = $_.LocalPath -replace '\\', '\\'
$cimObject = Get-WmiObject CIM_Directory -Filter "Name='$path'" -ComputerName $ComputerName
}
$lastModified = $null
if ($null -ne $cimObject) {
$lastModified = [System.Management.ManagementDateTimeConverter]::ToDateTime($cimObject.LastModified)
}
# See if LastUseTime is more useful.
$lastUsed = $null
if ($null -ne $_.LastUseTime) {
$lastUsed = [System.Management.ManagementDateTimeConverter]::ToDateTime($_.LastUseTime)
}
# Profile type
$profileType = switch ($_.Status) {
1 { "Temporary" }
2 { "Roaming" }
4 { "Mandatory" }
8 { "Corrupted" }
0 { "LOCAL" }
}
[PSCustomObject]#{
ComputerName = $ComputerName
Username = $userAccount.Caption
LastChanged = $lastModified
LastUsed = $lastUsed
SID = $_.SID
Path = $_.LocalPath
ProfileType = $profileType
}
}
}
}
$myDomain = Get-Content C:\temp\Domain.txt
Get-Content C:\temp\Computers1.txt | ForEach-Object {
$ComputerName = $_ + $myDomain
if (Test-Connection $ComputerName -Quiet -Count 3) {
Get-LastLogon -ComputerName $ComputerName | Select-Object *, #{Name='Status';Expression={ 'OK' }} |
Where-Object { $_.LastChanged -lt (Get-Date).AddDays(-30) }
} else {
# Normalise the output so we don't lose columns in the export
$ComputerName | Select-Object #{Name='ComputerName';e={ $ComputerName }},
Username, LastChanged, LastUsed, SID, Path, ProfileType, #{Name='Status';Expression={ 'PING FAILED' }}
}
} | Export-Csv 'C:\temp\Profiles.csv' -NoTypeInformation

Active logged in Users on Remote machine

I am using below script to get logged on user on remote machine . It works fine but I need to get the users those status "active"
How Can I get those active logged in users on remote machine ?
function Global:Get-LoggedOnUser {
#Requires -Version 2.0
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$false,
Position=0,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[String[]]$ComputerName = $env:COMPUTERNAME
)#End Param
Begin
{
Write-Host "`n Checking Users . . . "
$i = 0
$MyParams = #{
Class = "Win32_process"
Filter = "Name='Explorer.exe'"
ErrorAction = "Stop"
}
}#Begin
Process
{
$ComputerName | Foreach-object {
$Computer = $_
$MyParams["ComputerName"] = $Computer
try
{
$processinfo = #(Get-WmiObject #MyParams)
if ($Processinfo)
{
$Processinfo | ForEach-Object {
New-Object PSObject -Property #{
ComputerName=$Computer
LoggedOn =$_.GetOwner().User
SID =$_.GetOwnerSid().sid} } |
Select-Object ComputerName,LoggedOn,SID
}#If
}
catch
{
"Cannot find any processes running on $computer" | Out-Host
}
}#Forech-object(ComputerName)
}#Process
End
{
}#End
}#Get-LoggedOnUsers
Add a query for the Win32_ComputerSystem class:
Get-WMIObject -Class Win32_ComputerSystem -Computername $Computer | Select UserName
That'll grab the 'active' user, then you can build an object with an 'Active' boolean value.
Here's my implementation:
function Get-LoggedOnUser
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
Position=0,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[String[]]$ComputerName
)
Begin
{
$users = $null
$return = #()
}
Process
{
ForEach($Computer in $ComputerName)
{
$activeUser = Get-WMIObject -class Win32_ComputerSystem -ComputerName $Computer -EA stop | select UserName
Try
{
$processinfo = #(Get-WmiObject -class win32_process -ComputerName $Computer -EA "Stop")
If ($processinfo)
{
ForEach($process in $processinfo)
{
[string[]]$users += $process.GetOwner().user| Where{($_ -ne "NETWORK SERVICE") -and ($_ -ne "LOCAL SERVICE") -and ($_ -ne "SYSTEM")}
}
If($Users)
{
ForEach($user in ($Users | Select -unique))
{
If($ActiveUser.username -like "*$user")
{
$Return += New-Object PSObject -Property #{
"User" = $user
"Active" = $true
"Computer" = $Computer
}
}
Else
{
$Return += New-Object PSObject -Property #{
"User" = $user
"Active" = $false
"Computer" = $Computer
}
}
}
}
Else
{
"There are no users logged onto $computer" | Out-Host
}
}
}
Catch
{
"Cannot find any processes running on $computer" | Out-Host
}
}
}
End
{
$Return
}
}
It is worth it to point out that the Win32_ComputerSystem username is only populated if the user is logged in locally, so anyone logged in through remote desktop won't show as 'Active'.