Function Process not working as expected

Welcome Forums General PowerShell Q&A Function Process not working as expected

This topic contains 5 replies, has 2 voices, and was last updated by

 
Participant
3 months, 3 weeks ago.

  • Author
    Posts
  • #104600

    Participant
    Points: 0
    Rank: Member

    Greetings,

    I'm having an issue getting a function to work properly. The goal is to compare the output of some JSON data to existing unified groups in Office 365, and if the group already exists, skip it, otherwise, create a new group. The tricky part is that as part of function that creates the group, it prepends "gr-" to the group name. Because the compare function is comparing the original JSON data without the prepended data to Office 365, the compare function has to have the logic to prepend "gr-" on the fly.

    Here is the latest version of the function. There have been other variations, but none so far have worked. There are no errors, but the code does not identify lists that definitely do exist. I am using simple echo statements for the purpose of testing, the actual code will include the function to create a new group.

    # Test variable that cycles through each .json file.
    $jsonFiles = Get-ChildItem -path "c:\tmp\json" -filter *.json | get-content -raw
    $allobjects = $jsonFiles | convertfrom-json
    
    $alreadyCreatedGroup = Get-UnifiedGroup | select alias
    
    # Determine if list already exists in Office 365
    function checkForExistingGroup {
        [CmdletBinding()]
        Param(
            [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
            $InputObject
        )
    
        Process {
                if("gr-$($InputObject.alias)" -in $alreadyCreatedGroup){
                    echo "$($inputobject.alias) exists"
                    } 
                        else{
                            echo "$($InputObject.alias) does not exist"
                        }
                }
            }
    
    $allobjects | checkForExistingGroup
    

    The above code always produces the "does not exist" option for each alias from the JSON data.

    The individual variables appear to be outputting correctly:

    PS> $alreadyCreatedGroup
    
    Alias                     
    -----                     
    gr-jsonoffice365grouptest1
    gr-jsonoffice365grouptest2
    gr-jsonoffice365grouptest3
    
    
    PS> $allobjects.alias
    
    jsonoffice365grouptest3
    jsonoffice365grouptest4
    

    If I run the following on its own:

    "gr-$($allobjects.alias)"
    

    I get the following output:

    gr-jsonoffice365grouptest3 jsonoffice365grouptest4
    

    So on its own it appends the output from the JSON files, but I had hoped by using $InputObject in the function, this would resolve that issue.

  • #104603

    Keymaster
    Points: 1,638
    Helping HandTeam Member
    Rank: Community Hero

    As a note, we'd usually name this "Test-InExistingGroup" or something; confirming to PowerShell's naming conventions is an important part of using PowerShell :).

    Be aware that echo is an alias to Write-Host; using Write-Verbose would actually be beneficial, since you could enable/disable that output by running (or not running) your function with -Verbose.

    Also, the way you're using an out-of-scope reference to $alreadyCreatedGroup is a Very Bad Idea. Functions should NEVER rely on inputs that don't come to them as a parameter.

    The ultimate problem you're having is this: The -in operator checks for the *entire object*. What you've got in $alreadyCreatedGroup is a bunch fo objects, and you're triggering on the Alias property. But what's coming in $inputObject are strings. Basically, you're not comparing apples to apples; you're comparing apples to grocery stores. You're attempting to compare simple strings to an entire object, so it never comes out true. You've selected only the alias property, true, but it's still a property of an object.

    I'd suggest:

    $alreadyCreatedGroup = Get-UnifiedGroup | select -Expand alias
    
    # Determine if list already exists in Office 365
    function Test-ForExistingGroup {
        [CmdletBinding()]
        Param(
            [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
            $InputObject,
    
            [Parameter(Position=0, Mandatory=$true)]
            $ExistingGroups
        )
    
        Process {
                if("gr-$($InputObject.alias)" -in $ExistingGroups) {
                    Write-Verbose "$($inputobject.alias) exists"  
                } else{
                    Write-Verbose "$($InputObject.alias) does not exist"
                }
        }
    }
    
    $allobjects | Test-ForExistingGroup -Verbose -Existing $alreadyCreatedGroup
    

    – Conforming function name
    – Switched to verbose output
    – Extracted the contents of the Alias property into simple strings (using Select -Expand rather than just Select)
    – Existing groups being fed to function as a parameter

  • #104605

    Participant
    Points: 0
    Rank: Member

    Outstanding, that works perfectly! Thank you for your guidance, it is greatly appreciated.

  • #105221

    Participant
    Points: 0
    Rank: Member

    Hello again,

    I am attempting to use the same logic as above and this time it is not working for some reason. I am comparing a list of email addresses in a CSV file to all Azure AD external guest accounts in Office 365 and if it doesn't exist, create it. All external guest accounts will have the email address also as the displayname.

    Here is what I currently have:

    # Get email addresses (the CSV file has a header of externalGuest)
    $Guests = import-csv c:\tmp\externalGuests.csv
    
    # Get existing guest users:
    $alreadyCreatedGuestUsers = Get-AzureADUser -Filter "userType eq 'Guest'" -all $true | select -Expand displayname
    
    $timeFull = $((get-date).toString("yyyy-MM-dd--hh:mm:ss"))
    $timeShort = $((get-date).toString("yyyy-MM-dd"))
    
    filter timestamp {"$(Get-Date -Format yyy/MM/dd--hh:mm:ss): $_"}
    
    function add-ExternalGuests {
        [CmdletBinding()]
        Param(
            [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
            $InputObject
        )
    
        Process {
                 try
                {
                    New-AzureADMSInvitation -ErrorAction continue `
                        -InvitedUserEmailAddress $InputObject.externalGuest `
                        -InvitedUserDisplayName $InputObject.externalGuest `
                        -SendInvitationMessage:$false `
                        -InviteRedirectUrl "https://myapps.microsoft.com" | `
                        select inviteduserdisplayname | `
                        timestamp | `
                        out-file -Append "c:\tmp\logs\$($timeShort)_add-ExternalGuests.log"
                        }
                 catch
                {
                    write-host "Guest NOT created:  $($InputObject.externalGuest)"
                    $ErrorMessage = $_.Exception.Message
                    $ErrorType = $_.Exception.gettype().fullname
                    $ErrorItem = $_.Exception.ItemName
                    write-output "$($timeFull):`r`nError trying to create: $($InputObject.externalGuest);`r`n[ERROR MESSAGE]:  
                     $($ErrorMessage)`r`n[ERROR TYPE]: $($ErrorType) `r`n `
                        $($InputObject.externalGuest) NOT created." `r`n | `
                    out-file -append -filepath "c:\tmp\logs\$($timeShort)_add-ExternalGuests_error.log" 
                    }
            }
    }
    
    function test-ExternalGuests {
        [CmdletBinding()]
        Param(
            [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
            $InputObject,
    
            [Parameter(Position=0, Mandatory=$true)]
            $createdGuests
        )
    
        Process {
                    if($InputObject.externalGuest -notin $createdGuests){
                        # Write-Host "Creating external guest:  $($InputObject.externalGuest)"
                        $InputObject.externalGuest | add-ExternalGuests
                        }
                        else{
                                Write-Host "Guest already exists:  $($InputObject.externalGuest)"
                                write-output "Guest already exists:  $($InputObject.externalGuest)" | `
                                timestamp | `
                                out-file -Append "c:\tmp\logs\$($timeShort)_add-ExternalGuests.log"
                                }
                }
    }
    
    $Guests | test-ExternalGuests -Verbose -createdGuests $alreadyCreatedGuestUsers
    

    The issue is with the test-ExternalGuests if/else logic. I know that 9 out of 10 of the email addresses from $guests exist, and the variables on their own output correctly. When I use -notin, all of them come back as not existing and it attempts to call the add-ExternalGuests function. If I prepend -not at the beginning of the If statement (if(-not $InputObject.externalGuest -in $createdGuests), they all trigger the Else block. If I try -contains, it also triggers the Else block for all. Using -notcontains triggers the add-ExternalGuests function for all 10.

    I'm still experimenting, but I have a deadline approaching to get this to work, so I was hoping someone might have some recommendations on what might help.

    Thank you in advance for your consideration.

  • #105232

    Keymaster
    Points: 1,638
    Helping HandTeam Member
    Rank: Community Hero

    -in, -notin, -contains, and -notcontains are all basically the same thing. You've verified as much :).

    Bugs in PowerShell come from one source: Either a property or a variable doesn't contain what you think it does. But you've no code in place to help figure out what they do, in fact, contain. And you kinda persist in using Write-Host and Write-Output :). You've called -Verbose, but you didn't include any calls to Write-Verbose, so it's useless. Let's fix it up a bit.

    function test-ExternalGuests {
        [CmdletBinding(ConfirmImpact='Low', SupportsShouldProcess=$True)]
        Param(
            [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
            $InputObject,
    
            [Parameter(Position=0, Mandatory=$true)]
            $createdGuests
        )
    
        BEGIN {
          Write-Verbose "Already created: `n $createdGuests `n ---------"
        }
    
        Process {
          Write-Verbose "Testing '$($inputobject.externalguest)'"
                    if($InputObject.externalGuest -notin $createdGuests){
                        Write-Verbose "Creating external guest:  '$($InputObject.externalGuest)'"
                        if ($pscmdlet.shouldprocess("$($InputObject.externalGuest)")) {
                          $InputObject.externalGuest | add-ExternalGuests
                        }
                        } else{
                                Write-Verbose "Guest already exists:  '$($InputObject.externalGuest)'"
                                out-file -Append "c:\tmp\logs\$($timeShort)_add-ExternalGuests.log"
                                }
                }
    }
    
    $Guests | test-ExternalGuests -Verbose -whatif -createdGuests $alreadyCreatedGuestUsers
    

    Notice that I like to put 'single quotes' around my variable values. That way it's more apparent if one of them is empty – I'll see a " and know something's up.

    I'd run this now, and I'd verify that the content of $createdGuests is in fact what I expected – that is, it's a list of strings that correspond to what $InputObject.externalguest would contain. I suspect that $createdGuests doesn't contain the same kinds of information as $InputObject.externalguest, which is why your comparison isn't doing what you think it should.

    Notice that I've also added support for -WhatIf, so you can run this without ACTUALLY creating anything.

  • #105268

    Participant
    Points: 0
    Rank: Member

    Thanks again for the reply! You were right, it was a variable that was the issue. Turns out it was a malformed CSV file.

The topic ‘Function Process not working as expected’ is closed to new replies.