Skip to content

Commit abf3d08

Browse files
dcluomaxdavefalkus
authored andcommitted
New Script to add ability to download MAM app registration report via graph (#85)
* New Script to add ability to download report via graph In azure portal, javascript supports only up to 4.29 million items. VIa powershell, we can download more than this boundry. fix comment example fix comment example typo * Address David's comments and add some try-catches * formatting
1 parent 2898297 commit abf3d08

File tree

1 file changed

+376
-0
lines changed

1 file changed

+376
-0
lines changed
Lines changed: 376 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,376 @@
1+
<#
2+
3+
.COPYRIGHT
4+
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
5+
See LICENSE in the project root for license information.
6+
7+
#>
8+
9+
####################################################
10+
11+
function Get-AuthToken {
12+
13+
<#
14+
.SYNOPSIS
15+
This function is used to authenticate with the Graph API REST interface
16+
.DESCRIPTION
17+
The function authenticate with the Graph API Interface with the tenant name
18+
.EXAMPLE
19+
Get-AuthToken
20+
Authenticates you with the Graph API interface
21+
.NOTES
22+
NAME: Get-AuthToken
23+
#>
24+
25+
[cmdletbinding()]
26+
27+
param
28+
(
29+
[Parameter(Mandatory = $true)]
30+
$User
31+
)
32+
33+
$userUpn = New-Object "System.Net.Mail.MailAddress" -ArgumentList $User
34+
35+
$tenant = $userUpn.Host
36+
37+
Write-Host "Checking for AzureAD module..."
38+
39+
$AadModule = Get-Module -Name "AzureAD" -ListAvailable
40+
41+
if ($AadModule -eq $null) {
42+
43+
Write-Host "AzureAD PowerShell module not found, looking for AzureADPreview"
44+
$AadModule = Get-Module -Name "AzureADPreview" -ListAvailable
45+
46+
}
47+
48+
if ($AadModule -eq $null) {
49+
write-host
50+
write-host "AzureAD Powershell module not installed..." -f Red
51+
write-host "Install by running 'Install-Module AzureAD' or 'Install-Module AzureADPreview' from an elevated PowerShell prompt" -f Yellow
52+
write-host "Script can't continue..." -f Red
53+
write-host
54+
exit
55+
}
56+
57+
# Getting path to ActiveDirectory Assemblies
58+
# If the module count is greater than 1 find the latest version
59+
60+
if ($AadModule.count -gt 1) {
61+
62+
$Latest_Version = ($AadModule | select version | Sort-Object)[-1]
63+
64+
$aadModule = $AadModule | ? { $_.version -eq $Latest_Version.version }
65+
66+
# Checking if there are multiple versions of the same module found
67+
68+
if ($AadModule.count -gt 1) {
69+
70+
$aadModule = $AadModule | select -Unique
71+
72+
}
73+
74+
$adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
75+
$adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"
76+
77+
}
78+
79+
else {
80+
81+
$adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
82+
$adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"
83+
84+
}
85+
86+
[System.Reflection.Assembly]::LoadFrom($adal) | Out-Null
87+
88+
[System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null
89+
90+
$clientId = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547"
91+
92+
$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
93+
94+
$resourceAppIdURI = "https://graph.microsoft.com"
95+
96+
$authority = "https://login.microsoftonline.com/$Tenant"
97+
98+
try {
99+
100+
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
101+
102+
# https://msdn.microsoft.com/en-us/library/azure/microsoft.identitymodel.clients.activedirectory.promptbehavior.aspx
103+
# Change the prompt behaviour to force credentials each time: Auto, Always, Never, RefreshSession
104+
105+
$platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto"
106+
107+
$userId = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier" -ArgumentList ($User, "OptionalDisplayableId")
108+
109+
$authResult = $authContext.AcquireTokenAsync($resourceAppIdURI, $clientId, $redirectUri, $platformParameters, $userId).Result
110+
111+
# If the accesstoken is valid then create the authentication header
112+
113+
if ($authResult.AccessToken) {
114+
115+
# Creating header for Authorization token
116+
117+
$authHeader = @{
118+
'Content-Type' = 'application/json'
119+
'Authorization' = "Bearer " + $authResult.AccessToken
120+
'ExpiresOn' = $authResult.ExpiresOn
121+
}
122+
123+
return $authHeader
124+
125+
}
126+
127+
else {
128+
129+
Write-Host
130+
Write-Host "Authorization Access Token is null, please re-run authentication..." -ForegroundColor Red
131+
Write-Host
132+
break
133+
134+
}
135+
136+
}
137+
138+
catch {
139+
140+
write-host $_.Exception.Message -f Red
141+
write-host $_.Exception.ItemName -f Red
142+
write-host
143+
break
144+
145+
}
146+
147+
}
148+
149+
####################################################
150+
151+
Function Get-ManagedAppPolicyRegistrationSummary() {
152+
153+
<#
154+
.SYNOPSIS
155+
This function is used to download App Protection Report for iOS and Android.
156+
.DESCRIPTION
157+
The function connects to the Graph API Interface and gets the ManagedAppRegistrationSummary
158+
.EXAMPLE
159+
Get-ManagedAppPolicyRegistrationSummary -ReportType Android_iOS
160+
Returns any managed app policies configured in Intune
161+
.NOTES
162+
NAME: Get-ManagedAppPolicyRegistrationSummary
163+
#>
164+
165+
[cmdletbinding()]
166+
167+
param
168+
(
169+
[ValidateSet("Android_iOS", "WIP_WE", "WIP_MDM")]
170+
$ReportType,
171+
$NextPage
172+
)
173+
174+
$graphApiVersion = "Beta"
175+
$Stoploop = $false
176+
[int]$Retrycount = "0"
177+
do{
178+
try {
179+
180+
if ($ReportType -eq "" -or $ReportType -eq $null) {
181+
$ReportType = "Android_iOS"
182+
183+
}
184+
elseif ($ReportType -eq "Android_iOS") {
185+
186+
$Resource = "/deviceAppManagement/managedAppStatuses('appregistrationsummary')?fetch=6000&policyMode=0&columns=DisplayName,UserEmail,ApplicationName,ApplicationInstanceId,ApplicationVersion,DeviceName,DeviceType,DeviceManufacturer,DeviceModel,AndroidPatchVersion,AzureADDeviceId,MDMDeviceID,Platform,PlatformVersion,ManagementLevel,PolicyName,LastCheckInDate"
187+
if ($NextPage -ne "" -and $NextPage -ne $null) {
188+
$Resource += "&seek=$NextPage"
189+
}
190+
$uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
191+
Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get
192+
193+
}
194+
195+
elseif ($ReportType -eq "WIP_WE") {
196+
197+
$Resource = "deviceAppManagement/managedAppStatuses('windowsprotectionreport')"
198+
if ($NextPage -ne "" -and $NextPage -ne $null) {
199+
$Resource += "&seek=$NextPage"
200+
}
201+
$uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
202+
Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get
203+
204+
}
205+
206+
elseif ($ReportType -eq "WIP_MDM") {
207+
208+
$Resource = "deviceAppManagement/mdmWindowsInformationProtectionPolicies"
209+
210+
$uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
211+
Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get
212+
213+
}
214+
$Stoploop = $true
215+
}
216+
217+
catch {
218+
219+
$ex = $_.Exception
220+
221+
# Retry 4 times if 503 service time out
222+
if($ex.Response.StatusCode.value__ -eq "503") {
223+
$Retrycount = $Retrycount + 1
224+
$Stoploop = $Retrycount -gt 3
225+
if($Stoploop -eq $false) {
226+
Start-Sleep -Seconds 5
227+
continue
228+
}
229+
}
230+
$errorResponse = $ex.Response.GetResponseStream()
231+
$reader = New-Object System.IO.StreamReader($errorResponse)
232+
$reader.BaseStream.Position = 0
233+
$reader.DiscardBufferedData()
234+
$responseBody = $reader.ReadToEnd();
235+
Write-Host "Response content:`n$responseBody" -f Red
236+
Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
237+
write-host
238+
$Stoploop = $true
239+
break
240+
}
241+
}
242+
while ($Stoploop -eq $false)
243+
244+
}
245+
246+
####################################################
247+
248+
Function Test-AuthToken(){
249+
250+
# Checking if authToken exists before running authentication
251+
if ($global:authToken) {
252+
253+
# Setting DateTime to Universal time to work in all timezones
254+
$DateTime = (Get-Date).ToUniversalTime()
255+
256+
# If the authToken exists checking when it expires
257+
$TokenExpires = ($authToken.ExpiresOn.datetime - $DateTime).Minutes
258+
259+
if ($TokenExpires -le 0) {
260+
261+
write-host "Authentication Token expired" $TokenExpires "minutes ago" -ForegroundColor Yellow
262+
write-host
263+
264+
# Defining User Principal Name if not present
265+
266+
if ($User -eq $null -or $User -eq "") {
267+
268+
$global:User = Read-Host -Prompt "Please specify your user principal name for Azure Authentication"
269+
Write-Host
270+
271+
}
272+
273+
$global:authToken = Get-AuthToken -User $User
274+
275+
}
276+
}
277+
278+
# Authentication doesn't exist, calling Get-AuthToken function
279+
280+
else {
281+
282+
if ($User -eq $null -or $User -eq "") {
283+
284+
$global:User = Read-Host -Prompt "Please specify your user principal name for Azure Authentication"
285+
Write-Host
286+
287+
}
288+
289+
# Getting the authorization token
290+
$global:authToken = Get-AuthToken -User $User
291+
292+
}
293+
}
294+
295+
####################################################
296+
297+
Test-AuthToken
298+
299+
####################################################
300+
301+
Write-Host
302+
303+
$ExportPath = Read-Host -Prompt "Please specify a path to export the policy data to e.g. C:\IntuneOutput"
304+
305+
# If the directory path doesn't exist prompt user to create the directory
306+
307+
if (!(Test-Path "$ExportPath")) {
308+
309+
Write-Host
310+
Write-Host "Path '$ExportPath' doesn't exist, do you want to create this directory? Y or N?" -ForegroundColor Yellow
311+
312+
$Confirm = read-host
313+
314+
if ($Confirm -eq "y" -or $Confirm -eq "Y") {
315+
316+
new-item -ItemType Directory -Path "$ExportPath" | Out-Null
317+
Write-Host
318+
319+
}
320+
321+
else {
322+
323+
Write-Host "Creation of directory path was cancelled..." -ForegroundColor Red
324+
Write-Host
325+
break
326+
327+
}
328+
329+
}
330+
331+
Write-Host
332+
333+
####################################################
334+
335+
$AppType = Read-Host -Prompt "Please specify the type of report [Android_iOS, WIP_WE, WIP_MDM]"
336+
337+
if($AppType -eq "Android_iOS" -or $AppType -eq "WIP_WE" -or $AppType -eq "WIP_MDM") {
338+
339+
Write-Host
340+
write-host "Running query against Microsoft Graph to download App Protection Report for '$AppType'.." -f Yellow
341+
342+
$ofs = ','
343+
$stream = [System.IO.StreamWriter]::new("$ExportPath\AppRegistrationSummary_$AppType.csv", $false, [System.Text.Encoding]::UTF8)
344+
$ManagedAppPolicies = Get-ManagedAppPolicyRegistrationSummary -ReportType $AppType
345+
$stream.WriteLine([string]($ManagedAppPolicies.content.header | % {$_.columnName } ))
346+
347+
do {
348+
Test-AuthToken
349+
350+
write-host "Your data is being downloaded for '$AppType'..."
351+
$MoreItem = $ManagedAppPolicies.content.skipToken -ne "" -and $ManagedAppPolicies.content.skipToken -ne $null
352+
353+
foreach ($SummaryItem in $ManagedAppPolicies.content.body) {
354+
355+
$stream.WriteLine([string]($SummaryItem.values -replace ",","."))
356+
}
357+
358+
if ($MoreItem){
359+
360+
$ManagedAppPolicies = Get-ManagedAppPolicyRegistrationSummary -ReportType $AppType -NextPage ($ManagedAppPolicies.content.skipToken)
361+
}
362+
363+
} while ($MoreItem)
364+
365+
$stream.close()
366+
367+
write-host
368+
369+
}
370+
371+
else {
372+
373+
Write-Host "AppType isn't a valid option..." -ForegroundColor Red
374+
Write-Host
375+
376+
}

0 commit comments

Comments
 (0)