From 62f1cabafdd6c5bfa51e1fc3346608d0cdca9825 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Fri, 11 Oct 2024 10:48:45 -0400 Subject: [PATCH 1/2] PPPowerAppPolicyUrlPatterns - Initial Release --- CHANGELOG.md | 2 + .../MSFT_PPPowerAppPolicyUrlPatterns.psm1 | 421 ++++++++++++++++++ ...SFT_PPPowerAppPolicyUrlPatterns.schema.mof | 23 + .../readme.md | 6 + .../settings.json | 20 + .../PPPowerAppPolicyUrlPatterns/1-Create.ps1 | 51 +++ .../PPPowerAppPolicyUrlPatterns/2-Update.ps1 | 51 +++ .../PPPowerAppPolicyUrlPatterns/3-Remove.ps1 | 34 ++ .../Microsoft365DSC/Modules/M365DSCUtil.psm1 | 13 +- ...5DSC.PPPowerAppPolicyUrlPatterns.Tests.ps1 | 241 ++++++++++ Tests/Unit/Stubs/Microsoft365.psm1 | 65 +++ 11 files changed, 918 insertions(+), 9 deletions(-) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/MSFT_PPPowerAppPolicyUrlPatterns.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/MSFT_PPPowerAppPolicyUrlPatterns.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/PPPowerAppPolicyUrlPatterns/1-Create.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/PPPowerAppPolicyUrlPatterns/2-Update.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/PPPowerAppPolicyUrlPatterns/3-Remove.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.PPPowerAppPolicyUrlPatterns.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 181e2038b5..b096d97f8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ * Initial release * PPAdminDLPPolicy * Initial release. +* PPPowerAppPolicyUrlPatterns + * Initial release. * M365DSCDRGUtil * Fixes an issue for the handling of skipped one-property elements in the Settings Catalog. FIXES [#5086](https://github.com/microsoft/Microsoft365DSC/issues/5086) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/MSFT_PPPowerAppPolicyUrlPatterns.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/MSFT_PPPowerAppPolicyUrlPatterns.psm1 new file mode 100644 index 0000000000..b99fc8179b --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/MSFT_PPPowerAppPolicyUrlPatterns.psm1 @@ -0,0 +1,421 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $PPTenantId, + + [Parameter(Mandatory = $true)] + [System.String] + $PolicyName, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $RuleSet, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + New-M365DSCConnection -Workload 'PowerPlatforms' ` + -InboundParameters $PSBoundParameters | Out-Null + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $nullResult = $PSBoundParameters + $nullResult.Ensure = 'Absent' + try + { + $policy = Get-AdminDlpPolicy | Where-Object -FilterScript {$_.DisplayName -eq $PolicyName} + + if ($null -eq $policy) + { + return $nullResult + } + + $rules = Get-PowerAppPolicyUrlPatterns -TenantID $PPTenantId ` + -PolicyName $($policy.PolicyName) + $RulesValue = @() + foreach ($rule in $rules.rules) + { + $RulesValue += @{ + order = $rule.order + customConnectorRuleClassification = $rule.customConnectorRuleClassification + pattern = $rule.pattern + } + } + + $results = @{ + PPTenantId = $PPTenantId + PolicyName = $PolicyName + RuleSet = $RulesValue + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + return [System.Collections.Hashtable] $results + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullResult + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $PPTenantId, + + [Parameter(Mandatory = $true)] + [System.String] + $PolicyName, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $RuleSet, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + New-M365DSCConnection -Workload 'PowerPlatforms' ` + -InboundParameters $PSBoundParameters | Out-Null + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $policy = Get-AdminDlpPolicy | Where-Object -FilterScript {$_.DisplayName -eq $PolicyName} + $policyNameValue = $policy.PolicyName + + # CREATE + if ($Ensure -eq 'Present') + { + $body = @{ + rules = @() + } + + foreach ($rule in $RuleSet) + { + $body.rules += @{ + order = $rule.order + customConnectorRuleClassification = $rule.customConnectorRuleClassification + pattern = $rule.pattern + } + } + $payload = $(ConvertTo-Json $body -Depth 9 -Compress) + Write-Verbose -Message "Setting new Url Patterns for Policy {$($PolicyNameValue)} with parameters:`r`n$payload" + + New-PowerAppPolicyUrlPatterns -TenantId $PPTenantId ` + -PolicyName $policyNameValue ` + -NewUrlPatterns $body ` + -Verbose + } + # REMOVE + elseif ($Ensure -eq 'Absent') + { + Write-Verbose -Message "Removing Url Patterns for Policy {$($PolicyNameValue)}" + Remove-PowerAppPolicyUrlPatterns -TenantId $PPTenantId -PolicyName $policyNameValue + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $PPTenantId, + + [Parameter(Mandatory = $true)] + [System.String] + $PolicyName, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $RuleSet, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" + + $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] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + $ConnectionMode = New-M365DSCConnection -Workload 'PowerPlatforms' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + try + { + $Script:ExportMode = $true + $tenantInfo = Get-TenantDetailsFromGraph + [array] $policies = Get-AdminDlpPolicy -ErrorAction Stop + + $dscContent = '' + if ($policies.Length -eq 0) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + $i = 1 + foreach ($policy in $policies) + { + if ($null -ne $Global:M365DSCExportResourceInstancesCount) + { + $Global:M365DSCExportResourceInstancesCount++ + } + Write-Host " |---[$i/$($policies.Count)] $($policy.DisplayName)" -NoNewline + $params = @{ + PPTenantId = $tenantInfo.TenantId + PolicyName = $policy.DisplayName + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + + $Results = Get-TargetResource @Params + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $Results + + if ($null -ne $Results.RuleSet) + { + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.RuleSet ` + -CIMInstanceName 'PPPowerAPpPolicyUrlPatternsRule' + if (-Not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.RuleSet = $complexTypeStringResult + } + else + { + $Results.Remove('RuleSet') | Out-Null + } + } + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $Results ` + -Credential $Credential + if ($Results.RuleSet) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'RuleSet' -IsCIMArray:$true + } + $dscContent += $currentDSCBlock + Save-M365DSCPartialExport -Content $currentDSCBlock ` + -FileName $Global:PartialExportFileName + $k++ + Write-Host $Global:M365DSCEmojiGreenCheckMark + + $i++ + } + return $dscContent + } + catch + { + Write-Host $Global:M365DSCEmojiRedX + + New-M365DSCLogEntry -Message 'Error during Export:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return '' + } +} + +Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/MSFT_PPPowerAppPolicyUrlPatterns.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/MSFT_PPPowerAppPolicyUrlPatterns.schema.mof new file mode 100644 index 0000000000..9d323799a0 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/MSFT_PPPowerAppPolicyUrlPatterns.schema.mof @@ -0,0 +1,23 @@ +[ClassVersion("1.0.0.0")] +class MSFT_PPPowerAPpPolicyUrlPatternsRule +{ + [Write, Description("Rule priority order.")] String order; + [Write, Description("Rule classification.")] String customConnectorRuleClassification; + [Write, Description("Rule pattern.")] String pattern; +}; + +[ClassVersion("1.0.0.0"), FriendlyName("PPPowerAppPolicyUrlPatterns")] +class MSFT_PPPowerAppPolicyUrlPatterns : OMI_BaseResource +{ + [Key, Description("The policy name identifier.")] String PolicyName; + [Key, Description("The tenant identifier.")] String PPTenantId; + [Write, Description("Set of custom connector pattern rules associated with the policy."), EmbeddedInstance("MSFT_PPPowerAPpPolicyUrlPatternsRule")] String RuleSet[]; + + [Write, Description("Present ensures the instance exists, absent ensures it is removed."), ValueMap{"Absent","Present"}, Values{"Absent","Present"}] string Ensure; + [Write, Description("Credentials of the workload's Admin"), EmbeddedInstance("MSFT_Credential")] string Credential; + [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; + [Write, Description("Managed ID being used for authentication.")] Boolean ManagedIdentity; + [Write, Description("Access token used for authentication.")] String AccessTokens[]; +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/readme.md new file mode 100644 index 0000000000..2656b9f1e2 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/readme.md @@ -0,0 +1,6 @@ + +# PPPowerAppPolicyUrlPatterns + +## Description + +Configures custom connector patterns for Data Loss Prevention policies in Power Platforms. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/settings.json new file mode 100644 index 0000000000..b9f4f13d61 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/settings.json @@ -0,0 +1,20 @@ +{ + "resourceName": "PPPowerAppPolicyUrlPatterns", + "description": "Configures custom connector patterns for Data Loss Prevention policies in Power Platforms.", + "roles": { + "read": [], + "update": [] + }, + "permissions": { + "graph": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [], + "update": [] + } + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/PPPowerAppPolicyUrlPatterns/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/PPPowerAppPolicyUrlPatterns/1-Create.ps1 new file mode 100644 index 0000000000..f658e08a3d --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/PPPowerAppPolicyUrlPatterns/1-Create.ps1 @@ -0,0 +1,51 @@ +<# +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()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + PPPowerAppPolicyUrlPatterns "PPPowerAppPolicyUrlPatterns" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Ensure = "Present"; + PolicyName = "DSCPolicy"; + PPTenantId = "e91d4e0e-d5a5-4e3a-be14-2192592a59af"; + RuleSet = @( + MSFT_PPPowerAPpPolicyUrlPatternsRule{ + pattern = 'https://contoso.com' + customConnectorRuleClassification = 'General' + order = 1 + } + MSFT_PPPowerAPpPolicyUrlPatternsRule{ + pattern = 'https://fabrikam.com' + customConnectorRuleClassification = 'General' + order = 2 + } + MSFT_PPPowerAPpPolicyUrlPatternsRule{ + pattern = '*' + customConnectorRuleClassification = 'Ignore' + order = 3 + } + ); + TenantId = $TenantId; + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/PPPowerAppPolicyUrlPatterns/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/PPPowerAppPolicyUrlPatterns/2-Update.ps1 new file mode 100644 index 0000000000..e7a1509d29 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/PPPowerAppPolicyUrlPatterns/2-Update.ps1 @@ -0,0 +1,51 @@ +<# +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()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + PPPowerAppPolicyUrlPatterns "PPPowerAppPolicyUrlPatterns" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Ensure = "Present"; + PolicyName = "DSCPolicy"; + PPTenantId = "e91d4e0e-d5a5-4e3a-be14-2192592a59af"; + RuleSet = @( + MSFT_PPPowerAPpPolicyUrlPatternsRule{ + pattern = 'https://contoso.com' + customConnectorRuleClassification = 'General' + order = 1 + } + MSFT_PPPowerAPpPolicyUrlPatternsRule{ + pattern = 'https://tailspintoys.com' # drift + customConnectorRuleClassification = 'General' + order = 2 + } + MSFT_PPPowerAPpPolicyUrlPatternsRule{ + pattern = '*' + customConnectorRuleClassification = 'Ignore' + order = 3 + } + ); + TenantId = $TenantId; + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/PPPowerAppPolicyUrlPatterns/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/PPPowerAppPolicyUrlPatterns/3-Remove.ps1 new file mode 100644 index 0000000000..3c885442c3 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/PPPowerAppPolicyUrlPatterns/3-Remove.ps1 @@ -0,0 +1,34 @@ +<# +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()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + PPPowerAppPolicyUrlPatterns "PPPowerAppPolicyUrlPatterns" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Ensure = "Absent"; + PolicyName = "DSCPolicy"; + PPTenantId = "e91d4e0e-d5a5-4e3a-be14-2192592a59af"; + TenantId = $TenantId; + } + } +} diff --git a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 index 148c450c1a..93e4d39e0f 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 @@ -3830,20 +3830,15 @@ function Get-M365DSCExportContentForResource $primaryKey = $Results.UserPrincipalName } - if ([String]::IsNullOrEmpty($primaryKey) -and ` - -not $Keys.Contains('IsSingleInstance')) - { - foreach ($Key in $Keys) - { - $primaryKey += $Results.$Key - } - } - $instanceName = $ResourceName if (-not [System.String]::IsNullOrEmpty($primaryKey)) { $instanceName += "-$primaryKey" } + else + { + $instanceName += "-" + (New-Guid).ToString() + } if ($Results.ContainsKey('Workload')) { diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.PPPowerAppPolicyUrlPatterns.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.PPPowerAppPolicyUrlPatterns.Tests.ps1 new file mode 100644 index 0000000000..0c69d915a5 --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.PPPowerAppPolicyUrlPatterns.Tests.ps1 @@ -0,0 +1,241 @@ +[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) + +$CurrentScriptPath = $PSCommandPath.Split('\') +$CurrentScriptName = $CurrentScriptPath[$CurrentScriptPath.Length -1] +$ResourceName = $CurrentScriptName.Split('.')[1] +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource $ResourceName -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 (New-Guid | Out-String) -AsPlainText -Force + $Credential = New-Object System.Management.Automation.PSCredential ('tenantadmin@mydomain.com', $secpasswd) + + Mock -CommandName Confirm-M365DSCDependencies -MockWith { + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credentials" + } + + Mock -commandName Remove-PowerAppPolicyUrlPatterns -MockWith {} + Mock -commandName Get-PowerAppPolicyUrlPatterns -MockWith {} + Mock -commandName New-PowerAppPolicyUrlPatterns -MockWith {} + Mock -commandName Get-TenantDetailsFromGraph -MockWith { + return @{ + TenantId = 'xxxxxxx' + } + } + Mock -commandName Get-AdminDlpPolicy -MockWith { + return @{ + PolicyName = 'DSCPolicy' + DisplayName = 'DSCPolicy' + } + } + + # Mock Write-Host to hide output during the tests + Mock -CommandName Write-Host -MockWith { + } + $Script:exportedInstances =$null + $Script:ExportMode = $false + } + # Test contexts + Context -Name "The instance exists but it SHOULD NOT" -Fixture { + BeforeAll { + $testParams = @{ + PolicyName = "DSCPolicy"; + PPTenantId = "xxxxxxx"; + RuleSet = @( + (New-CimInstance -ClassName MSFT_PPPowerAPpPolicyUrlPatternsRule -Property @{ + pattern = 'https://contoso.com' + customConnectorRuleClassification = 'General' + order = 1 + } -ClientOnly) + (New-CimInstance -ClassName MSFT_PPPowerAPpPolicyUrlPatternsRule -Property @{ + pattern = 'https://fabrikam.com' + customConnectorRuleClassification = 'General' + order = 2 + } -ClientOnly) + ) + Ensure = 'Absent' + Credential = $Credential; + } + + Mock -CommandName Get-PowerAppPolicyUrlPatterns -MockWith { + return @{ + rules = @( + @{ + pattern = 'https://contoso.com' + customConnectorRuleClassification = 'General' + order = 1 + }, + @{ + pattern = 'https://fabrikam.com' + customConnectorRuleClassification = 'General' + order = 2 + } + ) + } + } + } + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + } + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should remove the instance from the Set method' { + Set-TargetResource @testParams + Should -Invoke -CommandName Remove-PowerAppPolicyUrlPatterns -Exactly 1 + } + } + + Context -Name "The instance exists and values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + PolicyName = "DSCPolicy"; + PPTenantId = "xxxxxxx"; + RuleSet = @( + (New-CimInstance -ClassName MSFT_PPPowerAPpPolicyUrlPatternsRule -Property @{ + pattern = 'https://contoso.com' + customConnectorRuleClassification = 'General' + order = 1 + } -ClientOnly) + (New-CimInstance -ClassName MSFT_PPPowerAPpPolicyUrlPatternsRule -Property @{ + pattern = 'https://fabrikam.com' + customConnectorRuleClassification = 'General' + order = 2 + } -ClientOnly) + ) + Ensure = 'Present' + Credential = $Credential; + } + + Mock -CommandName Get-PowerAppPolicyUrlPatterns -MockWith { + return @{ + rules = @( + @{ + pattern = 'https://contoso.com' + customConnectorRuleClassification = 'General' + order = 1 + }, + @{ + pattern = 'https://fabrikam.com' + customConnectorRuleClassification = 'General' + order = 2 + } + ) + } + } + } + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name "The instance exists and values are NOT in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + PolicyName = "DSCPolicy"; + PPTenantId = "xxxxxxx"; + RuleSet = @( + (New-CimInstance -ClassName MSFT_PPPowerAPpPolicyUrlPatternsRule -Property @{ + pattern = 'https://contoso.com' + customConnectorRuleClassification = 'General' + order = 1 + } -ClientOnly) + (New-CimInstance -ClassName MSFT_PPPowerAPpPolicyUrlPatternsRule -Property @{ + pattern = 'https://tailspintoys.com' #drift + customConnectorRuleClassification = 'General' + order = 2 + } -ClientOnly) + ) + Ensure = 'Present' + Credential = $Credential; + } + + Mock -CommandName Get-PowerAppPolicyUrlPatterns -MockWith { + return @{ + rules = @( + @{ + pattern = 'https://contoso.com' + customConnectorRuleClassification = 'General' + order = 1 + }, + @{ + pattern = 'https://fabrikam.com' + customConnectorRuleClassification = 'General' + order = 2 + } + ) + } + } + } + + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + } + + 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 New-PowerAppPolicyUrlPatterns -Exactly 1 + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential; + } + + Mock -CommandName Get-PowerAppPolicyUrlPatterns -MockWith { + return @{ + rules = @( + @{ + pattern = 'https://contoso.com' + customConnectorRuleClassification = 'General' + order = 1 + }, + @{ + pattern = 'https://fabrikam.com' + customConnectorRuleClassification = 'General' + order = 2 + } + ) + } + } + } + It 'Should Reverse Engineer resource from the Export method' { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index 1513a0bc6f..b75ebef3fc 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -96910,3 +96910,68 @@ function Remove-AdminDlpPolicy $EnvironmentName ) } + +function New-PowerAppPolicyUrlPatterns +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $PolicyName, + + [Parameter()] + [System.Object] + $NewUrlPatterns, + + [Parameter()] + [System.String] + $ApiVersion + ) +} +function Remove-PowerAppPolicyUrlPatterns +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $PolicyName, + + [Parameter()] + [System.String] + $ApiVersion + ) +} +function Get-PowerAppPolicyUrlPatterns +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $PolicyName, + + [Parameter()] + [System.String] + $ApiVersion + ) +} +function Get-TenantDetailsFromGraph +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $GraphApiVersion + ) +} From 167c5176276eac2ec0f3ec907f28beb0bddb0ce8 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Fri, 11 Oct 2024 11:01:07 -0400 Subject: [PATCH 2/2] Update MSFT_PPPowerAppPolicyUrlPatterns.psm1 --- .../MSFT_PPPowerAppPolicyUrlPatterns.psm1 | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/MSFT_PPPowerAppPolicyUrlPatterns.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/MSFT_PPPowerAppPolicyUrlPatterns.psm1 index b99fc8179b..fc0895a29b 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/MSFT_PPPowerAppPolicyUrlPatterns.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_PPPowerAppPolicyUrlPatterns/MSFT_PPPowerAppPolicyUrlPatterns.psm1 @@ -273,10 +273,33 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + #Compare Cim instances + foreach ($key in $PSBoundParameters.Keys) + { + $source = $PSBoundParameters.$key + $target = $CurrentValues.$key + if ($source.getType().Name -like '*CimInstance*') + { + $testResult = Compare-M365DSCComplexObject ` + -Source ($source) ` + -Target ($target) + + if (-Not $testResult) + { + $testResult = $false + break + } + + $ValuesToCheck.Remove($key) | Out-Null + } + } + if ($testResult) + { + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + } Write-Verbose -Message "Test-TargetResource returned $testResult"