Tuesday, December 6, 2016

Why does my Custom Objects goes all {} on me?

Well, when working with AD in i.e. ADSI you don't get single objects in return but collections.

So if adding a property of such an object, it will be presented to PoSH as multivalued and thus entered as a collection instead of a value.

For this example I've done an LDAP query to an AD returning a DN object, $objDN.

Now this collection has some properties I'm interested in.

Path             Properties
----             ----------
LDAP://CN=...    {operatingsystem, countrycode, cn, lastlogoff...}
LDAP://CN=...    {operatingsystem, countrycode, cn, lastlogoff...}


The properties property is obviously a collection.

PS U:\> $objDN[0].properties

Name                           Value
----                           -----
operatingsystem                {Windows Server 2008 R2 Standard}
countrycode                    {0}
cn                             ...
lastlogoff                     {0}
flags                          {16}
dscorepropagationdata          {2016-06-15 15:31:24, 2015-12-28 18:53:43, 2015-12-28 18:53:37, 2015-12-28 18:53:33...}
dnshostname                    ...
usncreated                     {1548162366}
objectguid                     ...

...


But all values of the properties is also collections, *even* when they are single valued.

So using one of the values will look kind if weird on some occasions.

Pure string handling is ok.

PS U:\> $objDN[0].properties.operatingsystem
Windows Server 2008 R2 Standard


 But Custom Object will look funny.

PS U:\> $objComp = @{}
PS U:\> $objComp.OS = $objDN[0].properties.operatingsystem
PS U:\> $objComp.Purchase = "2015-01-01"
PS U:\> $objComp.Modell = "Dell Lattitude"
PS U:\> $objComp

Name                           Value
----                           -----
Modell                         Dell Lattitude
Purchase                       2015-01-01
OS                             {Windows Server 2008 R2 Standard}


The .OS property can still be used as a string when needed, but it looks darned ugly. And the value is actually a string.

The collection type:

PS U:\> $objComp.OS
Windows Server 2008 R2 Standard


PS U:\> $objComp.OS.gettype()

IsPublic IsSerial Name                               BaseType
-------- -------- ----                               --------
True     False    ResultPropertyValueCollection      System.Collections.ReadOnlyCollectionBase


The type of the single value:

PS U:\> $objComp.OS[0]
Windows Server 2008 R2 Standard


PS U:\> $objComp.OS[0].gettype()

IsPublic IsSerial Name                               BaseType
-------- -------- ----                               --------
True     True     String                             System.Object


So for single valued collections, simply just use the first entry of the collection.

$objComp.OS = $objDN[0].properties.operatingsystem[0]

PS U:\> $objComp

Name                           Value
----                           -----
Modell                         Dell Lattitude
Purchase                       2015-01-01
OS                             Windows Server 2008 R2 Standard





Tuesday, April 12, 2016

Duh...

Remember me having some issues with Custom Objects not beeing ordered in PoSH v2?

$propertyOfSven = @{}
$propertyOfSven.first = "Sven"
$propertyOfSven.last = "Karlsson"
$propertyOfSven.phone = "040-11 12 13"

> $propertyOfSven

Name                           Value
----                           -----
last                           Karlsson
phone                          040-11 12 13
first                          Sven


> $sven = New-Object PSObject -Property $propertyOfSven

> $sven

last                      phone                       first
----                      -----                       -----
Karlsson                  040-11 12 13                Sven


Well, why don't we just Sort the object output (thanks to Bill Stewart)?

> $sven = New-Object PSObject -Property $propertyOfSven | Select-Object first,last,phone

> $sven

first                   last                           phone
-----                   ----                           -----
Sven                    Karlsson                       040-11 12 13


And done.

Tuesday, March 15, 2016

Objects made easy

Ok, so you've noticed me struggle.
But that's how you learn.

In my last post I was trying to simplify the creation of objects in various way but I forgot about the golden rule, KISS (Keep It Simple, Stupid).

