Change variable specific text with powershell and create a table - powershell

I'm trying to change some words inside a variable that contains a table.
I was able to do it using a foreach with -replace but then I can't form again the table.
1st try
$vms = Get-AZVm -status | Select-Object -Property Name, ResourceGroupName , PowerState
$tabla = foreach ($item in $vms) {
$item.Name
$item.ResourceGroupName
$item.PowerState -replace 'VM Deallocated','Apagada' -replace 'VM running','Encendida'
} | Format-Table
2nd try
$vms |foreach{$_.PowerState -replace 'VM Deallocated','Apagada' -replace 'VM running','Encendida'} | Select-Object -Property Name, ResourceGroupName , PowerState | format-table -autosize
I expect after the replacement of text inside the table to leave the same as a table and not text

I cannot test this myself, but I think using a calculated property would be easiest:
$vms = Get-AzVM -status |
Select-Object -Property Name, ResourceGroupName,
#{Name = 'PowerState'; Expression = {($_.PowerState -replace 'VM Deallocated','Apagada') -replace 'VM running','Encendida'}}
$vms | Format-Table -AutoSize

I would create my table inside an array and set its content...
$vms = Get-AZVm -status | Select-Object -Property Name, ResourceGroupName , PowerState
$tabla = #{
"Name" = ""
"ResourceGroupName" = ""
"PowerState" = ""
}
foreach ($item in $vms) {
$record."Name" = $item.Name
$record."ResourceGroupName" = $item.ResourceGroupName
$record."PowerState" = $item.PowerState -replace 'VM Deallocated','Apagada' -replace 'VM running','Encendida'
$objRecord = New-Object PSObject -Property $record
$tabla += $objRecord
}
$tabla

Related

Is there any Powershell script or how can i modify this script to import multiple ips as a csv file if a vm has multiple ip addresses?

