Formatting the output of repadmin - powershell - powershell

I'm creating a script that tells me the creation / modification date and other pieces of info of AD objects to determine upgrade status of the machines in large domains. I have no problem accomplishing this in a well formatted and easy to read manner in Server 2008 because it has Active Directory modules, but this isn't the case with Server 2003.
With server 2003 I had to use a different approach to the script to gather the information I want, but I am unsure how to format this.
This is my current script:
$filePath = “$ENV:UserProfile\Desktop\output.txt”
## Enter the name of the Domain controller below
$DCName = “Exchange”
$computers = dsquery computer domainroot -name * -limit 0
Foreach ($computer in $computers) {
repadmin /showattr $DCName $computer /atts:"WhenChanged,WhenCreated,OperatingSystem" /long | out-file –append $filepath
}
This is the sample output:
DN: CN=Sample-Object,CN=Computers,DC=Contoso,DC=com
1> whenCreated: 07/04/2011 14:00:02 Pacific Standard Time Pacific Daylight Time
1> whenChanged: 08/09/2012 11:24:22 Pacific Standard Time Pacific Daylight Time
1> operatingSystem: Windows 7 Professional
In server 2008 I'm able to use string formatting ('"{0}","{1}"' -F $computer.name, $computer.whatever) amd output it to a .csv to make it presentable but I don't think the same methods will apply to the results of repadmin.
My end goal would to simply have a CSV with Computer Name, along with the three or however many attributes I have extracted from repadmin.
Any help appreciated, thank you.

Give this a try, you can export it to CSV and import it back as objects:
$result = dsquery computer domainroot -name * -limit 0 | foreach {
repadmin /showattr $DCName $_ /atts:"WhenChanged,WhenCreated,OperatingSystem" /long
} | Out-String
$result.split([string[]]'DN:',[StringSplitOptions]::RemoveEmptyEntries) | Foreach-Object{
$attr = $_.Split("`r`n",[StringSplitOptions]::RemoveEmptyEntries)
New-Object -TypeName PSObject -Property #{
DN = $attr[0].Trim()
whenCreated = $attr[1].Trim() -replace '^1> whenCreated: '
whenChanged = $attr[2].Trim() -replace '^1> whenChanged: '
operatingSystem = $attr[3].Trim() -replace '^1> operatingSystem: '
}
}

Related

Get the Last Logon Time for Each User Profile

