Updating PSObject hash table in loop - powershell

Here is a part code of uploading data by FTP
foreach ($line in $FTPServer)
{
Start-Transcript -Path $results
Write-Host -Object "ftp url: $line"
If (Test-Connection $line -Count 1)
{
Set-FTPConnection -Credentials $FTPCredential -Server $line -Session MySession -UsePassive -ErrorAction SilentlyContinue
$Session = Get-FTPConnection -Session MySession
if($session.UsePassive -eq "True"){$connect="OK"}
else{$connect="!!!-FAIL-!!!"}
foreach ($item in (Get-ChildItem .\Upload))
{
Write-Host -Object "Uploading $item..."
$Send= Add-FTPItem -Session $Session -Path $FTPPlace -LocalPath .\Upload\$item -Overwrite -ErrorAction SilentlyContinue
if($Send.Name -eq $item.Name){$Rec="OK"}
else{$Rec="!!!-FAIL-!!!"}
$array = $line, $item, $connect, $Rec
$FailTable=New-Object -TypeName PSObject -Property ([ordered]#{"FTP Server"=$array[0]; "File"=$array[1];"Connected"=$array[2];"Uploaded"=$array[3]})
$FailTable|Out-File -Append '.\stats.txt'
}
Stop-Transcript
} Else {"$line">> .\DownServers.txt}
}
$Failtable is a hash-table that stores ip of FTP server ($line), name of uploaded file ($item), status if connected ($connect) and upload status ($Rec). The hash-table is piped to file .\stats.txt The problem is in every iteration to .\stats.txt are saved headers like that:
FTP Server File Connected Uploaded
---------- ---- --------- --------
192.168.1.1 ConfigurationDivide.xml OK !!!-FAIL-!!!
FTP Server File Connected Uploaded
---------- ---- --------- --------
192.168.1.1 test.txt OK !!!-FAIL-!!!
So I need that to be one under another and autosized like that:
FTP Server File Connected Uploaded
---------- ---- --------- --------
192.168.1.1 ConfigurationDivide.xml OK !!!-FAIL-!!!
192.168.1.1 test.txt OK !!!-FAIL-!!!
I tryed to put hash-table declaration $FailTable=New-Object -TypeName PSObject -Property ([ordered]#{"FTP Server"=$array[0]; "File"=$array[1];"Connected"=$array[2];"Uploaded"=$array[3]}) before loops and then- in the loops add values ($line, $item,...), but there aren't in hashtable .add method.

Ok, different answer :)
Try this:
$FailTable = #()
$spam = New-Object PSObject
$spam | Add-Member -type NoteProperty -Name 'FTP Server' -Value ""
$spam | Add-Member -type NoteProperty -Name 'File' -Value ""
$spam | Add-Member -type NoteProperty -Name 'Connected' -Value ""
$spam | Add-Member -type NoteProperty -Name 'Uploaded' -Value ""
$FailTable += $spam
$FailTable | Out-File -Append '.\stats.txt'
foreach ($line in $FTPServer)
{
Start-Transcript -Path $results
Write-Host -Object "ftp url: $line"
If (Test-Connection $line -Count 1)
{
Set-FTPConnection -Credentials $FTPCredential -Server $line -Session MySession -UsePassive -ErrorAction SilentlyContinue
$Session = Get-FTPConnection -Session MySession
if($session.UsePassive -eq "True"){$connect="OK"}
else{$connect="!!!-FAIL-!!!"}
foreach ($item in (Get-ChildItem .\Upload))
{
Write-Host -Object "Uploading $item..."
$Send= Add-FTPItem -Session $Session -Path $FTPPlace -LocalPath .\Upload\$item -Overwrite -ErrorAction SilentlyContinue
if($Send.Name -eq $item.Name){$Rec="OK"}
else{$Rec="!!!-FAIL-!!!"}
$spam = New-Object PSObject
$spam | Add-Member -type NoteProperty -Name 'FTP Server' -Value $line
$spam | Add-Member -type NoteProperty -Name 'File' -Value $item
$spam | Add-Member -type NoteProperty -Name 'Connected' -Value $connect
$spam | Add-Member -type NoteProperty -Name 'Uploaded' -Value $Rec
$FailTable += $spam
$FailTable | Select-Object -Last 1 | Format-Table -HideTableHeaders | Out-File -Append '.\stats.txt'
}
Stop-Transcript
} Else {"$line">> .\DownServers.txt}
}

You need to have FailTable created outside the loop, you are creating and appending an entire new PSCustomObject each loop to the file.
try implementing this:
$FailTable = #()
#Example loop
for($i =0;$i -lt 5; $i++){
#Inside the loop add new object to the array
$failTable += #{"FTPServer"=$i;"File"=$i;"Connected"=$i;"Uploaded"=$i}
}
#foreach hashmap in the array cast to a PSCustomObject (which gives you the headers you want)
#and then Select-Object The order you want them in.
$FailTable.foreach{[PSCustomObject]$_} | Select-Object "FTPServer", "File","Connected", "Uploaded" | Out-file test.txt
Edit:
PowerShell v2+ compatible version:
$FailTable = #()
$objTable = #()
#Example loop
for($i =0;$i -lt 5; $i++){
#Inside the loop add new object to the array
$failTable += #{"FTPServer"=$i;"File"=$i;"Connected"=$i;"Uploaded"=$i}
}
#foreach hashmap in the array cast to a PSCustomObject (which gives you the headers you want)
#and then Select-Object The order you want them in.
foreach($fail in $FailTable){
$objTable += New-Object -TypeName PSCustomObject -Property $fail
}
$objTable | Select-Object "FTPServer", "File","Connected", "Uploaded" | Out-file test.txt

Related

How can I convert this where I only get the information I need?

I have a PowerShell script that pulls all the OS versions of Azure cloud services and writes it to a CSV file. It does what I want, but I just want the WA-Guest piece without the #{}. Would I need to convert it into a string from a PowerShell object first then write it into my custom object?
Edit for Theo:
function CreateCustomObject{
Param(
[string]$Subscription,
[string]$cloudServiceName,
[string]$osVersion
)
#$instanceObj = $null
$instanceObj = New-Object -TypeName PSObject
$instanceObj | Add-Member -Name "DateOfReport" -MemberType Noteproperty -Value $date
$instanceObj | Add-Member -Name "Subscription" -MemberType Noteproperty -Value $Subscription
$instanceObj | Add-Member -Name "CloudServiceName" -MemberType Noteproperty -Value $cloudServiceName
$instanceObj | Add-Member -Name "OSVersion" -MemberType Noteproperty -Value $osVersion
return $instanceObj
}
##############################################################################
Add-AzureAccount -Credential $Credential
# Gets an array of the subscription names
$subs = (Get-AzureSubscription)
$data = #()
foreach ($sub in $subs) {
Select-AzureSubscription -Name $sub.SubscriptionName
$services = Get-AzureService -ErrorAction Silentlycontinue
foreach ($service in $services){
$VMs = Get-AzureVM -ServiceName $service.ServiceName -ErrorAction SilentlyContinue
if($null -eq $VMs)
{
$deployment = Get-AzureDeployment -ServiceName $service.ServiceName -Slot "Production" -ErrorAction SilentlyContinue -Verbose
if($deployment -ne $null) {
$osVersionCmdlet = Get-AzureRole -ServiceName $service.ServiceName | select OSVersion
$data += CreateCustomObject -Subscription $sub.SubscriptionName -cloudServiceName $service.ServiceName -osVersion $osVersionCmdlet
}
}
}
}
######################################################################
$timestamp = (Get-Date).ToString("yyyy-MM-ddTHH-mm-ss")
$date= (Get-Date).ToString("MM-dd-yy")
$newPath = New-Item -ItemType Directory -Path "C:\mypath\$date" -Force
$localfile = "$newPath\OSFamily_$timestamp.csv"
$data | Sort Subscription | Export-Csv -Path $localfile -NoTypeInformation
Just added -ExpandProperty to where the $OsVersionCmdlet line is and got what I needed. Thanks JosefZ!

How do I include on certain values when summing from a powershell array?

I want to get a sum for the total space a SQL server is using for Data and Log files.
From a few other sources on the internet I have the following code: (Yes, I'm a Powershell Noob)
$servers = "SQLSERVER1"
$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 "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 "Used(GB)" -Value $sused
$array += $obj
}
}
$array += write-output " "
$totalSize = ($array | Measure-Object 'Used(GB)' -Sum).Sum
$array += $totalsize
$array += write-output " "
}
$totalsize
This gives me the result of:
Processing Recovery from SQL-Group1-DB
Processing System from SQL-Group1-DB
Processing SQLInstall from SQL-Group1-DB
Processing OCTOPUS from SQL-Group1-DB
Processing SQL_DATA from SQL-Group1-DB
Processing SQL_LOG from SQL-Group1-DB
Processing TEMP_DB from SQL-Group1-DB
Processing SSS_X64FREV_EN-US_DV9 from SQL-Group1-DB
274.92
Of course that has included EVERY drive on the server.
I only want the SQL_DATA and SQL_LOG drives included.
Any ideas on how to achieve this?
(Happy to use entirely different code if it works)
TIA
If you do not want the info for all drives on the server, you could limit the results of the Get-WmiObject cmdlet in the $sysinfo variable by using a Where-Object{} clause like:
$sysinfo = Get-WmiObject Win32_Volume -ComputerName $server |
Where-Object { 'SQL_DATA', 'SQL_LOG' -contains $_.Label }

