Skip to content

Commit 856e7ac

Browse files
jasonzqshenknithinc
authored andcommitted
[Portal User Data GDRP] Add PS module for portal user data GDPR (#493)
* Add PS module for portal user data GDPR * Support MFA * Update according to the comments * Update according to the comments
1 parent b5cfdd8 commit 856e7ac

File tree

3 files changed

+441
-14
lines changed

3 files changed

+441
-14
lines changed
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
<###################################################
2+
# #
3+
# Copyright (c) Microsoft. All rights reserved. #
4+
# #
5+
##################################################>
6+
7+
$DefaultAdminSubscriptionName = "Default Provider Subscription"
8+
9+
<#
10+
.Synopsis
11+
Clear the portal user data
12+
#>
13+
function Clear-AzsUserData
14+
{
15+
param
16+
(
17+
# The directory tenant identifier of Azure Stack Administrator.
18+
[Parameter(Mandatory=$true)]
19+
[ValidateNotNullOrEmpty()]
20+
[string] $AzsAdminDirectoryTenantId,
21+
22+
# The Azure Stack ARM endpoint URI.
23+
[Parameter(Mandatory=$true)]
24+
[ValidateNotNullOrEmpty()]
25+
[Uri] $AzsAdminArmEndpoint,
26+
27+
# The user principal name of the account who's user data should be cleared.
28+
[Parameter(Mandatory=$true)]
29+
[ValidateNotNullOrEmpty()]
30+
[string] $UserPrincipalName,
31+
32+
# Optional: The directory tenant identifier of account who's user data should be cleared.
33+
# If it is not specified, it will delete all the
34+
[Parameter(Mandatory=$false)]
35+
[ValidateNotNullOrEmpty()]
36+
[string] $DirectoryTenantId,
37+
38+
# Indicate whether it is ADFS env or not
39+
[switch] $ADFS,
40+
41+
# Optional: A credential used to authenticate with Azure Stack. Must support a non-interactive authentication flow. If not provided, the script will prompt for user credentials.
42+
[ValidateNotNull()]
43+
[pscredential] $AutomationCredential = $null
44+
)
45+
#requires -Version 4.0
46+
#requires -Module "AzureRM.Profile"
47+
#requires -Module "Azs.Subscriptions.Admin"
48+
#requires -RunAsAdministrator
49+
50+
$ErrorActionPreference = 'Stop'
51+
$VerbosePreference = 'Continue'
52+
53+
Import-Module $PSScriptRoot\..\..\Identity\GraphAPI\GraphAPI.psm1 -Force
54+
Import-Module $PSScriptRoot\..\..\Identity\AzureStack.Identity.Common.psm1 -Force
55+
56+
Write-Verbose "Login to Azure Stack Admin ARM..." -Verbose
57+
$AzsAdminEnvironmentName = "AzureStackAdmin"
58+
$adminArmEnv = Initialize-AzureRmEnvironment -AdminResourceManagerEndpoint $AzsAdminArmEndpoint -DirectoryTenantId $AzsAdminDirectoryTenantId -EnvironmentName $AzsAdminEnvironmentName
59+
Write-Verbose "Created admin ARM env as $(ConvertTo-JSON $adminArmEnv)" -Verbose
60+
61+
$params = @{
62+
AzureEnvironment = $adminArmEnv
63+
SubscriptionName = $DefaultAdminSubscriptionName
64+
}
65+
if ($AutomationCredential)
66+
{
67+
$params.AutomationCredential = $AutomationCredential
68+
}
69+
$refreshToken = Initialize-AzureRmUserRefreshToken @params
70+
Write-Verbose "Login into admin ARM and got the refresh token." -Verbose
71+
72+
$adminSubscriptionId = (Get-AzureRmSubscription -Verbose | where { $_.Name -ieq $DefaultAdminSubscriptionName }).Id
73+
Write-Verbose "Get default Admin subscription id $adminSubscriptionId." -Verbose
74+
75+
if ($DirectoryTenantId)
76+
{
77+
$directoryTenantIdsArray = [string[]]$DirectoryTenantId
78+
}
79+
else
80+
{
81+
Write-Verbose "Input parameter 'DirectoryTenantId' is empty. Retrieving all the registered tenant directory..." -Verbose
82+
$directoryTenantIdsArray = (Get-AzsDirectoryTenant -Verbose).TenantId
83+
}
84+
85+
Write-Host "Clearing the user data with input user principal name $UserPrincipalName and directory tenants '$DirectoryTenantIdsArray'..."
86+
87+
$clearUserDataResults = @() # key is directory Id, value is clear response
88+
89+
$initializeGraphEnvParams = @{
90+
RefreshToken = $refreshToken
91+
}
92+
if ($ADFS)
93+
{
94+
$initializeGraphEnvParams.AdfsFqdn = (New-Object Uri $adminArmEnv.ActiveDirectoryAuthority).Host
95+
$initializeGraphEnvParams.GraphFqdn = (New-Object Uri $adminArmEnv.GraphUrl).Host
96+
97+
$QueryParameters = @{
98+
'$filter' = "userPrincipalName eq '$($UserPrincipalName.ToLower())'"
99+
}
100+
}
101+
else
102+
{
103+
$graphEnvironment = Resolve-GraphEnvironment -AzureEnvironment $adminArmEnv
104+
Write-Verbose "Resolve the graph env as '$graphEnvironment '" -Verbose
105+
$initializeGraphEnvParams.Environment = $graphEnvironment
106+
107+
$QueryParameters = @{
108+
'$filter' = "userPrincipalName eq '$($UserPrincipalName.ToLower())' or startswith(userPrincipalName, '$($UserPrincipalName.Replace("@", "_").ToLower())')"
109+
}
110+
}
111+
112+
foreach ($dirId in $directoryTenantIdsArray)
113+
{
114+
Write-Verbose "Intializing graph env..." -Verbose
115+
Initialize-GraphEnvironment @initializeGraphEnvParams -DirectoryTenantId $dirId
116+
Write-Verbose "Intialized graph env" -Verbose
117+
118+
Write-Verbose "Querying all users..." -Verbose
119+
$usersResponse = Invoke-GraphApi -ApiPath "/users" -QueryParameters $QueryParameters
120+
Write-Verbose "Retrieved user object as $(ConvertTo-JSON $usersResponse.value)" -Verbose
121+
122+
$userObjectId = $usersResponse.value.objectId
123+
Write-Verbose "Retrieved user object Id as $userObjectId" -Verbose
124+
if (-not $userObjectId)
125+
{
126+
Write-Warning "There is no user '$UserPrincipalName' under directory tenant Id $dirId."
127+
$clearUserDataResult += [pscustomobject]@{
128+
DirectoryTenantId = $dirId
129+
UserPrincipalName = $UserPrincipalName
130+
ErrorMessage = "User not found in directory."
131+
}
132+
continue
133+
}
134+
elseif (([string[]]$userObjectId).Length -gt 1)
135+
{
136+
Write-Warning "There is one more users retrieved with '$UserPrincipalName' under directory tenant Id $dirId."
137+
$clearUserDataResult += [pscustomobject]@{
138+
DirectoryTenantId = $dirId
139+
UserPrincipalName = $UserPrincipalName
140+
ErrorMessage = "One more user accounts found in directory. User principal name may be incorrect. "
141+
}
142+
continue
143+
}
144+
else
145+
{
146+
$params = @{
147+
AzsEnvironment = $adminArmEnv
148+
UserObjectId = $userObjectId
149+
DirectoryTenantId = $dirId
150+
AdminSubscriptionId = $adminSubscriptionId
151+
AzsAdminArmEndpoint = $AzsAdminArmEndpoint
152+
}
153+
$curResult = Clear-SinglePortalUserData @params
154+
$clearUserDataResult += @( $curResult )
155+
}
156+
}
157+
158+
return $clearUserDataResult
159+
}
160+
161+
function Clear-SinglePortalUserData
162+
{
163+
param
164+
(
165+
# The user credential with which to acquire an access token targeting Graph.
166+
[Parameter(Mandatory=$true)]
167+
[ValidateNotNull()]
168+
[Microsoft.Azure.Commands.Profile.Models.PSAzureEnvironment] $AzsEnvironment,
169+
170+
[Parameter(Mandatory=$true)]
171+
[ValidateNotNull()]
172+
[string] $UserObjectId,
173+
174+
[Parameter(Mandatory=$true)]
175+
[ValidateNotNull()]
176+
[string] $DirectoryTenantId,
177+
178+
[Parameter(Mandatory=$true)]
179+
[ValidateNotNull()]
180+
[string] $AdminSubscriptionId,
181+
182+
# The Azure Stack ARM endpoint URI.
183+
[Parameter(Mandatory=$true)]
184+
[ValidateNotNull()]
185+
[Uri] $AzsAdminArmEndpoint
186+
)
187+
188+
try
189+
{
190+
Write-Verbose "Retrieving access token..." -Verbose
191+
$accessToken = (Get-GraphToken -Resource $AzsEnvironment.ActiveDirectoryServiceEndpointResourceId -UseEnvironmentData).access_token
192+
193+
$clearUserDataEndpoint = "$AzsAdminArmEndpoint/subscriptions/$AdminSubscriptionId/providers/Microsoft.PortalExtensionHost.Providers/ClearUserSettings?api-version=2017-09-01-preview"
194+
$headers = @{
195+
Authorization = "Bearer $accessToken"
196+
"Content-Type" = "application/json"
197+
}
198+
$payload = @{
199+
UserObjectId = $UserObjectId
200+
DirectoryTenantId = $DirectoryTenantId
201+
}
202+
$httpPayload = ConvertTo-Json $payload -Depth 10
203+
Write-Verbose "Clearing user data with URI '$clearUserDataEndpoint' and payload: `r`n$httpPayload..." -Verbose
204+
$clearUserDataResponse = $httpPayload | Invoke-RestMethod -Headers $headers -Method POST -Uri $clearUserDataEndpoint -TimeoutSec 120 -Verbose
205+
206+
return [pscustomobject]@{
207+
DirectoryTenantId = $DirectoryTenantId
208+
UserPrincipalName = $UserPrincipalName
209+
ResponseData = $clearUserDataResponse
210+
}
211+
}
212+
catch
213+
{
214+
if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound -and (ConvertFrom-JSON $_.ErrorDetails.Message).error.code -eq "NoPortalUserData")
215+
{
216+
Write-Warning "No user data with user object Id and directory tenant Id"
217+
return [pscustomobject]@{
218+
DirectoryTenantId = $DirectoryTenantId
219+
UserPrincipalName = $UserPrincipalName
220+
ErrorMessage = "No portal user data"
221+
}
222+
}
223+
else
224+
{
225+
Write-Warning "Exception when clear user data with user object Id and directory tenant Id: $_`r`n$($_.Exception)"
226+
return [pscustomobject]@{
227+
DirectoryTenantId = $DirectoryTenantId
228+
UserPrincipalName = $UserPrincipalName
229+
ErrorMessage = "Exception when clearing user data"
230+
Exception = $_.Exception
231+
}
232+
}
233+
}
234+
}
235+
236+
Export-ModuleMember -Function Clear-AzsUserData

0 commit comments

Comments
 (0)