Loop through line for multiple values - powershell

Word Doc content: [(7)]/[(8)] [ Security Agent 1] as security trustee for the Secured Parties (the "Security Agent") ; and
Value to extract is "Security Agent 1"
It extracts 7 as thats the first within bracket.
Below code works fine, but will give only first occurence/value within brackets. Need to loop it through multiple values within bracket and give me the 3rd value in bracket
$FinalTable = Get-Content $SourceFile|
select-string -pattern $SearchKeyword |
Select -Property #{Name = 'Name'; Expression = {$_.Line}}, #{Name = 'LineNo'; Expression = {$_.LineNumber}}, #{Name='Criteria';Expression = {$_.Category}} |
ForEach-Object {
$str = $_.Name
$LineNumber = $_.LineNo
$Criteria = $_.Criteria
$start = $str.indexOf("[") + 1
$end = $str.indexOf("]", $start)
$length = $end - $start
$result = ($str.substring($start, $length)).trim()
#Creating a custom object to display in table format
$Obj = New-Object -TypeName PSCustomObject
Add-Member -InputObject $Obj -MemberType NoteProperty -Name Category -Value $Category
Add-Member -InputObject $Obj -MemberType NoteProperty -Name Description -Value $result
$obj
}
$FinalTable | Export-Csv -Path $DestinationFile -NoTypeInformation -Encoding ASCII
Trying this as theo suggested but didn't worked
$FinalTable = Get-Content $SourceFile|
select-string -pattern $SearchKeyword |
Select -Property #{Name = 'Name'; Expression = {$_.Line}}, #{Name = 'LineNo'; Expression = {$_.LineNumber}}, #{Name='Criteria';Expression = {$_.Category}} |
ForEach-Object {
$str = $_.Name
$LineNumber = $_.LineNo
$Criteria = $_.Criteria
#$start = $str.indexOf("[") + 1
#$end = $str.indexOf("]", $start)
#$length = $end - $start
#$result = ($str.substring($start, $length)).trim()
#Write-host $str
if ($str -match '(\[[^\]]+])\/(\[[^\]]+])\s*\[\s*([^\]]+)]') {
# $matches[1] --> "(7)"
# $matches[2] --> "(8)"
$result = $matches[3] # --> "Security Agent 1"
}
Write-Host $result
#Creating a custom object to display in table format
$Obj = New-Object -TypeName PSCustomObject
Add-Member -InputObject $Obj -MemberType NoteProperty -Name Category -Value $Category
Add-Member -InputObject $Obj -MemberType NoteProperty -Name Description -Value $result
$obj
}
$FinalTable | Export-Csv -Path $DestinationFile -NoTypeInformation -Encoding ASCII

You could use Regular Expression to get the parts between the brackets in a string like [(7)]/[(8)] [ Security Agent 1] as security trustee for the Secured Parties (the "Security Agent").
Instead of
$start = $str.indexOf("[") + 1
$end = $str.indexOf("]", $start)
$length = $end - $start
$result = ($str.substring($start, $length)).trim()
do
if ($str -match '\[([^\]]+)\]\/\[([^\]]+)\]\s*\[\s*([^\]]+)]') {
# $matches[1] --> "(7)"
# $matches[2] --> "(8)"
$result = $matches[3] # --> "Security Agent 1"
}
else {
$result = '' # should not happen..
}

Related

Splitting an array to different variables

