From 7e41b26897d4d10c5977736d7afdaf156e4dadc0 Mon Sep 17 00:00:00 2001 From: Peter Abele Date: Fri, 15 Jan 2021 20:04:17 +0100 Subject: [PATCH 1/6] Creates AADNamedLocationPolicy Resource --- .../MSFT_AADNamedLocationPolicy.psm1 | 399 ++++++++++++++++++ .../MSFT_AADNamedLocationPolicy.schema.mof | 17 + .../MSFT_AADNamedLocationPolicy/readme.md | 16 + 3 files changed, 432 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/readme.md diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 new file mode 100644 index 0000000000..cb4d1500df --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 @@ -0,0 +1,399 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet('#microsoft.graph.countryNamedLocation', '#microsoft.graph.ipNamedLocation')] + [System.String] + $OdataType, + + [Parameter(Mandatory = $true)] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String[]] + $IpRanges, + + [Parameter()] + [System.Boolean] + $IsTrusted, + + [Parameter()] + [System.String[]] + $CountriesAndRegions, + + [Parameter()] + [System.Boolean] + $IncludeUnknownCountriesAndRegions, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $GlobalAdminAccount, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + + Write-Verbose -Message "Getting configuration of AAD Named Location" + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace("MSFT_", "") + $data = [System.Collections.Generic.Dictionary[[String], [String]]]::new() + $data.Add("Resource", $ResourceName) + $data.Add("Method", $MyInvocation.MyCommand) + $data.Add("Principal", $GlobalAdminAccount.UserName) + $data.Add("TenantId", $TenantId) + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $ConnectionMode = New-M365DSCConnection -Platform 'AzureAD' -InboundParameters $PSBoundParameters + + try + { + $nullReturn = $PSBoundParameters + $nullReturn.Ensure = "Absent" + try + { + if ($null -ne $Id) + { + $NamedLocation = Get-AzureADMSNamedLocationPolicy -PolicyId $Id + } + } + catch + { + Write-Verbose -Message "Could not retrieve AAD Named Location by ID {$Id}" + } + if ($null -eq $NamedLocation) + { + try + { + $NamedLocation = Get-AzureADMSNamedLocationPolicy -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -eq $DisplayName } + } + catch + { + Write-Verbose -Message $_ + Add-M365DSCEvent -Message $_ -EntryType 'Error' ` + -EventID 1 -Source $($MyInvocation.MyCommand.Source) + } + } + if ($null -eq $NamedLocation) + { + return $nullReturn + } + else + { + Write-Verbose "Found existing AAD Named Location {$($NamedLocation.DisplayName)}" + $Result = @{ + OdataType = $NamedLocation.OdataType + Id = $NamedLocation.Id + DisplayName = $NamedLocation.DisplayName + IpRanges = $NamedLocation.IpRanges.CidrAddress + IsTrusted = $NamedLocation.IsTrusted + CountriesAndRegions = [String[]]$NamedLocation.CountriesAndRegions + IncludeUnknownCountriesAndRegions = $NamedLocation.IncludeUnknownCountriesAndRegions + Ensure = "Present" + GlobalAdminAccount = $GlobalAdminAccount + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + + Write-Verbose -Message "Get-TargetResource Result: `n $(Convert-M365DscHashtableToString -Hashtable $result)" + return $result + } + } + catch + { + Write-Verbose -Message $_ + Add-M365DSCEvent -Message $_ -EntryType 'Error' ` + -EventID 1 -Source $($MyInvocation.MyCommand.Source) + return $nullReturn + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet('#microsoft.graph.countryNamedLocation', '#microsoft.graph.ipNamedLocation')] + [System.String] + $OdataType, + + [Parameter(Mandatory = $true)] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String[]] + $IpRanges, + + [Parameter()] + [System.Boolean] + $IsTrusted, + + [Parameter()] + [System.String[]] + $CountriesAndRegions, + + [Parameter()] + [System.Boolean] + $IncludeUnknownCountriesAndRegions, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $GlobalAdminAccount, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + + Write-Verbose -Message "Setting configuration of AAD Named Location" + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace("MSFT_", "") + $data = [System.Collections.Generic.Dictionary[[String], [String]]]::new() + $data.Add("Resource", $ResourceName) + $data.Add("Method", $MyInvocation.MyCommand) + $data.Add("Principal", $GlobalAdminAccount.UserName) + $data.Add("TenantId", $TenantId) + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $currentAADNamedLocation = Get-TargetResource @PSBoundParameters + $currentParameters = $PSBoundParameters + $currentParameters.Remove("OdataType") | Out-Null + $currentParameters.Remove("ApplicationId") | Out-Null + $currentParameters.Remove("TenantId") | Out-Null + $currentParameters.Remove("CertificateThumbprint") | Out-Null + $currentParameters.Remove("GlobalAdminAccount") | Out-Null + $currentParameters.Remove("Ensure") | Out-Null + + # Named Location should exist but it doesn't + if ($Ensure -eq 'Present' -and $currentAADNamedLocation.Ensure -eq "Absent") + { + Write-Verbose -Message "Creating New AAD Named Location {$Displayname)}" + $currentParameters.Remove("Id") | Out-Null + New-AzureADMSNamedLocationPolicy @currentParameters + } + # Named Location should exist and will be configured to desired state + elseif ($Ensure -eq 'Present' -and $CurrentAADNamedLocation.Ensure -eq 'Present') + { + Write-Verbose -Message "Updating exisitng AAD Named Location {$Displayname)}" + $currentParameters["PolicyId"] = $currentAADNamedLocation.ID + $currentParameters.Remove("Id") | Out-Null + Set-AzureADMSNamedLocationPolicy @currentParameters + } + # Named Location exist but should not + elseif ($Ensure -eq 'Absent' -and $CurrentAADNamedLocation.Ensure -eq 'Present') + { + Write-Verbose -Message "Removing AAD Named Location {$Displayname)}" + Remove-AzureADMSNamedLocationPolicy -PolicyId $currentAADNamedLocation.ID + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet('#microsoft.graph.countryNamedLocation', '#microsoft.graph.ipNamedLocation')] + [System.String] + $OdataType, + + [Parameter(Mandatory = $true)] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String[]] + $IpRanges, + + [Parameter()] + [System.Boolean] + $IsTrusted, + + [Parameter()] + [System.String[]] + $CountriesAndRegions, + + [Parameter()] + [System.Boolean] + $IncludeUnknownCountriesAndRegions, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $GlobalAdminAccount, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + + ) + + Write-Verbose -Message "Testing configuration of AAD Named Location" + + $CurrentValues = Get-TargetResource @PSBoundParameters + + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" + + $ValuesToCheck = $PSBoundParameters + $ValuesToCheck.Remove('GlobalAdminAccount') | Out-Null + $ValuesToCheck.Remove("Id") | Out-Null + $ValuesToCheck.Remove("ApplicationId") | Out-Null + $ValuesToCheck.Remove("TenantId") | Out-Null + $ValuesToCheck.Remove("CertificateThumbprint") | Out-Null + + $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + Write-Verbose -Message "Test-TargetResource returned $TestResult" + + return $TestResult +} + +function Export-TargetResource +{ + [CmdletBinding()] + [OutputType([System.String])] + param + ( + [Parameter()] + [System.Management.Automation.PSCredential] + $GlobalAdminAccount, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace("MSFT_", "") + $data = [System.Collections.Generic.Dictionary[[String], [String]]]::new() + $data.Add("Resource", $ResourceName) + $data.Add("Method", $MyInvocation.MyCommand) + $data.Add("Principal", $GlobalAdminAccount.UserName) + $data.Add("TenantId", $TenantId) + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $dscContent = '' + $ConnectionMode = New-M365DSCConnection -Platform 'AzureAD' -InboundParameters $PSBoundParameters + $i = 1 + Write-Host "`r`n" -NoNewline + try + { + $AADNamedLocations = Get-AzureADMSNamedLocationPolicy -ErrorAction Stop + foreach ($AADNamedLocation in $AADNamedLocations) + { + Write-Host " |---[$i/$($AADNamedLocations.Count)] $($AADNamedLocation.DisplayName)" -NoNewline + $Params = @{ + GlobalAdminAccount = $GlobalAdminAccount + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + DisplayName = $AADNamedLocation.DisplayName + ID = $AADNamedLocation.ID + } + $Results = Get-TargetResource @Params + + # Fix quotes inside the Definition's JSON; + $NewDefinition = @() + foreach ($item in $Results.Definition) + { + $fixedContent = $item.Replace('"', '`"') + $NewDefinition += $fixedContent + } + $results.Definition = $NewDefinition + + if ($Results.Ensure -eq 'Present') + { + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $Results + $dscContent += Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $Results ` + -GlobalAdminAccount $GlobalAdminAccount + Write-Host $Global:M365DSCEmojiGreenCheckMark + $i++ + } + } + return $dscContent + } + catch + { + Write-Verbose -Message $_ + Add-M365DSCEvent -Message $_ -EntryType 'Error' ` + -EventID 1 -Source $($MyInvocation.MyCommand.Source) + return "" + } +} + +Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.schema.mof new file mode 100644 index 0000000000..ce5241e615 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.schema.mof @@ -0,0 +1,17 @@ +[ClassVersion("1.0.0.0"), FriendlyName("AADNamedLocationPolicy")] +class MSFT_AADNamedLocationPolicy : OMI_BaseResource +{ + [Write, Description("Odata Type of the Named Location. Accepted values: #microsoft.graph.countryNamedLocation, #microsoft.graph.ipNamedLocation")] string OdataType; + [Write, Description("ObjectID of the Named Location.")] String Id; + [Key, Description("DisplayName of the Named Location")] string DisplayName; + [Write, Description("IP ranges of the ipNamedLocation type Named Location.")] String IpRanges[]; + [Write, Description("Defines if the Named Location should be considered as trusted network")] Boolean IsTrusted; + [Write, Description("Country codes of the countryNamedLocation type Named Location.")] String CountriesAndRegions[]; + [Write, Description("Defines if unidentified countries should be considered as part of the countryNamedLocation type Named Location")] Boolean IncludeUnknownCountriesAndRegions; + [Write, Description("Specify if the Azure AD Named Location should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; + [Write, Description("Credentials of the Azure AD Admin"), EmbeddedInstance("MSFT_Credential")] string GlobalAdminAccount; + [Write, Description("Id of the Azure Active Directory application to authenticate with.")] String ApplicationId; + [Write, Description("Id of the Azure Active Directory tenant used for authentication.")] String TenantId; + [Write, Description("Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication.")] String CertificateThumbprint; + +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/readme.md new file mode 100644 index 0000000000..177c2fdca8 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/readme.md @@ -0,0 +1,16 @@ +# AAD Policy + +## Description + +This resource configures the Azure AD Named Locations + +## Azure AD Permissions + +To authenticate via Azure Active Directory, this resource required the following Application permissions: + +* **Automate** + * Policy.Read.All,Policy.ReadWrite.ApplicationConfiguration +* **Export** + * Policy.Read.All + +NOTE: All permisions listed above require admin consent. From bbfc25e47f78639de36f01edbb82b182897575e3 Mon Sep 17 00:00:00 2001 From: Peter Abele Date: Fri, 15 Jan 2021 20:25:36 +0100 Subject: [PATCH 2/6] Update MSFT_AADNamedLocationPolicy.psm1 --- .../MSFT_AADNamedLocationPolicy.psm1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 index cb4d1500df..3a89af175d 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 @@ -4,7 +4,7 @@ function Get-TargetResource [OutputType([System.Collections.Hashtable])] param ( - [Parameter(Mandatory = $true)] + [Parameter()] [ValidateSet('#microsoft.graph.countryNamedLocation', '#microsoft.graph.ipNamedLocation')] [System.String] $OdataType, @@ -136,7 +136,7 @@ function Set-TargetResource [CmdletBinding()] param ( - [Parameter(Mandatory = $true)] + [Parameter()] [ValidateSet('#microsoft.graph.countryNamedLocation', '#microsoft.graph.ipNamedLocation')] [System.String] $OdataType, @@ -236,7 +236,7 @@ function Test-TargetResource [OutputType([System.Boolean])] param ( - [Parameter(Mandatory = $true)] + [Parameter()] [ValidateSet('#microsoft.graph.countryNamedLocation', '#microsoft.graph.ipNamedLocation')] [System.String] $OdataType, From 3c148ad7d46c058aad9694fad612012a9ef9a34a Mon Sep 17 00:00:00 2001 From: Peter Abele Date: Fri, 15 Jan 2021 20:32:37 +0100 Subject: [PATCH 3/6] Update MSFT_AADNamedLocationPolicy.psm1 --- .../MSFT_AADNamedLocationPolicy.psm1 | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 index 3a89af175d..4437dabab1 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 @@ -363,15 +363,6 @@ function Export-TargetResource } $Results = Get-TargetResource @Params - # Fix quotes inside the Definition's JSON; - $NewDefinition = @() - foreach ($item in $Results.Definition) - { - $fixedContent = $item.Replace('"', '`"') - $NewDefinition += $fixedContent - } - $results.Definition = $NewDefinition - if ($Results.Ensure -eq 'Present') { $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` From 6bd86ad3c62af4474687205201d6b637c4d52503 Mon Sep 17 00:00:00 2001 From: Peter Abele Date: Tue, 19 Jan 2021 00:05:17 +0100 Subject: [PATCH 4/6] Ading tests --- .../MSFT_AADNamedLocationPolicy.psm1 | 7 +- .../1-ConfigureAADNamedLocationPolicy.ps1 | 42 ++++ ...oft365DSC.AADNamedLocationPolicy.Tests.ps1 | 226 ++++++++++++++++++ 3 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADNamedLocationPolicy/1-ConfigureAADNamedLocationPolicy.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADNamedLocationPolicy.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 index 4437dabab1..eba6f1e078 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 @@ -200,7 +200,6 @@ function Set-TargetResource $currentAADNamedLocation = Get-TargetResource @PSBoundParameters $currentParameters = $PSBoundParameters - $currentParameters.Remove("OdataType") | Out-Null $currentParameters.Remove("ApplicationId") | Out-Null $currentParameters.Remove("TenantId") | Out-Null $currentParameters.Remove("CertificateThumbprint") | Out-Null @@ -210,16 +209,18 @@ function Set-TargetResource # Named Location should exist but it doesn't if ($Ensure -eq 'Present' -and $currentAADNamedLocation.Ensure -eq "Absent") { - Write-Verbose -Message "Creating New AAD Named Location {$Displayname)}" $currentParameters.Remove("Id") | Out-Null + $VerboseAttributes = ($currentParameters | Out-String) + Write-Verbose -Message "Creating New AAD Named Location {$Displayname)} with attributes: $VerboseAttributes" New-AzureADMSNamedLocationPolicy @currentParameters } # Named Location should exist and will be configured to desired state elseif ($Ensure -eq 'Present' -and $CurrentAADNamedLocation.Ensure -eq 'Present') { - Write-Verbose -Message "Updating exisitng AAD Named Location {$Displayname)}" $currentParameters["PolicyId"] = $currentAADNamedLocation.ID $currentParameters.Remove("Id") | Out-Null + $VerboseAttributes = ($currentParameters | Out-String) + Write-Verbose -Message "Updating exisitng AAD Named Location {$Displayname)} with attributes: $VerboseAttributes" Set-AzureADMSNamedLocationPolicy @currentParameters } # Named Location exist but should not diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADNamedLocationPolicy/1-ConfigureAADNamedLocationPolicy.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADNamedLocationPolicy/1-ConfigureAADNamedLocationPolicy.ps1 new file mode 100644 index 0000000000..97fe2d1365 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADNamedLocationPolicy/1-ConfigureAADNamedLocationPolicy.ps1 @@ -0,0 +1,42 @@ +<# +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. +#> + +Configuration Example +{ + param( + [Parameter(Mandatory = $true)] + [PSCredential] + $credsGlobalAdmin + ) + Import-DscResource -ModuleName Microsoft365DSC + + node localhost + { + AADNamedLocationPolicy CompanyNetwork + { + ApplicationId = $ConfigurationData.NonNodeData.ApplicationId; + CertificateThumbprint = $ConfigurationData.NonNodeData.CertificateThumbprint; + DisplayName = "Company Network"; + Ensure = "Present"; + IpRanges = @("2.1.1.1/32", "1.2.2.2/32"); + IsTrusted = $True; + OdataType = "#microsoft.graph.ipNamedLocation"; + TenantId = $ConfigurationData.NonNodeData.TenantId; + GlobalAdminAccount = $credsGlobalAdmin; + } + AADNamedLocationPolicy AllowedCountries + { + ApplicationId = $ConfigurationData.NonNodeData.ApplicationId; + CertificateThumbprint = $ConfigurationData.NonNodeData.CertificateThumbprint; + CountriesAndRegions = @("GH", "AX", "DZ", "AI", "AM"); + DisplayName = "Allowed Countries"; + Ensure = "Present"; + IncludeUnknownCountriesAndRegions = $False; + OdataType = "#microsoft.graph.countryNamedLocation"; + TenantId = $ConfigurationData.NonNodeData.TenantId; + GlobalAdminAccount = $credsGlobalAdmin; + } + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADNamedLocationPolicy.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADNamedLocationPolicy.Tests.ps1 new file mode 100644 index 0000000000..4204339825 --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADNamedLocationPolicy.Tests.ps1 @@ -0,0 +1,226 @@ +[CmdletBinding()] +param( +) +$M365DSCTestFolder = Join-Path -Path $PSScriptRoot ` + -ChildPath "..\..\Unit" ` + -Resolve +$CmdletModule = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath "\Stubs\Microsoft365.psm1" ` + -Resolve) +$GenericStubPath = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath "\Stubs\Generic.psm1" ` + -Resolve) +Import-Module -Name (Join-Path -Path $M365DSCTestFolder ` + -ChildPath "\UnitTestHelper.psm1" ` + -Resolve) + +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource "AADNamedLocationPolicy" -GenericStubModule $GenericStubPath +Describe -Name $Global:DscHelper.DescribeHeader -Fixture { + InModuleScope -ModuleName $Global:DscHelper.ModuleName -ScriptBlock { + Invoke-Command -ScriptBlock $Global:DscHelper.InitializeScript -NoNewScope + BeforeAll { + $secpasswd = ConvertTo-SecureString "test@password1" -AsPlainText -Force + $GlobalAdminAccount = New-Object System.Management.Automation.PSCredential ("tenantadmin", $secpasswd) + + Mock -CommandName Update-M365DSCExportAuthenticationResults -MockWith { + return @{} + } + + Mock -CommandName Get-M365DSCExportContentForResource -MockWith { + + } + + Mock -CommandName Get-PSSession -MockWith { + + } + + Mock -CommandName Remove-PSSession -MockWith { + + } + + Mock -CommandName Set-AzureADMSNamedLocationPolicy -MockWith { + + } + + Mock -CommandName Remove-AzureADMSNamedLocationPolicy -MockWith { + + } + + Mock -CommandName New-AzureADMSNamedLocationPolicy -MockWith { + + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credential" + } + } + + # Test contexts + Context -Name "The Policy should exist but it does not" -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "Company Network" + Ensure = "Present" + IpRanges = @("2.1.1.1/32", "1.2.2.2/32") + IsTrusted = $True + OdataType = "#microsoft.graph.ipNamedLocation" + GlobalAdminAccount = $credsGlobalAdmin + } + + Mock -CommandName Get-AzureADMSNamedLocationPolicy -MockWith { + return $null + } + } + + It "Should return values from the get method" { + (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' + Should -Invoke -CommandName "Get-AzureADMSNamedLocationPolicy" -Exactly 2 + } + It 'Should return false from the test method' { + Test-TargetResource @testParams | Should -Be $false + } + It 'Should create the Policy from the set method' { + Set-TargetResource @testParams + Should -Invoke -CommandName "New-AzureADMSNamedLocationPolicy" -Exactly 1 + } + } + Context -Name "The Policy exists but it should not" -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "Company Network" + Ensure = "Absent" + IpRanges = @("2.1.1.1/32", "1.2.2.2/32") + IsTrusted = $True + OdataType = "#microsoft.graph.ipNamedLocation" + GlobalAdminAccount = $credsGlobalAdmin + } + + Mock -CommandName Get-AzureADMSNamedLocationPolicy -MockWith { + return @{ + DisplayName = "Company Network" + Id = "046956df-2367-4dd4-b7fd-c6175ec11cd5" + IpRanges = @(@{CidrAddress = "2.1.1.1/32" }, @{CidrAddress = "1.2.2.2/32" }) + IsTrusted = $True + OdataType = "#microsoft.graph.ipNamedLocation" + CountriesAndRegions = $null + IncludeUnknownCountriesAndRegions = $null + } + } + } + + It "Should return values from the get method" { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + Should -Invoke -CommandName "Get-AzureADMSNamedLocationPolicy" -Exactly 1 + } + + It 'Should return false from the test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should remove the app from the set method' { + Set-TargetResource @testParams + Should -Invoke -CommandName "Remove-AzureADMSNamedLocationPolicy" -Exactly 1 + } + } + + Context -Name "The Policy exists and values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "Company Network" + Ensure = "Present" + IpRanges = @("2.1.1.1/32", "1.2.2.2/32") + IsTrusted = $True + OdataType = "#microsoft.graph.ipNamedLocation" + GlobalAdminAccount = $credsGlobalAdmin + } + + Mock -CommandName Get-AzureADMSNamedLocationPolicy -MockWith { + return @{ + DisplayName = "Company Network" + Id = "046956df-2367-4dd4-b7fd-c6175ec11cd5" + IpRanges = @(@{CidrAddress = "2.1.1.1/32" }, @{CidrAddress = "1.2.2.2/32" }) + IsTrusted = $True + OdataType = "#microsoft.graph.ipNamedLocation" + CountriesAndRegions = $null + IncludeUnknownCountriesAndRegions = $null + } + } + } + + It "Should return Values from the get method" { + Get-TargetResource @testParams + Should -Invoke -CommandName "Get-AzureADMSNamedLocationPolicy" -Exactly 1 + } + + It 'Should return true from the test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name "Values are not in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "Company Network" + Ensure = "Present" + IpRanges = @("2.1.1.1/32", "1.2.2.2/32") + IsTrusted = $True + OdataType = "#microsoft.graph.ipNamedLocation" + GlobalAdminAccount = $credsGlobalAdmin + } + + Mock -CommandName Get-AzureADMSNamedLocationPolicy -MockWith { + return @{ + DisplayName = "Company Network" + Id = "046956df-2367-4dd4-b7fd-c6175ec11cd5" + IpRanges = @(@{CidrAddress = "1.1.1.1/32" }, @{CidrAddress = "2.2.2.2/32" }) + IsTrusted = $True + OdataType = "#microsoft.graph.ipNamedLocation" + CountriesAndRegions = $null + IncludeUnknownCountriesAndRegions = $null + } + } + } + + It "Should return values from the get method" { + Get-TargetResource @testParams + Should -Invoke -CommandName "Get-AzureADMSNamedLocationPolicy" -Exactly 1 + } + + It 'Should return false from the test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It "Should call the set method" { + Set-TargetResource @testParams + Should -Invoke -CommandName "Set-AzureADMSNamedLocationPolicy" -Exactly 1 + } + } + + Context -Name "ReverseDSC tests" -Fixture { + BeforeAll { + $testParams = @{ + GlobalAdminAccount = $GlobalAdminAccount + } + + Mock -CommandName Get-AzureADMSNamedLocationPolicy -MockWith { + return @{ + DisplayName = "Company Network" + Id = "046956df-2367-4dd4-b7fd-c6175ec11cd5" + IpRanges = @(@{CidrAddress = "2.1.1.1/32" }, @{CidrAddress = "1.2.2.2/32" }) + IsTrusted = $True + OdataType = "#microsoft.graph.ipNamedLocation" + CountriesAndRegions = $null + IncludeUnknownCountriesAndRegions = $null + } + } + } + + It "Should reverse engineer resource from the export method" { + Export-TargetResource @testParams + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope From 3477ee7223d8caa0f1ae0dce8307d76b73a1f44f Mon Sep 17 00:00:00 2001 From: Peter Abele Date: Wed, 20 Jan 2021 17:28:06 +0100 Subject: [PATCH 5/6] includes fixes after review --- .../MSFT_AADNamedLocationPolicy.psm1 | 5 ++--- .../MSFT_AADNamedLocationPolicy.schema.mof | 14 +++++++------- .../MSFT_AADNamedLocationPolicy/readme.md | 4 ++-- .../1-ConfigureAADNamedLocationPolicy.ps1 | 18 +++++++----------- 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 index eba6f1e078..108e74339e 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.psm1 @@ -87,7 +87,7 @@ function Get-TargetResource { try { - $NamedLocation = Get-AzureADMSNamedLocationPolicy -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -eq $DisplayName } + $NamedLocation = Get-AzureADMSNamedLocationPolicy -ErrorAction SilentlyContinue | Where-Object -FilterScript { $_.DisplayName -eq $DisplayName } } catch { @@ -220,7 +220,7 @@ function Set-TargetResource $currentParameters["PolicyId"] = $currentAADNamedLocation.ID $currentParameters.Remove("Id") | Out-Null $VerboseAttributes = ($currentParameters | Out-String) - Write-Verbose -Message "Updating exisitng AAD Named Location {$Displayname)} with attributes: $VerboseAttributes" + Write-Verbose -Message "Updating existing AAD Named Location {$Displayname)} with attributes: $VerboseAttributes" Set-AzureADMSNamedLocationPolicy @currentParameters } # Named Location exist but should not @@ -286,7 +286,6 @@ function Test-TargetResource [Parameter()] [System.String] $CertificateThumbprint - ) Write-Verbose -Message "Testing configuration of AAD Named Location" diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.schema.mof index ce5241e615..8c31b16f72 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/MSFT_AADNamedLocationPolicy.schema.mof @@ -1,13 +1,13 @@ [ClassVersion("1.0.0.0"), FriendlyName("AADNamedLocationPolicy")] class MSFT_AADNamedLocationPolicy : OMI_BaseResource { - [Write, Description("Odata Type of the Named Location. Accepted values: #microsoft.graph.countryNamedLocation, #microsoft.graph.ipNamedLocation")] string OdataType; - [Write, Description("ObjectID of the Named Location.")] String Id; - [Key, Description("DisplayName of the Named Location")] string DisplayName; - [Write, Description("IP ranges of the ipNamedLocation type Named Location.")] String IpRanges[]; - [Write, Description("Defines if the Named Location should be considered as trusted network")] Boolean IsTrusted; - [Write, Description("Country codes of the countryNamedLocation type Named Location.")] String CountriesAndRegions[]; - [Write, Description("Defines if unidentified countries should be considered as part of the countryNamedLocation type Named Location")] Boolean IncludeUnknownCountriesAndRegions; + [Write, Description("Specifies the Odata Type of a Named Location policy object in Azure Active Directory"), ValueMap{"#microsoft.graph.countryNamedLocation","#microsoft.graph.ipNamedLocation"}, Values{"#microsoft.graph.countryNamedLocation","#microsoft.graph.ipNamedLocation"}] string OdataType; + [Write, Description("Specifies the ID of a Named Location policy in Azure Active Directory.")] String Id; + [Key, Description("Specifies the Display Name of a Named Location policy in Azure Active Directory")] string DisplayName; + [Write, Description("Specifies the IP ranges of the Named Location policy in Azure Active Directory")] String IpRanges[]; + [Write, Description("Specifies the isTrusted value for the Named Location policy in Azure Active Directory")] Boolean IsTrusted; + [Write, Description("Specifies the countries and regions for the Named Location policy in Azure Active Directory")] String CountriesAndRegions[]; + [Write, Description("Specifies the includeUnknownCountriesAndRegions value for the Named Location policy in Azure Active Directory")] Boolean IncludeUnknownCountriesAndRegions; [Write, Description("Specify if the Azure AD Named Location should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [Write, Description("Credentials of the Azure AD Admin"), EmbeddedInstance("MSFT_Credential")] string GlobalAdminAccount; [Write, Description("Id of the Azure Active Directory application to authenticate with.")] String ApplicationId; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/readme.md index 177c2fdca8..5ca4b845a0 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/readme.md +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNamedLocationPolicy/readme.md @@ -1,8 +1,8 @@ -# AAD Policy +# AADNamedLocationPolicy ## Description -This resource configures the Azure AD Named Locations +This resource configures the Azure AD Named Location Policies in Azure Active Directory ## Azure AD Permissions diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADNamedLocationPolicy/1-ConfigureAADNamedLocationPolicy.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADNamedLocationPolicy/1-ConfigureAADNamedLocationPolicy.ps1 index 97fe2d1365..afd7d27942 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/AADNamedLocationPolicy/1-ConfigureAADNamedLocationPolicy.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/AADNamedLocationPolicy/1-ConfigureAADNamedLocationPolicy.ps1 @@ -16,20 +16,16 @@ Configuration Example { AADNamedLocationPolicy CompanyNetwork { - ApplicationId = $ConfigurationData.NonNodeData.ApplicationId; - CertificateThumbprint = $ConfigurationData.NonNodeData.CertificateThumbprint; - DisplayName = "Company Network"; - Ensure = "Present"; - IpRanges = @("2.1.1.1/32", "1.2.2.2/32"); - IsTrusted = $True; - OdataType = "#microsoft.graph.ipNamedLocation"; - TenantId = $ConfigurationData.NonNodeData.TenantId; - GlobalAdminAccount = $credsGlobalAdmin; + DisplayName = "Company Network"; + Ensure = "Present"; + IpRanges = @("2.1.1.1/32", "1.2.2.2/32"); + IsTrusted = $True; + OdataType = "#microsoft.graph.ipNamedLocation"; + TenantId = $ConfigurationData.NonNodeData.TenantId; + GlobalAdminAccount = $credsGlobalAdmin; } AADNamedLocationPolicy AllowedCountries { - ApplicationId = $ConfigurationData.NonNodeData.ApplicationId; - CertificateThumbprint = $ConfigurationData.NonNodeData.CertificateThumbprint; CountriesAndRegions = @("GH", "AX", "DZ", "AI", "AM"); DisplayName = "Allowed Countries"; Ensure = "Present"; From 49f52cbac5414c582b094fa589a02e587c63f357 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 27 Jan 2021 17:13:17 -0500 Subject: [PATCH 6/6] Release 1.21.127.1 --- CHANGELOG.md | 9 +++++++++ Modules/Microsoft365DSC/Microsoft365DSC.psd1 | 21 +++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ace44af07..3331a3505f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Change log for Microsoft365DSC +# 1.21.127.1 + +* AADNamedLocation + * Initial Release; +* DEPENDENCIES + * Updated PnP.PowerShell to version 1.2.0; + * Updated Microsoft.PowerApps.Administration.PowerShell to + 1.0.208; + # 1.21.120.1 * DEPENDENCIES diff --git a/Modules/Microsoft365DSC/Microsoft365DSC.psd1 b/Modules/Microsoft365DSC/Microsoft365DSC.psd1 index 9c7b4c362c..aa6f7a947f 100644 --- a/Modules/Microsoft365DSC/Microsoft365DSC.psd1 +++ b/Modules/Microsoft365DSC/Microsoft365DSC.psd1 @@ -3,7 +3,7 @@ # # Generated by: Microsoft Corporation # -# Generated on: 2021-01-20 +# Generated on: 2021-01-27 @{ @@ -11,7 +11,7 @@ # RootModule = '' # Version number of this module. - ModuleVersion = '1.21.120.1' + ModuleVersion = '1.21.127.1' # Supported PSEditions # CompatiblePSEditions = @() @@ -26,7 +26,7 @@ CompanyName = 'Microsoft Corporation' # Copyright statement for this module - Copyright = '(c) 2020 Microsoft Corporation. All rights reserved.' + Copyright = '(c) 2021 Microsoft Corporation. All rights reserved.' # Description of the functionality provided by this module Description = 'This DSC module is used to configure and monitor Microsoft tenants, including SharePoint Online, Exchange, Teams, etc.' @@ -89,7 +89,7 @@ }, @{ ModuleName = "Microsoft.PowerApps.Administration.PowerShell" - RequiredVersion = "2.0.104" + RequiredVersion = "2.0.108" }, @{ ModuleName = "MicrosoftTeams" @@ -101,7 +101,7 @@ }, @{ ModuleName = "PnP.PowerShell" - RequiredVersion = "1.1.0" + RequiredVersion = "1.2.0" }, @{ ModuleName = "ReverseDSC" @@ -180,10 +180,13 @@ IconUri = 'https://github.com/microsoft/Microsoft365DSC/blob/Dev/Modules/Microsoft365DSC/Dependencies/Images/Logo.png?raw=true' # ReleaseNotes of this module - ReleaseNotes = "* DEPENDENCIES - * Updated MSCloudLoginAssistant to version 1.0.45; - * Replaced the SharePointPnPPowerShellOnline dependency by the new - PnP.PowerShell core module;" + ReleaseNotes = " + * AADNamedLocation + * Initial Release; + * DEPENDENCIES + * Updated PnP.PowerShell to version 1.2.0; + * Updated Microsoft.PowerApps.Administration.PowerShell to + 1.0.208;" # Flag to indicate whether the module requires explicit user acceptance for install/update # RequireLicenseAcceptance = $false