I need to get the last logon time for each user profile on a remote computer (not the local accounts). I've tried something similar to the following using Win32_UserProfile & LastLogonTime, however this is not giving accurate results. For example, one this computer, only 1 account has been used in the past year, however LastUpdateTime is showing very recent dates. Some accounts have not even been logged into and should say "N/A", but it doesn't.
$RemoteSB_UserADID = Get-WmiObject win32_userprofile -Property * | Where-Object {$_.LocalPath -like "*users*"} | Sort-Object $_.LastUseTime | ForEach-Object{
$Parts = $_.LocalPath.Split("\")
$ADID = $Parts[$Parts.Length - 1]
if ($ADID -ne "SPECIALPURPOSEACCOUNT1" -and $ADID -ne "SPECIALPURPOSEACCOUNT2"){
$Time = $null
try{
$Time = $_.ConvertToDateTime($_.LastUseTime)
}catch{
$Time = "N/A"
}
"[$ADID | $Time]"
}
}
Example Output
[Acct1 | 03/13/2022 07:18:19]
[Acct2 | 03/15/202214:59:16]
[Acct3 | 03/13/2022 07:18:19]
[Acct4 | 03/16/2022 11:53:17] <--- only "active" account
How can I go about retrieving accurate (or decently accurate) login times for each user profile? Thanks!
It would help to know for what reason you need that, so that I know how to find a (better) solution for you.
If you need to cleanup your profiles not used for a long time at the target system, then take the last changed date of "ntuser.dat". That is the last logon if you define logon like logging on to a new session. If the user was logged on and simply locked the computer or used standby and then relogs then this date won't change.
Use this to get this date from all users you have access to but possibly not getting real user names
Get-ChildItem \\REMOTECOMPUTERNAMEHERE\Users\*\ntuser.dat -Attributes Hidden,Archive | Select #{Name="NameByFolder";Expression={($_.DirectoryName -split "\\")[-1]}},LastWriteTime
Or this a bit more complex version
Invoke-Command -ComputerName REMOTECOMPUTERNAMEHERE -ScriptBlock {$UsersWithProfilePath = #{}
dir "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" |
where {$_.name -like "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\S-1-5-21-*"} |
foreach {$UsersWithProfilePath[([System.Security.Principal.SecurityIdentifier]$_.name.split("\")[-1]).Translate( [System.Security.Principal.NTAccount]).Value] = $_.GetValue("ProfileImagePath")}
foreach ($Name in $UsersWithProfilePath.Keys) {#{$Name =(dir (join-path $UsersWithProfilePath.$Name ntuser.dat) -Attributes Hidden,Archive,System).LastWriteTime}}}
Depending on what you need you need to change it a bit.
Sorry for the long codelines... it is late here.

PowerShell Test-Connection -efficient Method

One Bronze medal = Newbi 😊, have scoured this site for years, so a million belated thanks, once again!
Just finished up a few different PowerShell ver 5.1 scripts that perform a test-connection on the same 2000 devices regularly.
Out to a csv file
And they are all slow, some minor speed improvements when tweaking it down to the bare bones.
I’ve scaled the script to the minimal, IPADDRESS and NAME only
I’ve run a few comparison tests against a batch file (ping 123.456.789.001 >> PingTest.txt ) and these tests APPEAR to run quicker than my PS script (will run a full apple to apples test tomorrow), but confident something is amiss in the PS code.
Am looking at arrays, and how piping works, but am afraid I’m putting the cart in front of the horse, or there is something going on with how PS handles the csv format and/or ping list in its memory
My most efficient current script below,
Is the following link pointing me in the right direction,? Specifically the asJob switch
PowerShell Mass Test-Connection
$info = "" | Select IPaddress,Name
$OutputFile = new-item -itemType File -path C:\Temp\Results.csv -force -value "IPaddress,Name`r`n" | out-null
Import-csv C:\Temp\GetList.csv | ForEach-Object {
if ($_.IPaddress) {
if (-Not (Test-Connection -ComputerName $_.IPaddress -Quiet -Count 2 -ErrorAction SilentlyContinue)) {
$info.IPaddress = $_.IPaddress
$info.Name = $_.Name
add-content -value "$($info.IPaddress),$($info.Name)" -path C:\Temp\Results.CSV
}
}
}
The export-csv is faster, thank you. I'm not sure if I have it in the right place, but it works, will run some tests on it tomorrow. I may have to include the passes tests though , still getting my head around the get-job cmdlet.
$info = "" | Select IPaddress,Name,Status
Import-csv c:\Temp\GetList.csv | ForEach-Object {
if($_.Ipaddress) {
if (-Not(Test-Connection -ComputerName $_.IPaddress -Quiet -Count 2 -ErrorAction SilentlyContinue)) {
$info.IPaddress = $_.IPaddress
$info.Name = $_.Name
$info.Status = "Failed"
$info | export-csv C:\Temp\Results1.csv -Force -Notypeinformation -Append
}
}
}
$list = Import-Csv C:\Temp\GetList.csv
ForEach ($item In $list) {
Start-Job -ScriptBlock {
param($item)
if (Test-Connection -Computername $item.IPaddress -Quiet -Count 1) {
Add-Content -value "$($item.IPaddress),$($item.Name),$($item.Stauts)" -Path C:\Temp\xlistlist.csv
$list | Export-csv c:\Temp\xCSvreults.csv -Force -NoTypeInformation -Append
}
} -argumentlist $list
}
Thanks Santiago, I did some exploring here, just ran out of time for the day. THANK YOU you for the code examples they cleared a few things up for me :-) –
Have Computer
Dec 2 at 3:59
Would you mind adding some inline comments so I can follow along? Just trynna understand multi-threading (using runspaces) a bit better. –
Abraham Zinala
Dec 2 at 4:58
1
#AbrahamZinala is there a specific part you're struggling to understand? i'm not an expert on this and wouldn't like to give false information. Might be better to pick up all the parts you don't understand and ask a new question –
Santiago Squarzon
Dec 2 at 12:19
Nah man, sorry, I figured it out lol –
Abraham Zinala
Dec 2 at 13:43
1
#AbrahamZinala the only hard part to understand is the [powershell] instance part imo, and you can think of it as a Start-Job but much faster and less memory consuming, you add your scriptblock with .AddScript({...}) and then the parameters with .AddParameters([hashtablehere]). It's important to save the PSInstance in a variable so that we can get the output out of it and it's handler (Status = $psinstance.BeginInvoke()) to know when the instance has completed. The rest is just pure blueprint, once you have one you can copy paste it :P –
Santiago Squarzon
Dec 2 at 13:50
#HaveComputer I have no idea what your comment means. –
Santiago Squarzon
Dec 5 at 1:10
the 2 solutions presented work much faster, my code however is lacking. –
Have Computer
Dec 5 at 1:16
#HaveComputer I know that, this can perform 50x faster or even more depending on hardware and bandwidth. –
Santiago Squarzon
This is a good place where you can use runspaces, I would love to test if Test-Connection -AsJob performs better than this but for some reason it is not available on PS Core on Linux.
Code below took around 10 seconds to scan 254 IPs using my private network IP range. There is a lot of tweaking that can be done, i.e. you can poke around the $Threshold variable, it's currently running 100 runspaces at a time, Count and TimeoutSeconds for Test-Connection have been set to 2 you can tweak that too.
$results variable can be exported using Export-Csv.
# Change this value for tweaking
$Threshold = 100
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $Threshold)
$RunspacePool.Open()
# This is for testing, use your CSV here instead
# => $list = Import-Csv C:\Temp\GetList.csv
$list = 1..254 | ForEach-Object {
[pscustomobject]#{
IPAddress = "192.168.1.$_"
Hostname = "ExampleHost$_"
}
}
$scriptBlock = {
param($ip, $hostname)
$params = #{
Quiet = $true
Count = 2
TimeoutSeconds = 2
ComputerName = $ip
}
$status = Test-Connection #params
[pscustomobject]#{
Hostname = $hostname
IPAddress = $ip
Status = ('Failed','Success')[[int]$status]
}
}
$runspaces = foreach($line in $list)
{
$params = #{
ip = $line.IPAddress
hostname = $line.Hostname
}
$psinstance = [powershell]::Create().AddScript($scriptBlock).AddParameters($params)
$psinstance.RunspacePool = $RunspacePool
[pscustomobject]#{
Instance = $psinstance
Status = $psinstance.BeginInvoke()
}
}
while($runspaces.Status.IsCompleted -contains $false)
{
Start-Sleep -Milliseconds 500
}
$results = $runspaces.ForEach({ $_.Instance.EndInvoke($_.Status) })
$RunspacePool.Dispose()
$results Sample:
Hostname IPAddress Status
-------- --------- ------
ExampleHost1 192.168.1.1 Success
ExampleHost2 192.168.1.2 Failed
ExampleHost3 192.168.1.3 Success
ExampleHost4 192.168.1.4 Failed
ExampleHost5 192.168.1.5 Success
ExampleHost6 192.168.1.6 Failed
ExampleHost7 192.168.1.7 Failed
ExampleHost8 192.168.1.8 Failed
ExampleHost9 192.168.1.9 Failed
ExampleHost10 192.168.1.10 Failed
...
...
Start-Job -ScriptBlock { Test-Connection -computername (Get-Content -Path “C:\Temp\GetList.csv”) }
Its lacking the fundamentals, works, DE fast.

