From 3526dee633fecfcd21cceca51239c8ee527027f9 Mon Sep 17 00:00:00 2001 From: Adam Gell Date: Wed, 23 Dec 2020 17:05:38 -0500 Subject: [PATCH 1/7] Personal changes to the DCToolbox module. Made the Import/Export CA policy commands accept an app token and tenantname paramter. --- DCToolbox.psm1 | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/DCToolbox.psm1 b/DCToolbox.psm1 index 047bffa..65430d3 100644 --- a/DCToolbox.psm1 +++ b/DCToolbox.psm1 @@ -1285,7 +1285,10 @@ function Export-DCConditionalAccessPolicyDesign { [string]$ClientSecret, [parameter(Mandatory = $false)] - [string]$FilePath = "$((Get-Location).Path)\Conditional Access Backup $(Get-Date -Format 'yyyy-MM-dd').json" + [string]$FilePath = "$((Get-Location).Path)\Conditional Access Backup $(Get-Date -Format 'yyyy-MM-dd').json", + + [parameter(Mandatory = $true)] + [string]$TenantName ) @@ -1298,7 +1301,7 @@ function Export-DCConditionalAccessPolicyDesign { # Authenticate to Microsoft Graph. Write-Verbose -Verbose -Message "Connecting to Microsoft Graph..." - $AccessToken = Connect-DCMsGraphAsDelegated -ClientID $ClientID -ClientSecret $ClientSecret + $AccessToken = Connect-DCMsGraphAsApplication -ClientID $ClientID -ClientSecret $ClientSecret -TenantName $TenantName # Export all Conditional Access policies from Microsoft Graph as JSON. @@ -1405,7 +1408,10 @@ function Import-DCConditionalAccessPolicyDesign { [switch]$SkipReportOnlyMode, [parameter(Mandatory = $false)] - [switch]$DeleteAllExistingPolicies + [switch]$DeleteAllExistingPolicies, + + [parameter(Mandatory = $true)] + [string]$TenantName ) @@ -1418,7 +1424,7 @@ function Import-DCConditionalAccessPolicyDesign { # Authenticate to Microsoft Graph. Write-Verbose -Verbose -Message "Connecting to Microsoft Graph..." - $AccessToken = Connect-DCMsGraphAsDelegated -ClientID $ClientID -ClientSecret $ClientSecret + $AccessToken = Connect-DCMsGraphAsApplication -ClientID $ClientID -ClientSecret $ClientSecret -TenantName $TenantName # Import policies from JSON file. From bfc310fce9775ac234c24f90e4f470d526baf3f4 Mon Sep 17 00:00:00 2001 From: Adam Gell Date: Wed, 23 Dec 2020 17:17:24 -0500 Subject: [PATCH 2/7] Created Access Policy as App function --- DCToolbox.psm1 | 101 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/DCToolbox.psm1 b/DCToolbox.psm1 index 65430d3..c705ccf 100644 --- a/DCToolbox.psm1 +++ b/DCToolbox.psm1 @@ -1274,6 +1274,107 @@ function Export-DCConditionalAccessPolicyDesign { + # ----- [Initialisations] ----- + + # Script parameters. + param ( + [parameter(Mandatory = $true)] + [string]$ClientID, + + [parameter(Mandatory = $true)] + [string]$ClientSecret, + + [parameter(Mandatory = $false)] + [string]$FilePath = "$((Get-Location).Path)\Conditional Access Backup $(Get-Date -Format 'yyyy-MM-dd').json" + ) + + + # Set Error Action - Possible choices: Stop, SilentlyContinue + $ErrorActionPreference = "Stop" + + + + # ----- [Execution] ----- + + # Authenticate to Microsoft Graph. + Write-Verbose -Verbose -Message "Connecting to Microsoft Graph..." + $AccessToken = Connect-DCMsGraphAsDelegated -ClientID $ClientID -ClientSecret $ClientSecret + + + # Export all Conditional Access policies from Microsoft Graph as JSON. + Write-Verbose -Verbose -Message "Exporting Conditional Access policies to '$FilePath'..." + + $GraphUri = 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies' + + Invoke-DCMsGraphQuery -AccessToken $AccessToken -GraphMethod 'GET' -GraphUri $GraphUri | Sort-Object createdDateTime | ConvertTo-Json -Depth 10 | Out-File -Force:$true -FilePath $FilePath + + # Perform some clean up in the file. + $CleanUp = Get-Content $FilePath | Select-String -Pattern '"id":', '"createdDateTime":', '"modifiedDateTime":' -notmatch + + $CleanUp | Out-File -Force:$true -FilePath $FilePath + + + Write-Verbose -Verbose -Message "Done!" +} + +function Export-DCConditionalAccessPolicyDesignAsApp { + <# + .SYNOPSIS + Export all Conditional Access policies to JSON. + + .DESCRIPTION + This CMDlet uses Microsoft Graph to export all Conditional Access policies in the tenant to a JSON file. This JSON file can be used for backup, documentation or to deploy the same policies again with Import-DCConditionalAccessPolicyDesign. You can treat Conditional Access as code! + + Before running this CMDlet, you first need to register a new application in your Azure AD according to this article: + https://danielchronlund.com/2018/11/19/fetch-data-from-microsoft-graph-with-powershell-paging-support/ + + The permissions for this CMDlet must be application versus delegation. + + The following Microsoft Graph API permissions are required for this script to work: + Policy.ReadWrite.ConditionalAccess + Policy.Read.All + Directory.Read.All + Agreement.Read.All + Application.Read.All + + Also, the user running this CMDlet (the one who signs in when the authentication pops up) must have the appropriate permissions in Azure AD (Global Admin, Security Admin, Conditional Access Admin, etc). + + .PARAMETER ClientID + Client ID for the Azure AD application with Conditional Access Microsoft Graph permissions. + + .PARAMETER ClientSecret + Client secret for the Azure AD application with Conditional Access Microsoft Graph permissions. + + .PARAMETER FilePath + The file path where the new JSON file will be created. Skip to use the current path. + + .PARAMETER TenantName + The .onmicrosoft.com domain inside your tenant. + + .INPUTS + None + + .OUTPUTS + JSON file with all Conditional Access policies. + + .NOTES + Author: Daniel Chronlund + GitHub: https://github.com/DanielChronlund/DCToolbox + Blog: https://danielchronlund.com/ + + .EXAMPLE + $Parameters = @{ + ClientID = '' + ClientSecret = '' + FilePath = 'C:\Temp\Conditional Access.json' + TenantName = "domain.onmicrosoft.com" + } + + Export-DCConditionalAccessPolicyDesign @Parameters + #> + + + # ----- [Initialisations] ----- # Script parameters. From f7b213322efa41e5bad3276c3416b8280e492a86 Mon Sep 17 00:00:00 2001 From: Adam Gell Date: Wed, 23 Dec 2020 17:19:52 -0500 Subject: [PATCH 3/7] Created-DCConditionalAccessPolicyDesignAsApp Corrected a typo for TenantName --- DCToolbox.psd1 | Bin 8912 -> 9104 bytes DCToolbox.psm1 | 149 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) diff --git a/DCToolbox.psd1 b/DCToolbox.psd1 index 1fa2428f27cfddd9a63db8d5ab2c915882d24928..3b2d28b0c19cd8aeb10cd2c8a97bfea0912c1bc3 100644 GIT binary patch delta 40 ncmccMI>CKIkm%%PB0ijs48;tN3e!+D^kmzP1u{vG=A+!W) diff --git a/DCToolbox.psm1 b/DCToolbox.psm1 index c705ccf..92fc789 100644 --- a/DCToolbox.psm1 +++ b/DCToolbox.psm1 @@ -1577,7 +1577,156 @@ function Import-DCConditionalAccessPolicyDesign { Write-Verbose -Verbose -Message "Done!" } +function Import-DCConditionalAccessPolicyDesignAsApp { + <# + .SYNOPSIS + Export all Conditional Access policies to JSON. + + .DESCRIPTION + This CMDlet uses Microsoft Graph to export all Conditional Access policies in the tenant to a JSON file. This JSON file can be used for backup, documentation or to deploy the same policies again with Import-DCConditionalAccessPolicyDesign. You can treat Conditional Access as code! + + Before running this CMDlet, you first need to register a new application in your Azure AD according to this article: + https://danielchronlund.com/2018/11/19/fetch-data-from-microsoft-graph-with-powershell-paging-support/ + + The permissions for this CMDlet must be application versus delegation. + + The following Microsoft Graph API permissions are required for this script to work: + Policy.ReadWrite.ConditionalAccess + Policy.Read.All + Directory.Read.All + Agreement.Read.All + Application.Read.All + + Also, the user running this CMDlet (the one who signs in when the authentication pops up) must have the appropriate permissions in Azure AD (Global Admin, Security Admin, Conditional Access Admin, etc). + + .PARAMETER ClientID + Client ID for the Azure AD application with Conditional Access Microsoft Graph permissions. + + .PARAMETER ClientSecret + Client secret for the Azure AD application with Conditional Access Microsoft Graph permissions. + + .PARAMETER FilePath + The file path of the JSON file containing your Conditional Access policies. + + .PARAMETER SkipReportOnlyMode + All Conditional Access policies created by this CMDlet will be set to report-only mode if you don't use this parameter. + + .PARAMETER DeleteAllExistingPolicies + WARNING: If you want to, you can delete all existing policies when deploying your new ones with -DeleteAllExistingPolicies, Use this parameter with causon and allways create a backup with Export-DCConditionalAccessPolicyDesign first!! + + .PARAMETER TenantName + The .onmicrosoft.com domain inside your tenant. + .INPUTS + JSON file containing your Conditional Access policies. + + .OUTPUTS + None + + .NOTES + Author: Daniel Chronlund + GitHub: https://github.com/DanielChronlund/DCToolbox + Blog: https://danielchronlund.com/ + + .EXAMPLE + $Parameters = @{ + ClientID = '' + ClientSecret = '' + FilePath = 'C:\Temp\Conditional Access.json' + SkipReportOnlyMode = $false + DeleteAllExistingPolicies = $false + TenantName = "domain.onmicrosoft.com" + } + + Import-DCConditionalAccessPolicyDesign @Parameters + #> + + + + # ----- [Initialisations] ----- + + # Script parameters. + param ( + [parameter(Mandatory = $true)] + [string]$ClientID, + + [parameter(Mandatory = $true)] + [string]$ClientSecret, + + [parameter(Mandatory = $true)] + [string]$FilePath, + + [parameter(Mandatory = $false)] + [switch]$SkipReportOnlyMode, + + [parameter(Mandatory = $false)] + [switch]$DeleteAllExistingPolicies, + + [parameter(Mandatory = $true)] + [string]$TenantName + ) + + + # Set Error Action - Possible choices: Stop, SilentlyContinue + $ErrorActionPreference = "Stop" + + + + # ----- [Execution] ----- + + # Authenticate to Microsoft Graph. + Write-Verbose -Verbose -Message "Connecting to Microsoft Graph..." + $AccessToken = Connect-DCMsGraphAsApplication -ClientID $ClientID -ClientSecret $ClientSecret -TenantName $TenantName + + + # Import policies from JSON file. + Write-Verbose -Verbose -Message "Importing JSON from '$FilePath'..." + $ConditionalAccessPolicies = Get-Content -Raw -Path $FilePath + + + # Modify enabled policies to report-only if not skipped with -SkipReportOnlyMode. + if (!($SkipReportOnlyMode)) { + Write-Verbose -Verbose -Message "Setting all new policys to report-only mode..." + $ConditionalAccessPolicies = $ConditionalAccessPolicies -replace '"enabled"', '"enabledForReportingButNotEnforced"' + } + + + # Delete all existing policies if -DeleteAllExistingPolicies is specified. + if ($DeleteAllExistingPolicies) { + Write-Verbose -Verbose -Message "Deleting all existing Conditional Access policies..." + $GraphUri = 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies' + $ExistingPolicies = Invoke-DCMsGraphQuery -AccessToken $AccessToken -GraphMethod 'GET' -GraphUri $GraphUri + + foreach ($Policy in $ExistingPolicies) { + Start-Sleep -Seconds 1 + $GraphUri = "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($Policy.id)" + + Invoke-DCMsGraphQuery -AccessToken $AccessToken -GraphMethod 'DELETE' -GraphUri $GraphUri | Out-Null + } + } + + + # URI for creating Conditional Access policies. + $GraphUri = 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies' + + $ConditionalAccessPolicies = $ConditionalAccessPolicies | ConvertFrom-Json + + foreach ($Policy in $ConditionalAccessPolicies) { + Start-Sleep -Seconds 1 + Write-Verbose -Verbose -Message "Creating '$($Policy.DisplayName)'..." + + try { + # Create new policies. + Invoke-DCMsGraphQuery -AccessToken $AccessToken -GraphMethod 'POST' -GraphUri $GraphUri -GraphBody ($Policy | ConvertTo-Json -Depth 10) | Out-Null + } + catch { + Write-Error -Message $_.Exception.Message -ErrorAction Continue + } + } + + + Write-Verbose -Verbose -Message "Done!" +} function New-DCConditionalAccessPolicyDesignReport { <# From a8eaed6ce0e729c7d77b70b007697b2e1b2be82b Mon Sep 17 00:00:00 2001 From: Adam Gell Date: Wed, 23 Dec 2020 17:21:05 -0500 Subject: [PATCH 4/7] modified Readme --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 5b5a9f7..83a1716 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,18 @@ This CMDlet uses Microsoft Graph to export all Conditional Access policies in th This CMDlet uses Microsoft Graph to automatically create Conditional Access policies from a JSON file. The JSON file can be created from existing policies with Export-DCConditionalAccessPolicyDesign or manually by following the syntax described in the Microsoft Graph documentation. +

Export-DCConditionalAccessPolicyDesignAsApp

+ +This CMDlet uses Microsoft Graph to export all Conditional Access policies in the tenant to a JSON file. This JSON file can be used for backup, documentation or to deploy the same policies again with Import-DCConditionalAccessPolicyDesign. + +This CMDlet is the same as Export-DCConditionalAccessPolicyDesign just uses Application level permissions rather than delegated. + +

Import-DCConditionalAccessPolicyDesignAsApp

+ +This CMDlet uses Microsoft Graph to automatically create Conditional Access policies from a JSON file. The JSON file can be created from existing policies with Export-DCConditionalAccessPolicyDesign or manually by following the syntax described in the Microsoft Graph documentation. + +This CMDlet is the same as Import-DCConditionalAccessPolicyDesign just uses Application level permissions rather than delegated. +

New-DCConditionalAccessPolicyDesignReport

Automatically generate an Excel report containing your current Conditional Access policy design. From ce26d5715e73b93071cd69ad5f7191f2cb41209d Mon Sep 17 00:00:00 2001 From: Adam Gell Date: Wed, 23 Dec 2020 17:23:26 -0500 Subject: [PATCH 5/7] added author infromation --- DCToolbox.psm1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/DCToolbox.psm1 b/DCToolbox.psm1 index 92fc789..78c2bdc 100644 --- a/DCToolbox.psm1 +++ b/DCToolbox.psm1 @@ -1361,7 +1361,10 @@ function Export-DCConditionalAccessPolicyDesignAsApp { Author: Daniel Chronlund GitHub: https://github.com/DanielChronlund/DCToolbox Blog: https://danielchronlund.com/ - + + Author: Adam Gell + GitHub: https://github.com/adamgell + .EXAMPLE $Parameters = @{ ClientID = '' @@ -1627,6 +1630,9 @@ function Import-DCConditionalAccessPolicyDesignAsApp { Author: Daniel Chronlund GitHub: https://github.com/DanielChronlund/DCToolbox Blog: https://danielchronlund.com/ + + Author: Adam Gell + GitHub: https://github.com/adamgell .EXAMPLE $Parameters = @{ From b9bb050d5993319ee629de13139c884ac9a76b51 Mon Sep 17 00:00:00 2001 From: Adam Gell Date: Wed, 23 Dec 2020 17:26:23 -0500 Subject: [PATCH 6/7] added help info for the new cmdlets. --- DCToolbox.psm1 | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/DCToolbox.psm1 b/DCToolbox.psm1 index 78c2bdc..2203d1b 100644 --- a/DCToolbox.psm1 +++ b/DCToolbox.psm1 @@ -230,6 +230,30 @@ $Parameters = @{ Import-DCConditionalAccessPolicyDesign @Parameters +# Export your Conditional Access policies to a JSON file for backup. +$Parameters = @{ + ClientID = '' + ClientSecret = '' + FilePath = 'C:\Temp\Conditional Access Backup.json' + TenantName = 'example.onmicrosoft.com' +} + +Export-DCConditionalAccessPolicyDesignAsApp @Parameters + + +# Import Conditional Access policies from a JSON file exported by Export-DCConditionalAccessPolicyDesign. +$Parameters = @{ + ClientID = '' + ClientSecret = '' + FilePath = 'C:\Temp\Conditional Access Backup.json' + SkipReportOnlyMode = $false + DeleteAllExistingPolicies = $false + TenantName = 'example.onmicrosoft.com' +} + +Import-DCConditionalAccessPolicyDesignAsApp @Parameters + + # Export Conditional Access policy design report to Excel. $Parameters = @{ @@ -1364,13 +1388,13 @@ function Export-DCConditionalAccessPolicyDesignAsApp { Author: Adam Gell GitHub: https://github.com/adamgell - + .EXAMPLE $Parameters = @{ ClientID = '' ClientSecret = '' FilePath = 'C:\Temp\Conditional Access.json' - TenantName = "domain.onmicrosoft.com" + TenantName = "example.onmicrosoft.com" } Export-DCConditionalAccessPolicyDesign @Parameters @@ -1641,7 +1665,7 @@ function Import-DCConditionalAccessPolicyDesignAsApp { FilePath = 'C:\Temp\Conditional Access.json' SkipReportOnlyMode = $false DeleteAllExistingPolicies = $false - TenantName = "domain.onmicrosoft.com" + TenantName = "example.onmicrosoft.com" } Import-DCConditionalAccessPolicyDesign @Parameters From 8f09c6b80f9bc394a73c3607919caba7d68ebe62 Mon Sep 17 00:00:00 2001 From: Adam Gell Date: Wed, 23 Dec 2020 17:37:17 -0500 Subject: [PATCH 7/7] removed vanity. --- DCToolbox.psm1 | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/DCToolbox.psm1 b/DCToolbox.psm1 index 2203d1b..bfb1d88 100644 --- a/DCToolbox.psm1 +++ b/DCToolbox.psm1 @@ -1386,9 +1386,6 @@ function Export-DCConditionalAccessPolicyDesignAsApp { GitHub: https://github.com/DanielChronlund/DCToolbox Blog: https://danielchronlund.com/ - Author: Adam Gell - GitHub: https://github.com/adamgell - .EXAMPLE $Parameters = @{ ClientID = '' @@ -1654,9 +1651,6 @@ function Import-DCConditionalAccessPolicyDesignAsApp { Author: Daniel Chronlund GitHub: https://github.com/DanielChronlund/DCToolbox Blog: https://danielchronlund.com/ - - Author: Adam Gell - GitHub: https://github.com/adamgell .EXAMPLE $Parameters = @{ @@ -2383,3 +2377,4 @@ function New-DCConditionalAccessAssignmentReport { # ----- [End] ----- } +