I have an XML file with multiple same-name fields like this:
<XMLDAT>
<Interpret>Crow Jonathan</Interpret>
<Interpret>Mcnabney Douglas</Interpret>
<Interpret>Haimovitz Matt</Interpret>
<Interpret>Sitkovetski Dmitri</Interpret>
</XMLDAT>
I'm trying to split these into a separate variable for each Interpret so I can be able to export it as a CSV file. Ex.
[xml]$XML = Get-Content -Path C:\TestFile.xml
$Interpret = $XML.XMLDAT.Interpret
$interpret1 = ""
$interpret2 = ""
$interpret3 = ""
$interpret4 = ""
$DATACOLLECTION = #()
$DATA = New-Object PSObject
Add-Member -inputObject $DATA -memberType NoteProperty -name "Interpret1" -value $interpret1
Add-Member -inputObject $DATA -memberType NoteProperty -name "Interpret2" -value $interpret2
Add-Member -inputObject $DATA -memberType NoteProperty -name "Interpret3" -value $interpret3
Add-Member -inputObject $DATA -memberType NoteProperty -name "Interpret4" -value $interpret4
$DATACOLLECTION += $DATA
$DATACOLLECTION | Export-Csv -append -path C:\test.csv -NoTypeInformation
I'm not sure how to proceed into splitting these into their own variables.
PowerShell supports multi-target variable assignments:
[xml]$XML = Get-Content -Path C:\TestFile.xml
$interpret1, $interpret2, $interpret3, $interpret4 = $XML.XMLDAT.Interpret
But you don't really need all those variables :)
You could construct the final object by dynamically adding all the "Interpret" node values to a hashtable and then convert that to an object:
[xml]$XML = Get-Content -Path C:\TestFile.xml
$properties = [ordered]#{}
$XML.XMLDAT.Interpret |ForEach-Object -Begin {$number = 1} -Process {
$properties["Interpret$($number++)"] = "$_"
}
#( [pscustomobject]$properties ) |Export-Csv -Append -Path C:\test.csv -NoTypeInformation
Was able to get desired result using this ForEach loop:
ForEach ($Interprets in $Interpret){
$interpret1 = $Interpret[0]
$interpret2 = $Interpret[1]
$interpret3 = $Interpret[2]
$interpret4 = $Interpret[3]
}
I would just do one column with four rows:
$xml.xmldat.interpret | % { [pscustomobject]#{Interpret = $_} }
Interpret
---------
Crow Jonathan
Mcnabney Douglas
Haimovitz Matt
Sitkovetski Dmitri

Array in a foreach loop

What am I doing wrong here?
The mailbox has an active an inactive mailbox so it will return two mailboxes.
However, when trying to capture the output, I am only getting the last account in the array
Note, this is a simplified version of a larger script, but kept it simple for this example.
$guid = import-csv "c:\temp\Mailboxes.csv"
$DAta = New-Object psobject
$Data | Add-Member -MemberType NoteProperty -Name alias -Value $null
$Data | Add-Member -MemberType NoteProperty -Name guid -Value $null
$mbxcol = #()
#$data = $null
foreach ($G in $Guid){
$mbx = Get-mailbox $g.alias -IncludeInactiveMailbox
$data.alias = $mbx.alias
$data.guid = $mbx.guid
$MBXCol += $Data
}
$mbxcol
As explained in comments, every array element is a reference of the same object ($Data), a simple way to demonstrate using Object.ReferenceEquals Mehod with this example:
foreach ($item in 0..10) {
$data.Alias = 'mailbox{0}' -f $item
$data.Guid = [guid]::NewGuid()
$mbxcol += $data
}
[object]::ReferenceEquals($data, $mbxcol[0]) # -> True
As for how to simplify and make your code more efficient, do not add elements (+=) to a fixed collection (#( )):
$result = (Import-Csv "c:\temp\Mailboxes.csv").Alias |
Get-Mailbox -IncludeInactiveMailbox |
Select-Object Alias, Guid
A much more simple example of your code is:
$guid = ("A","B")
$Data = New-Object psobject
$Data | Add-Member -MemberType NoteProperty -Name alias -Value $null
$mbxcol = #()
foreach ($G in $Guid){
$mbx = $g
$data.alias = $mbx
$MBXCol += $Data
}
$mbxcol
As #Santiago mentioned in his comment, $Data is a reference to an object, so each time you update it, you overwrite it, even if it is in an array. To fix this, instantiate the object each loop as follows:
$guid = ("A","B")
$mbxcol = #()
foreach ($G in $Guid){
$Data = New-Object psobject
$Data | Add-Member -MemberType NoteProperty -Name alias -Value $null
$mbx = $g
$data.alias = $mbx
$MBXCol += $Data
}
$mbxcol

Not able to add data to csv file powershell

I am trying to add data to an csv file.
I am creating the csv with header first and then trying to add the rows. but it is returning blank csv file
$props=[ordered]#{
ServerName=''
SystemFolderPath=''
IdenityReference=''
FileSystemRights=''
}
New-Object PsObject -Property $props |
Export-Csv "C:\status_report.csv" -NoTypeInformation
$serverlist = Get-Content -Path "C:\ServerList.txt"
foreach($server in $serverlist)
{
$paths_list = $env:Path -Split ';'
Foreach ($sys_Path in $paths_list)
{
$Permissions = Get-Acl -Path $sys_Path
$Users_Permissions = $Permissions.Access | Where-Object {$_.IdentityReference}
#$Users_Permission
Foreach ($user in $Users_Permissions)
{
$IdenityReference = $user.IdentityReference.Value
$FileSystemRights = $user.FileSystemRights
$NewLine = "{0},{1},{2},{3}" -f $server,$sys_Path,$IdenityReference,$FileSystemRights
$NewLine | Export-Csv -Path "C:\status_report.csv" -Append -NoTypeInformation -Force
}
}
}
Please let me know what I am doing wrong here
The main reason why you're seeing this is because Export-Csv expects an object or object[] through the pipeline and you're passing a formatted string instead. This is specified on MS Docs:
Do not format objects before sending them to the Export-CSV cmdlet. If Export-CSV receives formatted objects the CSV file contains the format properties rather than the object properties.
PS /> 'server01,C:\Windows,Computer\User,FullControl' | ConvertTo-Csv
"Length"
"45"
Instead of appending to a CSV which is quite inefficient, unless there is a specific need for this, what you will want to do is collect the results first and then export them.
I'm not too sure why | Where-Object { $_.IdentityReference } is needed, I left it there but I don't think it's needed.
Regarding $serverlist, if you will run this on remote hosts you would be better of using Invoke-Command since it allows parallel invocations. The outer loop wouldn't be needed in that case:
$serverlist = Get-Content -Path "C:\ServerList.txt"
# Collect results here
$result = Invoke-Command -ComputerName $serverlist -ScriptBlock {
$paths_list = $env:Path -Split [System.IO.Path]::PathSeparator
foreach($sys_Path in $paths_list)
{
$Permissions = (Get-Acl -Path $sys_Path).Access
foreach($acl in $Permissions)
{
if(-not $acl.IdentityReference)
{
continue
}
[pscustomobject]#{
ComputerName = $env:ComputerName
SystemFolderPath = $sys_Path
IdenityReference = $acl.IdentityReference.Value
FileSystemRights = $acl.FileSystemRights
}
}
}
} -HideComputerName
$result | Export-Csv -Path "C:\status_report.csv" -NoTypeInformation
Accept Santiago above but this is what I did with what you wrote.
$props = [ordered]#{
ServerName = ''
SystemFolderPath = ''
IdenityReference = ''
FileSystemRights = ''
}
New-Object PsObject -Property $props |
Export-Csv "C:\status_report.csv" -NoTypeInformation
$serverlist = Get-Content -Path "C:\ServerList.txt"
$result = $serverlist | ForEach-Object {
foreach ($server in $_) {
$paths_list = $null
$paths_list = $env:Path -Split ';'
Foreach ($sys_Path in $paths_list) {
$Permissions = Get-Acl -Path $sys_Path
$Users_Permissions = $Permissions.Access | Where-Object { $_.IdentityReference }
#$Users_Permission
Foreach ($user in $Users_Permissions) {
$IdenityReference = $null
$FileSystemRights = $null
$IdenityReference = $user.IdentityReference.Value
$FileSystemRights = $user.FileSystemRights
[PSCustomObject]#{
Server = $server
Sys_Path = $sys_Path
Referecent = $IdenityReference
Rights = $FileSystemRights
}
$sys_Path = $null
}
}
}
}
$result | Export-Csv -Path "C:\status_report.csv" -NoTypeInformation
Santiago's answer is correct and contains all the required information for you to understand the issue you have here.
I just wanted to provide you with the minimum modifications to be done in your script:
Replace the $props custom object by a function (i.e CreateCustomObject)
function CreateCustomObject($val1, $val2, $val3, $val4) {
$NewObject = New-Object PSObject ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "ServerName" -Value $val1 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "SystemFolderPath" -Value $val2 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "IdenityReference" -Value $val3 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "FileSystemRights" -Value $val4 ;
return $NewObject ;
}
Replace the String Variable $NewLine by an Array
$NewLine = #()
$NewLine += CreateCustomObject $server $sys_Path $IdenityReference $FileSystemRights
Write to CSV only once data is collected (move the command to the end of the script)
So the final script will look something like that:
function CreateCustomObject($val1, $val2, $val3, $val4) {
$NewObject = New-Object PSObject ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "ServerName" -Value $val1 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "SystemFolderPath" -Value $val2 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "IdenityReference" -Value $val3 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "FileSystemRights" -Value $val4 ;
return $NewObject ;
}
$serverlist = Get-Content -Path "C:\Temp\ServerList.txt"
$NewLine = #()
foreach($server in $serverlist) {
$paths_list = $env:Path -Split ';'
Foreach ($sys_Path in $paths_list) {
$Permissions = Get-Acl -Path $sys_Path
$Users_Permissions = $Permissions.Access | Where-Object {$_.IdentityReference}
#$Users_Permission
Foreach ($user in $Users_Permissions) {
$IdenityReference = $user.IdentityReference.Value
$FileSystemRights = $user.FileSystemRights
$NewLine += CreateCustomObject $server $sys_Path $IdenityReference $FileSystemRights
}
}
}
$NewLine | Export-Csv -Path "C:\temp\status_report.csv" -NoTypeInformation -Force

