System.Object[] in CSV File - powershell

I have read all I can read but I just don't get what the problem is when exporting something from PowerShell to a CSV and getting System.Object[]
This is just a section of code I use to extract missing updates from servers that are managed by SCCM. The Switch Array is there because I need to have the SCCM Deployment Unique ID translated into a "Friendly" name I use to identify that patching collection.
This works fine and is displayed correcting on the screen (I just do this for testing the Switch array to ensure it is working by putting $Updates in the script). However, when I attempt to export to a CSVfile, I get the System.Object[] underneath the column titles.
I know I could pipe the first line, then select the objects in the $TargetedUpdates array, and export them without any problem. But this only gives me the SCCM Deployment Unique ID with the server. I need to "resolve" this to a friendly name that makes sense in the CSV file. How can this be accomplished?
$TargetedUpdates = Get-WmiObject -Query "Select * from CCM_TargetedUpdateEX1 where UpdateState = 0" -Namespace root\ccm\SoftwareUpdates\DeploymentAgent -Computer ifdpv02
ForEach-Object {
$MissingUpdates = $TargetedUpdates.RefAssignments.TrimEnd(";")
$MonthlyPatch = switch ($MissingUpdates){
"{0C3267EE-F343-4577-B1A3-C24FA0406DDF}" {"October 2014 Patching for Test\DEV Servers"}
"{D849903A-4594-4D72-9224-39DC2ABA22E}" {"October 2014 Patching for Production Servers"}
"{A3F0E8A2-FB2F-4045-8E22-7726007844E6}" {"October 2014 Patching for Manual Servers"}
"{DC3991B7-30EB-4529-AA63-537968A651D0}" {"October 2014 Patching for New Server Builds"}
"{7C263094-4DA3-4AB8-9F79-0C169EA18D6D}" {"October 2014 Patching for Manual Test Servers"}
"{39EDE4AD-71C9-4393-B849-498C6D677FFF}" {"October 2014 Patching for Test\SQL Servers"}
#**********************************************************************************************
#**********************************************************************************************
default {"This is a System Center Endpoint Protection Update"}  
}
$Updates = New-Object PSobject
$Updates | Add-Member NoteProperty Server ($TargetedUpdates.PScomputerName)
$Updates | Add-Member NoteProperty MonthlyPatch $MonthlyPatch
$Updates
$Updates | Export-Csv C:\temp\test.csv -NoTypeInformation
}
Invoke-Item C:\temp\test.csv

In short: You are not processing each update individually.
Unfortunately I am not familiar with the data structure of your WMI return to know if this is going to work. You need to send data to the foreach-object loop to process is the main thing.
$TargetedUpdates = Get-WmiObject -Query "Select * from CCM_TargetedUpdateEX1 where UpdateState = 0" -Namespace root\ccm\SoftwareUpdates\DeploymentAgent -Computer ifdpv02
$TargetedUpdates | ForEach-Object {
$MissingUpdates = $_.RefAssignments.TrimEnd(";")
$MonthlyPatch = switch ($MissingUpdates){
"{0C3267EE-F343-4577-B1A3-C24FA0406DDF}" {"October 2014 Patching for Test\DEV Servers"}
"{D849903A-4594-4D72-9224-39DC2ABA22E}" {"October 2014 Patching for Production Servers"}
#TRUNCATED AS IT IS TOO LONG**********************************************************************************************
default {"This is a System Center Endpoint Protection Update"}
}
$Updates = New-Object PSobject
$Updates | Add-Member NoteProperty Server ($TargetedUpdates.PScomputerName)
$Updates | Add-Member NoteProperty MonthlyPatch $MonthlyPatch
$Updates
} | Export-Csv C:\temp\test.csv -NoTypeInformation
We loop each update individually create an object for each. Those are then sent to the Export-CSV for output.
Another Approach
Instead of the switch in the loop you could use a hashtable that stores all your id and friendly names and then use a calculated property to make the object for you rather easily.
$friendlies = #{
"{0C3267EE-F343-4577-B1A3-C24FA0406DDF}" = "October 2014 Patching for Test\DEV Servers"
"{D849903A-4594-4D72-9224-39DC2ABA22E}" = "October 2014 Patching for Production Servers"
# Add more here obviously.
}
$server = "ifdpv02"
$TargetedUpdates = Get-WmiObject -Query "Select * from CCM_TargetedUpdateEX1 where UpdateState = 0" -Namespace root\ccm\SoftwareUpdates\DeploymentAgent -Computer $server
$TargetedUpdates | Select-Object #{Label="Server";Expression={$server}},
#{Label="MonthlyPatch";Expression={$friendlies[$_.RefAssignments.TrimEnd(";")]}} |
Export-Csv C:\temp\test.csv -NoTypeInformation
Caveat for both solution here:
Like I said earlier I do not know the data structure. If RefAssignments returns as array we need to add some more logic but it can be done.