That is, if it looks complex, write a function...

So the only way to create an object in PowerShell v2 with ordered properties is like this, right?

$Kalle = New-Object PSObject
$Kalle| Add-Member -MemberType NoteProperty -Name First -Value "Kalle"
$Kalle| Add-Member -MemberType NoteProperty -Name Last -Value "Svensson"
$Kalle| Add-Member -MemberType NoteProperty -Name Phone -Value "555-55 55 55"
$Kalle
 
First               Last                    Phone
-----               ----                    -----
Kalle               Svensson                555-55 55 55 

So lets make it a an advanced function.

function New-CustomObject
    {
    param (
        [array]$PropertyArray
    )

    $Object = New-Object PSObject
   
    foreach ($Property in $PropertyArray){
        $Object | Add-Member -MemberType 'NoteProperty' -Name $Property -Value $null
    }
   
    return $Object
}


Then putting the new function to use

$Kalle = New-CustomObject -PropertyArray "First","Last","Phone"

$Kalle.First = "Kalle"
$Kalle.Last = "Svensson"
$Kalle.Phone = "555 - 55 55 55"


$Kalle
 
First               Last                    Phone
-----               ----                    -----
Kalle               Svensson                555-55 55 55 

And I'm done

Thursday, March 3, 2016

Finally, objects is making sence

I've been struggling with one last part of MS PowerShell for a while now, namely the internal data structures of PowerShell objects (and all .Net objects for that matter).

They are neat, but how do I define custom outputs in an easy way to read in my script code?

You've all read about Add-Member -type and New-Object System.Object.
They do work, but they are a pig to write.

Now, you might say "But why don't simply use hash tables?".
Well, they can't be ordered in PowerShell v2. You'll need PowerShell v3 to build it, i.e.

[ordered]@{First = "Sven"; Last = "Karlsson"}

and then there's the matter of hash tables only containing two values (key and value) which makes the construct confusing :/

Say I want to output a phone list as an object in PowerShell.

$propertyhashSven = @{}
$propertyhashSven.first = "Sven"
$propertyhashSven.last = "Karlsson"
$propertyhashSven.phone = "040-11 12 13"

> $propertyhashSven

Name                           Value
----                           -----
last                           Karlsson
phone                          040-11 12 13
first                          Sven


> $sven = New-Object PSObject -Property $propertyhashSven

> $sven

last                      phone                       first
----                      -----                       -----
Karlsson                  040-11 12 13                Sven


The properties is not ordered at ALL :/

But how about this...

First thing is to define the object. The easiest way I found is to first create my own data type in C#.

add-type @"
 public struct contact {
    public string First;
    public string Last;
    public string Phone;
 }
"@


Then, simply create the objects using that data type.

> $sven = New-Object contact
> $kalle = New-Object contact


And then populate it with some properties

> $sven.First = "Sven"
> $sven.Last = "Karlsson"
> $sven.Phone = "040-11 12 13"

> $kalle.first = "Kalle"
> $kalle.last = "Svensson"
> $kalle.phone = "08-555 555"

And then finally wrapping it up in a phone book as an array.

> $phonebook = @($sven,$kalle)

And there you go :)

> $phonebook

First                     Last                        Phone
-----                     ----                        -----
Sven                      Karlsson                    040-11 12 13
Kalle                     Svensson                    08-555 555


Want to sort it by first name? No problems.

> $phonebook | Sort-Object First

First                     Last                       Phone
-----                     ----                       -----
Kalle                     Svensson                   08-555 555
Sven                      Karlsson                   040-11 12 13


And adding more entries is just a matter of expanding the array...

> $sture = New-Object contact
> $sture.first = "Sture"

> $phonebook = $phonebook + $sture 

> $phonebook

First                     Last                       Phone
-----                     ----                       -----
Sven                      Karlsson                   040-11 12 13
Kalle                     Svensson                   08-555 555
Sture


And hopefully, it won't break anything ;)