Get the users who are having server access for my server

We need to extract a report every month for our servers which should report who all are having the server admin access .This will help us remove the people for whom access is no more needed. Its taking lot of time to extract manually and I read on blogs that PowerShell is used to automate such kind of administration jobs.
Other details:
Server - Win 2008 R2
Powershell 1.0
Could any one help me how to I extract this report?
Here's a quick way to enumerate the list of administrators for a server.
$group = [ADSI]"WinNT://./Administrators"
#($group.Invoke("Members")) | foreach {
$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
}
You can then use an email cmdlet to send it or however you want to send it back to you.
See: http://blogs.technet.com/b/heyscriptingguy/archive/2013/10/27/the-admin-s-first-steps-local-group-membership.aspx
There's no need for ADSI and PSRemoting. Example:
$cComputerNames = #("computer1", "computer2")
$cRows = #()
foreach ($sComputerName in $cComputerNames) {
$cAdminAccounts = Get-WmiObject -ComputerName $sComputerName `
-Class "Win32_GroupUser" `
| Where-Object {
($_.GroupComponent -split '"')[3] -eq "Administrators"
}
foreach ($cAdminAccount in $cAdminAccounts) {
$oRow = New-Object -TypeName PSObject -Property #{
"ComputerName" = $sComputerName
"AccountDomain" = ($cAdminAccount.PartComponent -split '"')[1]
"AccountName" = ($cAdminAccount.PartComponent -split '"')[3]
}
$cRows += $oRow
}
}
$cRows | Export-Csv -Path "admin-accounts.csv" -NoTypeInformation
You can use an associators of ... query to get more detailed info about accounts. See this blog post.

