Intune Management Tool

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 http://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 http://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><html lang="en"><head><meta charset="utf-8"><title>Intune Tenant Report</title></head><body><center><h1>Intune Tenant Report</h1></center><Center>Created with '+$global:ApplicationName+' '+$global:ApplicationVersion+'(by '+$global:ApplicationAuthor+')'+'</center>'
                $reportHTMLFoot = '</body></html>'

                #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 = "<h2>"+$Category+"</h2>"
                    $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 = "<h3>"+$countOnItem+"</h3>"
                            $countOnItemHTMLstring | Out-File -filepath $reportValuesFilePath -Append

                            #Start a bulleted list
                            $startHTMLList = '<ul style="list-style-type:square">'
                            $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 = "<li>"+$item+"</li>"
                                $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 = '</ul>'
                            $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 = "<h3>"+$countOnItem+"</h3>"
                            $countOnItemHTMLstring | Out-File -filepath $reportValuesFilePath -Append

                            #Create a bullet underneat showing we found no settings
                            $itemHTMLString = '<ul style="list-style-type:square"><li>You have not configured this setting in your intune tenant.</li></ul>'
                            $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

    }

}

Leave a Comment

Your email address will not be published.