Overview
I created this application, Microsoft Intune Management Tool, to allow you to easily View the Microsoft Graph return content behind the settings applied and configured within your Intune tenant. Additionally, it has the ability to Backup your entire tenant (as JSON content data), creating special database files of the settings and where they were applied, as well as the ability to Restore those settings back to the same or a different tenant. This would be ideal in a scenario where you might want to promote settings from a Pilot tenant to your Production tenant once you have verified the changes. Finally, the tool can create an HTML Report of the settings applied to your Intune tenant to give you a quick overview of settings, users, groups, and devices associated to your environment.
Getting Your Environment Ready for the Application
This application was written in PowerShell and needs to be connected to the internet in order to run. It also requires the AzureAD module for ADAL (Azure Active Directory Authentication Libraries) authentication. It will attempt to download this module automatically if you do not already have it installed. However, feel free to get your environment ready by running:
Install-Module AzureAD
Installing the Application
Download the required files below and unzip the files into a directory of your choosing.
Once downloaded, you can optionally choose to add your user account to the invocation script inside of the Invoke-MicrosoftIntuneManagementTool.ps1 file so you do not need to enter it every time. To do this, simply uncomment the $user variable at the end of the file and add your administrative Intune tenant account:
$user = user@company.onmicrosoft.com
Running the Application
To run the application, run the Invoke-MicrosoftIntuneManagementTool.ps1 script within PowerShell and you will immediately be prompted to log into your Azure tenant:
General Use of Application
Once logged in, you will be presented with the applications main panel. The layout is meant to mimic the general layout with your Intune web console.
Each of the non-colored menu items are standard view processes and will allow you to see the fine details surrounding your Intune setup. This content in this application is gathered from the Microsoft Graph API through GET request URIs, where JSON content is returned in the HTTP response body to the application. For example, if we click Users we can see the Azure AD users associated to this tenant. To retrieve these users the application sends the request URI https://graph.microsoft.com/v1.0/users to the Microsoft Graph API, and the users are returned as JSON content, converted to a PowerShell object and the object’s displayName property is used to create the menu structure. Selecting a user allows us to view the JSON content which contains the properties set for that user, including the displayName property used to build the menu item:
Every view component in this application works in a similar manner. Through this process, you can look at the finer details of each of the areas within your Intune setup. For a look at each section, read the A Look at Each Application Section below. Your intune console interacts with Microsoft Graph in the same was as this application where a request URI is sent to Microsoft Graph and JSON content is returned to populate the site content.
Backup Your Tenant
To back up your tenant, launch the tool and select Backup Your Tenant. This will create backup entries for each of your Intune settings, policies, devices, users and groups. Contained with this backup will be a special restore .CSV file that is used specifically with this application’s restore feature. This .CSV is a small collection of the settings and their categories/request URI the setting belongs to, as well as the associated JSON content file which contains the actual backup content. To start the process, select Backup Your Entire Intune Tenant and select a folder to place the backup files and folders inside of:
Once the backup process begins, the tool will determine which Intune settings are applicable to your environment to be backed up. This is accomplished by running a GET request to Microsogt Graph against each request URI that I programmed into the application (which I gathered through the Microsoft Graph API -> Intune documentation). When a request URI has a response header containing valid JSON content, it will back it up by saving the content as a JSON file and add a line to the .CSV file.
Once the process is complete, within your selected folder will be the backed up settings, policies, devices, users and groups associated to your Intune setup. The backups are pre-arranged into category sub-folders and restore files based on where they generally are found within the Intune console:
The .CSV files are special files used for this applications restore process. Because a content restore needs be sent back to Microsoft Graph as a POST method, the specific URI associated to the content when it was retrieved with the GET method can now be conveniently retrieved from the .CSV file to build the POST request URI.
Modifying a Backup
Contained within each backup folder are the JSON content source files:
You can easily modify or view the contents of a JSON content source file if you’d like to make adjustments before the data is restored. For example, here we could go into the MobileApps directory and modify one of the application’s name and description:
Restore Your Tenant
To restore your tenant, launch the tool and select Restore Your Tenant. You can either select to restore an individual JSON content file (such as a single application or a single user), or restore a complete section of your tenant, such as in this example where we could restore all the mobile applications:
However, before a restore can take place, the JSON content may need to be modified to allow it to work in a POST method back to Microsoft Graph. See more on this process below.
Import Notes on Restoring Tenant Information
Restoring your tenant may require modification to the JSON content source file, depending on the type of content being restored. For example, the JSON application backup files contained inside the MobileApps directory contain additional properties returned from the GET response that will not work in a POST method back to the Microsoft Graph API. To import a mobile application, ONLY the following JSON key/value pairs can be kept in the file as showing in the example below. You would need to delete the other pairs from the JSON file simply by deleting their line from the file.
A properly formatted Mobile Application JSON file only contains these key/value pairs and is now ready to be POSTed back to Microsoft Graph using the applications retore feature:
{
"@odata.type": "#microsoft.graph.androidStoreApp",
"displayName": "Intune Managed Browser",
"description": "Intune Managed Browser",
"publisher": "Microsoft Corporation",
"isFeatured": true,
"appStoreUrl": "https://play.google.com/store/apps/details id=com.microsoft.intune.mam.managedbrowser&hl=en",
"minimumSupportedOperatingSystem": {
"@odata.type": "#microsoft.graph.androidMinimumOperatingSystem",
"v4_0": true
}
Any applications that could not be POSTed will be skipped during the restore process with a warning message:
Restoring a Single Setting
To restore a single setting, run the backup wizard and choose Restore an Individual Setting (JSON File). This will allow you to pick an individual JSON backup file, such as a single application:
This will require you to know what URI the JSON backup needs to be POSTed to:
You can determine this by looking with the restore .CSV associated to this backup group:
Again, JSON restores by this method follow the same rules under the Import Notes on Restoring Tenant Information section above.
Create an HTML Report of Your Tenant Settings
The application has the ability to create a full HTML report of all settings, policies, users and groups within your tenant. To run the report choose Create HTML Report of Tenant Settings from the main menu. Next, select a folder where the HTML report file will be created.
During the process of report generation, you can monitor the output:
Generating the report takes about three minutes to complete, but could take longer if you have several more settings, policies, users or groups. Once the report has been generated, locate it within the folder you specified and open it in your browser. The report is intended to give you a better understanding of what general settings, policies, users and groups are configured in your tenant:
A Look at Each Application Section
This section will take a look at each of the application sections and give a brief overview of the setting, policy, user or group contained within.
Device Enrollment
The device enrollment section contains all of the policies centered on getting new devices into the Intune tenant. Device Identifiers are an IMEI or serial you have preassigned to Intune to identify your corporate devices. Windows Autopilot is used to set up and pre-configure new devices, getting them ready for productive use, allowing a device to be shipped from OEM straight to an end user. NDES (Network Device Enrolment Service) is a feature of AD Certificate Services (CS) and provides an implementation of the Simple Certification Enrolment Protocol (SCEP).
Device Compliance
The device compliance section contains all of the policies centered on getting new devices into the Intune tenant. Intune device compliance policies define the rules and settings that a device must comply with in order to be considered compliant by your Intune tenant. When you enroll a device into Intune, the Azure AD registration process happens, which updates the device properties with more information into Azure AD. One key device information is the device compliance status, which is used by conditional access policies to block or allow access to e-mail and other corporate resources. Device Management Partners include policies to coordinate device compliance from a 3rd party program, such as Jamf compliance for Apple computers.
Devices
This section lets you see what managed devices are enrolled into your Intune tenant. This differs from devices that might use some of your corporate resources through MAM-WE (Mobile Application Management Without Enrollment). These devices are consider unmanaged and would not be listed here.
Mobile Applications
This section lets you view the apps and associated policies centered on Mobile Application Management in your environment. Mobile App Categories can be used to help you sort apps to make them easier for users to find in the company portal. Managed App Policies, also known as App Protection Policies, are used to prevent company data from saving to the local storage of the device, and restrict data movement to other apps that are not protected by App protection policies.
eBooks
The Apple Volume Purchase Program (VPP) lets you purchase multiple licenses for a book that you want to distribute to users in your company. You can distribute books from the Business, or Education stores.
Condition Access
Conditional Access allow you to define conditions that prevent access to corporate data based on location, device, user state, and application sensitivity. Conditional Access settings in Intune are actually pulled from the Azure conditional access portal.
On-Premises Access
This looks at the policies centered on conditional access for Exchange on-premises based on device compliance.
Users
This section simply lists the users in your Azure Active Directory environment that are associated to your Intune tenant. Remember, after you’ve created a user, you need to assign an Intune license to that user. Without assigning them a license, they can’t enroll their device.
Groups
This section lists all of your Azure Active Directory security groups.
Intune Roles
This section lets you view your Intune roles and the users they are associated to. Intune Roles are a part of Role Based Access Controls in Intune. RBAC helps you control who can perform various Intune tasks within your organization, and who those tasks apply to.
Software Updates
Update policies for iOS let you view policies that would force your supervised iOS devices to automatically install the latest available software update.
Advanced: Adding Future Release Features to the Application
This application is based primarily around REST URI requests used by Intune to Microsoft Graph. When Microsoft adds a new feature to Intune, there will likely be a new REST URI associated to this feature. Finding the REST URI is a matter of locating it within the Microsoft Graph/Intune documentation provided by Microsoft or by finding it in your browser’s developer mode (F12) when a request to the new feature is made as show in this example:
Once a URI is obtained for a new feature, open the MicrosoftIntuneManagementToolModules.psm1 modules file. Arrays of the REST URIs are combined in the main menu function and are easily mapped toward the end of the file. For example, if we wanted to add a new Troubleshooting function, and we know the URI for it, we can go locate the Get-MicrosoftGraphIntuneTroubleshootingQueries function. All we need to do is add the new function as a line in the array creation as shown below:
function Get-MicrosoftGraphIntuneTroubleshootingQueries{
$MicrosoftGraphIntuneTroubleshootingQueries = @()
$MicrosoftGraphIntuneTroubleshootingQueries += [PSCustomObject] @{'Query' = 'https://graph.microsoft.com/beta/deviceManagement/troubleshootingEvents'; 'DisplayName' = 'Troubleshooting Events'};
$MicrosoftGraphIntuneTroubleshootingQueries += [PSCustomObject] @{'Query' = 'https://graph.microsoft.com/beta/deviceManagement/newTroubleshootingArea'; 'DisplayName' = 'New Troubleshooting Area!'};
return $MicrosoftGraphIntuneTroubleshootingQueries
}
Now when we re-run the application we will find our new URI function listed under troubleshooting, and it will also be included in the backup/restore and reporting functions. It’s that easy!
This application will automatically take any settings returned by that URI query and create a menu structure for them. Since I completely made up this URI for demonstration purposes (or if you picked one that didn’t work), we simply get the following message when trying to bring up that menu item:
The Invocation Function: Invoke-MicrosoftIntuneManagementTool.ps1
<# .Synopsis Invoke the Microsoft Intune Management Tool application. It provides basic structure for module imports and authentication. .Author David Maiolo .Notes $user variable is your InTune tenant admin account such as admin@yourcompany.onmicrosoft.com Additional documention on this program can be found at https://www.davidmaiolo.com/portfolio-item/intune-management-tool/ #>
function Invoke-MicrosoftIntuneManagementTool{
Param
(
[Parameter(Mandatory=$true,ValueFromPipeline=$false,Position=0)]
[ValidateScript({$_ -like '*onmicrosoft.com*'})]
[string]$user
)
clear
#Import Local Modules
$ModulePath = "$PSScriptRoot\MicrosoftIntuneManagementToolModules.psm1"
$localmodules = @("$ModulePath")
foreach ($localmodule in $localmodules){
if (Test-Path "$ModulePath") {
try{
Write-Host "Importing modules..."
Import-Module $localmodule -Force
}
catch{
Write-Error "Error: Could not import $localmodule."
break
}
}else {
Write-Error "Error: Module Path '$ModulePath' doesn't exist."
break
}
}
$modules = @("AzureAD")
#Install Azure AD modules
foreach ($module in $modules){
if (Get-Module -ListAvailable -Name $module) {
Write-Host "Confirmed: $module module was already imported."
} else {
try{
Write-Host "$module module does not exist. Installing $module..." -ForegroundColor Yellow
Install-Module $module
}catch{
Write-Error "Could not install required module $module. Tried to run `"Install-Module $module`" but it didn't work."
break
}
}
}
# Checking if authToken exists before running authentication
if($global:authToken){
#Get Universal Date and Time
$DateTime = (Get-Date).ToUniversalTime()
# If the authToken exists checking when it expires
$TokenExpires = ($authToken.ExpiresOn.datetime - $DateTime).Minutes
if($TokenExpires -le 0){
write-host "Warning: Authentication Token expired" $TokenExpires "minutes ago." -ForegroundColor Yellow
$global:authToken = Get-AuthToken -User $User
}else{
Write-Host "User $user has already been authenticated. (against Microsoft Graph)"
}
}else{
$global:authToken = Get-AuthToken -User $User
}
#Invoke the Application
Get-IntuneManagementToolMenu
}
#Choose Login User
#$user = "dmaiolo@stonystudio.onmicrosoft.com"
if(!($user)){$user = Read-Host "Intune Tenant Admin Account"}
#Invoke the Invoker :-)
Invoke-MicrosoftIntuneManagementTool -user $user
The Methods: MicrosoftIntuneManagementToolModules.psm1
#Main Program Function
function Get-IntuneManagementToolMenu{
<# .SYNOPSIS This function is used to call the main menu of the application. It will look to the Query functions as the structure for what should be included. .Author David Maiolo .NOTES Additional documention on this program can be found at https://www.davidmaiolo.com/portfolio-item/intune-management-tool/ #>
$global:ApplicationName = "Microsoft Intune Management Tool"
$global:ApplicationAuthor = "David Maiolo"
$global:ApplicationVersion = "0.8"
$IntuneManagementQueryCollections = @()
$IntuneManagementQueryCollections += [PSCustomObject] @{'Querys' = 'Get-MicrosoftGraphIntuneDeviceEnrollmentQueries'; 'DisplayName' = 'Device Enrollment'};
$IntuneManagementQueryCollections += [PSCustomObject] @{'Querys' = 'Get-MicrosoftGraphIntuneDeviceComplianceQueries'; 'DisplayName' = 'Device Compliance'};
$IntuneManagementQueryCollections += [PSCustomObject] @{'Querys' = 'Get-MicrosoftGraphIntuneDeviceQueries'; 'DisplayName' = 'Devices'};
$IntuneManagementQueryCollections += [PSCustomObject] @{'Querys' = 'Get-MicrosoftGraphIntuneAppManagementQueries'; 'DisplayName' = 'Mobile Apps'};
$IntuneManagementQueryCollections += [PSCustomObject] @{'Querys' = 'Get-MicrosoftGraphIntuneEbookQueries'; 'DisplayName' = 'eBooks'};
$IntuneManagementQueryCollections += [PSCustomObject] @{'Querys' = 'Get-MicrosoftGraphIntuneConditionalAccessQueries'; 'DisplayName' = 'Conditional Access'};
$IntuneManagementQueryCollections += [PSCustomObject] @{'Querys' = 'Get-MicrosoftGraphIntuneOnPremisesQueries'; 'DisplayName' = 'On-premises Access'};
$IntuneManagementQueryCollections += [PSCustomObject] @{'Querys' = 'Get-MicrosoftGraphAzureUsersQueries'; 'DisplayName' = 'Users'};
$IntuneManagementQueryCollections += [PSCustomObject] @{'Querys' = 'Get-MicrosoftGraphAzureGroupsQueries'; 'DisplayName' = 'Groups'};
$IntuneManagementQueryCollections += [PSCustomObject] @{'Querys' = 'Get-MicrosoftGraphIntuneRolesQueries'; 'DisplayName' = 'Intune Roles'};
$IntuneManagementQueryCollections += [PSCustomObject] @{'Querys' = 'Get-MicrosoftGraphIntuneSoftwareUpdatesQueries'; 'DisplayName' = 'Software Updates'};
$IntuneManagementQueryCollections += [PSCustomObject] @{'Querys' = 'Get-MicrosoftGraphIntuneTroubleshootingQueries'; 'DisplayName' = 'Troubleshoot'};
try{
#process the Main application Menu
clear
Write-Host "========================================="
Write-Host "$global:ApplicationName v$global:ApplicationVersion "
Write-Host "========================================="
Write-Host "Application Author: $global:ApplicationAuthor"
#Get-IntuneManagementDashBoard
#Write-Host "========================================="
$MenuArray = @()
for ($i=1;$i -le $IntuneManagementQueryCollections.count; $i++) {
Write-Host "[$i] $($IntuneManagementQueryCollections[$i-1].DisplayName)"
if($i -eq $IntuneManagementQueryCollections.count){
$iplusone = ($i+1)
Write-Host "[$iplusone] Create HTML Report of Tenant Settings" -ForegroundColor Blue
$iplustwo = ($i+2)
Write-Host "[$iplustwo] Restore Your Tenant" -ForegroundColor Cyan
$iplusthree = ($i+3)
Write-Host "[$iplusthree] Backup Your Tenant" -ForegroundColor Cyan
$iplusfour = ($i+4)
Write-Host "[$iplusfour] Exit" -ForegroundColor Yellow
}
}
do {
try {$numOk = $true;[int]$ans = Read-host "Enter Selection"}
catch {$numOK = $false}
} # end do
until (($ans -ge 1 -and $ans -le $iplusfour) -and $numOK)
if ($ans -eq ($IntuneManagementQueryCollections.count+1)){
Write-Host "Starting Backup program..."
sleep -Milliseconds 300
Get-IntuneTenantReport -IntuneManagementQueryCollections $IntuneManagementQueryCollections
}elseif ($ans -eq ($IntuneManagementQueryCollections.count+2)){
Write-Host "Starting Restore program..."
sleep -Milliseconds 300
Import-IntuneTenant
}
elseif ($ans -eq ($IntuneManagementQueryCollections.count+3)){
Write-Host "Starting Backup program..."
sleep -Milliseconds 300
Export-IntuneTenant -IntuneManagementQueryCollections $IntuneManagementQueryCollections
}
elseif ($ans -eq ($IntuneManagementQueryCollections.count+4)){
Write-Host "You Exited The Program."
exit
}else{
$DisplayName = $($IntuneManagementQueryCollections[$ans-1].DisplayName)
$Querys = Invoke-Expression $($IntuneManagementQueryCollections[$ans-1].Querys)
Write-Host "Invoking $($IntuneManagementQueryCollections[$ans-1].Querys) ($($IntuneManagementQueryCollections[$ans-1].DisplayName))..."
sleep -Milliseconds 300
Get-MainMenu -Querys $Querys -QueryName $DisplayName -UseSubMenu
}
}catch{
Write-Error "There was a problem rendering the main program menu."
break
}
}
function Get-IntuneManagementDashBoard{
<# .SYNOPSIS This function is used to call a small dashboard of settings. It currently is not being used but can easily added to the main menu function if desired. .Author David Maiolo .NOTES #>
#Tenant Users
$tenantUsers = Get-MicrosoftGraphRESTRequest -uri "https://graph.microsoft.com/v1.0/users"
#Tenant Groups
$tenantGroups = Get-MicrosoftGraphRESTRequest -uri "https://graph.microsoft.com/v1.0/groups"
#Devices
$tenantDevices = Get-MicrosoftGraphRESTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices"
#Applications
$tenantApplications = Get-MicrosoftGraphRESTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/detectedApps"
Write-Host "Azure Users:"($tenantUsers| Measure-Object).Count
Write-Host "Azure Groups:"($tenantGroups| Measure-Object).Count
Write-Host "Managed Devices:"($tenantDevices| Measure-Object).Count
Write-Host "Deteted Applications:"($tenantApplications| Measure-Object).Count
}
#Functions Specific To Microsoft Graph
function Get-MicrosoftGraphRESTRequest{
<# .SYNOPSIS This function is used to call your Microsoft Graph request of choice and return the results as an array. .Author David Maiolo .NOTES NAME: Get-MicrosoftGraphRESTRequest #>
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0)]
[string]$uri
)
try{
$GraphRequestValue = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value
if ($GraphRequestValue -eq $null){
Write-Host "This URI is has no values defined in your tenant: $uri" -ForegroundColor Yellow
return $false
}
return $GraphRequestValue
}catch{
<#$ex = $_.Exception $errorResponse = $ex.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($errorResponse) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $responseBody = $reader.ReadToEnd(); Write-Host "Response content:`n$responseBody" -ForegroundColor Red Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)" write-host break#>
$ex = $_.Exception
Write-Host "This URI is invalid in your tenant: $uri" -ForegroundColor Red
return $false
}
}
function Get-MicrosoftGraphURIValidation{
<# .SYNOPSIS This function is used to test a Microsoft Graph URI. It returns true if the URI query is good and false otherwise. .Author David Maiolo .NOTES NAME: Get-MicrosoftGraphRESTRequest #>
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0)]
[string]$uri
)
$QueryTest = Get-MicrosoftGraphRESTRequest -uri $uri
#return true or false depending on how the query faired
if($QueryTest){return $true}{else return $false}
}
function Get-MicrosoftGraphURIValidQuerys{
<# .SYNOPSIS This function builds a small valid query report. It used internally by the developed for debugging purposes. .Author David Maiolo .NOTES #>
Param
(
[Parameter(Mandatory=$true,ValueFromPipeline=$false,Position=0)]
$Querys
)
$validQuerys = @()
foreach ($Query in $Querys){
if (Get-MicrosoftGraphRESTRequest -uri $Query.Query){
Write-Host $Query.DisplayName"is valid." -ForegroundColor Green
$validQuerys+=$Query
}else{
Write-Host $Query.DisplayName"is invalid." -ForegroundColor Red
}
}
}
#Reporting Functions
function Get-IntuneTenantReport{
<# .SYNOPSIS This function creates the HTML Report of Tenant Settings. .Author David Maiolo .NOTES #>
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0)]
$IntuneManagementQueryCollections
)
#process the report application Menu
clear
Write-Host "========================================="
Write-Host "Intune Tenant HTML Report Wizard"
Write-Host "========================================="
Write-Host "[1] Create HTML Report Intune Tenant Settings"
Write-Host "[2] Instructions"
Write-Host "[3] Return To Main Menu" -ForegroundColor Yellow
do {
try {$numOk = $true;[int]$ans = Read-host "Enter Selection"}
catch {$numOK = $false}
} # end do
until (($ans -ge 1 -and $ans -le 3) -and $numOK)
switch ($ans)
{
'1' {
Write-Host "Please choose a folder to save the report..."
#Setup A CSV File That Can Be Used for the Report
$reportfolderroot = Get-Folder
$reportValuesFile = "intune_tenant_report(" + $(get-date -f dd-MM-yyyy-H-mm-ss) + ").html"
$reportValuesFilePath = Join-Path $reportfolderroot $reportValuesFile
#Create the template for the HTML report
$reportHTMLHead = '<!doctype html>
Intune Tenant Report
Created with ‘+$global:ApplicationName+’ ‘+$global:ApplicationVersion+'(by ‘+$global:ApplicationAuthor+’)’+’
'
$reportHTMLFoot = ''
#Create head of HTML Report
$reportHTMLHead | Out-File -filepath $reportValuesFilePath -Append
if(!(Test-Path $reportfolderroot)){Write-Host "You chose an improper folder or the folder could not be accessed. Cannot continue." -ForegroundColor Red;break}
Write-Host "Starting Intune Tenant HTML Report Process to $reportfolderroot..."
sleep 5
foreach ($IntuneManagementQueryCollection in $IntuneManagementQueryCollections){
$DisplayName = $($IntuneManagementQueryCollection.DisplayName)
$Querys = Invoke-Expression $($IntuneManagementQueryCollection.Querys)
#create the Main Category String
$Category = $($IntuneManagementQueryCollection.DisplayName)
Write-Host $Category -ForegroundColor Cyan
#Output Category Heading to HTML Report
$CategoryHTMLstring = "
“+$Category+”
” $CategoryHTMLstring | Out-File -filepath $reportValuesFilePath -Append foreach ($Query in $Querys){ #Create the JSON Objects by running a REST query to Microsft Graph $objects = Get-MicrosoftGraphRESTRequest -uri $Query.Query if ($objects -ne $null -and $objects -ne $false){ #create the sub category string and count the items within it $countOnItem = $Query.DisplayName+”: “+(($objects| Measure-Object).Count)+” settings configured.” Write-Host “>>>”$countOnItem #Output Category Heading to HTML Report $countOnItemHTMLstring = ”
“+$countOnItem+”
” $countOnItemHTMLstring | Out-File -filepath $reportValuesFilePath -Append #Start a bulleted list $startHTMLList = ‘
-
‘ $startHTMLList | Out-File -filepath $reportValuesFilePath -Append foreach ($object in $objects){ #create the item string $item = $object.id+”(“+$object.displayName+”)” Write-Host “>>>>>>”$item #Output item as bullet to HTML Report $itemHTMLstring = ”
- “+$item+”
” $itemHTMLstring | Out-File -filepath $reportValuesFilePath -Append #$valuesFileOutput += [PSCustomObject] @{‘Category’ = $($IntuneManagementQueryCollection.DisplayName);’uri’ = $Query.Query; ‘displayName’ = $object.displayName; ‘count’ = ($object| Measure-Object).Count }; } #End the bulleted list of items $endHTMLList = ‘
‘ $endHTMLList | Out-File -filepath $reportValuesFilePath -Append }else{ #create the sub category string and count the items within it $countOnItem = $Query.DisplayName+”: 0 settings configured.” Write-Host “>>>”$countOnItem #Output Category Heading to HTML Report $countOnItemHTMLstring = ”
“+$countOnItem+”
” $countOnItemHTMLstring | Out-File -filepath $reportValuesFilePath -Append #Create a bullet underneat showing we found no settings $itemHTMLString = ‘
- You have not configured this setting in your intune tenant.
‘ $itemHTMLstring | Out-File -filepath $reportValuesFilePath -Append } } } #Export the array of values to the restore values CSV Write-Host “Creating report values file $reportValuesFilePath…” #Create head of HTML Report $reportHTMLFoot | Out-File -filepath $reportValuesFilePath -Append Write-Host “The report process is complete. Please view your report $reportValuesFile” break } ‘2’ {Write-Host “Choose a folder to place your Intune Tenant report. This feature will report” Write-Host “all of your applied Intune Tenant settings in HTML format.” Write-Host “” Write-Host “Returning to the main menu in 20 seconds…” sleep 20 Get-IntuneManagementToolMenu } ‘3’ {Get-IntuneManagementToolMenu} } } #Backup/Restore Functions – General function Export-IntuneTenant{ <# .SYNOPSIS This function provides the main menu that backs up the intune tenant to file. .Author David Maiolo .NOTES #> param( [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0)] $IntuneManagementQueryCollections ) #process the backup menu clear Write-Host “=========================================” Write-Host “Backup Your Entire Intune Tenant ” Write-Host “=========================================” Write-Host “[1] Backup Your Entire Intune Tenant” Write-Host “[2] Instructions” Write-Host “[3] Return To Main Menu” -ForegroundColor Yellow do { try {$numOk = $true;[int]$ans = Read-host “Enter Selection”} catch {$numOK = $false} } # end do until (($ans -ge 1 -and $ans -le 3) -and $numOK) switch ($ans) { ‘1’ { Write-Host “Please choose a folder to start the backup process…” $backupfolderroot = Get-Folder if(!(Test-Path $backupfolderroot)){Write-Host “You chose an improper folder or the folder could not be accessed. Cannot continue.” -ForegroundColor Red;break} Write-Host “Starting Intune Tenant Backup Process to $backupfolderroot…” sleep 5 foreach ($IntuneManagementQueryCollection in $IntuneManagementQueryCollections){ #Set component backup folder based on the queries main display name (as seen in the programs main menu) $subfolder = ($IntuneManagementQueryCollection.DisplayName) #Clean Up The Sub Folder Name by removing spaces, etc. $pattern = ‘[^a-zA-Z]’ $subfolder = $subfolder -replace $pattern $backupfolder = Join-Path $backupfolderroot $subfolder #Setup A CSV Values File That Can Be Used for a Restore $backupValuesFile = $subfolder+”_tenantRestoreFile(” + $(get-date -f dd-MM-yyyy-H-mm-ss) + “).csv” $backupValuesFilePath = Join-Path $backupfolderroot $backupValuesFile #Create an array to hold the temp values that will go into the CSV Values file $valuesFileOutput = @() #Create The Backup Subfolder if it does not exist already if(!(Test-Path $backupfolder)){ try{ New-Item -ItemType Directory -Force -Path $backupfolder }catch{ Write-Host “Error: Could not create $backupfolder. You probably don’t have rights” -ForegroundColor Red } } $DisplayName = $($IntuneManagementQueryCollection.DisplayName) $Querys = Invoke-Expression $($IntuneManagementQueryCollection.Querys) Write-Host “Backing up $($IntuneManagementQueryCollection.Querys) ($($IntuneManagementQueryCollection.DisplayName))…” -ForegroundColor Cyan Write-Host “==============================================================” -ForegroundColor Cyan Write-Host “Sub Folder: $backupfolder” -ForegroundColor Cyan foreach ($Query in $Querys){ #Create the JSON Objects by running a REST query to Microsft Graph $objects = Get-MicrosoftGraphRESTRequest -uri $Query.Query if ($objects -ne $null -and $objects -ne $false){ #Add values to the temp array for CSV Values file foreach ($object in $objects){ $filename = $object.id+”_(” + $(get-date -f dd-MM-yyyy-H-mm-ss) + “).json” $valuesFileOutput += [PSCustomObject] @{‘Category’ = $subfolder; ‘backupDir’ = $backupfolder; ‘backupFile’ = $filename; ‘uri’ = $Query.Query; ‘displayName’ = $object.displayName; }; } #Export each JSON object to a file Export-ObjectAsJson -automateAnswers -objects $objects -backupfolder “$backupfolder” }else{ Write-Host “Skipping due to no existance in your tenant:”$Query.DisplayName -ForegroundColor Yellow } } #Export the array of values to the restore values CSV Write-Host “Creating restore values file $backupValuesFilePath…” $valuesFileOutput | Export-CSv -Path $backupValuesFilePath -NoTypeInformation } Write-Host “The backup process is complete.” break } ‘2’ {Write-Host “Choose a folder to place your Intune Tenant Backup. This feature will backup” Write-Host “all of your Intune Tenant settings in JSON format and will create special CSV” Write-Host “restore files so that they can be restored with this application. Note: As is” Write-Host “the nature with REST GET/POST operations, some of the JSON files may need to be” Write-Host “modified by removing extra properties before they can be restored succesfully.” Write-Host “” Write-Host “Returning to the main menu in 20 seconds…” sleep 20 Get-IntuneManagementToolMenu } ‘3’ {Get-IntuneManagementToolMenu} } } function Import-IntuneTenant{ <# .SYNOPSIS This function provides the main menu that restores the intune tenant from file. .Author David Maiolo .NOTES #> #process the Main application Menu clear Write-Host “=========================================” Write-Host “Restore Your Intune Tenant” Write-Host “=========================================” Write-Host “[1] Restore From a CSV Restore Tenant File Created By This Program” Write-Host “[2] Restore an Individual Setting (JSON File)” Write-Host “[3] Instructions” Write-Host “[4] Return To Main Menu” -ForegroundColor Yellow do { try {$numOk = $true;[int]$ans = Read-host “Enter Selection”} catch {$numOK = $false} } # end do until (($ans -ge 1 -and $ans -le 4) -and $numOK) switch ($ans) { ‘1’ { #Open the file select dialog box to select restore csv Write-Host “Select Your CSV Restore File…” $OpenChooser = New-Object System.Windows.Forms.OpenFileDialog $OpenChooser.initialDirectory = $initialDirectory $OpenChooser.filter = “CSV Restore files (*.csv)| *.csv” $OpenChooser.ShowDialog() | Out-Null #Test That We Selected an Existant File try{Test-Path -path ($OpenChooser.filename)}catch{Write-Host “Could Not Access Selected File. Must Quit” -ForegroundColor Red;Break} $valuesFileInputs = Import-Csv -LiteralPath $OpenChooser.filename Write-Host “Select the folder containing the JSON files for this restore (originally “($valuesFileInputs[0].backupDir)”)…” $restorefolder = Get-Folder -initialDirectory ($valuesFileInputs[0].backupDir) #iterate through each of the lines in the file foreach($valuesFileInput in $valuesFileInputs){ #Get line x values from the CSV Restore file to use in restore logic $Category = $valuesFileInput.Category $backupDir = $valuesFileInput.backupDir $backupFile = $valuesFileInput.backupFile $uri = $valuesFileInput.uri $displayName = $valuesFileInput.displayName #Import the JSON File as raw JSON data $JSONFilePath = Join-Path $restorefolder $backupFile $JSONObject = Import-ObjectAsJson -JSONFilePath $JSONFilePath -automateAnswers #Post the JSON data to Microsoft Graph if ($JSONObject -ne $null -and $JSONObject -ne $false){ New-MicrosoftGraphJSONPost -uri $uri -JSONObject $JSONObject }else{ Write-Host “A null or false JSON object was skipped.” -ForegroundColor Yellow } } Write-Host “Tenant Restore Complete.” break } ‘2’ { #Import the JSON File as raw JSON data try{$JSONObject = Import-ObjectAsJson}catch{Write-Host “Invalid File Selected.” -ForegroundColor Red;break} $uri = Read-Host “Enter the full URI this JSON will be restored to (ex. https://graph.microsoft.com/beta/deviceManagement/conditionalAccessSettings)” #Post the JSON data to Microsoft Graph New-MicrosoftGraphJSONPost -uri $uri -JSONObject $JSONObject } ‘3’ { Write-Host “Before Many of the JSON files can be restored, they may need to be modified by removing extra properties” Write-Host “before they will post properly. For example, a mobile application must have the following properties only ” Write-Host “to post properly:” $Browser = @” { “@odata.type”: “#microsoft.graph.androidStoreApp”, “displayName”: “Intune Managed Browser”, “description”: “Intune Managed Browser”, “publisher”: “Microsoft Corporation”, “isFeatured”: true, “appStoreUrl”: “https://play.google.com/store/apps/details?id=com.microsoft.intune.mam.managedbrowser&hl=en”, “minimumSupportedOperatingSystem”: { “@odata.type”: “#microsoft.graph.androidMinimumOperatingSystem”, “v4_0”: true } “@ $Browser Write-Host “” Write-Host “Returning to the main menu in 20 seconds…” sleep 20 Get-IntuneManagementToolMenu } ‘4’ {Get-IntuneManagementToolMenu} Default {} } } function Export-ObjectAsJson{ <# .SYNOPSIS This function exports a PowrShell Obkect to a JSON formatted file (used as part of the backup procedure). .Author David Maiolo .NOTES #> param( [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0)] $objects, [Parameter(Mandatory=$false,ValueFromPipeline=$true,Position=1)] [string]$backupfolder, [Parameter(Mandatory=$false,ValueFromPipeline=$true,Position=2)] [switch]$automateAnswers ) if($automateAnswers -and $backupfolder){ foreach ($object in $objects){ $filename = $object.id+”_(” + $(get-date -f dd-MM-yyyy-H-mm-ss) + “).json” #$filename = $object.id+ “.json” $backupfilepath = Join-Path “$backupfolder” “$filename” Write-Host “Backing Up $filename…” $JsonObject = $object | ConvertTo-Json $JsonObject | Out-File $backupfilepath } } else{ foreach ($object in $objects){ Write-Host “Manually choosing path…” $JsonObject = $object | ConvertTo-Json $SaveChooser = New-Object -TypeName System.Windows.Forms.SaveFileDialog $SaveChooser.filter = “JSON files (*.json)| *.json” $SaveChooser.filename = $object.DisplayName+”_” + $(get-date -f dd-MM-yyyy-H-mm-ss) + “.json” $SaveChooser.ShowDialog() $JsonObject | Out-File $SaveChooser.Filename } } } function Import-ObjectAsJson{ <# .SYNOPSIS This function imports a JSON formatted file as raw data (used as part of the restore procedure). .Author David Maiolo .NOTES #> param( [Parameter(Mandatory=$false,ValueFromPipeline=$true,Position=0)] [string]$JSONFilePath, [Parameter(Mandatory=$false,ValueFromPipeline=$false,Position=1)] [switch]$automateAnswers ) try{ if($JSONFilePath -and $automateAnswers){ $JSONData = Get-Content -Raw -Path $JSONFilePath }else{ $OpenChooser = New-Object System.Windows.Forms.OpenFileDialog $OpenChooser.initialDirectory = $initialDirectory $OpenChooser.filter = “JSON files (*.json)| *.json” $OpenChooser.ShowDialog() | Out-Null $JSONData = Get-Content -Raw -Path $OpenChooser.filename } }catch{ Throw “Could Not Import Object as JSON” } return $JSONData } function New-MicrosoftGraphJSONPost{ <# .SYNOPSIS This function is used to post to your Microsoft Graph request of choice. .Author David Maiolo .NOTES NAME: New-MicrosoftGraphJSONPost #> param( [Parameter(Mandatory=$true,ValueFromPipeline=$false,Position=0)] [string]$uri, [Parameter(Mandatory=$true,ValueFromPipeline=$false,Position=1)] [ValidateScript({ConvertFrom-Json $_ -ErrorAction Stop})] $JSONObject ) try{ Write-Host “Posting “($JSONObject | ConvertFrom-Json).id” to $uri…” Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSONObject -ContentType “application/json” Write-Host “Succesfully Posted JSON” -ForegroundColor Green }catch{ <#$ex = $_.Exception $errorResponse = $ex.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($errorResponse) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $responseBody = $reader.ReadToEnd(); Write-Host “Response content:`n$responseBody” -ForegroundColor Red Write-Error “Post to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)” write-host #> Write-Host “Could not post JSON. You may need to modify the JSON to make it postbale.” -ForegroundColor Red return $false } } Function Get-Folder{ <# .SYNOPSIS This function will show the select folder GUI dialog box to the user. .Author David Maiolo .NOTES #> param( [string]$initialDirectory ) [System.Reflection.Assembly]::LoadWithPartialName(“System.windows.forms”) | Out-Null $foldername = New-Object System.Windows.Forms.FolderBrowserDialog if ($initialDirectory){ $foldername.SelectedPath = $initialDirectory }else{ $foldername.rootfolder = “UserProfile” } $result = $foldername.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost = $true })) if ($result -eq [Windows.Forms.DialogResult]::OK){ $folder += $foldername.SelectedPath } else { return $false } return $folder } #Menu Functions function Get-MainMenu { <# .SYNOPSIS This function is used to provide a main compment menu for the application. .Author David Maiolo .NOTES NAME: Get-IntuneMainMenu #> Param ( [Parameter(Mandatory=$true,ValueFromPipeline=$false,Position=0)] $Querys, [Parameter(Mandatory=$true,ValueFromPipeline=$false,Position=1)] [String]$QueryName, [Parameter(Mandatory=$false,ValueFromPipeline=$false,Position=2)] [Switch]$UseSubMenu ) #if (!($global:intuneWarehouseAuthResult) -or !($global:authToken)) { # throw “No authentication context. Authenticate first!” #} try { do { clear Write-Host “=========================================” Write-Host “$QueryName ” Write-Host “=========================================” $menu = @{} $i=0 foreach ($Query in $Querys) { $i++ Write-Host “[$i] $($Query.DisplayName)” $menu.Add($i,($Query.DisplayName)) if($Query -eq $Querys[-1]){ $iplusone = ($i+1) Write-Host “[$iplusone] Return To Main Menu” -ForegroundColor Yellow $menu.Add($iplusone,(“Exit”)) } } #Validate selection input is proper number range do { try {$numOk = $true;[int]$ans = Read-host “Enter Selection”} catch {$numOK = $false} } # end do until (($ans -ge 1 -and $ans -le $iplusone) -and $numOK) $selection = $menu.Item($ans); if ($selection -eq “Exit”){ Write-Host “Returning to Main Menu…” sleep -Milliseconds 300 Get-IntuneManagementToolMenu }else{ Write-Host “You Chose To Continue” Get-SubMenu -Query $Querys[$ans-1].Query -QueryName $Querys[$ans-1].DisplayName sleep -Milliseconds 300 } }while($ans -ne ($Querys.count+2)) }catch{ Write-Error “There was a problem rendering the menu.” break } } function Get-SubMenu { <# .SYNOPSIS This function is used to provide a sub menu for the application. This was written generically and can be used for other applications. .Author David Maiolo .NOTES NAME: Get-SubMenu #> Param ( [Parameter(Mandatory=$true,ValueFromPipeline=$false,Position=0)] $Query, [Parameter(Mandatory=$true,ValueFromPipeline=$false,Position=1)] [String]$QueryName ) try { do{ clear Write-Host “$QueryName ” Write-Host “=========================================” $QueryResults = Get-MicrosoftGraphRESTRequest -uri $Query $menu = @{} if($QueryResults -eq $null -or $QueryResults -eq $false){ Write-Host “There Are No $QueryName Configured In Your Intune Tenant!” -ForegroundColor Yellow Write-Host “[1] Go Back” $menu.Add(1,(“GoBack”)) }else{ $i=0 foreach ($QueryResult in $QueryResults) { $i++ if($QueryResult.displayName){ $DisplayName = ($QueryResult.displayName) }elseif($QueryResult.id){ $DisplayName = ($QueryResult.id) }else{ $DisplayName = ‘Unknown’ } if($DisplayName -ne $null) {$DisplayName = $DisplayName.ToString()} if($QueryResult.description){ $Description = $QueryResult.description $Description = $Description -replace “`n”,”, ” -replace “`r”,”, ” $Description = ($Description.ToCharArray() | select -first 75) -join “” Write-Host ” [$i] $($DisplayName) ($Description)” }else{ Write-Host ” [$i] $($DisplayName)” } $menu.Add($i,($QueryResult)) if($QueryResult -eq $QueryResults[-1]){ $iplusone = ($i+1) Write-Host ” [$iplusone] Go Back” -ForegroundColor Yellow $menu.Add($iplusone,(“GoBack”)) } } } #Validate selection input is proper number range do { try {$numOk = $true;[int]$ans = Read-host “Enter Selection”} catch {$numOK = $false} } # end do until (($ans -ge 1 -and $ans -le $iplusone) -and $numOK) $selection = $menu.Item($ans); if ($selection -eq “GoBack”){ Write-Host “Returning…” sleep -Milliseconds 300 return }else{ Write-Host “Rending in Out-Gridview…” Sleep -Milliseconds 300 $selection | Out-GridView } }while($selection -ne “GoBack”) }catch{ Write-Error “There was a problem rendering the menu.” break } } #Intune Queries function Get-MicrosoftGraphIntuneDeviceEnrollmentQueries{ <# .SYNOPSIS This function contains all of the URI requests for Device Enrollment. .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneDeviceEnrollmentQueries = @() $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/termsAndConditions’; ‘DisplayName’ = ‘Terms and Conditions’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/androidForWorkSettings’; ‘DisplayName’ = ‘Android For Work Settings’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/androidForWorkAppConfigurationSchemas’; ‘DisplayName’ = ‘Android For Work App Configuration Schemas’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/androidForWorkEnrollmentProfiles’; ‘DisplayName’ = ‘Android For Work Enrollment Profiles’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/enrollmentProfiles’; ‘DisplayName’ = ‘Enrollment Profiles’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/importedDeviceIdentities’; ‘DisplayName’ = ‘Imported Device Identities’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/importedAppleDeviceIdentities’; ‘DisplayName’ = ‘Imported Apple Device Identities’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/remoteActionAudits’; ‘DisplayName’ = ‘Remote Action Audits’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/applePushNotificationCertificate’; ‘DisplayName’ = ‘Apple Push Notification Certificate’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/deviceCategories’; ‘DisplayName’ = ‘Device Categories’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotSettings’; ‘DisplayName’ = ‘Windows Autopilot Settings’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities’; ‘DisplayName’ = ‘Windows Autopilot Device Identities’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles’; ‘DisplayName’ = ‘Windows Autopilot Deployment Profiles’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations’; ‘DisplayName’ = ‘Device Enrollment Configurations’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings’; ‘DisplayName’ = ‘DEP Onboarding Settings’}; $MicrosoftGraphIntuneDeviceEnrollmentQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/ndesConnectors’; ‘DisplayName’ = ‘NDES Connectors’}; return $MicrosoftGraphIntuneDeviceEnrollmentQueries } function Get-MicrosoftGraphIntuneDeviceComplianceQueries{ <# .SYNOPSIS This function contains all of the URI requests for Device Compliance. .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneDeviceComplianceQueries = @() $MicrosoftGraphIntuneDeviceComplianceQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies’; ‘DisplayName’ = ‘Device Compliance Policies’}; $MicrosoftGraphIntuneDeviceComplianceQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicyDeviceStateSummary’; ‘DisplayName’ = ‘Device Compliance Policy Device State Summary’}; $MicrosoftGraphIntuneDeviceComplianceQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicySettingStateSummaries’; ‘DisplayName’ = ‘Device Compliance Policy Setting State Summaries’}; $MicrosoftGraphIntuneDeviceComplianceQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/auditEvents’; ‘DisplayName’ = ‘Audit Events’}; $MicrosoftGraphIntuneDeviceComplianceQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/mobileThreatDefenseConnectors’; ‘DisplayName’ = ‘Mobile Threat Defense Connectors’}; $MicrosoftGraphIntuneDeviceComplianceQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/deviceManagementPartners’; ‘DisplayName’ = ‘Device Management Partners’}; $MicrosoftGraphIntuneDeviceComplianceQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/remoteAssistancePartners’; ‘DisplayName’ = ‘Remote Assistance Partners’}; $MicrosoftGraphIntuneDeviceComplianceQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/notificationMessageTemplates’; ‘DisplayName’ = ‘Notification Message Templates’}; return $MicrosoftGraphIntuneDeviceComplianceQueries } function Get-MicrosoftGraphIntuneDeviceConfigurationQueries{ <# .SYNOPSIS This function contains all of the URI requests for Device Configurations. .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneDeviceConfigurationQueries = @() $MicrosoftGraphIntuneDeviceConfigurationQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations’; ‘DisplayName’ = ‘Device Configurations’}; $MicrosoftGraphIntuneDeviceConfigurationQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts’; ‘DisplayName’ = ‘Device Management Scripts’}; $MicrosoftGraphIntuneDeviceConfigurationQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/telecomExpenseManagementPartners’; ‘DisplayName’ = ‘Telecom Expense Management Partners’}; $MicrosoftGraphIntuneDeviceConfigurationQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/deviceSetupConfigurations’; ‘DisplayName’ = ‘Device Setup Configurations’}; $MicrosoftGraphIntuneDeviceConfigurationQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/deviceConfigurationDeviceStateSummaries’; ‘DisplayName’ = ‘Device Configuration Device State Summaries’}; return $MicrosoftGraphIntuneDeviceConfigurationQueries } function Get-MicrosoftGraphIntuneDeviceQueries{ <# .SYNOPSIS This function contains all of the URI requests for Devices. .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneDeviceQueries = @() $MicrosoftGraphIntuneDeviceQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/managedDeviceOverview’; ‘DisplayName’ = ‘Managed Device Overview’}; $MicrosoftGraphIntuneDeviceQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/managedDevices’; ‘DisplayName’ = ‘Managed Devices’}; return $MicrosoftGraphIntuneDeviceQueries } function Get-MicrosoftGraphIntuneAppQueries{ <# .SYNOPSIS This function contains all of the URI requests for App Queries. It is currently not used. .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneAppQueries = @() $MicrosoftGraphIntuneAppQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/detectedApps’; ‘DisplayName’ = ‘Detected Apps’}; $MicrosoftGraphIntuneAppQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/windowsInformationProtectionAppLearningSummaries’; ‘DisplayName’ = ‘Windows Information Protection App Learning Summaries’}; return $MicrosoftGraphIntuneAppQueries } function Get-MicrosoftGraphIntuneEbookQueries{ <# .SYNOPSIS This function contains all of the URI requests for eBooks. .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneEbookQueries = @() $MicrosoftGraphIntuneEbookQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/managedEBooks’; ‘DisplayName’ = ‘Managed eBooks’}; return $MicrosoftGraphIntuneEbookQueries } function Get-MicrosoftGraphIntuneConditionalAccessQueries{ <# .SYNOPSIS This function contains all of the URI requests for Conditional Access. .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneConditionalAccessQueries = @() $MicrosoftGraphIntuneConditionalAccessQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/conditionalAccessSettings’; ‘DisplayName’ = ‘Conditional Access Settings’}; $MicrosoftGraphIntuneConditionalAccessQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/exchangeConnectors’; ‘DisplayName’ = ‘Exchange Connectors’}; return $MicrosoftGraphIntuneConditionalAccessQueries } function Get-MicrosoftGraphIntuneOnPremisesQueries{ <# .SYNOPSIS This function contains all of the URI requests for On Premises Access. .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneOnPremisesQueries = @() $MicrosoftGraphIntuneOnPremisesQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/exchangeOnPremisesPolicy’; ‘DisplayName’ = ‘Exchange On Premises Policy’}; $MicrosoftGraphIntuneOnPremisesQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/exchangeOnPremisesPolicies’; ‘DisplayName’ = ‘Exchange On Premises Policies’}; return $MicrosoftGraphIntuneOnPremisesQueries } function Get-MicrosoftGraphIntuneRolesQueries{ <# .SYNOPSIS This function contains all of the URI requests for Intune Roles (RBAC). .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneRolesQueries = @() $MicrosoftGraphIntuneRolesQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/roleDefinitions’; ‘DisplayName’ = ‘Role Definitions’}; $MicrosoftGraphIntuneRolesQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/roleAssignments’; ‘DisplayName’ = ‘Role Assignments’}; return $MicrosoftGraphIntuneRolesQueries } function Get-MicrosoftGraphIntuneSoftwareUpdatesQueries{ <# .SYNOPSIS This function contains all of the URI requests for Software Updates. .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneSoftwareUpdatesQueries = @() $MicrosoftGraphIntuneSoftwareUpdatesQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/softwareUpdateStatusSummary’; ‘DisplayName’ = ‘Software Update Status Summary’}; $MicrosoftGraphIntuneSoftwareUpdatesQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/iosUpdateStatuses’; ‘DisplayName’ = ‘iOS Update Statuses’}; return $MicrosoftGraphIntuneSoftwareUpdatesQueries } function Get-MicrosoftGraphIntuneTroubleshootingQueries{ <# .SYNOPSIS This function contains all of the URI requests for Troubleshooting. .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneTroubleshootingQueries = @() $MicrosoftGraphIntuneTroubleshootingQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/troubleshootingEvents’; ‘DisplayName’ = ‘Troubleshooting Events’}; return $MicrosoftGraphIntuneTroubleshootingQueries } function Get-MicrosoftGraphIntuneMiscQueries{ <# .SYNOPSIS This function contains all of the URI requests for Misc Items. It is currently not being used. .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneMiscQueries = @() $MicrosoftGraphIntuneMiscQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/windowsMalwareInformation’; ‘DisplayName’ = ‘Windows Malware Information’}; $MicrosoftGraphIntuneMiscQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/cartToClassAssociations’; ‘DisplayName’ = ‘Cart To Class Associations’}; $MicrosoftGraphIntuneMiscQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/resourceOperations’; ‘DisplayName’ = ‘Resource Operations’}; $MicrosoftGraphIntuneMiscQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/windowsInformationProtectionNetworkLearningSummaries’; ‘DisplayName’ = ‘Windows Information Protection Network Learning Summaries’}; return $MicrosoftGraphIntuneMiscQueries } function Get-MicrosoftGraphIntuneAppManagementQueries{ <# .SYNOPSIS This function contains all of the URI requests for MAM. .Author David Maiolo .NOTES #> $MicrosoftGraphIntuneAppManagementQueries = @() $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/mobileApps’; ‘DisplayName’ = ‘Mobile Apps’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/mobileAppCategories’; ‘DisplayName’ = ‘Mobile App Categories’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/windowsManagementApp’; ‘DisplayName’ = ‘Windows Management App’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/enterpriseCodeSigningCertificates’; ‘DisplayName’ = ‘Enterprise Code Signing Certificates’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/iosLobAppProvisioningConfigurations’; ‘DisplayName’ = ‘iOS Lob App Provisioning Configurations’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/symantecCodeSigningCertificate’; ‘DisplayName’ = ‘Symantec CodeSigning Certificate’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/mobileAppConfigurations’; ‘DisplayName’ = ‘Mobile App Configurations’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/sideLoadingKeys’; ‘DisplayName’ = ‘Side Loading Keys’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/vppTokens’; ‘DisplayName’ = ‘VPP Tokens’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/managedAppPolicies’; ‘DisplayName’ = ‘Managed App Policies’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/iosManagedAppProtections’; ‘DisplayName’ = ‘iOS Managed App Protections’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/androidManagedAppProtections’; ‘DisplayName’ = ‘Android Managed App Protections’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/defaultManagedAppProtections’; ‘DisplayName’ = ‘Default Managed App Protections’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/targetedManagedAppConfigurations’; ‘DisplayName’ = ‘Targeted Managed App Configurations’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/mdmWindowsInformationProtectionPolicies’; ‘DisplayName’ = ‘MDM Windows Information Protection Policies’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/windowsInformationProtectionPolicies’; ‘DisplayName’ = ‘Windows Information Protection Policies’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/managedAppRegistrations’; ‘DisplayName’ = ‘Managed App Registrations’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceAppManagement/managedAppStatuses’; ‘DisplayName’ = ‘Managed App Statuses’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/detectedApps’; ‘DisplayName’ = ‘Detected Apps’}; $MicrosoftGraphIntuneAppManagementQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/beta/deviceManagement/windowsInformationProtectionAppLearningSummaries’; ‘DisplayName’ = ‘Windows Information Protection App Learning Summaries’}; return $MicrosoftGraphIntuneAppManagementQueries } function Get-MicrosoftGraphAzureUsersQueries{ <# .SYNOPSIS This function contains all of the URI requests for Azure Users. .Author David Maiolo .NOTES #> $MicrosoftGraphAzureUsersQueries = @() $MicrosoftGraphAzureUsersQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/v1.0/users’; ‘DisplayName’ = ‘Azure AD Users’}; return $MicrosoftGraphAzureUsersQueries } function Get-MicrosoftGraphAzureGroupsQueries{ <# .SYNOPSIS This function contains all of the URI requests for Azure Groups. .Author David Maiolo .NOTES #> $MicrosoftGraphAzureGroupsQueries = @() $MicrosoftGraphAzureGroupsQueries += [PSCustomObject] @{‘Query’ = ‘https://graph.microsoft.com/v1.0/groups’; ‘DisplayName’ = ‘Azure AD Groups’}; return $MicrosoftGraphAzureGroupsQueries } #Authentication Functions function Get-AuthToken { <# .SYNOPSIS This function returns an Auth Token used for the REST request. .Author Microsoft with small modifications by David Maiolo .NOTES #> [cmdletbinding()] param ( [Parameter(Mandatory=$true)] $User ) $userUpn = New-Object “System.Net.Mail.MailAddress” -ArgumentList $User $tenant = $userUpn.Host Write-Host “Checking for AzureAD module…” $AadModule = Get-Module -Name “AzureAD” -ListAvailable if ($AadModule -eq $null) { Write-Host “AzureAD PowerShell module not found, looking for AzureADPreview” $AadModule = Get-Module -Name “AzureADPreview” -ListAvailable } if ($AadModule -eq $null) { write-host write-host “AzureAD Powershell module not installed…” -f Red write-host “Install by running ‘Install-Module AzureAD’ or ‘Install-Module AzureADPreview’ from an elevated PowerShell prompt” -f Yellow write-host “Script can’t continue…” -f Red write-host exit } # Getting path to ActiveDirectory Assemblies # If the module count is greater than 1 find the latest version if($AadModule.count -gt 1){ $Latest_Version = ($AadModule | select version | Sort-Object)[-1] $aadModule = $AadModule | ? { $_.version -eq $Latest_Version.version } # Checking if there are multiple versions of the same module found if($AadModule.count -gt 1){ $aadModule = $AadModule | select -Unique } $adal = Join-Path $AadModule.ModuleBase “Microsoft.IdentityModel.Clients.ActiveDirectory.dll” $adalforms = Join-Path $AadModule.ModuleBase “Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll” } else { $adal = Join-Path $AadModule.ModuleBase “Microsoft.IdentityModel.Clients.ActiveDirectory.dll” $adalforms = Join-Path $AadModule.ModuleBase “Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll” } [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null [System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null $clientId = “d1ddf0e4-d672-4dae-b554-9d5bdfd93547” $redirectUri = “urn:ietf:wg:oauth:2.0:oob” $resourceAppIdURI = “https://graph.microsoft.com” $authority = “https://login.microsoftonline.com/$Tenant” try { $authContext = New-Object “Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext” -ArgumentList $authority # https://msdn.microsoft.com/en-us/library/azure/microsoft.identitymodel.clients.activedirectory.promptbehavior.aspx # Change the prompt behaviour to force credentials each time: Auto, Always, Never, RefreshSession $platformParameters = New-Object “Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters” -ArgumentList “Auto” $userId = New-Object “Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier” -ArgumentList ($User, “OptionalDisplayableId”) $authResult = $authContext.AcquireTokenAsync($resourceAppIdURI,$clientId,$redirectUri,$platformParameters,$userId).Result # If the accesstoken is valid then create the authentication header if($authResult.AccessToken){ # Creating header for Authorization token $authHeader = @{ ‘Content-Type’=’application/json’ ‘Authorization’=”Bearer ” + $authResult.AccessToken ‘ExpiresOn’=$authResult.ExpiresOn } return $authHeader } else { Write-Host Write-Host “Authorization Access Token is null, please re-run application and asure you are conneted to the internet.” -ForegroundColor Red Write-Host break } } catch { write-host $_.Exception.Message -f Red write-host $_.Exception.ItemName -f Red write-host break } }