From 2a92d72971a9a5cd2881d82ad73e07f43e14b317 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Mon, 9 Sep 2024 09:55:28 -0400 Subject: [PATCH 01/14] SCInsiderRiskEntityList - Initial Release --- CHANGELOG.md | 4 + .../MSFT_SCInsiderRiskEntityList.psm1 | 350 ++++++++++++++++++ .../MSFT_SCInsiderRiskEntityList.schema.mof | 13 + .../MSFT_SCInsiderRiskEntityList/readme.md | 6 + .../settings.json | 32 ++ .../Dependencies/Manifest.psd1 | 36 +- .../SCInsiderRiskEntityList/1-Create.ps1 | 26 ++ .../SCInsiderRiskEntityList/2-Update.ps1 | 26 ++ .../SCInsiderRiskEntityList/3-Remove.ps1 | 26 ++ ...ft365DSC.SCInsiderRiskEntityList.Tests.ps1 | 176 +++++++++ 10 files changed, 677 insertions(+), 18 deletions(-) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/1-Create.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/2-Update.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/3-Remove.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.SCInsiderRiskEntityList.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index cc7285ea30..1ee23c0323 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,12 @@ * AADPasswordRuleSettings * Initial release +* SCInsiderRiskEntityList + * Initial release. * SPOAccessControlSettings * Added support for property EnableRestrictedAccessControl. +* DEPENDENCIES + * Updated Microsoft.Graph to version 2.23.0. # 1.24.904.1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 new file mode 100644 index 0000000000..7d42466c4c --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 @@ -0,0 +1,350 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + ##TODO - Replace the PrimaryKey + [Parameter(Mandatory = $true)] + [System.String] + $PrimaryKey, + + ##TODO - Add the list of Parameters + + [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 + ) + + ##TODO - Replace the workload by the one associated to your resource + New-M365DSCConnection -Workload 'Workload' ` + -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 + { + if ($null -ne $Script:exportedInstances -and $Script:ExportMode) + { + ##TODO - Replace the PrimaryKey in the Filter by the one for the resource + $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.PrimaryKey -eq $PrimaryKey} + } + else + { + ##TODO - Replace the cmdlet by the one to retrieve a specific instance. + $instance = Get-cmdlet -PrimaryKey $PrimaryKey -ErrorAction Stop + } + if ($null -eq $instance) + { + return $nullResult + } + + $results = @{ + ##TODO - Add the list of parameters to be returned + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + return [System.Collections.Hashtable] $results + } + catch + { + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullResult + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + ##TODO - Replace the PrimaryKey + [Parameter(Mandatory = $true)] + [System.String] + $PrimaryKey, + + ##TODO - Add the list of Parameters + + [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 + + $currentInstance = Get-TargetResource @PSBoundParameters + + $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + + # CREATE + if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') + { + ##TODO - Replace by the New cmdlet for the resource + New-Cmdlet @SetParameters + } + # UPDATE + elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + { + ##TODO - Replace by the Update/Set cmdlet for the resource + Set-cmdlet @SetParameters + } + # REMOVE + elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') + { + ##TODO - Replace by the Remove cmdlet for the resource + Remove-cmdlet @SetParameters + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + ##TODO - Replace the PrimaryKey + [Parameter(Mandatory = $true)] + [System.String] + $PrimaryKey, + + ##TODO - Add the list of Parameters + + [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 + ) + + ##TODO - Replace workload + $ConnectionMode = New-M365DSCConnection -Workload 'Workload' ` + -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 + ##TODO - Replace Get-Cmdlet by the cmdlet to retrieve all instances + [array] $Script:exportedInstances = Get-Cmdlet -ErrorAction Stop + + $i = 1 + $dscContent = '' + if ($Script:exportedInstances.Length -eq 0) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + foreach ($config in $Script:exportedInstances) + { + $displayedKey = $config.Id + Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline + $params = @{ + ##TODO - Specify the Primary Key + #PrimaryKey = $config.PrimaryKey + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + + $Results = Get-TargetResource @Params + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $Results + + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $Results ` + -Credential $Credential + $dscContent += $currentDSCBlock + Save-M365DSCPartialExport -Content $currentDSCBlock ` + -FileName $Global:PartialExportFileName + $i++ + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + 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_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof new file mode 100644 index 0000000000..07accdc886 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof @@ -0,0 +1,13 @@ +[ClassVersion("1.0.0.0"), FriendlyName("SCInsiderRiskEntityList")] +class MSFT_SCInsiderRiskEntityList : OMI_BaseResource +{ + [Key, Description("")] String PrimaryKey; + [Write, Description("")] String OtherProperties; + + [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_SCInsiderRiskEntityList/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/readme.md new file mode 100644 index 0000000000..1a24877790 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/readme.md @@ -0,0 +1,6 @@ + +# SCInsiderRiskEntityList + +## Description + +##TODO - Provide a short description of what the resource is set to configure. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/settings.json new file mode 100644 index 0000000000..c44468a9df --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/settings.json @@ -0,0 +1,32 @@ +{ + "resourceName": "SCInsiderRiskEntityList", + "description": "Description of what the resource is about.", + "roles": { + "read": [ + "Role" + ], + "update": [ + "Role" + ] + }, + "permissions": { + "graph": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [ + { + "name": "Permission for Monitoring and Export" + } + ], + "update": [ + { + "name": "Permission for deploying" + } + ] + } + } + } +} diff --git a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 index 717197df8f..1c36e81530 100644 --- a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 +++ b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 @@ -10,75 +10,75 @@ }, @{ ModuleName = 'Microsoft.Graph.Applications' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Authentication' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Devices.CorporateManagement' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement.Administration' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement.Enrollment' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.DirectoryManagement' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.Governance' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.SignIns' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Reports' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Teams' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.DeviceManagement.Administration' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DirectoryObjects' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Groups' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Planner' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Sites' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Users' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.Graph.Users.Actions' - RequiredVersion = '2.20.0' + RequiredVersion = '2.23.0' }, @{ ModuleName = 'Microsoft.PowerApps.Administration.PowerShell' diff --git a/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/1-Create.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/1-Create.ps1 @@ -0,0 +1,26 @@ +<# +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 + { + + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/2-Update.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/2-Update.ps1 @@ -0,0 +1,26 @@ +<# +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 + { + + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/3-Remove.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/3-Remove.ps1 @@ -0,0 +1,26 @@ +<# +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 + { + + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SCInsiderRiskEntityList.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SCInsiderRiskEntityList.Tests.ps1 new file mode 100644 index 0000000000..20857e0393 --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SCInsiderRiskEntityList.Tests.ps1 @@ -0,0 +1,176 @@ +[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" + } + + ##TODO - Mock any Remove/Set/New cmdlets + + # 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 should exist but it DOES NOT" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return $null + Mock -CommandName Get-Cmdlet -MockWith { + return $null + } + } + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' + } + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should create a new instance from the Set method' { + ##TODO - Replace the New-Cmdlet by the appropriate one + Should -Invoke -CommandName New-Cmdlet -Exactly 1 + } + } + + Context -Name "The instance exists but it SHOULD NOT" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Absent' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return an instance + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + 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' { + ##TODO - Replace the Remove-Cmdlet by the appropriate one + Should -Invoke -CommandName Remove-Cmdlet -Exactly 1 + } + } + + Context -Name "The instance exists and values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return the desired values + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + + 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 = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return a drift + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + + 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 + ##TODO - Replace the Update-Cmdlet by the appropriate one + Should -Invoke -CommandName Update-Cmdlet -Exactly 1 + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return an instance + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + It 'Should Reverse Engineer resource from the Export method' { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope From 22c3569cd167b0c8c26bbba09e3738fae4212ddc Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 11 Sep 2024 07:32:32 -0400 Subject: [PATCH 02/14] Updated --- .../MSFT_SCInsiderRiskEntityList.psm1 | 61 ++++++++++++++----- .../Dependencies/Manifest.psd1 | 2 +- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 index 7d42466c4c..c477dc7fc2 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 @@ -4,12 +4,21 @@ function Get-TargetResource [OutputType([System.Collections.Hashtable])] param ( - ##TODO - Replace the PrimaryKey [Parameter(Mandatory = $true)] [System.String] - $PrimaryKey, + $Name, - ##TODO - Add the list of Parameters + [Parameter(Mandatory = $true)] + [System.String] + $ListType, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $DisplayName, [Parameter()] [System.Management.Automation.PSCredential] @@ -37,7 +46,7 @@ function Get-TargetResource ) ##TODO - Replace the workload by the one associated to your resource - New-M365DSCConnection -Workload 'Workload' ` + New-M365DSCConnection -Workload 'SecurityComplianceCenter' ` -InboundParameters $PSBoundParameters | Out-Null #Ensure the proper dependencies are installed in the current environment. @@ -58,13 +67,18 @@ function Get-TargetResource { if ($null -ne $Script:exportedInstances -and $Script:ExportMode) { - ##TODO - Replace the PrimaryKey in the Filter by the one for the resource - $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.PrimaryKey -eq $PrimaryKey} + if (-not [System.String]::IsNullOrEmpty($DisplayName)) + { + $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.ListType -eq $ListType -and $_.DisplayName -eq $DisplayName} + } + else + { + $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.ListType -eq $ListType -and $_.Name -eq $Name} + } } else { - ##TODO - Replace the cmdlet by the one to retrieve a specific instance. - $instance = Get-cmdlet -PrimaryKey $PrimaryKey -ErrorAction Stop + $instance = Get-InsiderRiskEntityList -Type $ListType -ErrorAction Stop } if ($null -eq $instance) { @@ -72,7 +86,10 @@ function Get-TargetResource } $results = @{ - ##TODO - Add the list of parameters to be returned + DisplayName = $instance.DisplayName + Name = $instance.Name + Description = $instance.Description + ListType = $instance.ListType Ensure = 'Present' Credential = $Credential ApplicationId = $ApplicationId @@ -269,8 +286,7 @@ function Export-TargetResource $AccessTokens ) - ##TODO - Replace workload - $ConnectionMode = New-M365DSCConnection -Workload 'Workload' ` + $ConnectionMode = New-M365DSCConnection -Workload 'SecurityComplianceCenter' ` -InboundParameters $PSBoundParameters #Ensure the proper dependencies are installed in the current environment. @@ -288,8 +304,16 @@ function Export-TargetResource try { $Script:ExportMode = $true - ##TODO - Replace Get-Cmdlet by the cmdlet to retrieve all instances - [array] $Script:exportedInstances = Get-Cmdlet -ErrorAction Stop + [array] $Script:exportedInstances = @() + $availableTypes = @('HveLists', 'DomainLists', 'CriticalAssetLists', 'WindowsFilePathRegexLists', 'SensitiveTypeLists', 'SiteLists', 'KeywordLists', ` + 'CustomDomainLists', 'CustomSiteLists', 'CustomKeywordLists', 'CustomFileTypeLists', 'CustomFilePathRegexLists', ` + 'CustomSensitiveInformationTypeLists', 'CustomMLClassifierTypeLists', 'GlobalExclusionSGMapping', 'DlpPolicyLists') + + # Retrieve entries for each type + foreach ($listType in $availableTypes) + { + $Script:exportedInstances += Get-InsiderRiskEntityList -Type $listType -ErrorAction Stop + } $i = 1 $dscContent = '' @@ -303,11 +327,16 @@ function Export-TargetResource } foreach ($config in $Script:exportedInstances) { - $displayedKey = $config.Id + if ($null -ne $Global:M365DSCExportResourceInstancesCount) + { + $Global:M365DSCExportResourceInstancesCount++ + } + $displayedKey = $config.ListType + ' - ' + $config.Name Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline $params = @{ - ##TODO - Specify the Primary Key - #PrimaryKey = $config.PrimaryKey + DisplayName = $config.DisplayName + Name = $config.Name + ListType = $config.ListType Credential = $Credential ApplicationId = $ApplicationId TenantId = $TenantId diff --git a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 index 1c36e81530..04f716b118 100644 --- a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 +++ b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 @@ -90,7 +90,7 @@ }, @{ ModuleName = "MSCloudLoginAssistant" - RequiredVersion = "1.1.20" + RequiredVersion = "1.1.22" }, @{ ModuleName = 'PnP.PowerShell' From 982d8500309fa34397916e0b4e66a0312b54039c Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 11 Sep 2024 07:52:12 -0400 Subject: [PATCH 03/14] Update MSFT_SCInsiderRiskEntityList.schema.mof --- .../MSFT_SCInsiderRiskEntityList.schema.mof | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof index 07accdc886..0343b1604a 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof @@ -1,8 +1,10 @@ [ClassVersion("1.0.0.0"), FriendlyName("SCInsiderRiskEntityList")] class MSFT_SCInsiderRiskEntityList : OMI_BaseResource { - [Key, Description("")] String PrimaryKey; - [Write, Description("")] String OtherProperties; + [Key, Description("")] String Name; + [Required, Description("")] String ListType; + [Write, Description("")] String Description; + [Write, Description("")] String DisplayName; [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; From 843ade4037b8f9f18ff4175a3775d04bb63a32c6 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 11 Sep 2024 07:52:42 -0400 Subject: [PATCH 04/14] Update M365DSCUtil.psm1 --- Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 index 9397159c76..614ea78655 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 @@ -1771,7 +1771,6 @@ function New-M365DSCConnection [System.Boolean] $SkipModuleReload = $false ) - $verbosepreference = 'Continue' $Global:MaximumFunctionCount = 32767 if ($Workload -eq 'MicrosoftTeams') { From 5f7f1f0fb6c69c043c5423eec6a6d267fdaa7ef6 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 11 Sep 2024 08:59:37 -0400 Subject: [PATCH 05/14] Updates --- .../MSFT_SCInsiderRiskEntityList.psm1 | 5 +++++ .../MSFT_SCInsiderRiskEntityList.schema.mof | 2 +- Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 index c477dc7fc2..fdd2b985bc 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 @@ -20,6 +20,11 @@ function Get-TargetResource [System.String] $DisplayName, + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + [Parameter()] [System.Management.Automation.PSCredential] $Credential, diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof index 0343b1604a..21cf04215e 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof @@ -5,7 +5,7 @@ class MSFT_SCInsiderRiskEntityList : OMI_BaseResource [Required, Description("")] String ListType; [Write, Description("")] String Description; [Write, Description("")] String DisplayName; - + [Write, Description("Specify if this entity should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] 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; diff --git a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 index 614ea78655..b43cd56a1f 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 @@ -3668,7 +3668,7 @@ function Get-M365DSCExportContentForResource { $primaryKey = '' } - elseif ($Keys.Contains('DisplayName')) + elseif ($Keys.Contains('DisplayName') -and -not [System.String]::IsNullOrEmpty($Results.DisplayName)) { $primaryKey = $Results.DisplayName } From 1022ef331de649b37c5f1fa1c8064e696af9f22a Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Mon, 16 Sep 2024 13:12:14 -0400 Subject: [PATCH 06/14] Updates --- .../MSFT_SCInsiderRiskEntityList.psm1 | 357 ++++++++++++++++-- .../MSFT_SCInsiderRiskEntityList.schema.mof | 9 + .../Dependencies/Manifest.psd1 | 4 +- 3 files changed, 342 insertions(+), 28 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 index fdd2b985bc..a684573e73 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 @@ -20,6 +20,34 @@ function Get-TargetResource [System.String] $DisplayName, + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Domains, + + [Parameter()] + [System.String[]] + $FilePaths, + + [Parameter()] + [System.String[]] + $FileTypes, + + [Parameter()] + [System.String[]] + $Keywords, + + [Parameter()] + [System.String[]] + $SensitiveInformationTypes, + + [Parameter()] + [System.String[]] + $Sites, + + [Parameter()] + [System.String[]] + $TrainableClassifiers, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] @@ -70,38 +98,231 @@ function Get-TargetResource $nullResult.Ensure = 'Absent' try { - if ($null -ne $Script:exportedInstances -and $Script:ExportMode) + $instance = Get-InsiderRiskEntityList -Identity $Name -ErrorAction Stop + + if ($null -eq $instance) + { + return $nullResult + } + + # CustomDomainLists + $DmnValues = @() + if ($instance.ListType -eq 'CustomDomainLists' -or ` + $instance.Name -eq 'IrmWhitelistDomains') { - if (-not [System.String]::IsNullOrEmpty($DisplayName)) + foreach ($entity in $instance.Entities) { - $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.ListType -eq $ListType -and $_.DisplayName -eq $DisplayName} + $entity = ConvertFrom-Json $entity + $current = @{ + Dmn = $entity.Dmn + isMLSubDmn = $entity.isMLSubDmn + } + $DmnValues += $current } - else + } + + # CustomFilePathRegexLists + $FilePathValues = @() + if ($instance.ListType -eq 'CustomFilePathRegexLists' -or ` + $instance.Name -eq 'IrmCustomExWinFilePaths') + { + foreach ($entity in $instance.Entities) { - $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.ListType -eq $ListType -and $_.Name -eq $Name} + $entity = ConvertFrom-Json $entity + $FilePathValues += $entity.FlPthRgx } } - else + + # CustomFileTypeLists + $FileTypeValues = @() + if ($instance.ListType -eq 'CustomFileTypeLists') { - $instance = Get-InsiderRiskEntityList -Type $ListType -ErrorAction Stop + foreach ($entity in $instance.Entities) + { + $entity = ConvertFrom-Json $entity + $FileTypeValues += $entity.Ext + } } - if ($null -eq $instance) + + # CustomKeywordLists + $KeywordValues = @() + if ($instance.ListType -eq 'CustomKeywordLists' -or ` + $instance.Name -eq 'IrmExcludedKeywords' -or $instance.Name -eq 'IrmNotExcludedKeywords') { - return $nullResult + foreach ($entity in $instance.Entities) + { + $entity = ConvertFrom-Json $entity + $KeywordValues += $entity.Name + } + } + + # CustomSensitiveInformationTypeLists + $SITValues = @() + if ($instance.ListType -eq 'CustomSensitiveInformationTypeLists' -or ` + $instance.Name -eq 'IrmCustomExSensitiveTypes') + { + foreach ($entity in $instance.Entities) + { + $entity = ConvertFrom-Json $entity + $SITObject = Get-DLPSensitiveInformationType -Identity $entity.GUID + $SITValues += $SITObject.Name + } + } + + # CustomSiteLists + $SiteValues = @() + if ($instance.ListType -eq 'CustomSiteLists' -or ` + $instance.Name -eq 'IrmExcludedSites') + { + foreach ($entity in $instance.Entities) + { + $entity = ConvertFrom-Json $entity + $SiteValues += $entity.Url + } + } + + # CustomMLClassifierTypeLists + $TrainableClassifierValues = @() + if ($instance.ListType -eq 'CustomMLClassifierTypeLists' -or $instance.Name -eq 'IrmCustomExMLClassifiers') + { + foreach ($entity in $instance.Entities) + { + $entity = ConvertFrom-Json $entity + $SiteValues += $entity.Url + } + } + + # Global Exclusions - Excluded Keyword Groups + $excludedKeywordGroupValue = @() + if ($instance.Name -eq 'IrmXSGExcludedKeywords') + { + $entities = $instance.Entities + foreach ($entity in $entities) + { + $entity = ConvertFrom-Json $entity + $group = Get-InsiderRiskEntityList -Identity $entity.GroupId + $excludedKeywordGroupValue += $group.DisplayName + } + } + + # Global Exclusions - Exception Keyword Groups + $exceptionKeywordGroupValue = @() + if ($instance.Name -eq 'IrmXSGExceptionKeywords') + { + $entities = $instance.Entities + foreach ($entity in $entities) + { + $entity = ConvertFrom-Json $entity + $group = Get-InsiderRiskEntityList -Identity $entity.GroupId + $exceptionKeywordGroupValue += $group.DisplayName + } + } + + # Global Exclusions - Excluded Classifier Groups + $excludedClassifierGroupValue = @() + if ($instance.Name -eq 'IrmXSGMLClassifierTypes') + { + $entities = $instance.Entities + foreach ($entity in $entities) + { + $entity = ConvertFrom-Json $entity + $group = Get-InsiderRiskEntityList -Identity $entity.GroupId + $excludedClassifierGroupValue += $group.DisplayName + } + } + + # Global Exclusions - Excluded Domain Groups + $excludedDomainGroupValue = @() + if ($instance.Name -eq 'IrmXSGDomains') + { + $entities = $instance.Entities + foreach ($entity in $entities) + { + $entity = ConvertFrom-Json $entity + $group = Get-InsiderRiskEntityList -Identity $entity.GroupId + $excludedDomainGroupValue += $group.DisplayName + } + } + + # Global Exclusions - Excluded File Path Groups + $excludedFilePathGroupValue = @() + if ($instance.Name -eq 'IrmXSGFilePaths') + { + $entities = $instance.Entities + foreach ($entity in $entities) + { + $entity = ConvertFrom-Json $entity + $group = Get-InsiderRiskEntityList -Identity $entity.GroupId + $excludedFilePathGroupValue += $group.DisplayName + } + } + + # Global Exclusions - Excluded Site Groups + $excludedSiteGroupValue = @() + if ($instance.Name -eq 'IrmXSGSites') + { + $entities = $instance.Entities + foreach ($entity in $entities) + { + $entity = ConvertFrom-Json $entity + $group = Get-InsiderRiskEntityList -Identity $entity.GroupId + $excludedSiteGroupValue += $group.DisplayName + } + } + + # Global Exclusions - Excluded Sensitive Info Type Groups + $excludedSITGroupValue = @() + if ($instance.Name -eq 'IrmXSGSensitiveInfoTypes') + { + $entities = $instance.Entities + foreach ($entity in $entities) + { + $entity = ConvertFrom-Json $entity + $group = Get-InsiderRiskEntityList -Identity $entity.GroupId + $excludedSITGroupValue += $group.DisplayName + } + } + + # Global Exclusions - Excluded File Type Groups + $excludedFileTypeGroupValue = @() + if ($instance.Name -eq 'IrmXSGFiletypes') + { + $entities = $instance.Entities + foreach ($entity in $entities) + { + $entity = ConvertFrom-Json $entity + $group = Get-InsiderRiskEntityList -Identity $entity.GroupId + $excludedFileTypeGroupValue += $group.DisplayName + } } $results = @{ - DisplayName = $instance.DisplayName - Name = $instance.Name - Description = $instance.Description - ListType = $instance.ListType - Ensure = 'Present' - Credential = $Credential - ApplicationId = $ApplicationId - TenantId = $TenantId - CertificateThumbprint = $CertificateThumbprint - ManagedIdentity = $ManagedIdentity.IsPresent - AccessTokens = $AccessTokens + DisplayName = $instance.DisplayName + Name = $instance.Name + Description = $instance.Description + ListType = $instance.ListType + Domains = $DmnValues + FilePaths = $FilePathValues + FileTypes = $FileTypeValues + Keywords = $KeywordValues + SensitiveInformationTypes = $SITValues + Sites = $SiteValues + #TrainableClassifiers = + ExcludedKeyworkGroups = $excludedKeywordGroupValue + ExceptionKeyworkGroups = $exceptionKeywordGroupValue + ExcludedClassifierGroups = $excludedClassifierGroupValue + ExcludedDomainGroups = $excludedDomainGroupValue + ExcludedFilePathGroup = $excludedFilePathGroupValue + ExcludedSiteGroups = $excludedSiteGroupValue + ExcludedSensitiveInformationTypeGroups = $excludedSITGroupValue + ExcludedFileTypeGroups = $excludedFileTypeGroupValue + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens } return [System.Collections.Hashtable] $results } @@ -122,12 +343,54 @@ function Set-TargetResource [CmdletBinding()] param ( - ##TODO - Replace the PrimaryKey [Parameter(Mandatory = $true)] [System.String] - $PrimaryKey, + $Name, + + [Parameter(Mandatory = $true)] + [System.String] + $ListType, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Domains, + + [Parameter()] + [System.String[]] + $FilePaths, - ##TODO - Add the list of Parameters + [Parameter()] + [System.String[]] + $FileTypes, + + [Parameter()] + [System.String[]] + $Keywords, + + [Parameter()] + [System.String[]] + $SensitiveInformationTypes, + + [Parameter()] + [System.String[]] + $Sites, + + [Parameter()] + [System.String[]] + $TrainableClassifiers, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', [Parameter()] [System.Management.Automation.PSCredential] @@ -196,12 +459,54 @@ function Test-TargetResource [OutputType([System.Boolean])] param ( - ##TODO - Replace the PrimaryKey [Parameter(Mandatory = $true)] [System.String] - $PrimaryKey, + $Name, - ##TODO - Add the list of Parameters + [Parameter(Mandatory = $true)] + [System.String] + $ListType, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Domains, + + [Parameter()] + [System.String[]] + $FilePaths, + + [Parameter()] + [System.String[]] + $FileTypes, + + [Parameter()] + [System.String[]] + $Keywords, + + [Parameter()] + [System.String[]] + $SensitiveInformationTypes, + + [Parameter()] + [System.String[]] + $Sites, + + [Parameter()] + [System.String[]] + $TrainableClassifiers, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', [Parameter()] [System.Management.Automation.PSCredential] diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof index 21cf04215e..23af1989e8 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof @@ -1,3 +1,11 @@ +[ClassVersion("1.0.0")] +class MSFT_SCInsiderRiskEntityListDomain +{ + [Required, Description("Domain name.")] String Dmn; + [Write, Description("Defines if the entry should include multi-level subdomains or not.")] Boolean isMLSubDmn; + [Write, Description("Type of the Sensitive Information label")] String type; +}; + [ClassVersion("1.0.0.0"), FriendlyName("SCInsiderRiskEntityList")] class MSFT_SCInsiderRiskEntityList : OMI_BaseResource { @@ -5,6 +13,7 @@ class MSFT_SCInsiderRiskEntityList : OMI_BaseResource [Required, Description("")] String ListType; [Write, Description("")] String Description; [Write, Description("")] String DisplayName; + [Write, Description("Domain group."), EmbeddedInstance("MSFT_SCInsiderRiskEntityListDomain")] string Domains; [Write, Description("Specify if this entity should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] 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; diff --git a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 index 54b3565d09..afd204cd31 100644 --- a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 +++ b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 @@ -14,7 +14,7 @@ }, @{ ModuleName = 'DSCParser' - RequiredVersion = '2.0.0.8' + RequiredVersion = '2.0.0.9' }, @{ ModuleName = 'ExchangeOnlineManagement' @@ -102,7 +102,7 @@ }, @{ ModuleName = "MSCloudLoginAssistant" - RequiredVersion = "1.1.22" + RequiredVersion = "1.1.24" }, @{ ModuleName = 'PnP.PowerShell' From 683917271e0a6bbb87c76f953a557c022f2af42c Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Mon, 16 Sep 2024 13:33:01 -0400 Subject: [PATCH 07/14] Fixes --- .../MSFT_SCInsiderRiskEntityList.psm1 | 96 +++++++++++++++++++ .../MSFT_SCInsiderRiskEntityList.schema.mof | 17 +++- 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 index a684573e73..b0a67fdcbc 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 @@ -48,6 +48,38 @@ function Get-TargetResource [System.String[]] $TrainableClassifiers, + [Parameter()] + [System.String[]] + $ExceptionKeyworkGroups, + + [Parameter()] + [System.String[]] + $ExcludedClassifierGroups, + + [Parameter()] + [System.String[]] + $ExcludedDomainGroups, + + [Parameter()] + [System.String[]] + $ExcludedFilePathGroup, + + [Parameter()] + [System.String[]] + $ExcludedFileTypeGroups, + + [Parameter()] + [System.String[]] + $ExcludedKeyworkGroups, + + [Parameter()] + [System.String[]] + $ExcludedSensitiveInformationTypeGroups, + + [Parameter()] + [System.String[]] + $ExcludedSiteGroups, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] @@ -387,6 +419,38 @@ function Set-TargetResource [System.String[]] $TrainableClassifiers, + [Parameter()] + [System.String[]] + $ExceptionKeyworkGroups, + + [Parameter()] + [System.String[]] + $ExcludedClassifierGroups, + + [Parameter()] + [System.String[]] + $ExcludedDomainGroups, + + [Parameter()] + [System.String[]] + $ExcludedFilePathGroup, + + [Parameter()] + [System.String[]] + $ExcludedFileTypeGroups, + + [Parameter()] + [System.String[]] + $ExcludedKeyworkGroups, + + [Parameter()] + [System.String[]] + $ExcludedSensitiveInformationTypeGroups, + + [Parameter()] + [System.String[]] + $ExcludedSiteGroups, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] @@ -503,6 +567,38 @@ function Test-TargetResource [System.String[]] $TrainableClassifiers, + [Parameter()] + [System.String[]] + $ExceptionKeyworkGroups, + + [Parameter()] + [System.String[]] + $ExcludedClassifierGroups, + + [Parameter()] + [System.String[]] + $ExcludedDomainGroups, + + [Parameter()] + [System.String[]] + $ExcludedFilePathGroup, + + [Parameter()] + [System.String[]] + $ExcludedFileTypeGroups, + + [Parameter()] + [System.String[]] + $ExcludedKeyworkGroups, + + [Parameter()] + [System.String[]] + $ExcludedSensitiveInformationTypeGroups, + + [Parameter()] + [System.String[]] + $ExcludedSiteGroups, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof index 23af1989e8..e1d0a0bbb0 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof @@ -13,7 +13,22 @@ class MSFT_SCInsiderRiskEntityList : OMI_BaseResource [Required, Description("")] String ListType; [Write, Description("")] String Description; [Write, Description("")] String DisplayName; - [Write, Description("Domain group."), EmbeddedInstance("MSFT_SCInsiderRiskEntityListDomain")] string Domains; + [Write, Description(""), EmbeddedInstance("MSFT_SCInsiderRiskEntityListDomain")] String Domains[]; + [Write, Description("")] String FilePaths[]; + [Write, Description("")] String FileTypes[]; + [Write, Description("")] String Keywords[]; + [Write, Description("")] String SensitiveInformationTypes[]; + [Write, Description("")] String Sites[]; + [Write, Description("")] String TrainableClassifiers[]; + [Write, Description("")] String ExceptionKeyworkGroups[]; + [Write, Description("")] String ExcludedClassifierGroups[]; + [Write, Description("")] String ExcludedDomainGroups[]; + [Write, Description("")] String ExcludedFilePathGroup[]; + [Write, Description("")] String ExcludedFileTypeGroups[]; + [Write, Description("")] String ExcludedKeyworkGroups[]; + [Write, Description("")] String ExcludedSensitiveInformationTypeGroups[]; + [Write, Description("")] String ExcludedSiteGroups[]; + [Write, Description("Domain group.")] string Domains; [Write, Description("Specify if this entity should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] 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; From a42a311b9f6be118a78555fc949cc02a333194a0 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 17 Sep 2024 16:47:31 -0400 Subject: [PATCH 08/14] Updates --- .../MSFT_SCInsiderRiskEntityList.psm1 | 64 +++++++++++++++++-- .../MSFT_SCInsiderRiskEntityList.schema.mof | 1 - .../Modules/M365DSCTelemetryEngine.psm1 | 29 +++++---- 3 files changed, 76 insertions(+), 18 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 index b0a67fdcbc..c5339bf426 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 @@ -500,14 +500,69 @@ function Set-TargetResource # CREATE if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { - ##TODO - Replace by the New cmdlet for the resource - New-Cmdlet @SetParameters + # Create a new Domain Group + if ($ListType -eq 'CustomDomainLists') + { + $value = @() + foreach ($domain in $Domains) + { + $value += "{`"Dmn`":`"$($domain.Dmn)`",`"isMLSubDmn`":$($domain.isMLSubDmn.ToString().ToLower())}" + } + Write-Verbose -Message "Creating new Domain Group {$Name} with values {$($value -join ',')}" + New-InsiderRiskEntityList -Type 'CustomDomainLists' ` + -Name $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -Entities $value | Out-Null + } + elseif ($ListType -eq 'CustomFilePathRegexLists') + { + $value = @() + foreach ($filePath in $FilePaths) + { + $value += "{`"FlPthRgx`":`"$($filePath)`",`"isSrc`":true,`"isTrgt`":true}" + } + Write-Verbose -Message "Creating new FilePath Group {$Name} with values {$($value -join ',')}" + New-InsiderRiskEntityList -Type 'CustomDomainLists' ` + -Name $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -Entities $value | Out-Null + } } # UPDATE elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') { - ##TODO - Replace by the Update/Set cmdlet for the resource - Set-cmdlet @SetParameters + # Update Domain Group + if ($ListType -eq 'CustomDomainLists') + { + $entitiesToAdd = @() + $entitiesToRemove = @() + $differences = Compare-Object -ReferenceObject $currentInstance.Domains.Dmn -DifferenceObject $Domains.Dmn + foreach ($diff in $differences) + { + if ($diff.SideIndicator -eq '=>') + { + $instance = $Domains | Where-Object -FilterScript {$_.Dmn -eq $diff.InputObject} + $entitiesToAdd += "{`"Dmn`":`"$($instance.Dmn)`",`"isMLSubDmn`":$($instance.isMLSubDmn.ToString().ToLower())}" + } + else + { + $instance = $currentInstance.Domains | Where-Object -FilterScript {$_.Dmn -eq $diff.InputObject} + $entitiesToRemove += "{`"Dmn`":`"$($instance.Dmn)`",`"isMLSubDmn`":$($instance.isMLSubDmn.ToString().ToLower())}" + } + } + + Write-Verbose -Message "Updating Domain Group {$Name}" + Write-Verbose -Message "Adding entities: $($entitiesToAdd -join ',')" + Write-Verbose -Message "Removing entities: $($entitiesToRemove -join ',')" + + Set-InsiderRiskEntityList -Identity $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -AddEntities $entitiesToAdd ` + -RemoveEntities $entitiesToRemove | Out-Null + } } # REMOVE elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') @@ -643,6 +698,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + $ValuesToCheck.Remove('Name') | Out-Null Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof index e1d0a0bbb0..e005642f04 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof @@ -28,7 +28,6 @@ class MSFT_SCInsiderRiskEntityList : OMI_BaseResource [Write, Description("")] String ExcludedKeyworkGroups[]; [Write, Description("")] String ExcludedSensitiveInformationTypeGroups[]; [Write, Description("")] String ExcludedSiteGroups[]; - [Write, Description("Domain group.")] string Domains; [Write, Description("Specify if this entity should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] 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; diff --git a/Modules/Microsoft365DSC/Modules/M365DSCTelemetryEngine.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCTelemetryEngine.psm1 index a62b0907b7..6d0b9f27e7 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCTelemetryEngine.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCTelemetryEngine.psm1 @@ -368,7 +368,10 @@ function Add-M365DSCTelemetryEvent # LCM Metadata Information try { - $LCMInfo = Get-DscLocalConfigurationManager -ErrorAction Stop + if ($null -eq $Global:LCMInfo) + { + $Global:LCMInfo = Get-DscLocalConfigurationManager -ErrorAction Stop + } $certificateConfigured = $false if (-not [System.String]::IsNullOrEmpty($LCMInfo.CertificateID)) @@ -377,17 +380,17 @@ function Add-M365DSCTelemetryEvent } $partialConfiguration = $false - if (-not [System.String]::IsNullOrEmpty($LCMInfo.PartialConfigurations)) + if (-not [System.String]::IsNullOrEmpty($Global:LCMInfo.PartialConfigurations)) { $partialConfiguration = $true } $Data.Add('LCMUsesPartialConfigurations', $partialConfiguration) $Data.Add('LCMCertificateConfigured', $certificateConfigured) - $Data.Add('LCMConfigurationMode', $LCMInfo.ConfigurationMode) - $Data.Add('LCMConfigurationModeFrequencyMins', $LCMInfo.ConfigurationModeFrequencyMins) - $Data.Add('LCMRefreshMode', $LCMInfo.RefreshMode) - $Data.Add('LCMState', $LCMInfo.LCMState) - $Data.Add('LCMStateDetail', $LCMInfo.LCMStateDetail) + $Data.Add('LCMConfigurationMode', $Global:LCMInfo.ConfigurationMode) + $Data.Add('LCMConfigurationModeFrequencyMins', $Global:LCMInfo.ConfigurationModeFrequencyMins) + $Data.Add('LCMRefreshMode', $Global:LCMInfo.RefreshMode) + $Data.Add('LCMState', $Global:LCMInfo.LCMState) + $Data.Add('LCMStateDetail', $Global:LCMInfo.LCMStateDetail) if ([System.String]::IsNullOrEmpty($Type)) { @@ -395,18 +398,18 @@ function Add-M365DSCTelemetryEvent { $Type = 'Export' } - elseif ($LCMInfo.LCMStateDetail -eq 'LCM is performing a consistency check.' -or ` - $LCMInfo.LCMStateDetail -eq 'LCM exécute une vérification de cohérence.' -or ` - $LCMInfo.LCMStateDetail -eq 'LCM führt gerade eine Konsistenzüberprüfung durch.') + elseif ($Global:LCMInfo.LCMStateDetail -eq 'LCM is performing a consistency check.' -or ` + $Global:LCMInfo.LCMStateDetail -eq 'LCM exécute une vérification de cohérence.' -or ` + $Global:LCMInfo.LCMStateDetail -eq 'LCM führt gerade eine Konsistenzüberprüfung durch.') { $Type = 'MonitoringScheduled' } - elseif ($LCMInfo.LCMStateDetail -eq 'LCM is testing node against the configuration.') + elseif ($Global:LCMInfo.LCMStateDetail -eq 'LCM is testing node against the configuration.') { $Type = 'MonitoringManual' } - elseif ($LCMInfo.LCMStateDetail -eq 'LCM is applying a new configuration.' -or ` - $LCMInfo.LCMStateDetail -eq 'LCM applique une nouvelle configuration.') + elseif ($Global:LCMInfo.LCMStateDetail -eq 'LCM is applying a new configuration.' -or ` + $Global:LCMInfo.LCMStateDetail -eq 'LCM applique une nouvelle configuration.') { $Type = 'ApplyingConfiguration' } From c7be3bba30031705cc23be2497082ce2cd3ce580 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 17 Sep 2024 20:23:03 -0400 Subject: [PATCH 09/14] Updates --- .../MSFT_SCInsiderRiskEntityList.psm1 | 208 +++++++++++++++++- .../MSFT_SCInsiderRiskEntityList.schema.mof | 10 +- .../Modules/M365DSCTelemetryEngine.psm1 | 28 +-- 3 files changed, 225 insertions(+), 21 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 index c5339bf426..a2d00ba6ba 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 @@ -41,7 +41,7 @@ function Get-TargetResource $SensitiveInformationTypes, [Parameter()] - [System.String[]] + [System.Management.Infrastructure.CimInstance[]] $Sites, [Parameter()] @@ -412,7 +412,7 @@ function Set-TargetResource $SensitiveInformationTypes, [Parameter()] - [System.String[]] + [System.Management.Infrastructure.CimInstance[]] $Sites, [Parameter()] @@ -520,10 +520,66 @@ function Set-TargetResource $value = @() foreach ($filePath in $FilePaths) { - $value += "{`"FlPthRgx`":`"$($filePath)`",`"isSrc`":true,`"isTrgt`":true}" + $value += "{`"FlPthRgx`":`"$($filePath.Replace('\', '\\'))`",`"isSrc`":true,`"isTrgt`":true}" } Write-Verbose -Message "Creating new FilePath Group {$Name} with values {$($value -join ',')}" - New-InsiderRiskEntityList -Type 'CustomDomainLists' ` + New-InsiderRiskEntityList -Type 'CustomFilePathRegexLists' ` + -Name $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -Entities $value | Out-Null + } + elseif ($ListType -eq 'CustomFileTypeLists') + { + $value = @() + foreach ($fileType in $FileTypes) + { + $value += "{`"Ext`":`"$fileType`"}" + } + Write-Verbose -Message "Creating new FileType Group {$Name} with values {$($value -join ',')}" + New-InsiderRiskEntityList -Type 'CustomFileTypeLists ' ` + -Name $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -Entities $value | Out-Null + } + elseif ($ListType -eq 'CustomKeywordLists') + { + $value = @() + foreach ($keyword in $Keywords) + { + $value += "{`"Name`":`"$keyword`"}" + } + Write-Verbose -Message "Creating new Keyword Group {$Name} with values {$($value -join ',')}" + New-InsiderRiskEntityList -Type 'CustomKeywordLists' ` + -Name $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -Entities $value | Out-Null + } + elseif ($ListType -eq 'CustomSensitiveInformationTypeLists') + { + $value = @() + foreach ($sit in $SensitiveInformationTypes) + { + $value += "{`"Guid`":`"$sit`"}" + } + Write-Verbose -Message "Creating new SIT Group {$Name} with values {$($value -join ',')}" + New-InsiderRiskEntityList -Type 'CustomSensitiveInformationTypeLists' ` + -Name $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -Entities $value | Out-Null + } + elseif ($ListType -eq 'CustomSiteLists') + { + $value = @() + foreach ($site in $Sites) + { + $value += "{`"Url`":`"$($site.Url)`";`"Name`":`"$($site.Name)`";`"Guid`":`"$($site.Guid)`"}" + } + Write-Verbose -Message "Creating new Site Group {$Name} with values {$($value -join ',')}" + New-InsiderRiskEntityList -Type 'CustomSiteLists' ` -Name $Name ` -DisplayName $DisplayName ` -Description $Description ` @@ -557,6 +613,148 @@ function Set-TargetResource Write-Verbose -Message "Adding entities: $($entitiesToAdd -join ',')" Write-Verbose -Message "Removing entities: $($entitiesToRemove -join ',')" + Set-InsiderRiskEntityList -Identity $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -AddEntities $entitiesToAdd ` + -RemoveEntities $entitiesToRemove | Out-Null + } + # Update File Path Group + elseif ($ListType -eq 'CustomFilePathRegexLists') + { + $entitiesToAdd = @() + $entitiesToRemove = @() + $differences = Compare-Object -ReferenceObject $currentInstance.FilePaths -DifferenceObject $FilePaths + foreach ($diff in $differences) + { + if ($diff.SideIndicator -eq '=>') + { + $entitiesToAdd += "{`"FlPthRgx`":`"$($diff.InputObject.Replace('\', '\\'))`",`"isSrc`":true,`"isTrgt`":true}" + } + else + { + $entitiesToRemove += "{`"FlPthRgx`":`"$($diff.InputObject.Replace('\', '\\'))`",`"isSrc`":true,`"isTrgt`":true}" + } + } + + Write-Verbose -Message "Updating File Path Group {$Name}" + Write-Verbose -Message "Adding entities: $($entitiesToAdd -join ',')" + Write-Verbose -Message "Removing entities: $($entitiesToRemove -join ',')" + + Set-InsiderRiskEntityList -Identity $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -AddEntities $entitiesToAdd ` + -RemoveEntities $entitiesToRemove | Out-Null + } + # Update File Type Group + elseif ($ListType -eq 'CustomFileTypeLists') + { + $entitiesToAdd = @() + $entitiesToRemove = @() + $differences = Compare-Object -ReferenceObject $currentInstance.FileTypes -DifferenceObject $FileTypes + foreach ($diff in $differences) + { + if ($diff.SideIndicator -eq '=>') + { + $entitiesToAdd += "{`"Ext`":`"$($diff.InputObject)`"}" + } + else + { + $entitiesToRemove += "{`"Ext`":`"$($diff.InputObject)`"}" + } + } + + Write-Verbose -Message "Updating File Type Group {$Name}" + Write-Verbose -Message "Adding entities: $($entitiesToAdd -join ',')" + Write-Verbose -Message "Removing entities: $($entitiesToRemove -join ',')" + + Set-InsiderRiskEntityList -Identity $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -AddEntities $entitiesToAdd ` + -RemoveEntities $entitiesToRemove | Out-Null + } + # Update Keywords Group + elseif ($ListType -eq 'CustomKeywordLists') + { + $entitiesToAdd = @() + $entitiesToRemove = @() + $differences = Compare-Object -ReferenceObject $currentInstance.Keywords -DifferenceObject $Keywords + foreach ($diff in $differences) + { + if ($diff.SideIndicator -eq '=>') + { + $entitiesToAdd += "{`"Name`":`"$($diff.InputObject)`"}" + } + else + { + $entitiesToRemove += "{`"Name`":`"$($diff.InputObject)`"}" + } + } + + Write-Verbose -Message "Updating Keyword Group {$Name}" + Write-Verbose -Message "Adding entities: $($entitiesToAdd -join ',')" + Write-Verbose -Message "Removing entities: $($entitiesToRemove -join ',')" + + Set-InsiderRiskEntityList -Identity $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -AddEntities $entitiesToAdd ` + -RemoveEntities $entitiesToRemove | Out-Null + } + # Update SIT Group + elseif ($ListType -eq 'CustomSensitiveInformationTypeLists') + { + $entitiesToAdd = @() + $entitiesToRemove = @() + $differences = Compare-Object -ReferenceObject $currentInstance.SensitiveInformationTypes -DifferenceObject $SensitiveInformationTypes + foreach ($diff in $differences) + { + if ($diff.SideIndicator -eq '=>') + { + $entitiesToAdd += "{`"Guid`":`"$($diff.InputObject)`"}" + } + else + { + $entitiesToRemove += "{`"Guid`":`"$($diff.InputObject)`"}" + } + } + + Write-Verbose -Message "Updating SIT Group {$Name}" + Write-Verbose -Message "Adding entities: $($entitiesToAdd -join ',')" + Write-Verbose -Message "Removing entities: $($entitiesToRemove -join ',')" + + Set-InsiderRiskEntityList -Identity $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -AddEntities $entitiesToAdd ` + -RemoveEntities $entitiesToRemove | Out-Null + } + # Update Sites Group + elseif ($ListType -eq 'CustomSiteLists') + { + $entitiesToAdd = @() + $entitiesToRemove = @() + $differences = Compare-Object -ReferenceObject $currentInstance.Sites.Url -DifferenceObject $Sites.Url + foreach ($diff in $differences) + { + if ($diff.SideIndicator -eq '=>') + { + $entry = $Sites | Where-Object -FilterScript {$_.Url -eq $diff.InputObject} + $entitiesToAdd += "{`"Url`":`"$($entry.Url)`";`"Name`":`"$($entry.Name)`";`"Guid`":`"$($entry.Guid)`"}" + } + else + { + $entry = $currentInstance.Sites | Where-Object -FilterScript {$_.Url -eq $diff.InputObject} + $entitiesToRemove += "{`"Url`":`"$($entry.Url)`";`"Name`":`"$($entry.Name)`";`"Guid`":`"$($entry.Guid)`"}" + } + } + + Write-Verbose -Message "Updating Sites Group {$Name}" + Write-Verbose -Message "Adding entities: $($entitiesToAdd -join ',')" + Write-Verbose -Message "Removing entities: $($entitiesToRemove -join ',')" + Set-InsiderRiskEntityList -Identity $Name ` -DisplayName $DisplayName ` -Description $Description ` @@ -615,7 +813,7 @@ function Test-TargetResource $SensitiveInformationTypes, [Parameter()] - [System.String[]] + [System.Management.Infrastructure.CimInstance[]] $Sites, [Parameter()] diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof index e005642f04..51d79fa639 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof @@ -5,7 +5,13 @@ class MSFT_SCInsiderRiskEntityListDomain [Write, Description("Defines if the entry should include multi-level subdomains or not.")] Boolean isMLSubDmn; [Write, Description("Type of the Sensitive Information label")] String type; }; - +[ClassVersion("1.0.0")] +class MSFT_SCInsiderRiskEntityListSite +{ + [Required, Description("Url of the site.")] String Url; + [Write, Description("Name of the site.")] String Name; + [Write, Description("Unique identifier of the site.")] String Guid; +}; [ClassVersion("1.0.0.0"), FriendlyName("SCInsiderRiskEntityList")] class MSFT_SCInsiderRiskEntityList : OMI_BaseResource { @@ -18,7 +24,7 @@ class MSFT_SCInsiderRiskEntityList : OMI_BaseResource [Write, Description("")] String FileTypes[]; [Write, Description("")] String Keywords[]; [Write, Description("")] String SensitiveInformationTypes[]; - [Write, Description("")] String Sites[]; + [Write, Description(""), EmbeddedInstance("MSFT_SCInsiderRiskEntityListSite")] String Sites[]; [Write, Description("")] String TrainableClassifiers[]; [Write, Description("")] String ExceptionKeyworkGroups[]; [Write, Description("")] String ExcludedClassifierGroups[]; diff --git a/Modules/Microsoft365DSC/Modules/M365DSCTelemetryEngine.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCTelemetryEngine.psm1 index 6d0b9f27e7..a393a98017 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCTelemetryEngine.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCTelemetryEngine.psm1 @@ -368,9 +368,9 @@ function Add-M365DSCTelemetryEvent # LCM Metadata Information try { - if ($null -eq $Global:LCMInfo) + if ($null -eq $Script:LCMInfo) { - $Global:LCMInfo = Get-DscLocalConfigurationManager -ErrorAction Stop + $Script:LCMInfo = Get-DscLocalConfigurationManager -ErrorAction Stop } $certificateConfigured = $false @@ -380,17 +380,17 @@ function Add-M365DSCTelemetryEvent } $partialConfiguration = $false - if (-not [System.String]::IsNullOrEmpty($Global:LCMInfo.PartialConfigurations)) + if (-not [System.String]::IsNullOrEmpty($Script:LCMInfo.PartialConfigurations)) { $partialConfiguration = $true } $Data.Add('LCMUsesPartialConfigurations', $partialConfiguration) $Data.Add('LCMCertificateConfigured', $certificateConfigured) - $Data.Add('LCMConfigurationMode', $Global:LCMInfo.ConfigurationMode) - $Data.Add('LCMConfigurationModeFrequencyMins', $Global:LCMInfo.ConfigurationModeFrequencyMins) - $Data.Add('LCMRefreshMode', $Global:LCMInfo.RefreshMode) - $Data.Add('LCMState', $Global:LCMInfo.LCMState) - $Data.Add('LCMStateDetail', $Global:LCMInfo.LCMStateDetail) + $Data.Add('LCMConfigurationMode', $Script:LCMInfo.ConfigurationMode) + $Data.Add('LCMConfigurationModeFrequencyMins', $Script:LCMInfo.ConfigurationModeFrequencyMins) + $Data.Add('LCMRefreshMode', $Script:LCMInfo.RefreshMode) + $Data.Add('LCMState', $Script:LCMInfo.LCMState) + $Data.Add('LCMStateDetail', $Script:LCMInfo.LCMStateDetail) if ([System.String]::IsNullOrEmpty($Type)) { @@ -398,18 +398,18 @@ function Add-M365DSCTelemetryEvent { $Type = 'Export' } - elseif ($Global:LCMInfo.LCMStateDetail -eq 'LCM is performing a consistency check.' -or ` - $Global:LCMInfo.LCMStateDetail -eq 'LCM exécute une vérification de cohérence.' -or ` - $Global:LCMInfo.LCMStateDetail -eq 'LCM führt gerade eine Konsistenzüberprüfung durch.') + elseif ($Script:LCMInfo.LCMStateDetail -eq 'LCM is performing a consistency check.' -or ` + $Script:LCMInfo.LCMStateDetail -eq 'LCM exécute une vérification de cohérence.' -or ` + $Script:LCMInfo.LCMStateDetail -eq 'LCM führt gerade eine Konsistenzüberprüfung durch.') { $Type = 'MonitoringScheduled' } - elseif ($Global:LCMInfo.LCMStateDetail -eq 'LCM is testing node against the configuration.') + elseif ($Script:LCMInfo.LCMStateDetail -eq 'LCM is testing node against the configuration.') { $Type = 'MonitoringManual' } - elseif ($Global:LCMInfo.LCMStateDetail -eq 'LCM is applying a new configuration.' -or ` - $Global:LCMInfo.LCMStateDetail -eq 'LCM applique une nouvelle configuration.') + elseif ($Script:LCMInfo.LCMStateDetail -eq 'LCM is applying a new configuration.' -or ` + $Script:LCMInfo.LCMStateDetail -eq 'LCM applique une nouvelle configuration.') { $Type = 'ApplyingConfiguration' } From 7999032962423ac6f330316dbbdc1145d77795e0 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 18 Sep 2024 06:57:25 -0400 Subject: [PATCH 10/14] Tests --- .../MSFT_SCInsiderRiskEntityList.psm1 | 1 + .../MSFT_SCInsiderRiskEntityList.schema.mof | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 index a2d00ba6ba..8ab587b2aa 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 @@ -481,6 +481,7 @@ function Set-TargetResource $AccessTokens ) + Write-Verbose -Message "Start Set-TargetResource" #Ensure the proper dependencies are installed in the current environment. Confirm-M365DSCDependencies diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof index 51d79fa639..7aec32fdc3 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof @@ -8,9 +8,9 @@ class MSFT_SCInsiderRiskEntityListDomain [ClassVersion("1.0.0")] class MSFT_SCInsiderRiskEntityListSite { - [Required, Description("Url of the site.")] String Url; - [Write, Description("Name of the site.")] String Name; - [Write, Description("Unique identifier of the site.")] String Guid; + [Required, Description("Url of the site.")] String SiteUrl; + [Write, Description("Name of the site.")] String SiteName; + [Write, Description("Unique identifier of the site.")] String SiteGuid; }; [ClassVersion("1.0.0.0"), FriendlyName("SCInsiderRiskEntityList")] class MSFT_SCInsiderRiskEntityList : OMI_BaseResource From 3f5b08db548f15984b81b7fe5d558b8e494ca7b9 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 18 Sep 2024 22:08:38 -0400 Subject: [PATCH 11/14] Fixes for Unit Tests --- .../MSFT_SCInsiderRiskEntityList.psm1 | 288 +++++++++++++++--- .../MSFT_SCInsiderRiskEntityList.schema.mof | 9 +- .../MSFT_SCInsiderRiskEntityList/readme.md | 2 +- .../settings.json | 22 +- .../SCInsiderRiskEntityList/1-Create.ps1 | 14 +- .../SCInsiderRiskEntityList/2-Update.ps1 | 14 +- .../SCInsiderRiskEntityList/3-Remove.ps1 | 14 +- .../Modules/M365DSCReport.psm1 | 9 +- .../Microsoft365DSC/Modules/M365DSCUtil.psm1 | 49 ++- ...ft365DSC.SCInsiderRiskEntityList.Tests.ps1 | 115 +++++-- Tests/Unit/Stubs/Microsoft365.psm1 | 92 ++++++ .../Microsoft365DSC.ResourceName.Tests.ps1 | 1 + 12 files changed, 533 insertions(+), 96 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 index 8ab587b2aa..a0d5e92ed3 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.psm1 @@ -41,7 +41,7 @@ function Get-TargetResource $SensitiveInformationTypes, [Parameter()] - [System.Management.Infrastructure.CimInstance[]] + [Microsoft.Management.Infrastructure.CimInstance[]] $Sites, [Parameter()] @@ -62,7 +62,7 @@ function Get-TargetResource [Parameter()] [System.String[]] - $ExcludedFilePathGroup, + $ExcludedFilePathGroups, [Parameter()] [System.String[]] @@ -209,7 +209,12 @@ function Get-TargetResource foreach ($entity in $instance.Entities) { $entity = ConvertFrom-Json $entity - $SiteValues += $entity.Url + $site = @{ + Url = $entity.Url + Name = $entity.Name + Guid = $entity.Guid + } + $SiteValues += $site } } @@ -220,7 +225,7 @@ function Get-TargetResource foreach ($entity in $instance.Entities) { $entity = ConvertFrom-Json $entity - $SiteValues += $entity.Url + $TrainableClassifierValues += $entity.Guid } } @@ -233,7 +238,7 @@ function Get-TargetResource { $entity = ConvertFrom-Json $entity $group = Get-InsiderRiskEntityList -Identity $entity.GroupId - $excludedKeywordGroupValue += $group.DisplayName + $excludedKeywordGroupValue += $group.Name } } @@ -246,7 +251,7 @@ function Get-TargetResource { $entity = ConvertFrom-Json $entity $group = Get-InsiderRiskEntityList -Identity $entity.GroupId - $exceptionKeywordGroupValue += $group.DisplayName + $exceptionKeywordGroupValue += $group.Name } } @@ -259,7 +264,7 @@ function Get-TargetResource { $entity = ConvertFrom-Json $entity $group = Get-InsiderRiskEntityList -Identity $entity.GroupId - $excludedClassifierGroupValue += $group.DisplayName + $excludedClassifierGroupValue += $group.Name } } @@ -272,12 +277,12 @@ function Get-TargetResource { $entity = ConvertFrom-Json $entity $group = Get-InsiderRiskEntityList -Identity $entity.GroupId - $excludedDomainGroupValue += $group.DisplayName + $excludedDomainGroupValue += $group.Name } } # Global Exclusions - Excluded File Path Groups - $excludedFilePathGroupValue = @() + $ExcludedFilePathGroupsValue = @() if ($instance.Name -eq 'IrmXSGFilePaths') { $entities = $instance.Entities @@ -285,7 +290,7 @@ function Get-TargetResource { $entity = ConvertFrom-Json $entity $group = Get-InsiderRiskEntityList -Identity $entity.GroupId - $excludedFilePathGroupValue += $group.DisplayName + $ExcludedFilePathGroupsValue += $group.Name } } @@ -298,7 +303,7 @@ function Get-TargetResource { $entity = ConvertFrom-Json $entity $group = Get-InsiderRiskEntityList -Identity $entity.GroupId - $excludedSiteGroupValue += $group.DisplayName + $excludedSiteGroupValue += $group.Name } } @@ -311,7 +316,7 @@ function Get-TargetResource { $entity = ConvertFrom-Json $entity $group = Get-InsiderRiskEntityList -Identity $entity.GroupId - $excludedSITGroupValue += $group.DisplayName + $excludedSITGroupValue += $group.Name } } @@ -324,7 +329,7 @@ function Get-TargetResource { $entity = ConvertFrom-Json $entity $group = Get-InsiderRiskEntityList -Identity $entity.GroupId - $excludedFileTypeGroupValue += $group.DisplayName + $excludedFileTypeGroupValue += $group.Name } } @@ -339,12 +344,12 @@ function Get-TargetResource Keywords = $KeywordValues SensitiveInformationTypes = $SITValues Sites = $SiteValues - #TrainableClassifiers = + TrainableClassifiers = $TrainableClassifierValues ExcludedKeyworkGroups = $excludedKeywordGroupValue ExceptionKeyworkGroups = $exceptionKeywordGroupValue ExcludedClassifierGroups = $excludedClassifierGroupValue ExcludedDomainGroups = $excludedDomainGroupValue - ExcludedFilePathGroup = $excludedFilePathGroupValue + ExcludedFilePathGroups = $ExcludedFilePathGroupsValue ExcludedSiteGroups = $excludedSiteGroupValue ExcludedSensitiveInformationTypeGroups = $excludedSITGroupValue ExcludedFileTypeGroups = $excludedFileTypeGroupValue @@ -412,7 +417,7 @@ function Set-TargetResource $SensitiveInformationTypes, [Parameter()] - [System.Management.Infrastructure.CimInstance[]] + [Microsoft.Management.Infrastructure.CimInstance[]] $Sites, [Parameter()] @@ -433,7 +438,7 @@ function Set-TargetResource [Parameter()] [System.String[]] - $ExcludedFilePathGroup, + $ExcludedFilePathGroups, [Parameter()] [System.String[]] @@ -481,7 +486,6 @@ function Set-TargetResource $AccessTokens ) - Write-Verbose -Message "Start Set-TargetResource" #Ensure the proper dependencies are installed in the current environment. Confirm-M365DSCDependencies @@ -496,8 +500,6 @@ function Set-TargetResource $currentInstance = Get-TargetResource @PSBoundParameters - $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters - # CREATE if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { @@ -577,21 +579,39 @@ function Set-TargetResource $value = @() foreach ($site in $Sites) { - $value += "{`"Url`":`"$($site.Url)`";`"Name`":`"$($site.Name)`";`"Guid`":`"$($site.Guid)`"}" + $value += "{`"Url`":`"$($site.Url.ToString())`",`"Name`":`"$($site.Name.ToString())`",`"Guid`":`"$((New-GUID).ToString())`"}" } - Write-Verbose -Message "Creating new Site Group {$Name} with values {$($value -join ',')}" + Write-Verbose -Message "Creating new Site Group {$Name} with values {$($value)}" New-InsiderRiskEntityList -Type 'CustomSiteLists' ` -Name $Name ` -DisplayName $DisplayName ` -Description $Description ` -Entities $value | Out-Null } + elseif ($ListType -eq 'CustomMLClassifierTypeLists') + { + $value = @() + foreach ($clasifier in $TrainableClassifiers) + { + $value += "{`"Guid`":`"$($classifier)`"}" + } + Write-Verbose -Message "Creating new Trainable classifier Group {$Name} with values {$($value)}" + New-InsiderRiskEntityList -Type 'CustomMLClassifierTypeLists' ` + -Name $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -Entities $value | Out-Null + } + else + { + throw "Couldn't not identify operation to perform on {$Name}" + } } # UPDATE elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') { # Update Domain Group - if ($ListType -eq 'CustomDomainLists') + if ($ListType -eq 'CustomDomainLists' -or $Name -eq 'IrmWhitelistDomains') { $entitiesToAdd = @() $entitiesToRemove = @() @@ -621,7 +641,8 @@ function Set-TargetResource -RemoveEntities $entitiesToRemove | Out-Null } # Update File Path Group - elseif ($ListType -eq 'CustomFilePathRegexLists') + elseif ($ListType -eq 'CustomFilePathRegexLists' -or $Name -eq 'IrmCustomExWinFilePaths' -or ` + $Name -eq 'IrmDsbldSysExWinFilePaths') { $entitiesToAdd = @() $entitiesToRemove = @() @@ -677,7 +698,7 @@ function Set-TargetResource -RemoveEntities $entitiesToRemove | Out-Null } # Update Keywords Group - elseif ($ListType -eq 'CustomKeywordLists') + elseif ($ListType -eq 'CustomKeywordLists' -or $Name -eq 'IrmExcludedKeywords' -or $Name -eq 'IrmNotExcludedKeywords') { $entitiesToAdd = @() $entitiesToRemove = @() @@ -705,7 +726,8 @@ function Set-TargetResource -RemoveEntities $entitiesToRemove | Out-Null } # Update SIT Group - elseif ($ListType -eq 'CustomSensitiveInformationTypeLists') + elseif ($ListType -eq 'CustomSensitiveInformationTypeLists' -or $Name -eq 'IrmCustomExSensitiveTypes ' -or ` + $Name -eq 'IrmDsbldSysExSensitiveTypes') { $entitiesToAdd = @() $entitiesToRemove = @() @@ -733,8 +755,9 @@ function Set-TargetResource -RemoveEntities $entitiesToRemove | Out-Null } # Update Sites Group - elseif ($ListType -eq 'CustomSiteLists') + elseif ($ListType -eq 'CustomSiteLists' -or $Name -eq 'IrmExcludedSites') { + Write-Verbose -Message "Calculating the difference in the Site list." $entitiesToAdd = @() $entitiesToRemove = @() $differences = Compare-Object -ReferenceObject $currentInstance.Sites.Url -DifferenceObject $Sites.Url @@ -743,12 +766,46 @@ function Set-TargetResource if ($diff.SideIndicator -eq '=>') { $entry = $Sites | Where-Object -FilterScript {$_.Url -eq $diff.InputObject} - $entitiesToAdd += "{`"Url`":`"$($entry.Url)`";`"Name`":`"$($entry.Name)`";`"Guid`":`"$($entry.Guid)`"}" + $guid = $entry.Guid + if ([System.String]::IsNullOrEmpty($guid)) + { + $guid = (New-Guid).ToString() + } + $entitiesToAdd += "{`"Url`":`"$($entry.Url)`",`"Name`":`"$($entry.Name)`",`"Guid`":`"$($guid)`"}" } else { $entry = $currentInstance.Sites | Where-Object -FilterScript {$_.Url -eq $diff.InputObject} - $entitiesToRemove += "{`"Url`":`"$($entry.Url)`";`"Name`":`"$($entry.Name)`";`"Guid`":`"$($entry.Guid)`"}" + $entitiesToRemove += "{`"Url`":`"$($entry.Url)`",`"Name`":`"$($entry.Name)`",`"Guid`":`"$($entry.Guid)`"}" + } + } + + Write-Verbose -Message "Updating Sites Group {$Name}" + Write-Verbose -Message "Adding entities: $($entitiesToAdd -join ',')" + Write-Verbose -Message "Removing entities: $($entitiesToRemove -join ',')" + + Set-InsiderRiskEntityList -Identity $Name ` + -DisplayName $DisplayName ` + -Description $Description ` + -AddEntities $entitiesToAdd ` + -RemoveEntities $entitiesToRemove | Out-Null + } + # Update Trainable Classifiers Group + elseif ($ListType -eq 'CustomMLClassifierTypeLists' -or $Name -eq 'IrmCustomExMLClassifiers' -or ` + $Name -eq 'IrmDsbldSysExMLClassifiers') + { + $entitiesToAdd = @() + $entitiesToRemove = @() + $differences = Compare-Object -ReferenceObject $currentInstance.Sites.Url -DifferenceObject $Sites.Url + foreach ($diff in $differences) + { + if ($diff.SideIndicator -eq '=>') + { + $entitiesToAdd += "{`"Guid`":`"$($diff.InputObject)`"}" + } + else + { + $entitiesToRemove += "{`"Guid`":`"$($diff.InputObject)`"}" } } @@ -762,12 +819,62 @@ function Set-TargetResource -AddEntities $entitiesToAdd ` -RemoveEntities $entitiesToRemove | Out-Null } + + <################## Group Exclusions #############> + if ($null -ne $ExcludedDomainGroups -and $ExcludedDomainGroups.Length -gt 0) + { + Set-M365DSCSCInsiderRiskExclusionGroup -CurrentValues $currentInstance.ExcludedDomainGroups ` + -DesiredValues $ExcludedDomainGroups ` + -Name 'IrmXSGDomains' + } + elseif ($null -ne $ExcludedFilePathGroups -and $ExcludedFilePathGroups.Length -gt 0) + { + Set-M365DSCSCInsiderRiskExclusionGroup -CurrentValues $currentInstance.ExcludedFilePathGroups ` + -DesiredValues $ExcludedFilePathGroups ` + -Name 'IrmXSGFilePaths' + } + elseif ($null -ne $ExcludedFileTypeGroups -and $ExcludedFileTypeGroups.Length -gt 0) + { + Set-M365DSCSCInsiderRiskExclusionGroup -CurrentValues $currentInstance.ExcludedFileTypeGroups ` + -DesiredValues $ExcludedFileTypeGroups ` + -Name 'IrmXSGFiletypes' + } + elseif ($null -ne $ExceptionKeyworkGroups -and $ExceptionKeyworkGroups.Length -gt 0) + { + Set-M365DSCSCInsiderRiskExclusionGroup -CurrentValues $currentInstance.ExceptionKeyworkGroups ` + -DesiredValues $ExceptionKeyworkGroups ` + -Name 'IrmXSGExcludedKeywords ' + } + elseif ($null -ne $ExcludedKeyworkGroups -and $ExcludedKeyworkGroups.Length -gt 0) + { + Set-M365DSCSCInsiderRiskExclusionGroup -CurrentValues $currentInstance.ExcludedKeyworkGroups ` + -DesiredValues $ExcludedKeyworkGroups ` + -Name 'IrmXSGExcludedKeywords ' + } + elseif ($null -ne $ExcludedSensitiveInformationTypeGroups -and $ExcludedSensitiveInformationTypeGroups.Length -gt 0) + { + Set-M365DSCSCInsiderRiskExclusionGroup -CurrentValues $currentInstance.ExcludedSensitiveInformationTypeGroups ` + -DesiredValues $ExcludedSensitiveInformationTypeGroups ` + -Name 'IrmXSGSensitiveInfoTypes ' + } + elseif ($null -ne $ExcludedSiteGroups -and $ExcludedSiteGroups.Length -gt 0) + { + Set-M365DSCSCInsiderRiskExclusionGroup -CurrentValues $currentInstance.ExcludedSiteGroups ` + -DesiredValues $ExcludedSiteGroups ` + -Name 'IrmXSGSites ' + } + elseif ($null -ne $ExcludedClassifierGroups -and $ExcludedClassifierGroups.Length -gt 0) + { + Set-M365DSCSCInsiderRiskExclusionGroup -CurrentValues $currentInstance.ExcludedClassifierGroups ` + -DesiredValues $ExcludedClassifierGroups ` + -Name 'IrmXSGMLClassifierTypes ' + } } # REMOVE elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') { - ##TODO - Replace by the Remove cmdlet for the resource - Remove-cmdlet @SetParameters + Write-Verbose -Message "Removing group {$Name}" + Remove-InsiderRiskEntityList -Identity $Name -ForceDeletion } } @@ -814,7 +921,7 @@ function Test-TargetResource $SensitiveInformationTypes, [Parameter()] - [System.Management.Infrastructure.CimInstance[]] + [Microsoft.Management.Infrastructure.CimInstance[]] $Sites, [Parameter()] @@ -835,7 +942,7 @@ function Test-TargetResource [Parameter()] [System.String[]] - $ExcludedFilePathGroup, + $ExcludedFilePathGroups, [Parameter()] [System.String[]] @@ -897,7 +1004,6 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() - $ValuesToCheck.Remove('Name') | Out-Null Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" @@ -1007,6 +1113,19 @@ function Export-TargetResource } $Results = Get-TargetResource @Params + + if ($null -ne $Results.Domains -and $Results.Domains.Length -gt 0 -and ` + ($Results.ListType -eq 'CustomDomainLists' -or $Results.ListType -eq 'DomainLists')) + { + $Results.Domains = ConvertTo-M365DSCSCInsiderRiskDomainToString -Domains $Results.Domains + } + + if ($null -ne $Results.Sites -and $Results.Sites.Length -gt 0 -and ` + ($Results.ListType -eq 'CustomSiteLists' -or $Results.ListType -eq 'SiteLists')) + { + $Results.Sites = ConvertTo-M365DSCSCInsiderRiskSiteToString -Sites $Results.Sites + } + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results @@ -1015,6 +1134,19 @@ function Export-TargetResource -ModulePath $PSScriptRoot ` -Results $Results ` -Credential $Credential + + if ($null -ne $Results.Domains -and ` + ($Results.ListType -eq 'CustomDomainLists' -or $Results.ListType -eq 'DomainLists')) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Domains' -IsCIMArray $true + } + + if ($null -ne $Results.Sites -and ` + ($Results.ListType -eq 'CustomSiteLists' -or $Results.ListType -eq 'SiteLists')) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Sites' -IsCIMArray $true + } + $dscContent += $currentDSCBlock Save-M365DSCPartialExport -Content $currentDSCBlock ` -FileName $Global:PartialExportFileName @@ -1037,4 +1169,92 @@ function Export-TargetResource } } +function ConvertTo-M365DSCSCInsiderRiskDomainToString +{ + [CmdletBinding()] + [OutputType([System.String])] + param( + [Parameter(Mandatory=$true)] + [System.Object[]] + $Domains + ) + + $content = "@(" + foreach ($domain in $Domains) + { + $content += "MSFT_SCInsiderRiskEntityListDomain`r`n" + $content += "{`r`n" + $content += " Dmn = '$($domain.Dmn)'`r`n" + $content += " isMLSubDmn = `$$($domain.isMLSubDmn)`r`n" + $content += "}`r`n" + } + $content += ")" + return $content +} + +function ConvertTo-M365DSCSCInsiderRiskSiteToString +{ + [CmdletBinding()] + [OutputType([System.String])] + param( + [Parameter(Mandatory=$true)] + [System.Object[]] + $Sites + ) + + $content = "@(" + foreach ($site in $Sites) + { + $content += "MSFT_SCInsiderRiskEntityListSite`r`n" + $content += "{`r`n" + $content += " Url = '$($site.Url)'`r`n" + $content += " Name = '$($site.Name)'`r`n" + $content += " Guid = '$($site.Guid)'`r`n" + $content += "}`r`n" + } + $content += ")" + return $content +} + +function Set-M365DSCSCInsiderRiskExclusionGroup +{ + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [System.String[]] + $CurrentValues, + + [Parameter(Mandatory = $true)] + [System.String[]] + $DesiredValues, + + [Parameter(Mandatory = $true)] + [System.String] + $Name + ) + + $entitiesToAdd = @() + $entitiesToRemove = @() + $differences = Compare-Object -ReferenceObject $CurrentValues -DifferenceObject $DesiredValues + foreach ($diff in $differences) + { + if ($diff.SideIndicator -eq '=>') + { + $entitiesToAdd += "{`"GroupId`":`"$($diff.InputObject)`"}" + } + else + { + $entitiesToRemove += "{`"GroupId`":`"$($diff.InputObject)`"}" + } + } + + Write-Verbose -Message "Updating Group Exclusions for {$Name}" + Write-Verbose -Message "Adding entities: $($entitiesToAdd -join ',')" + Write-Verbose -Message "Removing entities: $($entitiesToRemove -join ',')" + + Set-InsiderRiskEntityList -Identity $Name ` + -AddEntities $entitiesToAdd ` + -RemoveEntities $entitiesToRemove | Out-Null +} + Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof index 7aec32fdc3..dbf3fcf00f 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof @@ -3,14 +3,13 @@ class MSFT_SCInsiderRiskEntityListDomain { [Required, Description("Domain name.")] String Dmn; [Write, Description("Defines if the entry should include multi-level subdomains or not.")] Boolean isMLSubDmn; - [Write, Description("Type of the Sensitive Information label")] String type; }; [ClassVersion("1.0.0")] class MSFT_SCInsiderRiskEntityListSite { - [Required, Description("Url of the site.")] String SiteUrl; - [Write, Description("Name of the site.")] String SiteName; - [Write, Description("Unique identifier of the site.")] String SiteGuid; + [Required, Description("Url of the site.")] String Url; + [Write, Description("Name of the site.")] String Name; + [Write, Description("Unique identifier of the site.")] String Guid; }; [ClassVersion("1.0.0.0"), FriendlyName("SCInsiderRiskEntityList")] class MSFT_SCInsiderRiskEntityList : OMI_BaseResource @@ -29,7 +28,7 @@ class MSFT_SCInsiderRiskEntityList : OMI_BaseResource [Write, Description("")] String ExceptionKeyworkGroups[]; [Write, Description("")] String ExcludedClassifierGroups[]; [Write, Description("")] String ExcludedDomainGroups[]; - [Write, Description("")] String ExcludedFilePathGroup[]; + [Write, Description("")] String ExcludedFilePathGroups[]; [Write, Description("")] String ExcludedFileTypeGroups[]; [Write, Description("")] String ExcludedKeyworkGroups[]; [Write, Description("")] String ExcludedSensitiveInformationTypeGroups[]; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/readme.md index 1a24877790..75a6e455f5 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/readme.md +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/readme.md @@ -3,4 +3,4 @@ ## Description -##TODO - Provide a short description of what the resource is set to configure. +Configures settings for Insider Risk in Purview. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/settings.json index c44468a9df..98c729187d 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/settings.json +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/settings.json @@ -1,13 +1,9 @@ { "resourceName": "SCInsiderRiskEntityList", - "description": "Description of what the resource is about.", + "description": "Configures settings for Insider Risk in Purview.", "roles": { - "read": [ - "Role" - ], - "update": [ - "Role" - ] + "read": [], + "update": [] }, "permissions": { "graph": { @@ -16,16 +12,8 @@ "update": [] }, "application": { - "read": [ - { - "name": "Permission for Monitoring and Export" - } - ], - "update": [ - { - "name": "Permission for deploying" - } - ] + "read": [], + "update": [] } } } diff --git a/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/1-Create.ps1 index b516274848..b1d724763e 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/1-Create.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/1-Create.ps1 @@ -21,6 +21,18 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + SCInsiderRiskEntityList "SCInsiderRiskEntityList-MyFileType" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Description = "Test file type"; + DisplayName = "MyFileType"; + Ensure = "Present"; + FileTypes = @(".exe",".cmd",".bat"); + Keywords = @(); + ListType = "CustomFileTypeLists"; + Name = "MyFileTypeList"; + TenantId = $OrganizationName; + } } } diff --git a/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/2-Update.ps1 index b516274848..c230194a30 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/2-Update.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/2-Update.ps1 @@ -21,6 +21,18 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + SCInsiderRiskEntityList "SCInsiderRiskEntityList-MyFileType" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Description = "Test file type"; + DisplayName = "MyFileType"; + Ensure = "Present"; + FileTypes = @(".exe",".txt",".bat"); # Drfit + Keywords = @(); + ListType = "CustomFileTypeLists"; + Name = "MyFileTypeList"; + TenantId = $OrganizationName; + } } } diff --git a/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/3-Remove.ps1 index b516274848..cf49044588 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/3-Remove.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/SCInsiderRiskEntityList/3-Remove.ps1 @@ -21,6 +21,18 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + SCInsiderRiskEntityList "SCInsiderRiskEntityList-MyFileType" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Description = "Test file type"; + DisplayName = "MyFileType"; + Ensure = "Absent"; + FileTypes = @(".exe",".cmd",".bat"); + Keywords = @(); + ListType = "CustomFileTypeLists"; + Name = "MyFileTypeList"; + TenantId = $OrganizationName; + } } } diff --git a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 index e10606ca0c..c7209dbf4d 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 @@ -1169,7 +1169,7 @@ function Compare-M365DSCConfigurations This function gets the key parameter for the specified CIMInstance .Functionality -Internal, Hidden +Public #> function Get-M365DSCCIMInstanceKey { @@ -1223,6 +1223,10 @@ function Get-M365DSCCIMInstanceKey { $primaryKey = 'dataType' } + elseif ($CIMInstance.ContainsKey("Dmn")) + { + $primaryKey = 'Dmn' + } return $primaryKey } @@ -1931,5 +1935,6 @@ function Initialize-M365DSCReporting Export-ModuleMember -Function @( 'Compare-M365DSCConfigurations', 'New-M365DSCDeltaReport', - 'New-M365DSCReportFromConfiguration' + 'New-M365DSCReportFromConfiguration', + 'Get-M365DSCCIMInstanceKey' ) diff --git a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 index ac59719bcf..d2baffcc43 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 @@ -462,7 +462,7 @@ function Compare-PSCustomObjectArrays Desired = $DesiredEntry.$KeyProperty Current = $null } - $DriftedProperties += $DesiredEntry + $DriftedProperties += $result } else { @@ -496,6 +496,53 @@ function Compare-PSCustomObjectArrays } } + foreach ($currentEntry in $currentValues) + { + $KeyProperty = Get-M365DSCCIMInstanceKey -CIMInstance $currentEntry + + $EquivalentEntryInDesired = $DesiredValues | Where-Object -FilterScript { $_.$KeyProperty -eq $currentEntry.$KeyProperty } + if ($null -eq $EquivalentEntryInDesired) + { + $result = @{ + Property = $currentEntry + PropertyName = $KeyProperty + Desired = $currentEntry.$KeyProperty + Current = $null + } + $DriftedProperties += $result + } + else + { + foreach ($property in $Properties) + { + $propertyName = $property.Name + + if ((-not [System.String]::IsNullOrEmpty($currentEntry.$PropertyName) -and -not [System.String]::IsNullOrEmpty($EquivalentEntryInDesired.$PropertyName)) -and ` + $currentEntry.$PropertyName -ne $EquivalentEntryInDesired.$PropertyName) + { + $drift = $true + if ($currentEntry.$PropertyName.GetType().Name -eq 'String' -and $currentEntry.$PropertyName.Contains('$OrganizationName')) + { + if ($currentEntry.$PropertyName.Split('@')[0] -eq $EquivalentEntryInDesired.$PropertyName.Split('@')[0]) + { + $drift = $false + } + } + if ($drift) + { + $result = @{ + Property = $currentEntry + PropertyName = $PropertyName + Desired = $currentEntry.$PropertyName + Current = $EquivalentEntryInDesired.$PropertyName + } + $DriftedProperties += $result + } + } + } + } + } + return $DriftedProperties } diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SCInsiderRiskEntityList.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SCInsiderRiskEntityList.Tests.ps1 index 20857e0393..8fb7fa383d 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SCInsiderRiskEntityList.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SCInsiderRiskEntityList.Tests.ps1 @@ -35,7 +35,14 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { return "Credentials" } - ##TODO - Mock any Remove/Set/New cmdlets + Mock -CommandName Set-InsiderRiskEntityList -MockWith { + } + + Mock -CommandName New-InsiderRiskEntityList -MockWith { + } + + Mock -CommandName Remove-InsiderRiskEntityList -MockWith { + } # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { @@ -47,13 +54,16 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Context -Name "The instance should exist but it DOES NOT" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + Description = "Test Description"; + DisplayName = "TestFileTypeList"; + Ensure = "Present"; + FileTypes = @(".exe",".cmd",".bat"); + ListType = "CustomFileTypeLists"; + Name = "TestName"; + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return $null - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-InsiderRiskEntityList -MockWith { return $null } } @@ -65,23 +75,34 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } It 'Should create a new instance from the Set method' { - ##TODO - Replace the New-Cmdlet by the appropriate one - Should -Invoke -CommandName New-Cmdlet -Exactly 1 + Set-TargetResource @testParams + Should -Invoke -CommandName New-InsiderRiskEntityList -Exactly 1 } } Context -Name "The instance exists but it SHOULD NOT" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Absent' - Credential = $Credential; + Description = "Test Description"; + DisplayName = "TestFileTypeList"; + Ensure = "Absent"; + FileTypes = @(".exe",".cmd",".bat"); + ListType = "CustomFileTypeLists"; + Name = "TestName"; + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return an instance - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-InsiderRiskEntityList -MockWith { return @{ - + ListType = 'CustomFileTypeLists' + Name = 'TestName'; + DisplayName = "TestFileTypeList"; + Description = "Test Description"; + Entities = @( + '{"Ext":".exe"}', + '{"Ext":".cmd"}', + '{"Ext":".bat"}' + ) } } } @@ -93,23 +114,34 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } It 'Should remove the instance from the Set method' { - ##TODO - Replace the Remove-Cmdlet by the appropriate one - Should -Invoke -CommandName Remove-Cmdlet -Exactly 1 + Set-TargetResource @testParams + Should -Invoke -CommandName Remove-InsiderRiskEntityList -Exactly 1 } } Context -Name "The instance exists and values are already in the desired state" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + Description = "Test Description"; + DisplayName = "TestFileTypeList"; + Ensure = "Present"; + FileTypes = @(".exe",".cmd",".bat"); + ListType = "CustomFileTypeLists"; + Name = "TestName"; + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return the desired values - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-InsiderRiskEntityList -MockWith { return @{ - + ListType = 'CustomFileTypeLists' + Name = 'TestName'; + DisplayName = "TestFileTypeList"; + Description = "Test Description"; + Entities = @( + '{"Ext":".exe"}', + '{"Ext":".cmd"}', + '{"Ext":".bat"}' + ) } } } @@ -122,15 +154,26 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Context -Name "The instance exists and values are NOT in the desired state" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + Description = "Test Description"; + DisplayName = "TestFileTypeList"; + Ensure = "Present"; + FileTypes = @(".exe",".cmd",".bat"); + ListType = "CustomFileTypeLists"; + Name = "TestName"; + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return a drift - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-InsiderRiskEntityList -MockWith { return @{ - + ListType = 'CustomFileTypeLists' + Name = 'TestName'; + DisplayName = "TestFileTypeList"; + Description = "Test Description"; + Entities = @( + '{"Ext":".exe"}', + '{"Ext":".txt"}', #drift + '{"Ext":".bat"}' + ) } } } @@ -145,8 +188,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { It 'Should call the Set method' { Set-TargetResource @testParams - ##TODO - Replace the Update-Cmdlet by the appropriate one - Should -Invoke -CommandName Update-Cmdlet -Exactly 1 + Should -Invoke -CommandName Set-InsiderRiskEntityList -Exactly 1 } } @@ -158,10 +200,17 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return an instance - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-InsiderRiskEntityList -MockWith { return @{ - + ListType = 'CustomFileTypeLists' + Name = 'TestName'; + DisplayName = "TestFileTypeList"; + Description = "Test Description"; + Entities = @( + '{"Ext":".exe"}', + '{"Ext":".cmd"}', + '{"Ext":".bat"}' + ) } } } diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index 36fb0879a9..b9278ef7cc 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -70030,6 +70030,98 @@ function Set-SupervisoryReviewPolicy $SamplingRate ) } +function Set-InsiderRiskEntityList +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Identity, + + [Parameter()] + [System.Object[]] + $Entities, + + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.Object[]] + $AddEntities, + + [Parameter()] + [System.Object[]] + $RemoveEntities + ) +} + +function New-InsiderRiskEntityList +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Identity, + + [Parameter()] + [System.Object[]] + $Entities, + + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $Type + ) +} + +function Remove-InsiderRiskEntityList +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Identity, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ForceDeletion + ) +} + +function Get-InsiderRiskEntityList +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Identity, + + [Parameter()] + [System.String] + $Type + ) +} + function Set-SupervisoryReviewPolicyV2 { [CmdletBinding()] diff --git a/dev-package/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 b/dev-package/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 index 20857e0393..f3ddc7d594 100644 --- a/dev-package/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 +++ b/dev-package/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 @@ -66,6 +66,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { It 'Should create a new instance from the Set method' { ##TODO - Replace the New-Cmdlet by the appropriate one + Set-TargetResource @testParams Should -Invoke -CommandName New-Cmdlet -Exactly 1 } } From 029e0fd6047132abd35f28088f5f3e10ae37bd98 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 18 Sep 2024 22:30:41 -0400 Subject: [PATCH 12/14] Update MSFT_SCInsiderRiskEntityList.schema.mof --- .../MSFT_SCInsiderRiskEntityList.schema.mof | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof index dbf3fcf00f..4c2bba503b 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof @@ -14,25 +14,25 @@ class MSFT_SCInsiderRiskEntityListSite [ClassVersion("1.0.0.0"), FriendlyName("SCInsiderRiskEntityList")] class MSFT_SCInsiderRiskEntityList : OMI_BaseResource { - [Key, Description("")] String Name; - [Required, Description("")] String ListType; - [Write, Description("")] String Description; - [Write, Description("")] String DisplayName; - [Write, Description(""), EmbeddedInstance("MSFT_SCInsiderRiskEntityListDomain")] String Domains[]; - [Write, Description("")] String FilePaths[]; - [Write, Description("")] String FileTypes[]; - [Write, Description("")] String Keywords[]; - [Write, Description("")] String SensitiveInformationTypes[]; - [Write, Description(""), EmbeddedInstance("MSFT_SCInsiderRiskEntityListSite")] String Sites[]; - [Write, Description("")] String TrainableClassifiers[]; - [Write, Description("")] String ExceptionKeyworkGroups[]; - [Write, Description("")] String ExcludedClassifierGroups[]; - [Write, Description("")] String ExcludedDomainGroups[]; - [Write, Description("")] String ExcludedFilePathGroups[]; - [Write, Description("")] String ExcludedFileTypeGroups[]; - [Write, Description("")] String ExcludedKeyworkGroups[]; - [Write, Description("")] String ExcludedSensitiveInformationTypeGroups[]; - [Write, Description("")] String ExcludedSiteGroups[]; + [Key, Description("The name of the group or setting.")] String Name; + [Required, Description("The setting type.")] String ListType; + [Write, DescriptionDescription for the group or setting. String Description; + [Write, Description("The display name of the group or setting.")] String DisplayName; + [Write, Description("List of domains"), EmbeddedInstance("MSFT_SCInsiderRiskEntityListDomain")] String Domains[]; + [Write, Description("List of file paths.")] String FilePaths[]; + [Write, Description("List of file types")] String FileTypes[]; + [Write, Description("List of keywords")] String Keywords[]; + [Write, Description("List of sensitive information types.")] String SensitiveInformationTypes[]; + [Write, Description("List of sites."), EmbeddedInstance("MSFT_SCInsiderRiskEntityListSite")] String Sites[]; + [Write, Description("List of trainable classifiers.")] String TrainableClassifiers[]; + [Write, Description("List of keywords for exception.")] String ExceptionKeyworkGroups[]; + [Write, Description("List of excluded trainable classifiers.")] String ExcludedClassifierGroups[]; + [Write, Description("List of excluded domains.")] String ExcludedDomainGroups[]; + [Write, Description("List of excluded file paths.")] String ExcludedFilePathGroups[]; + [Write, Description("List of excluded file types.")] String ExcludedFileTypeGroups[]; + [Write, Description("List of excluded keywords.")] String ExcludedKeyworkGroups[]; + [Write, Description("List of excluded sensitive information types.")] String ExcludedSensitiveInformationTypeGroups[]; + [Write, Description("List of excluded sites.")] String ExcludedSiteGroups[]; [Write, Description("Specify if this entity should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] 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; From 232aee04fa1872fca0457c5bb5ffe3f01f3e95f9 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 18 Sep 2024 22:41:46 -0400 Subject: [PATCH 13/14] Update MSFT_SCInsiderRiskEntityList.schema.mof --- .../MSFT_SCInsiderRiskEntityList.schema.mof | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof index 4c2bba503b..020a45d679 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SCInsiderRiskEntityList/MSFT_SCInsiderRiskEntityList.schema.mof @@ -16,12 +16,12 @@ class MSFT_SCInsiderRiskEntityList : OMI_BaseResource { [Key, Description("The name of the group or setting.")] String Name; [Required, Description("The setting type.")] String ListType; - [Write, DescriptionDescription for the group or setting. String Description; + [Write, Description("Description for the group or setting.")] String Description; [Write, Description("The display name of the group or setting.")] String DisplayName; [Write, Description("List of domains"), EmbeddedInstance("MSFT_SCInsiderRiskEntityListDomain")] String Domains[]; [Write, Description("List of file paths.")] String FilePaths[]; - [Write, Description("List of file types")] String FileTypes[]; - [Write, Description("List of keywords")] String Keywords[]; + [Write, Description("List of file types.")] String FileTypes[]; + [Write, Description("List of keywords.")] String Keywords[]; [Write, Description("List of sensitive information types.")] String SensitiveInformationTypes[]; [Write, Description("List of sites."), EmbeddedInstance("MSFT_SCInsiderRiskEntityListSite")] String Sites[]; [Write, Description("List of trainable classifiers.")] String TrainableClassifiers[]; From 84e8d2b2baef7e4e78407fc3d7f86ca8a28de32b Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Thu, 19 Sep 2024 07:39:19 -0400 Subject: [PATCH 14/14] Fixed Unit Tests --- .../Modules/M365DSCReport.psm1 | 8 ++++ .../Microsoft365DSC/Modules/M365DSCUtil.psm1 | 45 ++++++++++++++----- ...oft365DSC.SPOUserProfileProperty.Tests.ps1 | 6 ++- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 index c7209dbf4d..2545a03b39 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCReport.psm1 @@ -1227,6 +1227,14 @@ function Get-M365DSCCIMInstanceKey { $primaryKey = 'Dmn' } + elseif ($CIMInstance.ContainsKey('EmergencyDialString')) + { + $primaryKey = 'EmergencyDialString' + } + else + { + $primaryKey = $CIMInstance.Keys[0] + } return $primaryKey } diff --git a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 index d2baffcc43..d899a833d1 100644 --- a/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 +++ b/Modules/Microsoft365DSC/Modules/M365DSCUtil.psm1 @@ -498,15 +498,24 @@ function Compare-PSCustomObjectArrays foreach ($currentEntry in $currentValues) { - $KeyProperty = Get-M365DSCCIMInstanceKey -CIMInstance $currentEntry + if ($currentEntry.GetType().Name -eq 'PSCustomObject') + { + $fixedEntry = @{} + $currentEntry.psobject.properties | Foreach { $fixedEntry[$_.Name] = $_.Value } + } + else + { + $fixedEntry = $currentEntry + } + $KeyProperty = Get-M365DSCCIMInstanceKey -CIMInstance $fixedEntry - $EquivalentEntryInDesired = $DesiredValues | Where-Object -FilterScript { $_.$KeyProperty -eq $currentEntry.$KeyProperty } + $EquivalentEntryInDesired = $DesiredValues | Where-Object -FilterScript { $_.$KeyProperty -eq $fixedEntry.$KeyProperty } if ($null -eq $EquivalentEntryInDesired) { $result = @{ - Property = $currentEntry + Property = $fixedEntry PropertyName = $KeyProperty - Desired = $currentEntry.$KeyProperty + Desired = $fixedEntry.$KeyProperty Current = $null } $DriftedProperties += $result @@ -517,13 +526,13 @@ function Compare-PSCustomObjectArrays { $propertyName = $property.Name - if ((-not [System.String]::IsNullOrEmpty($currentEntry.$PropertyName) -and -not [System.String]::IsNullOrEmpty($EquivalentEntryInDesired.$PropertyName)) -and ` - $currentEntry.$PropertyName -ne $EquivalentEntryInDesired.$PropertyName) + if ((-not [System.String]::IsNullOrEmpty($fixedEntry.$PropertyName) -and -not [System.String]::IsNullOrEmpty($EquivalentEntryInDesired.$PropertyName)) -and ` + $fixedEntry.$PropertyName -ne $EquivalentEntryInDesired.$PropertyName) { $drift = $true - if ($currentEntry.$PropertyName.GetType().Name -eq 'String' -and $currentEntry.$PropertyName.Contains('$OrganizationName')) + if ($fixedEntry.$PropertyName.GetType().Name -eq 'String' -and $fixedEntry.$PropertyName.Contains('$OrganizationName')) { - if ($currentEntry.$PropertyName.Split('@')[0] -eq $EquivalentEntryInDesired.$PropertyName.Split('@')[0]) + if ($fixedEntry.$PropertyName.Split('@')[0] -eq $EquivalentEntryInDesired.$PropertyName.Split('@')[0]) { $drift = $false } @@ -531,9 +540,9 @@ function Compare-PSCustomObjectArrays if ($drift) { $result = @{ - Property = $currentEntry + Property = $fixedEntry PropertyName = $PropertyName - Desired = $currentEntry.$PropertyName + Desired = $fixedEntry.$PropertyName Current = $EquivalentEntryInDesired.$PropertyName } $DriftedProperties += $result @@ -747,8 +756,20 @@ function Test-M365DSCParameterState } $AllDesiredValuesAsArray += [PSCustomObject]$currentEntry } - $arrayCompare = Compare-PSCustomObjectArrays -CurrentValues $CurrentValues.$fieldName ` - -DesiredValues $AllDesiredValuesAsArray + try + { + $arrayCompare = $null + if ($CurrentValues.$fieldName.GetType().Name -ne 'CimInstance' -and ` + $CurrentValues.$fieldName.GetType().Name -ne 'CimInstance[]') + { + $arrayCompare = Compare-PSCustomObjectArrays -CurrentValues $CurrentValues.$fieldName ` + -DesiredValues $AllDesiredValuesAsArray + } + } + catch + { + Write-Verbose -Message $_ + } if ($null -ne $arrayCompare) { diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SPOUserProfileProperty.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SPOUserProfileProperty.Tests.ps1 index 64b8e2e90c..d082a08989 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SPOUserProfileProperty.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SPOUserProfileProperty.Tests.ps1 @@ -101,7 +101,11 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Mock -CommandName Get-PnPUserProfileProperty -MockWith { return @{ AccountName = 'john.smith@contoso.com' - UserProfileProperties = @{'MyOldKey' = 'MyValue' } + UserProfileProperties = @( + @{ + MyOldKey = 'MyValue' + } + ) } } }