# 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: 6334 # x-archived: 2016-05-05T01:36:45 # # <# .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