How to verify whether a windows server has mountpoint or not using WMI

I am generating a report where I need to find which servers has mountpoints configured on it..
can you help how to get that infor using WMI or powershell.
I mean I need to identify the servers, if mountpoints exists in it.. and also their names....
Get a list of all servers from textfile, AD, etc. and run a foreach loop with something like this:
Get-Wmiobject -query “select name,driveletter,freespace from win32_volume where drivetype=3 AND driveletter=NULL” -computer servername
A quick google search for "windows mount point wmi" would return THIS (source).
Then export the results to CSV, HTML or whatever you need. Your question is lacking a lot of details and any sign of effort from your part, so I can't/won't go any further.
UPDATE: Does this help? It lists mount points(folder paths, not driveletters).
$servers = #("server1","server2","server3","server4","server5")
$servers | % {
$mountpoints = #(Get-WmiObject Win32_MountPoint -ComputerName $_ | Select-Object -ExpandProperty Directory | ? { $_ -match 'Win32_Directory.Name="(\w:\\\\.+)"' }) | % { [regex]::Match($_,'Win32_Directory.Name="(\w:\\\\.+)"').Groups[1].Value -replace '\\\\', '\' }
if($mountpoints.Count -gt 0) {
New-Object psobject -Property #{
Server = $_
MountPoints = $mountpoints
}
}
}
Server MountPoints
------ -----------
{server1} {D:\SSD, C:\Test}

Wbadmin & powershell - latest backup version identifier

