Jump to content

[Powershell] Creating Users from CSV - Home folder


Recommended Posts

Posted
Thanks. :)

Currently getting this error:

Exception calling "Substring" with "2" argument(s): [b]"Index and length must refer to a location within the string.[/b]
Parameter name: length"
At D:\PS\Complete.ps1:12 char:51
+ $sAMAccountName = $Leaving + ($Forename).Substring <<<< (0,3) + ($Surname).Substring(0,4)
   + CategoryInfo          : NotSpecified: ( [], MethodInvocationException
   + FullyQualifiedErrorId : DotNetMethodException

 

 

I've bolded the important part here. It basically means you've got a name which is only 1 or 2 characters long by the looks of it.

  • Thanks 1
Posted

It turns out that it was down to my current test username being 'PS' (only two characters long), and this script counts the first three letters in from the left.

Is there any way to keep it at the current max of three letters from the left, but make it so it won't fall over if we have a user with a two letter name?

Posted
It turns out that it was down to my current test username being 'PS' (only two characters long), and this script counts the first three letters in from the left.

Is there any way to keep it at the current max of three letters from the left, but make it so it won't fall over if we have a user with a two letter name?

 

You'd have to test each forename to ensure it's length is greater than 2 and then do something if it's not. What you do depends on how you want to handle it.

 

if ($Forename.length -lt 3) {
#do something here to short forenames
}
if ($surname.length -lt 4) {
#do something here to short surnames
}

  • Thanks 1
Posted

Currently unable to create the user again:

19 + (PSH).Substring(0,3) + (Test).Substring(0,4)
New-ADUser : The name provided is not a properly formed account name
At D:\PS\Complete.ps1:29 char:11
+ New-ADUser <<<<  -ErrorAction SilentlyContinue `
   + CategoryInfo          : NotSpecified: (CN=PSH Test,OU=...hnwall,DC=local:String) [New-ADUser], ADException
   + FullyQualifiedErrorId : The name provided is not a properly formed account name,Microsoft.ActiveDirectory.Management.Commands.NewADUser

As you can see in the first line (which is meant to be $sAMAccountName, it isn't building the string because $Leaving is an integer. I've tried encapsulating, but that still isn't working. :(

Posted
Currently unable to create the user again:

19 + (PSH).Substring(0,3) + (Test).Substring(0,4)
New-ADUser : The name provided is not a properly formed account name
At D:\PS\Complete.ps1:29 char:11
+ New-ADUser <<<<  -ErrorAction SilentlyContinue `
   + CategoryInfo          : NotSpecified: (CN=PSH Test,OU=...hnwall,DC=local:String) [New-ADUser], ADException
   + FullyQualifiedErrorId : The name provided is not a properly formed account name,Microsoft.ActiveDirectory.Management.Commands.NewADUser

As you can see in the first line (which is meant to be $sAMAccountName, it isn't building the string because $Leaving is an integer. I've tried encapsulating, but that still isn't working. :(

 

Might be worth trying something a little more complex for the string concatenation:

 

$sAMAccountName = "$Leaving$($Forename.substring(0,3))$($Surname.substring(0,4)"

 

The way it works is that double quotation marks will take the value of a variable and put it in the string (single quotes will print whatever is between them including variable names), but if you have something like "$forename.substring" then it will read that as "PSH.substring". The solution to this is use $() and it will evaluate the code between the brackets first and then convert it to a string before adding it to the main string. That should mean that your $leaving is converted to a string and the other parts are evaluated correctly as well.

  • Thanks 1
Posted (edited)

It's now working as I would like! :D Thanks for your help so far!

Now I want to implement some changes to help with errors and such. First I'm going to look at short names.

You'd have to test each forename to ensure it's length is greater than 2 and then do something if it's not. What you do depends on how you want to handle it.

 

if ($Forename.length -lt 3) {
#do something here to short forenames
}
if ($surname.length -lt 4) {
#do something here to short surnames
}

Let's say a pupil comes and their name is Si Lee. This defeats both character limits...I'm trying to figure out how to accommodate short names. Ideally I'd like to keep them as they are and it would create the account, so it would be a username of 19SiLee.

Edited by CHiLL
Posted

There are a few ways to handle this, the easiest way would be to have some if/elseif/else statements and create the account name within that. It would result in something like this:

 

if ($forename.length -lt 3 -and $surname.length -lt 4)
{
$samaccountname = "$Leaving$Forename$Surname"
}
elseif ($forename.length -lt 3 )
{
$samaccountname="$Leaving$Forename$($surname.substring(0,4))"
}
elseif ($surname.length -lt 4)
{
$samaccountname="$Leaving$($Forename.substring(0,3))$surname"
}
else
{
$samaccountname= "$Leaving$($forename.substring(0,3))$(surname.substring(0,4))"
}

  • Thanks 1
Posted (edited)

The script is now working when it comes to creating the account and setting the details, however the only thing that isn't working correctly is setting the home folder permissions and ownership. It is displaying an unknown SID in the security details rather than the sAMAccountName that it should be and adding nothing into the owner section. No errors are being thrown by the script, which makes it harder to diagnose! I'll attach a screenshot of the permissions..

 

[ATTACH=CONFIG]31152[/ATTACH]

 

Import-Module ActiveDirectory

$CSV = Import-CSV -Delimiter "," "D:\PS\New-ADUser.csv”

foreach ($User in $CSV) {

if ($User.Forename.Length -lt 3)
{
$Forename = $User.Forename
}
else
{
$Forename = ($User.Forename).Substring(0,3)
}
if ($User.Surname.Length -lt 4)
{
$Surname = $User.Surname
}
else
{
$Surname = ($User.Surname).Substring(0,4)
}
Write-Host $Forename
Write-Host $Surname

$Intake = $User.Intake
$Leaving = [int]$User.Intake + 5 #Based on 2 digit year in CSV
$GivenName = $User.Forename
$LastName = $User.Surname
$Name = $GivenName + " " + $LastName
$sAMAccountName = "$Leaving" + "$Forename" + "$Surname"
$UserPrincipalName = $sAMAccountName + “@stjohnwall.local”
$DisplayName = $Name
$Path = "OU=Intake " + $User.Intake + ",OU=Curriculum,OU=St John Wall Student Accounts,DC=stjohnwall,DC=local"
$EmailAddress = ($sAMAccountName + "@sjw.bham.sch.uk")
$AccountPassword = ConvertTo-SecureString -AsPlainText "password" -Force
$Enabled = $true
$ChangePasswordAtLogon = $true
$Description = "Intake 20" + $User.Intake
$HomeFolderLocation = "\\curricsvr-01\users\"
$HomeDirectory = $HomeFolderLocation + $sAMAccountName
$HomeDrive = "H:"
$ProfilePath = "\\CURRICSVR-01\netlogon\mandatory3"
$ScriptPath = "students.bat"

New-ADUser -ErrorAction SilentlyContinue `
-Name $Name `
-sAMAccountName $sAMAccountName `
-UserPrincipalName $UserPrincipalName `
-DisplayName $DisplayName `
-Surname $LastName `
-GivenName $GivenName `
-Path $Path `
-EmailAddress $EmailAddress `
-AccountPassword $AccountPassword `
-Enabled $Enabled `
-ChangePasswordAtLogon $ChangePasswordAtLogon `
-Description $Description `
-HomeDirectory $HomeDirectory `
-HomeDrive $HomeDrive `
-ProfilePath $ProfilePath `
-ScriptPath $ScriptPath `
-OtherAttributes @{'extensionAttribute1'="Students"; 'msExchExtensionCustomAttribute1'="Students"}

if(!$?) {
   if($error[0] = "The specified account already exists") {
       $errorStr = $error[0] + ": " + $SAM
       Write-Warning $errorStr
       $tentry = "`n" + $DisplayName + " " + $sAMAccountName
       Add-Content D:\PS\duplicates.txt $tentry }
} else {

   New-Item -path $HomeFolderLocation -Name $sAMAccountName -ItemType Directory
       $userDir = "$HomeDirectory"
       $Rights= [system.Security.AccessControl.FileSystemRights]::Read -bor [system.Security.AccessControl.FileSystemRights]::Write -bor [system.Security.AccessControl.FileSystemRights]::Modify
           $Inherit=[system.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [system.Security.AccessControl.InheritanceFlags]::ObjectInherit
           $Propogation=[system.Security.AccessControl.PropagationFlags]::None
           $Access=[system.Security.AccessControl.AccessControlType]::Allow
           $AccessRule = new-object System.Security.AccessControl.FileSystemAccessRule("$sAMAccountName",$Rights,$Inherit,$Propogation,$Access)
           $ACL = Get-Acl $userDir
           $ACL.AddAccessRule($AccessRule)
           $Account = new-object system.security.principal.ntaccount($sAMAccountName)
           $ACL.setowner($Account)
           $ACL.SetAccessRule($AccessRule)
           Set-Acl $userDir $ACL
           
           Add-ADGroupMember -Identity "Domain Guests" -Member $sAMAccountName
           Add-ADGroupMember -Identity "Curriculum" -Member $sAMAccountName
           Add-ADGroupMember -Identity ("Intake " + $Intake) -Member $sAMAccountName

 $out = "The AD Account: " + $DisplayName + " and Home Directory: " + $userDir + " have been created."
   Write-Host $out
   }
}

Edited by CHiLL
Posted

It may not be associating the $sAMAccountname with the one that's on the domain. The method I use for these sort of things is to specify which domain to look up the $sAMAccountName by changing the two lines to something like this:

 

$AccessRule = new-object System.Security.AccessControl.FileSystemAccessRule("domain\$sAMAccountName",$Rights,$Inherit,$Propogation,$Access)


$Account = new-object system.security.principal.ntaccount("domain\$sAMAccountName")

  • Thanks 1
Posted (edited)
It may not be associating the $sAMAccountname with the one that's on the domain. The method I use for these sort of things is to specify which domain to look up the $sAMAccountName by changing the two lines to something like this:

 

$AccessRule = new-object System.Security.AccessControl.FileSystemAccessRule("domain\$sAMAccountName",$Rights,$Inherit,$Propogation,$Access)


$Account = new-object system.security.principal.ntaccount("domain\$sAMAccountName")

Thanks, but unfortunately that hasn't worked. :( I'm getting the following errors:

Mode                LastWriteTime     Length Name                                                                                                                                                                                                                  
----                -------------     ------ ----                                                                                                                                                                                                                  
d----        19/06/2015     09:53            19ChrJohn                                                                                                                                                                                                             
Exception calling "AddAccessRule" with "1" argument(s): "Some or all identity references could not be translated."
At D:\PS\Complete.ps1:110 char:27
+         $ACL.AddAccessRule <<<< ($AccessRule)
   + CategoryInfo          : NotSpecified: ( [], MethodInvocationException
   + FullyQualifiedErrorId : DotNetMethodException

Exception calling "SetOwner" with "1" argument(s): "Some or all identity references could not be translated."
At D:\PS\Complete.ps1:113 char:22
+         $ACL.setowner <<<< ($Account)
   + CategoryInfo          : NotSpecified: ( [], MethodInvocationException
   + FullyQualifiedErrorId : DotNetMethodException

Exception calling "SetAccessRule" with "1" argument(s): "Some or all identity references could not be translated."
At D:\PS\Complete.ps1:114 char:27
+         $ACL.SetAccessRule <<<< ($AccessRule)
   + CategoryInfo          : NotSpecified: ( [], MethodInvocationException
   + FullyQualifiedErrorId : DotNetMethodException

The AD Account: Christopher Johnson and Home Directory: \\curricsvr-01\users\19ChrJohn have been created.

That error occurs when I try using the domain "curricdom" or the FQDN "stjohnwall.local".

 

I have also tried defining a $DomainAccount variable which is:

$DomainAccount = "curricdom\" + $sAMAccountName

and the $DomainAccount variable into both

$AccessRule = new-object System.Security.AccessControl.FileSystemAccessRule("$DomainAccount",$Rights,$Inherit,$Propogation,$Access)

$Account = new-object system.security.principal.ntaccount("$DomainAccount")

It now doesn't throw any errors, however it still shows an unknown SID in both the security permissions and owner information.

Edited by CHiLL
Posted
Are you running the script on a DC or from your machine running RSAT? Does the group ownership get added to the users? I've had a problem where groups wouldn't be added as the DC hadn't fully created the account when I was trying to add them to a group, I put in a Start-Sleep -s 5 after the New-ADUser and that resolved it. I'm wondering if it's a similar problem but I guess it depends on where you are running the script from.
  • Thanks 1
Posted (edited)
Are you running the script on a DC or from your machine running RSAT? Does the group ownership get added to the users? I've had a problem where groups wouldn't be added as the DC hadn't fully created the account when I was trying to add them to a group, I put in a Start-Sleep -s 5 after the New-ADUser and that resolved it. I'm wondering if it's a similar problem but I guess it depends on where you are running the script from.

Well I'm running the script from my machine running RSAT, not a DC.

The account is being added into those groups though.

I might add the sleep command in to see if that helps.

 

Edit: Unforunately the "Start-Sleep -s 5" command didn't help, its still adding an unknown SID in.

When I manually add the user account into the permissions, it adds the sAMAccountName but the unknown SID still remains, it isn't replaced or anything.

Edited by CHiLL
Posted
Then I'm stumped on that. Only other thing you could try is using something like NTFS Security to assign the permissions, it's basically just a wrapper for the commands you're using already but maybe it does things a little differently that allows it to work. You might hit the same problem but it's worth a try.
  • Thanks 1
Posted (edited)

I have added a 10 second wait in, and that worked. I then took the wait command out, and its still working! It seems inconsistent and it could be down to the fact that our domain isn't in great shape. I'm going to keep playing with it and see what happens!

 

Then I'm stumped on that. Only other thing you could try is using something like NTFS Security to assign the permissions, it's basically just a wrapper for the commands you're using already but maybe it does things a little differently that allows it to work. You might hit the same problem but it's worth a try.

Damn, I was hoping to achieve this within the ActiveDirectory module so that basically any machine running RSAT (such as my colleage's PC) or a DC, or a server running RSAT can run the script to add users. It looks as though this is an extra module that needs to be installed.

 

Thank you for you help so far though!

Edited by CHiLL
Posted
Damn, I was hoping to achieve this within the ActiveDirectory module so that basically any machine running RSAT (such as my colleage's PC) or a DC, or a server running RSAT can run the script to add users. It looks as though this is an extra module that needs to be installed.

I might continue to play about with the script but so far it looks like I might be beaten!

 

Thank you for you help so far though!

 

It's actually pretty easy to install modules (just copy a folder to a few machines) and PS version 3+ auto loads them when you try to use them.

Posted
It's actually pretty easy to install modules (just copy a folder to a few machines) and PS version 3+ auto loads them when you try to use them.

Edited the previous post with a couple of findings, I don't like the inconsistency!

  • 4 weeks later...
Posted

Bump!

Thought I'd post mine up, which I just finalised today. It generates users a ##XxxxxY username (2-digit year of entry, Surname, Forename initial) based upon a CSV file (such as the one SIMS exports) but also includes error checking to remove dashes and apostrophes from usernames, and capitalise where necessary.

 

I daresay there's a kludge or two in there, so if anyone sees any improvements to be made, point 'em out!

 

$ErrorActionPreference = "Inquire"

$CSVLocation = ("[color="#FF0000"]Location of the CSV file with new student details[/color]")
Write-Warning ("CSV target: " + $CSVLocation)
if (Test-Path $CSVLocation) {
Write-Warning "Ensure all junk data has been stripped and columns are headed 'Surname' and 'Forename'"
$Continue = (Read-Host "Continue?")
} else {
$Continue = "n"
Write-Warning ("Path " + $CSVLocation + " does not exist.")
cmd /c pause
}
if ($Continue -imatch "n") {
exit
} elseif ($Continue -imatch "y") {
ForEach ($User in (Import-CSV $CSVLocation)) {
	
	# Forename fixing
	ForEach ($item in ($User.Forename -split("( )") -split("(-)") -split("(')"))) {
		[string]$Forename = $Null
		if ($item -ne '') {
			[string]$item = ($item.SubString(0,1).ToUpper() + $item.SubString(1).ToLower())
			[string]$Forename2 += $item
		}
	}
	[string]$Forename = [string]$Forename2; [string]$Forename2 = $null

	#Surname fixing
	ForEach ($item in ($User.Surname -split("( )") -split("(mc)") -split("(mac)") -split("(-)") -split("(')"))) {
		[string]$Surname = $Null
		if ($item -ne '') {
			[string]$item = ($item.SubString(0,1).ToUpper() + $item.SubString(1).ToLower())
			[string]$Surname2 += $item
		}
	}
	[string]$Surname = [string]$Surname2; [string]$Surname2 = $null
	
	# Calculate Yeargroup
	[string]$YearGroup = [string]((Get-Date).Year)
	
	# Calculate username
	[string]$UserName = ((([string](Get-Date).Year).SubString(2,2)) + ([string]$Surname -replace "-","" -replace "'", "" -replace " ","") + ([string]$Forename.SubString(0,1)))
	if (!(Get-ADUser -Filter {SamAccountName -eq $UserName}) -eq $False) {
		Write-Warning ("Username " + $UserName + " Already in use.")
		$UserName = (Read-Host "Specify new Username for user '$Forename $Surname'.")
	}
	
	# Calculate account details
	[string]$HomeDirectory = "\\[color="#FF0000"]File-Server[/color]\$YearGroup\$UserName" 
	[string]$Path = "OU=$([string]$YearGroup) Intake,OU=Pupils,OU=[color="#FF0000"]School[/color] Users,DC=[color="#FF0000"]DOMAIN[/color],DC=local"
	[string]$UserPrincipalName = "$UserName@[color="#FF0000"]Schoolname[/color]"
	
	# User account creation
	$ExpirationDate = ((((Get-Date).AddMonths(62)).ToString("dd/MM/yyyy")) + " 00:05:00 AM")
	New-ADUser `
		-SamAccountName $UserName `
		-UserPrincipalName $UserPrincipalName `
		-Name $UserName `
		-DisplayName $UserName `
		-GivenName $Forename `
		-SurName $Surname `
		-Description "Pupil" `
		-EmailAddress $UserPrincipalName `
		-HomeDrive "M:" `
		-HomeDirectory $HomeDirectory `
		-Path $Path `
		-ProfilePath "[color="#FF0000"]C:\Profiles\PupilProfile[/color]" `
		-AccountPassword (ConvertTo-SecureString "[color="#FF0000"]DefaultPassword[/color]" -AsPlainText -force) `
		-Enabled $True `
		-ChangePasswordAtLogon $True
		Set-ADAccountExpiration -Identity $UserName -DateTime $ExpirationDate
	
	# Assign user to AD Group
	Add-ADGroupMember "[color="#FF0000"]Pupil Group[/color]" $UserName
	
	# Grant user full control of home folder
	New-Item -ItemType Directory -Path $HomeDirectory
	$ACL = (Get-ACL -Path $HomeDirectory)
	$FullControlAccessRule = (New-Object System.Security.AccessControl.FileSystemAccessRule([system.Security.Principal.NTAccount]"[color="#FF0000"]DOMAIN[/color]\$UserName","FullControl", "ContainerInherit, ObjectInherit", "None", "Allow"))
	$ACL.AddAccessRule($FullControlAccessRule)
	Set-ACL -Path $HomeDirectory $ACL
	
}
}
cmd /c pause

Script will run natively in Powershell 3+, no need for external modules. Red bits are stuff you'll need to change to suit your environment :)

... I should really get into the habit of commenting my code more.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now



×
×
  • Create New...