I tried to automate creating AD users in my lab environment and came across an interesting issue that I cannot seem to grasp. When I tried using the following code, Powershell threw "New-ADUser : The name provided is not a properly formed account name"
$Attributes = #{
Enabled = $true
ChangePasswordAtLogon = $false
Departent = "MyDepartment"
UserPrincipalNAme = "first.name#domain.lel"
City = "MySite"
DisplayName = "My display name"
EmailAddress = "My#mail.address"
GivenName = "First Name"
Manager = "cbu"
PasswordNeverExpires = $true
SamAccountName = "abc123"
Surname = "Last name"
Title = "Title"
Name = "abc123"
AccountPassword = $("Thishere1234" | ConvertTo-SecureString -AsPlainText -Force)
}
New-ADUser $Attributes
The above example is close to a copy paste from some post that I found using hashtables as a way to pass the information to New-ADUser. Since this did not work, I instead tried the following:
$Attributes = [pscustomobject]#{
Enabled = $true
ChangePasswordAtLogon = $false
Departent = "MyDepartment"
UserPrincipalNAme = "first.name#domain.lel"
City = "MySite"
DisplayName = "My display name"
EmailAddress = "My#mail.address"
GivenName = "First Name"
Manager = "cbu"
PasswordNeverExpires = $true
SamAccountName = "abc123"
Surname = "Last name"
Title = "Title"
Name = "abc123"
AccountPassword = $("Thishere1234" | ConvertTo-SecureString -AsPlainText -Force)
}
$Attributes | New-ADUser
This works just fine. My question is: why would the first hashtable approach not accept the name property but the second PSCustomObject does accept the exact same approach?
As a bonus question, is there any particular reason I cannot use New-ADUser $Attributes but rather I have to pipe $Attributes to New-ADUser? I was under the impression you could use the former too?
When splatting using a hashtable, you need to use the # sign on the cmdlet, not a $:
New-ADUser #Attributes
Related
I have a simple script that I use to create new AD users. The basic script works great and gets me to where I need to be when I create a new user. There is one part that is missing that I would like to have automated so that I wont have to update the info manually. I need to add the address and phone number for a user based on their location. From the code below, I added the if elseif statement. What I have below gives the error - The term 'Austin' is not recognized as the name of a cmdlet, function, script file, or operable program. Obviously the variable $location doesn't work with the script, however, I am not as familar with PowerShell to see where to correct this or if there should be better way to write this out. Should I add Get-ADUser in front of it to pull the user info once its been added? Or add the if, elseif at the end of the script?
#Prompt for user information
$first = Read-Host "First name"
$last = Read-Host "Last name"
$title = Read-Host "Title"
$location = Read-Host "Location (location1,location2)"
$department = Read-Host "Business Practice"
$password = read-host -assecurestring "Password"
if($location = location1) {
Set-ADUser -street "StreetName" -city "City" -state "State" -postalcode "Zip" -officephone "Phone"
}
elseif($location = location2) {
Set-ADUser -street "StreetName" -city "City" -state "State" -postalcode "Zip" -officephone "Phone"
}
#Create new user
$Attributes = #{
Enabled = $true
ChangePasswordAtLogon = $false
UserPrincipalName = $first.substring(0,1)+$last+"#domain.com"
Name = $first + " " + $last
GivenName = $first
Surname = $last
DisplayName = $first + " " + $last
Office = $location
Department = $department
Title = $title
samAccountName = $first.substring(0,1)+$last
emailaddress = $first.substring(0,1)+$last+"#domain.com"
AccountPassword = $password
Path = "OU Path"
}
New-ADUser #Attributes
Set-ADUser needs to know the user for which these properties need to be set through its Identity parameter which is missing
You can simply add these properties to the $Attributes hashtable you use for the New-ADUser cmdlet.
You just need to define some logic as to where these properties come from.
Something like using a switch:
# with '$location' just ask for the city:
$location = Read-Host "City"
# I'm making this up of course
switch ($location) {
'Austin' {
$city = 'Austin'
$state = 'Texas'
$postalcode = '73301'
$officephone = '555-123456'
break
}
'Salt Lake City' {
$city = 'Salt Lake City'
$state = 'Utah'
$postalcode = '84044'
$officephone = '555-654321'
break
}
default { $city = $null }
}
if ($city) {
$Attributes = #{
Enabled = $true
ChangePasswordAtLogon = $false
UserPrincipalName = $first.substring(0,1)+$last+"#domain.com"
Name = $first + " " + $last
GivenName = $first
Surname = $last
DisplayName = $first + " " + $last
Office = $location
Department = $department
Title = $title
samAccountName = $first.substring(0,1)+$last
emailaddress = $first.substring(0,1)+$last+"#domain.com"
AccountPassword = $password
Path = "OU Path"
# these are from the switch() above
City = $city
State = $state
PostalCode = $postalcode
OfficePhone = $officephone
}
New-ADUser #Attributes
}
Or you can create a CSV file like
City,State,PostalCode,OfficePhone
Austin,Texas,73301,555-123456
Salt Lake City,Utah,84044,555-654321
and read that using
$locations = Import-Csv -Path '<PathToTheLocations.csv>'
# with '$location' just ask for the city:
$location = Read-Host "City"
# then get the address info from there based on the given city
$address = $locations | Where-Object { $_.City -eq $location }
if ($address) {
$Attributes = #{
Enabled = $true
ChangePasswordAtLogon = $false
UserPrincipalName = $first.substring(0,1)+$last+"#domain.com"
Name = $first + " " + $last
GivenName = $first
Surname = $last
DisplayName = $first + " " + $last
Office = $location
Department = $department
Title = $title
samAccountName = $first.substring(0,1)+$last
emailaddress = $first.substring(0,1)+$last+"#domain.com"
AccountPassword = $password
Path = "OU Path"
# these are from the CSV
City = $address.City
State = $address.State
PostalCode = $address.PostalCode
OfficePhone = $address.OfficePhone
}
New-ADUser #Attributes
}
Of course, NEVER trust user input from Read-Host and build in some validation checks before creating a new user
I am working on a script creating a new user via PowerShell with user (creator) input. The input I am looking for is for the first name and last name along with some attributes. I would like the samaccountname and the UPN to be auto created from the input. Not sure if this can be done completely but would like to get some input on my current script. I highlighted firstinital as a placeholder to show what I am trying to accomplish.
new-aduser -givenname($givenname = read-host "Input Firstname") -surname($surname = read-host "Input Lastname") -samAccountName ("***firstinitial***"+"$._surname") -userprincipalname "$._surname+"#domain.com" -path "OUName" -whatif
Alrighty thanks for the help below. I was able to do a few more searches and can up with the following. All looks to work except the distingushed name comes up as a single name instead of a space between the first and last name.
#User info entered
$first = Read-Host "First name"
$last = Read-Host "Last name"
$title = Read-Host "Title"
$location = Read-Host "Location"
$department = Read-Host "Business Practice"
$password = read-host -assecurestring "Password"
#Create new user
$Attributes = #{
Enabled = $true
ChangePasswordAtLogon = $false
UserPrincipalName = $first.split(" ")[0]+$last+"#domain.com"
Name = $first+$last
GivenName = $first
Surname = $last
DisplayName = "$first "+" $last"
Office = $location
Department = $department
Title = $title
samAccountName = $first.split(" ")[0] + $last
AccountPassword = $password
}
New-ADUser #Attributes -whatif
You can add this to get the $_.givenName as the first initial:
$gn = (read-host "Input Firstname")
$sn = (read-host "Input Lastname")
new-aduser -givenname $gn -surname $sn -samAccountName $gn.split(" ")[0]+$sn -userprincipalname $sn+"#kfriese.com" -path "OUName" -whatif
Here is a more advanced and robust way to do it: a custom function, that makes use of PowerShell integrated functionality.
It uses attributes that make the parameters mandatory, so user input will automatically be inquired when the function is called. Also a validation attribute to make sure the input is not empty and has no invalid characters (you might want to adjust the regex according to your needs).
The arguments for New-ADUser are passed using splatting. The rest is pretty straight-forward...
function makeuser {
param(
[Parameter(Mandatory, Position = 0)]
[ValidatePattern("[a-z]+")]
[string]$GivenName,
[Parameter(Mandatory, Position = 1)]
[ValidatePattern("[a-z]+")]
[string]$Surname
)
$params = #{
GivenName = $GivenName
Surname = $Surname
SamAccountName = $GivenName[0] + $Surname
UserPrincipalName = $Surname + "#kfriese.com"
Path = "OUName"
}
New-AdUser #params
}
To call the function, just type (parameter values will be inquired automatically)
makeuser
Or specify the values explicitly:
makeuser -GivenName Foo -Surname Bar
# or
makeuser foo bar
I'm pulling some user info from a .csv to create new users,
I've splatted the New User Params at the suggestion of someone here
but I'm getting this error
New-ADUser : Cannot convert 'System.String' to the type 'System.Management.Automation.SwitchParameter' required by parameter
'Confirm'.
At C:\Users\Administrator\Documents\GitHub\cyclone-internal-user-sync-1\Bamboo Attributes form a csv.ps1:68 char:28
+ New-ADUser #NewUserParms
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [New-ADUser], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.ActiveDirectory.Management.Commands.NewADUser
I have no idea what this is haha, I've tried adding an erroraction stop to the new-aduser but that didn't have any effect
I have added trims and a section to remove spaces from usernames. to deal with multipart names such as Van der.... etc
#Bamboo Attributes from a .csv
#Enter a path to your import CSV file
$ADUsers = Import-csv 'path'
#Bamboo Attributes from a .csv
#Enter a path to your import CSV file
$ADUsers = Import-csv 'C:\Users\Administrator\Documents\GitHub\cyclone-internal-user-sync-1\documentation\SampleUserAttributes.csv'
#$apiRequest = Get-Content -Raw -Path C:\Users\alexh\Documents\GitHub\cyclone-internal-user-sync-1\cyclone-internal-user-sync-1\fake-api-query.json | ConvertFrom-Json
foreach ($User in $ADUsers) {
$firstName = $user.FirstName.Trim()
$surname = $user.Surname.Trim()
$vaildUsernameFormat = "[^a-zA-Z_.]" # identifies anything that's _not_ a-z or underscore or .
$username = "($firstName'.'$surname)" -replace $vaildUsernameFormat, '' #removes anything that isn't a-z
$DefaultPassword = 'Pa$$w0rd'
$NewUserParms = #{
'samAccountName' = $username;
'Name' = "$firstname $surname";
'DisplayName' = "$firstname $surname";
'UserPrincipalName' = "$username#domain.com";
'GivenName' = $firstname;
'Surname' = $surname;
'EmailAddress' = $User.Email;
'AccountPassword' = (ConvertTo-SecureString $DefaultPassword -AsPlainText -Force);
'Enabled' = $true;
'Path' = "OU=Users,DC=domain,DC=com";
'co' = $User.Country;
'company' = $User.CompanyName;
'countryCode' = $user.countryCode;
'department' = $user.OrgDepartmentName;
'Employeeid' = $user.EmployeeId;
'exstentionAttribute1' = $user.ExstentionNumber;
'ipPhone' = $user.ExstentionNumber;
'L' = $user.location;
'mail' = $user.Email;
'mobile' = $user.Mobile;
'Manager' = $user.Manager;
'physicalDeliveryOffice' = $user.Branch;
'postalCode' = $user.PostalCode;
'postOfficeBox' = $user.PostOfficeBox;
'proxyAddresses' = $user.ProxyEmail;
'scriptPath' = $user.scriptPath;
'st' = $user.StreetName;
'Title' = $user.Title
}
write-host "$username this is username value"
#Check if the user account already exists in AD
if (Get-ADUser -F {
sAMAccountName -eq $username
}) {
#If user does exist, output a warning message
Write-Warning "A user account $username has already exist in Active Directory."
}
else {
#If a user does not exist then create a new user account
New-ADUser #NewUserParms
}
}
I've removed some of the user attributes just to make this a bit smaller.
here is the.csv as well in case I've messed something up there
link to .csv file on git
A little known fact about PowerShell is that you don't need to use the whole parameter name. You can use the partial name and as long as it matches only one parameter name, that's what PowerShell assumes you mean.
The one it's choking on is this:
'co' = $User.Country;
If you look at the documentation for New-ADUser, it does not have a parameter called co. So PowerShell assumes it's a partial match to a known parameter, and the closest match is -Confirm. And the value in $User.Country doesn't make any sense for the -Confirm parameter, so it throws the error.
You will have to use the -OtherAttributes parameter to set all the other attributes that New-ADUser doesn't have a dedicated parameter for:
$NewUserParms = #{
...
'OtherAttributes = # {
'co' = $User.Country;
'exstentionAttribute1' = $user.ExstentionNumber;
...
}
...
}
As commented in this and previous questions, you are using New-ADUser $NewUserParms, where it should be New-ADUser #NewUserParms.
Also, to catch errors (you did add -ErrorAction Stop), you need to put that inside a try{..} catch{..} block.
I would also change the syntax you use for the -Filter parameter. Instead of using a scriptblock syntax {something -eq someotherthing}, you should create a string like "something -eq 'someotherthing'"
Try:
# define some 'constants'
$csvFile = 'X:\Folder\NewUsers.csv' # Enter a path to your import CSV file
$invalidCharacters = '[^a-z_.]' # identifies anything that's _not_ a-z or underscore or .
$DefaultPassword = 'Pa$$w0rd'
$securePassword = ConvertTo-SecureString -String $DefaultPassword -AsPlainText -Force
# read the input csv and loop through
Import-Csv -Path $csvFile | ForEach-Object {
$firstName = $_.FirstName.Trim()
$surname = $_.Surname.Trim()
$username = ('{0}.{1}' -f $firstName, $surname) -replace $invalidCharacters
# test if a user with that name already exists
$user = Get-ADUser -Filter "SamAccountName -eq '$username'" -ErrorAction SilentlyContinue
if ($user) {
Write-Warning "A user account $username already exist in Active Directory."
}
else {
Write-Host "Creating user $username"
$NewUserParms = #{
'SamAccountName' = $username
'Name' = "$firstname $surname"
'DisplayName' = "$firstname $surname"
'UserPrincipalName' = "$username#domain.com"
'GivenName' = $firstname
'Surname' = $surname
'EmailAddress' = $_.Email
'AccountPassword' = $securePassword
'Enabled' = $true
'Path' = "OU=Users,DC=domain,DC=com"
# add other properties to set from the CSV here.
# make sure you get the parameter data types correct and always check here:
# https://learn.microsoft.com/en-us/powershell/module/addsadministration/new-aduser?view=win10-ps#parameters
# switch parameters for the cmdlet can also be entered with a value $false or $true
}
try {
# '-ErrorAction Stop' ensures that also non-terminating errors get handled in the catch block
New-ADUser #NewUserParms -ErrorAction Stop
}
catch {
# something bad happened. Change 'Write-Warning' into 'throw' if you want your script to exit here
# inside a catch block, the '$_' automatic variable represents the actual exception object.
Write-Warning "Could not create account $username. $($_.Exception.Message)"
}
}
}
I'm trying to create a new AD user through a script. Unfortunately there are several unusual attributes of the account I need to set. I tried to use the -OtherAttributes parameter, but it didn't work, Powershell said they are unknown parameters. I'm talking about these attributes:
Anyone knows how to set these parameters?
Have you tried it with Set-AdUser?
Set-ADUser $Name –replace #{extensionAttribute5="Company"}
or with a hash table
$Attributes = #{
Name = "Test User"
SamAccountName = "Testuser"
Enabled = $true
Path = "OU=Users,DC=Domain,DC=local"
AccountPassword = "1234567"
ChangePasswordAtLogon = $False
GivenName = "Test"
EmployeeID = "1234"
Surname = "Test User"
DisplayName = "Test User"
Initials = "TU"
Description = "Test User for tests"
UserPrincipalName = "Test#domain.local"
Office = "Test Office"
Company = "Test Company"
Department = "Test Department"
Division = "Test Division"
EmailAddress = "test#test.local"
OtherAttributes = #{
extensionAttribute4 = "Test Data"
extensionAttribute5 = "Test Data"
}
}
New-ADUser #Attributes -PassThru
I stopped over at Code Review, asking how I could streamline a script and was advised to use a hashtable as it would clean up the code. I was given a very basic example but it wasn't plug-and-play. I've worked up some basic code but it's not doing what I think it should. Knowing the Code Review folks aren't there for support like this, here i am, looking for help with combining a variable from a CSV and a hashtable. I'll leave sample data from my CSV and the Powershell code below.
Sample CSV:
Student First Name,I,Student Last Name,Other ID,Stu Access Login,Student's School Email,School,Grad Year
Johosofat,L,Smith,999999,smithjoh000,smithjoh000#mydomain.org,30,2017
Tome,M,Smith,999998,smithtom000,smithtom000#mydomain.org,40,2021
Sample Powershell:
# Testing simple hash table
$SchoolCodes = #{
20 = "Exeter Township Senior High"
30 = "Exeter Township Junior High"
40 = "Lorane Elementary School"
50 = "Jacksonwald ES"
70 = "Reiffton School"
90 = "Owatin Creek Elementary School"
}
# CSV file being imported.
$CsvFile = "$env:USERPROFILE\Downloads\SampleData.csv"
# Import the contents of the CSV file.
$Users = Import-Csv -Path "$CsvFile"
# Loop through each line of the CSV, creating variables for each field.
ForEach ($User in $Users) {
# Creating the basic variables.
$FirstName = $User.'Student First Name'
$MiddleInitial = $User.'I'
$LastName = $User.'Student Last Name'
$ADUserName = $User.'Stu Access Login'
$StudentID = $User.'Other ID'
$GradYear = $User.'Grad Year'
$CapFInitial = $FirstName.substring(0,1).ToUpper()
$MInitial = $MiddleInitial.substring(0,1).ToLower()
$LInitial = $LastName.substring(0,1).ToLower()
$Password = "$CapFInitial$MInitial$LInitial" + "#" + "$StudentID"
$SchoolCode = $SchoolCodes[$User.School]
If (-Not(Get-ADUser -Filter {SamAccountName -eq $ADUserName})) {
Try {
# Create user.
New-ADUser `
-Name "$FirstName $LastName" `
-SamAccountName "$ADUserName" `
-GivenName "$FirstName" `
-Initials "$MiddleInitial" `
-Surname "$LastName" `
-DisplayName "$FirstName $MiddleInitial. $LastName" `
-UserPrincipalName "$ADUserName#mydomain.k12.pa.us" `
-EmailAddress "$ADUserName#mydomain.k12.pa.us" `
-AccountPassword (ConvertTo-SecureString $Password -AsPlainText -Force) `
-Enabled $false `
-PasswordNeverExpires $true `
-CannotChangePassword $true `
-Path "OU=$GradYear,OU=Students,OU=$SchoolCode,OU=accounts,DC=academic,DC=mydomain,DC=k12,DC=pa,DC=us" `
-WhatIf
}
Catch {
Write-Error "[ERROR] Can't create user [$($ADUserName)] : $_"
}
}
}
My issue:
The script ultimately errors out because of the $SchoolCode variable being set to null, I think. I'm wanting the script to find the number (code) from the school field in the CSV and match that to the name which ends up being an OU in AD - where the User Object will get created. Basically, the code tries to create the User Object in "CN=Tome Smith,OU=2021,OU=Students,OU=,OU=accounts,DC=academic,DC=exeter,DC=k12,DC=pa,DC=us" which shows the $SchoolCode variable is either blank or otherwise not getting set correctly.
As I mentioned in a comment, we're thinking of adding other static data to the hashtable as a (nested?) hashtable. Here's an example of what we're thinking about. As time goes by, the list of AD groups may grow.
Example of the nested hashtable:
$SchoolCodes = #{
20 = #{
Name = "Exeter Township Senior High"
ADGroup1 = "Students"
ADGroup2 = "Secondary Students"
}
30 = #{
Name = "Exeter Township Junior High"
ADGroup1 = "Students"
ADGroup2 = "Secondary Students"
}
40 = #{
Name = "Lorane Elementary School"
ADGroup1 = "Students"
ADGroup2 = "K4 Students"
}
50 = #{
Name = "Jacksonwald ES"
ADGroup1 = "Students"
ADGroup2 = "K4 Students"
}
70 = #{
Name = "Reiffton School"
ADGroup1 = "Students"
ADGroup2 = "Secondary Students"
}
90 = #{
Name = "Owatin Creek Elementary School"
ADGroup1 = "Students"
ADGroup2 = "K4 Students"
}
}
I'm scouring the web and trying to get a better understanding of hashtables. If I can wrap my head around it, nesting them would be my next step.
Unless you're re-using the data, it's not important to turn it into a hashtable. Also, the error is in accessing the $SchoolCodes value. For some reason, the accessor isn't working with a [String], but does work when you cast to an [Int]
Sample dataset:
Student First Name,I,Student Last Name,Other ID,Stu Access Login,Student's School Email,School,Grad Year
Johosofat,L,Smith,999999,smithjoh000,smithjoh000#mydomain.org,30,2017
Tome,M,Smith,999998,smithtom000,smithtom000#mydomain.org,40,2021
Code:
#requires -Version 3
$SchoolCodes = #{
20 = "Exeter Township Senior High"
30 = "Exeter Township Junior High"
40 = "Lorane Elementary School"
50 = "Jacksonwald ES"
70 = "Reiffton School"
90 = "Owatin Creek Elementary School"
}
# CSV file being imported.
$CsvFile = "$env:USERPROFILE\Downloads\SampleData.csv"
# Import the contents of the CSV file.
$Users = Import-Csv -Path "$CsvFile"
# Loop through each line of the CSV, creating variables for each field.
ForEach ($User in $Users)
{
[String]$LoginName = $User.'Stu Access Login'
If (-not (Get-ADUser -Filter {SamAccountName -eq $LoginName}))
{
$FirstName = $User.'Student First Name'
$LastName = $User.'Student Last Name'
$Params = #{
Name = "$FirstName $LastName"
SamAccountName = $LoginName
GivenName = $FirstName
Initials = $User.I
Surname = $LastName
DisplayName = "$FirstName $($User.I) $LastName"
UserPrincipalName = "$LoginName#mydomain.k12.pa.us"
EmailAddress = "$LoginName#mydomain.k12.pa.us"
AccountPassword = ConvertTo-SecureString -String (
'{0}{1}{2}#{3}' -f #(
$FirstName[0].ToString().ToUpper(),
$User.I[0].ToString().ToLower(),
$LastName[0].ToString().ToLower(),
$User.'Other ID')) -AsPlainText -Force
Enabled = $False
PasswordNeverExpires = $True
CannotChangePassword = $True
Path = 'OU={0},OU=Students,OU={1},OU=accounts,DC=academic,DC=mydomain,DC=k12,DC=pa,DC=us' -f #(
$User.'Grad Year',
$SchoolCodes[[Int]$User.School])
WhatIf = $True
}
Try {New-ADUser #Params}
Catch {Write-Error "[ERROR] Can't create user [$LoginName] : $_"}
}
}