Skip to content

Commit 498866b

Browse files
committed
Feat: Support JIT admin for guest users
Fix: Move logging into the functions Feat: Copy button only copies the info you actually want Fixes KelvinTegelaar/CIPP#5072 Feat: Add error handling for JIT admin user creation and execution
1 parent 57ad95c commit 498866b

File tree

2 files changed

+92
-27
lines changed

2 files changed

+92
-27
lines changed

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecJITAdmin.ps1

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,9 @@ function Invoke-ExecJITAdmin {
1616
$TenantFilter = $Request.Body.tenantFilter.value ? $Request.Body.tenantFilter.value : $Request.Body.tenantFilter
1717

1818

19-
if ($Request.Body.existingUser.value -match '^[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}$') {
20-
$Username = (New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/users/$($Request.Body.existingUser.value)" -tenantid $TenantFilter).userPrincipalName
21-
}
22-
2319
$Start = ([System.DateTimeOffset]::FromUnixTimeSeconds($Request.Body.StartDate)).DateTime.ToLocalTime()
2420
$Expiration = ([System.DateTimeOffset]::FromUnixTimeSeconds($Request.Body.EndDate)).DateTime.ToLocalTime()
25-
$Results = [System.Collections.Generic.List[string]]::new()
21+
$Results = [System.Collections.Generic.List[object]]::new()
2622

2723
if ($Request.Body.userAction -eq 'create') {
2824
$Domain = $Request.Body.Domain.value ? $Request.Body.Domain.value : $Request.Body.Domain
@@ -39,17 +35,62 @@ function Invoke-ExecJITAdmin {
3935
Reason = $Request.Body.reason
4036
Action = 'Create'
4137
TenantFilter = $TenantFilter
38+
Headers = $Headers
39+
APIName = $APIName
40+
}
41+
try {
42+
$CreateResult = Set-CIPPUserJITAdmin @JITAdmin
43+
} catch {
44+
return ([HttpResponseContext]@{
45+
StatusCode = [HttpStatusCode]::BadRequest
46+
Body = @{'Results' = @("Failed to create JIT Admin user: $($_.Exception.Message)") }
47+
})
4248
}
43-
$CreateResult = Set-CIPPUserJITAdmin @JITAdmin
44-
Write-LogMessage -Headers $Headers -API $APIName -tenant $TenantFilter -message "Created JIT Admin user: $Username. Reason: $($Request.Body.reason). Roles: $($Request.Body.adminRoles.label -join ', ')" -Sev 'Info' -LogData $JITAdmin
45-
$Results.Add("Created User: $Username")
49+
$Results.Add(@{
50+
resultText = "Created User: $Username"
51+
copyField = $Username
52+
state = 'success'
53+
})
4654
if (!$Request.Body.UseTAP) {
47-
$Results.Add("Password: $($CreateResult.password)")
55+
$Results.Add(@{
56+
resultText = "Password: $($CreateResult.password)"
57+
copyField = $CreateResult.password
58+
state = 'success'
59+
})
4860
}
4961
$Results.Add("JIT Admin Expires: $($Expiration)")
5062
Start-Sleep -Seconds 1
63+
} else {
64+
65+
$Username = $Request.Body.existingUser.value
66+
if ($Username -match '^[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}$') {
67+
Write-Information "Resolving UserPrincipalName from ObjectId: $($Request.Body.existingUser.value)"
68+
$Username = (New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/users/$($Request.Body.existingUser.value)" -tenantid $TenantFilter).userPrincipalName
69+
70+
# If the resolved username is a guest user, we need to use the id instead of the UPN
71+
if ($Username -clike '*#EXT#*') {
72+
$Username = $Request.Body.existingUser.value
73+
}
74+
}
75+
76+
# Validate we have a username
77+
if ([string]::IsNullOrWhiteSpace($Username)) {
78+
return [HttpResponseContext]@{
79+
StatusCode = [HttpStatusCode]::BadRequest
80+
Body = @{ 'Results' = @("Could not resolve username from existingUser value: $($Request.Body.existingUser.value)") }
81+
}
82+
}
83+
84+
# Add username result for existing user
85+
$Results.Add(@{
86+
resultText = "User: $Username"
87+
copyField = $Username
88+
state = 'success'
89+
})
5190
}
5291

92+
93+
5394
#Region TAP creation
5495
if ($Request.Body.UseTAP) {
5596
try {
@@ -82,13 +123,21 @@ function Invoke-ExecJITAdmin {
82123
$PasswordLink = New-PwPushLink -Payload $TempPass
83124
$Password = $PasswordLink ? $PasswordLink : $TempPass
84125

85-
$Results.Add("Temporary Access Pass: $Password")
126+
$Results.Add(@{
127+
resultText = "Temporary Access Pass: $Password"
128+
copyField = $Password
129+
state = 'success'
130+
})
86131
$Results.Add("This TAP is usable starting at $($TapRequest.startDateTime) UTC for the next $PasswordExpiration minutes")
87132
} catch {
88133
$Results.Add('Failed to create TAP, if this is not yet enabled, use the Standards to push the settings to the tenant.')
89134
Write-Information (Get-CippException -Exception $_ | ConvertTo-Json -Depth 5)
90135
if ($Password) {
91-
$Results.Add("Password: $Password")
136+
$Results.Add(@{
137+
resultText = "Password: $Password"
138+
copyField = $Password
139+
state = 'success'
140+
})
92141
}
93142
}
94143
}
@@ -103,6 +152,8 @@ function Invoke-ExecJITAdmin {
103152
Action = 'AddRoles'
104153
Reason = $Request.Body.Reason
105154
Expiration = $Expiration
155+
Headers = $Headers
156+
APIName = $APIName
106157
}
107158
if ($Start -gt (Get-Date)) {
108159
$TaskBody = @{
@@ -125,11 +176,16 @@ function Invoke-ExecJITAdmin {
125176
Set-CIPPUserJITAdminProperties -TenantFilter $TenantFilter -UserId $Request.Body.existingUser.value -Expiration $Expiration -Reason $Request.Body.Reason
126177
}
127178
$Results.Add("Scheduling JIT Admin enable task for $Username")
128-
Write-LogMessage -Headers $Headers -API $APIName -message "Scheduling JIT Admin for existing user: $Username. Reason: $($Request.Body.reason). Roles: $($Request.Body.adminRoles.label -join ', ') " -tenant $TenantFilter -Sev 'Info'
129179
} else {
130-
$Results.Add("Executing JIT Admin enable task for $Username")
131-
Set-CIPPUserJITAdmin @Parameters
132-
Write-LogMessage -Headers $Headers -API $APIName -message "Executing JIT Admin for existing user: $Username. Reason: $($Request.Body.reason). Roles: $($Request.Body.adminRoles.label -join ', ') " -tenant $TenantFilter -Sev 'Info'
180+
try {
181+
$Results.Add("Executing JIT Admin enable task for $Username")
182+
Set-CIPPUserJITAdmin @Parameters
183+
} catch {
184+
return ([HttpResponseContext]@{
185+
StatusCode = [HttpStatusCode]::BadRequest
186+
Body = @{'Results' = @("Failed to execute JIT Admin enable task: $($_.Exception.Message)") }
187+
})
188+
}
133189
}
134190

135191
$DisableTaskBody = [pscustomobject]@{
@@ -158,7 +214,6 @@ function Invoke-ExecJITAdmin {
158214
$null = Add-CIPPScheduledTask -Task $DisableTaskBody -hidden $false
159215
$Results.Add("Scheduling JIT Admin $($Request.Body.ExpireAction.value) task for $Username")
160216

161-
# TODO - We should find a way to have this return a HTTP status code based on the success or failure of the operation. This also doesn't return the results of the operation in a Results hash table, like most of the rest of the API.
162217
return ([HttpResponseContext]@{
163218
StatusCode = [HttpStatusCode]::OK
164219
Body = @{'Results' = @($Results) }

Modules/CIPPCore/Public/Set-CIPPUserJITAdmin.ps1

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ function Set-CIPPUserJITAdmin {
2424
.PARAMETER Reason
2525
Reason for JIT admin assignment. Defaults to 'No reason provided' as due to backwards compatibility this is not a mandatory field.
2626
27+
.PARAMETER Headers
28+
Headers to include in logging
29+
2730
.EXAMPLE
2831
Set-CIPPUserJITAdmin -TenantFilter 'contoso.onmicrosoft.com' -Headers@{UserPrincipalName = '[email protected]'} -Roles @('62e90394-69f5-4237-9190-012177145e10') -Action 'AddRoles' -Expiration (Get-Date).AddDays(1) -Reason 'Emergency access'
2932
@@ -32,19 +35,16 @@ function Set-CIPPUserJITAdmin {
3235
param(
3336
[Parameter(Mandatory = $true)]
3437
[string]$TenantFilter,
35-
3638
[Parameter(Mandatory = $true)]
3739
[hashtable]$User,
38-
3940
[string[]]$Roles,
40-
4141
[Parameter(Mandatory = $true)]
4242
[ValidateSet('Create', 'AddRoles', 'RemoveRoles', 'DeleteUser', 'DisableUser')]
4343
[string]$Action,
44-
4544
[datetime]$Expiration,
46-
47-
[string]$Reason = 'No reason provided'
45+
[string]$Reason = 'No reason provided',
46+
$Headers,
47+
[string]$APIName = 'Set-CIPPUserJITAdmin'
4848
)
4949

5050
if ($PSCmdlet.ShouldProcess("User: $($User.UserPrincipalName)", "Action: $Action")) {
@@ -83,6 +83,7 @@ function Set-CIPPUserJITAdmin {
8383
if ($PasswordLink) {
8484
$Password = $PasswordLink
8585
}
86+
Write-LogMessage -Headers $Headers -API $APIName -tenant $TenantFilter -message "Created JIT Admin user: $($User.UserPrincipalName). Reason: $Reason" -Sev 'Info'
8687
[PSCustomObject]@{
8788
id = $NewUser.id
8889
userPrincipalName = $NewUser.userPrincipalName
@@ -116,6 +117,8 @@ function Set-CIPPUserJITAdmin {
116117
}
117118

118119
Set-CIPPUserJITAdminProperties -TenantFilter $TenantFilter -UserId $UserObj.id -Enabled -Expiration $Expiration -Reason $Reason | Out-Null
120+
$Message = "Added admin roles to user $($UserObj.displayName) ($($UserObj.userPrincipalName)). Reason: $Reason"
121+
Write-LogMessage -Headers $Headers -API $APIName -tenant $TenantFilter -message $Message -Sev 'Info'
119122
return "Added admin roles to user $($UserObj.displayName) ($($UserObj.userPrincipalName))"
120123
}
121124
'RemoveRoles' {
@@ -125,15 +128,20 @@ function Set-CIPPUserJITAdmin {
125128
} catch {}
126129
}
127130
Set-CIPPUserJITAdminProperties -TenantFilter $TenantFilter -UserId $UserObj.id -Clear | Out-Null
131+
$Message = "Removed admin roles from user $($UserObj.displayName) ($($UserObj.userPrincipalName))"
132+
Write-LogMessage -Headers $Headers -API $APIName -tenant $TenantFilter -message $Message -Sev 'Info'
128133
return "Removed admin roles from user $($UserObj.displayName)"
129134
}
130135
'DeleteUser' {
131136
try {
132137
$null = New-GraphPOSTRequest -type DELETE -uri "https://graph.microsoft.com/beta/users/$($UserObj.id)" -tenantid $TenantFilter
133-
return "Deleted user $($UserObj.displayName) ($($UserObj.userPrincipalName)) with id $($UserObj.id)"
138+
$Message = "Deleted user $($UserObj.displayName) ($($UserObj.userPrincipalName)) with id $($UserObj.id)"
139+
Write-LogMessage -Headers $Headers -API $APIName -tenant $TenantFilter -message $Message -Sev 'Info'
140+
return $Message
134141
} catch {
135142
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
136-
return "Error deleting user $($UserObj.displayName) ($($UserObj.userPrincipalName)): $ErrorMessage"
143+
Write-LogMessage -Headers $Headers -API $APIName -tenant $TenantFilter -message "Error deleting user $($UserObj.displayName) ($($UserObj.userPrincipalName)): $ErrorMessage" -Sev 'Error'
144+
throw "Error deleting user $($UserObj.displayName) ($($UserObj.userPrincipalName)): $ErrorMessage"
137145
}
138146
}
139147
'DisableUser' {
@@ -147,11 +155,13 @@ function Set-CIPPUserJITAdmin {
147155
Write-Information "https://graph.microsoft.com/beta/users/$($User.UserPrincipalName)"
148156
$null = New-GraphPOSTRequest -type PATCH -uri "https://graph.microsoft.com/beta/users/$($User.UserPrincipalName)" -tenantid $TenantFilter -body $Json
149157
Set-CIPPUserJITAdminProperties -TenantFilter $TenantFilter -UserId $User.UserPrincipalName -Clear | Out-Null
150-
return "Disabled user $($UserObj.displayName) ($($UserObj.userPrincipalName))"
158+
$Message = "Disabled user $($UserObj.displayName) ($($UserObj.userPrincipalName))"
159+
Write-LogMessage -Headers $Headers -API $APIName -tenant $TenantFilter -message $Message -Sev 'Info'
160+
return $Message
151161
} catch {
152162
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
153-
return "Error disabling user $($UserObj.displayName) ($($UserObj.userPrincipalName)): $ErrorMessage"
154-
163+
Write-LogMessage -Headers $Headers -API $APIName -tenant $TenantFilter -message "Error disabling user $($UserObj.displayName) ($($UserObj.userPrincipalName)): $ErrorMessage" -Sev 'Error'
164+
throw "Error disabling user $($UserObj.displayName) ($($UserObj.userPrincipalName)): $ErrorMessage"
155165
}
156166
}
157167
}

0 commit comments

Comments
 (0)