I believe I solved the problem by changing my variable for $MonthlyPatchAssignment. Excuse me for the clutter, the code I will post here is much different than I originally posted. The key seems to be grabbing the variable in the pipeline ($.RefAssignments) and putting that into my Swich array. Before I was trying to take that same variable but make it equal to another variable (i.e. $MissingUpdates = $.RefAssignments.TrimEnd(";"))
Now I am getting the correct value instead of System.Object[] in my CSV file
$MonthlyPatchAssignment = switch ($_.RefAssignments.TrimEnd(";")) {
     
"{0C3267EE-F343-4577-B1A3-C24FA0406DDF}" {"October 2014 Patching for Test\DEV Servers"}
"{D849903A-4594-4D72-9224-39DC2ABA22E}" {"October 2014 Patching for Production Servers"}
"{A3F0E8A2-FB2F-4045-8E22-7726007844E6}" {"October 2014 Patching for Manual Servers"}
"{DC3991B7-30EB-4529-AA63-537968A651D0}" {"October 2014 Patching for New Server Builds"}
"{7C263094-4DA3-4AB8-9F79-0C169EA18D6D}" {"October 2014 Patching for Manual Test Servers"}
"{39EDE4AD-71C9-4393-B849-498C6D677FFF}" {"October 2014 Patching for Test\SQL Servers"}
default {"This is a System Center Endpoint Protection Update"}  
}
$NonCompliantDetail = New-Object PSobject
$NonCompliantDetail | Add-Member NoteProperty Server $($_.PScomputerName)
$NonCompliantDetail | Add-Member NoteProperty PatchName $MonthlyPatchAssignment
$NonCompliantDetail | Add-Member NoteProperty BullentinID $uBulletinID
$NonCompliantDetail | Add-Member NoteProperty Description $uTitle
$NonCompliantDetail | Export-Csv C:\Temp\sccm\"$FileNamePreface"_MissingUpdatesRAW.csv -NoTypeInformation -Append

Related

Power shell For Loop not Looping

So the output works fine but I'm having an issue with it only outputing the last line it runs. Is there anyway to check for loops to test in the future?
but i have a list of ip address and im trying to check if the firewall in windows is enabled or disabled.
They are on one LARGE (300+ workgroup). Any help in getting this to loop properly would be appreciated. Security and other things are not a concern cause i have other scripts that run fine. And i dont get any errors. just the single output.
ive already tried moving the array and that didn't help. im thinking it could be the PSCustomObject part as i'm just starting to learn these. Or could it be my input and output formats are different and that's causing issues??
clear
$ComputerList = get-content C:\Users\Administrator\Desktop\DavidsScripts\TurnOffFirewall\input.txt
$Status = #(
foreach ($Computer in $ComputerList) {
netsh -r $Computer advfirewall show currentprofile state})[3] -replace 'State' -replace '\s'
$Object = [PSCustomObject]#{
Computer = $Computer
Firewall = $Status
}
Write-Output $Object
$Object | Export-Csv -Path "C:\FirewallStatus.csv" -Append -NoTypeInformation
Your previous code was not escaping the loop and was only adding the last computer in the loop to the object.
The best way I have found, is to make a temp object and add it to an array list then export that. Much nicer.
$ComputerList = get-content C:\Users\Administrator\Desktop\DavidsScripts\TurnOffFirewall\input.txt
$collectionVariable = New-Object System.Collections.ArrayList
ForEach ($Computer in $ComputerList) {
# Create temp object
$temp = New-Object System.Object
# Add members to temp object
$temp | Add-Member -MemberType NoteProperty -Name "Computer" -Value $Computer
$temp | Add-Member -MemberType NoteProperty -Name "Firewall" -Value $((netsh -r $Computer advfirewall show currentprofile state)[3] -replace 'State' -replace '\s')
# Add the temp object to ArrayList
$collectionVariable.Add($temp)
}
Write-Output $collectionVariable
$collectionVariable | Export-Csv -Path "C:\FirewallStatus.csv" -Append -NoTypeInformation
Here's a streamlined, functional version of your code, using a single pipeline:
Get-Content C:\Users\Administrator\Desktop\DavidsScripts\TurnOffFirewall\input.txt |
ForEach-Object {
[pscustomobject] #{
Computer = $_
Firewall = (-split ((netsh -r $_ advfirewall show currentprofile state) -match '^State'))[-1]
}
} | Export-Csv -Path C:\FirewallStatus.csv -NoTypeInformation
Note:
No intermediate variables are needed; each computer name read from the input file is processed one by one, and each custom object constructed based on it is sent to the output CSV file.
The command for extracting the firewall status from netsh's output was made more robust in order to extract the state information based on the line content (regex ^State, i.e., a line starting with State) rather than a line index ([3]); the unary form of -split splits the line of interest into tokens by whitespace, and index [-1] extracts the last token, which is the state value.
As for what you tried:
Your foreach loop ended before $Object was constructed, so you ended up constructing just 1 object to send to the output file with Export-Csv.
If you had formatted your code properly, that fact would have been more obvious; try using Visual Studio Code with the PowerShell extension, which offers automatic formatting via the >Format Document (Shift+Alt+F) command.