Sum Columns Using Powershell

I have written the following PowerShell script for getting disk space information for servers in our environment.
$servers = Get-Content E:\POC.txt
$array = #()
foreach($server in $servers){
$sysinfo = Get-WmiObject Win32_Volume -ComputerName $server
for($i = 0;$i -lt $sysinfo.Count; $i++){
$sname = $sysinfo[$i].SystemName
$servername = $server
$label = $sysinfo[$i].Label
if(($label) -and (!($label.Contains("FILLER")))){
write-host "Processing $label from $server"
$name = $sysinfo[$i].Name
$capacity = [math]::round(($sysinfo[$i].Capacity/1GB),2)
$fspace = [math]::round(($sysinfo[$i].FreeSpace/1GB),2)
$sused = [math]::round((($sysinfo[$i].Capacity - $sysinfo[$i].FreeSpace)/1GB),2)
$fspacepercent = [math]::Round((($sysinfo[$i].FreeSpace*100)/$sysinfo[$i].Capacity),2)
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "SystemName" -Value $sname
$obj | Add-Member -MemberType NoteProperty -Name "ServerName" -Value $server
$obj | Add-Member -MemberType NoteProperty -Name "Label" -Value $label
$obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $name
$obj | Add-Member -MemberType NoteProperty -Name "Capacity(GB)" -Value $capacity
$obj | Add-Member -MemberType NoteProperty -Name "FreeSpace(GB)" -Value $fspace
$obj | Add-Member -MemberType NoteProperty -Name "Used(GB)" -Value $sused
$obj | Add-Member -MemberType NoteProperty -Name "FreeSpace%" -Value $fspacepercent
$array += $obj
}
}
$array += write-output " "
$totalSize = ($array | Measure-Object 'Capacity(GB)' -Sum).Sum
$array += $totalsize
$array += write-output " "
}
$filename = "E:\VolumeReport.csv"
$array | Export-CSV $filename -NoTypeInformation
One additional requirement here is to get the sum of the columns for Capacity, Size and Freespace for each server. I tried using Measure-Object but no success.
No values are getting outputted here. Just blank. Please look into this and kindly assist.
Let try this on for size shall we.
$servers = Get-Content E:\POC.txt
$propertyOrdered = "SystemName","ServerName","Label","Name","Capacity(GB)","FreeSpace(GB)","Used(GB)","FreeSpace%"
$filename = "C:\temp\VolumeReport.csv"
('"{0}"' -f ($propertyOrdered -join '","')) | Set-Content $filename
foreach($server in $servers){
$sysinfo = Get-WmiObject Win32_Volume -ComputerName $server
$serverDetails = #()
for($i = 0;$i -lt $sysinfo.Count; $i++){
$sname = $sysinfo[$i].SystemName
$servername = $server
$label = $sysinfo[$i].Label
if(($label) -and (!($label.Contains("FILLER")))){
write-host "Processing $label from $server"
$name = $sysinfo[$i].Name
$capacity = [math]::round(($sysinfo[$i].Capacity/1GB),2)
$fspace = [math]::round(($sysinfo[$i].FreeSpace/1GB),2)
$sused = [math]::round((($sysinfo[$i].Capacity - $sysinfo[$i].FreeSpace)/1GB),2)
$fspacepercent = [math]::Round((($sysinfo[$i].FreeSpace*100)/$sysinfo[$i].Capacity),2)
$props = #{
"SystemName" = $sname
"ServerName" = $server
"Label" = $label
"Name" = $name
"Capacity(GB)" = $capacity
"FreeSpace(GB)" = $fspace
"Used(GB)" = $sused
"FreeSpace%" = $fspacepercent
}
# Build this server object.
$serverDetails += New-Object PSObject -Property $props
}
}
# Output current details to file.
$serverDetails | Select $propertyOrdered | ConvertTo-Csv -NoTypeInformation | Select-Object -Skip 1 | Add-Content $filename
#Calculate Totals and append to file.
$totals = '"","","","Totals",{0},{1},{2},""' -f ($serverDetails | Measure-Object -Property "Capacity(GB)" -Sum).Sum,
($serverDetails | Measure-Object -Property "FreeSpace(GB)" -Sum).Sum,
($serverDetails | Measure-Object -Property "Used(GB)" -Sum).Sum
$totals | Add-Content $filename
}
Part of the issue here is that you were mixing object output and static string output which most likely would have been holding you back. I tidied up the object generation in a way that should be 2.0 compliant. Not that what you were going was wrong in anyway but this is a little more pleasing to the eye then all the Add-Members
I removed $array since it did not have a place anymore since the logic here is constantly output data to the output file as supposed to storing it temporarily.
For every $server we build an array of disk information in the variable $serverDetails. Once all the disks have been calculated (using your formulas still) we then create a totals line. You were not really clear on how you wanted your output so I guessed. The above code should net output like the following. (It looks a lot nicer in Excel or in a csv aware reader. )
"SystemName","ServerName","Label","Name","Capacity(GB)","FreeSpace(GB)","Used(GB)","FreeSpace%"
"server01","server01","System Reserved","\\?\Volume{24dbe945-3ea6-11e0-afbd-806e6f6e6963}\","0.1","0.07","0.03","71.85"
"","","","Totals",0.1,0.07,0.03,""
"server02","server02","System Reserved","\\?\Volume{24dbe945-3ea6-11e0-afbd-806e6f6e6963}\","0.1","0.07","0.03","69.27"
"server02","server02","images","I:\","1953.12","152.1","1801.02","7.79"
"server02","server02","Data","E:\","79.76","34.59","45.18","43.36"
"","","","Totals",2032.98,186.76,1846.23,""