Is there any Powershell script or how can i modify this script to import multiple ips as a csv file if a vm has multiple ip addresses ?
This is my existing script
# Create Report Array
$report = #()
# Get all the VMs from the selected subscription
$vms = Get-AzVM
# Get all the Network Interfaces
$nics = Get-AzNetworkInterface | Where-Object { $_.VirtualMachine -NE $null }
foreach ($nic in $nics) {
$ReportDetails = "" | Select-Object ip-address, vm_name, interface_name
$vm = $vms | Where-Object -Property Id -eq $nic.VirtualMachine.id
$ReportDetails.vm_name = $vm.Name
$ReportDetails.ip-address = [String]$nic.IpConfigurations.PrivateIpAddress
$ReportDetails.interface_name = $nic.Name
$report += $ReportDetails
}
$report | Sort-Object VMName | Format-Table ip-address, vm_name, interface_name
$report | Export-Csv -NoTypeInformation -Path $reportFile
}
As a general recommendation, you should try to avoid using the increase assignment operator (+=) to create a collection, besides $null` should be on the left side of the equality comparison.
For the concerned script and expanding the ip_address property this would mean:
# Get all the VMs from the selected subscription
$vms = Get-AzVM
# Get all the Network Interfaces
$nics = Get-AzNetworkInterface | Where-Object { $Null -ne $_.VirtualMachine }
$ReportDetails = foreach ($nic in $nics) {
$vm = $vms | Where-Object -Property Id -eq $nic.VirtualMachine.id
$nic.IpConfigurations.PrivateIpAddress.foreach{
[PSCustomObject]#{
vm_name = $vm.Name
ip_address = $_
interface_name = $nic.Name
}
}
}
csv is not designed to support properties with multivalues (e.g. array). you could use json instead:
$report | convertto-json | set-content -path $reportFile
Or if it has to be a csv you can flattern the structure or join the array to a delimited string, e.g.
$ReportDetails.ip-address = ($nic.IpConfigurations.PrivateIpAddress -join "|")

Powershell - Export-CSV outside loop only last line is printed/exported

Is it possible to adjust this code to export all lines outside foreach loop:
This works fine (inside loop):
$vms = Get-VM | Where { $_.State –eq ‘Running’ } | Select-Object -ExpandProperty Name
foreach($vm in $vms) {
# Get network interface details
$out = Get-VMNetworkAdapter -vmname $vm | select VMName, MacAddress, IPAddresses
$vm_name = $out.VMName | Get-Unique
$ip = ($out.IPAddresses | ForEach-Object {
$_ | ? {$_ -notmatch ':'}
}) -join " "
# If more than 1 MAC , put it in same row separated by space (00:15:5D:58:12:5E 00:15:5D:58:12:5F )
$mac = ($out.MacAddress | ForEach-Object {
$_.Insert(2,":").Insert(5,":").Insert(8,":").Insert(11,":").Insert(14,":")
}) -join ' '
$results = #()
$comp = Get-WmiObject Win32_ComputerSystem | Select-Object -ExpandProperty name
$obj = New-Object -TypeName psobject
$obj | Add-Member -MemberType NoteProperty -Name "VM NAME" -Value $vm_name
$obj | Add-Member -MemberType NoteProperty -Name "IP ADDRESS" -Value $ip
$obj | Add-Member -MemberType NoteProperty -Name "MAC ADDRESS" -Value $mac
$obj | Add-Member -MemberType NoteProperty -Name "HYPER-V HOST" -Value $comp
$results += $obj
Write-Output $results
$results| Export-Csv -Path "c:\1.csv" -NoTypeInformation -append
}
However, when i move $results| Export-Csv -Path "c:\1.csv" -NoTypeInformation -append outside loop,
only one (last) line is saved to CSV
Inside loop, $results variable contains all lines, when i move this variable outside loop write-host $results only one (last) line is printed
For what it's worth, your code can be condensed quite a bit. Many of your steps are not necessary:
$results = Get-VM | Where State –eq Running | Get-VMNetworkAdapter | ForEach-Object {
[pscustomobject]#{
'VM NAME' = $_.VMName
'IP ADDRESS' = ($_.IPAddresses -notmatch ':') -join ' '
'MAC ADDRESS' = ($_.MacAddress -replace '(..)(..)(..)(..)(..)','$1:$2:$3:$4:$5:') -join ' '
'HYPER-V HOST' = $env:COMPUTERNAME
}
}
$results | Export-Csv -Path "c:\1.csv" -NoTypeInformation
Notes:
You can pipe the VMs that Get-VM returns directly into Get-VMNetworkAdapter
If you filter on a single property you don't need a script block for Where-Object. Where State -eq Running is a bit easier to write and read than Where { $_.State -eq 'Running' }.
$_.IPAddresses -notmatch ':' Operators like -notmatch work on arrays. 'a','b','0','c' -notmatch '\d' will return 'a','b','c'.
The same goes for -replace. 'a0','b1','c2' -replace '\d','' will return return 'a','b','c'. No foreach loops necessary at all.
$env:COMPUTERNAME should be faster than using WMI to get the computer name
Any object you create in a script block (like the ForEach-Object {...} script block) that you do not assign to a variable will be in the script block's output. This is why $results = ... | ForEach-Object {...} works. There is no need to explicitly create arrays with #() and add values to them.
Casting a hash table to [pscustomobject] is much easier than using Add-Member.
Figured it out:
moved $results variable outside loop (make it "global")
$vms = Get-VM | Where { $_.State –eq ‘Running’ } | Select-Object -ExpandProperty Name
$results = #()
foreach($vm in $vms) {
# Get network interface details
$out = Get-VMNetworkAdapter -vmname $vm | select VMName, MacAddress, IPAddresses
# Remove duplicate VM names
$vm_name = $out.VMName | Get-Unique
# In case more than 1 IP, put it in same row separated by space (192.168.1.1, 192.168.1.2)
$ip = ($out.IPAddresses | ForEach-Object {
$_ | ? {$_ -notmatch ':'}
}) -join " "
# If more than 1 MAC , put it in same row separated by space (00:15:5D:58:12:5E 00:15:5D:58:12:5F )
$mac = ($out.MacAddress | ForEach-Object {
$_.Insert(2,":").Insert(5,":").Insert(8,":").Insert(11,":").Insert(14,":")
}) -join ' '
$comp = Get-WmiObject Win32_ComputerSystem | Select-Object -ExpandProperty name
$obj = New-Object -TypeName psobject
$obj | Add-Member -MemberType NoteProperty -Name "VM NAME" -Value $vm_name
$obj | Add-Member -MemberType NoteProperty -Name "IP ADDRESS" -Value $ip
$obj | Add-Member -MemberType NoteProperty -Name "MAC ADDRESS" -Value $mac
$obj | Add-Member -MemberType NoteProperty -Name "HYPER-V HOST" -Value $comp
$results += $obj
}
$results| Export-Csv -Path "c:\1.csv" -NoTypeInformation

Transpose CSV Row to Column based on Column Value

I am trying to convert a 80K row data csv with 30 columns to sorted and filtered CSV based on specific column data from orignal CSV.
For Example My Data is in below format:
PatchName MachineName IPAddress DefaultIPGateway Domain Name USERID UNKNOWN NOTAPPLICABLE INSTALLED APPLICABLE REBOOTREQUIRED FAILED
KB456982 XXX1002 xx.yy.65.148 xx.yy.64.1 XYZ.NET XYZ\ayzuser YES
KB589631 XXX1003 xx.yy.65.176 xx.yy.64.1 XYZ.NET XYZ\cdfuser YES
KB456982 ABC1004 xx.zz.83.56 xx.zz.83.1 XYZ.NET XYZ\mnguser YES
KB456982 8797XCV xx.yy.143.187 xx.yy.143.184 XYZ.NET WPX\abcuser YES
Here MachineName would be filtered to Uniq and PatchName would transpose to Last Columns headers with holding "UNKNOWN, NOAPPLICABLE, INSTALLED, FAILED, REBOOTREQUIRED columns Values if YES occurred -
Expected Result:
MachineName IPAddress DefaultIPGateway Domain Name USERID KB456982 KB589631
XXX1002 xx.yy.65.148 xx.yy.64.1 XYZ.NET XYZ\ayzuser UNKNOWN
XXX1003 xx.yy.65.176 xx.yy.64.1 XYZ.NET XYZ\cdfuser NOTAPPLICATBLE
ABC1004 xx.zz.83.56 xx.zz.83.1 XYZ.NET XYZ\mnguser UNKNOWN
8797XCV xx.yy.143.187 xx.yy.143.184 XYZ.NET WPX\abcuser FAILED
Looking for help to achieve this, so far I am able to transpose PathcName rows to columns but not able to include all the columns along with and apply the condition. [It takes 40 Minutes to process this]
$b = #()
foreach ($Property in $a.MachineName | Select -Unique) {
$Props = [ordered]#{ MachineName = $Property }
foreach ($Server in $a.PatchName | Select -Unique){
$Value = ($a.where({ $_.PatchName -eq $Server -and $_.MachineName -eq $Property })).NOTAPPLICABALE
$Props += #{ $Server = $Value }
}
$b += New-Object -TypeName PSObject -Property $Props
}
This is what I came up with:
$data = Import-Csv -LiteralPath 'C:\path\to\data.csv'
$lookup = #{}
$allPatches = $data.PatchName | Select-Object -Unique
# Make 1 lookup entry for each computer, to keep the username and IP and so on.
# Add the patch details from the current row (might add more than one patch per computer)
foreach ($row in $data)
{
if (-not $lookup.ContainsKey($row.MachineName))
{
$lookup[$row.MachineName] = ($row | Select-Object -Property MachineName, IPAddress, DefaultIPGateway, DomainName, UserID)
}
$patchStatus = $row.psobject.properties |
Where-Object {
$_.name -in #('applicable', 'notapplicable', 'installed', 'rebootrequired', 'failed', 'unknown') -and
-not [string]::IsNullOrWhiteSpace($_.value)
} |
Select-Object -ExpandProperty Name
$lookup[$row.MachineName] | Add-Member -NotePropertyName $row.PatchName -NotePropertyValue $patchStatus
}
# Pull the computer details out of the lookup, and add all the remaining patches
# so they will convert to CSV properly, then export to CSV
$lookup.Values | ForEach-Object {
$computer = $_
foreach ($patch in $allPatches | where-object {$_ -notin $computer.psobject.properties.name})
{
$computer | Add-Member -NotePropertyName $patch -NotePropertyValue ''
}
$computer
} | Export-Csv -LiteralPath 'c:\path\to\output.csv' -NoTypeInformation

Reading txt-file, change rows to columns, save txt file

I have a txt files (semicolon separated) containing over 3 million records where columns 1 to 4 have some general information. Columns 5 and 6 have detailed information. There can be up to 4 different detailed information for the same general information in columns 1 to 4.
My sample input:
Server;Owner;Company;Username;Property;Value
Srv1;Dave;Sandbox;kwus91;Memory;4GB
Srv1;Dave;Sandbox;kwus91;Processes;135
Srv1;Dave;Sandbox;kwus91;Storage;120GB
Srv1;Dave;Sandbox;kwus91;Variant;16
Srv2;Pete;GWZ;aiwq71;Memory;8GB
Srv2;Pete;GWZ;aiwq71;Processes;234
Srv3;Micael;P12;mxuq01;Memory;16GB
Srv3;Micael;P12;mxuq01;Processes;239
Srv3;Micael;P12;mxuq01;Storage;160GB
Srv4;Stefan;MTC;spq61ep;Storage;120GB
Desired output:
Server;Owner;Company;Username;Memory;Processes;Storage;Variant
Srv1;Dave;Sandbox;kwus91;4GB;135;120GB;16
Srv2;Pete;GWZ;aiwq71;8GB;234;;
Srv3;Micael;P12;mxuq01;16GB;239;160GB;
Srv4;Stefan;MTC;spq61ep;;;120GB;
If a values doesn't exist for general information (Columns 1-4) it has to stay blank.
My current code:
$a = Import-csv .\Input.txt -Delimiter ";"
$a | FT -AutoSize
$b = #()
foreach ($Server in $a.Server | Select -Unique) {
$Props = [ordered]#{ Server = $Server }
$Owner = ($a.where({ $_.Server -eq $Server})).Owner | Select -Unique
$Company = ($a.where({ $_.Server -eq $Server})).Company | Select -Unique
$Username = ($a.where({ $_.Server -eq $Server})).Username | Select -Unique
$Props += #{Owner = $Owner}
$Props += #{Company = $Company}
$Props += #{Username = $Username}
foreach ($Property in $a.Property | Select -Unique){
$Value = ($a.where({ $_.Server -eq $Server -and
$_.Property -eq $Property})).Value
$Props += #{ $Property = $Value }
}
$b += New-Object -TypeName PSObject -Property $Props
}
$b | FT -AutoSize
$b | Export-Csv .\Output.txt -NoTypeInformation -Delimiter ";"
After a lot of trying and getting errors: My script works.
But it takes a lot of time.
Is there a possibility to make performance better for around 3 Million lines in txt file? I'm calculating with more or less 2.5 Million unique values for $Server.
I'm running Windows 7 64bit with PowerShell 4.0.
try Something like this:
#Import Data and create empty columns
$List=import-csv "C:\temp\file.csv" -Delimiter ";"
#get all properties name with value not empty
$ListProperty=($List | where Value -ne '' | select property -Unique).Property
#group by server
$Groups=$List | group Server
#loop every rows and store data by group and Property Name
$List | %{
$Current=$_
#Take value not empty and group by Property Name
$Group=($Groups | where Name -eq $Current.Server).Group | where Value -ne '' | group Property
#Add all property and first value not empty
$ListProperty | %{
$PropertyName=$_
$PropertyValue=($Group | where Name -eq $PropertyName | select -first 1).Group.Value
$Current | Add-Member -Name $PropertyName -MemberType NoteProperty -Value $PropertyValue
}
$Current
} | select * -ExcludeProperty Property, Value -unique | export-csv "c:\temp\result.csv" -notype -Delimiter ";"

How to format piped output in powershell to a line

I have a script powershell to get PercentProcessorTime of processes.
$Processes = (get-wmiobject Win32_PerfFormattedData_PerfProc_Process)
$Processes | %{ New-Object psobject -Property `
#{ Time = $_.PercentProcessorTime;
Name = ($_.name -replace "#\d+", "" )}}`
| ?{ $_.Name -notmatch "_Total|Idle" } `
| Group-Object Name `
| %{ New-Object psobject -Property `
#{ Name = $_.Name;
Sum = ($_.Group | Measure-Object Time -Sum ).Sum }} `
| Format-Table
Format of result as below:
But I want to format result as below to insert to database:
OK |Idle=100 System=6 smss=0 csrss=0 wininit=0 services=0 lsass=0 lsm=0 svchost=18 ICTrigger=0 nvvsvc=0 ICDCMGR64=0 svchost#2=0 winlogon=0 svchost#3=0 svchost#4=0 svchost#5=0 igfxCUIService=0 svchost#6=0 spoolsv=0 svchost#7=0 armsvc=0 ShieldStart=0 svchost#8=0 GateMan=0 HeciServer=0 Jhi_service=0 NHCAAgent=0 nvxdsync=0 nvvsvc#1=0 nscp=0 PaSvc=0 pcdrmsvc=0 NSCHIM=0 ASDSvc=0 secugate64=0 WindowsSecuService=0 WinFil32=0 WmiPrvSE=0 svchost#9=0 papersrv64=0 WUDFHost=0 svchost#10=0 svchost#11=0 WmiP rvSE#1=0 PaTray=0 unsecapp=0 WmiPrvSE#2=0 LMS=0 SWMAgent=0
Please help me to solve it.
Thanks very much ^^
I don't think there is an "easy", builtin command for it, but it doesn't take much to update the function to do what you want:
$Processes = (get-wmiobject Win32_PerfFormattedData_PerfProc_Process)
$procStrings = #()
$Processes | %{ New-Object psobject -Property `
#{ Time = $_.PercentProcessorTime;
Name = ($_.name -replace "#\d+", "" )}}`
| ?{ $_.Name -notmatch "_Total|Idle" } `
| Group-Object Name `
| %{
$procStrings += "$($_.Name)=$(($_.Group | Measure-Object Time -Sum ).Sum)"}
$procStrings -join " "
Basically, I added an array to hold the string key/value paris and replaced the final New-Object with code to create the desired string. Finally it is joined with spaces to create the final string.
You could absolutely skip the array part and just build up the string directly, instead of creating an array, I just like that approach :)
It doesn't include the "OK |" in the beginning, i don't know what you constitutes an OK, but I'm sure you can add yourself to get what you want :)