I have a two-dimensional array of property names and values, which I need to add to a PowerShell object.
I have NO issues creating and displaying an object like this using New-Object and Add-Member:
$obj = New-Object PSObject
$obj | Add-Member NoteProperty IName($fiel.IName)
$obj | Add-Member NoteProperty SName($fiel.SName)
$obj | Add-Member NoteProperty Taggy($fiel.Taggy)
$obj | Add-Member NoteProperty Title($fiel.Title)
Write-Output $obj
But when I try something like this:
for ($k=0; $k -lt $fieldsArray.Count; $k++)
{
$itemobj | Add-Member –MemberType NoteProperty –Name $fieldsArray[$k].InternalName –Value $itemki[$j][$fieldsArray[$k].InternalName]
#Write-Host $k
#Write-Host $fieldsArray[$k].InternalName.ToString()
#Write-Host $itemki[$j][$fieldsArray[$k].InternalName]
}
Write-Output $itemobj
The Write-Output $itemobj will return only one property member that should be added without any neat column names.
The commented out parts were added for testing purposes and return correct values for all items.
I also tried
$itemobj | Add-Member NoteProperty $fieldsArray[$k].InternalName.ToString()($fieldsArray[$k].InternalName)
without any improvement.
Why are the other property members not added?
I have the data I need. If I write:
for ($k=0; $k -lt $fieldsArray.Count; $k++)
{
Write-Host $k
Write-Host $fieldsArray[$k].InternalName.ToString()
Write-Host $itemki[$j][$fieldsArray[$k].InternalName]
}
I get:
0 ID 1
1 ContentTypeId 0x0108007345CD807822EA4E85691E5C642F3A27
2 ContentType
3 Title Task0
4 Modified 11/24/2014 12:29:30 PM
And these are exactly the values that I expect and want. The problem is adding them as properties to an object. I think I cannot have a variable as a NotePropertyName, but it's a wild guess based on the results I am getting.
Some of the values in $itemki[$j][$fieldsArray[$k].InternalName] are empty - could it be it?
Forget all the arrays. They were just for the context:
Write-Host $fieldsArray[$k].InternalName.ToString() # Writes out the correct value
Write-Host $itemki[$j][$fieldsArray[$k].InternalName] # writes out the correct value
$itemobj | Add-Member NoteProperty $fieldsArray[$k].InternalName.ToString()($fieldsArray[$k].InternalName) # The values/property are not added
The question is: WHY NOT? Are there any restrictions in Add-Member on passing values as variables? Empty values?
I am still not sure completely, but if you are just focusing on the Add-Member then consider the following.
$fieldsarray = "ID", "ContentTypeID", "ContentType", "Title", "Modified"
$itemki = "1", "0x0108007345CD807822EA4E85691E5C642F3A27", "", "Task0", "11/24/2014 12:29:30 PM"
$itemobj = New-Object pscustomobject
for($k=0;$k -lt $fieldsArray.Count ; $k++)
{
$itemobj | Add-Member NoteProperty $fieldsarray[$k] $itemki[$k]
}
$itemobj
Notice the empty string entry in the array $itemki. This would generate the output.
ID : 1
ContentTypeID : 0x0108007345CD807822EA4E85691E5C642F3A27
ContentType :
Title : Task0
Modified : 11/24/2014 12:29:30 PM
Change the "" to an empty element: "1","0x0108007345CD807822EA4E85691E5C642F3A27",,"Task0","11/24/2014 12:29:30 PM", and you get this output:
ID : 1
ContentTypeID : 0x0108007345CD807822EA4E85691E5C642F3A27
ContentType : {Task0}
Title : 11/24/2014 12:29:30 PM
Modified :
Which is wrong. Then if you change the empty element to $null your output looks much like the first:
ID : 1
ContentTypeID : 0x0108007345CD807822EA4E85691E5C642F3A27
ContentType :
Title : Task0
Modified : 11/24/2014 12:29:30 PM
Concerning your output
You say you only get the last element when you do $itemobj outside the loop. What does $itemobj look like after each pass? for(;;){} loops don't send data to the output stream in the same way that a ForEach-Object{} would which is worth mentioning.
You could use a hash table instead of two arrays. It's very easy to create an object from a hash table. Here's an example:
$hash = #{
ID = '1'
ContentTypeID = '0x0108007345CD807822EA4E85691E5C642F3A27'
ContentType = ''
Title = 'Task0'
Modified = '11/24/2014 12:29:30 PM'
}
$Object = New-Object PSObject -Property $hash
Don't do a For loop, do a ForEach-Object loop. Something like:
$Object = New-Object PSObject
$Array | ForEach{
Add-Member -InputObject $Object -NotePropertyName $_[0] -NotePropertyValue $_[1]
}
That should do what you're looking for I think.
Related
I'm having a problem when outputting my foreach loop to a csv file.
My Groups are set like this:
$Groups = "Group1", "Group2", "Group3"
My code is:
$results = ForEach ($Group in $Groups) {
$memberof = get-adgroup $Group | select -expandproperty distinguishedname
Write-Output $Group
Get-ADObject -Filter 'memberof -eq $memberof -and (ObjectClass -eq "user" -or ObjectClass -eq "contact")' -properties * | select name, Objectclass, mail
Write-Output ""
Write-Output ""
}
$results | Export-csv Contacts.csv -NoTypeInformation
The problem seems to be coming from the Write-Output lines but I have no clue why. When I run my code without writing to a csv file, I get the expected result, something like:
NameOfGroup1
name Objectclass mail
---- ----------- ----
User1 user User1#mail.com
User2 user User2#mail.com
#Spaces caused by write-output ""
NameOfGroup2
User1 user User1#mail.com
Contact1 contact Contact1#externalmail.com
Then again when I run my code to write to csv file and have the write-output $Group commented out I get a similar result.
But if I run my full code from the top of this page including the write-output $Group, it comes out like this:
I've figured out what these results represent but I haven't got clue why they do print out like this.
Eseentially the numbers refer to the length of the group name, so the first 17 would be a 17 character group name, and then the number of lines below is equal to the number of contacts and users that are inside that group. The 2 zeros at the end of each group are the length of the write-output "" lines.
What is causing this behavior?
The following code will closely output what you are attempting.
$results = ForEach ($Group in $Groups) {
$memberof = get-adgroup $Group | select -expandproperty distinguishedname
Get-ADUser -Filter "memberof -eq '$memberof' -and (ObjectClass -eq 'user' -or ObjectClass -eq 'contact')" -properties name,ObjectClass,Mail | Select-Object #{n='Group';e={$Group}},name, Objectclass, mail
[pscustomobject]"" | Select-Object Group,Name,ObjectClass,Mail
[pscustomobject]"" | Select-Object Group,Name,ObjectClass,Mail
}
$results | Export-csv Contacts.csv -NoTypeInformation
Explanation:
Export-Csv converts an object or array of objects with properties into a CSV file. You can see the same result in the console with ConvertTo-Csv. Properties are converted into columns and property values are placed under their associated columns. When you output a string as in Write-Output $Group, it has a property of Length. To fix this, you need to add $Group as a calculated property in your Select-Object. If you want to do blank lines in your CSV, then you should output another object with all of the property values as ''.
When you mix objects in your PowerShell outputs, you can see unexpected results. Your Get-ADObject outputs a custom object. Your Write-Output lines output a string. Those two object types do not share properties. So you only see the properties for the first object in your array, which is a string. If you put all of the Write-Output statements at the end of your loop, you will see more properties in your CSV. See below for an example that just by reversing the order of processed objects, you get a different result.
$str = "string"
$obj = [pscustomobject]#{property1 = "value1"; property2 = "value2"}
$str,$obj | convertto-csv -notype
"Length"
"6"
$obj,$str | convertto-csv -notype
"property1","property2"
"value1","value2"
,
Notice the properties available to the custom object $obj and the string $str.
$obj | get-member -Type Properties
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
property1 NoteProperty string property1=value1
property2 NoteProperty string property2=value2
$str | get-member -Type Properties
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Length Property int Length {get;}
I'm trying to split a URL into an object.
$url = "https://api.somedomain.com/v2/direct-access/producing-entities-details?entity_id=104194825&format=json&page=1
The desired result would be
PS C:\WINDOWS\system32> $url.api
https://api.somedomain.com/v2/direct-access
PS C:\WINDOWS\system32> $url.dataset
producing-entities-details
PS C:\WINDOWS\system32> $url.params
entity_id=104194825&format=json&page=1
I'm sure this can be done in combination with regex, but I am also wondering if this can be done just using built in PowerShell functionality.
No need to mess around with regex. The [uri] type accelerator would do some of that work for you. The other parts seem specific to how you choose to interpret the data and not how URL anatomy works.
PS C:\Users\matt> $url = [uri]"https://api.somedomain.com/v2/direct-access/producing-entities-details?entity_id=104194825&format=json&page=1"
PS C:\Users\matt> $url.Query
?entity_id=104194825&format=json&page=1
You can explore the other properties and see how they will help you. For instance, you might need to build the Segments to get the other parts you are looking for.
PS C:\Users\matt> $url.Segments
/
v2/
direct-access/
producing-entities-details
$url = "https://api.somedomain.com/v2/direct-access/producing-entities-details?entity_id=104194825&format=json&page=1"
$uri = [System.Uri]$url
$ParsedQueryString = [System.Web.HttpUtility]::ParseQueryString($uri.Query)
$i = 0
$queryParams = #()
foreach($QueryStringObject in $ParsedQueryString){
$queryObject = New-Object -TypeName psobject
$queryObject | Add-Member -MemberType NoteProperty -Name Query -Value $QueryStringObject
$queryObject | Add-Member -MemberType NoteProperty -Name Value -Value $ParsedQueryString[$i]
$queryParams += $queryObject
$i++
}
$queryParams
Output:
Query Value
----- -----
entity_id 104194825
format json
page 1
I have created a custom Object using the code below, but I now need to add an additional property.
Reading the online documentation and help files suggests that I need to use add-member (as per my example below).
But When I run this I get:
Add-Member : A positional parameter cannot be found that accepts argument 'newvar'.
What is the correct syntax to add a new parameter to an Object.
Also, if I wanted to update a parameter with a different value, can I use the the same add-member? (there doesn't appear to be an 'update-member' cmdlet)
$TestList = #(
"item1"
"item2"
)
$TESTObject = #()
foreach($a in $TestList)
{
$dItem = $a
$TESTObject += New-Object PSObject -property #{
item = "$dItem";
}
}
FOREACH($a in $TESTObject)
{
#DO STUFF HERE
$newVar = 1234
$a | Add-Member newvar $newVar
}
The way I have found to do this is:
Add a member to a custom (PSObject) Object
Add-Member -InputObject $TESTObject -MemberType NoteProperty -Name newVar -Value $newVar
And to update an existing member I use
Add-Member -InputObject $TESTObject -MemberType NoteProperty -Name newVar -Value $newVar -force
whether this is the best way to do this (or even the correct way) I'm not sure. But it seems to work.
I have a simple business requirement to design a "Roster" spreadsheet that can be read by a powershell script to extract out data for down stream processes. The spreadsheet has some headers such as FirstName, LastName, Company, StartDate. These headers start on A1 and continue to J1. The spreadsheet is 2007 (xlsm) and contains 1 worksheet with a formatted Table (DataTable?). To the right of the DataTable are a few fields such as NumberOfRecords and ContactInfo.
The powershell script so far works fine as long as the DataTable is the first column/row. If I were to switch the NumberOfRecords & ContactInfo field above or to the left, then it throws off the whole order of record reading.
Below is the current script
function getData {
$excelPath = "c:\temp\Roster.xlsm"
$global:roster = #()
$conn = new-object System.Data.OleDb.OleDbConnection
$conn.ConnectionString = 'Provider=Microsoft.ACE.OLEDB.12.0;Data Source='+$excelPath+';Extended Properties="Excel 12.0 Xml;HDR=YES";'
$cmd = new-object System.Data.OleDb.OleDbCommand("SELECT * FROM [Roster$] WHERE [Valid] = true",$conn)
$conn.open()
$reader = $cmd.ExecuteReader()
while ($reader.Read())
{
$user = new-object object
$user | add-member -MemberType NoteProperty -Name "FirstName" -Value $reader.item(1).ToString()
$user | add-member -MemberType NoteProperty -Name "MName" -Value $reader.item(2).ToString()
$user | add-member -MemberType NoteProperty -Name "LastName" -Value $reader.item(3).ToString()
$user | add-member -MemberType NoteProperty -Name "Company" -Value $reader.item(6).ToString()
$user | add-member -MemberType NoteProperty -Name $reader.GetName(8) -Value $reader.item(8).ToString()
$global:roster += $user
}
$conn.close()
$global:roster | out-gridview
#
}
Is there any way I can reference this DataTable directly? I know in Excel 2010 when formatting data as a table you can give it a name (default Table1). Could I do something like "SELECT Table1.* FROM [Roster$] WHERE [VALUE] = true" ??
My goal is to give a nicely formatted spreadsheet, extracting the data from the formatted table via Powershell.
I found a solution but it requires using COM instead of OLEDB or ADO. I was not able to find any solution online with OLE to call named objects. This solution is a bit better since I can arrange my DataTable anywhere in the worksheet, however I'm not too fond of it since it requires Office to be installed, but basically I can call reference to the Named Table by creating an Excel object and then calling it's ListObjects on the ActiveSheet. This returns the DataTable with all of it's Columns/Rows/Ranges. All I need to do then is loop through each row and column to grab the data.
$excel = new-object -ComObject Excel.Application
$tmp = $excel.Workbooks.Open('c:\temp\Roster.xlsm')
$table = $excel.ActiveSheet.ListObjects | where-object { $_.DisplayName -eq "MyNamedRange" }
$rows = $table.ListRows
I have an object which looks for example like this
Name Number
---- ------
John one
Major two
Mars one
I want to go through each member and check on Number and add a property that in the end it looks like this.
Name Number IsItOne
---- ------ -------
John one True
Major two False
Mars one True
What I got so far is do a foreach loop through the object but then I have two objects and have no chance as far as I know to change the original object.
Just another (shorter) version:
$obj | add-member -type scriptproperty -name IsItOne -value {$this.Number -eq 'one'} -passthru
It seems to be like you are talking about a set of objects with properties Name and Number.
If so, you can do like this:
$a | %{ $isitone = $false; if($_.Number -eq "one") {$isitone=$true} $_ |
add-member -Type noteproperty -name IsItOne -value $isitone }
Here is a possible alternative.
function new-stuff ($name, $number) {
New-Object PSObject -Property #{name=$name; number=$number}
}
$(
new-stuff John one
new-stuff Major two
new-stuff Mars one
) | ForEach { $_ | Add-Member -PassThru ScriptProperty IsItOne {$this.Number-eq"one"} }