Want to Wrap PSList in a Powershell function to use pipeline values

I like PSList, and use the CPU and Elapsed times to indentify processes that need to be killed. I would like to wrap it in a powershell function that returns pipeline values so that I could do something like the following:
get-remoteprocess server1 | where {$_.CPUMinutes -gt 4}
I get stuck in returning values from the function - which I understand would need to be an array of objects.
Heres what my first version looks like:
function get-remoteproc {
param ($hostname)
$results = pslist "\\$hostname"
for ($i= 3; $i -le $results.length; $i++) {
$strline = $results[$i]
$StrWithPrefix = " "+$results[$i]
$trimmedline = $StrWithPrefix -replace '\s+', " ";
$Splitline = $trimmedline.Split(" ")
$ProcessName = $Splitline[1]
.
.
$ProcessCPUTime = ":"+[string]$Splitline[7]
$SplitCpuTime = $ProcessCPUTime.Split(":")
$CpuHours = $SplitCpuTime[1]
$CpuMinutes = $SplitCpuTime[2]
$CpuSeconds = $SplitCpuTime[3]
.
.
.
$obj = New-Object PSObject
$obj | Add-Member Noteproperty -Name "Name" -Value $Name
$obj | Add-Member Noteproperty -Name "PPid" -Value $Ppid
$obj | Add-Member Noteproperty -Name "Pri" -Value $Pri
}
Taking Doug Finke's suggestion, which is pleasingly terse, here is my slightly adapted version, which works well with pipeline values.
function get-remoteproc {
param ($hostname=$env:COMPUTERNAME)
$results = PsList "\\$hostname"
foreach($record in $results[3..($results.Count)]) {
# Remove spaces
while ($record.IndexOf(" ") -ge 0) {
$record = $record -replace " "," "
}
$Name,$Processid,$Pri,$Thd,$Hnd,$Priv,$CPUTime,$ElapsedTime = $record.Split(" ")
$properties = #{
Name = $Name
Pid = $Processid
Pri = $Pri
Thd = $Thd
Hnd = $Hnd
Priv = $Priv
CPUTime = $CPUTime
ElapsedTime = $ElapsedTime
}
New-Object PSObject -Property $properties |
Add-Member -PassThru ScriptProperty CPUH {[int]$this.CPUTime.Split(":")[0]} |
Add-Member -PassThru ScriptProperty CPUM {[int]$this.CPUTime.Split(":")[1]} |
Add-Member -PassThru ScriptProperty CPUS {[int]$this.CPUTime.Split(":")[2]} |
Add-Member -PassThru ScriptProperty ElaH {[int]$this.ElapsedTime.Split(":")[0]} |
Add-Member -PassThru ScriptProperty ElaM {[int]$this.ElapsedTime.Split(":")[1]} |
Add-Member -PassThru ScriptProperty ElaS {[int]$this.ElapsedTime.Split(":")[2]}
}
}
And a call to the function, which shows that the objects are unrolled correctly for consumption by the pipeline:
get-remoteproc "Server1" | where {(($_.CPUM * $_.CPUS) -gt 60) -and ($_.name -notmatch "Idle" )}|
ft name, pid, pri, thd, hnd, priv, CPUH, cpuM, cpuS, ElaH, ElaM, ElaS -auto
Thanks, everyone!
As empo points out, you need to emit the $obj back to the pipeline. Here is another way to work the plist text into PowerShell objects.
function get-remoteproc {
param ($hostname=$env:COMPUTERNAME)
$results = PsList "\\$hostname"
foreach($record in $results[3..($results.Count)]) {
# Remove spaces
while ($record.IndexOf(" ") -ge 0) {
$record = $record -replace " "," "
}
$Name,$Processid,$Pri,$Thd,$Hnd,$Priv,$CPUTime,$ElapsedTime = $record.Split(" ")
$properties = #{
Name = $Name
Pid = $Processid
Pri = $Pri
Thd = $Thd
Hnd = $Hnd
Priv = $Priv
CPUTime = $CPUTime
ElapsedTime = $ElapsedTime
}
New-Object PSObject -Property $properties |
Add-Member -PassThru ScriptProperty CPUHours {$this.CPUTime.Split(":")[0]} |
Add-Member -PassThru ScriptProperty CPUMinutes {$this.CPUTime.Split(":")[1]} |
Add-Member -PassThru ScriptProperty CPUSeconds {$this.CPUTime.Split(":")[2]}
}
}
To return an array of values passed to the pipeline one by one, you just need to return multiple values in your function. For example:
function get-remoteproc {
param ($hostname)
$results = pslist "\\$hostname"
for ($i= 3; $i -le $results.length; $i++) {
# your staff to $obj
# output $obj
$obj
}
}
The function result is an array of $obj. When connected to a pipeline the array will be enrolled, and all the values will be passed to the stream one by one.