From 7fb1aecf0a3c21dc5c312c357986a5b50db50961 Mon Sep 17 00:00:00 2001 From: Yorick Kuijs Date: Tue, 14 Jan 2025 12:54:53 +0100 Subject: [PATCH 1/7] Updated documentation and AADNamedLocationPolicy --- .../MSFT_AADNamedLocationPolicy.psm1 | 36 ++++++------------- .../authentication-and-permissions.md | 10 +++++- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 index abd58c3f0c..1563d9a530 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 @@ -104,11 +104,12 @@ function Get-TargetResource { Write-Verbose -Message "Could not retrieve AAD Named Location by ID {$Id}" } + if ($null -eq $NamedLocation) { try { - $NamedLocation = Get-MgBetaIdentityConditionalAccessNamedLocation -ErrorAction SilentlyContinue | Where-Object -FilterScript { $_.DisplayName -eq $DisplayName } + $NamedLocation = Get-MgBetaIdentityConditionalAccessNamedLocation -ErrorAction Stop | Where-Object -FilterScript { $_.DisplayName -eq $DisplayName } if ($NamedLocation.Length -gt 1) { throw "More than one instance of a Named Location Policy with name {$DisplayName} was found. Please provide the ID parameter." @@ -125,8 +126,10 @@ function Get-TargetResource return $nullReturn } } + if ($null -eq $NamedLocation) { + Write-Verbose "No existing AAD Named Location found with DisplayName {$DisplayName}" return $nullReturn } else @@ -254,26 +257,6 @@ function Set-TargetResource Add-M365DSCTelemetryEvent -Data $data #endregion - try - { - if ($Id) - { - $NamedLocation = Get-MgBetaIdentityConditionalAccessNamedLocation -NamedLocationId $Id -ErrorAction Stop - } - } - catch - { - Write-Verbose -Message "Could not retrieve AAD Named Location by ID {$Id}" - } - if ($null -eq $NamedLocation) - { - $NamedLocation = Get-MgBetaIdentityConditionalAccessNamedLocation -ErrorAction SilentlyContinue | Where-Object -FilterScript { $_.DisplayName -eq $DisplayName } - if ($NamedLocation.Length -gt 1) - { - throw "More than one instance of a Named Location Policy with name {$DisplayName} was found. Please provide the ID parameter." - } - } - $currentAADNamedLocation = Get-TargetResource @PSBoundParameters $desiredValues = @{ @@ -312,24 +295,25 @@ function Set-TargetResource if ($Ensure -eq 'Present' -and $currentAADNamedLocation.Ensure -eq 'Absent') { $VerboseAttributes = ($desiredValues | Out-String) - Write-Verbose -Message "Creating New AAD Named Location {$Displayname)} with attributes: $VerboseAttributes" + Write-Verbose -Message "Creating New AAD Named Location {$Displayname} with attributes: $VerboseAttributes" + $JSONValue = ConvertTo-Json $desiredValues | Out-String Write-Verbose -Message "JSON: $JSONValue" + $APIUrl = (Get-MSCloudLoginConnectionProfile -Workload MicrosoftGraph).ResourceUrl + 'v1.0/identity/conditionalAccess/namedLocations' Invoke-MgGraphRequest -Method POST ` -Uri $APIUrl ` -Body $JSONValue | Out-Null } # Named Location should exist and will be configured to desired state - elseif ($Ensure -eq 'Present' -and $CurrentAADNamedLocation.Ensure -eq 'Present') + elseif ($Ensure -eq 'Present' -and $currentAADNamedLocation.Ensure -eq 'Present') { $VerboseAttributes = ($desiredValues | Out-String) - Write-Verbose -Message "Updating existing AAD Named Location {$Displayname)} with attributes: $VerboseAttributes" + Write-Verbose -Message "Updating existing AAD Named Location {$Displayname} with attributes: $VerboseAttributes" - $VerboseAttributes = ($desiredValues | Out-String) - Write-Verbose -Message "Updating AAD Named Location {$Displayname)} with attributes: $VerboseAttributes" $JSONValue = ConvertTo-Json $desiredValues | Out-String Write-Verbose -Message "JSON: $JSONValue" + $APIUrl = (Get-MSCloudLoginConnectionProfile -Workload MicrosoftGraph).ResourceUrl + "v1.0/identity/conditionalAccess/namedLocations/$($currentAADNamedLocation.Id)" Invoke-MgGraphRequest -Method PATCH ` -Uri $APIUrl ` diff --git a/docs/docs/user-guide/get-started/authentication-and-permissions.md b/docs/docs/user-guide/get-started/authentication-and-permissions.md index 8c7e4c5262..994b15f4dd 100644 --- a/docs/docs/user-guide/get-started/authentication-and-permissions.md +++ b/docs/docs/user-guide/get-started/authentication-and-permissions.md @@ -163,6 +163,8 @@ Use the "Create a new app registration in Azure AD yourself and grant the correct permissions to this app. The documentation on this website for each of the SharePoint Online resources list the permissions needed for the resource. +> Note: Make sure your app has the "Allow Public Client Flows" setting set to "Yes". This is required for SharePoint. More information can be found here + As an alternative, you can use the "Register-PnPAzureADApp" cmdlet to have PnP PowerShell create the app registration for you and grant the correct permissions. ### Using Application Secret @@ -205,7 +207,9 @@ Get-M365DSCCompiledPermissionList -ResourceNameList @('EXOAcceptedDomain') Then make sure your service account is a member of the specified Role Group or has been granted the required roles. -**NOTE:** There are resources, like the EXOAddressList which roles by default are not granted to any of the default role groups. Make sure you grant these permissions correctly before using them. +> **NOTE:** There are resources, like the EXOAddressList which roles by default are not granted to any of the default role groups. Make sure you grant these permissions correctly before using them. + +When using service principals to authenticate against Exchange, make sure your service principal is created using these instructions. ## Security and Compliance Center Permissions @@ -353,6 +357,10 @@ From the Export-M365DSCConfiguration GUI the following fields should be used: ![Export using Certificate Path](/Images/CertPath.png){ align=center width=500 } +## Teams Permissions + +When using Service Principals to authenticate against Teams, you have to make sure the correct permissions are configured. Besides the permissions specified in the resource documentation, the service principal also needs to get added to the Teams Administrator role in Entra ID. For more information on App-Only authentication with Teams, check here. + ## Using Authentication in DSC configurations See the next chapter to see how to use the Authentication options in DSC configurations From 21d46f94c2289c488aa0c917805ca6c28b472785 Mon Sep 17 00:00:00 2001 From: Yorick Kuijs Date: Tue, 14 Jan 2025 12:54:53 +0100 Subject: [PATCH 2/7] Updated documentation and AADNamedLocationPolicy --- .../MSFT_AADNamedLocationPolicy.psm1 | 36 ++++++------------- .../authentication-and-permissions.md | 10 +++++- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 index abd58c3f0c..1563d9a530 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 @@ -104,11 +104,12 @@ function Get-TargetResource { Write-Verbose -Message "Could not retrieve AAD Named Location by ID {$Id}" } + if ($null -eq $NamedLocation) { try { - $NamedLocation = Get-MgBetaIdentityConditionalAccessNamedLocation -ErrorAction SilentlyContinue | Where-Object -FilterScript { $_.DisplayName -eq $DisplayName } + $NamedLocation = Get-MgBetaIdentityConditionalAccessNamedLocation -ErrorAction Stop | Where-Object -FilterScript { $_.DisplayName -eq $DisplayName } if ($NamedLocation.Length -gt 1) { throw "More than one instance of a Named Location Policy with name {$DisplayName} was found. Please provide the ID parameter." @@ -125,8 +126,10 @@ function Get-TargetResource return $nullReturn } } + if ($null -eq $NamedLocation) { + Write-Verbose "No existing AAD Named Location found with DisplayName {$DisplayName}" return $nullReturn } else @@ -254,26 +257,6 @@ function Set-TargetResource Add-M365DSCTelemetryEvent -Data $data #endregion - try - { - if ($Id) - { - $NamedLocation = Get-MgBetaIdentityConditionalAccessNamedLocation -NamedLocationId $Id -ErrorAction Stop - } - } - catch - { - Write-Verbose -Message "Could not retrieve AAD Named Location by ID {$Id}" - } - if ($null -eq $NamedLocation) - { - $NamedLocation = Get-MgBetaIdentityConditionalAccessNamedLocation -ErrorAction SilentlyContinue | Where-Object -FilterScript { $_.DisplayName -eq $DisplayName } - if ($NamedLocation.Length -gt 1) - { - throw "More than one instance of a Named Location Policy with name {$DisplayName} was found. Please provide the ID parameter." - } - } - $currentAADNamedLocation = Get-TargetResource @PSBoundParameters $desiredValues = @{ @@ -312,24 +295,25 @@ function Set-TargetResource if ($Ensure -eq 'Present' -and $currentAADNamedLocation.Ensure -eq 'Absent') { $VerboseAttributes = ($desiredValues | Out-String) - Write-Verbose -Message "Creating New AAD Named Location {$Displayname)} with attributes: $VerboseAttributes" + Write-Verbose -Message "Creating New AAD Named Location {$Displayname} with attributes: $VerboseAttributes" + $JSONValue = ConvertTo-Json $desiredValues | Out-String Write-Verbose -Message "JSON: $JSONValue" + $APIUrl = (Get-MSCloudLoginConnectionProfile -Workload MicrosoftGraph).ResourceUrl + 'v1.0/identity/conditionalAccess/namedLocations' Invoke-MgGraphRequest -Method POST ` -Uri $APIUrl ` -Body $JSONValue | Out-Null } # Named Location should exist and will be configured to desired state - elseif ($Ensure -eq 'Present' -and $CurrentAADNamedLocation.Ensure -eq 'Present') + elseif ($Ensure -eq 'Present' -and $currentAADNamedLocation.Ensure -eq 'Present') { $VerboseAttributes = ($desiredValues | Out-String) - Write-Verbose -Message "Updating existing AAD Named Location {$Displayname)} with attributes: $VerboseAttributes" + Write-Verbose -Message "Updating existing AAD Named Location {$Displayname} with attributes: $VerboseAttributes" - $VerboseAttributes = ($desiredValues | Out-String) - Write-Verbose -Message "Updating AAD Named Location {$Displayname)} with attributes: $VerboseAttributes" $JSONValue = ConvertTo-Json $desiredValues | Out-String Write-Verbose -Message "JSON: $JSONValue" + $APIUrl = (Get-MSCloudLoginConnectionProfile -Workload MicrosoftGraph).ResourceUrl + "v1.0/identity/conditionalAccess/namedLocations/$($currentAADNamedLocation.Id)" Invoke-MgGraphRequest -Method PATCH ` -Uri $APIUrl ` diff --git a/docs/docs/user-guide/get-started/authentication-and-permissions.md b/docs/docs/user-guide/get-started/authentication-and-permissions.md index 8c7e4c5262..994b15f4dd 100644 --- a/docs/docs/user-guide/get-started/authentication-and-permissions.md +++ b/docs/docs/user-guide/get-started/authentication-and-permissions.md @@ -163,6 +163,8 @@ Use the "Create a new app registration in Azure AD yourself and grant the correct permissions to this app. The documentation on this website for each of the SharePoint Online resources list the permissions needed for the resource. +> Note: Make sure your app has the "Allow Public Client Flows" setting set to "Yes". This is required for SharePoint. More information can be found here + As an alternative, you can use the "Register-PnPAzureADApp" cmdlet to have PnP PowerShell create the app registration for you and grant the correct permissions. ### Using Application Secret @@ -205,7 +207,9 @@ Get-M365DSCCompiledPermissionList -ResourceNameList @('EXOAcceptedDomain') Then make sure your service account is a member of the specified Role Group or has been granted the required roles. -**NOTE:** There are resources, like the EXOAddressList which roles by default are not granted to any of the default role groups. Make sure you grant these permissions correctly before using them. +> **NOTE:** There are resources, like the EXOAddressList which roles by default are not granted to any of the default role groups. Make sure you grant these permissions correctly before using them. + +When using service principals to authenticate against Exchange, make sure your service principal is created using these instructions. ## Security and Compliance Center Permissions @@ -353,6 +357,10 @@ From the Export-M365DSCConfiguration GUI the following fields should be used: ![Export using Certificate Path](/Images/CertPath.png){ align=center width=500 } +## Teams Permissions + +When using Service Principals to authenticate against Teams, you have to make sure the correct permissions are configured. Besides the permissions specified in the resource documentation, the service principal also needs to get added to the Teams Administrator role in Entra ID. For more information on App-Only authentication with Teams, check here. + ## Using Authentication in DSC configurations See the next chapter to see how to use the Authentication options in DSC configurations From d1a62687f99c49b72f8358c1e69eba61d207a00e Mon Sep 17 00:00:00 2001 From: Yorick Kuijs Date: Tue, 14 Jan 2025 13:09:00 +0100 Subject: [PATCH 3/7] Updated changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b107f47be..3bb1a2c4f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ * AADGroupEligibilitySchedule * New resource for Privileged Identity Management (PIM) for Groups +* AADNamingLocationPolicy + * Improved logging and fixed issue that caused creation of duplicate + locations with same name. * EXOSmtpDaneInbound * initial release * IntuneVPNConfigurationPolicyAndroidWork From ebe9c9050f9c637462be5292ac3661e6f1ba2c74 Mon Sep 17 00:00:00 2001 From: Yorick Kuijs Date: Tue, 14 Jan 2025 13:12:35 +0100 Subject: [PATCH 4/7] Corrected some typos --- .../get-started/authentication-and-permissions.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/user-guide/get-started/authentication-and-permissions.md b/docs/docs/user-guide/get-started/authentication-and-permissions.md index 994b15f4dd..2ae6458436 100644 --- a/docs/docs/user-guide/get-started/authentication-and-permissions.md +++ b/docs/docs/user-guide/get-started/authentication-and-permissions.md @@ -11,8 +11,8 @@ Currently, each Microsoft 365 workload can support a different combination of au **Important**: The recommendation is to use Service Principal whenever possible because: -- Service principals offers the most granular levels of security and do not introduce the risk of having to send high privileged credentials across the wire to authenticate. -- Since Desired State Configuration is an unattended process, the use of Multi Factor Authentication for user credentials is not supported by Microsoft365DSC. +- Service principals offer the most granular levels of security and do not introduce the risk of having to send high privileged credentials across the wire to authenticate. +- Since Desired State Configuration is an unattended process, the use of Multi-Factor Authentication for user credentials is not supported by Microsoft365DSC. - ***Note:*** The only exception here is creating an Export of an existing tenant. Most often this is an interactive process where the ask for a second factor is possible. ## Authentication Methods @@ -66,7 +66,7 @@ Most components of the Microsoft365DSC solution are using the Microsoft Graph Po This option is using an AzureAD app in the background to call the Graph API (named "Microsoft Graph PowerShell"). However the effective permissions will be the intersection of the delegated permissions **and** the user privileges. By default, the Graph app has no permissions meaning it can't access anything and therefore won't work. You have to grant these permissions to the app before using them. Consent for these permissions can be given by the user himself or by an admin for all users in the tenant. - For example: If your account only has permissions on three SharePoint sites, only these sites can be retrieved. Even when the AzureAD app has Sites.FullControll.All permissions granted. + For example: If your account only has permissions on three SharePoint sites, only these sites can be retrieved. Even when the AzureAD app has Sites.FullControl.All permissions granted.
![Using the Graph API with Delegated Permissions and the default App Registration](/Images/PermissionsGraphDelegatedApp.png) @@ -245,7 +245,7 @@ Add-RoleGroupMember -Identity eDiscoveryManager -Member $SPN.ObjectId
  • Add the Service Principal as a case admin: -

    The Service Principal requires one last permission in order to be able to retrieve values from the Security and COmpliance center cmdlets. Run the following PowerShell command to add it as a case admin:

    +

    The Service Principal requires one last permission in order to be able to retrieve values from the Security and Compliance center cmdlets. Run the following PowerShell command to add it as a case admin:

    Grant the eDiscovery Case Admin role to your service principal From 2c150cf55037f4da6923ad4744752cc80e49d5bc Mon Sep 17 00:00:00 2001 From: Yorick Kuijs Date: Tue, 14 Jan 2025 13:41:11 +0100 Subject: [PATCH 5/7] Fixing issue #5625 --- CHANGELOG.md | 3 +++ .../MSFT_AADAuthenticationRequirement.psm1 | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bb1a2c4f4..d236c80740 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ # UNRELEASED +* AADAuthenticationRequirement + * Filtered guests from the export, to prevent errors during export + FIXES [#5625](https://github.com/microsoft/Microsoft365DSC/issues/5625) * AADGroupEligibilitySchedule * New resource for Privileged Identity Management (PIM) for Groups * AADNamingLocationPolicy diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationRequirement/MSFT_AADAuthenticationRequirement.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationRequirement/MSFT_AADAuthenticationRequirement.psm1 index e8e8fae959..c32a0c931c 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationRequirement/MSFT_AADAuthenticationRequirement.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationRequirement/MSFT_AADAuthenticationRequirement.psm1 @@ -310,7 +310,7 @@ function Export-TargetResource try { - [array]$getValue = Get-MgUser -ErrorAction Stop -All | Where-Object -FilterScript { $null -ne $_.Id } + [array]$getValue = Get-MgUser -Filter "userType eq 'member'" -All -ErrorAction Stop | Where-Object -FilterScript { $null -ne $_.Id } $i = 1 $dscContent = '' From 72bc54fdb4687518e8c6412a92a4537361eb1387 Mon Sep 17 00:00:00 2001 From: Yorick Kuijs Date: Tue, 14 Jan 2025 13:49:06 +0100 Subject: [PATCH 6/7] Fixed unit tests --- .../MSFT_AADNamedLocationPolicy.psm1 | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 index 1563d9a530..af69cfc90b 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 @@ -257,6 +257,26 @@ function Set-TargetResource Add-M365DSCTelemetryEvent -Data $data #endregion + try + { + if ($Id) + { + $NamedLocation = Get-MgBetaIdentityConditionalAccessNamedLocation -NamedLocationId $Id -ErrorAction Stop + } + } + catch + { + Write-Verbose -Message "Could not retrieve AAD Named Location by ID {$Id}" + } + if ($null -eq $NamedLocation) + { + $NamedLocation = Get-MgBetaIdentityConditionalAccessNamedLocation -ErrorAction SilentlyContinue | Where-Object -FilterScript { $_.DisplayName -eq $DisplayName } + if ($NamedLocation.Length -gt 1) + { + throw "More than one instance of a Named Location Policy with name {$DisplayName} was found. Please provide the ID parameter." + } + } + $currentAADNamedLocation = Get-TargetResource @PSBoundParameters $desiredValues = @{ From 99d9a97fad84940a2df116a61d6ccc8ef93d2c13 Mon Sep 17 00:00:00 2001 From: Yorick Kuijs Date: Tue, 14 Jan 2025 14:21:22 +0100 Subject: [PATCH 7/7] Fixed one more scenario in AuthRequirement resource --- .../MSFT_AADAuthenticationRequirement.psm1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationRequirement/MSFT_AADAuthenticationRequirement.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationRequirement/MSFT_AADAuthenticationRequirement.psm1 index c32a0c931c..9e56d5d973 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationRequirement/MSFT_AADAuthenticationRequirement.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAuthenticationRequirement/MSFT_AADAuthenticationRequirement.psm1 @@ -310,7 +310,9 @@ function Export-TargetResource try { - [array]$getValue = Get-MgUser -Filter "userType eq 'member'" -All -ErrorAction Stop | Where-Object -FilterScript { $null -ne $_.Id } + [array]$getValue = Get-MgUser -Filter "userType eq 'member'" -All -ErrorAction Stop | Where-Object -FilterScript { + $null -ne $_.Id -and $_.UserPrincipalName -notlike "*#EXT#*" + } $i = 1 $dscContent = ''