Invite-AzureADUserToApplication.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230

<#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

    }

}