public/AzureActiveDirectory/Register-AADReport.ps1

<# Customer infos#>
function Get-OrganizationReport {
    $Organization = Get-MgOrganization -Property DisplayName, Id
    $script:CustomerName = $Organization.DisplayName
    return  "<h2>$($Organization.DisplayName) ($($Organization.Id))</h2>"
}

<# User settings policy section #>
function Get-UserSettingsReport {
    param(
        [System.Boolean]$DisableUserConsent,
        [System.Boolean]$DisableUsersToCreateAppRegistrations,
        [System.Boolean]$DisableUsersToReadOtherUsers,
        [System.Boolean]$DisableUsersToCreateSecurityGroups,
        [System.Boolean]$DisableUsersToCreateUnifiedGroups,
        [System.Boolean]$CreateUnifiedGroupCreationAllowedGroup,
        [System.Boolean]$EnableBlockMsolPowerShell
    )
    if ($DisableUserConsent) { Disable-ApplicationUserConsent }
    if ($DisableUsersToCreateAppRegistrations) { Disable-UsersToCreateAppRegistrations }
    if ($DisableUsersToReadOtherUsers) { Disable-UsersToReadOtherUsers }
    if ($DisableUsersToCreateSecurityGroups) { Disable-UsersToCreateSecurityGroups }
    if ($DisableUsersToCreateUnifiedGroups) { Disable-UsersToCreateUnifiedGroups }
    if ($CreateUnifiedGroupCreationAllowedGroup) { New-UnifiedGroupCreationAllowedGroup }
    if ($EnableBlockMsolPowerShell) { Enable-BlockMsolPowerShell }
    Write-Host "Checking user settings"
    $Policy = Get-MgPolicyAuthorizationPolicy -Property BlockMsolPowerShell, DefaultUserRolePermissions
    $Report = $Policy | Select-Object -Property @{Name = "PermissionGrantPoliciesAssigned"; Expression = { [string]$_.DefaultUserRolePermissions.PermissionGrantPoliciesAssigned } },
    @{Name = "AllowedToCreateApps"; Expression = { [string]$_.DefaultUserRolePermissions.AllowedToCreateApps } },
    @{Name = "AllowedToCreateSecurityGroups"; Expression = { [string]$_.DefaultUserRolePermissions.AllowedToCreateSecurityGroups } },
    @{Name = "AllowedToCreateUnifiedGroups"; Expression = { Request-AllowedToCreateUnifiedGroups } },
    @{Name = "AllowedToCreateUnifiedGroupsGroupName"; Expression = { Request-UnifiedGroupCreationAllowedGroup } },
    @{Name = "AllowedToReadOtherUsers"; Expression = { [string]$_.DefaultUserRolePermissions.AllowedToReadOtherUsers } },
    @{Name = "AllowedToCreateTenants"; Expression = { Request-AllowedToCreateTenants } },
    BlockMsolPowerShell | ConvertTo-Html -As List -Fragment -PreContent "<h3 id='AAD_USER_SETTINGS'>User settings</h3>" -PostContent "<p>PermissionGrantPoliciesAssigned: empty (user consent not allowed), microsoft-user-default-legacy (user consent allowed for all apps), microsoft-user-default-low (user consent allowed for low permission apps)</p><p>Unified groups = Microsoft 365 groups</p><p>AllowedToReadOtherUsers: Should only be disabled if you do not use Microsoft Planner</p><p>BlockMsolPowerShell: Should only be disabled if you do not use Azure AD Connect Sync</p>"

    $Report = $Report -Replace "<td>PermissionGrantPoliciesAssigned:</td><td>ManagePermissionGrantsForSelf.microsoft-user-default-legacy</td>", "<td>PermissionGrantPoliciesAssigned:</td><td class='red'>microsoft-user-default-legacy</td>"
    $Report = $Report -Replace "<td>PermissionGrantPoliciesAssigned:</td><td>ManagePermissionGrantsForSelf.microsoft-user-default-low</td>", "<td>PermissionGrantPoliciesAssigned:</td><td class='orange'>microsoft-user-default-low</td>"
    $Report = $Report -Replace "<td>AllowedToCreateApps:</td><td>True</td>", "<td>AllowedToCreateApps:</td><td class='red'>True</td>"
    $Report = $Report -Replace "<td>AllowedToCreateSecurityGroups:</td><td>True</td>", "<td>AllowedToCreateSecurityGroups:</td><td class='red'>True</td>"
    $Report = $Report -Replace "<td>AllowedToCreateUnifiedGroups:</td><td>True</td>", "<td>AllowedToCreateUnifiedGroups:</td><td class='red'>True</td>"
    $Report = $Report -Replace "<td>AllowedToCreateUnifiedGroups:</td><td>false</td>", "<td>AllowedToCreateUnifiedGroups:</td><td>False</td>"
    $Report = $Report -Replace "<td>AllowedToReadOtherUsers:</td><td>True</td>", "<td>AllowedToReadOtherUsers:</td><td class='orange'>True</td>"
    $Report = $Report -Replace "<td>AllowedToCreateTenants:</td><td>True</td>", "<td>AllowedToCreateTenants:</td><td class='red'>True</td>"
    $Report = $Report -Replace "<td>BlockMsolPowerShell:</td><td>False</td>", "<td>BlockMsolPowerShell:</td><td class='orange'>False</td>"
    return $Report
}
function Disable-ApplicationUserConsent {
    Write-Host "Disable enterprise application user consent"
    Update-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions @{ "PermissionGrantPoliciesAssigned" = @() }
}
function Disable-UsersToCreateAppRegistrations {
    Write-Host "Disable users to create app registrations"
    Update-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions @{ "AllowedToCreateApps" = $false }
}
function Disable-UsersToReadOtherUsers {
    Write-Host "Disable users to read other users"
    Update-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions @{ "AllowedToReadOtherUsers" = $false }
}
function Disable-UsersToCreateSecurityGroups {
    Write-Host "Disable users to create security groups"
    Update-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions @{ "AllowedToCreateSecurityGroups" = $false }
}
function Enable-BlockMsolPowerShell {
    Write-Host "Disable legacy MsolPowerShell access"
    Update-MgPolicyAuthorizationPolicy -BlockMsolPowerShell
}
function Request-AllowedToCreateUnifiedGroups {
    if ($GroupSettingsUnified = Get-GroupSettingsUnified) {
        return ($GroupSettingsUnified.values | Where-Object name -EQ "EnableGroupCreation").value
    }
    return $true
}
function Request-UnifiedGroupCreationAllowedGroup {
    $GroupSettingsUnified = Get-GroupSettingsUnified
    if ($GroupId = ($GroupSettingsUnified.values | Where-Object name -EQ "GroupCreationAllowedGroupId").value) {
        return (Get-MgGroup -GroupId $GroupId -Property DisplayName).DisplayName
    }
}
function New-UnifiedGroupCreationAllowedGroup {
    Write-Host "Creating UnifiedGroupCreationAllowed group:" $script:UnifiedGroupCreationAllowedGroupName
    if (Request-UnifiedGroupCreationAllowedGroup) {
        Write-Host "UnifiedGroupCreationAllowed group already assigned"
        return
    }
    if ($GroupId = (Get-MgGroup -Property Id, DisplayName -Filter "DisplayName eq '$($script:UnifiedGroupCreationAllowedGroupName)'").Id) { Write-Host "UnifiedGroupCreationAllowed group already exists" } else { $GroupId = (New-MgGroup -DisplayName $script:UnifiedGroupCreationAllowedGroupName -MailEnabled:$False -MailNickname $script:UnifiedGroupCreationAllowedGroupName -SecurityEnabled).Id }
    $Body = @{
        templateId = (Get-GroupSettingsTemplateUnified).id
        values     = @( @{ Name = "GroupCreationAllowedGroupId" ; Value = $GroupId } )
    }
    if ($GroupSettingsUnified = Get-GroupSettingsUnified) { Invoke-MgGraphRequest -Method PATCH -Uri "https://graph.microsoft.com/v1.0/groupSettings/$($GroupSettingsUnified.id)" -Body $Body }
    else { Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/v1.0/groupSettings" -Body $Body }
}
function Get-GroupSettingsTemplateUnified {
    $GroupSettingTemplates = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/groupSettingTemplates?$select=value"
    return $GroupSettingTemplates.value | Where-Object { $_.displayName -eq "Group.Unified" } | Select-Object -Property id, DisplayName
}
function Get-GroupSettingsUnified {
    $GroupSettings = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/groupSettings?$select=value" 
    return $GroupSettings.value | Where-Object { $_.templateId -eq (Get-GroupSettingsTemplateUnified).id } | Select-Object -Property id, templateId, values
}
function Disable-UsersToCreateUnifiedGroups {
    $Body = @{
        templateId = (Get-GroupSettingsTemplateUnified).id
        values     = @( @{ Name = "EnableGroupCreation" ; Value = "false" } )
    }
    if ($GroupSettingsUnified = Get-GroupSettingsUnified) { Invoke-MgGraphRequest -Method PATCH -Uri "https://graph.microsoft.com/v1.0/groupSettings/$($GroupSettingsUnified.id)" -Body $Body }
    else { Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/v1.0/groupSettings" -Body $Body }
}
function Request-AllowedToCreateTenants {
    $DefaultUserRolePermissions = (Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy").defaultUserRolePermissions
    return $DefaultUserRolePermissions["allowedToCreateTenants"]
}

<# Device join settings#>
function Get-DeviceJoinSettingsReport {
    Write-Host "Checking device join settings"
    $DeviceSettings = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy?$select=azureAdJoin, userDeviceQuota, multiFactorAuthConfiguration"
    $Report = $DeviceSettings | Select-Object -Property @{Name = "Require MFA"; Expression = { if ($_.multiFactorAuthConfiguration -eq 1) { return $true } return $false } },
    @{Name = "Allowed to join"; Expression = { if ($_.azureAdJoin.appliesTo -eq 1) { return "All users" } if ($_.azureAdJoin.appliesTo -eq 2) { return "Selected users" } return "No users" } },
    @{Name = "Users"; Expression = { $Users = @() ; foreach ($UserId in $_.azureAdJoin.allowedUsers) { $Users += (Get-MgUser -UserId $UserId -Property UserPrincipalName).UserPrincipalName } return $Users -join "; " } },
    @{Name = "Groups"; Expression = { $Groups = @() ; foreach ($GroupId in $_.azureAdJoin.allowedGroups) { $Groups += (Get-MgGroup -GroupId $GroupId -Property DisplayName).DisplayName } return $Groups -join "; " } },
    userDeviceQuota | ConvertTo-Html -As List -Fragment -PreContent "<br><h3 id='AAD_DEVICE_JOIN_SETTINGS'>Device join settings</h3>"
    $Report = $Report -Replace "<td>Require MFA:</td><td>False</td>", "<td>Require MFA:</td><td class='red'>False</td>"
    $Report = $Report -Replace "<td>Allowed to join:</td><td>All users</td>", "<td>Allowed to join:</td><td class='red'>All users</td>"
    return $Report
}

<# License SKU section#>
function Get-UsedSKUReport {
    Write-Host "Checking licenses"
    $SKU = Get-MgSubscribedSku -Property SkuPartNumber, ConsumedUnits, PrepaidUnits, AppliesTo
    return $SKU | Select-Object -Property @{Name = "Name"; Expression = { 
            if ($_.SkuPartNumber -eq "EXCHANGESTANDARD") { return "Exchange Online (Plan 1)" }
            if ($_.SkuPartNumber -eq "EXCHANGEENTERPRISE") { return "Exchange Online (PLAN 2)" }
            if ($_.SkuPartNumber -eq "EXCHANGEARCHIVE_ADDON") { return "Exchange Online Archiving for Exchange Online" }
            if ($_.SkuPartNumber -eq "EXCHANGE_S_ESSENTIALS") { return "Exchange Online Essentials" }

            if ($_.SkuPartNumber -eq "SHAREPOINTSTANDARD") { return "SharePoint Online (Plan 1)" }
            if ($_.SkuPartNumber -eq "SHAREPOINTENTERPRISE") { return "SharePoint Online (Plan 2)" }

            if ($_.SkuPartNumber -eq "AAD_BASIC") { return "Azure Active Directory Basic" }
            if ($_.SkuPartNumber -eq "AAD_PREMIUM") { return "Azure Active Directory Premium P1" }
            if ($_.SkuPartNumber -eq "AAD_PREMIUM_P2") { return "Azure Active Directory Premium P2" }

            if ($_.SkuPartNumber -eq "EMS") { return "Enterprise Mobility + Security E3" }
            if ($_.SkuPartNumber -eq "EMSPREMIUM") { return "Enterprise Mobility + Security E5" }

            if ($_.SkuPartNumber -eq "INTUNE_A") { return "Intune" }
            if ($_.SkuPartNumber -eq "INTUNE_A_D") { return "Microsoft Intune Device" }
            if ($_.SkuPartNumber -eq "INTUNE_SMB") { return "Microsoft Intune SMB" }

            if ($_.SkuPartNumber -eq "WINDOWS_STORE") { return "Windows Store for Business" }
            if ($_.SkuPartNumber -eq "RMSBASIC") { return "Rights Management Service Basic Content Protection" }
            if ($_.SkuPartNumber -eq "RIGHTSMANAGEMENT_ADHOC") { return "Rights Management Adhoc" }

            if ($_.SkuPartNumber -eq "VISIO_PLAN1_DEPT") { return "Visio Plan 1" }
            if ($_.SkuPartNumber -eq "VISIO_PLAN2_DEPT ") { return "Visio Plan 2" }
            if ($_.SkuPartNumber -eq "VISIOONLINE_PLAN1") { return "Visio Online Plan 1" }
            if ($_.SkuPartNumber -eq "VISIOCLIENT") { return "Visio Online Plan 2" }

            if ($_.SkuPartNumber -eq "PROJECTESSENTIALS") { return "Project Online Essentials" }
            if ($_.SkuPartNumber -eq "PROJECTPREMIUM") { return "Project Online Premium" }
            if ($_.SkuPartNumber -eq "PROJECT_P1") { return "Project Plan 1" }
            if ($_.SkuPartNumber -eq "PROJECTPROFESSIONAL") { return "Project Plan 3" }

            if ($_.SkuPartNumber -eq "MS_TEAMS_IW") { return "Microsoft Teams Trial" }
            if ($_.SkuPartNumber -eq "MCOCAP") { return "Microsoft Teams Shared Devices" }
            if ($_.SkuPartNumber -eq "MCOEV") { return "Microsoft Teams Phone Standard" }
            if ($_.SkuPartNumber -eq "MCOEV_DOD") { return "Microsoft Teams Phone Standard for DOD" }
            if ($_.SkuPartNumber -eq "MCOTEAMS_ESSENTIALS") { return "Teams Phone with Calling Plan" }
            if ($_.SkuPartNumber -eq "TEAMS_FREE") { return "Microsoft Teams (Free)" }
            if ($_.SkuPartNumber -eq "Teams_Ess") { return "Microsoft Teams Essentials" }
            if ($_.SkuPartNumber -eq "Microsoft_Teams_Premium") { return "Microsoft Teams Premium" }
            if ($_.SkuPartNumber -eq "TEAMS_EXPLORATORY") { return "Microsoft Teams Exploratory" }
            if ($_.SkuPartNumber -eq "BUSINESS_VOICE_DIRECTROUTING") { return "Microsoft 365 Business Voice (without calling plan)" }
            if ($_.SkuPartNumber -eq "PHONESYSTEM_VIRTUALUSER") { return "Microsoft Teams Phone Resoure Account" }
            if ($_.SkuPartNumber -eq "Microsoft_Teams_Rooms_Basic_without_Audio_Conferencing") { return "Microsoft Teams Rooms Basic without Audio Conferencing" }
            if ($_.SkuPartNumber -eq "Microsoft_Teams_Rooms_Pro") { return "Microsoft Teams Rooms Pro" }
            if ($_.SkuPartNumber -eq "BUSINESS_VOICE_MED2") { return "Microsoft 365 Business Voice" }
            if ($_.SkuPartNumber -eq "MCOPSTN_5") { return "Microsoft 365 Domestic Calling Plan (120 Minutes)" }

            if ($_.SkuPartNumber -eq "POWER_BI_PRO") { return "Power BI Pro" }
            if ($_.SkuPartNumber -eq "POWERAPPS_VIRAL") { return "Microsoft Power Apps Plan 2 Trial" }
            if ($_.SkuPartNumber -eq "SPZA_IW") { return "App Connect IW" }
            if ($_.SkuPartNumber -eq "FLOW_FREE") { return "Microsoft Flow Free" }
            if ($_.SkuPartNumber -eq "CCIBOTS_PRIVPREV_VIRAL") { return "Power Virtual Agents Viral Trial" }
            if ($_.SkuPartNumber -eq "VIRTUAL_AGENT_BASE") { return "Power Virtual Agent" }

            if ($_.SkuPartNumber -eq "WIN_DEF_ATP") { return "Microsoft Defender for Endpoint" }
            if ($_.SkuPartNumber -eq "ADALLOM_STANDALONE") { return "Microsoft Cloud App Security" }
            if ($_.SkuPartNumber -eq "DEFENDER_ENDPOINT_P1") { return "Microsoft Defender for Endpoint P1" }
            if ($_.SkuPartNumber -eq "MDATP_Server") { return "Microsoft Defender for Endpoint Server" }
            if ($_.SkuPartNumber -eq "ATP_ENTERPRISE_FACULTY") { return "Microsoft Defender for Office 365 (Plan 1) Faculty" }
            if ($_.SkuPartNumber -eq "ATA") { return "Microsoft Defender for Identity" }
            if ($_.SkuPartNumber -eq "ATP_ENTERPRISE") { return "Microsoft Defender for Office 365 (Plan 1)" }

            if ($_.SkuPartNumber -eq "M365_F1") { return "Microsoft 365 F1" }
            if ($_.SkuPartNumber -eq "SPE_F1") { return "Microsoft 365 F3" }
            if ($_.SkuPartNumber -eq "DESKLESSPACK") { return "Office 365 F3" }

            if ($_.SkuPartNumber -eq "SPE_E3") { return "Microsoft 365 E3" }
            if ($_.SkuPartNumber -eq "SPE_E5") { return "Microsoft 365 E5" }
            if ($_.SkuPartNumber -eq "SPE_E5_CALLINGMINUTES") { return "Microsoft 365 E5 with Calling Minutes" }
            if ($_.SkuPartNumber -eq "INFORMATION_PROTECTION_COMPLIANCE") { return "Microsoft 365 E5 Compliance" }
            if ($_.SkuPartNumber -eq "IDENTITY_THREAT_PROTECTION") { return "Microsoft 365 E5 Security" }
            if ($_.SkuPartNumber -eq "SPE_E5_NOPSTNCONF") { return "Microsoft 365 E5 without Audio Conferencing" }
        
            if ($_.SkuPartNumber -eq "STANDARDPACK") { return "Office 365 E1" }
            if ($_.SkuPartNumber -eq "STANDARDWOFFPACK") { return "Office 365 E2" }
            if ($_.SkuPartNumber -eq "ENTERPRISEPACK") { return "Office 365 E3" }
            if ($_.SkuPartNumber -eq "ENTERPRISEWITHSCAL") { return "Office 365 E4" }
            if ($_.SkuPartNumber -eq "ENTERPRISEPREMIUM") { return "Office 365 E5" }
            if ($_.SkuPartNumber -eq "ENTERPRISEPREMIUM_NOPSTNCONF") { return "Office 365 E5 without Audio Conferencing" }

            if ($_.SkuPartNumber -eq "SPB") { return "Microsoft 365 Business Premium" }
            if ($_.SkuPartNumber -eq "O365_BUSINESS_PREMIUM") { return "Microsoft 365 Business Standard" }
            if (($_.SkuPartNumber -eq "O365_BUSINESS") -or ($_.SkuPartNumber -eq "SMB_BUSINESS")) { return "Microsoft 365 Apps for Business" }
            if ($_.SkuPartNumber -eq "OFFICESUBSCRIPTION") { return "Microsoft 365 Apps for enterprise" }
            if (($_.SkuPartNumber -eq "O365_BUSINESS_ESSENTIALS") -or ($_.SkuPartNumber -eq "SMB_BUSINESS_ESSENTIALS")) { return "Microsoft 365 Business Basic" }
            else { return $_.SkuPartNumber }
        } 
    }, @{Name = "Total"; Expression = { $_.PrepaidUnits.Enabled } }, @{Name = "Assigned"; Expression = { $_.ConsumedUnits } } , @{Name = "Available"; Expression = { ($_.PrepaidUnits.Enabled) - ($_.ConsumedUnits) } } , AppliesTo | ConvertTo-Html -As Table -Fragment -PreContent "<br><h3 id='AAD_SKU'>Licenses</h3>"
}

<# User Account section #>
function Disable-UserAccount {
    param (
        $Users
    )
    $params = @{
        AccountEnabled = "false"
    }
    Write-Host "Disable user accounts"
    foreach ($User in $Users) {
        Update-MgUser -UserId $User.UserPrincipalName -BodyParameter $params
    }
}
function Request-UserAccountStatus {
    param(
        $UserId
    )
    return (Get-MgUser -UserId $UserId -Property AccountEnabled).AccountEnabled
}

<# Admin role section #>
function Get-AdminRoleReport {
    Write-Host "Checking admin role assignments"
    $Assignments = Get-MgRoleManagementDirectoryRoleAssignment -Property PrincipalId, RoleDefinitionId
    foreach ($Assignment in $Assignments) {
        $ProcessedCount++
        Write-Progress -Activity "Processed count: $ProcessedCount; Currently processing: $($Assignment.PrincipalId)"
        if ($User = Get-MgUser -UserId $Assignment.PrincipalId -Property DisplayName, UserPrincipalName -ErrorAction SilentlyContinue) {
            $Assignment | Add-Member -NotePropertyName "DisplayName" -NotePropertyValue $User.DisplayName
            $Assignment | Add-Member -NotePropertyName "UserPrincipalName" -NotePropertyValue $User.UserPrincipalName
            $Assignment | Add-Member -NotePropertyName "RoleName" -NotePropertyValue (Get-MgRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $Assignment.RoleDefinitionId -Property DisplayName).DisplayName
        }
    }
    Write-Progress -Activity "Processed count: $ProcessedCount; Currently processing: $($Assignment.PrincipalId)" -Status "Ready" -Completed
    return $Assignments | Where-Object { -not ($null -eq $_.DisplayName) } | Sort-Object -Property UserPrincipalName | ConvertTo-Html -Property DisplayName, UserPrincipalName, RoleName -As Table -Fragment -PreContent "<br><h3 id='AAD_ADMINS'>Admin role assignments</h3>"
}

<# BreakGlass account Section #>
function Get-BreakGlassAccountReport {
    param (
        $Create
    )
    if ($BGAccount = Get-BreakGlassAccount) {
        $Report = $BGAccount | ConvertTo-Html -Property DisplayName, UserPrincipalName, AccountEnabled, GlobalAdmin, LastSignIn -As Table -Fragment -PreContent "<br><h3 id='AAD_BG'>BreakGlass account</h3>"
        $Report = $Report -Replace "<td>False</td>", "<td class='red'>False</td>"
        return $Report

    }
    if ($create) {
        New-BreakGlassAccount
        $Report = Get-BreakGlassAccount | ConvertTo-Html -Property DisplayName, UserPrincipalName, AccountEnabled, GlobalAdmin, LastSignIn -As Table -Fragment -PreContent "<br><h3 id='AAD_BG'>BreakGlass account</h3>" -PostContent "<p>Check console log for credentials</p>"
        $Report = $Report -Replace "<td>False</td>", "<td class='red'>False</td>"
        return $Report
    }
    return "<br><h3 id='AAD_BG'>BreakGlass account</h3><p>Not found</p>"
}
function Get-BreakGlassAccount {
    Write-Host "Checking BreakGlass account"
    Select-MgProfile -Name "beta"
    $BGAccounts = Get-MgUser -Filter "startswith(displayName, 'BreakGlass ')" -Property Id, DisplayName, UserPrincipalName, AccountEnabled, SignInActivity
    Select-MgProfile -Name "v1.0"
    if (-not $bgAccounts) { 
        $BGAccounts = Get-MgUser -Filter "startswith(displayName, 'BreakGlass ')" -Property Id, DisplayName, UserPrincipalName, AccountEnabled
    }
    if (-not $bgAccounts) { return }
    foreach ($BGAccount in $BGAccounts) {
        Add-Member -InputObject $BGAccount -NotePropertyName "GlobalAdmin" -NotePropertyValue (Request-GlobalAdminRole $BGAccount.Id)
        Add-Member -InputObject $BGAccount -NotePropertyName "LastSignIn" -NotePropertyValue $BGAccount.SignInActivity.LastSignInDateTime
    }
    return $BGAccounts
}
function Get-GlobalAdminRoleId {
    return (Get-MgDirectoryRole -Filter "DisplayName eq 'Global Administrator'" -Property Id).Id
}
function Request-GlobalAdminRole {
    param (
        $AccountId
    )
    if (Get-MgDirectoryRoleMember -DirectoryRoleId (Get-GlobalAdminRoleId) -Filter "id eq '$($AccountId)'") {
        return $true
    }
}
function New-BreakGlassAccount {
    Write-Host "Creating BreakGlass account:"
    $Name = -join ((97..122) | Get-Random -Count 64 | ForEach-Object { [char]$_ })
    $DisplayName = "BreakGlass $Name"
    $Domain = (Get-MgDomain -Property id, IsInitial | Where-Object { $_.IsInitial -eq $true }).Id
    $UPN = "$Name@$Domain"
    $PasswordProfile = @{
        ForceChangePasswordNextSignIn        = $false
        ForceChangePasswordNextSignInWithMfa = $false
        Password                             = New-Password
    }
    $BGAccount = New-MgUser -DisplayName $DisplayName -UserPrincipalName $UPN -MailNickname $Name -PasswordProfile $PasswordProfile -PreferredLanguage "en-US" -AccountEnabled
    $DirObject = @{
        "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($BGAccount.id)"
    }
    New-MgDirectoryRoleMemberByRef -DirectoryRoleId (Get-GlobalAdminRoleId) -BodyParameter $DirObject
    Add-Member -InputObject $BGAccount -NotePropertyName "Password" -NotePropertyValue $PasswordProfile.Password
    Write-Host ($BGAccount | Select-Object -Property Id, DisplayName, UserPrincipalName, Password | Format-List | Out-String)
}
function New-Password {
    param (
        [ValidateRange(4, [int]::MaxValue)]
        [int] $Length = 64,
        [int] $Upper = 4,
        [int] $Lower = 4,
        [int] $Numeric = 4,
        [int] $Special = 4
    )
    if ($Upper + $Lower + $Numeric + $Special -gt $Length) {
        throw "number of Upper/Lower/Numeric/Special char must be Lower or equal to Length"
    }
    $UpperCaseCharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    $LowerCaseCharSet = "abcdefghijklmnopqrstuvwxyz"
    $NumberCharSet = "0123456789"
    $SpecialCharCharSet = "/*-+, !?=()@; :._"
    $CharSet = ""
    if ($Upper -gt 0) { $CharSet += $UpperCaseCharSet }
    if ($Lower -gt 0) { $CharSet += $LowerCaseCharSet }
    if ($Numeric -gt 0) { $CharSet += $NumberCharSet }
    if ($Special -gt 0) { $CharSet += $SpecialCharCharSet }
    $CharSet = $CharSet.ToCharArray()
    $RNG = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
    $Bytes = New-Object byte[]($Length)
    $RNG.GetBytes($Bytes)
    $Result = New-Object char[]($Length)
    for ($i = 0 ; $i -lt $Length ; $i++) {
        $Result[$i] = $CharSet[$Bytes[$i] % $CharSet.Length]
    }
    $Password = (-join $Result)
    $Valid = $true
    if ($Upper -gt ($Password.ToCharArray() | Where-Object { $_ -cin $UpperCaseCharSet.ToCharArray() }).Count) { $Valid = $false }
    if ($Lower -gt ($Password.ToCharArray() | Where-Object { $_ -cin $LowerCaseCharSet.ToCharArray() }).Count) { $Valid = $false }
    if ($Numeric -gt ($Password.ToCharArray() | Where-Object { $_ -cin $NumberCharSet.ToCharArray() }).Count) { $Valid = $false }
    if ($Special -gt ($Password.ToCharArray() | Where-Object { $_ -cin $SpecialCharCharSet.ToCharArray() }).Count) { $Valid = $false }
    if (!$Valid) {
        $Password = Get-RandomPassword $Length $Upper $Lower $Numeric $Special
    }
    return $Password
}

<# User MFA section#>
function Get-UserMfaStatusReport {
    Write-Host "Checking user MFA status"
    $Users = Get-MgUser -All -Filter "UserType eq 'Member'" -Property Id, DisplayName, UserPrincipalName, AssignedLicenses, AccountEnabled
    $Users | ForEach-Object {
        $ProcessedCount++
        if (($_.AssignedLicenses).Count -ne 0) {
            $LicenseStatus = "Licensed"
        }
        else {
            $LicenseStatus = "Unlicensed"
        }
        Write-Progress -Activity "Processed count: $ProcessedCount; Currently processing: $($_.DisplayName)"
        [array]$MFAData = Get-MgUserAuthenticationMethod -UserId $_.Id
        $AuthenticationMethod = @()
        $AdditionalDetails = @()
        foreach ($MFA in $MFAData) {
            switch ($MFA.AdditionalProperties["@odata.type"]) { 
                "#microsoft.graph.passwordAuthenticationMethod" {
                    $AuthMethod = 'PasswordAuthentication'
                    $AuthMethodDetails = $MFA.AdditionalProperties["displayName"] 
                } 
                "#microsoft.graph.microsoftAuthenticatorAuthenticationMethod" {
                    $AuthMethod = 'AuthenticatorApp'
                    $AuthMethodDetails = $MFA.AdditionalProperties["displayName"] 
                }
                "#microsoft.graph.phoneAuthenticationMethod" {
                    $AuthMethod = 'PhoneAuthentication'
                    $AuthMethodDetails = $MFA.AdditionalProperties["phoneType", "phoneNumber"] -join ' '
                } 
                "#microsoft.graph.fido2AuthenticationMethod" {
                    $AuthMethod = 'Fido2'
                    $AuthMethodDetails = $MFA.AdditionalProperties["model"] 
                }  
                "#microsoft.graph.windowsHelloForBusinessAuthenticationMethod" {
                    $AuthMethod = 'WindowsHelloForBusiness'
                    $AuthMethodDetails = $MFA.AdditionalProperties["displayName"] 
                }                        
                "#microsoft.graph.emailAuthenticationMethod" {
                    $AuthMethod = 'EmailAuthentication'
                    $AuthMethodDetails = $MFA.AdditionalProperties["emailAddress"] 
                }               
                "microsoft.graph.temporaryAccessPassAuthenticationMethod" {
                    $AuthMethod = 'TemporaryAccessPass'
                    $AuthMethodDetails = 'Access pass lifetime (minutes): ' + $MFA.AdditionalProperties["lifetimeInMinutes"] 
                }
                "#microsoft.graph.passwordlessMicrosoftAuthenticatorAuthenticationMethod" {
                    $AuthMethod = 'PasswordlessMSAuthenticator'
                    $AuthMethodDetails = $MFA.AdditionalProperties["displayName"]
                }
                "#microsoft.graph.softwareOathAuthenticationMethod" {
                    $AuthMethod = 'SoftwareOath'
                    $AuthMethodDetails = 'Third-party OTP app'
                }
            }
            $AuthenticationMethod += $AuthMethod
            if ($null -ne $AuthMethodDetails) {
                $AdditionalDetails += "$($AuthMethod): $AuthMethodDetails"
            }
        }
        $AuthenticationMethod = $AuthenticationMethod | Sort-Object | Get-Unique
        $AdditionalDetail = $AdditionalDetails -join ', '
        [array]$StrongMFAMethods = ("Fido2", "PasswordlessMSAuthenticator", "AuthenticatorApp", "WindowsHelloForBusiness", "SoftwareOath")
        $MFAStatus = "Disabled"
        foreach ($StrongMFAMethod in $StrongMFAMethods) {
            if ($AuthenticationMethod -contains $StrongMFAMethod) {
                $MFAStatus = "Strong"
                break
            }
        }
        if ( ($AuthenticationMethod -contains "PhoneAuthentication") -or ($AuthenticationMethod -contains "EmailAuthentication")) {
            $MFAStatus = "Weak"
        }
        Add-Member -InputObject $_ -NotePropertyName "LicenseStatus" -NotePropertyValue $LicenseStatus
        Add-Member -InputObject $_ -NotePropertyName "MFAStatus" -NotePropertyValue $MFAStatus
        Add-Member -InputObject $_ -NotePropertyName "AdditionalDetail" -NotePropertyValue $AdditionalDetail
    }
    Write-Progress -Activity "Processed count: $ProcessedCount; Currently processing: $($_.DisplayName)" -Status "Ready" -Completed
    $Report = $Users | Sort-Object -Property UserPrincipalName | ConvertTo-Html -Property DisplayName, UserPrincipalName, LicenseStatus, AccountEnabled, MFAStatus, AdditionalDetail -As Table -Fragment -PreContent "<br><h3 id='AAD_MFA'>User MFA status</h3>" -PostContent "<p>Weak: PhoneAuthentication, EmailAuthentication</p><p>Strong: Fido2, PasswordlessMSAuthenticator, AuthenticatorApp, WindowsHelloForBusiness, SoftwareOath</p>"
    $Report = $Report -Replace "<td>True</td><td>Disabled</td>", "<td>True</td><td class='red'>Disabled</td>"
    $Report = $Report -Replace "<td>True</td><td>Weak</td>", "<td>True</td><td class='orange'>Weak</td>"
    return $Report
}

<# Guest user section#>
function Get-GuestUserReport {
    Write-Host "Checking guest accounts"
    Select-MgProfile -Name "beta"
    $Users = Get-MgUser -All -Filter "UserType eq 'Guest'" -Property Id, DisplayName, UserPrincipalName, AccountEnabled, SignInActivity
    Select-MgProfile -Name "v1.0"
    if (-not $Users) {
        return "<br><h3 id='AAD_GUEST'>Guest accounts</h3><p>Not found</p>"
    }
    return $Users | Select-Object -Property DisplayName, UserPrincipalName, AccountEnabled, @{Name = "LastSignIn"; Expression = { $_.SignInActivity.LastSignInDateTime } } | Sort-Object -Property LastSignIn | ConvertTo-Html -As Table -Fragment -PreContent "<br><h3 id='AAD_GUEST'>Guest accounts</h3>"
}

<# Security defaults section #>
function Get-SecurityDefaultsReport {
    param (
        [System.Boolean]$EnableSecurityDefaults,
        [System.Boolean]$DisableSecurityDefaults
    )
    if ($EnableSecurityDefaults -and (-not $DisableSecurityDefaults)) {
        Update-SecurityDefaults -Enable $true
    }  
    if ($DisableSecurityDefaults -and (-not $EnableSecurityDefaults)) {
        Update-SecurityDefaults -Enable $false
    }
    if (Request-SecurityDefaults) {
        return "<br><h3 id='AAD_SEC_DEFAULTS'>Security defaults</h3><p>Enabled</p>"
    }
    return "<br><h3 id='AAD_SEC_DEFAULTS'>Security defaults</h3><p>Disabled</p>"
}
function Request-SecurityDefaults {
    Write-Host "Checking security defaults"
    return (Get-MgPolicyIdentitySecurityDefaultEnforcementPolicy -Property "isEnabled").IsEnabled
}
function Update-SecurityDefaults {
    param ([System.Boolean]$Enable)
    $params = @{
        IsEnabled = $Enable
    }
    Write-Host "Updating security defaults enable:" $Enable
    Update-MgPolicyIdentitySecurityDefaultEnforcementPolicy -BodyParameter $params
}

<# Conditional access section #>
function Get-ConditionalAccessPolicyReport {
    Write-Host "Checking conditional access policies"
    if ($Policy = Get-MgIdentityConditionalAccessPolicy -Property Id, DisplayName, State) {
        return $Policy | ConvertTo-Html -Property DisplayName, Id, State -As Table -Fragment -PreContent "<br><h3 id='AAD_CA'>Conditional access policies</h3>"
    }
    return "<br><h3 id='AAD_CA'>Conditional access policies</h3><p>Not found</p>"
}
function Get-NamedLocationReport {
    Write-Host "Checking named locations"
    if ($Locations = Get-MgIdentityConditionalAccessNamedLocation) {
        return $Locations | Select-Object -Property DisplayName, @{Name = "Trusted"; Expression = { $_.additionalProperties["isTrusted"] } }, @{Name = "IPRange"; Expression = {
                $IpRangesReport = @()
                foreach ($IpRange in ($_.additionalProperties["ipRanges"])) {
                    $IpRangesReport += $IpRange["cidrAddress"]
                }
                return $IpRangesReport
            }
        }, @{Name = "Countries"; Expression = { $_.additionalProperties["countriesAndRegions"] } } | ConvertTo-Html -As Table -Fragment -PreContent "<br><h3 id='AAD_CA_LOCATIONS'>Named locations</h3>"
    }
    return "<br><h3 id='AAD_CA_LOCATIONS'>Named locations</h3><p>Not found</p>"
}

<# Application protection polices section#>
function Get-AppProtectionPolicesReport {
    Write-Host "Checking app protection policies"
    if ($Polices = Get-AppProtectionPolices) {
        return $Polices | ConvertTo-Html -As Table -Property DisplayName, IsAssigned -Fragment -PreContent "<br><h3 id='AAD_APP_POLICY'>App protection policies</h3>"
    }
    return "<br><h3 id='AAD_APP_POLICY'>App protection policies</h3><p>Not found</p>"
}
function Get-AppProtectionPolices {
    $IOSPolicies = Get-MgDeviceAppManagementiOSManagedAppProtection -Property DisplayName, IsAssigned
    $AndroidPolicies = Get-MgDeviceAppManagementAndroidManagedAppProtection -Property DisplayName, IsAssigned
    $Policies = @()
    $Policies += $IOSPolicies
    $Policies += $AndroidPolicies
    return $Policies
}