powershell script to return all forwarding rules in org

I need to pull all forwarding rules for an exchange online environment, and output them to a csv. this sounds simple, but I have an additional caveat. there are 23,000 mailboxes in the org.
I was able to write the script I needed, it outputted the data, but it timed out.
then I was able to break out only certain mailboxes that were critical (11,000) but I was still timing out in powershell.
so finally, I found an article that detailed breaking up a script into blocks of 1,000, and running numerous sessions. and runs! it runs without timing out.
but it doesn't output to the csv anymore.
since my script has gone through several iterations, I'm pretty sure that my problem is the way I'm storing, or outputting the array, but for all my staring at this, I cant figure it out. short of asking the doc for a prescription of Adderall, I figured id ask here. below is the offending script.
the aliaslist.csv that it mentions is just a csv with a list of aliases for 11,000 mailboxes. if you would like to run your own tests, you can adjust $pagesize down and paste a few mailboxes into a csv called aliaslist, stored in c:\temp
Function New-O365ExchangeSession()
{
param(
[parameter(mandatory=$true)]
$365master)
#close any old remote session
Get-PSSession | Remove-PSSession -Confirm:$false
#start a new office 365 remote session
$365session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $365master -Authentication Basic -AllowRedirection
$office365 = Import-PSSession $365session
}
#set input variables
$path = "C:\temp"
$InputFile = aliaslist.csv"
$UserEmail = "admin#domain.com"
#set variables for csv usage
$Offset = 0;
$PageSize = 1000;
$MbxMax = (Import-Csv "$path/$InputFile").count
#Loop in the list and retrieve the device’s information
$file = “c:\temp\office365-$((get-date).tostring(“yyyy-MM-dd”)).csv”
$365master = get-credential $UserEmail
New-O365ExchangeSession $365master
# call the office365 remote connection function
do{
$mbxlist=#(import-csv "$path/$InputFile"|select-object -skip $Offset -First $PageSize)
"Process entry $($Offset) to $($Offset+$PageSize)"
#end csv input count reference
ForEach($mbx in $MbxList)
{
#Write to Host
"start Processing $($mbx.alias)"
#end Write to host,
#Check rules
$rules = Get-InboxRule -mailbox $_.alias | ? {$_.RedirectTo -ne $null -or $_.ForwardTo -ne $null -or $_.ForwardAsAttachmentTo -ne $null}
If ($rules -ne $null)
{
$rules | % {
#check for forwardAsAttachments
If ($_.ForwardAsAttachmentTo -ne $null)
{
$obj = New-Object system.object
$obj | Add-Member -name "NetID" -Value $_.alias -MemberType NoteProperty
$obj | Add-Member -name "ForwardType" -Value "Forward As Attachment Rule" -MemberType NoteProperty
$obj | Add-Member -name "ForwardAddress" -Value $_.forwardAsAttachmentTo -MemberType NoteProperty
$obj | Add-Member -name "Enabled" -Value $_.Enabled -MemberType NoteProperty
$obj | Add-Member -name "Description" -Value $f -MemberType NoteProperty
If (Test-Path $file)
{
$mbx.alias + ”,” + ($obj | ConvertTo-Csv)[2] | Out-File $file –Append
}
Else
{
$obj | Export-Csv $file -Encoding ASCII -notypeinformation
}
}
$obj = $null
#check for redirects
If ($_.redirectto -ne $null)
{
$obj = New-Object system.object
$obj | Add-Member -name "NetID" -Value $_.alias -MemberType NoteProperty
$obj | Add-Member -name "ForwardType" -Value "Redirct Rule" -MemberType NoteProperty
$obj | Add-Member -name "ForwardAddress" -Value $_.redirectto -MemberType NoteProperty
$obj | Add-Member -name "Enabled" -Value $_.Enabled -MemberType NoteProperty
$obj | Add-Member -name "Description" -Value $c -MemberType NoteProperty
If (Test-Path $file)
{
$mbx.alias + ”,” + ($obj | ConvertTo-Csv)[2] | Out-File $file –Append
}
Else
{
$obj | Export-Csv $file -Encoding ASCII -notypeinformation
}
}
$obj = $null
#check for forwards
If ($_.ForwardTo -ne $null)
{
$obj = New-Object system.object
$obj | Add-Member -name "NetID" -Value $_.alias -MemberType NoteProperty
$obj | Add-Member -name "ForwardType" -Value "Forward Rule" -MemberType NoteProperty
$obj | Add-Member -name "ForwardAddress" -Value $_.forwardto -MemberType NoteProperty
$obj | Add-Member -name "Enabled" -Value $_.Enabled -MemberType NoteProperty
$obj | Add-Member -name "Description" -Value $f -MemberType NoteProperty
If (Test-Path $file)
{
($obj | ConvertTo-Csv)[2] | Out-File $file –Append
}
Else
{
$obj | Export-Csv $file -Encoding ASCII -notypeinformation
}
}
$obj = $null
}
}
}
#increment the start point for the next chunk
$Offset+=$PageSize
#Call the office365 remote session function to close the current one and open a new session
New-O365ExchangeSession $365master
} while($Offset -lt $MbxMax)

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,""