Get the term set name that is being used in sharepoint sites

I have been tasked to get the MMS term sets that are being used based on terms(not with null value of MMS column in the list's items ) in all the sites so that only those MMS terms sets can get migrated to the other sharepoint environment. On a base level I'm using below script
$FieldCollection= (Get-SPWeb https:/sharepoint.com/sites/pssl/mgmt).Lists.Fields
$MetadataField = New-Object psobject
$MetadataField | Add-Member -MemberType NoteProperty -Name "ParentListUrl" -value ""
$MetadataField | Add-Member -MemberType NoteProperty -Name "ParentListTitle" -value ""
$MetadataField | Add-Member -MemberType NoteProperty -Name "FieldTitle" -value ""
$MetadataField | Add-Member -MemberType NoteProperty -Name "FieldId" -value ""
$matches = #();
foreach($field in $FieldCollection)
{
if($field.GetType().Name -ne "TaxonomyField"){
continue;
}
#if($field.TermSetId.ToString() -ne $TermSet.Id.ToString()){continue;}
$tf = $MetadataField | Select-Object *;
$tf.ParentListUrl = $field.ParentList.ParentWeb.Url;
$tf.ParentListTitle = $field.ParentList.Title;
$tf.FieldTitle = $field.Title;
$tf.FieldId = $field.ID;
$matches += $tf;
}
return $matches;
but it returns only managed metadata columns defined in the list, but not they are being used in the list. Can anybody help me to achieve the task.
I'm not an expert in Sharepoint API, but I'm trying to understand what the problem is to help you and I can't.
I notice that you create an object $MetadataField before a loop, then kind of create a replica $MetadataField | Select-Object * and then add in a array.
As you say, the returned objects should only have the columns of ParentListUrl,ParentListTitle,FieldTitle and FieldId which is what I expect from the sample above. Can you elaborate more on what you are looking for? Maybe update the entire function into your question and post your returned expectation. this way I can try to help you out.
Btw, the ; is not required and you should create a new instance of the object within the loop. You can use the same method or first create a hash key that drives the properties of a custom object. For example in your loop adjust the following.
$hash=#{
Property1="Value1"
Property2="Value2"
}
New-Object -Type PSObject -Property $hash
Also if you function returns directly each found item without extra processing, then you can skip adding them in an array and just write in the output like I do in my example. To make it more clear, if I would put a loop around my example in a function and execute, then I would get a recordset with custom object with Property1 and Property2

Listing Superseded Updates using powershell in WSUS 3

