Invite-AzureADUserToApplication.ps1


<#PSScriptInfo
 
.VERSION 1.0.1
 
.GUID ceed3727-5009-43f0-ac9a-31f5f1dea88f
 
.AUTHOR Chris Martin
 
.COMPANYNAME Microsoft
 
.COPYRIGHT
 
.TAGS AzureAD Application ServicePrincipal
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES AzureAD
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
.PRIVATEDATA
 
#>


#Requires -Module AzureAD

<#
 
.SYNOPSIS
 Add a collection of users (represented by email addresses) to an Enterprise Application role in Azure AD.
 
.DESCRIPTION
 Add a collection of users (represented by email addresses) to an Enterprise Application role in Azure AD. External users that are not already represented in the directory will be invited. Invitation details can be exported by supplying -LogFilePath
 
.PARAMETER EmailAddress
 One or more email addresses of users to add to the directory.
 
.PARAMETER Application
 Will take either the ServicePrincipal TenantId or DisplayName. If the string evaluates to a GUID it will attempt to find it with TenantId, otherwise searches for DisplayName.
 
.PARAMETER Role
 The name of the role to which the users will be added.
 
.PARAMETER InternalDomains
 The list of InternalDomains to match so you don't try to invite internal users.
 
.PARAMETER TenantId
 The ID of the tenant to access. If you leave this out, it will connect to your default tenant.
 
.PARAMETER LogFilePath
 Path where the system will export the invited user details.
 
.PARAMETER SuppressLogin
 Don't log in (Use this if you've already connected in the session and don't want to be prompted again.)
 
.EXAMPLE
 Invite-AzureADUserToApplication.ps1 -EmailAddress 'mickey.mouse@externaldomain.com','minnie.mouse@externaldomain.com','donald.duck@internaldomain.com' -Application 'My Application' -Role User -InternalDomains 'internaldomain.com' -LogFilePath C:\users\mousem\Documents\InvitedUsers.csv
 
 Script would log you in to your default tenant, then check if 'mickey.mouse@externaldomain.com' and 'minnie.mouse@externaldomain.com' are registered in the directory.
  
 If they are not, they will invite them, and details about that invitation will be found at 'C:\users\mousem\Documents\InvitedUsers.csv'
 
 All specified users will then be added to the directory. If any internal users are on the list and not in the directory, the script will report an error.
 
.EXAMPLE
 Import-Csv -Path 'C:\Users\MouseM\Documents\UsersToAddToApplication.csv' |
  
 Select-Object -ExpandProperty 'Email' |
  
 Invite-AzureADUserToApplication.ps1 -Application 'My Application' -Role User -InternalDomains 'internaldomain.com' -LogFilePath C:\users\mousem\Documents\InvitedUsers.csv
 
 Import all rows from a CSV, then isolate the 'Email' column. This becomes the -EmailAddress input, and the script would then log you in and add users to 'My Application'.
  
 If email addresses supplied are external and not in the directory, they will be invited, and invitation details will be found at 'C:\users\mousem\Documents\InvitedUsers.csv'.
 
 If any internal users are on the list and not in the directory, the script will report an error.
 
#>

[CmdletBinding()]
Param(
    [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
    [string[]] $EmailAddress,

    [Parameter(Mandatory = $true, Position = 1)]
    [string] $Application,

    [Parameter(Mandatory = $true, Position = 2)]
    [string] $Role,

    [Parameter(Mandatory = $false, Position = 3)]
    [string[]] $InternalDomains,

    [Parameter(Mandatory = $false, Position = 4)]
    [Guid] $TenantId,

    [Parameter(Mandatory = $false, Position = 5)]
    [ValidateScript({$_ -like "*.csv"})]
    [string] $LogFilePath,

    [switch] $SuppressLogin
)
Begin {
    if (-not $SuppressLogin) {

        $connectParams = @{
            ErrorAction = 'Stop'
        }

        if ($PSBoundParameters.ContainsKey('TenantId')) {
            $connectParams['TenantId'] = $TenantId
        }

        Connect-AzureAD @connectParams
    }

    $getAdspParams = @{
        ErrorAction = 'SilentlyContinue'
    }
    
    $appId = [Guid]::Empty
    
    if ([Guid]::TryParse($Application, [ref] $appId)) {

        $getAdspParams['ObjectId'] = $appId
    }
    else {
        $escapedApplicationName = $Application.Replace("'","''")
    
        $getAdspParams['Filter'] = "DisplayName eq '$escapedApplicationName'"
    }

    $sp = Get-AzureADServicePrincipal @getAdspParams

    if ($null -eq $sp) {
        Write-Error -Message "No application found matching $Application in the directory."
    }

    $roleToAdd = $sp.AppRoles | Where-Object -Property DisplayName -EQ $Role

    if ($null -eq $roleToAdd) {
        Write-Error -Message "No role found on application $Application matching name $Role" -ErrorAction Stop
    }
}
Process {

    function Send-NewExUserInvitation {
        [CmdletBinding()]
        Param($EmailAddress)

        $exUser = New-AzureADMSInvitation -InvitedUserDisplayName $EmailAddress -InvitedUserEmailAddress $EmailAddress -SendInvitationMessage $false -InviteRedirectUrl "https://portal.azure.com"

        $escapedEmailAddress = $email.Replace("'","''")

        $user = Get-AzureADUser -Filter "mail eq '$escapedEmailAddress'" -ErrorAction Stop

        $csvInfo = [PSCustomObject]@{
            DisplayName = $exUser.InvitedUserDisplayName
            EmailAddress = $exUser.InvitedUserEmailAddress
            InvitationRedeemUrl = $exUser.InviteRedeemUrl
        }

        Out-Log -InputObject $csvInfo

        Write-Output -InputObject $user
    }

    function Test-IsExternalEmail {
        [CmdletBinding()]
        Param($EmailAddress, $InternalDomains)

        $external = $true

        foreach ($domain in $InternalDomains) {
            if ($EmailAddress -like "*$($domain)") {
                $external = $false
            }
        }

        return $external
    }

    function Out-Log {
        Param($InputObject)
        if (-not [string]::IsNullOrWhiteSpace($LogFilePath))
        {
            $InputObject | Export-Csv -Path $LogFilePath -Append
        }
    }
    

    foreach ($email in $EmailAddress) {

        $escapedEmailAddress = $email.Replace("'","''")

        $user = Get-AzureADUser -Filter "mail eq '$escapedEmailAddress'" -ErrorAction Stop

        if ($null -eq $user) {

            if (Test-IsExternalEmail -EmailAddress $email) {
                $user = Send-NewExUserInvitation -EmailAddress $email -ErrorAction Stop
            }
            else {
                Write-Error -Message "Internal email address $($email) that is not in the directory cannot be added. Only external users can be added to the directory with this script."
                continue
            }
        }

        New-AzureADUserAppRoleAssignment -ObjectId $user.ObjectId -PrincipalId $user.ObjectId -ResourceId $sp.ObjectId -Id $roleToAdd.Id -ErrorAction Continue

    }

}