Creating AD users with help from SharePoint Online and PowerShell

Creating users in different systems is a common task that in many cases can be quite challenging. It gets even more challenging having migrated email to Office 365, decommisioned the Exchange Servers, still keeping the local Active Directory. Using Directory Synchronization/Password sync, as of today, creating users in the correct way in your local Active Directory is not optional. By the correct way, I mean a user with proper UserPrincipalName, proxyAddresses, mail etc, which usually is where we get problems.

We do of course have lots of tools, like FIM/MIM and others to help us do this in an automated and predictable way. But if we don’t have that, what are the options?

In this post, I will give you an example on how to create a “poor mans solution” on this topic, using tools that you already have if using Office 365. SharePoint Online and PowerShell. If you have System Center Orchestrator in your environment, the integration with SharePoint Online can be handled by an integration pack instead.

OVERVIEW
Overview on that the post will cover:

  • Read list items from an already existing list in SharePoint Online.
  • Create an AD user in a predictable way based on input from SharePoint Online.
  • Send an email to the new users manager with the account information.
  • Report back status to SharePoint  with status and username/email address to the SharePoint list item.

GETTING STARTED
First of all, you need to create a SharePoint list with the columns that you want to use as input for your new users. In my example I have called the list “OnBoarding” and created  the columns as below. The status column will in my case be extra important since I will use that to determine if the user has been created or not. (there are other ways to do that of course).

2015-05-14_13-33-52 2015-05-14_15-07-11

Next up is preparing our “automation server” with the right tools. We will use the Client Side Object Model (CSOM) in SharePoint to integrate with SharePoint Online. For that we need the SharePoint Server 2013 Client Components SDK, which can be downloaded from here.  You also need to have the ActiveDirectory PowerShell module on the machine running the script.

CREATING THE USER / RUNNING THE SCRIPT
First of all, we need input data in the SharePoint list for the user to create. 

2015-05-14_15-50-072015-05-14_15-50-41

Then configure the script to fit your AD Settings, SPO credentials, addresses and the column name mappings in the $SPListItemColumns hashtable. Note that spaces will be replaced by ‘_x0020_’, so for example ‘Last Name’ will be ‘Last_x0020_Name’.
2015-05-14_19-35-10

Apart from the above, the example script consists of the simple functions described below:

  • Convert-ToLatinCharacters – will get rid of all the non-unicode characters and some more creating the username. (Thanks to Johan Akerstrom)
  • Get-JDDNFromUPN – returns distinguishedname of a userprincipalname, to support adding a manager to the AD User.
  • New-JDSamAccountName – Generates a samaccountname for the user, based on firstname + lastname (default 3+3 characters). Will handle duplicates.
  • New-JDUPNAndMail – Generates a userprincipalname and emailaddress based on firstname.lastname. Will also handle duplicates but assumes that UPN and Email are the same in your environment.
  • New-JDADUser  Creates the AD user and returns sAMAccountName, UserPrincipalName and the Password. Password generated by the System.Web GeneratePassword method (there are of course other and better methods)
  • Send-JDWelcomeEmail – Basic function to send a “Welcome Email” to the manager with the account information. Lots of room for design improvement in the email sent out, but it works. The email will contain the users password in clear text, so be careful with the recipient.

CreateADUsersFromSPO.ps1

<#
.SYNOPSIS
    The scripts creates and active directory users based on input from a SharePoint list in SharePoint Online.
    For more details, read at 365lab.net
.EXAMPLE
   .\CreateADUsersFromSPO.ps1 -Verbose
.NOTES
    File Name: CreateADUsersFromSPO.ps1
    Author   : Johan Dahlbom, johan[at]dahlbom.eu
    Blog     : 365lab.net 
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
    Requires PowerShell Version 3.0!
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
    $UPNSuffix = "365lab.net",
    $OrganizationalUnit = "OU=Users,OU=365lab Inc.,DC=ad,DC=365lab,DC=net",
    $PasswordLength = 12
)
#Import the Required Assemblys from the Client components SDK (https://www.microsoft.com/en-us/download/details.aspx?id=35585)
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName(“System.Web”) | Out-Null
 
#Import Active Directory Module
Import-Module ActiveDirectory -Verbose:$false
 