I am trying to automate the process of managing WSUS reports. I managed to
I) report the updates that I approve to WSUS console.
II) run a cleanup Process for the superseeding
So the script I use to list approved updated updates is:
$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$updatescope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::LatestRevisionApproved
$updatescope.FromArrivalDAte = [datetime]"10/08/2013"
$wsusgroup = $wsus.GetComputerTargetGroups() | Where {$_.Name -eq "PCM_WSUS_spec"}
$updatescope
$updatescope.gettype()
$updatescope.count
$updateScope.ApprovedComputerTargetGroups.add($wsusgroup)
$wsus.GetUpdates($updatescope) | Select KnowledgebaseArticles,Title
$Updates = $wsus.GetUpdates($updatescope) | Select KnowledgebaseArticles
What I really need is a function to list of the updates went superseded based on the aboce list; updates that got approved after the given date.
Any ideas?
To build a list of superseded updates will require you to know the current update. Superseded updates are maintained as a list of UpdateIDs associated with the current update. (The list of superseding updates is built by traversing that list backwards.)
In the actual package XML, it looks like this:
<sdp:SupersededPackages xmlns:sdp="http://schemas.microsoft.com/wsus/2005/04/CorporatePublishing/SoftwareDistributionPackage.xsd">
<sdp:PackageID>b87cc8c1-b03d-4548-aa53-f0138ec6e2a3</sdp:PackageID>
<sdp:PackageID>7d8fcd7b-49d1-440d-8140-d6c33ce2cb80</sdp:PackageID>
<sdp:PackageID>76f68136-436f-42a5-9029-560b23702416</sdp:PackageID>
<sdp:PackageID>64b8c9d0-ecbc-4711-90de-b190d2ee7ee1</sdp:PackageID>
<sdp:PackageID>6bd77c33-1b2d-450d-91c6-259e101e57bb</sdp:PackageID>
<sdp:PackageID>94642c22-d70e-45b8-a70f-c09b86e2c4f5</sdp:PackageID>
</sdp:SupersededPackages>
But I do not know how it's actually accessed via the API (if even possible), or where in the database schema to find it.
Well I achieved to get a list using the following :
'[reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")
$WSUS = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer('wsupdates',$false,80)
#creating the update scope. Different parameters can be used each time for different reports needed
$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$updatescope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::LatestRevisionApproved
$updatescope.FromArrivalDAte = [datetime]"10/08/2013"
$wsusgroup = $wsus.GetComputerTargetGroups() | Where {$_.Name -eq "PCM_WSUS_spec"}
$updateScope.ApprovedComputerTargetGroups.add($wsusgroup)
$updatescope
$updates = $wsus.GetUpdates($updatescope) | Select Title,UpdateClassificationTitle,SupersededUpdates | Sort Title |Export-csv 'C:\WSUS\test_approved.csv' -notype
$updates = $wsus.GetUpdates($updatescope)
$updates
$updates | ForEach-Object {
$temp =$_
Write-Output ' temp is'
$temp
$temp | Select Title| Export-CSV 'C:\wsus\superseded.csv' -Append -Notype -Force
$SupersededOnes = $_.GetRelatedUpdates(([Microsoft.UpdateServices.Administration.UpdateRelationship]::UpdatesSupersededByThisUpdate)) |Select Title
Write-Output 'after finding out superseded'
$SupersededOnes
Start-Sleep 8
$SupersededOnes | Export-CSV 'C:\wsus\superseded.csv' -Append -Notype -Force
}'
My issue is it appends the superseded beneath the approved, I need them to the column besides the associating superseding update.

Is there a way to force powershell -Export-CSV cmdlet to maintain a specific column order?

