Functions/Public/New-CT365User.ps1
<#
.SYNOPSIS This function creates new Microsoft 365 users from data in an Excel file and assigns them a license. .DESCRIPTION The New-CT365User function imports user data from an Excel file, creates new users in Microsoft 365, and assigns them a license. It performs these tasks using the Microsoft.Graph.Users and Microsoft.Graph.Groups modules. .PARAMETER FilePath The path of the Excel file containing user data. The file should have a worksheet named 'Users' with columns for UserName, FirstName, LastName, Title, Department, StreetAddress, City, State, PostalCode, Country, PhoneNumber, MobilePhone, UsageLocation, and License. This parameter is mandatory and accepts pipeline input and property names. .PARAMETER domain The domain to be appended to the UserName to create the UserPrincipalName for each user. This parameter is mandatory and accepts pipeline input and property names. .EXAMPLE New-CT365User -FilePath "C:\Path\to\file.xlsx" -domain "contoso.com" This command imports user data from the 'file.xlsx' file and creates new users in Microsoft 365 under the domain 'contoso.com'. .NOTES The function connects to Microsoft Graph using 'Directory.ReadWrite.All' scope. Make sure the account running this script has the necessary permissions. The function lets you set the password for each new user to what you want it to be and does not require the user to change the password at the next sign-in. Modify the password setting to meet your organization's security requirements. Connect-MgGraph -Scopes "Directory.ReadWrite.All" - is needed to connect to Graph #> function New-CT365User { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateScript({ #making sure the Filepath leads to a file and not a folder and has a proper extension switch ($psitem){ {-not([System.IO.File]::Exists($psitem))}{ throw "The file path '$PSitem' does not lead to an existing file. Please verify the 'FilePath' parameter and ensure that it points to a valid file (folders are not allowed). " } {-not(([System.IO.Path]::GetExtension($psitem)) -match "(.xlsx)")}{ "The file path '$PSitem' does not have a valid Excel format. Please make sure to specify a valid file with a .xlsx extension and try again." } Default{ $true } } })] [string]$FilePath, [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateScript({ # Check if the domain fits the pattern switch ($psitem) { {$psitem -notmatch '^(((?!-))(xn--|_)?[a-z0-9-]{0,61}[a-z0-9]{1,1}\.)*(xn--)?[a-z]{2,}(?:\.[a-z]{2,})+$'}{ throw "The provided domain is not in the correct format." } Default { $true } } })] [string]$Domain, [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [Security.SecureString]$Password = $(Read-Host -Prompt "Enter the password" -AsSecureString) ) # Import Required Modules $ModulesToImport = "ImportExcel","Microsoft.Graph.Users","Microsoft.Graph.Groups","Microsoft.Graph.Identity.DirectoryManagement","Microsoft.Graph.Users.Actions","PSFramework" Import-Module $ModulesToImport # Connect to Microsoft Graph - Pull these out eventually still in here for testing $Scopes = @("Directory.ReadWrite.All") $Context = Get-MgContext if ([string]::IsNullOrEmpty($Context) -or ($Context.Scopes -notmatch [string]::Join('|', $Scopes))) { Connect-MGGraph -Scopes $Scopes } # Import user data from Excel file $userData = $null try { $userData = Import-Excel -Path $FilePath -WorksheetName Users } catch { Write-PSFMessage -Level Error -Message "Failed to import user data from Excel file." return } # Iterate through each user in the Excel file and create them foreach ($user in $userData) { $NewUserParams = @{ UserPrincipalName = "$($user.UserName)@$domain" GivenName = $user.FirstName Surname = $user.LastName DisplayName = "$($user.FirstName) $($user.LastName)" MailNickname = $user.UserName JobTitle = $user.Title Department = $user.Department StreetAddress = $user.StreetAddress City = $user.City State = $user.State PostalCode = $user.PostalCode Country = $user.Country BusinessPhones = $user.PhoneNumber MobilePhone = $user.MobilePhone UsageLocation = $user.UsageLocation AccountEnabled = $true } $PasswordProfile = @{ 'ForceChangePasswordNextSignIn' = $false 'Password' = $password | ConvertFrom-SecureString -AsPlainText } Write-PSFMessage -Level Output -Message "Creating user: '$($NewUserParams.UserPrincipalName)'" -Target $user.UserName $createdUser = New-MgUser @NewUserParams -PasswordProfile $PasswordProfile # Validate user creation if ($null -ne $createdUser) { Write-PSFMessage -Level Output -Message "User: '$($NewUserParams.UserPrincipalName)' created successfully" -Target $user.UserName } else { Write-PSFMessage -Level Warning -Message "Failed to create user: '$($NewUserParams.UserPrincipalName)'" -Target $user.UserName # if the creation failed go ahead with the next user and skip the license part continue } $licenses = Get-MgSubscribedSku | Where-Object {$_.SkuPartNumber -eq $user.License } $user = Get-MgUser -Filter "DisplayName eq '$($NewUserParams.DisplayName)'" Write-PSFMessage -Level Host -Message "Assigning license $($user.License) to user: '$($NewUserParams.UserPrincipalName)'" -Target $user.UserName Set-MgUserLicense -UserId $user.Id -AddLicenses @{SkuId = ($licenses.SkuId)} -RemoveLicenses @() # Retrieve the user's licenses after assignment $assignedLicenses = Get-MgUserLicenseDetail -UserId $user.Id | Select-Object -ExpandProperty SkuId # Check if the assigned license ID is in the user's licenses if ($assignedLicenses -contains $licenses.SkuId) { Write-PSFMessage -Level Output -Message "License $License successfully assigned to user: '$($NewUserParams.UserPrincipalName)'" -Target $user.UserName } else { Write-PSFMessage -Level Warning -Message "Failed to assign license $License to user: '$($NewUserParams.UserPrincipalName)'" -Target $user.UserName } } # Disconnect Microsoft Graph sessions if (-not [string]::IsNullOrEmpty($(Get-MgContext))) { Disconnect-MgGraph } } |