#Define SharePoint Online Credentials with proper permissions
$User = 'admin@tenant.onmicrosoft.com'
$Pass = ConvertTo-SecureString 'password' -AsPlainText -Force
#SharePoint online address and list name
$SPOUrl = "https://tenant.sharepoint.com/"
$SPOList = "UserOnboarding"
#Column Name mapping in SharePoint 
$SPListItemColumns = @{
        FirstName = "First_x0020_Name"
        LastName = "Last_x0020_Name"
        Title = "Title"
        Manager = "Manager"
        Status = "Status"
        Mail = "EmailAddress"
}

#region functions
function Convert-ToLatinCharacters {
    param(
        [string]$inputString
    )
    [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($inputString))
}
 
function Get-JDDNFromUPN {
    param (
        [ValidateScript({Get-ADUser -Filter {UserprincipalName -eq $_}})] 
        [Parameter(Mandatory=$true)][string]$UserPrincipalName
    )
        $ADUser = Get-ADUser -Filter {UserprincipalName -eq $UserPrincipalName} -ErrorAction stop
        return $ADUser.distinguishedname
}
 
function New-JDSamAccountName {
    param (
        [Parameter(Mandatory=$true)][string]$FirstName,
        [Parameter(Mandatory=$true)][string]$LastName,
        [parameter(Mandatory=$false)][int]$FirstNameCharCount = 3,
        [parameter(Mandatory=$false)][int]$LastNameCharCount = 3
    )
    #Construct the base sAMAccountName
    $BaseSam = "{0}{1}" -f (Convert-ToLatinCharacters $FirstName).Substring(0,$FirstNameCharCount),(Convert-ToLatinCharacters $LastName).Substring(0,$LastNameCharCount)
 
    #Add a number until you find a free sAMAccountName
    if (Get-ADUser -Filter {samaccountname -eq $BaseSam} -ErrorAction SilentlyContinue) {
        $index = 0
        do {
            $index++
            $sAMAccountName = "{0}{1}" -f $BaseSam.ToLower(),$index
        } until (-not(Get-ADUser -Filter {samaccountname -eq $sAMAccountName } -ErrorAction SilentlyContinue))
    } else {
        $sAMAccountName = $BaseSam.tolower()
    }
    return $sAMAccountName
}
     
function New-JDUPNAndMail {
    param (
        [Parameter(Mandatory=$true)][string]$FirstName,
        [Parameter(Mandatory=$true)][string]$LastName,
        [Parameter(Mandatory=$true)][string]$UPNSuffix
     )
    #Construct the base userPrincipalName
    $BaseUPN = "{0}.{1}@{2}" -f (Convert-ToLatinCharacters $FirstName).replace(' ','.').tolower(),(Convert-ToLatinCharacters $LastName).replace(' ','.').tolower(),$UPNSuffix
     
    if (Get-ADUser -Filter {userprincipalname -eq $BaseUPN} -ErrorAction SilentlyContinue) {
        $index = 0
        do {
            $index++
            $UserPrincipalName = "{0}{1}@{2}" -f $BaseUPN.Split("@")[0],$index,$UPNSuffix
        } until (-not(Get-ADUser -Filter {userprincipalname -eq $UserPrincipalName} -ErrorAction SilentlyContinue))
 
    } else {
        $UserPrincipalName = $BaseUPN
    }
    return $UserPrincipalName
}
 
function New-JDADUser {
    [CmdletBinding(SupportsShouldProcess=$true)]
    param (
        [Parameter(Mandatory=$true)][string]$FirstName,
        [Parameter(Mandatory=$true)][string]$LastName,
        [Parameter(Mandatory=$true)][string]$UserPrincipalName,
        [Parameter(Mandatory=$true)][string]$sAMAccountName,
        [Parameter(Mandatory=$true)][string]$Title,
        [Parameter(Mandatory=$true)][string]$OU,
        [Parameter(Mandatory=$true)][string]$Manager,
        [Parameter(Mandatory=$true)][int]$PasswordLength = 12
    )
     #Generate a password
     $Password = [System.Web.Security.Membership]::GeneratePassword($PasswordLength,2)
 
     #Construct the user HT
     $ADUserHt = @{
        GivenName = $FirstName
        SurName = $LastName
        ChangePasswordAtLogon = $true
        EmailAddress = $UserPrincipalName
        UserPrincipalName = $UserPrincipalName
        sAMAccountName = $sAMAccountName
        Title = $Title
        Name = "$FirstName $LastName ($sAMAccountName)"
        Displayname = "$FirstName $LastName"
        Manager = $Manager
        Path = $OU
        AccountPassword = (ConvertTo-SecureString -String $Password -AsPlainText -Force)
        Enabled = $true
        OtherAttribute = @{proxyAddresses = "SMTP:$UserPrincipalName"}
     }
     try {
        #Create the user and return a custom object
        New-ADUser @ADUserHt -ErrorAction Stop 
        Write-Verbose "Successfully created the user $($ADUserHt.Name)"
        [pscustomobject] @{
            sAMAccountName = $ADUserHt.sAMAccountName 
            UserPrincipalName = $ADUserHt.UserPrincipalName 
            Password = $Password
        }
     } catch {
        Write-Warning "Error creating the user $($ADUserHt.Name) `r`n$_"
     }
}
 
