I've got a function that checks AD before attempting to "choose" a username; it'll start by assuming first initial+last name, and see if that's already in AD.
If it is, it'll add letters from the first name until it finds an unused username. If it exhausts all letters in the first name, it'll tack an incrementing number on the end (i.e. jdoe, jodoe, johdoe, johndoe, johndoe1, johndoe2, etc.):
Note: this assumes you have the first name as $FirstName and the last name as $LastName
When attempting to run script then I got the following the error message:
Get-ADUser : Cannot find an object with identity: 'JDoe' under: >'DC=contoso,DC=com'.
At line:18 char:31
+ $usernameExists = Get-ADUser <<<< $username -ErrorAction >SilentlyContinue | format-wide IsValid
+ CategoryInfo : ObjectNotFound: (JDoe:ADUser) [Get-ADUser], >ADIdentityNotFoundException
+ FullyQualifiedErrorId : Cannot find an object with identity: 'JDoe' >under: >'DC=contoso,DC=com'.,Microsoft.ActiveDirectory.Management.Commands.GetADUser
Setting username as JDoe
I am assuming there is a problem related to the IsValid parameter?
$firstname_auto = $firstname.ToLower()
$lastname_auto = $lastname.ToLower()
$FirstNameLength = ($firstname_auto | Measure-Object -Character).Characters
$letterCount = 0
$username = ''
#Try to spell out the entire first name until we can get a unique username
do {
$letterCount++
$usernameExists = $false
$username = "$($firstname_auto.Substring(0,$letterCount))$($lastname_auto)"
$usernameExists = Get-ADUser $username -ErrorAction SilentlyContinue | format-wide IsValid
if ($usernameExists -eq $true) {
Write-Verbose "$username exists in AD. Trying again."
}
} until (($usernameExists -eq $false) -OR ($letterCount -eq $FirstNameLength))
#If we've used all the letters in the first name, and the username still isn't unique,
#start counting until we find one that is.
if ($usernameExists -eq $true) {
$usernameNumber = 0
Write-Verbose "Exhausted all non-numeric options! Trying with numbers."
do {
$usernameNumber++
$usernameExists = $false
$username = "$($firstname_auto.Substring(0,$letterCount))$lastname_auto$usernameNumber"
$usernameExists = Get-ADUser $username -ErrorAction SilentlyContinue | format-wide IsValid
if ($usernameExists -eq $true) {
Write-Verbose "$username already exists in AD. Trying again."
}
} until ($usernameExists -eq $false)
}
Write-host "Setting username as $username" -foregroundcolor Green
Related
Goal: Use a script to add 3 workstations to all users.
Problem: Receiving error thats says variable's format is invalid. $finalworkstations.gettype() brings up null-valued expression.
Receive an error:
Set-ADUser : The format of the specified computer name is invalid
At \\pathwaystuff\file.ps1:37 char:9
+ Set-ADUser $logonname -LogonWorkstations $finalworkstations
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (tester:ADUser) [Set-ADUser], ADException
+ FullyQualifiedErrorId : ActiveDirectoryServer:1210,Microsoft.ActiveDirectory.Management.Commands.SetADUser
Here's the script I have now.
$list = Get-ADUser -filter {LogonWorkstations -like "*"} -properties name, LogonWorkstations | select name, logonworkstations
$logonname = "someone"
Function addspecificLogon {
param (
$logonname
)
$wrklist = Import-Csv "\\pathwaystuff\anotherplace\file.csv"
$Workstations = (Get-Aduser $logonname ` -Properties LogonWorkstations).LogonWorkstations
$workstations += ",work1, work2, work3"
#CONVERT WORKSTATION LIST TO AN ARRAY TO ALLOW FOR BETTER MANIPULATION OF DATA
$Workarray = $Workstations.Split(",")
#ERROR-CHECKING MEASURE: ELIMINATE DUPLICATE WORKSTATION NAMES
$Workarray = $Workarray | Sort-Object | Get-Unique
#CONVERTING ARRAY BACK TO STRING TO SET IN AD WORKSTATIONS (NEEDS TO BE A SPECIFIC STRING FORMAT TO ADD TO AD WORKSTATION)
$finalworkstations = ''
foreach ($work in $Workarray) {
$finalworkstations += ", $($work)"
}
Set-ADUser $logonname -LogonWorkstations $finalworkstations
$finalworkstations
}
addspecificLogon($logonname)
Get-ADUser -identity $logonname -properties * | select logonworkstations
Found a solution. That proved a lot simpler overall. Don't know exactly why but manipulating an array as long as possible helped.
function workstations{
$userWorkstationListLocation = Read-Host -Prompt "Enter the filepath of the CSV containing the allowed workstations"
if ($userworkstationlistlocation -eq ""){
$userWorkstationList = "empty"
$script:userworkstationlist = $userworkstationList
$script:userworkstationListLocation = $userWorkstationListLocation
} elseif ((test-path -path $userWorkstationListLocation) -eq $false) {
$userWorkstationListLocation = verify($userWorkstationListLocation)
}
$userWorkstations = Import-Csv -Path "$userWorkstationListLocation" | ForEach-Object {$_.userPC}
#CSV REFERENCE LIST FOR DEFAULT LOGON RIGHTS
$defaultWorkstationslist = Import-Csv '\\url\link\of\stations.csv'
foreach ($station in $defaultworkstationslist) {
$userWorkstations += "$($station.Infrastructure)"
}
#CONFIRMING IF NEEDED TO USE CITRIX
$CitrixUser = Read-Host "Will this user need access to Citrix? [Y/N]"
#FUNCTION TO CONFIRM ADD OR SKIP CITRIX LOGON RIGHTS
if ($CitrixUser -eq "Y"){
foreach($station in $defaultworkstationslist){
if ($station.Citrix -eq ""){
continue
} else {
$userWorkstations += "$($station.Citrix)"
}
}
}
#Removing Duplicates
$userworkstations = $userWorkstations | Sort-Object | Get-Unique
#Fixing for blanks
$userWorkstations.where({$_ -ne "" })
#converting to a list Set-AD can use
foreach ($s in $userWorkstations){
$s
if ($s -eq ""){
continue
} else {
$userworkstationlist += "$s,"
}
}
$userworkstationlist
$script:userworkstationListLocation = $userWorkstationListLocation
$script:userworkstationlist = $userworkstationlist
Write-Host "Within function $($userworkstationlist)"
}
I'm trying to create kind of a solution to create thousands of accounts in AD add them to specific group or for service accounts add them to specific OU. Keep a log of what was done and what the errors are.
The script ingest a csv file with the following headers.
SamAccountName,name,password,ou,domain,isAdded
$Domain = [system.directoryservices.activedirectory.domain]::GetCurrentDomain().Name
$NewUserADGroup = 'Print Operators'
$NewUsersList = Import-Csv .\bulk_user1.csv | Where-Object{$_.domain -like "$Domain"}
$NewUsersList | ForEach-Object{
$NewUserAttributes = #{
SamAccountName = $_.SamAccountName
name = $_.name
#path = $_.parentou
#UserPrincipalName = $_."samAccountName" + "#lovely.Local"
AccountPassword = (convertto-securestring "$NewUsersList.password" -AsPlainText -Force)
Enabled = $true
#Server = $dcname
#isAdded = $Issue
}
try{
#Create new User and add to specific group
New-ADUser $NewUserAttributes
Add-ADGroupMember -Identity $NewUserADGroup -Members $_.SamAccountName
#Delete Specific User
#Remove-ADUser -Identity $_.SamAccountName
}catch{
Write-Warning $_
$Issue = $_.ToString()
}
$count = $count + 1
Write-Host $_.SamAccountName " " $_.Name " " $_.SamAccountName.Enabled " Total:" $NewUsersList.Count + "Processed:" $count
$NewUserAttributes| Select-Object -Property SamAccountName,name,AccountPassword,Enabled,isAdded | Export-Csv ".\$Domain.NewAccountsCreatedStatus.csv"
}
I'm getting the following error:
WARNING: The name provided is not a properly formed account name
When I look at the variable
$NewUserAttributes
I do see the name and the value:
Name Value
---- -----
Enabled True
name bfmbsngfilexfer2
AccountPassword System.Security.SecureString
SamAccountName bfmbsngfilexfer2
As promised, below a rewrite of your code.
I have inserted comments to hopefully explain what the code does:
$Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name
$NewUserADGroup = 'Print Operators'
$successCount = 0
$NewUsersList = Import-Csv .\bulk_user1.csv | Where-Object { $_.domain -eq $Domain } | ForEach-Object {
# capture the human readable password for output use
$password = $_.password
$userParams = #{
SamAccountName = $_.SamAccountName
Name = $_.name
Path = $_.parentou
UserPrincipalName = '{0}#lovely.Local' -f $_.SamAccountName
AccountPassword = ConvertTo-SecureString $_.password -AsPlainText -Force
Enabled = $true
#Server = $dcname
}
try{
# Create new User and add to specific group
$user = New-ADUser #userParams -PassThru -ErrorAction Stop
Add-ADGroupMember -Identity $NewUserADGroup -Members $user -ErrorAction Stop
# add the 'isAdded' element to the $userParams hashtable
$userParams['isAdded'] = $true
$successCount++
}
catch{
Write-Warning $_.Exception.Message
$userParams['isAdded'] = $false
}
# output a PsCustomObject with values taken from the Hashtable
# AccountPassword is a SecureString, which will be of no use to you..
# Output the human readable password instead so you can inform the new users.
[PsCustomObject]$userParams | Select-Object SamAccountName, Name,
#{Name = 'Password'; Expression = {$password}},
Enabled, isAdded
}
# output
# use '#($NewUsersList)' to force it as array, so the Count property is accurate
if (#($NewUsersList).Count) {
Write-Host ('Processed: {0} Succeeded: {1}' -f $NewUsersList.Count, $successCount) -ForegroundColor Green
$NewUsersList | Export-Csv ".\$Domain.NewAccountsCreatedStatus.csv" -NoTypeInformation
}
else {
Write-Host 'No users successfully processed!' -ForegroundColor Red
}
I don't know how to add expressions in property declaration.
I created a script to get local admin members on servers and it works fine.
Now i want to add another property which should check if user specified in "Administrators" property is enabled or not, so i added property, "User Enabled" but it doesn't show anything
Foreach ($server in $servers){
Try{
$admins = Gwmi win32_groupuser –computer $server -Erroraction Stop
$admins = $admins |? {$_.groupcomponent –like '*"Administrators"'}
$admins = $admins |% { $_.partcomponent –match “.+Domain\=(.+)\,Name\=(.+)$” > $nul
$matches[1].trim('"') + “\” + $matches[2].trim('"') | Where-Object {$_ -like "$domain*" -and $_ -notlike "*Domain Admins*"}}
foreach ($admin in $admins){
# remove domain name
$username = $admin.Split('\')[1]
# Create properties
$administratorProperties = #{
"Administrators" = $admin
"Local Admin type" = "Domain users"
"Machine name" = $server
# check if local admin is enabled or not in AD
Label ="User Enabled" ; expression = {(Get-AdUser -Filter "SamAccountName -eq '$userName'").Enabled}
}
$adm += New-Object psobject -Property $administratorProperties
}
$adm | Select "Machine Name", "Administrators", "Local Admin type", "User Enabled" | Export-CSV "C:\Temp\LOcalAdmins.CSV" –NoTypeInformation
}
catch [Exception]
{
if ($_.Exception.GetType().Name -like "*COMException*") {
Write-Verbose -Message ('{0} is unreachable' -f $server) -Verbose
}
else{
Write-Warning $Error[0]
}
}
}
Machine name : MACHINE
Administrators : DOMAIN\User1
Label : User Enabled
Local Admin type : Domain users
expression : (Get-AdUser -Filter "SamAccountName -eq '$userName'").Enabled
Is it possible to check if user in $username variable is enabled or not.
Property expressions are for calculating a property value dynamically - you don't need that since you're constructing the object with all its property values known up front, so all you need to do is assign the property value to the relevant key in the hashtable as-is:
$administratorProperties = #{
"Administrators" = $admin
"Local Admin type" = "Domain users"
"Machine name" = $server
# check if local admin is enabled or not in AD
"User Enabled" = (Get-AdUser -Filter "SamAccountName -eq '$userName'").Enabled
}
$adm += New-Object psobject -Property $administratorProperties
I have been given the task of creating a school's worth of users (UK Secondary). The PS to create the users from a CSV, what I need to do is add the newly created users to various groups at the same time.
The code I am using is as follows
$DCName = '<DC FQDN>'
Import-Csv -Path "D:\Import.csv" |
ForEach-Object {
$Displayname = $_.'FirstName' + " " + $_.'LastName'
$UPN = $_.'UPN'
$GroupName = $_.'GroupName'
$Prop = #{
Name = $Displayname
DisplayName = $_.'FirstName' + " " + $_.'LastName'
GivenName = $_.'FirstName'
Surname = $_.'LastName'
UserPrincipalName = $UPN
EmailAddress = $UPN
SamAccountName = $_.'SAM'
AccountPassword = (ConvertTo-SecureString $_.'Password' -AsPlainText -Force)
Enabled = $true
Path = $_.'OU'
ChangePasswordAtLogon = $false
Title = $_.'JobTitle'
StreetAddress = $_.'Street'
City = $_.'Town'
State = $_.'County'
PostalCode = $_.'PostCode'
OfficePhone = $_.'Telephone'
Company = $_.'Company'
Department = $_.'Department'
HomeDrive = $_.'HomeDrive'
HomeDirectory = $_.'Home-Directory'
OtherAttributes = #{
'extensionAttribute1'= $_.'ExtendedAttribute1';
'extensionAttribute2'= $_.'ExtendedAttribute2';
'extensionAttribute14'= $_.'ExtendedAttribute14';
'extensionAttribute15'= $_.'ExtendedAttribute15';
'proxyAddresses' = "SMTP:" + $UPN;}
Server = $DCName
}
New-ADUser #prop
Add-ADGroupMember -Identity $GroupName -Members $_.'SAM'
}
The user gets created with all properties correctly set. It fails with the following error
Add-ADGroupMember : Cannot find an object with identity: 'Test.User' under: 'DC=AD,DC=example,DC=uk'.
At C:\Scripts\NewUserFromCSV2.ps1:47 char:10
+ Add-ADGroupMember -Identity $GroupName -Members $_.'SAM'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Test.USer:ADPrincipal) [Add-ADGroupMember], ADIdentityNotFoundException
+ FullyQualifiedErrorId : SetADGroupMember.ValidateMembersParameter,Microsoft.ActiveDirectory.Management.Commands.AddADGroupMember
It looks like the Add-ADGroupMember command can't find the user that has just been created, however, if that is the case I don't understand why.
Also at the moment, my CSV has only one group in the 'GroupName', what would be the best way to add the user to multiple groups? e.g. School-All-Staff, Teaching-Staff, Science-Teachers etc.
Thanks in advance for any help received.
As it's a bulk operation, I would just split the user creation from the group membership.
Create all the users first, then add them to the groups:
$DCName = '<DC FQDN>'
$Users = Import-Csv -Path "D:\Import.csv"
$Users | ForEach-Object {
$Displayname = $_.'FirstName' + " " + $_.'LastName'
$UPN = $_.'UPN'
$Prop = #{
## properties as per original code ##
}
New-ADUser #prop
}
$Users | ForEach-Object {
$GroupName = $_.'GroupName'
Add-ADGroupMember -Identity $GroupName -Members $_.'SAM'
}
The to add the users to multiple groups:
If you've got a semicolon separated list of the groups in GroupName eg
School-All-Staff;Teaching-Staff;Science-Teachers
Split will convert this to an array then you can loop through them:
$_.'GroupName' -split ';' | ForEach-Object {
Add-ADGroupMember $_ –Member $user.'SAM'
}
(edit: updated to semicolon as you have a csv source)
I got it working as a combined script in the end and added in error checking for pre-existing users, existing staff often move to the new school that is being added to the Trust prior to its addition to our AD and get included in the list of users to create.
Also added log file creation to record newly created users and list those whose SAMAccount name already exists so we can check to see if the user does need creating or if they need moving from another School OU.
This is my final code:
#Get deafult variables to tidy up created variables at the end
$ExistingVariables = Get-Variable | Select-Object -ExpandProperty Name
#New User Code Starts Here>
#Variables not set by CSV
#Set DC name to update - prevents errors due to replication delay
$DCName = '<DC FQDN>'
#Create log files
"Users Exist in AD" | Out-File -FilePath "D:\Logs\ExistingUsers-$(get-date -f yyyyMMdd).txt" -Append
"New Users Created" | Out-File -FilePath "D:\Logs\NewUsers-$(get-date -f yyyyMMdd).txt" -Append
#Specify path and file to import
Import-Csv -Path "D:\Import.csv" |
#Iterate through each row in the CSV
ForEach-Object {
#Set per object variables from fields in the CSV
$DisplayName = $_.'FirstName' + " " + $_.'LastName'
$UPN = $_.'UPN'
$GroupName1 = $_.'GroupName1'
$GroupName2 = $_.'GroupName2'
$GroupName3 = $_.'GroupName3'
$GroupName4 = $_.'GroupName4'
$SAM = $_.'SAM'
$Password = $_.'Password'
$SAMTest = Get-ADUser -Filter {(sAMAccountName -eq $SAM)} -Server $DCName
#Splatting Hash Table holds all user attribute properties set in the CSV
$Prop = #{
Name = $DisplayName
DisplayName = $DisplayName
GivenName = $_.'FirstName'
Surname = $_.'LastName'
UserPrincipalName = $UPN
EmailAddress = $UPN
SamAccountName = $_.'SAM'
AccountPassword = (ConvertTo-SecureString $_.'Password' -AsPlainText -Force)
Enabled = $true
Path = $_.'OU'
ChangePasswordAtLogon = $false
Title = $_.'JobTitle'
StreetAddress = $_.'Street'
City = $_.'Town'
State = $_.'County'
PostalCode = $_.'PostCode'
OfficePhone = $_.'Telephone'
Company = $_.'Company'
Department = $_.'Department'
OtherAttributes = #{
'extensionAttribute1'= $_.'ExtendedAttribute1';
'extensionAttribute2'= $_.'ExtendedAttribute2';
'extensionAttribute14'= $_.'ExtendedAttribute14';
'extensionAttribute15'= $_.'ExtendedAttribute15';
'proxyAddresses' = "SMTP:" + $UPN;}
Server = $DCName
}
#Check if SAMAccount name exists in AD and skip existing users
if ($SAMTest -ne $Null)
{
#Get UPN property of the pre-existing user
$Exist = Get-ADUser -Filter {(sAMAccountName -eq $SAM)} -Properties 'userprincipalname'
#write UPN value to variable
$ExistUPN = $Exist.userprincipalname
#Update log of pre-existing users
"$DisplayName exists with email $ExistUPN" | Out-File -FilePath "D:\Logs\ExistingUsers-$(get-date -f yyyyMMdd).txt" -Append
#Write to screen
Write-Host "$DisplayName already exists in AD" -ForegroundColor Red
}
else
{
#Create new user with the attribute properties collected above
New-ADUser #prop
#Check if group fields in CSV were populated, if true add user to group, if false skip
if ($_.'GroupName1'){Add-ADGroupMember -Identity $_.'GroupName1' -Members $_.'SAM' -Server $DCName}
if ($_.'GroupName2'){Add-ADGroupMember -Identity $_.'GroupName2' -Members $_.'SAM' -Server $DCName}
if ($_.'GroupName3'){Add-ADGroupMember -Identity $_.'GroupName3' -Members $_.'SAM' -Server $DCName}
if ($_.'GroupName4'){Add-ADGroupMember -Identity $_.'GroupName4' -Members $_.'SAM' -Server $DCName}
#Update New user log
"$UPN" | Out-File -FilePath "D:\Logs\NewUsers-$(get-date -f yyyyMMdd).txt" -Append
#Write to screen
Write-Host "User $SAM created at $((Get-Date).ToString('hh:mm'))" -ForegroundColor Green
}
}
#End Of New User Code
#Remove variables set by script - keeps PS memory space tidy
$NewVariables = Get-Variable | Select-Object -ExpandProperty Name | Where-Object {$ExistingVariables -notcontains $_ -and $_ -ne "ExistingVariables"}
if ($NewVariables)
{
Write-Host "Removing the following variables:`n`n$NewVariables"
Remove-Variable $NewVariables
}
else
{
Write-Host "No new variables to remove!"
}
I used the bit about clearing up variables because the values seemed to persist if the PowerShell session remained open and it was causing odd things to happen. I also removed the home drive attributes because the file server specified hasn't been implemented yet but management still wants the users in AD now.
For reference my import.csv looks like this
FirstName,LastName,UPN,SAM,Password,OU,JobTitle,Street,Town,County,PostCode,Telephone,Company,Department,ExtendedAttribute1,ExtendedAttribute2,ExtendedAttribute14,ExtendedAttribute15,GroupName1,GroupName2,GroupName3,GroupName4
Test,User,Test.Users#domain.uk,Test.User,,"OU=Admin Staff,OU=User Resources,OU=School,OU=Trust Schools,DC=AD,DC=Trust,DC=org",,Street Name,TownName,County,AA11 1AA,116123,Name Of School,Name Of Trust,,Staff,,,AllStaffGroup,AdminStaffGroup,SpecialPermissionsGroup,Group4
I have a list of users and and I need to query whether or not they are in a particular OU or not. I have a Powershell that gives me the nearly the exact results I want in the console, but I would like it written to CSV file instead. Could someone please help me with the syntax.
$Usernames = Get-Content -path .\Usernames.txt
# Running against user list
ForEach ($Username in $Usernames)
{
$ADuser = Get-ADUser -identity $Username –properties *
$UserOU = $ADuser.CanonicalName.ToString().Split('/')[2]
if ($userOU -match "OUName_A") {
$OU = "FriendlyOUName_A"
write-host $Username $OU -foregroundcolor green
}
elseif ($userOU -match "OUName_B") {
$OU = "FriendlyOUName_B"
write-host $Username $OU
}
else {
write-host $Username "Disabled" -foregroundcolor red
}
}
So the screen output now looks like:
username#1 FriendlyOUName_A
username#2 FriendlyOUName_B
username#3 disabled
Get-ADUser : Cannot find an object with identity: 'username#4' under: 'DC=subdomain,DC=company,DC=com'.
At C:\temp\FindOUofUsers.ps1:8 char:11
+ $ADuser = Get-ADUser -identity $Username –properties *
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (username#4:username#4) [Get-ADUser], ADIdentityNotFoundException
+ FullyQualifiedErrorId : Cannot find an object with identity: 'username#4' under: 'DC=subdomain,DC=company,DC=com'.,Microsoft.ActiveDirectory.Management.Commands.GetADUser
It would be helpful to capture the error state as username#4 Deleted
As TheMadTechnician pointed out, you are just outputting text to the screen (and not stdout). That's the job of Write-Host. If you want to write to stdout, you should use Write-Output, or simply output the string.
But an even better way is to output objects, instead of strings. That's where the PowerShell pipeline shines. Once you have actual objects, you can do whatever you want with them -- print them, sort them, and yes, even write them to a CSV file.
This should do what you want. Note that the script creates custom objects with two properties (UserName and OU). The objects then get returned to the pipeline, which then gets piped to the Export-Csv commandlet. I urge you to try the script without the Export-Csv part first -- this will dump the output to the screen, and you'll see what's going on.
$Usernames = Get-Content -path .\Usernames.txt
$Usernames | ForEach-Object {
$Username = $_
$returnObj = New-Object PSCustomObject
$returnObj | Add-Member -MemberType NoteProperty -Name UserName -Value $Username
$returnObj | Add-Member -MemberType NoteProperty -Name OU -Value 'Deleted'
try {
$ADuser = Get-ADUser -Identity $Username -Properties CanonicalName -ErrorAction Stop
$UserOU = $ADuser.CanonicalName.ToString().Split('/')[2]
if ($userOU -match "OUName_A") {
$returnObj.OU = "FriendlyOUName_A"
}
elseif ($userOU -match "OUName_B") {
$returnObj.OU = "FriendlyOUName_B"
}
else {
$returnObj.OU = "Disabled"
}
}
catch { }
$returnObj # This returns the object to the pipeline
} | Export-Csv '.\myFile.csv' -Encoding ASCII -NoTypeInformation