Passing multiple parameters to a function and returning multiple values

Hi All,

I am trying to rewrite an existing program to use a function since I need to run the same steps more than once. I am running into issues passing values in and out of the function. My input file has three columns IDNum,UPN,Role. I am updating the script variables and creating a list of UPNs for each Role. I keep getting

 Compare-Object : Cannot bind argument to parameter 'DifferenceObject' because it is null.
At C:\Util\Update-Roles.ps1:79 char:86
+ ... t -ReferenceObject $currentGroupUsers -DifferenceObject $updatedUsers
+                                                             ~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Compare-Object], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.CompareObjectCommand 

I have tried casting the parameters in the prototype using string, array

function name([type]varname1,[type]varname2){
}

as well as

function name() {
Param(
[type]$varname1,
[type]$varname2
)

}

Nothing has worked to get values into the function.

$groupAdmin = "Admin-Group"
$groupAdvisor =  "Advisor-Group"
$updatedUserFile = "D:\<path>\update.csv"

$csvAdmin = ""
$csvAdvisor = ""

$adminRemoved = 0
$adminAdded = 0
$advisorRemoved = 0
$advisorAdded = 0

 function updateGroup($csvUsers, $groupName) {

    $usersAdded = 0
    $usersRemoved = 0

    $updatedUsers = ForEach ($user in $csvUsers) {
        # Get AD user object based on UPN and add object to group
        Get-ADUser -Filter {userPrincipalName -eq $user } -Properties DistinguishedName
    }

    # Build list of current group members
    $currentGroupUsers = (Get-ADGroup $groupName -Properties member).member

    # Create file with differences between current users and users from user file  
    $modGroup = Compare-Object -ReferenceObject $currentGroupUsers -DifferenceObject $updatedUsers

    # Add/Remove based on difference between current and updated users list
    $modGroup | foreach {

        # Remove uses from group
        if ($_.sideindicator -eq '<='){
            $DN = $_.InputObject
            $usersRemoved++
            Remove-ADGroupMember -Identity $groupName -Members "$DN" -Confirm:$false
        }

        # Add users to group
        if ($_.sideindicator -eq '=>'){
            $DN = $_.InputObject
            $usersAdded++
            Add-ADGroupMember -Identity $groupName -Members "$DN"
        }
    }
    return $usersRemoved, $usersAdded
} 

 # Build sorted membership lists from input file
Import-Csv $updatedUserFile | ForEach-Object {
    $user = $_."UPN"
    $user = $user.trim()
    $user = $user.Trim('"')

    switch ($_.Role) {
        admin {
            $script:csvAdmin += $user
        }
        advisor {
            $script:csvAdvisor += $user
        }
    }
}

$returnedAdminCnt = updateGroup $csvAdmin $groupAdmin;
$adminRemoved = $returnedAdminCnt[0];
$adminAdded = $returnedAdminCnt[1];

$returnedAdvisorCnt = updateGroup $csvAdvisor $groupAdvisor;
$advisorRemoved = $returnedAdvisorCnt[0];
$advisorAdded = $returnedAdvisorCnt[1];

Thanks in advance

Chris,
Welcome back to the forum. :wave:t4: … long time no see. :wink:

It might be a better way to start new from scratch instead of trying to improve or migrate old and potentially low quality code. :wink:

The error message is pretty obvious, isn’t it? :smirk: To check your variables you should use Write-Debug and output the variables to the console during runtime. How do you populate the values you pass as parameter $csvUsers?

What did you use as [type]? And if you want to use an array it should be [type[]] instead.

I urgently recommend to read

… and write your code much more verbose.

The array you create for the variable $updatedUsers is an array of objects with properties while the array you create for the variable $currentGroupUsers is just strings. They should be at least of the same type and when you use objects you should specify the property you want to compare on.

1 Like

Hi Olaf,

Thanks for the feedback.

I tried type casting as string and as an array. I even used .getType to identify the right type plus moved my Get-ADUser out of the function and passed in an array of objects. Ultimately I didn’t get it to work which is why I left the type off in my current code. Inside the function $csvUsers and $groupName are null. The code works if it’s inline. If I simplify it I am not seeing output

 function updateGroup($csvUsers, $groupName) {

Write-Debug $csvUsers
Write-Debug $groupName

    $usersAdded = 0
    $usersRemoved = 0
 
     return $usersRemoved, $usersAdded
} 

$returnedAdminCnt = updateGroup $csvAdmin $groupAdmin;

Do you have an example of passing in a list of UPNs and a string to a function?

Using the same approach again and again and expecting different results is pointless.

Input data:

$UPNList = @'
UPN
John@beatles.co.uk
George@beatles.co.uk
Paul@beatles.co.uk
Ringo@beatles.co.uk
'@ |
    ConvertFrom-Csv

Function:

function Show-SingleValue {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true)]
        [Alias('UserPrincipalName', 'UPN', 'Name')]
        [string[]]
        $User,
        [Parameter(Mandatory = $true)]
        [string]
        $ADGroup
    )

    process {
        foreach ($SingleUser in $User) {
            [PSCustomObject]@{
                User  = $SingleUser
                Group = $ADGroup
            }
        }
    }
}

Sample use case 1 providing values directly to the parameters:

Show-SingleValue -User $UPNList.UPN -ADGroup SupiDupiAdGroup

Sample use case 2 using pipeline:

$UPNList | Show-SingleValue -ADGroup BestAdGroupEver
1 Like