Sorry for the wierd title, I didn't know how to phrase the question. I'm relatively new to Powershell and I'm writing a program. Basically, I have an array where the user has selected settings or wish to have "Selected" from the GWMI query stored in "$settings_array". I want to output the results to a CSV. When I try to run it, only the first Select statement gets output to the CSV. Output to the textbox works fine. I know it has something to do with how it's being stored in the array at each ieration. $resultList is intialized as an array ($resultList = #()). There are hundreds of lines of code for the Form and other functions, but here is the relevant code. Thanks for the help! Let me know if I need to post more of the code.
$colItems = GWMI Win32_NetworkAdapterConfiguration -computername $PCname -namespace "root\CimV2" -filter "IpEnabled = TRUE"
ForEach ($objItem in $colItems)
{
ForEach ($objItem2 in $settings_array)
{
$resultList += $objItem | Select $objItem2
$richTextBox1.Appendtext("$objItem2" + ": " + $objItem.$objItem2 + "`r`n")
$richtextbox1.ScrollToCaret()
}
}
$resultList | export-csv "$ScriptDir\Exports\$Outputfile"
CSV is made with rows and columns. Each object in the array you export gets a row, and each object gets a column value for each property. You add a new record/object to the resultlist with a single property every time(every object has only one property). The reason you only get the first is because your records contain different property-names. To solve this "non-static propertyname" problem, powershell takes the first object's properties as a template for the csv file. Since object2,object3 etc. doesn't include the same property, they will be blank. However, when you hit another object with the same property as the first object had, the value will be included too. Ex. you get the Name property for all network adapters, but blank values on the rest.
Your sample is missing information, ex. how $settings_array is built. If it's a normal string-array like:
$settings_array = "Name", "DisplayName", "Test"
or
$settings_array = #("Name", "DisplayName", "Test")
Then you can pass the whole array to select.
$colItems = GWMI Win32_NetworkAdapterConfiguration -computername $PCname -namespace "root\CimV2" -filter "IpEnabled = TRUE"
ForEach ($objItem in $colItems)
{
#Write to screen
ForEach ($objItem2 in $settings_array)
{
$richTextBox1.Appendtext("$objItem2" + ": " + $objItem.$objItem2 + "`r`n")
$richtextbox1.ScrollToCaret()
}
}
#Save to CSV
$colItems | Select-Object -Property $settings_array | export-csv "$ScriptDir\Exports\$Outputfile"
Notice the last line. Now, the foreach loop is only used for your textbox-content, while the last line formats the CSV as it should.
EDIT Try this to get your settings:
Function GetSettings {
$out = #()
Foreach ($objItem in $chklstGetMIPRet.CheckedItems) {
$out += $objItem.ToString()
}
$out
}
$settings_array = GetSettings
Just a simpe tip: you can export in XML with export-clixml and whatever the number of column, they are all added in the file.
Related
I'm still new with the powershell, there's a questions with my script, my intention is to import the CSV data under column Name
then do a split() for each object with "computername" and "username", however it can catch the arrays with two results, but after the loop in Foreach it only runs the last object. Can someone help Thanks!
whole code
$ImportPath ="C:\data.csv"
$ComputerArray= #()
Import-Csv -Path $ImportPath |ForEach-Object{$ComputerArray+= $_.Name}
Foreach($Hostname in $ComputerArray){
$CharArray =$Hostname.Split("\")
$ComputerName = $CharArray[0]
$Username = $CharArray[1]
}
CSV date looks like
CSV data
$ComputerArray results
----------------------
Computer1\Local User
Computer2\Remote User
Hopes to look like
$ComputerName results
---------------------
Computer1
Computer2
$Username results
---------------------
Local User
Remote User
It's not running the last object only, it's running all the objects but you're only capturing the last one. Both variables $computerName and $userName are getting re-assigned on each iteration.
Below will give you an array of objects with the Computer and User properties for each line of your CSV.
$ImportPath = "C:\data.csv"
$ComputerArray = (Import-Csv -Path $ImportPath).Name
$result = foreach($Hostname in $ComputerArray)
{
$ComputerName, $Username = $Hostname.Split("\")
[pscustomobject]#{
Computer = $ComputerName
User = $Username
}
}
$result | Format-Table
If you want to get the computers on one array and the users on other array like in your expected output you can do $result.Computer and $result.User.
I have been researching the web to see what am I missing and can't find out, I run the command it goes thru the list of computers but the export doc is always empty.
Here is the code
foreach ($computer in Get-Content "\\NETWORK PATH\user-computers.txt") {
Write-host $computer
$colDrives = Get-WmiObject Win32_MappedLogicalDisk -ComputerName $computer
$Report = #()
# Set our filename based on the execution time
$filenamestring = "$computer-$(get-date -UFormat "%y-%b-%a-%H%M").csv"
foreach ($objDrive in $colDrives) {
# For each mapped drive – build a hash containing information
$hash = #{
ComputerName = $computer
MappedLocation = $objDrive.ProviderName
DriveLetter = $objDrive.DeviceId
}
# Add the hash to a new object
$objDriveInfo = new-object PSObject -Property $hash
# Store our new object within the report array
$Report += $objDriveInfo
}}
# Export our report array to CSV and store as our dynamic file name
$Report | Export-Csv -LiteralPath "\\NETWORK PATH\Drive-Maps.csv" -NoTypeInformation
I want to know what each computer currently got mapped network drives, thanks for all your help and guidance.
I'm not sure why you're not getting output. I've rewritten your script for a few reasons I'd like to point out. First, your variable naming is not very clear. I'm guessing you come from a VBScripting background. Next, you're creating an array and then adding to it - this is simply not needed. You can capture the output of any loop/scriptblock/etc directly by assigning like tihs.
$Report = foreach($thing in $manythings){Do lots of stuff and everything in stdout will be captured}
If you write your script in a way that takes advantage of the pipeline, you can do even more. Next, creating the object with New-Object is slow compared to using the [PSCustomObject] type accelerator introduced in V3. Finally, it seems you create a custom csv for each computer but in the end you just export everything to one file. I'm going to assume you are wanting to collect all this info and put in one CSV.
My recommendation for you to help troubleshoot, run this against your machines and confirm the output on the screen. Whatever you see on the screen should be captured in the report variable. (Except write-host, it's special and just goes to the console)
$computerList = "\\NETWORK PATH\user-computers.txt"
$reportFile = "\\NETWORK PATH\Drive-Maps.csv"
Get-Content $computerList | ForEach-Object {
Write-host $_
$mappedDrives = Get-WmiObject Win32_MappedLogicalDisk -ComputerName $_
foreach ($drive in $mappedDrives)
{
# For each mapped drive – build a hash containing information
[PSCustomObject]#{
ComputerName = $_
MappedLocation = $drive.ProviderName
DriveLetter = $drive.DeviceId
}
}
} -OutVariable Report
Once you know you have all the correct info, run this to export it.
$Report | Export-Csv -LiteralPath $reportFile -NoTypeInformation
I am trying to get a list of servers and the last time they rebooted to show in a table. However, if it doesn't respond to a ping, I just need it to show in the list. I can't seem to figure out how to get it to add to the table after else.
Import-CSV $Downtime | % {
if(Test-Connection $_.server -Quiet -count 1){
Get-WmiObject Win32_OperatingSystem -ComputerName $_.server |
select #{LABEL="Name"; EXPRESSION = {$_.PSComputerName}}, #{LABEL="Last Bootup"; EXPRESSION = {$_.convertToDateTime($_.LastBootupTime)}}
}
else{#{LABEL="Name"; EXPRESSION = {$_.server}}
}
} | Out-GridView
I can always save the else results in a text file but this would be more convenient.
You need to make the same object, with the same properties!, in both cases so that PowerShell will understand the association between the two. The follwing example builds a custom hashtable using the if/else and outputs the object for each loop pass.
Import-CSV $Downtime | ForEach-Object {
$props = #{}
$server = $_.server
if(Test-Connection $server -Quiet -count 1){
$wmi= Get-WmiObject Win32_OperatingSystem -ComputerName $server
$props.Name = $wmi.PSComputerName
$props."Last Bootup" = $wmi.convertToDateTime($wmi.LastBootupTime)
}else{
$props.Name = $server
$props."Last Bootup" = "Could not contact"
}
New-Object -TypeName psobject -Property $props
} | Out-GridView
I used $server as the $_ changes context a couple of time so we wanted to be able to refer to the current row in the CSV we are processing.
I don't know what your PowerShell version is so I will assume 2.0 and create objects that support that.
In both cases an object is created with a Name and Last Bootup property which is populated based on the success of the ping.
As an aside I had a similar question a while ago about created similar object based output.
Using POSH 3.0, as a quick example, if I populate an array and try to display the results with FT -Autosize, I can't access the variable after that anymore. There are quite a few properties to display so -autosize is important to utilize the whole width.
$x | Select #{E={$_.AID};L="Action"},#{E={$_.ID};L="SSN"}... | `
FT -AutoSize
}
#Now the object is no longer accessible
$x | ForEach {
$_
}
So my goal is to view the results in a nicely formatted way, while also keeping the values accessible in the variable. I had tried to make a copy of the variable, one to view the results and the other to proceed with the values in the variable, but same thing.
Anyone have any good thoughts about this? Thanks!
Better example:
[array]$recs= Invoke-Sqlcmd -ServerInstance $server `
-Database $db `
-InputFile 'c:\sqlquery.sql'
$arrRecs = #()
ForEach ($record in $recs) {
$newObjectRecs = New-Object PSCustomObject -Property #{
"todaysDate" = $_.TodaysDate
"cats" = $record.cats
"dogs" = $record.dogs
"surname" = $record.surname
"givenName" = $record.givenName }
$arrRecs += $newObjectRecs
}
$arrRecs | `
Select #{E={$_.cats};L="cats"},#{E={$_.dogs};L="dogs"},#{E={$_.TodaysDate};L="Date"},surname,givenName | `
Format-Table -AutoSize
$arrRecs | ForEach {
$_
Write-Host "---------"
}
$arrRecs
As an update to a comment earlier, if I iterate through the ForEach statement below when trying to display to the screen, it won't display the current item in the pipe, but when it gets to the end of the array, it displays all the items that were in the array.
If I comment out Format-Table -Autosize, it displays the current item in the pipe as expected.
As a kind of hack-around (yes, I made up a hyphenated word, but I liked it so you have to live with it) you could try pumping $x through a ForEach and creating a custom object for each record in $x. Something like:
$x | %{[PSCustomObject][Ordered]#{
"Action"=$_.AID
"SSN"=$_.ID
"Taco"=$_.FishBeefOrChicken
"More"=$_.OtherProperties
}}| FT -Auto
While the script you show should not alter $x at all, and $x should be both intact and accessible afterwards, sometimes all it takes to work through a problem is a new perspective and a different method of doing things to find the error that's eluding us.
A fairly basic script - searches Ou in AD and exports computers - I want to store each computer into an array so I can loop through later and run some commands against the computers. Although not having much luck with the array itself - am I completely off the track here?
$computers = #()
$i = 0
$ou = [ADSI]"LDAP://OU=Domain Controllers,DC=test,DC=local"
foreach ($child in $ou.psbase.Children) {
if ($child.ObjectCategory -like '*computer*') {
Write-Host $child.Name
$computers[$i] = $child.name
}
$i += 1
}
You're indexing into an empty array with $computer[$i]. If you don't know how big the array should be but you know it won't be huge, change to this:
$computers += $child.Name
If you know the size then allocate the array that size like so:
$computers = new-object string[] $arraySize
Then you can index into the array up to size - 1.
If you don't know the size and think it will be large, use a list instead e.g.:
$computers = new-object system.collections.arraylist
[void]$computers.Add($child.Name)
Use a pipeline, filter the objects with Where-Object (alias ?), and output the names in a ForEach-Object loop (alias %). The result is either an array of names or $null (when the LDAP query did not return any computer objects).
$ou = [ADSI]"LDAP://OU=Domain Controllers,DC=test,DC=local"
$computers = $ou.PSBase.Children |
Where-Object { $_.ObjectCategory -like '*computer*' } |
ForEach-Object { $_.Name }
If you want the result to be an empty array when no computer objects are found, wrap the command pipeline in #():
$computers = #($ou.PSBase.Children | ...)