Simplify the function. pscustomobject for output

Hello. help simplify the function. I use pscustomobject for output

Function Get-UserADInfo {
    [CmdletBinding()]
    param(
        [Parameter(
            Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
 
        [string]  $UserAccount
 
    )
 
    BEGIN {}
 
    PROCESS { 
            $UserADInfo=Get-ADUser -Filter {userprincipalname -like $UserAccount } -Properties enabled, accountExpirationDate, city, title, manager, info | 
                        Select samAccountName, userprincipalname, enabled, accountExpirationDate, title, city, @{N='Manager';
                                                                                                                 E={if ($_.name -like "120*"){(Get-ADUser ($_.info).substring(16)).userprincipalname} 
                                                                                                                    else {(Get-ADUser $_.Manager).userprincipalname}
                                                                                                                    }
                                                                                                                }
            Connect-MsolService 
            $UserAzureInfo=Get-MsolUser -UserPrincipalName $UserAccount | 
                Select-object @{n='MSOLicense';
                                e={
                                    Switch (($_.licenses).AccountSkuId) {
                                        'rus:SPE_E3' {"E3"}
                                        'rus:SPE_F1' {"F3"}
                                    }
                                  }
                               }
    }
 
    END {
            [PSCustomObject]@{
                samAccountName=$UserADInfo.samAccountName
                userprincipalname=$UserADInfo.userprincipalname
                enabled=$UserADInfo.enabled
                accountExpirationDate=$UserADInfo.accountExpirationDate
                title=$UserADInfo.title
                city=$UserADInfo.city
                Manager=$UserADInfo.Manager
                MSOLicense=$UserAzureInfo.MSOLicense
            }
    
    }
 
}

Hi alyam, welcome to the forum. It appears pretty simple as it is. What are you trying to accomplish?

I don’t like listing a long list in pscustomobject

What’s wrong with that? If you format it a little more nice it even looks quite good. (VSCode does that automatically for you)

[PSCustomObject]@{
    samAccountName        = $UserADInfo.samAccountName
    userprincipalname     = $UserADInfo.userprincipalname
    enabled               = $UserADInfo.enabled
    accountExpirationDate = $UserADInfo.accountExpirationDate
    title                 = $UserADInfo.title
    city                  = $UserADInfo.city
    Manager               = $UserADInfo.Manager
    MSOLicense            = $UserAzureInfo.MSOLicense
}

And since you want to combine results from 2 independend queries you don’t have that many other options. :man_shrugging:t3:

Hello.

If you want your function to work in all cases, you need to ask for a string array parameter, and enclose your “process” statements in a “foreach ($CurrentUser in $UserAccount) {…}”

For the same reason, your “connect-Msolservice” must be in the “begin” bloc.

And for your question, if you don’t want a PSCustomObject, you could use “select-object” on your $UserADInfo object, with a ‘@{name=“”;expression={}}’ for adding MSOLicense property.

The code is not cleaner with this option, but you still have an ADUser object returned by your function, it may be usefull.

And since you use “ValueFromPipelineByPropertyName”, it’s better to use a real Object property name for your parameter. In your case, a real ADUser property like “userprincipalname” probably.

I did not quite understand. can you explain?

Since you use “ValueFromPipelineByPropertyName”, you could use the “|” to call your function like :

get-aduser -filter {UserPrincipalName -like "a*"} | Get-UserADInfo

But, for this to work, the parameter of your function must have the same name of a property of the object return by - for this exemple - get-aduser. For what you want to do, UserPrincipalName seems to be a good choice.

Since you use “ValueFromPipeline”, you could call your function with an array of string before a “|”, like :

@("upn1", "upn2", "upn3") | Get-UserADInfo

In both case, the “begin” code bloc will be execute only one time (with no access to any parameter pass to the function), the process code bloc will be execute for each object/value preceding the “|”, store in the parameter variable, and the “end” code block, only once, at the end.

So, if you don’t want to connect to Msol for each object/value preceding the “|”, you have to put the connect command in the begin bloc.

But, you can also call your function like this :

Get-UserADInfo -UserAccount @(“upn1”, “upn2”, “upn3”)

But, if you call it this way, the “process” code block will be execute only one time, and the value of $UserAccount will be the full array. So, if you want to do the job for each value of the array, the only way is to have :

process {
   foreach ($CurrentUser in $UserAccount) {
      Do what you want using $CurrentUser
   }
}

This will also work when you call your function with the “|”, each execution of “process” bloc will be done with an array containing only one value.

“voila”

Is it clear ?

Hey there Alastor!

Apologies for being a bit nit-picky here but I have a small favor to ask. Would you format so its easier to read ? :slight_smile: Much appreciated!

Some format like that ?

1 Like

Finally, if you want your function to always work, no matter the way you call it, you have to code it this way

Function Get-UserADInfo {
    [CmdletBinding()]
    param(
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
 
        [string[]]  $UserPrincipalName
 
    )
 
    BEGIN {
        Connect-MsolService
     }
 
    PROCESS {
        foreach ($CurrentUser in $UserPrincipalName} {
             Do the job with $CurrentUser variable

             $UserADInfo | select-object *, @{n="MSOLicence";e={$UserAzureInfo.MSOLicense}}
        }
    }

   END {
   }

Don’t forget the [ ] in the parameter type specification, for my last exemple to work.

PS : mandatory have to be $true…

This is incorrect. This is one way to do it, but it’s not the only way.

Another method is to use the $input automatic variable

Function Test-Function {
    $input
}

'a', 'b', 'c' | Test-Function

a
b
c

Even if you accept pipeline input for something else, $input is available

Function Test-Function {
    [cmdletbinding()]
    Param(
        [parameter(ValueFromPipeline)]
        $a
    )

    $a
    $input
}


'a', 'b', 'c' | Test-Function

c
a
b
c

Notice without the process block only the last value was output from $a and that all happened before the output of $Input was seen.

When you do use a process block, $input will be available inside it.

Function Test-Function {
    [cmdletbinding()]
    Param(
        [parameter(ValueFromPipeline)]
        $a
    )

    process{
        $a
        $input
    }

}

'a', 'b', 'c' | Test-Function

a
a
b
b
c
c

and only inside the process block

Function Test-Function {
    [cmdletbinding()]
    Param(
        [parameter(ValueFromPipeline)]
        $a
    )

    begin{$input}

    process{
        $a
    }

    end{$input}
}


'a', 'b', 'c' | Test-Function

a
b
c

Don’t forget about Filter as well.

filter Test-Function {
    $input
}

'a', 'b', 'c' | Test-Function

a
b
c

Nice !

But how $input work when the function have multiple parameters ?

It holds only the items that were piped in.

Function Test-Function {
    [cmdletbinding()]
    Param(
        [parameter(ValueFromPipeline)]
        $a,

        $b
    )
    $a
    $b
    $input
}

'a', 'b', 'c' | Test-Function -b d

c
d
a
b
c

You can see here that even though $a took ‘a’, $b took ‘b’, $input still had the whole object

Function Test-Function {
    [cmdletbinding()]
    Param(
        [parameter(ValueFromPipelineByPropertyName)]
        $a,

        [parameter(ValueFromPipelineByPropertyName)]
        $b
    )

    "a: $a"
    "b: $b"
    "input: $($input)"
}

[PSCustomObject]@{
    a = 'a'
    b = 'b'
} | Test-Function

a: a
b: b
input: @{a=a; b=b}
1 Like

Thank’s a lot ! I didn’t see $input before. :flushed:

1 Like

Filter is another tool in the toolbelt that most have never heard of.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.