11

I created a custom object in PowerShell. I was able to solve the problem I wanted to solve. I wanted to have an object with two columns, one for the site collection, one for the email.

However, I am wondering if there is an easier solution. Do you have any suggestions?

Here is my code:

$cred = Get-Credential

Connect-PnPOnline "https://tenant.sharepoint.com" -Credentials $cred
$SiteCollections = Get-PnPTenantSite

$object = @()

foreach ($SiteCollection in $SiteCollections) {
    Connect-PnPOnline -Url $SiteCollection.Url -Credentials $cred
    $email = Get-PnPRequestAccessEmails
    Write-Host "Email for $($SiteCollection.Url): $($email)"

    $obj = New-Object System.Object
    $obj | Add-Member -type NoteProperty -name Url -value $SiteCollection.Url
    $obj | Add-Member -type NoteProperty -name Email -value $email
    $object += $obj
}

Write-Output $object
Zoltán
  • 125
  • 1
  • 1
  • 4
  • **See Also**: [How do I create an anonymous object in PowerShell?](https://stackoverflow.com/q/36081372/1366033) – KyleMit Nov 28 '20 at 14:43

3 Answers3

20

Objects can be constructed from a hashtable either with the New-Object cmdlet:

$obj = New-Object -Type PSObject -Property @{
    'Url'   = $SiteCollection.Url
    'Email' = $email
}

or (if you have PowerShell v3 or newer) the [PSCustomObject] type accelerator:

$obj = [PSCustomObject]@{
    'Url'   = $SiteCollection.Url
    'Email' = $email
}

Also, you can simply output the objects inside the loop and collect the entire loop output in a variable like this:

$object = @(foreach ($SiteCollection in $SiteCollections) {
    ...
    New-Object -Type PSObject -Property @{
        'Url'   = $SiteCollection.Url
        'Email' = $email
    }
})

The array subexpression operator (@()) around the foreach loop ensures that the result is an array, even if the loop output is less than 2 objects.

Using a calculated property would be another option:

$object = @(Get-PnPTenantSite | Select-Object Url, @{n='Email';e={
    Connect-PnPOnline -Url $_.Url -Credentials $cred | Out-Null
    Get-PnPRequestAccessEmails
}})
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • 3
    Alternative to wrapping things in `@()`: I prefer to cast things to the `[array]` type, as it is easier for me to spot later on. Such as `[array]$object = ForEach ($SiteCollection in $SiteCollections) {` – TheMadTechnician Nov 06 '17 at 23:32
  • 1
    I thought the whole array issue was in PowerShell v2 and earlier? Wouldn't using `[pscustomobject]` obviate the need to wrap the variable in an array? – Bacon Bits Nov 06 '17 at 23:35
  • 1
    @BaconBits Some issues were mitigated in PowerShell v3, yes, but you still won't magically get an array unless you make it happen. Consider for instance a situation where the loop doesn't return any results. In that case you'd end up with a null value. Trying to invoke a method on a null value would still give you an error, even in PowerShell v3 and newer. Also, I prefer to keep my answers backwards compatible if possible, although this answer doesn't set a good example. I should've used `New-Object` in the 3rd example. – Ansgar Wiechers Nov 07 '17 at 01:03
3

Shortening the code a bit:

$cred = Get-Credential
Get-PnPTenantSite | ForEach-Object {
  Connect-PnPOnline -Url $_.Url -Credentials $cred
  [PSCustomObject] @{
    Url = $_.Url
    Email = Get-PnPRequestAccessEmails
  }
}

The [PSCustomObject] type accelerator only exists in PowerShell 3.0 and later.

Bill_Stewart
  • 22,916
  • 4
  • 51
  • 62
0

Especially if your array is going to get very large, you might want to consider using an arraylist instead of a vanilla array. With a standard array, every time you add data to it, it recreates the whole array, as opposed to just tacking your data on the end. An arraylist simply appends and is much faster for building large arrays.

$outArray = New-Object System.Collections.ArrayList

foreach ($SiteCollection in $SiteCollections) {
    Connect-PnPOnline -Url $SiteCollection.Url -Credentials $cred
    $email = Get-PnPRequestAccessEmails
    Write-Host "Email for $($SiteCollection.Url): $($email)"

    $obj = New-Object System.Object
    $obj | Add-Member -type NoteProperty -name Url -value $SiteCollection.Url
    $obj | Add-Member -type NoteProperty -name Email -value $email
    [void]$outArray.Add($obj)
}
user3723688
  • 47
  • 1
  • 10