New Bulk ADUser

I wish to do a bulk import .csv and then do a New-ADUser. I have this:

$secpass = Read-Host "Password" -AsSecureString 
Import-Csv .\SVCAccounts_test.csv |
foreach {
  $name = "$($_.DisplayName)"
   
 New-ADUser -Name $name -DisplayName $($_.DisplayName) `
    -SamAccountName $($_.SamAccountName) `
    -Department $($_.Department) `
    -Description $($_.Description) `
    -EmailAddress $($_.mail) `
    -AccountPassword $secpass -Path "OU=ServiceAccts,DC=corp,DC=com" `
    -Enabled:$true
    -PasswordNeverExpires:$true
    -otherAttributes @{'otherMailbox'=$_.otherMailbox}
   
}

There are errors with it however, balking of parameters that don’t exist. Please tell me what’s wrong with my above code.

…but I also have this code because it seems to allow me to work with the ‘otherMailbox’ parameter of Set-ADuser:

$User = Import-Csv -Path .\Manager.csv | Set-ADUser -Identity $User -EmailAddress $User.mail -Department $User.Department -Add @{otherMailbox=$User.OtherMailbox}

How do I implement a bulk New-ADUser as well as set some of it’s non-default parameters such as ‘otherMailbox’ in one piece of code?

thank you

Not a fan of the accent characters, I prefer splatting. You can just pipe the new user to Set-AdUser, something like this:

$secpass = Read-Host "Password" -AsSecureString 
Import-Csv .\SVCAccounts_test.csv |
foreach {
    $newUserParams = @{
        Name                 = $_.DisplayName
        DisplayName          = $_.DisplayName
        SamAccountName       = $_.SamAccountName
        Department           = $_.Department
        Description          = $_.Description
        EmailAddress         = $_.mail
        AccountPassword      = $secpass
        Path                 = "OU=ServiceAccts,DC=corp,DC=com"
        Enabled              = $true
        PasswordNeverExpires = $true
    }

    $setUserParams = @{
        Add = @{'otherMailbox'=$_.otherMailbox}
    }

   
    New-ADUser @newUserParams | 
    Set-ADUser @setUserParams
   
}

Pre-validate your attribute, avoiding the null values like the code behind:

OtherAttributes = if(!([string]::IsNullOrEmpty($_.otherMailbox))){@{otherMailbox = $_.otherMailbox}}else {@{otherMailbox = 'NA'}}

I used this link as a base:
https://stackoverflow.com/questions/50319539/new-aduser-argument-otherattributes

Very clean approach Rob, thank you.

Since most of parameters for New-ADUser accept pipeline by property name, you could take advantage of that approach as well. I have not tested this, but don’t see why it wouldn’t work since we are declaring values for all parameters that do not accept pipeline input.

$secpass = Read-Host "Password" -AsSecureString 
Import-Csv .\SVCAccounts_test.csv | New-ADUser -AccountPassword $secpass -PasswordNeverExpires $true -Path "OU=ServiceAccts,DC=corp,DC=com" -OtherAttributes @{'otherMailbox'=$_.otherMailbox} -Verbose

L-Bo, I’m trying ton understand your premise. you say most parameters of New-ADUser accept pipeline input (assuming this includes all of the one I’ll be needing) but then you state your one-liner would most likely work because the (same?) parameters do not accept pipeline input?

Sorry, I lost your reasoning.

Jeff,

  Most of the parameters have pipeline support by property name. SamAccountName,Department,Description, etc all accept pipeline input. (get-help is useful for determining what does and doesn't accept pipeline input) 

An example of a parameter that doesn’t accept pipeline input is “OtherAttribute” which is why it is defined in the command, where “description” isn’t. -Path as well as -PasswordNeverExprires are also capable of accepting pipeline input, but since it was statically defined in your original script I made an educated guess that it wasn’t included in your csv file.
You can use the below command to get a list of all the parameters that accept pipeline input for the New-ADUser cmdlet

Get-Help New-ADUser -Parameter * | ? {$_.pipelineInput -match 'true'} | ft -AutoSize

I was simply demonstrating that although Splatting is useful, and an approach I often take, why NOT take advantage of the great pipeline support that is built right into the cmdlet? Its 6 in one, half a dozen in the other.

thanks L-Bo that’s clearer.

So, during this part of the pipeline approach

Import-Csv .\SVCAccounts_test.csv 
…on the other side of the pipeline (not including the calculated property) will automatically see the comma delimited properties that I have, and then set those in the New-User statement?

I don’t actually have to define each and every one ( the 3, etc. you mention and that I have in my csv) on the other side of the pipeline?

“accept Pipeline by Input” does it for me?

NOTE: I tested your one-liner but get an error and the new Users are not created:

New-ADUser : Cannot validate argument on parameter 'OtherAttributes'. The argument is 
null or an element of the argument collection contains a null value.

Just want to make sure I understand that the problem was\is just how to call Set-ADUser. If your CSV has NULL values for some values, this can be handled with splatting as well to dynamically build the parameters:

$secpass = Read-Host "Password" -AsSecureString 
Import-Csv .\SVCAccounts_test.csv |
foreach {
    $newUserParams = @{
        Name                 = $_.DisplayName
        DisplayName          = $_.DisplayName
        SamAccountName       = $_.SamAccountName
        Department           = $_.Department
        Description          = $_.Description
        EmailAddress         = $_.mail
        AccountPassword      = $secpass
        Path                 = "OU=ServiceAccts,DC=corp,DC=com"
        Enabled              = $true
        PasswordNeverExpires = $true
    }

 
    $setUserParams = @{
        Department = $_.Department
    }

    #Only if the otherMailbox has a value, add the "Add" param
    If ( $_.otherMailbox ) {
        $setUserParams.Add('Add', @{'otherMailbox'=$_.otherMailbox})
    }

    New-ADUser @newUserParams | 
    Set-ADUser @setUserParams
   
}

Like Rob pointed out, its better to do splatting here. When your input is subjected for changes, its always good to “See failures ahead” and code. You can do a lot of error handling here.

Rob,

In your second example, you have

$setUserParams = @{

Department = $_.Department

}

…but you already had it defined in the $newUserParams array. Not sure why it would need here.

Jeff,

Yes, ValueFromPipelineByPropertyName "does it for you" so to speak. If you feed the cmdlet an object with property names that match it's parameters then you do not need to define them. Keep in mind that the headers of your csv file (it's "properties") must match the corresponding parameter exactly for ValueFromPipelineByPropertyName to work. I wasn't suggesting this is the best way to achieve this for your purposes, I am simply providing an example that utilizes a slightly different approach and yields the same results. If the data in the .csv file is "dirty" (null values, formatting issues, etc.) then the pipeline is probably not the best approach.

L-Bo, yep got it thanks again for the additional clarification.

@Jeff-Taylor I just don’t want to pipe nothing to Set-ADUser, if I know Department will always be there but otherMailBox could be NULL I won’t get an error sending nothing to Set-ADUser. So, the code you referenced is a static reference. If the initial problem for otherMailBox was that certain users have a NULL value, you could get rid of Set-ADUser all together and use the splat $newUserParams to add otherAttributes like you were doing in your initial post.