function Send-JDWelcomeEmail {
    param (
        [Parameter(Mandatory=$true)][string]$Recipient,
        [Parameter(Mandatory=$false)][string]$Sender = "useronboarding@365lab.net",
        [Parameter(Mandatory=$true)][string]$uUserPrincipalName,
        [Parameter(Mandatory=$true)][string]$usAMAccountName,
        [Parameter(Mandatory=$true)][string]$uPassword
    )
    $message = @"
    Hi,
 
    A user with the following account details was created in Active Directory:
    Username: $usAMAccountName
    EmailAddress: $uUserPrincipalName
    Password: $uPassword (needs to be changed at first logon)
 
    Best Regards
    365lab IT department
"@
    $MailMessageHt = @{
        From = $Sender
        To = $Recipient
        Body = $message
        SmtpServer = "365lab-net.mail.protection.outlook.com"
        Subject = "User $uUserPrincipalName was created"
    }
    Send-MailMessage @MailMessageHt
}
#endregion functions
 
#Connect to SharePoint Online
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($SPOUrl)
$Context.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($User, $Pass)
#Get the list
$List = $Context.Web.Lists.GetByTitle($SPOList)
$Query = [Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery(100) 
$Items = $List.GetItems($Query)
$Context.Load($Items)
$Context.ExecuteQuery()
 
#Loop through list items
foreach($item in $Items) {
    #Get list item for later reference
    $ListItem = [Microsoft.SharePoint.Client.ListItem]$listItem = $List.GetItemById($Item.FieldValues["ID"])
    if ($item.FieldValues[$SPListItemColumns.Status] -eq "New") {
        Write-Verbose "Processing list item $Firstname $LastName with ID=$($item.FieldValues["ID"])"
 
        #Put the fieldvalues in variables.
        $FirstName = $item.FieldValues[$SPListItemColumns.FirstName]
        $LastName = $item.FieldValues[$SPListItemColumns.LastName]
        $Title = $item.FieldValues[$SPListItemColumns.Title]
        $Manager = $item.FieldValues[$SPListItemColumns.Manager].LookupValue
        #Generate Sam, UserPrincipalName/Email and the managers DN
        try {
            $sAMAccountName = New-JDSamAccountName -FirstName $Firstname -LastName $LastName
            $UPNandMail = New-JDUPNAndMail -FirstName $Firstname -LastName $LastName -UPNSuffix $UPNSuffix
            $ManagerDN = Get-JDDNFromUPN -UserPrincipalName $Manager
 
            #Create the user in Active Directory
            $NewAdUserHt = @{
                FirstName = $Firstname
                LastName = $LastName
                Manager = $ManagerDN
                sAMAccountName = $sAMAccountName
                UserPrincipalName = $UPNandMail
                Title = $Title
                OU = $OrganizationalUnit
                PasswordLength = $PasswordLength
            }
            $ADUser = New-JDADUser @NewAdUserHt -ErrorAction Stop
            Write-Verbose "Created the AD user $sAMAccountName with UPN $UPNandMail"
 
            #Update EmailAddress in the SharePoint List
            $ListItem[$SPListItemColumns.Mail] = $ADUser.UserPrincipalName
            $ListItem[$SPListItemColumns.Status] = "Completed"
            Write-Verbose "Updated the SharePoint list item with status=Completed"
 
            #Send information to manager
            Send-JDWelcomeEmail -Recipient $Manager -uUserPrincipalName $UPNandMail -usAMAccountName $sAMAccountName -uPassword $ADUser.Password
            Write-Verbose "Sent account information to $Manager"
        } catch {
            Write-Warning $_
        }
        #Execute the changes
        $ListItem.Update()
        $Context.ExecuteQuery()
    }
}

Having understood all the functions and settings, we are now ready to run the script!
2015-05-14_20-33-46
The script executed successfully, which means we should now be able to add the script as a scheduled task to automate the task even further.

RESULTS
We now have a properly created AD user according to my organizations needs, and the manager has got an email with the account information needed. The only thing left now is waiting for the directory synchronization and licensing scripts to kick in, and the user will be all set.

2015-05-14_21-09-06
Created AD user

2015-05-14_20-07-25
Updated SharePoint list item

2015-05-14_20-06-31
Information email sent to manager

Hopefully this has given you some useful information regarding how you can create your simple self service tool with some help from things you already have. Let me know if you have any feedback!

/Johan

Advertisement

10 thoughts on “Creating AD users with help from SharePoint Online and PowerShell

  1. Anders Bäck

    Hi Johan, as always, awesome scripts!

    A small suggestion though; to avoid problems related to replication in larger environments with several domain controllers, I usually “target” all my Get-AD*/Set-AD* cmdlets with the -Server parameter. To dynamically get a domain controller I usually use this snippet (after I have loaded the Active Directory module):
    $DomainController = (Get-ADDomainController).hostname
    and then in the different Get-AD*/Set-AD* cmdlets I use the $DomainController variable, for example like this:
    Get-ADUser -Filter {} -Server $DomainController

    /A

    Reply
    1. Johan Dahlbom Post author

      Yes, that’s a good thing to do if you have a large environment 🙂 I have another function for that and are often using something like the following.


      $DomainController = (Get-ADDomainController).hostname
      $PSDefaultParameterValues = @{
      '*-AD*:Server'="$DomainController"
      }

      By doing that I don’t have to enter the -Server parameter each time.

      Reply
  2. Everton Menezes

    Hi Johan,

    It is an awesome script thanks for sharing it.
    When I run the script I am getting the error below:
    WARNING: Cannot bind argument to parameter ‘FirstName’ because it is an empty string.

    I made sure the onboarding list in sharepoint is correct and that I added a new item.
    The script is stopping here for some reason.

    $SPListItemColumns = @{
    FirstName = “First_x0020_Name”
    LastName = “Last_x0020_Name”
    Title = “Title”
    Manager = “Manager”
    Status = “Status”
    Mail = “EmailAddress”

    I tried to change the script and the list to FirstName without space and removed the _x0020_ but not luck.

    I would like to know if you can share your ideas on how can I resolve it.

    Thanks

    Reply
  3. Pingback: Create AD Users with help from Azure Automation and SharePoint Online | Tailspintoys – 365lab.net

  4. Lauri Rantanen

    Awesome script !

    I was wondering would (any of) you be able to help me with one problem.. I would love to use old sAMAccount names in new environment (Sharepoint 2013 online), like i did in old environment (sharepoint 2010 on premise). How’s that possible or is it?

    We have old infopath forms and with sAMAccount name we could get nice amount of info from Active Directory (like manager, costcenter, locations etc etc)..

    This Share2013 online people picker offers so limited info, because that info is coming from azure ad, and ppl would love to use that old and familiar sAMAccount name in forms… So is this possible? Best hint gets fee 😉

    Reply
  5. Jens

    Hat of to you and your waste skillz.

    Like many people here I have a question. I just need to know if its possible, then i will try and hash it out.

    I want to delegate the ability to unlock AD accounts to helpdesk-staff and some are using mac, so I thought that it might be possible to use a sharepoint online page where they can type in the username of the locked account, or even better, populate a list of accounts that is currently locked out and from sharepoint run a ps script or something that unlocks the requested account.

    if you dont have time/want to I can try and fix it, but i just need to know if this is a possible solution.

    Running Win2012r2 DC´s with Office E1 accounts for users. No payed Azure.

    Thanks so much for this grate site

    Reply
  6. Rob

    Great script!

    I’m trying to pull a location drop down field from Sharepoint and then set certain properties in AD depending on which city is selected. I know I need an if statement but i’m unsure on where to place it in the script. Can you please help me out with what I need and how to insert it?

    Thanks again.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s