I'm new to powershell, so the script is a Frankenstein of various examples of from sites.
My question is how can I make sure that a csv file I am creating for a DataTable keeps the column order I specify?
My script does this to populate the csv headers and values like so:
...snip...
$dataTable | ForEach-Object {
$csv+=New-Object PSObject -Property #{
program_code=$_.ProgramCode;
first_name=$_.FirstName;
last_name=$_.LastName;
email=$_.email;
phone=$_.phone;
phone2=$_.otherphone;
address=$_.addr1;
address2=$_.addr2;
city=$_.city;
state=$_.state;
postal_code=$_.Zip;
country=$_.Country;
grad_year=$_.HsGradDate;
lead_date=$_.LeadDate;
lead_source=$_.LeadSource;
school_status=$_.SchoolStatus;
}
}
$csv | Export-CSV C:\scripts\NewLeads$(Get-Date -Format yyyyMMdd).csv -notype -Append
...snip...
I want the file to have to columns in the order that I specify in the script, but when I open it in notepad or excel the columns appear in a seemingly random order. Keyword being seemingly since they may have some method of ordering themselves.
In PowerShell V3, instead of:
$csv+=New-Object PSObject -Property #{
I would use:
$csv+=[pscustomobject]#{
The PowerShell V3 parser will preserve the order of the keys when you cast a hash literal to either [ordered] or [pscustomobject]. A small side benefit to this approach - it will also be faster.
If you are using V2, you'll need to skip the -Property parameter to New-Object and instead use multiple calls to Add-Member. It will look something like:
$csv+=New-Object PSObject |
Add-Member -Name program_code -Value $_.ProgramCode -MemberType NoteProperty -PassThru |
Add-Member -Name first_name -Value $_.FirstName -MemberType NoteProperty -PassThru |
...
Select the fields in the order required, then export.
$csv | select-object -property program_code,first_name,last_name,email,phone,phone2,address,address2,city,state,psotal_code,country,grad_year,lead_date,lead_source,school_status |
Export-CSV C:\scripts\NewLeads$(Get-Date -Format yyyyMMdd).csv -notype -Append
However, you may be able to short-circuit this a little. Depending on what $dataTable really is, you may (should, in most cases) be able to select directly from that object and bypass creating the collection of PSObjects. But if you need the custom headers, you'll need to use expressions in select-object (linebreaks for readability).
$dataTable| select-object #{Name="program_code";Expression={$_.ProgramCode}},`
#{Name="first_name";Expression={$_.firstname}},`
#{Name="last_name";Expression={$_.lastname}},email,phone,`
#{Name="phone2";Expression={$_.otherphone}},`
#{Name="addr1";Expression={$_.address}},`
#{Name="addr2";Expression={$_.address2}},city,state,`
#{Name="postal_code";Expression={$_.zip}},country,`
#{Name="grad_year";Expression={$_.hsgraddate}},`
#{Name="lead_date";Expression={$_.leaddate}},`
#{Name="lead_source";Expression={$_.leadsource}},`
#{Name="school_status ";Expression={$_.schoolstatus }}|
Export-CSV C:\scripts\NewLeads$(Get-Date -Format yyyyMMdd).csv -notype -Append

Formatting the output of repadmin - powershell

I'm creating a script that tells me the creation / modification date and other pieces of info of AD objects to determine upgrade status of the machines in large domains. I have no problem accomplishing this in a well formatted and easy to read manner in Server 2008 because it has Active Directory modules, but this isn't the case with Server 2003.
With server 2003 I had to use a different approach to the script to gather the information I want, but I am unsure how to format this.
This is my current script:
$filePath = “$ENV:UserProfile\Desktop\output.txt”
## Enter the name of the Domain controller below
$DCName = “Exchange”
$computers = dsquery computer domainroot -name * -limit 0
Foreach ($computer in $computers) {
repadmin /showattr $DCName $computer /atts:"WhenChanged,WhenCreated,OperatingSystem" /long | out-file –append $filepath
}
This is the sample output:
DN: CN=Sample-Object,CN=Computers,DC=Contoso,DC=com
1> whenCreated: 07/04/2011 14:00:02 Pacific Standard Time Pacific Daylight Time
1> whenChanged: 08/09/2012 11:24:22 Pacific Standard Time Pacific Daylight Time
1> operatingSystem: Windows 7 Professional
In server 2008 I'm able to use string formatting ('"{0}","{1}"' -F $computer.name, $computer.whatever) amd output it to a .csv to make it presentable but I don't think the same methods will apply to the results of repadmin.
My end goal would to simply have a CSV with Computer Name, along with the three or however many attributes I have extracted from repadmin.
Any help appreciated, thank you.
Give this a try, you can export it to CSV and import it back as objects:
$result = dsquery computer domainroot -name * -limit 0 | foreach {
repadmin /showattr $DCName $_ /atts:"WhenChanged,WhenCreated,OperatingSystem" /long
} | Out-String
$result.split([string[]]'DN:',[StringSplitOptions]::RemoveEmptyEntries) | Foreach-Object{
$attr = $_.Split("`r`n",[StringSplitOptions]::RemoveEmptyEntries)
New-Object -TypeName PSObject -Property #{
DN = $attr[0].Trim()
whenCreated = $attr[1].Trim() -replace '^1> whenCreated: '
whenChanged = $attr[2].Trim() -replace '^1> whenChanged: '
operatingSystem = $attr[3].Trim() -replace '^1> operatingSystem: '
}
}