# encoding: utf-8
# api: powershell
# title:
# description: PS Module for creating new hires with functions for Exchange Online Mailbox, Skype voice provisioning, and UM Mailbox provisioning
# version: 3.0
# type: module
# license: CC0
# function: Test-DependentModule
# x-poshcode-id: 6360
# x-archived: 2016-09-30T14:22:33
#
# Updated with corrections to bad markup from $defaultProperties = @(‘DisplayName’,’EmployeeID’,‘SamAccountName’,‘UPN’,‘SkypeNumber’,‘Password’)
#
<#
.SYNOPSIS
This module automates many of the tasks involved in processing a new starter on key Contoso systems.
.DESCRIPTION
This module contains a number of helper and core functions for the purpose of provisioning a new starter on crucial Contoso systems like Active Directory, Office 365 and Lync/Skype. Two main functions
allow the user to either process a single new starter or import a group of new starters from a CSV file. Please note that this module checks for and will warn the user if a number of pre-requisite modules
are not present on the system. The module will prompt for on-premise and Office 365 credentials if not found, and depends on an external script called Get-CSPhoneList.ps1 to provision a Skype phone number.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0, Active Directory Module, Service Manager SMLets module, MSOnline module, Get-CsPhoneList.ps1 script for phone numbers
#>
#region Module level variables
$GLOBAL:smdefaultcomputer = "scsm.Contoso.com"
$GLOBAL:SAMAccountRefreshTimestamp = (Get-Date).AddDays(-1)
$GLOBAL:UPNRefreshTimeStamp = (Get-Date).AddDays(-1)
$GLOBAL:AllSAMAccountNames = @()
$GLOBAL:AllUPNs = @()
$CompanyDomains = @("Contoso.com","TailSpinToys.com")
$RegionMappings = @{'US'='AMER';'AR'='AMER';'PE'='AMER';'PA'='AMER';'CL'='AMER';'CA'='AMER';'AU'='ASIA';'NZ'='ASIA';'CN'='ASIA';'IN'='ASIA';'BH'='EMEA';'BE'='EMEA'`
;'CY'='EMEA';'EG'='EMEA';'ET'='EMEA';'FJ'='ASIA';'IL'='EMEA';'IT'='EMEA';'JM'='AMER';'KW'='EMEA';'LS'='EMEA';'BN'='ASIA';'NP'='ASIA';'NL'='EMEA'`
;'PK'='ASIA';'QA'='EMEA';'WS'='ASIA';'SG'='ASIA';'LK'='ASIA';'TW'='ASIA';'TR'='EMEA';'AE'='EMEA';'UK'='EMEA'}
#endregion
#region Check for dependent modules
##Check for MSOnline and ActiveDirectory modules when Contosostarter module loads
Function Test-DependentModule {
If (Get-Module -ListAvailable -Name MSOnline) {
Write-Host "MSOnline Module exists"
} else {
Write-Host "MSOnline Module does not exist. Please install it from https://msdn.microsoft.com/en-us/library/jj151815.aspx#bkmk_installmodule before using this module." -ForegroundColor Red
}
If (Get-Module -ListAvailable -Name ActiveDirectory) {
Write-Host "ActiveDirectory Module exists"
} else {
Write-Host "ActiveDirectory Module does not exist. Please install it from https://www.microsoft.com/en-us/download/details.aspx?id=7887 before using this module." -ForegroundColor Red
}
If (Get-Module -ListAvailable -Name SMLets) {
Write-Host "SMLets Module exists"
} else {
Write-Host "SMLets Module does not exist. Please install System Center Service Manager Console before using this module." -ForegroundColor Red
}
}
Test-DependentModule
#endregion
#region Connect to Services
Function Connect-Office365 {
$MSOLServiceCred = Get-MSOLCredential
$Session = New-PSSession -ConfigurationName microsoft.exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $MSOLServiceCred -Authentication Basic -AllowRedirection
Import-PSSession $Session -AllowClobber -DisableNameChecking
}
Function Connect-Exchange2013 {
Param(
[Parameter(Position=0,Mandatory=$False)]
[string]$ExchangeServer = "ExchangeServer.Contoso.com"
)
$ExSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$ExchangeServer/PowerShell/" -Authentication Kerberos
Import-PSSession $ExSession -DisableNameChecking -AllowClobber
}
Function Connect-AzureAD {
$MSOLServiceCred = Get-MSOLCredential
Connect-MsolService -Credential $MSOLServiceCred
}
Function Connect-Skype {
$DownLevelCred = Get-DownLevelCredential
$SkypeSessionOption = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$SkypeSession = New-PSSession -ConnectionUri https://lyncserver.Contoso.com/ocspowershell -SessionOption $SkypeSessionOption -Credential $DownLevelCred -Name "SkypeServerSession"
Import-PSSession $SkypeSession -DisableNameChecking -AllowClobber
}
Function Connect-DirSync {
Param(
[Parameter(Position=0,Mandatory=$False)]
[string]$DirSyncServer = "Dirsync.Contoso.com"
)
$DirSyncSession = New-PSSession -ComputerName $DirSyncServer -Authentication Kerberos
Import-PSSession $DirSyncSession -AllowClobber -DisableNameChecking
}
#endregion
#region Supporting Functions
Function Test-Credential {
<#
.SYNOPSIS
Takes a PSCredential object and validates it against the domain.
.PARAMETER Credential
A PScredential object with the username/password you wish to test. Typically this is generated using the Get-Credential cmdlet. Accepts pipeline input.
.OUTPUTS
A boolean, indicating whether the credentials were successfully validated.
#>
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true)]
[System.Management.Automation.PSCredential]$credential
)
$Username = $credential.username
$Password = $credential.GetNetworkCredential().password
# Get current domain using logged-on user's credentials
$CurrentDomain = "LDAP://" + ([ADSI]"").distinguishedName
$Domain = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,$Username,$Password)
$DomainName = $Domain.name
If ($DomainName -eq $null) {
$false
} else {
$true
}
$Password = $null
}
Function Get-MSOLCredential {
<#
.SYNOPSIS
Checks for and imports a local encrypted credential file.
.DESCRIPTION
This function checks for an encrypted XML file in the local directory to use for Cloud credentials. If the file isn't found, the credentials are prompted for and then stored in the
encrypted XML file.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0, Active Directory Module
.EXAMPLE
Get-MSOLCredential
#>
If (Test-Path -Path ".\MSOLServiceCredential.xml") {
$MSOLServiceCred = Import-Clixml .\MSOLServiceCredential.xml
If (!(Test-Credential $MSOLServiceCred)) {
Get-Credential -Message "Please enter a valid Office 365 username and password" | Export-Clixml .\MSOLServiceCredential.xml
$MSOLServiceCred = Import-Clixml .\MSOLServiceCredential.xml
}
}
Else {
Get-Credential -Message "Please enter a valid Office 365 username and password" | Export-Clixml .\MSOLServiceCredential.xml
$MSOLServiceCred = Import-Clixml .\MSOLServiceCredential.xml
}
Return $MSOLServiceCred
}
Function Get-DownLevelCredential {
<#
.SYNOPSIS
Checks for and imports a local encrypted credential file.
.DESCRIPTION
This function checks for an encrypted XML file in the local directory to use for Domain credentials. If the file isn't found, the credentials are prompted for and then stored in the
encrypted XML file.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0, Active Directory Module
.EXAMPLE
Get-DownLevelCredential
#>
If (Test-Path -Path ".\DownlevelCredential.xml") {
$DownLevelCred = Import-Clixml .\DownlevelCredential.xml
If (!(Test-Credential $DownLevelCred)) {
Get-Credential -Message "Please enter credentials using the DOMAIN\username format" | Export-Clixml .\DownlevelCredential.xml
$DownLevelCred = Import-Clixml .\DownlevelCredential.xml
}
}
Else {
Get-Credential -Message "Please enter credentials using the DOMAIN\username format" | Export-Clixml .\DownlevelCredential.xml
$DownLevelCred = Import-Clixml .\DownlevelCredential.xml
}
Return $DownLevelCred
}
Function Get-RandomPassword {
<#
.SYNOPSIS
Generates a random password based on approved characters and a given length.
.DESCRIPTION
This function generates a random password string that meets company requirements for length and complexity based on an approved character set. Longer or shorter passwords can be generated
based on an optional length however the function will throw an error if the requested password length is too short.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0, Active Directory Module
.EXAMPLE
Get-RandomPassword -length 14
.PARAMETER length
Optional parameter for the length of the password to return. If not specified the function defaults to a 10 character password.
#>
Param(
[Parameter(Position=0,Mandatory=$False)]
[int]$length=10
)
if ($length -lt 8) {
throw "Password length is too short"
}
# Define list of numbers, this will be CharType 1
$numbers=$null
For ($a=48;$a –le 57;$a++) {$numbers+=,[char][byte]$a }
# Define list of uppercase letters, this will be CharType 2
$uppercase=$null
For ($a=65;$a –le 90;$a++) {$uppercase+=,[char][byte]$a }
# Define list of lowercase letters, this will be CharType 3
$lowercase=$null
For ($a=97;$a –le 122;$a++) {$lowercase+=,[char][byte]$a }
# Define list of special characters, this will be CharType 4
$specialchars='!#%$&~?<>+-:;'.ToCharArray()
# Need to ensure that result contains at least one of each CharType
# Initialize buffer for each character in the password
$Buffer = @()
For ($a=1;$a –le $length;$a++) {$Buffer+=0 }
# Randomly chose one character to be number
while ($true) {
$CharNum = (Get-Random -minimum 0 -maximum $length)
if ($Buffer[$CharNum] -eq 0) {$Buffer[$CharNum] = 1; break}
}
# Randomly chose one character to be uppercase
while ($true) {
$CharNum = (Get-Random -minimum 0 -maximum $length)
if ($Buffer[$CharNum] -eq 0) {$Buffer[$CharNum] = 2; break}
}
# Randomly chose one character to be lowercase
while ($true) {
$CharNum = (Get-Random -minimum 0 -maximum $length)
if ($Buffer[$CharNum] -eq 0) {$Buffer[$CharNum] = 3; break}
}
# Randomly chose one character to be special
while ($true) {
$CharNum = (Get-Random -minimum 0 -maximum $length)
if ($Buffer[$CharNum] -eq 0) {$Buffer[$CharNum] = 4; break}
}
# Cycle through buffer to get a random character from the available types
# if the buffer already contains the CharType then use that type
$Password = ""
foreach ($CharType in $Buffer) {
if ($CharType -eq 0) {$CharType = ((1,2,3,4)|Get-Random)}
switch ($CharType) {
1 {$Password+=($numbers | Get-Random)}
2 {$Password+=($uppercase | Get-Random)}
3 {$Password+=($lowercase | Get-Random)}
4 {$Password+=($specialchars | Get-Random)}
}
}
return $Password
}
Function Get-AllSAMAccountNames {
<#
.SYNOPSIS
Queries Active Directory for all SAMAccountNames.
.DESCRIPTION
This function queries Active Directory for all SAMAccountNames and stores them in a variable for use by other functions.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0, Active Directory Module
.EXAMPLE
Get-AllUPNs
#>
$GLOBAL:SAMAccountRefreshTimestamp = Get-Date
Write-Verbose "Refreshing SAM Accounts..."
$GLOBAL:AllSAMAccountNames = (Get-ADUser -Filter * | Select-Object SAMAccountName).SAMAccountName
Write-Verbose "Refresh complete."
}
Function Get-AllUPNs {
<#
.SYNOPSIS
Queries Active Directory for all UserPrincipalNames.
.DESCRIPTION
This function queries Active Directory for all UserPrincipalNames and stores them in a variable for use by other functions.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0, Active Directory Module
.EXAMPLE
Get-AllUPNs
#>
$GLOBAL:UPNRefreshTimeStamp = Get-Date
Write-Verbose "Refreshing UPNs..."
$GLOBAL:AllUPNs = (Get-ADUser -Filter * | Select-Object UserPrincipalName).UserPrincipalName
Write-Verbose "Refresh complete."
}
Function Get-UniqueSAMAccountName {
<#
.SYNOPSIS
Queries Active Directory for a unique SAMAccountName based on provided attributes.
.DESCRIPTION
This function queries Active Directory for a unique SAMAccountName based on provided information. The function checks to see if it's local cache of SAMAccountNames is older than 5 minutes and will
update if necessary. Then based on a recent cache of SAMAccountNames, the function will calculate several possible SAMAccountName combinations and compare the results against the cache. If a unique
value is found the function will return the value. If a unique value could not be determined, the function will throw an error.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0, Active Directory Module
.EXAMPLE
Get-UniqueSAMAccountName -GivenName John -Surname Smith
.EXAMPLE
Get-UniqueSAMAccountName -GivenName John -Initial A -Surname Smith
.PARAMETER GivenName
Required parameter for the first name of the user. Apostrophes should be ommitted from the name to ensure compatibility with IT systems.
.PARAMETER Surname
Required parameter for the last name of the user. Apostrophes should be ommitted from the name to ensure compatibility with IT systems.
.PARAMETER Initial
Optional parameter for the first letter of the user's middle initial.
#>
Param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$GivenName,
[Parameter(Position=1,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Surname,
[Parameter(Position=2,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Initial
)
#Check if SAM Account cache is older than 5 minutes, update if needed
If ((Get-Date) -gt ($GLOBAL:SAMAccountRefreshTimestamp.AddMinutes(5))) {
Get-AllSAMAccountNames
}
#Validate that SAMAccountNames has at least 10 entries in it
If ($GLOBAL:AllSAMAccountNames.Length -lt 10) {
throw "SAM Account List is blank, unable to refresh from Active Directory"
}
#Remove Spaces from GivenName and Surname along with Apostrophes
$GivenName = $GivenName.Replace("'","")
$GivenName = $GivenName.Split(" ")[0]
$Surname = $Surname.Replace("'","")
$Surname = $Surname.Replace(" ","")
#CombinationsToTry
$ComboArray = @()
$ComboArray += ,@(6,1,'')
$ComboArray += ,@(6,2,'')
$ComboArray += ,@(5,2,'')
$ComboArray += ,@(5,3,'')
$ComboArray += ,@(6,3,'')
$ComboArray += ,@(5,4,'')
$ComboArray += ,@(4,4,'')
$ComboArray += ,@(6,2,'x')
$ComboArray += ,@(5,3,'x')
#Attempt to create a unique SamAccountName less than 9 characters, required for E1 compatibility
$ValidSAMAccountName = $false
If ($Initial -match "\w+") {
Foreach ($Combo in $ComboArray) {
#Use Regex to get correct number of characters without erroring out if there are fewer than the inital values
$PotentialSAMAccountName = ([regex]"\w{1,$($combo[0])}").Match($Surname).Groups[0].Value + ([regex]"\w{1,$($combo[1])}").Match($GivenName).Groups[0].Value + $Initial.Substring(0,1)
$result = $GLOBAL:AllSAMAccountNames | Where-Object {$_ -eq $PotentialSAMAccountName}
If ($result -eq $null) {
#Unique SAM accountname found, return the object and break the foreach loop
$ValidSAMAccountName = $true
$PotentialSAMAccountName = $PotentialSAMAccountName.ToLower()
break
}
}
}
Else {
Foreach ($Combo in $ComboArray) {
#Use Regex to get correct number of characters without erroring out if there are fewer than the inital values
$PotentialSAMAccountName = ([regex]"\w{1,$($combo[0])}").Match($Surname).Groups[0].Value + ([regex]"\w{1,$($combo[1])}").Match($GivenName).Groups[0].Value
$result = $GLOBAL:AllSAMAccountNames | Where-Object {$_ -eq $PotentialSAMAccountName}
If ($result -eq $null) {
#Unique SAM accountname found, return the object and break the foreach loop
$ValidSAMAccountName = $true
$PotentialSAMAccountName = $PotentialSAMAccountName.ToLower()
break
}
}
}
If (($ValidSAMAccountName -eq $true) -and ($PotentialSAMAccountName -match "^\w{2,9}$")) {
return $PotentialSAMAccountName
}
Else {
throw "No valid SAMAccountName was able to be generated"
}
}
Function Get-UniqueUPN {
<#
.SYNOPSIS
Queries Active Directory for a unique UserPrincipalName based on provided attributes.
.DESCRIPTION
This function queries Active Directory for a unique userprincipalname based on provided information. The function checks to see if it's local cache of UPNs is older than 5 minutes and will
update if necessary. Then based on a recent cache of UPNs, the function will calculate several possible UserPrincipalName combinations and compare the results against the cache. If a unique
value is found the function will return the value. If a unique value could not be determined, the function will throw an error.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0, Active Directory Module
.EXAMPLE
Get-UniqueUPN -GivenName John -Surname Smith -Company Contoso
.EXAMPLE
Get-UniqueUPN -GivenName John -Initial A -Surname Smith -Company TailSpinToys
.PARAMETER GivenName
Required parameter for the first name of the user. Apostrophes should be ommitted from the name to ensure compatibility with IT systems.
.PARAMETER Surname
Required parameter for the last name of the user. Apostrophes should be ommitted from the name to ensure compatibility with IT systems.
.PARAMETER Initial
Optional parameter for the first letter of the user's middle initial.
.PARAMETER Company
Required parameter for the sub-company of the user. This will be used to populate the UPN suffix; valid entries are 'Contoso','TailSpinToys','Hawksley','Burton','Slayden','JV'.
#>
Param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$GivenName,
[Parameter(Position=1,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Surname,
[Parameter(Position=2,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Initial,
[Parameter(Position=3,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)][ValidateSet('Contoso','TailSpinToys','JV')]
[string]$Company
)
#Check if SAM Account cache is older than 5 minutes, update if needed
If ((Get-Date) -gt ($GLOBAL:UPNRefreshTimeStamp.AddMinutes(5))) {
Get-AllUPNs
}
#Validate that SAMAccountNames has at least 10 entries in it
If ($GLOBAL:AllUPNs.Length -lt 10) {
throw "UPN List is blank, unable to refresh from Active Directory"
}
#Remove Spaces from GivenName and Surname along with Apostrophes
$GivenName = $GivenName.Replace("'","")
$GivenName = $GivenName.Split(" ")[0]
$Surname = $Surname.Replace("'","")
$Surname = $Surname.Replace(" ","")
#Get domain name from company list, if no match default to Contoso.com
If ($CompanyDomains -match $Company) {
$DomainName = ($CompanyDomains -match $Company)
}
Else {
$DomainName = "Contoso.com"
}
#First attempt to generate a unique UPN
If ($Initial -match "\w+") {
$PotentialUPN = $GivenName + "." + $Initial + "." + $Surname + "@" + $DomainName
}
Else {
$PotentialUPN = $GivenName + "." + $Surname + "@" + $DomainName
}
#Test the name for any matches
$result = $GLOBAL:AllUPNs | Where-Object {$_ -eq $PotentialUPN}
If ($result -eq $null) {
return $PotentialUPN
}
Else {
#Generate another potential UPN and test
$PotentialUPN = $GivenName + "." + $Surname + "@" + $DomainName
$result = $GLOBAL:AllUPNs | Where-Object {$_ -eq $PotentialUPN}
If ($result -eq $null) {
return $PotentialUPN
}
Else {
#Generate one last potential UPN and test
$PotentialUPN = $GivenName + "." + "X" + "." + $Surname + "@" + $DomainName
$result = $GLOBAL:AllUPNs | Where-Object {$_ -eq $PotentialUPN}
If ($result -eq $null) {
return $PotentialUPN
}
Else {
throw "No valid UPN was able to be generated"
}
}
}
}
Function Invoke-DirSync {
<#
.SYNOPSIS
Runs a manual sync for the Azure AD Connect server when called.
.DESCRIPTION
This function uses PowerShell Remoting to start a manual delta sync on the Azure AD Connect server. The server can be specified or the default accepted for the Contoso domain. If
running the function against an Azure AD Connect Server in another domain, the user running the cmdlet should have rights to remote into and execute AAD Connect cmdlets on that server.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0
.EXAMPLE
Invoke-DirSync
.EXAMPLE
Invoke-DirSync -DirSyncServer "12345-Dirsync01.contoso.com"
.PARAMETER DirSyncServer
Optional parameter for when using an alternate Azure AD Connect server. A full FQDN is recommended to ensure the correct server can be found.
#>
Param(
[Parameter(Position=0,Mandatory=$False)]
[string]$DirSyncServer = "dirsync.Contoso.com",
[Parameter(Position=1,Mandatory=$False)]
[int]$TimetoWait = 60
)
For ($TimetoWait=60; $TimetoWait -gt 1; $TimetoWait--) {
Write-Progress -Activity "Waiting $TimetoWait seconds for Active Directory synchronization..." `
-SecondsRemaining $TimetoWait -Status "Please wait."
Start-Sleep 1
}
$DirSyncSession = New-PSSession -ComputerName $DirSyncServer -Authentication Kerberos
Invoke-Command -Session $DirSyncSession -ScriptBlock {Import-Module -Name adsync;$ErrorActionPreference = 'SilentlyContinue'}
Invoke-Command -Session $DirSyncSession -ScriptBlock {Start-ADSyncSyncCycle -PolicyType Delta;$ErrorActionPreference = 'Continue'}
For ($TimetoWait=60; $TimetoWait -gt 1; $TimetoWait--) {
Write-Progress -Activity "Waiting $TimetoWait seconds for DirSync to complete..." `
-SecondsRemaining $TimetoWait -Status "Please wait."
Start-Sleep 1
}
}
Function Confirm-ValidUser {
<#
.SYNOPSIS
Checks user input for valid data.
.DESCRIPTION
This function cleans up and checks incoming data for a new user for valid input so that it can be passed to other functions. If any unrecoverable errors are found, the function will
throw an appropriate error. The object returned should match all fields that were given to it.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0
.EXAMPLE
Confirm-ValidUser -GivenName John -Surname Smith -EmployeeID 12345 -EmployeeType F -OfficeCode "US-DEN-1"
.EXAMPLE
Confirm-ValidUser -GivenName John -Surname Smith -Initial A -EmployeeID 12345 -EmployeeType F -BU 11111 -OfficeCode "US-DEN-1"
.PARAMETER GivenName
Required parameter for the first name of the user. Apostrophes should be ommitted from the name to ensure compatibility with IT systems.
.PARAMETER Surname
Required parameter for the last name of the user. Apostrophes should be ommitted from the name to ensure compatibility with IT systems.
.PARAMETER Initial
Optional parameter for the first letter of the user's middle name.
.PARAMETER PreferredGivenName
Optional parameter for a preferred first name provided by the user upon hire. This will only populate the Display Name, and does not change the logon attributes for the user.
.PARAMETER EmployeeID
Required parameter for the new user's employee number. Valid entries should be either between 5-8 digits, a C for contractor or a dash if unknown.
.PARAMETER EmployeeType
Required parameter for the employee type. Valid examples include F for Full-time, T for Temporary, C for Contractor, JV for a Joint Venture user, or - if unknown.
.PARAMETER Description
Optional parameter for the user description. Often includes the title and office code for the user, but can be any additional information such as the requestor of a contractor account, etc.
.PARAMETER BU
Optional parameter for the Business Unit of the new user. Valid entries are a 5 digit number only, and if not provided will usually be filled in by sync from EID.
.PARAMETER OfficeCode
Required parameter for the office code of the new user. Valid entries should start with a two letter country code and then a dash. For most new users, this is later replaced by
the sync from EID, but should be provided so that the Office 365 country code can be filled in appropriately.
.PARAMETER Company
Required parameter for the sub-company of the new user. Valid Entries include Contoso, TailSpinToys, Hawksley, JV. This is required to set the primary email address, Skype
address and UPN correctly.
.PARAMETER SRNumber
Optional parameter for the Service Request number of the new user. If provided PowerShell will attempt to update the Service Request ticket with the information from the new starter script.
.PARAMETER EnableMailbox
Optional parameter indicating if the user should be enabled for a mailbox or not. A value of 'N' or 'No' will cause the functions to not create the mailbox. Anything else entered or nothing at all will
cause the script to assume an account is to be created.
.PARAMETER EnableSkype
Optional parameter indicating if the user should be enabled for a skype account or not. A value of 'N' or 'No' will cause the functions to not create the skype account. Anything else entered or nothing at all will
cause the script to assume an account is to be created.
#>
[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$GivenName="",
[Parameter(Position=1,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Surname="",
[Parameter(Position=2,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Initial="",
[Parameter(Position=3,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$PreferredGivenName="",
[Parameter(Position=4,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$EmployeeID="",
[Parameter(Position=5,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)][ValidateSet('F','T','C','JV','-')]
[string]$EmployeeType="",
[Parameter(Position=6,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Description="",
[Parameter(Position=7,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)][ValidateRange(00000,99999)]
[int]$BU="",
[Parameter(Position=8,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$OfficeCode="",
[Parameter(Position=9,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)][ValidateSet('Contoso','TailSpinToys','JV')]
[string]$Company="Contoso",
[Parameter(Position=10,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$SRNumber="",
[Parameter(Position=11,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$EnableMailbox="",
[Parameter(Position=12,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$EnableSkype=""
)
#Trim any excess white spaces from the incoming data
$UserObject = New-Object -TypeName psobject -Property @{
EmployeeID = $EmployeeID.Trim()
OfficeCode = $OfficeCode.Trim()
BU = $BU
Surname = $Surname.Trim()
PreferredGivenName = $PreferredGivenName.Trim()
GivenName = $GivenName.Trim()
Initial = $Initial.Trim()
Company = $Company.Trim()
EmployeeType = $EmployeeType.Trim()
Description = $Description.Trim()
ValidInput = $False
FullName = ""
SRNumber = $SRNumber.Trim()
EnableSkype = $EnableSkype
EnableMailbox = $EnableMailbox
}
#Create FullName depending on if initial was provided or not
If ($Initial -match "\w+") {
$FullName = $UserObject.GivenName + " " + $UserObject.Initial + " " + $UserObject.Surname
}
Else {
$FullName = $UserObject.GivenName + " " + $UserObject.Surname
}
#Check for mandatory fields and throw an error if the tests fail
If (!(($UserObject.EmployeeID -match "^\d{5,8}$") -or ($UserObject.EmployeeID -match "c") -or ($UserObject.EmployeeID -eq "-"))) {
#Employee ID doesn't meet any of the valid conditions
throw "$FullName with ID ($UserObject.EmployeeID) has invalid Employee ID, valid entries are 5-8 digits, 'c' or '-'"
}
Elseif (!(($UserObject.OfficeCode -match "^\w{2}\-\w{2}") -or ($UserObject.OfficeCode -eq "-"))) {
#Office Code doesn't meet any of the valid conditions
throw "$FullName with ID ($UserObject.EmployeeID) has invalid Office Code"
}
Else {
$UserObject.FullName = $FullName
$UserObject.ValidInput = $True
return $UserObject
}
}
Function Update-SCSMStarterTicket {
<#
.SYNOPSIS
Updates the SCSM Activities for a given new starter.
.DESCRIPTION
This function updates the activities for a new starter if required. The function requires the ticket number for the starter and key pieces of information in ordert to populate the
notes on the activities. All fields are required except SkyperNumber in order to ensure the ticket is updated correctly, and the SR itself will not be closed out until manual review is
performed. Quotation marks are recommended around all fields to ensure that the data is read in correctly.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0, SMLets module for Service Manager
.EXAMPLE
Update-SCSMStarterTicket -SRNumber SR12345 -UserPrincipalName "John.Tester@Contoso.com" -SAMAccountName testerj -SkypeNumber "+1 720 887 8700"
.EXAMPLE
Update-SCSMStarterTicket -SRNumber SR12345 -UserPrincipalName "John.Tester@Contoso.com" -SAMAccountName testerj
.PARAMETER SRNumber
Required parameter for the ticket in Service Manager to update. If no ticket exists, one will need to be created before this can be run.
.PARAMETER UserPrincipalName
Required parameter for the UPN of the user. This will be added to the AD/O365/Lync Activities linked in the ticket. Should these differ, the ticket should be manually changed
to reflect the discrepancy.
.PARAMETER SAMAccountName
Required parameter for the SAMAccountName of the user. This will be added to the AD and E1 activities in the ticket and is required before an E1 login can be provisioned.
.PARAMETER SkypeNumber
Optional parameter for the Skype/Lync number assigned to the user. This only needs to be populated if the Office location is Lync Enterprise Voice enabled. This can be in any format required but should
be enclosed in quotation marks to ensure that the data is input correctly.
#>
Param(
[Parameter(Position=0,Mandatory=$True)]
[string]$SRNumber,
[Parameter(Position=1,Mandatory=$True)]
[string]$UserPrincipalName,
[Parameter(Position=2,Mandatory=$True)]
[string]$SAMAccountName,
[Parameter(Position=3,Mandatory=$False)]
[string]$SkypeNumber
)
Write-Host "Updating Service Manager Activities in ticket $SRNumber for Employee: $UserPrincipalName" -ForegroundColor Green
#Get the Service Request Class and values for the ticket
$serviceRequestClass = Get-SCSMClass -name System.WorkItem.ServiceRequest$
$manualActivityClass = Get-SCSMClass -Name System.WorkItem.Activity.ManualActivity$
$ServiceRequest = Get-SCSMObject -Class $serviceRequestClass -Filter "ID -eq $SRNumber"
If ($ServiceRequest.DisplayName -match "Starter") {
#Get the manual activities related to the SR
$ManualActivities = Get-SCSMRelationshipObject -BySource $serviceRequest | Select-Object sourceobject -unique | Where-Object {$_.SourceObject -like "MA*"}
$ManualActivityIDs = $ManualActivities | ForEach-Object {$string=$_.sourceobject.tostring();$string.split(':')[0]}
Foreach ($ActID in $ManualActivityIDs) {
$MAObject = Get-SCSMObject -Class $manualActivityClass -Filter "ID -eq $ActID"
Switch -Wildcard ($MAObject.Title) {
"*AD Activities*" {
Get-SCSMObject -Class $manualActivityClass -Filter "ID -eq $ActID" | Set-SCSMObject -PropertyHashtable @{'Status'='Completed';'Notes'="Activity Completed. `nUserPrincipalName set to $UserPrincipalName `nSAMAccountName set to $SAMAccountName"}
}
"*E1*" {
$MADescription = $MAObject.Description
Get-SCSMObject -Class $manualActivityClass -Filter "ID -eq $ActID" | Set-SCSMObject -PropertyHashtable @{'Status'='Active';'Description'="$MADescription `nSAMAccountName generated as $SAMAccountName"}
}
"*O365*" {
Get-SCSMObject -Class $manualActivityClass -Filter "ID -eq $ActID" | Set-SCSMObject -PropertyHashtable @{'Status'='Completed';'Notes'="Activity Completed. `nSIP address set to $UserPrincipalName"}
}
"*Enterprise Voice*" {
If (!($SkypeNumber -eq $null)) {
#If a Lync voice number was assigned, set "Lync Unified Messaging and Enterprise Voice" activity as complete and put LineURI in notes
Get-SCSMObject -Class $manualActivityClass -Filter "ID -eq $ActID" | Set-SCSMObject -PropertyHashtable @{'Status'='Completed';'Notes'="Activity Completed. `nLineURI set to $SkypeNumber"}
}
Else {
Get-SCSMObject -Class $manualActivityClass -Filter "ID -eq $ActID" | Set-SCSMObject -Property Status -Value Active
}
}
"*Communication*" {
Get-SCSMObject -Class $manualActivityClass -Filter "ID -eq $ActID" | Set-SCSMObject -Property Status -Value Active
}
"*IFS Access*" {
Get-SCSMObject -Class $manualActivityClass -Filter "ID -eq $ActID" | Set-SCSMObject -Property Status -Value Active
}
}
}
}
Else {
throw "Service Request $SRNumber could not be found"
}
}
#endregion
#region Core Functions
Function New-ContosoStarter {
<#
.SYNOPSIS
Creates the necessary accounts for a new user in Active Directory, Exchange/Office 365, and Skype for Business.
.DESCRIPTION
This function takes in user input and kicks off the processes required to create a new user on several key Contoso systems. The script will create an AD account with the various required
parameters, enable a Hybrid mailbox for that person, provision the necessary Office 365 license, configure a Skype for Business account and enable a phone number/voicemail box if the office
that the user is located in is enabled for Enterprise Voice. Due to sync processes, please note that the function can take up to 10-15 minutes to run.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0, Active Directory PowerShell module, Remote connections to Exchange/Lync, MSOnline Module for Office 365 provisioning, SMLets module for Service Manager
.EXAMPLE
New-ContosoStarter -GivenName John -Surname Smith -EmployeeID 12345 -EmployeeType F -OfficeCode "US-DEN-1"
.EXAMPLE
New-ContosoStarter -GivenName John -Surname Smith -Initial A -EmployeeID 12345 -EmployeeType F -BU 11111 -OfficeCode "US-DEN-1"
.PARAMETER GivenName
Required parameter for the first name of the user. Apostrophes should be ommitted from the name to ensure compatibility with IT systems.
.PARAMETER Surname
Required parameter for the last name of the user. Apostrophes should be ommitted from the name to ensure compatibility with IT systems.
.PARAMETER Initial
Optional parameter for the first letter of the user's middle name.
.PARAMETER PreferredGivenName
Optional parameter for a preferred first name provided by the user upon hire. This will only populate the Display Name, and does not change the logon attributes for the user.
.PARAMETER EmployeeID
Required parameter for the new user's employee number. Valid entries should be either between 5-8 digits, a C for contractor or a dash if unknown.
.PARAMETER EmployeeType
Required parameter for the employee type. Valid examples include F for Full-time, T for Temporary, C for Contractor, JV for a Joint Venture user, or - if unknown.
.PARAMETER Description
Optional parameter for the user description. Often includes the title and office code for the user, but can be any additional information such as the requestor of a contractor account, etc.
.PARAMETER BU
Optional parameter for the Business Unit of the new user. Valid entries are a 5 digit number only, and if not provided will usually be filled in by sync from EID.
.PARAMETER OfficeCode
Required parameter for the office code of the new user. Valid entries should start with a two letter country code and then a dash. For most new users, this is later replaced by
the sync from EID, but should be provided so that the Office 365 country code can be filled in appropriately.
.PARAMETER Company
Required parameter for the sub-company of the new user. Valid Entries include Contoso, TailSpinToys, Hawksley, JV, Slayden, or Burton. This is required to set the primary email address, Skype
address and UPN correctly.
.PARAMETER SRNumber
Optional parameter for the Service Request number of the new user. If provided PowerShell will attempt to update the Service Request ticket with the information from the new starter script.
#>
[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$GivenName="",
[Parameter(Position=1,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Surname="",
[Parameter(Position=2,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Initial="",
[Parameter(Position=3,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$PreferredGivenName="",
[Parameter(Position=4,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$EmployeeID="",
[Parameter(Position=5,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)][ValidateSet('F','T','C','JV','-')]
[string]$EmployeeType="",
[Parameter(Position=6,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Description="",
[Parameter(Position=7,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)][ValidateRange(00000,99999)]
[int]$BU="",
[Parameter(Position=8,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$OfficeCode="",
[Parameter(Position=9,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)][ValidateSet('Contoso','TailSpinToys','JV')]
[string]$Company="Contoso",
[Parameter(Position=10,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$EnableMailbox="",
[Parameter(Position=11,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$EnableSkype="",
[Parameter(Position=12,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$SRNumber
)
$ExecutionStart = Get-Date
Write-Output "New account creation started at $ExecutionStart"
$StarterObject = New-Object -TypeName psobject -Property @{
EmployeeID = "$EmployeeID"
OfficeCode = "$OfficeCode"
BU = $BU
Surname = "$Surname"
PreferredGivenName = "$PreferredGivenName"
GivenName = "$GivenName"
Initial = "$Initial"
Company = "$Company"
EmployeeType = "$EmployeeType"
EnableSkype = $EnableSkype
EnableMailbox = $EnableMailbox
Description = "$Description"
SRNumber = "$SRNumber"
}
Try {
$UserResult = $StarterObject | Confirm-ValidUser
}
Catch {
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
exit
}
##Create AD Account
Write-Output "Attempting to create AD account for $($UserResult.FullName)"
$ADresult = $UserResult | New-ContosoADUser
Write-Host "$($ADresult.UserPrincipalName) created successfully" -ForegroundColor Green
##Enable S4B (if necessary)
If (!(($StarterObject.EnableSkype -eq 'N') -or ($StarterObject.EnableSkype -eq 'No'))) {
Write-Output "Attempting to create Skype account for $($UserResult.FullName)"
$SkypeResult = Enable-ContosoSkypeUser -UserPrincipalName $ADresult.UserPrincipalName
If ($($SkypeResult.LineURI) -like "tel*") {
$ADPhoneNumber = $SkypeResult.LineURI.Split(':')[1]
$ADPhoneNumber = $ADPhoneNumber.Split(';')[0]
Set-Aduser -Identity $SkypeResult.Identity -OfficePhone $ADPhoneNumber
}
}
##Create mailbox and enable O365 license (if necessary)
If (!(($StarterObject.EnableMailbox -eq 'N') -or ($StarterObject.EnableMailbox -eq 'No'))) {
Write-Output "Attempting to create mailbox for $($UserResult.FullName)"
Enable-ContosoMailbox -UserPrincipalName $ADresult.UserPrincipalName
$Country = $ADresult.Office.SubString(0,2)
Invoke-DirSync
Enable-ContosoO365License -UserPrincipalName $ADresult.UserPrincipalName -Country $Country
}
Write-Host "Pausing for 30 seconds to allow Exchange Online to provision mailboxes" -ForegroundColor Cyan
Start-Sleep -Seconds 30
##Enable UM mailbox in O365 (if a Skype number was assigned)
If ($($SkypeResult.LineURI) -like "tel*") {
Write-Output "Attempting to create UM Mailbox for $($UserResult.FullName)"
$MbxResult = Enable-ContosoUMMailbox -UserPrincipalName $ADresult.UserPrincipalName -Office $ADresult.Office
}
$FinishedAccount = New-Object -TypeName psobject -Property @{
Path = $ADresult.Path
DisplayName = $ADresult.DisplayName
EmployeeID = "$EmployeeID"
OfficeCode = "$OfficeCode"
BU = $BU
GivenName = $GivenName
Surname = $Surname
SamAccountName = $ADresult.SamAccountName
UserPrincipalName = $ADresult.UserPrincipalName
SkypeNumber = $ADPhoneNumber
Company = "$Company"
EmployeeType = "$EmployeeType"
Description = "$Description"
EnableSkype = $EnableSkype
EnableMailbox = $EnableMailbox
Password = $ADresult.AccountPassword
SRNumber = $UserResult.SRNumber
}
#If an SR or SR and phone number are found, update the SCSM ticket based on provided parameters
If ($SRNumber -match "^SR*") {
Update-SCSMStarterTicket -SRNumber $SRNumber -UserPrincipalName $FinishedAccount.UserPrincipalName -SAMAccountName $FinishedAccount.SamAccountName
}
Elseif (($SRNumber -match "^SR*") -and ($ADPhoneNumber -match "\d{4,}")) {
Update-SCSMStarterTicket -SRNumber $SRNumber -UserPrincipalName $FinishedAccount.UserPrincipalName -SAMAccountName $FinishedAccount.SamAccountName -SkypeNumber $ADPhoneNumber
}
#Format the finished account object to only return a few properties by default
$defaultProperties = @('DisplayName','EmployeeID','SamAccountName','UPN','SkypeNumber','Password')
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet(‘DefaultDisplayPropertySet’,[string[]]$defaultProperties)
$PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
$FinishedAccount | Add-Member MemberSet PSStandardMembers $PSStandardMembers
#Generate time taken to complete the script
$ExecutionStop = Get-Date
$Timetaken = $ExecutionStop - $ExecutionStart
$TimetakenInMin = "{0:N0}" -f ($Timetaken.TotalMinutes)
Write-Output "Script completed in $TimetakenInMin minutes`n"
Get-PSSession | Remove-PSSession -Confirm:$False
return $FinishedAccount
}
Function Import-ContosoStarterFile {
<#
.SYNOPSIS
Creates the necessary accounts for a new user in Active Directory, Exchange/Office 365, and Skype for Business based on a CSV import
.DESCRIPTION
This function takes in user accounts from a CSV file and creates new AD, Office 365 and Skype for Business accounts based on the parameters for each user in the file. .
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0, Active Directory PowerShell module, Remote connections to Exchange/Lync, MSOnline Module for Office 365 provisioning
.EXAMPLE
Import-ContosoStarterFile -CSVFilePath .\NewStarters.csv
.PARAMETER CSVFilePath
Required parameter for the path to the CSV file that contains the new user details. The file should contain the following Column headers, the order is not important: EmployeeID, Officecode
BU, Surname, GivenName, PreferredGivenName, Initial, Company, EmployeeType, Description. At a minimum GivenName, Surname and EmployeeType are required and all other fields can be different
depending on new starter information provided.
#>
Param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$CSVFilePath=""
)
$ExecutionStart = Get-Date
Write-Output "New account creation started at $ExecutionStart"
$CSVImportArray = @()
$ValidUserArray = @()
$ADResultArray = @()
$SkypeResultArray = @()
try {
$CSVImportArray = Import-Csv $CSVFilePath
}
catch {
Write-Output "$($_.Exception.Message)"
exit
}
#Validate each user account and discard any that failed validation with output
Foreach ($UserObject in $CSVImportArray) {
Try {
$UserResult = $UserObject | Confirm-ValidUser
$ValidUserArray = $ValidUserArray + $UserResult
}
Catch {
Write-Output "$($_.Exception.Message)"
}
}
##Create AD Account
Foreach ($Line in $ValidUserArray) {
Write-Output "Attempting to create AD account for $($Line.FullName)"
$ADresult = $Line | New-ContosoADUser
Write-Host "$($ADresult.UserPrincipalName) created successfully" -ForegroundColor Green
$FinishedAccount = New-Object -TypeName psobject -Property @{
Path = $ADresult.Path
DisplayName = $ADresult.DisplayName
GivenName = $line.GivenName
Surname = $line.Surname
EmployeeID = $line.EmployeeID
OfficeCode = $line.OfficeCode
BU = $line.BU
SamAccountName = $ADresult.SamAccountName
UserPrincipalName = $ADresult.UserPrincipalName
SkypeNumber = $null
Company = $line.Company
EnableSkype = $line.EnableSkype
EnableMailbox = $line.EnableMailbox
SRNumber = $line.SRNumber
EmployeeType = $line.EmployeeType
Description = $line.Description
Password = $ADresult.AccountPassword
}
$ADResultArray = $ADResultArray + $FinishedAccount
}
##Enable S4B (if necessary by looking up the user first name and last in the CSV array)
Foreach ($Line in $ADResultArray) {
If (!(($Line.EnableSkype -eq 'N') -or ($Line.EnableSkype -eq 'No'))) {
Write-Output "Attempting to create Skype account for $($Line.DisplayName)"
$SkypeResult = Enable-ContosoSkypeUser -UserPrincipalName $Line.UserPrincipalName
If ($($SkypeResult.LineURI) -like "tel*") {
$ADPhoneNumber = $SkypeResult.LineURI.Split(':')[1]
$ADPhoneNumber = $ADPhoneNumber.Split(';')[0]
Set-Aduser -Identity $SkypeResult.Identity -OfficePhone $ADPhoneNumber
}
$SkypeResultArray = $SkypeResultArray + $SkypeResult
}
}
##Create mailbox for each user if necessary
Foreach ($Line in $ADResultArray) {
If (!(($Line.EnableMailbox -eq 'N') -or ($Line.EnableMailbox -eq 'No'))) {
Write-Output "Attempting to create mailbox for $($Line.DisplayName)"
Enable-ContosoMailbox -UserPrincipalName $Line.UserPrincipalName
}
}
#Kick off a single dirsync for all the users (should take about 2 minutes)
Invoke-DirSync
##Provision Office 365 license if a mailbox was required for the user
Foreach ($Line in $ADResultArray) {
If (!(($Line.EnableMailbox -eq 'N') -or ($Line.EnableMailbox -eq 'No'))) {
Write-Output "Attempting to provision O365 License for $($Line.DisplayName)"
$Country = $Line.OfficeCode.SubString(0,2)
Enable-ContosoO365License -UserPrincipalName $Line.UserPrincipalName -Country $Country
}
}
Write-Host "Pausing for 30 seconds to allow Exchange Online to provision mailboxes" -ForegroundColor Cyan
Start-Sleep -Seconds 30
##Enable UM mailbox in O365 (if a Skype number was assigned)
Foreach ($Line in $SkypeResultArray) {
If ($($Line.LineURI) -like "tel*") {
Write-Output "Attempting to create UM Mailbox for $($Line.DisplayName)"
$Upn = $Line.SipAddress.Split(':')[1]
$Office = $Line.DialPlan.Split('.')[0]
Enable-ContosoUMMailbox -UserPrincipalName $Upn -Office $Office
}
}
##Loop through Skype Account array and update AD Result array with any skype numbers that were assigned
Foreach ($Line in $ADResultArray) {
$SIP = "sip:" + $($Line.UserPrincipalName)
$SkypeResultLookup = $SkypeResultArray | Where-Object {$_.SipAddress -eq $SIP}
If ($SkypeResultLookup.LineURI -like "tel*") {
$PhoneNumber = $SkypeResultLookup.LineURI.Split(':')[1]
$PhoneNumber = $PhoneNumber.Split(';')[0]
$Line.SkypeNumber = $PhoneNumber
}
}
#If an SR or SR and phone number are found, update the SCSM ticket based on provided parameters
Foreach ($Line in $ADResultArray) {
If (($Line.SRNumber -match "^SR*") -and ($Line.SkypeNumber -match "\d{4,}")) {
Update-SCSMStarterTicket -SRNumber $Line.SRNumber -UserPrincipalName $Line.UserPrincipalName -SAMAccountName $Line.SamAccountName -SkypeNumber $Line.SkypeNumber
}
ElseIf ($Line.SRNumber -match "^SR*") {
Update-SCSMStarterTicket -SRNumber $Line.SRNumber -UserPrincipalName $Line.UserPrincipalName -SAMAccountName $Line.SamAccountName
}
}
##Format the finished account array to only return a few properties by default
$defaultProperties = @('DisplayName','EmployeeID','SamAccountName','UserPrincipalName','SkypeNumber','Password')
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet(‘DefaultDisplayPropertySet’,[string[]]$defaultProperties)
$PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
$ADResultArray | Add-Member MemberSet PSStandardMembers $PSStandardMembers
#Generate time taken to complete the script
$ExecutionStop = Get-Date
$Timetaken = $ExecutionStop - $ExecutionStart
$TimetakenInMin = "{0:N0}" -f ($Timetaken.TotalMinutes)
Write-Output "Script completed in $TimetakenInMin minutes`n"
Get-PSSession | Remove-PSSession -Confirm:$False
return $ADResultArray
}
Function New-ContosoADUser {
<#
.SYNOPSIS
Creates a new AD account for a starter given the necessary parameters.
.DESCRIPTION
This function creates a new AD user account for a starter based on the parameters defined below. The function will automatically generate a
unique SAM Account and UPN and will copy an existing template if available. Of note only specific values for EmployeeType and Company are accepted.
.NOTES
Author : TheDudeAbides
Requires: PowerShell Version 3.0, Active Directory PowerShell module
.EXAMPLE
.\New-ContosoAdUser -GivenName Robert -Surname Public -Initial Q -PreferredGivenName Bob -EmployeeID 123456 -EmployeeType F -Description "Job Site Manager" -BU 12345 -OfficeCode "US-DEN-1"
.PARAMETER GivenName
Required parameter for the first or given name of the user to be created
.PARAMETER Surname
Required parameter for the last name or names of the user to be created. The function will automatically remove any spaces from the last name or apostrophes if found.
.PARAMETER Initial
Optional parameter for the middle initial of the user. This should only be the first character of the middle name as applicable.
.PARAMETER PreferredGivenName
Optional parameter for the prefferred given name of the user if supplied. This will be used in the Display name for the account.
.PARAMETER EmployeeID
Optional parameter for the employee ID of the user. If not supplied, it will be filled in by FIM at a later time or automatically filled in if the user
is a temp, contractor, or JV user.
.PARAMETER EmployeeType
Required parameter for the employee type, should be specified as either (F)ull-time, (T)emporary, (C)ontractor, or (JV).
.PARAMETER Description
Optional parameter for the description on the user. This field can include details of the account, requestor, or Helpdesk ticket to help document the user
to be created.
.PARAMETER BU
Optional parameter for the business unit of the account to be created.
.PARAMETER OfficeCode
Optional parameter for the Office code of the account to be created.
.PARAMETER Company
Required parameter for the company name of the account to be created. Accepted values are Contoso, TailSpinToys, JV and the company name is used to generate
the correct UPN and email address.
#>
[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$GivenName="",
[Parameter(Position=1,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Surname="",
[Parameter(Position=2,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Initial="",
[Parameter(Position=3,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$PreferredGivenName="",
[Parameter(Position=4,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$EmployeeID="",
[Parameter(Position=5,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)][ValidateSet('F','T','C','JV','-')]
[string]$EmployeeType="",
[Parameter(Position=6,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$Description="",
[Parameter(Position=7,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)][ValidateRange(00000,99999)]
[int]$BU="",
[Parameter(Position=8,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$OfficeCode="",
[Parameter(Position=9,Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)][ValidateSet('Contoso','TailSpinToys','JV')]
[string]$Company="Contoso"
)
$StarterObject = New-Object -TypeName psobject -Property @{
EmployeeID = "$EmployeeID"
OfficeCode = "$OfficeCode"
BU = $BU
Surname = "$Surname"
PreferredGivenName = "$PreferredGivenName"
GivenName = "$GivenName"
Initial = "$Initial"
Company = "$Company"
EmployeeType = "$EmployeeType"
Description = "$Description"
}
Try {
$UserResult = $StarterObject | Confirm-ValidUser
$UserResult.EnableMailbox = $null
$UserResult.EnableSkype = $null
$UserResult.SRNumber = $null
}
Catch {
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
exit
}
#Generate initial derived properties collapsing any spaces or apostrophes in the last name if necessary
$CleanedSurname = $UserResult.Surname.replace(" ","")
$CleanedSurname = $UserResult.Surname.replace("'","")
If ($line.PreferredGivenName -match "\w+") {
$FullName = $UserResult.PreferredGivenName + " " + $UserResult.Surname
}
Else {
$FullName = $UserResult.GivenName + " " + $UserResult.Surname
}
$Country = $UserResult.OfficeCode.Substring(0,2)
$Region = $RegionMappings.$Country
#Attempt to generate a unique SAMAccountName
Try {
$PotentialSAMAccountName = Get-UniqueSAMAccountName -GivenName $UserResult.GivenName -Surname $CleanedSurname -Initial $Initial
}
Catch {
throw 'Could not generate a unique SAMAccountName'
}
#Attempt to create a unique UserPrincipalName
Try {
$PotentialUPN = Get-UniqueUPN -GivenName $UserResult.GivenName -Surname $CleanedSurname -Initial $UserResult.Initial -Company $UserResult.Company
}
Catch {
throw 'Could not generate a unique UserPrincipalName'
}
#Generate a random password for the user
$Password = Get-RandomPassword
$SecurePW = ConvertTo-SecureString $Password -AsPlainText -Force
$Searchbase = "OU=Users & Groups,dc=Contoso,dc=com"
$NewADUserParams = @{
Name=$FullName
SamAccountName=$PotentialSAMAccountName
UserPrincipalName=$PotentialUPN
GivenName=$UserResult.GivenName
Surname=$UserResult.Surname
Instance=$null
EmployeeID=$UserResult.EmployeeID
Company=$UserResult.Company
Department=$UserResult.BU
Description=$UserResult.Description
DisplayName=$FullName
Initials=$UserResult.Initial
Office=$UserResult.OfficeCode
AccountPassword=$SecurePW
Enabled=$false
ChangePasswordAtLogon=$True
Path=$Null
EmailAddress=$Null
OtherAttributes = @{EmployeeType = ($UserResult.EmployeeType)};
}
#Find an appropriate template for the user if Full Time or temp
If ($UserResult.EmployeeType -match "^(F|T)$") {
$OfficeCodeSplit = $OfficeCode.Split("-")
$OfficeCodeSearchterms = (($OfficeCode),($OfficeCode.Replace("-","")),($OfficeCodeSplit[0]+"-"+$OfficeCodeSplit[1]+$OfficeCodeSplit[2]),($OfficeCodeSplit[0]+$OfficeCodeSplit[1]+"-"+$OfficeCodeSplit[2]))
$AllActiveDirectoryTemplates = Get-ADUser -Filter {Name -like "*Template*"}
Foreach ($term in $OfficeCodeSearchterms) {
If ($AllActiveDirectoryTemplates -match $term) {
$Instance = $AllActiveDirectoryTemplates -match $term
#Strip off the first part of the DistinguishedName to derive the OU path
$DN = $Instance.DistinguishedName.Split(',')
$NewADUserParams.Path = ($DN[1..($DN.Length-1)]) -join ','
$NewADUserParams.Instance = $($Instance)
}
}
If ($NewADUserParams.Path -eq $null) {
#Create the account in the regional OU if no other options are available
$NewADUserParams.Path = (Get-ADOrganizationalUnit -SearchBase $Searchbase -SearchScope OneLevel -Filter * -Properties Description | Where-Object {$_.DistinguishedName -like "*$Region*"}).DistinguishedName
}
}
Elseif ($EmployeeType -eq "C") {
#If user is a consultant, drop the account in the default consultant OU for the appropriate region
$NewADUserParams.Path = (Get-ADOrganizationalUnit -SearchBase $Searchbase -Filter {(Description -eq "Default Consultants")} -Properties Description | Where-Object {$_.DistinguishedName -like "*$Region*"}).DistinguishedName | Out-String
}
Elseif ($EmployeeType -eq "JV") {
#If user is for a JV, drop the account into the external contacts folder, can be moved later
$NewADUserParams.Path = "OU=_External,OU=Microsoft Exchange Contacts,OU=Domain,OU=Users & Groups,DC=Contoso,DC=com"
}
#If no user template was found to copy from, drop that property from the parameters
If ($NewADUserParams.Instance -eq $null) {
$NewADUserParams.Remove('Instance')
}
#Attempt to create the new account
Try {
$NewAccount = New-ADUser @NewADUserParams -PassThru -ErrorAction Stop
#Write-Output "User account $PotentialUPN created successfully"
#Update list of UPNs and SAM Accounts with newly created name
$GLOBAL:AllUPNs = $GLOBAL:AllUPNs + $PotentialUPN
$GLOBAL:AllSAMAccountNames = $GLOBAL:AllSAMAccountNames + $PotentialSAMAccountName
}
Catch {
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
throw "Account creation failed, error message is $ErrorMessage"
}
#Remove user from all groups except Domain users
$UserMembership = (Get-ADUser $NewAccount -Properties memberof).memberof
$UserMembership | Remove-ADGroupMember -Members $NewAccount.DistinguishedName -Confirm:$False
#Add to correct user groups based on region/office code
If ($Region -eq 'AMER') {
Add-ADGroupMember -Identity "AMERICAS-Users" -Members $NewAccount
Add-ADGroupMember -Identity "Contoso-Users" -Members $NewAccount
$OfficeGroup = "AM-Office-" + $UserResult.OfficeCode
Add-ADGroupMember -Identity $OfficeGroup -Members $NewAccount -ErrorAction SilentlyContinue
}
Elseif ($Region -eq 'EMEA') {
If ($EmpType -eq "P") {
Add-ADGroupMember -Identity "EMEAI-CITRIX" -Members $NewAccount
Add-ADGroupMember -Identity "EMEAI-SSL-General" -Members $NewAccount
$OfficeGroup = "EMEAI-Office-" + $UserResult.OfficeCode
Add-ADGroupMember -Identity $OfficeGroup -Members $NewAccount -ErrorAction SilentlyContinue
}
Elseif ($EmpType -eq "C") {
Add-ADGroupMember -Identity "EMEAI-Office-External" -Members $NewAccount
}
Add-ADGroupMember -Identity "Contoso-Users" -Members $NewAccount
Add-ADGroupMember -Identity "EMEAI Users" -Members $NewAccount
}
Elseif ($Region -eq 'ASIA') {
Add-ADGroupMember -Identity "ASIA Users" -Members $NewAccount
Add-ADGroupMember -Identity "Contoso-Users" -Members $NewAccount
If (($Country -eq 'CN') -or ($Country -eq 'TW')) {
$OfficeGroup = $UserResult.OfficeCode.Replace('-','') + "Users"
Add-ADGroupMember -Identity $OfficeGroup -Members $NewAccount -ErrorAction SilentlyContinue
}
Else {
$ShortOfficeCode = $OfficeCode.Replace("-","")
$OfficeGroup = "ASIA-Office-" + $ShortOfficeCode
Add-ADGroupMember -Identity $OfficeGroup -Members $NewAccount -ErrorAction SilentlyContinue
}
}
$FinishedAccount = New-Object -TypeName psobject -Property @{
Name=$FullName
SamAccountName=$PotentialSAMAccountName
UserPrincipalName=$PotentialUPN
GivenName=$UserResult.GivenName
Surname=$UserResult.Surname
TemplateUsed=$Instance.Name
EmployeeID=$UserResult.EmployeeID
Company=$UserResult.Com