Files
macOS_IntuneManagement/Scripts/New-ConditionalAccessBaseline.ps1
tomas.kracmar d3e0769799 release: v4.1.0 — restructure entry points, add CIS baselines, reporting tools and fzf hints
- Restructure launchers: Start-IntuneToolkit.ps1 moves to repo root;
  Start-HeadlessIntune.ps1 moves to Scripts/; TUI helper moves to Scripts/Private/
- Add AGENTS.md with project architecture, entry points, and security notes
- Add CIS M365 baseline assets (CISM365-v7, M365-CIS-Rapid) and reporting scripts
- Add Python reporting utilities (Export-SettingsReport, Export-AssignmentReport,
  Export-ObjectInventoryReport) and CA wizard helpers
- Update Deploy-IntuneBaseline.ps1 with Merge conflict resolution, ReportPath,
  and optimized group loading
- Update Initialize-IntuneAuth.ps1 with -RotateSecret and configurable secret expiry
- Update Extensions for Settings Catalog definition auto-export
- Update README with v4.1.0, new entry points and script catalog
- Bump VERSION to 4.1.0
- Harden .gitignore against .DS_Store, __pycache__, .venv-pdf/, local exports,
  Settings.json and IntuneManagement.log
2026-06-14 15:24:42 +02:00

