Public/Configuration.ps1
|
<# .SYNOPSIS Gets the current desired state configuration for the tenant. .DESCRIPTION This function reads and displays the current desired state configuration from a JSON file. .PARAMETER Path The path to the configuration file. Defaults to '~/.O365-Toolkit/configs/default.json'. .EXAMPLE Get-O365Configuration .EXAMPLE Get-O365Configuration -Path 'C:\MyConfigs\custom.json' #> function Get-O365Configuration { [CmdletBinding()] param( [string]$Path = (Join-Path -Path $HOME -ChildPath '.O365-Toolkit\configs\default.json') ) if (Test-Path -Path $Path) { return (Get-Content -Path $Path | ConvertFrom-Json) } else { Write-Warning "Configuration file not found at: $Path" return $null } } <# .SYNOPSIS Generates a template for the desired state configuration. .DESCRIPTION This function generates an empty or example configuration file that can be used as a template for defining the desired state of the tenant. .PARAMETER Path The path to save the template file to. Defaults to '~/.O365-Toolkit/configs/template.json'. .EXAMPLE New-O365ConfigurationTemplate .EXAMPLE New-O365ConfigurationTemplate -Path 'C:\MyConfigs\new_template.json' #> function New-O365ConfigurationTemplate { [CmdletBinding()] param( [string]$Path = (Join-Path -Path $HOME -ChildPath '.O365-Toolkit\configs\template.json') ) $ConfigDir = Split-Path -Path $Path -Parent if (-not (Test-Path -Path $ConfigDir)) { New-Item -ItemType Directory -Path $ConfigDir | Out-Null } $TemplateConfig = @{ "GlobalTenantSettings" = @{ "ExternalSharingEnabled" = $true "GuestAccessAllowed" = $true }; "UserMFA" = @{ "AllUsersMFAEnabled" = $true "ExceptionUsers" = @() }; "MailboxForwarding" = @{ "ExternalForwardingAllowed" = $false "InternalOnlyForwarding" = $true }; "TeamsGovernance" = @{ "AllowGuestCreateUpdateChannels" = $false } } $TemplateConfig | ConvertTo-Json -Depth 10 | Out-File -FilePath $Path Write-Verbose "Configuration template saved to: $Path" } <# .SYNOPSIS Compares the active tenant configuration against a desired state configuration file. .DESCRIPTION This function reads a desired state configuration file and compares it against the current configuration of the tenant. It reports on any discrepancies (drift). .PARAMETER Path The path to the desired state configuration file. Defaults to '~/.O365-Toolkit/configs/default.json'. .EXAMPLE Test-O365Configuration .EXAMPLE Test-O365Configuration -Path 'C:\MyConfigs\custom.json' #> function Test-O365Configuration { [CmdletBinding()] param( [string]$Path = (Join-Path -Path $HOME -ChildPath '.O365-Toolkit\configs\default.json') ) $DesiredConfig = Get-O365Configuration -Path $Path if (-not $DesiredConfig) { return } $Report = [System.Collections.Generic.List[Object]]::new() # Global Tenant Settings if ($DesiredConfig.GlobalTenantSettings) { $CurrentSettings = Get-O365GlobalTenantSettings if ($DesiredConfig.GlobalTenantSettings.ExternalSharingEnabled -ne $CurrentSettings.ExternalSharingEnabled) { $Report.Add([PSCustomObject]@{ Setting = 'GlobalTenantSettings.ExternalSharingEnabled' Desired = $DesiredConfig.GlobalTenantSettings.ExternalSharingEnabled Current = $CurrentSettings.ExternalSharingEnabled Drift = $true }) } if ($DesiredConfig.GlobalTenantSettings.GuestAccessAllowed -ne $CurrentSettings.GuestAccessAllowed) { $Report.Add([PSCustomObject]@{ Setting = 'GlobalTenantSettings.GuestAccessAllowed' Desired = $DesiredConfig.GlobalTenantSettings.GuestAccessAllowed Current = $CurrentSettings.GuestAccessAllowed Drift = $true }) } } # User MFA if ($DesiredConfig.UserMFA) { $CurrentSettings = Get-O365UserMFAStatus if ($DesiredConfig.UserMFA.AllUsersMFAEnabled -ne $CurrentSettings.AllUsersMFAEnabled) { $Report.Add([PSCustomObject]@{ Setting = 'UserMFA.AllUsersMFAEnabled' Desired = $DesiredConfig.UserMFA.AllUsersMFAEnabled Current = $CurrentSettings.AllUsersMFAEnabled Drift = $true }) } } # Mailbox Forwarding if ($DesiredConfig.MailboxForwarding) { $CurrentSettings = Get-O365MailboxForwardingSettings if ($DesiredConfig.MailboxForwarding.ExternalForwardingAllowed -ne $CurrentSettings.ExternalForwardingAllowed) { $Report.Add([PSCustomObject]@{ Setting = 'MailboxForwarding.ExternalForwardingAllowed' Desired = $DesiredConfig.MailboxForwarding.ExternalForwardingAllowed Current = $CurrentSettings.ExternalForwardingAllowed Drift = $true }) } if ($DesiredConfig.MailboxForwarding.InternalOnlyForwarding -ne $CurrentSettings.InternalOnlyForwarding) { $Report.Add([PSCustomObject]@{ Setting = 'MailboxForwarding.InternalOnlyForwarding' Desired = $DesiredConfig.MailboxForwarding.InternalOnlyForwarding Current = $CurrentSettings.InternalOnlyForwarding Drift = $true }) } } # Teams Governance if ($DesiredConfig.TeamsGovernance) { $CurrentSettings = Get-O365TeamsGovernanceSettings if ($DesiredConfig.TeamsGovernance.AllowGuestCreateUpdateChannels -ne $CurrentSettings.AllowGuestCreateUpdateChannels) { $Report.Add([PSCustomObject]@{ Setting = 'TeamsGovernance.AllowGuestCreateUpdateChannels' Desired = $DesiredConfig.TeamsGovernance.AllowGuestCreateUpdateChannels Current = $CurrentSettings.AllowGuestCreateUpdateChannels Drift = $true }) } } return $Report } <# .SYNOPSIS Applies the settings from a desired state configuration file to the tenant. .DESCRIPTION This function reads a desired state configuration file and applies its settings to the tenant. .PARAMETER Path The path to the desired state configuration file. Defaults to '~/.O365-Toolkit/configs/default.json'. .PARAMETER Confirm Prompts for confirmation before making changes. .EXAMPLE Set-O365Configuration -Confirm .EXAMPLE Set-O365Configuration -Path 'C:\MyConfigs\custom.json' -Confirm:$false #> function Set-O365Configuration { [CmdletBinding(SupportsShouldProcess=$true)] param( [string]$Path = (Join-Path -Path $HOME -ChildPath '.O365-Toolkit\configs\default.json'), [switch]$Confirm ) $DesiredConfig = Get-O365Configuration -Path $Path if (-not $DesiredConfig) { return } if ($PSCmdlet.ShouldProcess("applying configuration from $Path")) { # Global Tenant Settings if ($DesiredConfig.GlobalTenantSettings) { Set-O365GlobalTenantSettings -ExternalSharingEnabled $DesiredConfig.GlobalTenantSettings.ExternalSharingEnabled -GuestAccessAllowed $DesiredConfig.GlobalTenantSettings.GuestAccessAllowed } # User MFA if ($DesiredConfig.UserMFA) { Set-O365UserMFAStatus -AllUsersMFAEnabled $DesiredConfig.UserMFA.AllUsersMFAEnabled -ExceptionUsers $DesiredConfig.UserMFA.ExceptionUsers } # Mailbox Forwarding if ($DesiredConfig.MailboxForwarding) { Set-O365MailboxForwardingSettings -ExternalForwardingAllowed $DesiredConfig.MailboxForwarding.ExternalForwardingAllowed -InternalOnlyForwarding $DesiredConfig.MailboxForwarding.InternalOnlyForwarding } # Teams Governance if ($DesiredConfig.TeamsGovernance) { Set-O365TeamsGovernanceSettings -AllowGuestCreateUpdateChannels $DesiredConfig.TeamsGovernance.AllowGuestCreateUpdateChannels } Write-Verbose "Configuration applied successfully." } } <# .SYNOPSIS Creates a DSC snapshot of the current tenant state for onboarding workflows. .DESCRIPTION Captures the current state of all tenant configuration settings and exports them to a JSON snapshot file. This snapshot serves as a baseline for onboarding validation and can be used to compare against desired configurations. .PARAMETER OutputPath The path to save the snapshot file. Defaults to '~/.O365-Toolkit/snapshots/onboarding-baseline-<timestamp>.json'. .PARAMETER IncludeConditionalAccess When set, includes Conditional Access policy snapshot in the baseline. .PARAMETER IncludeAccessPackages When set, includes Access Package snapshot in the baseline. .PARAMETER IncludeDirectoryRoles When set, includes directory role assignments snapshot in the baseline. .EXAMPLE New-O365DscSnapshot -OutputPath './onboarding-baseline.json' .EXAMPLE New-O365DscSnapshot -IncludeConditionalAccess -IncludeAccessPackages -IncludeDirectoryRoles .NOTES Requires appropriate Graph scopes for each included component. #> function New-O365DscSnapshot { [CmdletBinding()] param( [string]$OutputPath, [switch]$IncludeConditionalAccess, [switch]$IncludeAccessPackages, [switch]$IncludeDirectoryRoles ) if (-not $OutputPath) { $snapshotDir = Join-Path -Path $HOME -ChildPath '.O365-Toolkit\snapshots' if (-not (Test-Path -Path $snapshotDir)) { New-Item -ItemType Directory -Path $snapshotDir | Out-Null } $timestamp = (Get-Date).ToString('yyyyMMdd-HHmmss') $OutputPath = Join-Path -Path $snapshotDir -ChildPath "onboarding-baseline-$timestamp.json" } $tenant = Get-EntraTenantDetail | Select-Object -First 1 $snapshot = @{ snapshotDate = (Get-Date).ToString("o") tenantId = $tenant.TenantId tenantName = $tenant.DisplayName } # Capture Global Tenant Settings try { $snapshot['GlobalTenantSettings'] = Get-O365GlobalTenantSettings } catch { Write-Warning "Failed to capture GlobalTenantSettings: $_" } # Capture User MFA Status try { $snapshot['UserMFA'] = Get-O365UserMFAStatus } catch { Write-Warning "Failed to capture UserMFA: $_" } # Capture Mailbox Forwarding Settings try { $snapshot['MailboxForwarding'] = Get-O365MailboxForwardingSettings } catch { Write-Warning "Failed to capture MailboxForwarding: $_" } # Capture Teams Governance Settings try { $snapshot['TeamsGovernance'] = Get-O365TeamsGovernanceSettings } catch { Write-Warning "Failed to capture TeamsGovernance: $_" } # Capture Conditional Access Policies if requested if ($IncludeConditionalAccess) { try { $snapshot['ConditionalAccessPolicies'] = Get-O365ConditionalAccessPolicy | Select-Object -Property Id, DisplayName, State, Description } catch { Write-Warning "Failed to capture ConditionalAccessPolicies: $_" } } # Capture Access Packages if requested if ($IncludeAccessPackages) { try { $snapshot['AccessPackages'] = Get-O365AccessPackagePlan | Select-Object -Property Id, DisplayName, CatalogId, State } catch { Write-Warning "Failed to capture AccessPackages: $_" } } # Capture Directory Role Assignments if requested if ($IncludeDirectoryRoles) { try { $snapshot['DirectoryRoles'] = Get-EntraDirectoryRoleAssignment | Select-Object -Property PrincipalId, RoleDefinitionId, DirectoryScopeId } catch { Write-Warning "Failed to capture DirectoryRoles: $_" } } # Ensure output directory exists $outputDir = Split-Path -Path $OutputPath -Parent if ($outputDir -and -not (Test-Path -Path $outputDir)) { New-Item -ItemType Directory -Path $outputDir | Out-Null } # Export snapshot to JSON $json = $snapshot | ConvertTo-Json -Depth 10 $json | Out-File -FilePath $OutputPath -Encoding utf8 Write-Verbose "DSC snapshot saved to: $OutputPath" return $OutputPath } <# .SYNOPSIS Compares a DSC snapshot against the current tenant state to identify configuration drift. .DESCRIPTION Takes a previously captured snapshot and compares it against the current tenant state. Reports on any changes or drift detected since the snapshot was created. .PARAMETER SnapshotPath The path to the DSC snapshot file to compare. .PARAMETER ReportPath Optional path to save the drift report as JSON. .EXAMPLE Test-O365DscSnapshot -SnapshotPath './onboarding-baseline.json' .EXAMPLE Test-O365DscSnapshot -SnapshotPath './onboarding-baseline.json' -ReportPath './drift-report.json' .NOTES Returns a list of configuration items with their previous and current states. #> function Test-O365DscSnapshot { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string]$SnapshotPath, [string]$ReportPath ) if (-not (Test-Path -Path $SnapshotPath)) { Write-Warning "Snapshot file not found: $SnapshotPath" return } $snapshot = Get-Content -Path $SnapshotPath -Raw | ConvertFrom-Json $report = [System.Collections.Generic.List[Object]]::new() # Compare Global Tenant Settings if ($snapshot.GlobalTenantSettings) { try { $current = Get-O365GlobalTenantSettings foreach ($setting in $snapshot.GlobalTenantSettings.PSObject.Properties) { $currentValue = $current.($setting.Name) if ($currentValue -ne $setting.Value) { $report.Add([PSCustomObject]@{ Category = 'GlobalTenantSettings' Setting = $setting.Name SnapshotValue = $setting.Value CurrentValue = $currentValue Drift = $true }) } } } catch { Write-Warning "Failed to compare GlobalTenantSettings: $_" } } # Compare User MFA if ($snapshot.UserMFA) { try { $current = Get-O365UserMFAStatus foreach ($setting in $snapshot.UserMFA.PSObject.Properties) { $currentValue = $current.($setting.Name) if ($currentValue -ne $setting.Value) { $report.Add([PSCustomObject]@{ Category = 'UserMFA' Setting = $setting.Name SnapshotValue = $setting.Value CurrentValue = $currentValue Drift = $true }) } } } catch { Write-Warning "Failed to compare UserMFA: $_" } } # Compare Mailbox Forwarding if ($snapshot.MailboxForwarding) { try { $current = Get-O365MailboxForwardingSettings foreach ($setting in $snapshot.MailboxForwarding.PSObject.Properties) { $currentValue = $current.($setting.Name) if ($currentValue -ne $setting.Value) { $report.Add([PSCustomObject]@{ Category = 'MailboxForwarding' Setting = $setting.Name SnapshotValue = $setting.Value CurrentValue = $currentValue Drift = $true }) } } } catch { Write-Warning "Failed to compare MailboxForwarding: $_" } } # Compare Teams Governance if ($snapshot.TeamsGovernance) { try { $current = Get-O365TeamsGovernanceSettings foreach ($setting in $snapshot.TeamsGovernance.PSObject.Properties) { $currentValue = $current.($setting.Name) if ($currentValue -ne $setting.Value) { $report.Add([PSCustomObject]@{ Category = 'TeamsGovernance' Setting = $setting.Name SnapshotValue = $setting.Value CurrentValue = $currentValue Drift = $true }) } } } catch { Write-Warning "Failed to compare TeamsGovernance: $_" } } # Save report if path provided if ($ReportPath) { $reportDir = Split-Path -Path $ReportPath -Parent if ($reportDir -and -not (Test-Path -Path $reportDir)) { New-Item -ItemType Directory -Path $reportDir | Out-Null } $report | ConvertTo-Json -Depth 10 | Out-File -FilePath $ReportPath -Encoding utf8 Write-Verbose "Drift report saved to: $ReportPath" } return $report } |