I need to get the latest backups version identifier in a powershell script. If I run wbadmin get versions, I get a list of backups and the last one is the one I need.
Is there a way to do a kind of select top 1 version identifier from backups order by date or parsing the wbadmin output and getting this.
edit
It may be the windows.serverbackup module and versionId of Get-WBBackupSet I'm looking for but still need help parsing this.
VersionId : 04/17/2013-21:00
BackupTime : 17/04/2013 22:00:55
BackupTarget : U:
RecoverableItems : Volumes, SystemState, Applications, Files, BareMetalRecovery
Volume : {System Reserved, Local disk (C:), Local disk (I:), Local disk (O:)...}
Application : {"Cluster", "Registry", "Microsoft Hyper-V VSS Writer"}
VssBackupOption : VssFullBackup
SnapshotId : 58999c7d-dfbf-4272-a5b9-21361d171486
Give this a try, Use -Last instead of -First to get the last item:
Get-WBBackupSet |
Sort-Object BackupTime |
Select-Object -First 1 -ExpandProperty VersionId
You can also play with the order of sorting with the -Ascending switch
Edit: revised version
For use with mixed environments (Windows Server 2008, 2008R2, 2012, 2012R2 as of this writing):
function Get-MyWBSummary
{
<#
.SYNOPSIS
Retrieves the history of backup operations on the local or any number of remote computers.
.DESCRIPTION
The Get-MyWBSummary cmdlet retrieves the history of backup operations on the local or any number of remote computers with remoting enabled. This information includes backuptime, backuplocation, bersion identifier and recovery information.
To use this cmdlet, you must be a member of the Administrators group or Backup Operators group on the local or remote computer, or supply credentials that are.
.PARAMETER ComputerName
Retrives backup results on the specified computers. The default is the local computer.
Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more computers. To specify the local computer ignore the ComputerName parameter.
This parameter rely on Windows PowerShell remoting, so your computer has to be configured to run remote commands.
.PARAMETER Credential
Specifies a user account that has permission to perform this action. The default is the current user. Type a user name, such as "User01", "Domain01\User01", or User#Contoso.com. Or, enter a PSCredential object, such as an object that is returned by the Get-Credential cmdlet. When you type a user name, you are prompted for a password.
.PARAMETER Last
Specifies the last (newest/latest) backup versions.
.EXAMPLE
Get-MyWBSummary
Retrieves all Windows Server backupversions from the local computer
.EXAMPLE
Get-MyWBSummary | Where BackupTime -gt (Get-Date).AddDays(-7)
Retrieves all Windows Server backupversions from the local computer within the last week
.EXAMPLE
Get-MyWBSummary -ComputerName $server1, $server2 -Last 1 -Credential $credential -ErrorAction SilentlyContinue -ErrorVariable sessionErrors
Retrieves the last (newest) Windows Server Backup backupversion from remote servers $server1 and $server2
.NOTES
Written by Anders Præstegaard (#aPowershell).
Version 1.0 (20-01-2016)
#>
[CmdletBinding()]
[OutputType([PSCustomObject])]
param
(
[string[]]$ComputerName = $env:COMPUTERNAME,
[System.Management.Automation.PSCredential]$Credential,
[int]$Last
)
begin
{
if ($Credential)
{
$PSDefaultParameterValues['New-PSSession:Credential'] = $Credential
}
$psSession = New-PSSession -ComputerName $ComputerName
}
Process
{
$scriptBlock = {
if (-not (Test-Path -Path 'C:\Windows\System32\wbadmin.exe'))
{
## Windows Server Backup not installed
continue
}
$content = WBAdmin.exe GET VERSIONS
if (-not $content)
{
## no versions found
continue
}
## Get linenumbers for each entity
$newJobLines = #($content | Select-String -Pattern 'Backup time: ')
if ($Using:Last -and $using:Last -lt $newJobLines.Count)
{
$newJobLines = $newJobLines[- $using:Last.. -1]
}
$newJobLines |
ForEach-Object{
## Location
$lineNumberLocation = $_.LineNumber
$backupLocation = $content[$lineNumberLocation] -replace 'Backup location: '
## Version Identifier
$lineNumberVersionIdentifier = $_.LineNumber + 1
$backupVersionIdentifier = $content[$lineNumberVersionIdentifier] -replace 'Version identifier: '
## Backuptime UTC
# Version identifier string in WBAdmin output represents the UTC datetime formated in 'MM/dd/yyyy-HH:mm'
$wbAdminDateStringFormat = 'MM\/dd\/yyyy-HH:mm'
$backupDateTimeFromVersionIdentifier = [DateTime]::ParseExact($backupVersionIdentifier, $wbAdminDateStringFormat, $null)
$backupDateTimeUtcSpecified = [DateTime]::SpecifyKind($backupDateTimeFromVersionIdentifier, [System.DateTimeKind]::Utc)
# NB WBAdmin calculates the time statically compared to your timezone (ie +1 hour)
# If your timezone support "Daylight Saving Time" then WBAdmin calculation is wrong
# ~ half of the year (as far as I can perceive)
$backupDateTimeLocalTime = $backupDateTimeUtcSpecified.ToLocalTime()
## Can recover
$lineNumberCanRecover = $_.LineNumber + 2
$backupVersionCanRecover = $content[$lineNumberCanRecover] -replace 'Can recover: '
[PSCustomObject]#{
BackupTime = $backupDateTimeLocalTime
BackupTimeUtc = $backupDateTimeUtcSpecified
BackupLocation = $backupLocation
VersionIdentifier = $backupVersionIdentifier
CanRecover = $backupVersionCanRecover
}
}
} # Scriptblock
Invoke-Command -Session $psSession -ScriptBlock $scriptBlock |
Select-Object -Property * -ExcludeProperty RunspaceId
}
end
{
if ($psSession)
{
Remove-PSSession -Session $psSession
}
}
}