Search code examples
powershellforeachactive-directory

Add a member to one or more Active Directory groups with PowerShell


I've imported users and groups into my AD.

What I'm trying to achieve is add an user to multiple groups.

I mean, user01 be part of It,Security,Admin groups.

In the import of users I have used a CSV file which in resume looks like this:

Username Group
-------- ------
user01   It;Security;Admin
user02   It
user03   Finance;Hr

The import code works, a foreach to create all users from the csv, same as the group import.

But I'm trying another foreach to do these Memberships, I'm using Add-ADPrincipalGroupMembership to achieve this, and I have this code so far:

But it just assign the user to the first group in the csv, not all under Group Key. i.e user01 and user02 are part of "It" only and user03 is part of "Finance" only.

What am I doing wrong or missing guys?

EDIT

In my script what I do first is defining my csv. Which I separate the values using ';' as mentioned.

$Csvfile = "path\to\file\users.csv"
$Users = Import-Csv $Csvfile

Then I do a foreach loop to check the file and create the user if doesn't exist.

# Loop through each user
foreach ($User in $Users) {
    $GivenName = $User.'First name'
    $Surname = $User.'Last name'
    $DisplayName = $User.'Display name'
    $SamAccountName = $User.'Username'
    $UserPrincipalName = $User.'User principal name'
    $OU = $User.'OU'
    $Email = $User.'E-mail'
    $AccountStatus = $User.'Account status'
    $Group = $User.'Group'

    # Check if the user already exists in AD
    $UserExists = Get-ADUser -Filter { SamAccountName -eq 
    $SamAccountName } -ErrorAction SilentlyContinue

    if ($UserExists) {
        Write-Warning "User '$SamAccountName' already exists in Active Directory."
        continue
    }

    # Create new user parameters
    $NewUserParams = @{
        Name                  = "$GivenName $Surname"
        GivenName             = $GivenName
        Surname               = $Surname
        DisplayName           = $DisplayName
        SamAccountName        = $SamAccountName
        UserPrincipalName     = $UserPrincipalName
        Path                  = $OU
        EmailAddress          = $Email
        AccountPassword       = (ConvertTo-SecureString "P@ssw0rd1234" -AsPlainText -Force)
        Enabled               = if ($AccountStatus -eq "Enabled") { $true } else { $false }
        ChangePasswordAtLogon = $true # Set the "User must change password at next logon" flag
    }

    try {
        # Create the new AD user
        New-ADUser @NewUserParams
        Write-Host "User $SamAccountName created successfully." -ForegroundColor Cyan
    }
    catch {
        # Failed to create the new AD user
        Write-Warning "Failed to create user $SamAccountName. $_"
    }
}

And after this is when I'm trying to do a foreach loop again and assign memberships

$Users | ForEach-Object {
    $addADPrincipalGroupMembershipSplat = @{
        MemberOf = $_.Group.Split(';', [System.StringSplitOptions]::TrimEntries)
        Identity = $_.Username
    }

    try {
        Add-ADPrincipalGroupMembership @addADPrincipalGroupMembershipSplat
    }
    catch {
        Write-Warning $_
    }
}

The whole code runs and shows me that each user in my file already exists in the AD, but when it gets to the Add-ADPrincipalGroupMembership loop it shows me Null Warning in the MemberOf.

WARNING: Cannot validate argument on parameter 'MemberOf'. The argument is null, empty, or an element of the argument collection contains a null value.

Solution

  • Presumably, the CSV you have in question is exactly how your CSV looks like which explains the issue:

    But it just assign the user to the first group in the csv, not all under Group Key

    You're using , as the group delimiter while CSV (comma separated values) uses the same delimiter character by default, thus you're losing data:

    @'
    Username,Group
    user01,It,Security,Admin
    user02,It
    user03,Finance,Hr
    '@ | ConvertFrom-Csv
    
    # Outputs:
    
    Username Group
    -------- -----
    user01   It
    user02   It
    user03   Finance
    

    The first step to solve the problem is to use a different delimiter for your groups, for example ;:

    Username,Group
    user01,It;Security;Admin
    user02,It
    user03,Finance;Hr
    

    After doing this, the next problem you will have is that you can pass a single string semi-colon delimited to the -MemberOf parameter, it needs to be an array of groups which leads to the use of String.Split method over the .Group property of each object:

    Import-Csv path\to\thecsv.csv | ForEach-Object {
        $addADPrincipalGroupMembershipSplat = @{
            MemberOf = $_.Group.Split(';')
            Identity = $_.Username
            # AD Cmdlets don't require -ErrorAction,
            # their default behavior is to throw terminating errors invariantly
        }
    
        try {
            Add-ADPrincipalGroupMembership @addADPrincipalGroupMembershipSplat
        }
        catch {
            # Error handling here
            Write-Warning $_
        }
    }