683 lines
24 KiB
PowerShell
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#requires -Version 7.0
<#
.SYNOPSIS
Generates a Conditional Access baseline YAML manifest from high-level security requirements.
.DESCRIPTION
Creates a CIS M365-compatible baseline YAML file covering Conditional Access policies.
The output can be reviewed and then deployed with Deploy-CISM365Baseline.ps1.
Policy names follow the structured naming convention:
<INDEX>-<TARGET>-<APP/RESOURCE>-<CONTROL>-<SCOPE>
Index ranges:
CA0xx User policies
CA1xx Guest policies
CA2xx Application policies
CA3xx Admin policies
CA4xx Threat policies
Example: CA001-AllUsers-AllApps-BlockLegacyAuth-Prod
.PARAMETER RequireTrustedLocations
Enforce that users can only sign in from trusted named locations.
- None: No location restriction policy
- AllUsers: All users must be on trusted locations
- Admins: Only administrative roles must be on trusted locations
- All: Both AllUsers and Admins policies
.PARAMETER AdminDeviceCompliance
Device requirements for administrative roles.
- None: No device policy for admins
- Required: Admins must use compliant or hybrid-joined devices
- RequiredWithMFA: Admins must use compliant/hybrid-joined devices AND MFA
.PARAMETER GuestMFA
Require MFA for guest and external users.
.PARAMETER SessionTimeoutHours
Require re-authentication after N hours. 0 disables session timeout policies.
.PARAMETER DisablePersistentBrowser
Prevent persistent browser sessions (users must re-auth when browser restarts).
.PARAMETER TrustedLocationsExemptFromReauth
When SessionTimeoutHours is set, do not require re-authentication from trusted locations.
This creates an exclusion so users on trusted networks are not nagged.
.PARAMETER RequireMFAForAllUsers
Require MFA for all member users.
.PARAMETER BlockLegacyAuth
Block all legacy authentication protocols (Exchange ActiveSync, basic auth, etc.).
.PARAMETER BlockHighRiskSignIns
Block sign-ins with medium or high risk level (requires Entra ID P2).
.PARAMETER RequireMFAForAdminPortals
Require MFA when accessing Microsoft admin portals (Azure, M365, Exchange, etc.).
.PARAMETER RequireMFAForAdmins
Require MFA for all administrative roles across all applications.
.PARAMETER RequirePhishingResistantMFAForAdmins
Require phishing-resistant MFA (FIDO2, certificate) for administrative roles.
.PARAMETER BlockDeviceCodeFlow
Block sign-ins using the device code authentication flow.
.PARAMETER RequireManagedDeviceForAllUsers
Require all users to use compliant or hybrid-joined devices.
.PARAMETER OutputPath
Path where the generated YAML baseline will be written.
.PARAMETER Scope
Deployment stage suffix applied to every policy name.
- Test, Pilot1, Pilot2, Pilot3, Prod
.PARAMETER UseDescriptiveNames
Use human-readable descriptive names instead of the structured naming convention.
.PARAMETER Prefix
Optional prefix applied before the INDEX (e.g. "ACME-" produces ACME-CA001-...).
.PARAMETER BreakGlassGroup
Name of the break-glass group to auto-exclude from every CA policy.
.PARAMETER ReportOnly
Default all generated policies to report-only mode (recommended for initial rollout).
.EXAMPLE
# Minimal baseline: MFA for all + block legacy auth
./Scripts/New-ConditionalAccessBaseline.ps1 `
-RequireMFAForAllUsers `
-BlockLegacyAuth `
-OutputPath ./Baselines/MyCA.yaml
.EXAMPLE
# Full security baseline with structured names scoped to production
./Scripts/New-ConditionalAccessBaseline.ps1 `
-RequireTrustedLocations AllUsers `
-AdminDeviceCompliance RequiredWithMFA `
-GuestMFA `
-SessionTimeoutHours 8 `
-DisablePersistentBrowser `
-TrustedLocationsExemptFromReauth `
-BlockLegacyAuth `
-BlockHighRiskSignIns `
-OutputPath ./Baselines/SecureTenant-CA.yaml `
-Scope Prod
.EXAMPLE
# Pilot rollout with descriptive names instead of structured convention
./Scripts/New-ConditionalAccessBaseline.ps1 `
-RequireMFAForAllUsers `
-BlockLegacyAuth `
-OutputPath ./Baselines/Pilot-CA.yaml `
-Scope Pilot1 `
-UseDescriptiveNames
#>
[CmdletBinding()]
param(
[Parameter()]
[ValidateSet('None','AllUsers','Admins','All')]
[string]$RequireTrustedLocations = 'None',
[Parameter()]
[ValidateSet('None','Required','RequiredWithMFA')]
[string]$AdminDeviceCompliance = 'None',
[Parameter()]
[switch]$GuestMFA,
[Parameter()]
[ValidateRange(0,24)]
[int]$SessionTimeoutHours = 0,
[Parameter()]
[switch]$DisablePersistentBrowser,
[Parameter()]
[switch]$TrustedLocationsExemptFromReauth,
[Parameter()]
[switch]$RequireMFAForAllUsers,
[Parameter()]
[switch]$BlockLegacyAuth,
[Parameter()]
[switch]$BlockHighRiskSignIns,
[Parameter()]
[switch]$RequireMFAForAdminPortals,
[Parameter()]
[switch]$RequireMFAForAdmins,
[Parameter()]
[switch]$RequirePhishingResistantMFAForAdmins,
[Parameter()]
[switch]$BlockDeviceCodeFlow,
[Parameter()]
[switch]$RequireManagedDeviceForAllUsers,
[Parameter(Mandatory = $true)]
[string]$OutputPath,
[Parameter()]
[ValidateSet('Test','Pilot1','Pilot2','Pilot3','Prod')]
[string]$Scope = 'Prod',
[Parameter()]
[switch]$UseDescriptiveNames,
[Parameter()]
[string]$Prefix = '',
[Parameter()]
[string]$BreakGlassGroup = 'CIS-BreakGlass',
[Parameter()]
[switch]$ReportOnly
)
$ErrorActionPreference = 'Stop'
# =====================================================================
# Naming convention engine
# =====================================================================
# Format: CA<area><scope><seq2digit>-<TARGET>-<APP/RESOURCE>-<CONTROL>
# Area: 0=Threat/Tenant, 1=User, 2=Admin, 3=Guest, 4=Application
# Scope: 0=Test, 1=Pilot1, 2=Pilot2, 3=Pilot3, 9=Prod
# Seq: auto-increment per area
# =====================================================================
$script:AreaDigitMap = @{
'User' = '1'
'Guest' = '3'
'Application' = '4'
'Admin' = '2'
'Threat' = '0'
}
$script:ScopeDigitMap = @{
'Test' = '0'
'Pilot1' = '1'
'Pilot2' = '2'
'Pilot3' = '3'
'Prod' = '9'
}
$script:NextSeq = @{
'0' = 1
'1' = 1
'2' = 1
'3' = 1
'4' = 1
}
function Get-StructuredPolicyName {
param(
[Parameter(Mandatory)]
[ValidateSet('User','Guest','Application','Admin','Threat')]
[string]$Category,
[Parameter(Mandatory)]
[string]$Target,
[Parameter(Mandatory)]
[string]$AppResource,
[Parameter(Mandatory)]
[string]$Control
)
$area = $script:AreaDigitMap[$Category]
$scope = $script:ScopeDigitMap[$Scope]
$seq = $script:NextSeq[$area]++
$idx = "$area$scope$($seq.ToString('D2'))"
$name = "CA$idx-${Target}-${AppResource}-${Control}"
if ($Prefix) { $name = "$Prefix$name" }
return $name
}
function Get-DescriptivePolicyName {
param([string]$Name)
if ($Prefix) { return "$Prefix$Name" }
return $Name
}
function Get-DefaultState {
if ($ReportOnly) { return 'enabledForReportingButNotEnforced' }
return 'enabled'
}
# =====================================================================
# Shared data
# =====================================================================
$script:AdminRoles = @(
'Global Administrator',
'Privileged Role Administrator',
'Security Administrator',
'Exchange Administrator',
'SharePoint Administrator',
'Conditional Access Administrator',
'Application Administrator',
'Cloud Application Administrator',
'User Administrator',
'Helpdesk Administrator',
'Billing Administrator',
'Authentication Administrator',
'Password Administrator'
)
$script:AdminPortalAppIds = @(
'797f4846-ba00-4fd7-ba43-dac1f8f63013', # Azure Management
'c44b4083-3bb0-49c1-b47d-974e53cbdf3c', # Azure AD PowerShell
'1b730954-1685-4b74-9bfd-dac224a7b894', # Microsoft Graph PowerShell
'00000003-0000-0ff1-ce00-000000000000', # Office 365 Exchange Online
'00000003-0000-0000-c000-000000000000', # Microsoft Graph
'de8bc8b5-d9f9-48b1-a8ad-b748da725064', # Microsoft Intune
'00000002-0000-0ff1-ce00-000000000000', # Office 365 SharePoint Online
'66a88757-258c-4c72-893c-3e8bed4d6899' # Microsoft365DSC
)
# =====================================================================
# Policy builders
# =====================================================================
function New-PolicyBlockLegacyAuth {
$policy = @{
name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Block-Legacy-Authentication' } else { Get-StructuredPolicyName -Category Threat -Target AllUsers -AppResource AllApps -Control BlockLegacyAuth }
description = 'Block all legacy authentication protocols (EAS, basic auth, IMAP, POP, etc.)'
state = Get-DefaultState
conditions = @{
applications = @{ includeApplications = @('All') }
users = @{ includeUsers = @('All') }
clientAppTypes = @('exchangeActiveSync', 'other')
}
grantControls = @{
builtInControls = @('block')
operator = 'OR'
}
}
return $policy
}
function New-PolicyRequireMFAAllUsers {
$policy = @{
name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Require-MFA-All-Users' } else { Get-StructuredPolicyName -Category User -Target AllUsers -AppResource AllApps -Control RequireMFA }
description = 'Require multi-factor authentication for all users'
state = Get-DefaultState
conditions = @{
applications = @{ includeApplications = @('All') }
users = @{ includeUsers = @('All') }
}
grantControls = @{
builtInControls = @('mfa')
operator = 'OR'
}
}
return $policy
}
function New-PolicyRequireMFAAdmins {
$policy = @{
name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Require-MFA-Admins' } else { Get-StructuredPolicyName -Category Admin -Target Admins -AppResource AllApps -Control RequireMFA }
description = 'Require multi-factor authentication for all administrative roles'
state = Get-DefaultState
conditions = @{
applications = @{ includeApplications = @('All') }
users = @{ includeRoles = $script:AdminRoles }
}
grantControls = @{
builtInControls = @('mfa')
operator = 'OR'
}
}
return $policy
}
function New-PolicyRequireMFAAdminPortals {
$policy = @{
name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Require-MFA-Admin-Portals' } else { Get-StructuredPolicyName -Category Application -Target AllUsers -AppResource AdminPortals -Control RequireMFA }
description = 'Require MFA when accessing Microsoft admin portals'
state = Get-DefaultState
conditions = @{
applications = @{ includeApplications = $script:AdminPortalAppIds }
users = @{ includeUsers = @('All') }
}
grantControls = @{
builtInControls = @('mfa')
operator = 'OR'
}
}
return $policy
}
function New-PolicyTrustedLocations {
param([switch]$ForAdmins)
if ($ForAdmins) {
$name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Trusted-Locations-Only-Admins' } else { Get-StructuredPolicyName -Category Admin -Target Admins -AppResource AllApps -Control BlockUntrustedLocations }
$desc = 'Administrators can only sign in from trusted named locations'
$userDef = @{ includeRoles = $script:AdminRoles }
} else {
$name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Trusted-Locations-Only-All-Users' } else { Get-StructuredPolicyName -Category User -Target AllUsers -AppResource AllApps -Control BlockUntrustedLocations }
$desc = 'All users can only sign in from trusted named locations'
$userDef = @{ includeUsers = @('All') }
}
$policy = @{
name = $name
description = $desc
state = Get-DefaultState
conditions = @{
applications = @{ includeApplications = @('All') }
users = $userDef
locations = @{
includeLocations = @('All')
excludeLocations = @('AllTrusted')
}
}
grantControls = @{
builtInControls = @('block')
operator = 'OR'
}
}
return $policy
}
function New-PolicyAdminDeviceCompliance {
param([switch]$WithMFA)
$controls = @('compliantDevice', 'domainJoinedDevice')
$operator = 'OR'
$desc = 'Administrators must use compliant or hybrid-joined devices'
if ($WithMFA) {
$controls = @('compliantDevice', 'domainJoinedDevice', 'mfa')
$operator = 'AND'
$desc = 'Administrators must use compliant/hybrid-joined devices AND MFA'
$name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Require-Compliant-Device-and-MFA-Admins' } else { Get-StructuredPolicyName -Category Admin -Target Admins -AppResource AllApps -Control RequireCompliantDeviceAndMFA }
} else {
$name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Require-Compliant-Device-Admins' } else { Get-StructuredPolicyName -Category Admin -Target Admins -AppResource AllApps -Control RequireCompliantDevice }
}
$policy = @{
name = $name
description = $desc
state = Get-DefaultState
conditions = @{
applications = @{ includeApplications = @('All') }
users = @{ includeRoles = $script:AdminRoles }
}
grantControls = @{
builtInControls = $controls
operator = $operator
}
}
return $policy
}
function New-PolicyGuestMFA {
$policy = @{
name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Require-MFA-Guests' } else { Get-StructuredPolicyName -Category Guest -Target Guests -AppResource AllApps -Control RequireMFA }
description = 'Require multi-factor authentication for guest and external users'
state = Get-DefaultState
conditions = @{
applications = @{ includeApplications = @('All') }
users = @{
includeGuestsOrExternalUsers = @{
guestTypes = @('internalGuest', 'b2bCollaborationGuest', 'b2bCollaborationMember', 'b2bDirectConnectUser')
externalTenants = @{ membershipKind = 'all' }
}
}
}
grantControls = @{
builtInControls = @('mfa')
operator = 'OR'
}
}
return $policy
}
function New-PolicySessionControls {
param(
[int]$TimeoutHours = 0,
[switch]$DisablePersistent,
[switch]$ExemptTrustedLocations
)
$sessionControls = @{}
$parts = [System.Collections.Generic.List[string]]::new()
if ($TimeoutHours -gt 0) {
$sessionControls['signInFrequency'] = @{
value = $TimeoutHours
type = 'hours'
isEnabled = $true
}
$parts.Add("re-authenticate every $TimeoutHours hours")
}
if ($DisablePersistent) {
$sessionControls['persistentBrowser'] = @{
mode = 'never'
isEnabled = $true
}
$parts.Add('no persistent browser sessions')
}
$desc = 'Session controls: ' + ($parts -join '; ')
if ($ExemptTrustedLocations) {
$desc += ' (exempt when on trusted locations)'
}
$controlTag = if ($TimeoutHours -gt 0 -and $DisablePersistent) {
'SessionControls'
} elseif ($TimeoutHours -gt 0) {
'SignInFrequency'
} else {
'NoPersistentBrowser'
}
$name = if ($UseDescriptiveNames) {
if ($TimeoutHours -gt 0 -and $DisablePersistent) {
Get-DescriptivePolicyName 'Session-Timeout-and-No-Persistent-Browser'
} elseif ($TimeoutHours -gt 0) {
Get-DescriptivePolicyName "Session-Timeout-${TimeoutHours}h"
} else {
Get-DescriptivePolicyName 'No-Persistent-Browser'
}
} else {
Get-StructuredPolicyName -Category User -Target AllUsers -AppResource AllApps -Control $controlTag
}
$conditions = @{
applications = @{ includeApplications = @('All') }
users = @{ includeUsers = @('All') }
}
if ($ExemptTrustedLocations) {
$conditions['locations'] = @{
excludeLocations = @('AllTrusted')
}
}
$policy = @{
name = $name
description = $desc
state = Get-DefaultState
conditions = $conditions
grantControls = @{
builtInControls = @('mfa')
operator = 'OR'
}
}
if ($sessionControls.Count -gt 0) {
$policy['sessionControls'] = $sessionControls
}
return $policy
}
function New-PolicyBlockHighRisk {
$policy = @{
name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Block-High-Risk-SignIns' } else { Get-StructuredPolicyName -Category Threat -Target AllUsers -AppResource AllApps -Control BlockHighRisk }
description = 'Block sign-ins with medium or high risk score (requires Entra ID P2)'
state = Get-DefaultState
conditions = @{
applications = @{ includeApplications = @('All') }
users = @{ includeUsers = @('All') }
signInRiskLevels = @('medium', 'high')
}
grantControls = @{
builtInControls = @('block')
operator = 'OR'
}
}
return $policy
}
function New-PolicyPhishingResistantMFAAdmins {
$policy = @{
name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Require-PhishingResistant-MFA-Admins' } else { Get-StructuredPolicyName -Category Admin -Target Admins -AppResource AllApps -Control RequirePhishingResistantMFA }
description = 'Require phishing-resistant MFA (FIDO2, certificate) for administrative roles'
state = Get-DefaultState
conditions = @{
applications = @{ includeApplications = @('All') }
users = @{ includeRoles = $script:AdminRoles }
}
grantControls = @{
builtInControls = @('authenticationStrength')
authenticationStrength = @{ id = '00000000-0000-0000-0000-000000000004' }
operator = 'OR'
}
}
return $policy
}
function New-PolicyBlockDeviceCodeFlow {
$policy = @{
name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Block-Device-Code-Flow' } else { Get-StructuredPolicyName -Category Threat -Target AllUsers -AppResource AllApps -Control BlockDeviceCodeFlow }
description = 'Block sign-ins using the device code authentication flow'
state = Get-DefaultState
conditions = @{
applications = @{ includeApplications = @('All') }
users = @{ includeUsers = @('All') }
authenticationFlows = @{
deviceCodeFlow = @{ isEnabled = $true }
}
}
grantControls = @{
builtInControls = @('block')
operator = 'OR'
}
}
return $policy
}
function New-PolicyRequireManagedDeviceAllUsers {
$policy = @{
name = if ($UseDescriptiveNames) { Get-DescriptivePolicyName 'Require-Managed-Device-All-Users' } else { Get-StructuredPolicyName -Category User -Target AllUsers -AppResource AllApps -Control RequireCompliantDevice }
description = 'Require all users to use compliant or hybrid-joined devices'
state = Get-DefaultState
conditions = @{
applications = @{ includeApplications = @('All') }
users = @{ includeUsers = @('All') }
}
grantControls = @{
builtInControls = @('compliantDevice', 'domainJoinedDevice')
operator = 'OR'
}
}
return $policy
}
# =====================================================================
# Build the policy list based on parameters
# =====================================================================
$policies = [System.Collections.Generic.List[hashtable]]::new()
if ($BlockLegacyAuth) { $policies.Add((New-PolicyBlockLegacyAuth)) }
if ($RequireMFAForAllUsers) { $policies.Add((New-PolicyRequireMFAAllUsers)) }
if ($RequireMFAForAdmins) { $policies.Add((New-PolicyRequireMFAAdmins)) }
if ($RequireMFAForAdminPortals) { $policies.Add((New-PolicyRequireMFAAdminPortals)) }
if ($BlockHighRiskSignIns) { $policies.Add((New-PolicyBlockHighRisk)) }
if ($BlockDeviceCodeFlow) { $policies.Add((New-PolicyBlockDeviceCodeFlow)) }
if ($RequirePhishingResistantMFAForAdmins) { $policies.Add((New-PolicyPhishingResistantMFAAdmins)) }
if ($RequireManagedDeviceForAllUsers) { $policies.Add((New-PolicyRequireManagedDeviceAllUsers)) }
switch ($RequireTrustedLocations) {
'AllUsers' { $policies.Add((New-PolicyTrustedLocations)) }
'Admins' { $policies.Add((New-PolicyTrustedLocations -ForAdmins)) }
'All' { $policies.Add((New-PolicyTrustedLocations)); $policies.Add((New-PolicyTrustedLocations -ForAdmins)) }
}
switch ($AdminDeviceCompliance) {
'Required' { $policies.Add((New-PolicyAdminDeviceCompliance)) }
'RequiredWithMFA' { $policies.Add((New-PolicyAdminDeviceCompliance -WithMFA)) }
}
if ($GuestMFA) { $policies.Add((New-PolicyGuestMFA)) }
if ($SessionTimeoutHours -gt 0 -or $DisablePersistentBrowser) {
$policies.Add((New-PolicySessionControls `
-TimeoutHours $SessionTimeoutHours `
-DisablePersistent:$DisablePersistentBrowser `
-ExemptTrustedLocations:$TrustedLocationsExemptFromReauth))
}
if ($policies.Count -eq 0) {
throw "No policies requested. Specify at least one requirement parameter (e.g. -RequireMFAForAllUsers, -BlockLegacyAuth, etc.)."
}
# =====================================================================
# Serialize to YAML (requires powershell-yaml)
# =====================================================================
function Test-YamlModule {
return [bool](Get-Module -ListAvailable -Name powershell-yaml)
}
if (-not (Test-YamlModule)) {
Write-Host "powershell-yaml module is required but not installed." -ForegroundColor Yellow
$confirm = Read-Host "Install powershell-yaml from PSGallery now? [Y/n]"
if ($confirm -match "^\s*n") {
throw "powershell-yaml is required. Install it with: Install-Module powershell-yaml -Scope CurrentUser -Force"
}
Install-Module powershell-yaml -Scope CurrentUser -Force
}
Import-Module powershell-yaml -Force
# Build the root document
$yamlRoot = [ordered]@{
baseline = [ordered]@{
name = 'Generated-ConditionalAccess-Baseline'
conflictResolution = 'Skip'
whatIf = $false
tenantConfig = [ordered]@{
conditionalAccess = [ordered]@{
reportOnly = $true
breakGlassGroup = $BreakGlassGroup
policies = $policies
}
}
}
}
$yamlText = ConvertTo-Yaml -Data $yamlRoot
# Ensure output directory exists
$outDir = Split-Path -Parent $OutputPath
if ($outDir -and -not (Test-Path $outDir)) {
New-Item -ItemType Directory -Path $outDir -Force | Out-Null
}
$yamlText | Set-Content -Path $OutputPath -Encoding UTF8 -Force
Write-Host "Generated Conditional Access baseline with $($policies.Count) policies." -ForegroundColor Green
Write-Host "Output written to: $(Resolve-Path $OutputPath)" -ForegroundColor Green
Write-Host ""
Write-Host "Review the file, then deploy with:" -ForegroundColor Cyan
Write-Host " ./Scripts/Deploy-CISM365Baseline.ps1 -BaselinePath '$OutputPath' -Mode Assess" -ForegroundColor Yellow
Write-Host " ./Scripts/Deploy-CISM365Baseline.ps1 -BaselinePath '$OutputPath' -Mode Deploy -Apply" -ForegroundColor Yellow