I'm running a simple script to check if several specific services are running on an array of computers. It works fine, except the each 'row' appears' for every iteration of the loop. In this case 3 times. How do I edit/add to a property of an existing object? I.e. I want to add extra properties for the same object/row...
ComputerName Qualys Cloud Agent SplunkForwarder Service Cb Defense
------------ ------------------ ----------------------- ----------
dc01 Found Found Found
dc01 Found Found Found
dc01 Found Found Found
dc02 Found Found Found
dc02 Found Found Found
dc02 Found Found Found
ds01 Found Found Found
ds01 Found Found Found
ds01 Found Found Found
$ComputerName = 'dc01','ds01','dc02'
$ServiceList ='Qualys Cloud Agent',
'SplunkForwarder Service',
'Cb Defense'
$objarray = #()
$obj = #()
$ServiceArray = get-service -ComputerName $ComputerName -DisplayName $ServiceList | select-object -property MachineName,DisplayName,Name,Status
Foreach ($Computer in $ComputerName){
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $Computer
Foreach($Service in $ServiceList){
If ((gsv -computername $computer -displayname $Service).Status -eq 'Running'){
#write-output "Found $Service on $Computer"
$obj | Add-Member -MemberType NoteProperty -Name $service -Value 'Found'
$objArray += $obj
}
else{
#write-output "Not Found"
$obj | Add-Member -MemberType NoteProperty -Name $service -Value 'Not Found'
$objArray += $obj
}
}
}
$objarray | sort computername
Move the $objArray += $obj statement outside the inner loop - this way each object is only referenced by the array once:
Foreach ($Computer in $ComputerName){
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $Computer
Foreach($Service in $ServiceList){
If ((gsv -computername $computer -displayname $Service).Status -eq 'Running'){
#write-output "Found $Service on $Computer"
$obj | Add-Member -MemberType NoteProperty -Name $service -Value 'Found'
}
else{
#write-output "Not Found"
$obj | Add-Member -MemberType NoteProperty -Name $service -Value 'Not Found'
}
}
# Only need to add the resulting object ONCE
$objArray += $obj
}
I personally prefer constructing each list of object properties as a dictionary, and then convert + output it at the very end of the loop - at which point we simply assign the result of the entire foreach() statement to the desired variable:
$objArray = foreach($Computer in $ComputerName){
# Create dictionary to keep track of the new properties
$props = [ordered]#{ ComputerName = $Computer}
foreach($Service in $ServiceList){
# Populate dictionary with remaining properties inside the inner loop
if((gsv -computername $computer -displayname $Service).Status -eq 'Running'){
$props["$service"] = 'Found'
}
else{
$props["$service"] = 'Not Found'
}
}
# convert to object,
[pscustomobject]$props
}
If you want to add a property to an object, pipe the object to "add-member". Here is a quick example:
$files = Get-childitem "$env:USERPROFILE\Documents\*"
Foreach ($item in $files)
{
$item | Add-Member -MemberType NoteProperty -Name "extention" -Value $item.Name.Substring($item.name.Length - 3)
}
$files.extention
Related
We have a text log file similar to below with many users
<user>sandip</user>
something
<time>4:38 PM</time>
anything
<elapsed time> 60 mins </elapsed time>
We want to extract all users and we did the same simply by
Get-Content "C:\LOG\test.txt" | Select-String '(<user>.+</user>)' | ForEach-Object {
$_.Matches[0].Groups[1].Value
}
We want to parse test file line by line, check if it contains
user/time/elapsed time and [insert it in a dynamic variable if required] make a table of the same
Considering that your Log File follows the same format that i tested with:
(i.e something like this one:)
LogFile
This code should work just fine:
*
$Lines = get-content .\log.txt
$array = #()
foreach ($line in $lines)
{
if($line -like "<user>*")
{
$obj = New-Object psobject
Add-Member -InputObject $obj -MemberType NoteProperty -Name "UserName" -value $line.Replace("<user>","").Replace("</user>","")
}
if($line -like "<time>*")
{
Add-Member -InputObject $obj -MemberType NoteProperty -Name "Time" -value $line.Replace("<time>","").Replace("</time>","")
}
if($line -like "<elapsed time>*")
{
Add-Member -InputObject $obj -MemberType NoteProperty -Name "ElapsedTime" -value $line.Replace("<elapsed time>","").Replace("</elapsed time>","")
$array += $obj
}
}
$array | Export-Csv .\test.csv
*
A quite compact solution using two regular expressions,
with a nonconsuming positive
lookahead to
split the source file into sections starting with <user>
inside an if to get -match the current line with an alternation and a backreference to insert the found key and value into the current Row of the new table. See this RegEx live on regex101.com
## Q:\Test\2018\06\22\SO_50988379.ps1
$Table = ForEach ($Section in ((Get-Content .\Test.log -raw) -split '(?=<user>)' -ne '')) {
$Row = New-Object psobject
ForEach ($Line in ($Section -split "`r?`n")) {
if($Line -match "<(user|time|elapsed time)>([^<]+)</\1>"){
Add-Member -InputObject $Row -MemberType NoteProperty `
-Name "$($Matches[1])" -value $Matches[2].Trim()
}
}
$Row
}
$Table #| Export-Csv .\test.csv
Sample output:
> .\SO_50988379.ps1
user time elapsed time
---- ---- ------------
sandip 4:38 PM 60 mins
Joshi 8:15 PM 60 mins
My goal is to save some info into a variable that later will be written out ($Server).
I have created a cmdlet that gets some info from the CPU, which gives me the result i want to add into my server variable.
When i add it i get a $null value.
PS C:\> $CPU = Get-CPUInfo -Computername .
PS C:\> $CPU
CPUSpecification CPUCurrentClockSpeed CPUMaxClockSpeed Socket
---------------- -------------------- ---------------- ------
Intel(R) Core(TM) i5-6200U CPU # 2.30GHz 2300 2400 {#{SocketDesignation=U3E1; NumberOfCores=2; NumberOfLogicalProcessors=4}}
PS C:\> $Server = New-Object PSObject
PS C:\> Add-Member -InputObject $Server -MemberType NoteProperty -Name "CPU" -Value $CPU
PS C:\> $Server
CPU
---
{$null, #{CPUSpecification=Intel(R) Core(TM) i5-6200U CPU # 2.30GHz; CPUCurrentClockSpeed=2300; CPUMaxClockSpeed=2400; Socket=System.Object[]}}
PS C:\>
I would like to know why i get this $null inside, and how do i change it if it is some usefull information?
Later on i will create a .json file that stores my $Server variable, and it also saves the null value inside there.
I really do not like the idea of my users recieving null values where none are expected.
EDIT:
I have made this custom Get-CPUInfo cmdlet that i use, however it gives me what i need and no $null value:
function Get-CPUInfo
{
[CmdletBinding()]
Param
(
# Name of the computer to get version from
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[Alias("CN", "Host", "HostName")]
[string]$Computername
)
$MySocket = Get-WmiObject -Class Win32_Processor -ComputerName $ComputerName
$IsSocketArray = $MySocket -is [system.array]
$SocketInfo
$SocketInfoArray = #()
if(-not $IsSocketarray)
{
$SocketInfo = New-Object PSObject
Add-Member -InputObject $SocketInfo -MemberType NoteProperty -Name "SocketDesignation" -Value $MySocket.SocketDesignation
Add-Member -InputObject $SocketInfo -MemberType NoteProperty -Name "NumberOfCores" -Value $MySocket.NumberOfCores
Add-Member -InputObject $SocketInfo -MemberType NoteProperty -Name "NumberOfLogicalProcessors" -Value $MySocket.NumberOfLogicalProcessors
$SocketInfoArray += $SocketInfo
}
else
{
foreach ($s in $MySocket)
{
$SocketInfo = New-Object PSObject
Add-Member -InputObject $SocketInfo -MemberType NoteProperty -Name "SocketDesignation" -Value $s.SocketDesignation
Add-Member -InputObject $SocketInfo -MemberType NoteProperty -Name "NumberOfCores" -Value $s.NumberOfCores
Add-Member -InputObject $SocketInfo -MemberType NoteProperty -Name "NumberOfLogicalProcessors" -Value $s.NumberOfLogicalProcessors
$SocketInfoArray += $SocketInfo
}
}
$CPUSpecification = if($IsSocketarray){ $MySocket[0].Name} else{ $MySocket.Name}
$CPUCurrentClockSpeed = if($IsSocketarray){ $MySocket[0].CurrentClockSpeed} else{ $MySocket.CurrentClockSpeed}
$CPUMaxClockSpeed = if($IsSocketarray){ $MySocket[0].MaxClockSpeed} else{ $MySocket.MaxClockSpeed}
$CPUInfo = New-Object PSObject
Add-Member -InputObject $CPUInfo -MemberType NoteProperty -Name "CPUSpecification" -Value $CPUSpecification
Add-Member -InputObject $CPUInfo -MemberType NoteProperty -Name "CPUCurrentClockSpeed" -Value $CPUCurrentClockSpeed
Add-Member -InputObject $CPUInfo -MemberType NoteProperty -Name "CPUMaxClockSpeed" -Value $CPUMaxClockSpeed
Add-Member -InputObject $CPUInfo -MemberType NoteProperty -Name "Socket" -Value $SocketInfoArray
return $CPUInfo
}
PowerShell doesn't print null elements of an array when you display it directly, but it'll be printed in a compact representation of properties.
Let's check:
(Get-CPUInfo -Computername .).GetType()
Object[] System.Array
(Get-CPUInfo -Computername .).Count
2
(Get-CPUInfo -Computername .)[0] -eq $null
True
Something outputs a null value in the Get-CPUInfo function:
$SocketInfo
Remove it.
While trying to create an CSV file with information about certificates I have an issue to store the userrights on the private key.
The problem is that I want to store multiple values in one attribute so I use an array.
At first I had no errors, however the column in my csv-file remained empty even in the case where the array has a value.
With a simple Write-Host I can see my array has the expected value so this part works okay.
For further investigations I have added the line:
Get-Member $certs.GetValue("UserRights")
This gives an error indicating I have to convert my variable to a string-variable.
So next I have tried to convert this array to a single string.
I have tried several ways but my error doesn't disappear so it doesn't work.
Underneath is my full code with some former attempts commented.
cls $certs = Get-ChildItem cert:\LocalMachine -Recurse | Where-Object {-not $_.PSIsContainer} | Select * Write-Host ("There were {0} certificates" -f ($certs | Measure-Object).Count)
foreach($certificate in $certs) {
if($certificate.HasPrivateKey)
{
Write-Host "Certificate's PSChildName is" $certificate.PSChildName
$rsaFile = $certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
$fullPath = "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\" + $rsaFile
$acl = Get-Acl -Path $fullPath
foreach($accessrule in $acl.Access)
{
Write-Host "User" $accessrule.IdentityReference "has the following rights:" $accessrule.FileSystemRights
}
Write-Host "------"
$UserRechten = #()
foreach($accessrule in $acl.Access)
{
$UserRechten += "{0}:{1};" -f ($accessrule.IdentityReference,$accessrule.FileSystemRights)
}
Write-Host "================================================================"
# -join $UserRechten
# $Userrechten | out-string
# $UserRechten = [system.String]::Join(" ", $UserRechten)
$separator = ";"
[string]::Join($separator,$UserRechten)
$certs | Add-Member -MemberType NoteProperty -Name "UserRights" -Value $UserRechten -Force
Write-Host "UserRechten has value : "$UserRechten
Get-Member $certs.GetValue("UserRights")
Write-Host "================================================================"
} }
$Certs | Add-Member -MemberType NoteProperty -Name "MachineName" -Value $env:COMPUTERNAME -Force
# $certs | Add-Member -MemberType NoteProperty -Name "Store" -Value 'My' -Force $RunDate = Get-Date -Format 'yyyy-MM-dd' $certs | Add-Member -MemberType NoteProperty -Name "RunDate" -Value $RunDate -Force $certs | Add-Member -MemberType NoteProperty -Name "Owner" -Value $env:USERNAME -Force
$Certs | Select * | Export-Csv c:\Certificaten\LocalCertsAll_$env:COMPUTERNAME.csv
$Certs | Select MachineName, Owner, PSParentPath, DnsNameList, PSChildName, NotBefore, NotAfter, Rundate, EnhancedKeyUsageList, HasPrivateKey, SerialNumber, Issuer, Subject, FriendlyName, UserRigthts |
Export-CSV c:\Certificaten\Localcerts_$env:COMPUTERNAME.csv
As noted in the comments, Get-Member is probably not what you're looking for
You (almost certainly) don't want to add the UserRights member property to the $Certs array, but rather to the individual objects in $Certs.
(I removed a bunch of superfluous Write-Host statements for readability):
$CertsAmended = foreach($Certificate in $certs)
{
if($certificate.HasPrivateKey)
{
$rsaFile = $certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
$fullPath = "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\" + $rsaFile
$acl = Get-Acl -Path $fullPath
# Create the UserRights value using -join
$UserRechten = #(foreach($accessrule in $acl.Access){
Write-Host "User" $accessrule.IdentityReference "has the following rights:" $accessrule.FileSystemRights
"{0}:{1}" -f ($accessrule.IdentityReference,$accessrule.FileSystemRights)
}) -join ";"
# Add the property to the individual object
$Certificate | Add-Member -MemberType NoteProperty -Name "UserRights" -Value $UserRechten
Write-Host "Userrights: " $UserRechten
# "Drop" the certificate object (now with a UserRights value) back onto the pipeline
$Certificate
}
}
Now you can export the $CertsAmended array to CSV all you want
If you find the $var = #(foreach($item in $collection){}) -join ';' displeasing to the eye, break it into two statements:
$UserRechten = foreach($accessrule in $acl.Access)
{
# Create UserRight string here, without ;
}
$UserRechten = $UserRechten -join ';'
For the $fullPath variable, you may want to use the Join-Path cmdlet:
$fullPath = Join-Path "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\" $rsaFile
I need to create a Powershell object, array or hash table to store a list of users and assorted details, these are extracted from a CSV file and located using Get-ADUser. As below:
$userList = Import-CSV $CSVInputFile
$users = #{}
Foreach ($csvUser in $userList)
{
$userSearchString = $csvUser | Select -ExpandProperty SamAccountName
$currentUser = (Get-ADUser -Filter {SamAccountName -eq $userSearchString} `
-Properties PasswordExpired,PasswordLastSet,EmailAddress |
Where {$_.Enabled -eq "True"})
If ($currentUser.EmailAddress -ne $null)
{
$currentUserEmailString = $csvUser | Select -ExpandProperty EmailAddress
$currentUserEmailString = ($currentUserEmailString -as [string])
$currentUser.EmailAddress = $currentUserEmailString
}
$Users = New-Object PSObject -Property #{
DistinguishedName = $currentUser.DistinguishedName
EmailAddress = $currentUser.EmailAddress
Enabled = $currentUser.Enabled
GivenName = $currentUser.GivenName
Name = $currentUser.Name
PasswordExpired = $currentUser.PasswordExpired
PasswordLastSet = $currentUser.PasswordLastSet
SamAccountName = $currentUser.SamAccountName
Surname = $currentUser.Surname
}
$Users
}
How can I add the details of each user for each iteration of the loop to the object.
I want to end up with an object containing the details of a number of users, same as the output directly from Get-ADUser:
Name SamAccountName EmailAddress
---- -------------- ------------
User1 user1 user1#domain.com
User2 user2 user2#domain.com
User3 user3 user3#domain.com
Any help would be greatly appreciated!
Not sure if I'm missing the point on this but I see you are building a custom object right in your loop. The only issue I do see is you are not keeping the results after each loop. Rather you are destroying the objects history.
I would change the declaration of $users to an array $users = #() and instead of populating a user hashtable into users add the current object into the array. You will then have an array of hashtables:
$Users += New-Object PSObject -Property #{...
Then you could the $Users output line outside the loop and you will have the whole thing. Then you could just output to a Select to get the output you desire.
$Users | Select-Object name,SamAccountName,EmailAddress
There is a potential major drawback of this approach though. When using += on arrays a new array is created and resized for the new element and the old array is discarded. This has huge performance implications for larger arrays.
An even better way to approach this would be to leverage the pipeline. This would be a performance boost when you have larger user groups.
Import-CSV $CSVInputFile | ForEach-Object{
$userSearchString = $_.SamAccountName
$currentUser = Get-ADUser -Filter {SamAccountName -eq $userSearchString} `
-Properties PasswordExpired,PasswordLastSet,EmailAddress |
Where {$_.Enabled -eq "True"}
If ($currentUser.EmailAddress -ne $null){
$currentUser.EmailAddress = $_.EmailAddress
}
[pscustomobject][ordered]#{
DistinguishedName = $currentUser.DistinguishedName
# ..... truncated
Surname = $currentUser.Surname
}
}
Now you could send that to something like Export-CSV or just save it into a variable. Your options are open now. [pscustomobject][ordered] are type accelerators available in PowerShell v3.0+
Define an $users as Array
$users = #()
and append the New-Object into $Users.
$Users += New-Object
Can't believe both of you guys got in before me! Oh well.
Hope this helps anyway.
$userList = Import-CSV $CSVInputFile
$users = #()
Foreach ($csvUser in $userList)
{
$userSearchString = $csvUser | Select -ExpandProperty SamAccountName
$currentUser = (Get-ADUser -Filter {SamAccountName -eq $userSearchString} `
-Properties PasswordExpired,PasswordLastSet,EmailAddress |
Where {$_.Enabled -eq "True"})
If ($currentUser.EmailAddress -ne $null)
{
$currentUserEmailString = $csvUser | Select -ExpandProperty EmailAddress
$currentUserEmailString = ($currentUserEmailString -as [string])
$currentUser.EmailAddress = $currentUserEmailString
}
#clears the properties of the previous object and starts collecting properties
$UserObj = New-Object PSObject
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "DistinguishedName" -Value $($currentUser.DistinguishedName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "EmailAddress" -Value $($currentUser.EmailAddress)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Enabled" -Value $($currentUser.Enabled)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "GivenName" -Value $($currentUser.GivenName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "UserName" -Value $($currentUser.Name)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "PasswordExpired" -Value $($currentUser.PasswordExpired)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "PasswordLastSet" -Value $($currentUser.PasswordLastSet)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "SamAccountName" -Value $($currentUser.SamAccountName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Surname" -Value $($currentUser.Surname)
#saves the properties in an array that exists outside of the loop to preserve information beyond one interation
$users += $UserObj
}
$users | Format-Table -Property UserName,SamAccountName,EmailAddress
I am looking to create either a non-jagged array or a hash table (I am not sure the difference nor what I need to get the job done). Here is what I am trying to do.
I would like to query a list of servers for several values and then store those values for output to a CSV file. Here is the code.
$allServers = "svr1","svr2","svr3"
$a = #{}
$outData = New-Object PSObject
$allServers | ForEach-Object {
$cpu = (Get-WmiObject win32_processor -ComputerName $_).length
$mem = (Get-WmiObject win32_physicalmemory -ComputerName $_).Capacity /1GB
$outData | Add-Member -Type NoteProperty -Name "SERVERNAME" -Value $_
$outData | Add-Member -Type NoteProperty -Name "#CPU" -Value $cpu
$outData | Add-Member -Type NoteProperty -Name "#GBRAM" -Value $mem
}
Write-Host $outData
I am getting errors because it seems like it is trying to create the same entries repeatedly. Is it possible to make an empty hash table (or non-jagged array) with column names and then just populate values?
Create the object inside the loop, output it, and assign the value of the whole pipeline to your array variable:
$allServers = "svr1","svr2","svr3"
$a = $allServers | ForEach-Object {
$cpu = (Get-WmiObject win32_processor -ComputerName $_).length
$mem = (Get-WmiObject win32_physicalmemory -ComputerName $_).Capacity /1GB
$outData = New-Object PSObject
$outData | Add-Member -Type NoteProperty -Name "SERVERNAME" -Value $_
$outData | Add-Member -Type NoteProperty -Name "#CPU" -Value $cpu
$outData | Add-Member -Type NoteProperty -Name "#GBRAM" -Value $mem
$outData
}
$a | Export-Csv $path -NoTypeInformation