How can i get the disk details in csv format in windows server using powershell

Hi all I want to get details in below format
Hostname Drive_0 Drive_1 Drive_2
Name C: 99.899227142334 d: 99.899227142334 e: 99.899227142334
I can get this detail using below script but this works on PowerShell 3.0
how can I change to execute on PowerShell 2.0
$result = #()
$obj = new-object PSobject
$server = hostname
$obj |Add-Member -MemberType NoteProperty -Name "Hostname" -Value $server -ErrorAction SilentlyContinue
$z = Get-WmiObject -Class win32_Logicaldisk -Filter 'DriveType=3' | Select-Object -Property DeviceID, #{LABEL='TotalSize';EXPRESSION={$_.Size/1GB}}
$z3 = $z.DeviceID
$z4 = $z.TotalSize
$i = 0
foreach($z3 in $z3){
$z1 = $z.DeviceID[$i]
$z2 = $z.TotalSize[$i]
$zx = "$z1" + ": $z2"
$obj | Add-Member -MemberType NoteProperty -Name "Drive_$i" -Value $zx -ErrorAction SilentlyContinue
$i++
}
$result+=$obj
$result | Export-Csv "$env:userprofile\Desktop\Result.csv" -NoTypeInformation
You can change your code to the below. It's a bit neater and sorts out your loops and limits so you don't need to manage them
$result = #()
$obj = new-object PSobject
$server = hostname
$obj |Add-Member -MemberType NoteProperty -Name "Hostname" -Value $server -ErrorAction SilentlyContinue
$z = Get-WmiObject -Class win32_Logicaldisk -Filter 'DriveType=3' | Select-Object -Property DeviceID, #{LABEL='TotalSize';EXPRESSION={$_.Size/1GB}}
$i = 0
$z | % {
$z1 = $_.DeviceID
$z2 = $_.TotalSize
$zx = "$z1" + ": $z2"
$obj | Add-Member -MemberType NoteProperty -Name "Drive_$i" -Value $zx
$i++
}
$result+=$obj
$result | Export-Csv "$env:userprofile\Desktop\Result.csv" -NoTypeInformation
I also removed the -ErrorAction off the Add-Member as you should try and handle anything that crops up yourself, but add it back if you feel the need to.