From cab09e60c7dd9f924481e4d8b5a11cfbb040bf74 Mon Sep 17 00:00:00 2001 From: Sai Sistla Date: Mon, 2 Sep 2024 19:10:32 +0530 Subject: [PATCH 01/46] Added changes for EmailTenantSettings --- .../MSFT_EXOEmailTenantSettings.psm1 | 450 ++++++++++++++++++ .../MSFT_EXOEmailTenantSettings.schema.mof | 19 + .../MSFT_EXOEmailTenantSettings/readme.md | 5 + .../MSFT_EXOEmailTenantSettings/settings.json | 34 ++ .../EXOEmailTenantSettings/2-Update.ps1 | 37 ++ ...oft365DSC.EXOEmailTenantSettings.Tests.ps1 | 136 ++++++ Tests/Unit/Stubs/Microsoft365.psm1 | 18 + 7 files changed, 699 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 new file mode 100644 index 0000000000..89d50d48a3 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 @@ -0,0 +1,450 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + [ValidateSet('Yes')] + $IsSingleInstance, + + [Parameter()] + [System.Boolean] + $EnablePriorityAccountProtection, + + [Parameter()] + [System.Boolean] + $IsValid, + + [Parameter()] + [System.String] + $Identity, + + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.String] + $ObjectState, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [System.String] + $CertificatePath, + + [Parameter()] + [System.Management.Automation.PSCredential] + $CertificatePassword, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + Write-Verbose -Message 'Getting EXO Email Tenant Settings' + + if ($Global:CurrentModeIsExport) + { + $ConnectionMode = New-M365DSCConnection -Workload 'ExchangeOnline' ` + -InboundParameters $PSBoundParameters ` + -SkipModuleReload $true + } + else + { + $ConnectionMode = New-M365DSCConnection -Workload 'ExchangeOnline' ` + -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 + + $nullReturn = $PSBoundParameters + $nullReturn.Ensure = 'Absent' + + try + { + $EmailTenantSettings = Get-EmailTenantSettings -ErrorAction Stop + + $result = @{ + IsSingleInstance = 'Yes' + Identity = $EmailTenantSettings.Identity + EnablePriorityAccountProtection = $EmailTenantSettings.EnablePriorityAccountProtection + Name = $EmailTenantSettings.Name + IsValid = $EmailTenantSettings.IsValid + ObjectState = $EmailTenantSettings.ObjectState + Credential = $Credential + Ensure = 'Present' + ApplicationId = $ApplicationId + CertificateThumbprint = $CertificateThumbprint + CertificatePath = $CertificatePath + CertificatePassword = $CertificatePassword + Managedidentity = $ManagedIdentity.IsPresent + TenantId = $TenantId + AccessTokens = $AccessTokens + } + + Write-Verbose -Message 'Found Email Tenant Settings config ' + Write-Verbose -Message "Get-TargetResource Result: `n $(Convert-M365DscHashtableToString -Hashtable $result)" + return $result + } + catch + { + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullReturn + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + [ValidateSet('Yes')] + $IsSingleInstance, + + [Parameter()] + [System.Boolean] + $EnablePriorityAccountProtection, + + [Parameter()] + [System.Boolean] + $IsValid, + + [Parameter()] + [System.String] + $Identity, + + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.String] + $ObjectState, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [System.String] + $CertificatePath, + + [Parameter()] + [System.Management.Automation.PSCredential] + $CertificatePassword, + + [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 + + Write-Verbose -Message 'Setting configuration of Email tenant setings' + + $ConnectionMode = New-M365DSCConnection -Workload 'ExchangeOnline' ` + -InboundParameters $PSBoundParameters + + $EmailTenantSettingsParams = [System.Collections.Hashtable]($PSBoundParameters) + $EmailTenantSettingsParams.Remove('Ensure') | Out-Null + $EmailTenantSettingsParams.Remove('Credential') | Out-Null + $EmailTenantSettingsParams.Remove('ApplicationId') | Out-Null + $EmailTenantSettingsParams.Remove('TenantId') | Out-Null + $EmailTenantSettingsParams.Remove('CertificateThumbprint') | Out-Null + $EmailTenantSettingsParams.Remove('CertificatePath') | Out-Null + $EmailTenantSettingsParams.Remove('CertificatePassword') | Out-Null + $EmailTenantSettingsParams.Remove('ManagedIdentity') | Out-Null + $EmailTenantSettingsParams.Remove('IsSingleInstance') | Out-Null + $EmailTenantSettingsParams.Remove('AccessTokens') | Out-Null + + #removing params that cannot be set. + $EmailTenantSettingsParams.Remove('Name') | Out-Null + $EmailTenantSettingsParams.Remove('IsValid') | Out-Null + $EmailTenantSettingsParams.Remove('ObjectState') | Out-Null + + if (('Present' -eq $Ensure ) -and ($Null -ne $EmailTenantSettingsParams)) + { + Write-Verbose -Message "Setting Email tenant settings with values: $(Convert-M365DscHashtableToString -Hashtable $EmailTenantSettingsParams)" + Set-EmailTenantSettings @EmailTenantSettingsParams + + Write-Verbose -Message 'Email tenant settings updated successfully' + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + [ValidateSet('Yes')] + $IsSingleInstance, + + [Parameter()] + [System.Boolean] + $EnablePriorityAccountProtection, + + [Parameter()] + [System.Boolean] + $IsValid, + + [Parameter()] + [System.String] + $Identity, + + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.String] + $ObjectState, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [System.String] + $CertificatePath, + + [Parameter()] + [System.Management.Automation.PSCredential] + $CertificatePassword, + + [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 + + Write-Verbose -Message 'Testing configuration of Email tenant settings' + + $CurrentValues = Get-TargetResource @PSBoundParameters + + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" + + $ValuesToCheck = $PSBoundParameters + $ValuesToCheck.Remove('Ensure') | Out-Null + + $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + Write-Verbose -Message "Test-TargetResource returned $($TestResult)" + + return $TestResult +} + +function Export-TargetResource +{ + [CmdletBinding()] + [OutputType([System.String])] + param + ( + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [System.String] + $CertificatePath, + + [Parameter()] + [System.Management.Automation.PSCredential] + $CertificatePassword, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + $ConnectionMode = New-M365DSCConnection -Workload 'ExchangeOnline' -InboundParameters $PSBoundParameters -SkipModuleReload $true + + #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 + { + if ($null -ne $Global:M365DSCExportResourceInstancesCount) + { + $Global:M365DSCExportResourceInstancesCount++ + } + + $EmailTenantSettings = Get-EmailTenantSettings -ErrorAction Stop + $dscContent = '' + Write-Host "`r`n" -NoNewline + + Write-Host " |---[1/1] $($EmailTenantSettings.Identity)" -NoNewline + + $Params = @{ + IsSingleInstance = 'Yes' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + CertificatePassword = $CertificatePassword + Managedidentity = $ManagedIdentity.IsPresent + CertificatePath = $CertificatePath + 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 + 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_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.schema.mof new file mode 100644 index 0000000000..722a76bcd3 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.schema.mof @@ -0,0 +1,19 @@ +[ClassVersion("1.0.0.0"), FriendlyName("EXOEmailTenantSettings")] +class MSFT_EXOEmailTenantSettings : OMI_BaseResource +{ + [Key, Description("Only valid value is 'Yes'."), ValueMap{"Yes"}, Values{"Yes"}] String IsSingleInstance; + [Write, Description("Identity which indicates the organization name.")] String Identity; + [Write, Description("Specifies whether priority account protection is enabled.")] Boolean EnablePriorityAccountProtection; + [Write, Description("Specifies whether the migration configuration is valid.")] Boolean IsValid; + [Write, Description("Specifies the state of the object.")] String ObjectState; + [Write, Description("Specifies the name of the object.")] String Name; + [Write, Description("Specifies if this EmailTenantSettings should exist."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; + [Write, Description("Credentials of the Exchange Global 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("Path to certificate used in service principal usually a PFX file.")] String CertificatePath; + [Write, Description("Username can be made up to anything but password will be used for CertificatePassword"), EmbeddedInstance("MSFT_Credential")] String CertificatePassword; + [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_EXOEmailTenantSettings/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/readme.md new file mode 100644 index 0000000000..b07d58c77b --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/readme.md @@ -0,0 +1,5 @@ +EXOEmailTenantSettings + +## Description + +This resource allows users to manage email tenant settings. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/settings.json new file mode 100644 index 0000000000..a3fb9d4fcf --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/settings.json @@ -0,0 +1,34 @@ +{ + "resourceName": "EXOEmailTenantSettings", + "description": "", + "roles": { + "read": [ + "Global Reader" + ], + "update": [ + "Exchange Administrator" + ] + }, + "permissions": { + "graph": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [], + "update": [] + } + }, + "exchange": { + "requiredroles": [ + "Organization Management", + "Security Reader" + ], + "requiredrolegroups": [ + "Organization Management", + "Security Administrator" + ] + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 new file mode 100644 index 0000000000..c94feac8ee --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 @@ -0,0 +1,37 @@ +<# +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 + { + EXOEmailTenantSettings "EXOEmailTenantSettings-Test" + { + EnablePriorityAccountProtection = $True; + Identity = $TenantId; + IsValid = $True; + ObjectState = "Unchanged" + Ensure = "Present"; + Name = "Default" + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ApplicationId = $ApplicationId + } + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 new file mode 100644 index 0000000000..43cf5d4ba9 --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 @@ -0,0 +1,136 @@ +[CmdletBinding()] +param( +) +$M365DSCTestFolder = Join-Path -Path $PSScriptRoot ` + -ChildPath '..\..\Unit' ` + -Resolve +$CmdletModule = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Microsoft365.psm1' ` + -Resolve) +$GenericStubPath = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Generic.psm1' ` + -Resolve) +Import-Module -Name (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\UnitTestHelper.psm1' ` + -Resolve) + +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource 'EXOEmailTenantSettings' -GenericStubModule $GenericStubPath +Describe -Name $Global:DscHelper.DescribeHeader -Fixture { + InModuleScope -ModuleName $Global:DscHelper.ModuleName -ScriptBlock { + Invoke-Command -ScriptBlock $Global:DscHelper.InitializeScript -NoNewScope + BeforeAll { + $secpasswd = ConvertTo-SecureString (New-Guid | Out-String) -AsPlainText -Force + $Credential = New-Object System.Management.Automation.PSCredential ('tenantadmin@mydomain.com', $secpasswd) + + Mock -CommandName Confirm-M365DSCDependencies -MockWith { + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return 'Credentials' + } + + Mock -CommandName Get-PSSession -MockWith { + } + + Mock -CommandName Remove-PSSession -MockWith { + } + + Mock -CommandName Set-EmailTenantSettings -MockWith { + } + + # Mock Write-Host to hide output during the tests + Mock -CommandName Write-Host -MockWith { + } + $Script:exportedInstances =$null + $Script:ExportMode = $false + } + + # Test contexts + Context -Name 'Configuration needs updating' -Fixture { + BeforeAll { + $testParams = @{ + IsSingleInstance = 'Yes' + EnablePriorityAccountProtection = $True; + Identity = "sotmcpoc.onmicrosoft.com\Default"; + IsValid = $True; + ObjectState = "New" + Credential = $Credential + Name = "Default" + Ensure = "Present" + } + + Mock -CommandName Get-EmailTenantSettings -MockWith { + return @{ + EnablePriorityAccountProtection = $False; + IsValid = $True; + ObjectState = "New" + } + } + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should call the Set method' { + Set-TargetResource @testParams + Should -Invoke -CommandName Set-EmailTenantSettings -Exactly 1 + } + } + + Context -Name 'Update not required.' -Fixture { + BeforeAll { + $testParams = @{ + IsSingleInstance = 'Yes' + EnablePriorityAccountProtection = $True; + Identity = "sotmcpoc.onmicrosoft.com\Default"; + IsValid = $True; + ObjectState = "New" + Credential = $Credential + Name = "Default" + Ensure = "Present" + } + + Mock -CommandName Get-EmailTenantSettings -MockWith { + return @{ + EnablePriorityAccountProtection = $True; + IsValid = $True; + ObjectState = "New" + Identity = "sotmcpoc.onmicrosoft.com\Default" + Name = "Default" + } + } + } + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential + } + + Mock -CommandName Get-EmailTenantSettings -MockWith { + return @{ + EnablePriorityAccountProtection = $False; + IsValid = $True; + ObjectState = "New" + } + } + } + + It 'Should Reverse Engineer resource from the Export method' { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index d8d8db6694..09ef698c93 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -1,4 +1,22 @@ # region ExchangeOnlineManagement +function Get-EmailTenantSettings +{ + [CmdletBinding()] + param( + ) +} + +function Set-EmailTenantSettings +{ + [CmdletBinding()] + param( + [Parameter()] + [System.Boolean] + $EnablePriorityAccountProtection + ) +} + + function Get-SweepRule { [CmdletBinding()] From 2a92d72971a9a5cd2881d82ad73e07f43e14b317 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Mon, 9 Sep 2024 09:55:28 -0400 Subject: [PATCH 02/46] 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 03/46] 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 04/46] 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 05/46] 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 06/46] 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 41a9b61b0c90481af94dfed0cd90f51c1ab242f6 Mon Sep 17 00:00:00 2001 From: Sai Sistla Date: Fri, 13 Sep 2024 17:23:56 +0530 Subject: [PATCH 07/46] Addressed comments --- CHANGELOG.md | 2 + .../MSFT_EXOEmailTenantSettings.psm1 | 414 ++++++++++++++++++ .../MSFT_EXOEmailTenantSettings.schema.mof | 18 + .../MSFT_EXOEmailTenantSettings/readme.md | 5 + .../MSFT_EXOEmailTenantSettings/settings.json | 34 ++ .../EXOEmailTenantSettings/2-Update.ps1 | 38 ++ ...oft365DSC.EXOEmailTenantSettings.Tests.ps1 | 134 ++++++ 7 files changed, 645 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index f5f8336f10..bd3f851627 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ * Initial Release. * EXOArcConfig * Initial Release. +* EXOEmailTenantSettings + * Initial Release. * EXOFocusedInbox * Initial Release. * EXOMailboxCalendarConfiguration diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 new file mode 100644 index 0000000000..3173f29bae --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 @@ -0,0 +1,414 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + [ValidateSet('Yes')] + $IsSingleInstance, + + [Parameter()] + [System.Boolean] + $EnablePriorityAccountProtection, + + [Parameter()] + [System.Boolean] + $IsValid, + + [Parameter()] + [System.String] + $Identity, + + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.String] + $ObjectState, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [System.String] + $CertificatePath, + + [Parameter()] + [System.Management.Automation.PSCredential] + $CertificatePassword, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + Write-Verbose -Message 'Getting EXO Email Tenant Settings' + + if ($Global:CurrentModeIsExport) + { + $ConnectionMode = New-M365DSCConnection -Workload 'ExchangeOnline' ` + -InboundParameters $PSBoundParameters ` + -SkipModuleReload $true + } + else + { + $ConnectionMode = New-M365DSCConnection -Workload 'ExchangeOnline' ` + -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 + + $nullReturn = $PSBoundParameters + + try + { + $EmailTenantSettings = Get-EmailTenantSettings -ErrorAction Stop + + $result = @{ + IsSingleInstance = 'Yes' + Identity = $EmailTenantSettings.Identity + EnablePriorityAccountProtection = $EmailTenantSettings.EnablePriorityAccountProtection + Name = $EmailTenantSettings.Name + IsValid = $EmailTenantSettings.IsValid + ObjectState = $EmailTenantSettings.ObjectState + Credential = $Credential + ApplicationId = $ApplicationId + CertificateThumbprint = $CertificateThumbprint + CertificatePath = $CertificatePath + CertificatePassword = $CertificatePassword + Managedidentity = $ManagedIdentity.IsPresent + TenantId = $TenantId + AccessTokens = $AccessTokens + } + + Write-Verbose -Message 'Found Email Tenant Settings config ' + Write-Verbose -Message "Get-TargetResource Result: `n $(Convert-M365DscHashtableToString -Hashtable $result)" + return $result + } + catch + { + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullReturn + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + [ValidateSet('Yes')] + $IsSingleInstance, + + [Parameter()] + [System.Boolean] + $EnablePriorityAccountProtection, + + [Parameter()] + [System.Boolean] + $IsValid, + + [Parameter()] + [System.String] + $Identity, + + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.String] + $ObjectState, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [System.String] + $CertificatePath, + + [Parameter()] + [System.Management.Automation.PSCredential] + $CertificatePassword, + + [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 + + Write-Verbose -Message 'Setting configuration of Email tenant setings' + + $ConnectionMode = New-M365DSCConnection -Workload 'ExchangeOnline' ` + -InboundParameters $PSBoundParameters + + $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + + #removing params that cannot be set. + $EmailTenantSettingsParams.Remove('Name') | Out-Null + $EmailTenantSettingsParams.Remove('IsValid') | Out-Null + $EmailTenantSettingsParams.Remove('ObjectState') | Out-Null +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + [ValidateSet('Yes')] + $IsSingleInstance, + + [Parameter()] + [System.Boolean] + $EnablePriorityAccountProtection, + + [Parameter()] + [System.Boolean] + $IsValid, + + [Parameter()] + [System.String] + $Identity, + + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.String] + $ObjectState, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [System.String] + $CertificatePath, + + [Parameter()] + [System.Management.Automation.PSCredential] + $CertificatePassword, + + [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 + + Write-Verbose -Message 'Testing configuration of Email tenant settings' + + $CurrentValues = Get-TargetResource @PSBoundParameters + + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" + + $ValuesToCheck = $PSBoundParameters + + $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.String] + $CertificateThumbprint, + + [Parameter()] + [System.String] + $CertificatePath, + + [Parameter()] + [System.Management.Automation.PSCredential] + $CertificatePassword, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + $ConnectionMode = New-M365DSCConnection -Workload 'ExchangeOnline' -InboundParameters $PSBoundParameters -SkipModuleReload $true + + #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 + { + if ($null -ne $Global:M365DSCExportResourceInstancesCount) + { + $Global:M365DSCExportResourceInstancesCount++ + } + + $EmailTenantSettings = Get-EmailTenantSettings -ErrorAction Stop + $dscContent = '' + Write-Host "`r`n" -NoNewline + + Write-Host " |---[1/1] $($EmailTenantSettings.Identity)" -NoNewline + + $Params = @{ + IsSingleInstance = 'Yes' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + CertificatePassword = $CertificatePassword + Managedidentity = $ManagedIdentity.IsPresent + CertificatePath = $CertificatePath + 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 + 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_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.schema.mof new file mode 100644 index 0000000000..3f6bdbbd6e --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.schema.mof @@ -0,0 +1,18 @@ +[ClassVersion("1.0.0.0"), FriendlyName("EXOEmailTenantSettings")] +class MSFT_EXOEmailTenantSettings : OMI_BaseResource +{ + [Key, Description("Only valid value is 'Yes'."), ValueMap{"Yes"}, Values{"Yes"}] String IsSingleInstance; + [Write, Description("Identity which indicates the organization name.")] String Identity; + [Write, Description("Specifies whether priority account protection is enabled.")] Boolean EnablePriorityAccountProtection; + [Write, Description("Specifies whether the migration configuration is valid.")] Boolean IsValid; + [Write, Description("Specifies the state of the object.")] String ObjectState; + [Write, Description("Specifies the name of the object.")] String Name; + [Write, Description("Credentials of the Exchange Global 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("Path to certificate used in service principal usually a PFX file.")] String CertificatePath; + [Write, Description("Username can be made up to anything but password will be used for CertificatePassword"), EmbeddedInstance("MSFT_Credential")] String CertificatePassword; + [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_EXOEmailTenantSettings/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/readme.md new file mode 100644 index 0000000000..b07d58c77b --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/readme.md @@ -0,0 +1,5 @@ +EXOEmailTenantSettings + +## Description + +This resource allows users to manage email tenant settings. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/settings.json new file mode 100644 index 0000000000..a3fb9d4fcf --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/settings.json @@ -0,0 +1,34 @@ +{ + "resourceName": "EXOEmailTenantSettings", + "description": "", + "roles": { + "read": [ + "Global Reader" + ], + "update": [ + "Exchange Administrator" + ] + }, + "permissions": { + "graph": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [], + "update": [] + } + }, + "exchange": { + "requiredroles": [ + "Organization Management", + "Security Reader" + ], + "requiredrolegroups": [ + "Organization Management", + "Security Administrator" + ] + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 new file mode 100644 index 0000000000..59e7cd4060 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 @@ -0,0 +1,38 @@ +<# +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 + { + EXOEmailTenantSettings "EXOEmailTenantSettings-Test" + { + IsSingleInstance = "Yes" + EnablePriorityAccountProtection = $True; + Identity = $TenantId; + IsValid = $True; + ObjectState = "Unchanged" + Ensure = "Present"; + Name = "Default" + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ApplicationId = $ApplicationId + } + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 new file mode 100644 index 0000000000..7d00da460f --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 @@ -0,0 +1,134 @@ +[CmdletBinding()] +param( +) +$M365DSCTestFolder = Join-Path -Path $PSScriptRoot ` + -ChildPath '..\..\Unit' ` + -Resolve +$CmdletModule = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Microsoft365.psm1' ` + -Resolve) +$GenericStubPath = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Generic.psm1' ` + -Resolve) +Import-Module -Name (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\UnitTestHelper.psm1' ` + -Resolve) + +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource 'EXOEmailTenantSettings' -GenericStubModule $GenericStubPath +Describe -Name $Global:DscHelper.DescribeHeader -Fixture { + InModuleScope -ModuleName $Global:DscHelper.ModuleName -ScriptBlock { + Invoke-Command -ScriptBlock $Global:DscHelper.InitializeScript -NoNewScope + BeforeAll { + $secpasswd = ConvertTo-SecureString (New-Guid | Out-String) -AsPlainText -Force + $Credential = New-Object System.Management.Automation.PSCredential ('tenantadmin@mydomain.com', $secpasswd) + + Mock -CommandName Confirm-M365DSCDependencies -MockWith { + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return 'Credentials' + } + + Mock -CommandName Get-PSSession -MockWith { + } + + Mock -CommandName Remove-PSSession -MockWith { + } + + Mock -CommandName Set-EmailTenantSettings -MockWith { + } + + # Mock Write-Host to hide output during the tests + Mock -CommandName Write-Host -MockWith { + } + $Script:exportedInstances =$null + $Script:ExportMode = $false + } + + # Test contexts + Context -Name 'Configuration needs updating' -Fixture { + BeforeAll { + $testParams = @{ + IsSingleInstance = 'Yes' + EnablePriorityAccountProtection = $True; + Identity = "sotmcpoc.onmicrosoft.com\Default"; + IsValid = $True; + ObjectState = "New" + Credential = $Credential + Name = "Default" + Ensure = "Present" + } + + Mock -CommandName Get-EmailTenantSettings -MockWith { + return @{ + EnablePriorityAccountProtection = $False; + IsValid = $True; + ObjectState = "New" + } + } + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should call the Set method' { + Set-TargetResource @testParams + Should -Invoke -CommandName Set-EmailTenantSettings -Exactly 1 + } + } + + Context -Name 'Update not required.' -Fixture { + BeforeAll { + $testParams = @{ + IsSingleInstance = 'Yes' + EnablePriorityAccountProtection = $True; + Identity = "sotmcpoc.onmicrosoft.com\Default"; + IsValid = $True; + ObjectState = "New" + Credential = $Credential + Name = "Default" + Ensure = "Present" + } + + Mock -CommandName Get-MigrationConfig -MockWith { + return @{ + EnablePriorityAccountProtection = $False; + IsValid = $True; + ObjectState = "New" + } + } + } + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential + } + + Mock -CommandName Get-EmailTenantSettings -MockWith { + return @{ + EnablePriorityAccountProtection = $False; + IsValid = $True; + ObjectState = "New" + } + } + } + + 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 1022ef331de649b37c5f1fa1c8064e696af9f22a Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Mon, 16 Sep 2024 13:12:14 -0400 Subject: [PATCH 08/46] 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 09/46] 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 ed304ee9e2c5c175e5cfb503d8241541e6c34e99 Mon Sep 17 00:00:00 2001 From: Sai Sistla Date: Tue, 17 Sep 2024 10:04:34 +0530 Subject: [PATCH 10/46] Addressed comments --- CHANGELOG.md | 2 ++ .../MSFT_EXOEmailTenantSettings.psm1 | 31 ++----------------- .../MSFT_EXOEmailTenantSettings.schema.mof | 1 - .../EXOEmailTenantSettings/2-Update.ps1 | 1 + 4 files changed, 5 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cb19259c2..ed39936aec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ # UNRELEASED +* EXOEmailTenantSettings + * Initial Release * EXOMailboxIRMAccess * Initial Release. * AADPasswordRuleSettings diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 index 89d50d48a3..a43d086c64 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 @@ -29,11 +29,6 @@ function Get-TargetResource [System.String] $ObjectState, - [Parameter()] - [ValidateSet('Present', 'Absent')] - [System.String] - $Ensure = 'Present', - [Parameter()] [System.Management.Automation.PSCredential] $Credential, @@ -107,7 +102,6 @@ function Get-TargetResource IsValid = $EmailTenantSettings.IsValid ObjectState = $EmailTenantSettings.ObjectState Credential = $Credential - Ensure = 'Present' ApplicationId = $ApplicationId CertificateThumbprint = $CertificateThumbprint CertificatePath = $CertificatePath @@ -163,11 +157,6 @@ function Set-TargetResource [System.String] $ObjectState, - [Parameter()] - [ValidateSet('Present', 'Absent')] - [System.String] - $Ensure = 'Present', - [Parameter()] [System.Management.Automation.PSCredential] $Credential, @@ -217,24 +206,14 @@ function Set-TargetResource $ConnectionMode = New-M365DSCConnection -Workload 'ExchangeOnline' ` -InboundParameters $PSBoundParameters - $EmailTenantSettingsParams = [System.Collections.Hashtable]($PSBoundParameters) - $EmailTenantSettingsParams.Remove('Ensure') | Out-Null - $EmailTenantSettingsParams.Remove('Credential') | Out-Null - $EmailTenantSettingsParams.Remove('ApplicationId') | Out-Null - $EmailTenantSettingsParams.Remove('TenantId') | Out-Null - $EmailTenantSettingsParams.Remove('CertificateThumbprint') | Out-Null - $EmailTenantSettingsParams.Remove('CertificatePath') | Out-Null - $EmailTenantSettingsParams.Remove('CertificatePassword') | Out-Null - $EmailTenantSettingsParams.Remove('ManagedIdentity') | Out-Null - $EmailTenantSettingsParams.Remove('IsSingleInstance') | Out-Null - $EmailTenantSettingsParams.Remove('AccessTokens') | Out-Null + $EmailTenantSettingsParams = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters #removing params that cannot be set. $EmailTenantSettingsParams.Remove('Name') | Out-Null $EmailTenantSettingsParams.Remove('IsValid') | Out-Null $EmailTenantSettingsParams.Remove('ObjectState') | Out-Null - if (('Present' -eq $Ensure ) -and ($Null -ne $EmailTenantSettingsParams)) + if ($Null -ne $EmailTenantSettingsParams) { Write-Verbose -Message "Setting Email tenant settings with values: $(Convert-M365DscHashtableToString -Hashtable $EmailTenantSettingsParams)" Set-EmailTenantSettings @EmailTenantSettingsParams @@ -274,11 +253,6 @@ function Test-TargetResource [System.String] $ObjectState, - [Parameter()] - [ValidateSet('Present', 'Absent')] - [System.String] - $Ensure = 'Present', - [Parameter()] [System.Management.Automation.PSCredential] $Credential, @@ -331,7 +305,6 @@ function Test-TargetResource Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" $ValuesToCheck = $PSBoundParameters - $ValuesToCheck.Remove('Ensure') | Out-Null $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` -Source $($MyInvocation.MyCommand.Source) ` diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.schema.mof index 722a76bcd3..3f6bdbbd6e 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.schema.mof @@ -7,7 +7,6 @@ class MSFT_EXOEmailTenantSettings : OMI_BaseResource [Write, Description("Specifies whether the migration configuration is valid.")] Boolean IsValid; [Write, Description("Specifies the state of the object.")] String ObjectState; [Write, Description("Specifies the name of the object.")] String Name; - [Write, Description("Specifies if this EmailTenantSettings should exist."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [Write, Description("Credentials of the Exchange Global 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/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 index c94feac8ee..c09677e388 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 @@ -23,6 +23,7 @@ Configuration Example { EXOEmailTenantSettings "EXOEmailTenantSettings-Test" { + IsSingleInstance = = $True; EnablePriorityAccountProtection = $True; Identity = $TenantId; IsValid = $True; From f4eaadd3b429d41f701107740d2da105dc470121 Mon Sep 17 00:00:00 2001 From: Sai Sistla Date: Tue, 17 Sep 2024 11:10:22 +0530 Subject: [PATCH 11/46] Fixed unit tests --- .../MSFT_EXOEmailTenantSettings.psm1 | 1 + .../Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 index a43d086c64..dd5a345ee6 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOEmailTenantSettings/MSFT_EXOEmailTenantSettings.psm1 @@ -212,6 +212,7 @@ function Set-TargetResource $EmailTenantSettingsParams.Remove('Name') | Out-Null $EmailTenantSettingsParams.Remove('IsValid') | Out-Null $EmailTenantSettingsParams.Remove('ObjectState') | Out-Null + $EmailTenantSettingsParams.Remove('IsSingleInstance') | Out-Null if ($Null -ne $EmailTenantSettingsParams) { diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 index 43cf5d4ba9..cec4d939c4 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOEmailTenantSettings.Tests.ps1 @@ -52,12 +52,10 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { $testParams = @{ IsSingleInstance = 'Yes' EnablePriorityAccountProtection = $True; - Identity = "sotmcpoc.onmicrosoft.com\Default"; IsValid = $True; ObjectState = "New" Credential = $Credential Name = "Default" - Ensure = "Present" } Mock -CommandName Get-EmailTenantSettings -MockWith { @@ -89,7 +87,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { ObjectState = "New" Credential = $Credential Name = "Default" - Ensure = "Present" } Mock -CommandName Get-EmailTenantSettings -MockWith { From ed7720371239675cd34d543f4f9671344a23a999 Mon Sep 17 00:00:00 2001 From: Peter Richards Date: Tue, 17 Sep 2024 09:26:49 -0700 Subject: [PATCH 12/46] Add initial EXOATPProtectionPolicyRule resource --- .../MSFT_EXOATPProtectionPolicyRule.psm1 | 533 ++++++++++++++++++ ...MSFT_EXOATPProtectionPolicyRule.schema.mof | 25 + .../MSFT_EXOATPProtectionPolicyRule/readme.md | 6 + .../settings.json | 32 ++ .../EXOATPProtectionPolicyRule/1-Create.ps1 | 26 + .../EXOATPProtectionPolicyRule/2-Update.ps1 | 26 + .../EXOATPProtectionPolicyRule/3-Remove.ps1 | 26 + ...65DSC.EXOATPProtectionPolicyRule.Tests.ps1 | 176 ++++++ 8 files changed, 850 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/1-Create.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/2-Update.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/3-Remove.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOATPProtectionPolicyRule.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.psm1 new file mode 100644 index 0000000000..c8a612eb27 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.psm1 @@ -0,0 +1,533 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Identity, + + [Parameter()] + [System.String] + $Comments, + + [Parameter()] + [System.Boolean] + $Enabled, + + [Parameter()] + [System.String[]] + $ExceptIfRecipientDomainIs, + + [Parameter()] + [System.String[]] + $ExceptIfSentTo, + + [Parameter()] + [System.String[]] + $ExceptIfSentToMemberOf, + + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.UInt32] + $Priority, + + [Parameter()] + [System.String[]] + $RecipientDomainIs, + + [Parameter()] + [System.String] + $SafeAttachmentPolicy, + + [Parameter()] + [System.String] + $SafeLinksPolicy, + + [Parameter()] + [System.String[]] + $SentTo, + + [Parameter()] + [System.String[]] + $SentToMemberOf, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + New-M365DSCConnection -Workload 'ExchangeOnline' ` + -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) + { + $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.Identity -eq $Identity} + } + else + { + $instance = Get-ATPProtectionPolicyRule -Identity $Identity -ErrorAction Stop + } + if ($null -eq $instance) + { + return $nullResult + } + + $results = @{ + Identity = $instance.Identity + Ensure = 'Present' + Comments = $instance.Comments + Enabled = $instance.State -eq 'Enabled' + ExceptIfRecipientDomainIs = $instance.ExceptIfRecipientDomainIs + ExceptIfSentTo = $instance.ExceptIfSentTo + ExceptIfSentToMemberOf = $instance.ExceptIfSentToMemberOf + Name = $instance.Name + Priority = $instance.Priority + RecipientDomainIs = $instance.RecipientDomainIs + SafeAttachmentPolicy = $instance.SafeAttachmentPolicy + SafeLinksPolicy = $instance.SafeLinksPolicy + SentTo = $instance.SentTo + SentToMemberOf = $instance.SentToMemberOf + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + return [System.Collections.Hashtable] $results + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullResult + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Identity, + + [Parameter()] + [System.String] + $Comments, + + [Parameter()] + [System.Boolean] + $Enabled, + + [Parameter()] + [System.String[]] + $ExceptIfRecipientDomainIs, + + [Parameter()] + [System.String[]] + $ExceptIfSentTo, + + [Parameter()] + [System.String[]] + $ExceptIfSentToMemberOf, + + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.UInt32] + $Priority, + + [Parameter()] + [System.String[]] + $RecipientDomainIs, + + [Parameter()] + [System.String] + $SafeAttachmentPolicy, + + [Parameter()] + [System.String] + $SafeLinksPolicy, + + [Parameter()] + [System.String[]] + $SentTo, + + [Parameter()] + [System.String[]] + $SentToMemberOf, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $currentInstance = Get-TargetResource @PSBoundParameters + + $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + + # CREATE + if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') + { + New-ATPProtectionPolicyRule @SetParameters + } + # UPDATE + elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + { + if ($currentInstance.SafeAttachmentPolicy -ne $SetParameters.SafeAttachmentPolicy) + { + throw "SafeAttachmentPolicy cannot be changed after creation" + } + if ($currentInstance.SafeLinksPolicy -ne $SetParameters.SafeLinksPolicy) + { + throw "SafeLinksPolicy cannot be changed after creation" + } + + # Enabled state can only be changed by the Enabled/Disable-ATPProtectionPolicyRule cmdlets + if ($currentInstance.Enabled -ne $setParameters.Enabled) + { + Write-Verbose -Message "Changing Enabled state of the ATPProtectionPolicyRule $($currentInstance.Identity) from $($currentInstance.Enabled) to $($setParameters.Enabled)" + if ($setParameters.Enabled) + { + Enable-ATPProtectionPolicyRule -Identity $currentInstance.Identity + } + else + { + Disable-ATPProtectionPolicyRule -Identity $currentInstance.Identity + } + } + + $SetParameters.Remove("SafeLinksPolicy") | Out-Null + $SetParameters.Remove("SafeAttachmentPolicy") | Out-Null + $SetParameters.Remove("Enabled") | Out-Null + + Set-ATPProtectionPolicyRule @SetParameters + } + # REMOVE + elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') + { + ##TODO - Replace by the Remove cmdlet for the resource + Remove-ATPProtectionPolicyRule -Identity $currentInstance.Identity + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Identity, + + [Parameter()] + [System.String] + $Comments, + + [Parameter()] + [System.Boolean] + $Enabled, + + [Parameter()] + [System.String[]] + $ExceptIfRecipientDomainIs, + + [Parameter()] + [System.String[]] + $ExceptIfSentTo, + + [Parameter()] + [System.String[]] + $ExceptIfSentToMemberOf, + + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.UInt32] + $Priority, + + [Parameter()] + [System.String[]] + $RecipientDomainIs, + + [Parameter()] + [System.String] + $SafeAttachmentPolicy, + + [Parameter()] + [System.String] + $SafeLinksPolicy, + + [Parameter()] + [System.String[]] + $SentTo, + + [Parameter()] + [System.String[]] + $SentToMemberOf, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" + + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + Write-Verbose -Message "Test-TargetResource returned $testResult" + + return $testResult +} + +function Export-TargetResource +{ + [CmdletBinding()] + [OutputType([System.String])] + param + ( + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + ##TODO - Replace workload + $ConnectionMode = New-M365DSCConnection -Workload 'ExchangeOnline' ` + -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 + [array] $Script:exportedInstances = Get-ATPProtectionPolicyRule -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.Identity + Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline + $params = @{ + Identity = $config.Identity + 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_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.schema.mof new file mode 100644 index 0000000000..2c7fcdfa6a --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.schema.mof @@ -0,0 +1,25 @@ +[ClassVersion("1.0.0.0"), FriendlyName("EXOATPProtectionPolicyRule")] +class MSFT_EXOATPProtectionPolicyRule : OMI_BaseResource +{ + [Key, Description("Identifier for the rule")] String Identity; + [Write, Description("Specifies whether the rule is enabled")] Boolean Enabled; + [Write, Description("Informative comments for the rule, such as what the rule is used for or how it has changed over time. The length of the comment can't exceed 1024 characters.")] String Comments; + [Write, Description("Specifies an exception that looks for recipients with email addresses in the specified domains.")] String ExceptIfRecipientDomainIs[]; + [Write, Description("Specifies an exception that looks for recipients in messages. You can use any value that uniquely identifies the recipient")] String ExceptIfSentTo[]; + [Write, Description("Specifies an exception that looks for messages sent to members of groups. You can use any value that uniquely identifies the group.")] String ExceptIfSentToMemberOf[]; + [Write, Description("Unique name for the rule. The maximum length is 64 characters.")] String Name; + [Write, Description("Specifies a priority value for the rule that determines the order of rule processing. A lower integer value indicates a higher priority, the value 0 is the highest priority, and rules can't have the same priority value.")] UInt32 Priority; + [Write, Description("Specifies a condition that looks for recipients with email addresses in the specified domains.")] String RecipientDomainIs[]; + [Write, Description("Specifies the existing Safe Attachments policy that's associated with the preset security policy.")] String SafeAttachmentPolicy; + [Write, Description("Specifies the existing Safe Links policy that's associated with the preset security policy.")] String SafeLinksPolicy; + [Write, Description("Specifies a condition that looks for recipients in messages. You can use any value that uniquely identifies the recipient.")] String SentTo[]; + [Write, Description("Specifies a condition that looks for messages sent to members of distribution groups, dynamic distribution groups, or mail-enabled security groups. ")] String SentToMemberOf[]; + + [Write, Description("Present ensures the instance exists, absent ensures it is removed."), 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; + [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_EXOATPProtectionPolicyRule/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/readme.md new file mode 100644 index 0000000000..32e0e7fb27 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/readme.md @@ -0,0 +1,6 @@ + +# ResourceName + +## Description + +##TODO - Provide a short description of what the resource is set to configure. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/settings.json new file mode 100644 index 0000000000..edf14b05e4 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/settings.json @@ -0,0 +1,32 @@ +{ + "resourceName": "ResourceName", + "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/Examples/Resources/EXOATPProtectionPolicyRule/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/1-Create.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/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/EXOATPProtectionPolicyRule/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/2-Update.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/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/EXOATPProtectionPolicyRule/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/3-Remove.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/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.EXOATPProtectionPolicyRule.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOATPProtectionPolicyRule.Tests.ps1 new file mode 100644 index 0000000000..20857e0393 --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOATPProtectionPolicyRule.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 c9fe30291e82d0cb6d93f3190d972a69ac6fe38e Mon Sep 17 00:00:00 2001 From: Peter Richards Date: Tue, 17 Sep 2024 11:07:00 -0700 Subject: [PATCH 13/46] Working unit tests --- .../MSFT_EXOATPProtectionPolicyRule.psm1 | 1 + ...65DSC.EXOATPProtectionPolicyRule.Tests.ps1 | 134 +++++++++++++--- Tests/Unit/Stubs/Microsoft365.psm1 | 148 ++++++++++++++++++ 3 files changed, 258 insertions(+), 25 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.psm1 index c8a612eb27..46ffac1760 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.psm1 @@ -261,6 +261,7 @@ function Set-TargetResource # CREATE if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { + $SetParameters.Remove('Identity') | Out-Null New-ATPProtectionPolicyRule @SetParameters } # UPDATE diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOATPProtectionPolicyRule.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOATPProtectionPolicyRule.Tests.ps1 index 20857e0393..fe48cd1ed0 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOATPProtectionPolicyRule.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOATPProtectionPolicyRule.Tests.ps1 @@ -35,8 +35,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { return "Credentials" } - ##TODO - Mock any Remove/Set/New cmdlets - # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { } @@ -47,15 +45,20 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Context -Name "The instance should exist but it DOES NOT" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters + Identity = 'TestRule' Ensure = 'Present' Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return $null - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-ATPProtectionPolicyRule -MockWith { return $null } + + Mock -CommandName New-ATPProtectionPolicyRule -MockWith { + return @{ + Identity = 'TestRule' + } + } } It 'Should return Values from the Get method' { (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' @@ -65,25 +68,28 @@ 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-ATPProtectionPolicyRule -Exactly 1 } } Context -Name "The instance exists but it SHOULD NOT" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters + Identity = 'TestRule' Ensure = 'Absent' Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return an instance - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-ATPProtectionPolicyRule -MockWith { return @{ - + Identity = 'TestRule' } } + + Mock -CommandName Remove-ATPProtectionPolicyRule -MockWith { + return $null + } } It 'Should return Values from the Get method' { (Get-TargetResource @testParams).Ensure | Should -Be 'Present' @@ -93,23 +99,28 @@ 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-ATPProtectionPolicyRule -Exactly 1 } } Context -Name "The instance exists and values are already in the desired state" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters + Identity = 'TestRule' Ensure = 'Present' Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return the desired values - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-ATPProtectionPolicyRule -MockWith { return @{ + Identity = 'TestRule' + } + } + Mock -CommandName Set-ATPProtectionPolicyRule -MockWith { + return @{ + Identity = 'TestRule' } } } @@ -122,17 +133,22 @@ 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 + Identity = 'TestRule' + Comments = 'TestComment' Ensure = 'Present' Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return a drift - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-ATPProtectionPolicyRule -MockWith { return @{ - + Identity = 'TestRule' + Comments = 'TestComment #DriftValue' } } + + Mock -CommandName Set-ATPProtectionPolicyRule -MockWith { + return $testParams + } } It 'Should return Values from the Get method' { @@ -145,8 +161,77 @@ 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-ATPProtectionPolicyRule -Exactly 1 + } + } + + Context -Name "The instance exists and should be disabled, but is not" -Fixture { + BeforeAll { + $testParams = @{ + Identity = 'TestRule' + Ensure = 'Present' + Enabled = $false + Credential = $Credential; + } + + Mock -CommandName Get-ATPProtectionPolicyRule -MockWith { + return @{ + Identity = 'TestRule' + State = 'Enabled' + } + } + + Mock -CommandName Set-ATPProtectionPolicyRule -MockWith { + return $testParams + } + + Mock -CommandName Disable-ATPProtectionPolicyRule -MockWith { + return $null + } + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should call the Disable method' { + Set-TargetResource @testParams + Should -Invoke -CommandName Disable-ATPProtectionPolicyRule -Exactly 1 + } + } + + Context -Name "The instance exists and should be enabled, but is not" -Fixture { + BeforeAll { + $testParams = @{ + Identity = 'TestRule' + Ensure = 'Present' + Enabled = $True + Credential = $Credential; + } + + Mock -CommandName Get-ATPProtectionPolicyRule -MockWith { + return @{ + Identity = 'TestRule' + State = 'Disabled' + } + } + + Mock -CommandName Set-ATPProtectionPolicyRule -MockWith { + return $testParams + } + + Mock -CommandName Enable-ATPProtectionPolicyRule -MockWith { + return $null + } + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should call the Enable method' { + Set-TargetResource @testParams + Should -Invoke -CommandName Enable-ATPProtectionPolicyRule -Exactly 1 } } @@ -158,10 +243,9 @@ 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-ATPProtectionPolicyRule -MockWith { return @{ - + Identity = 'TestRule' } } } diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index 36fb0879a9..c0d69578be 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -67,6 +67,16 @@ function Get-AzSubscription ) } +function Enable-ATPProtectionPolicyRule +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Identity + ) +} + function Enable-AzSubscription { [CmdletBinding()] @@ -77,6 +87,16 @@ function Enable-AzSubscription ) } +function Disable-ATPProtectionPolicyRule +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Identity + ) +} + function Disable-AzSubscription { [CmdletBinding()] @@ -1234,6 +1254,21 @@ function Get-ApplicationAccessPolicy $Identity ) } + +function Get-ATPProtectionPolicyRule +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Identity, + + [Parameter()] + [System.String] + $State + ) +} + function Get-AtpPolicyForO365 { [CmdletBinding()] @@ -3410,6 +3445,61 @@ function New-ApplicationAccessPolicy $AppId ) } + +function New-ATPProtectionPolicyRule +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.String] + $SafeAttachmentPolicy, + + [Parameter()] + [System.String] + $SafeLinksPolicy, + + [Parameter()] + [System.String] + $Comments, + + [Parameter()] + [System.Boolean] + $Enabled, + + [Parameter()] + [System.String[]] + $ExceptIfRecipientDomainIs, + + [Parameter()] + [System.String[]] + $ExceptIfSentTo, + + [Parameter()] + [System.String[]] + $ExceptIfSentToMemberOf, + + [Parameter()] + [System.UInt32] + $Priority, + + [Parameter()] + [System.String[]] + $RecipientDomainIs, + + [Parameter()] + [System.String[]] + $SentTo, + + [Parameter()] + [System.String[]] + $SentToMemberOf + ) +} + function New-AuthenticationPolicy { [CmdletBinding()] @@ -7121,6 +7211,17 @@ function Remove-ApplicationAccessPolicy $Identity ) } + +function Remove-ATPProtectionPolicyRule +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Identity + ) +} + function Remove-AuditConfigurationPolicy { [CmdletBinding()] @@ -8370,6 +8471,53 @@ function Set-AtpPolicyForO365 $Confirm ) } + +function Set-ATPProtectionPolicyRule +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Identity, + + [Parameter()] + [System.String] + $Comments, + + [Parameter()] + [System.String] + $ExceptIfRecipientDomainIs, + + [Parameter()] + [System.String] + $ExceptIfSentTo, + + [Parameter()] + [System.String] + $ExceptIfSentToMemberOf, + + [Parameter()] + [System.String] + $String, + + [Parameter()] + [System.UInt32] + $Priority, + + [Parameter()] + [System.String[]] + $RecipientDomainIs, + + [Parameter()] + [System.String[]] + $SentTo, + + [Parameter()] + [System.String[]] + $SentToMemberOf + ) +} + function Set-AuthenticationPolicy { [CmdletBinding()] From 0e452a620ae7b235afc9f1f36879857fd05c6d18 Mon Sep 17 00:00:00 2001 From: Peter Richards Date: Tue, 17 Sep 2024 12:03:11 -0700 Subject: [PATCH 14/46] Update Readme + settings.json --- .../MSFT_EXOATPProtectionPolicyRule/readme.md | 4 +-- .../settings.json | 27 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/readme.md index 32e0e7fb27..5d125b34c4 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/readme.md +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/readme.md @@ -1,6 +1,6 @@ -# ResourceName +# EXOATPProtectionPolicyRule ## Description -##TODO - Provide a short description of what the resource is set to configure. +Manage ATP Protection policy rules that are associated with Microsoft Defender for Office 365 protections in preset security policies. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/settings.json index edf14b05e4..8ca47640d7 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/settings.json +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/settings.json @@ -1,12 +1,12 @@ { "resourceName": "ResourceName", - "description": "Description of what the resource is about.", + "description": "Manage ATP Protection policy rules that are associated with Microsoft Defender for Office 365 protections in preset security policies.", "roles": { "read": [ - "Role" + "Security Reader" ], "update": [ - "Role" + "Security Administrator" ] }, "permissions": { @@ -16,17 +16,18 @@ "update": [] }, "application": { - "read": [ - { - "name": "Permission for Monitoring and Export" - } - ], - "update": [ - { - "name": "Permission for deploying" - } - ] + "read": [], + "update": [] } + }, + "exchange": { + "requiredroles": [ + "Transport Hygiene", + "Security Admin", + "View-Only Configuration", + "Security Reader" + ], + "requiredrolegroups": "Organization Management" } } } From ca43fcabb51accb0cb88b9b20570123cf3b7838c Mon Sep 17 00:00:00 2001 From: Peter Richards Date: Tue, 17 Sep 2024 12:13:58 -0700 Subject: [PATCH 15/46] Fix casing of resource name --- .../MSFT_EXOAtpProtectionPolicyRule.psm1} | 0 .../MSFT_EXOAtpProtectionPolicyRule.schema.mof} | 4 ++-- .../readme.md | 0 .../settings.json | 0 .../1-Create.ps1 | 0 .../2-Update.ps1 | 0 .../3-Remove.ps1 | 0 ...1 => Microsoft365DSC.EXOAtpProtectionPolicyRule.Tests.ps1} | 0 8 files changed, 2 insertions(+), 2 deletions(-) rename Modules/Microsoft365DSC/DSCResources/{MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.psm1 => MSFT_EXOAtpProtectionPolicyRule/MSFT_EXOAtpProtectionPolicyRule.psm1} (100%) rename Modules/Microsoft365DSC/DSCResources/{MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.schema.mof => MSFT_EXOAtpProtectionPolicyRule/MSFT_EXOAtpProtectionPolicyRule.schema.mof} (96%) rename Modules/Microsoft365DSC/DSCResources/{MSFT_EXOATPProtectionPolicyRule => MSFT_EXOAtpProtectionPolicyRule}/readme.md (100%) rename Modules/Microsoft365DSC/DSCResources/{MSFT_EXOATPProtectionPolicyRule => MSFT_EXOAtpProtectionPolicyRule}/settings.json (100%) rename Modules/Microsoft365DSC/Examples/Resources/{EXOATPProtectionPolicyRule => EXOAtpProtectionPolicyRule}/1-Create.ps1 (100%) rename Modules/Microsoft365DSC/Examples/Resources/{EXOATPProtectionPolicyRule => EXOAtpProtectionPolicyRule}/2-Update.ps1 (100%) rename Modules/Microsoft365DSC/Examples/Resources/{EXOATPProtectionPolicyRule => EXOAtpProtectionPolicyRule}/3-Remove.ps1 (100%) rename Tests/Unit/Microsoft365DSC/{Microsoft365DSC.EXOATPProtectionPolicyRule.Tests.ps1 => Microsoft365DSC.EXOAtpProtectionPolicyRule.Tests.ps1} (100%) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAtpProtectionPolicyRule/MSFT_EXOAtpProtectionPolicyRule.psm1 similarity index 100% rename from Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.psm1 rename to Modules/Microsoft365DSC/DSCResources/MSFT_EXOAtpProtectionPolicyRule/MSFT_EXOAtpProtectionPolicyRule.psm1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAtpProtectionPolicyRule/MSFT_EXOAtpProtectionPolicyRule.schema.mof similarity index 96% rename from Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.schema.mof rename to Modules/Microsoft365DSC/DSCResources/MSFT_EXOAtpProtectionPolicyRule/MSFT_EXOAtpProtectionPolicyRule.schema.mof index 2c7fcdfa6a..436b9628c8 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/MSFT_EXOATPProtectionPolicyRule.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAtpProtectionPolicyRule/MSFT_EXOAtpProtectionPolicyRule.schema.mof @@ -1,5 +1,5 @@ -[ClassVersion("1.0.0.0"), FriendlyName("EXOATPProtectionPolicyRule")] -class MSFT_EXOATPProtectionPolicyRule : OMI_BaseResource +[ClassVersion("1.0.0.0"), FriendlyName("EXOAtpProtectionPolicyRule")] +class MSFT_EXOAtpProtectionPolicyRule : OMI_BaseResource { [Key, Description("Identifier for the rule")] String Identity; [Write, Description("Specifies whether the rule is enabled")] Boolean Enabled; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAtpProtectionPolicyRule/readme.md similarity index 100% rename from Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/readme.md rename to Modules/Microsoft365DSC/DSCResources/MSFT_EXOAtpProtectionPolicyRule/readme.md diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAtpProtectionPolicyRule/settings.json similarity index 100% rename from Modules/Microsoft365DSC/DSCResources/MSFT_EXOATPProtectionPolicyRule/settings.json rename to Modules/Microsoft365DSC/DSCResources/MSFT_EXOAtpProtectionPolicyRule/settings.json diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/1-Create.ps1 similarity index 100% rename from Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/1-Create.ps1 rename to Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/1-Create.ps1 diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/2-Update.ps1 similarity index 100% rename from Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/2-Update.ps1 rename to Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/2-Update.ps1 diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/3-Remove.ps1 similarity index 100% rename from Modules/Microsoft365DSC/Examples/Resources/EXOATPProtectionPolicyRule/3-Remove.ps1 rename to Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/3-Remove.ps1 diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOATPProtectionPolicyRule.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOAtpProtectionPolicyRule.Tests.ps1 similarity index 100% rename from Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOATPProtectionPolicyRule.Tests.ps1 rename to Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOAtpProtectionPolicyRule.Tests.ps1 From a8bd28515cfd37967fc2ec310844876c988cedbc Mon Sep 17 00:00:00 2001 From: Peter Richards Date: Tue, 17 Sep 2024 12:20:44 -0700 Subject: [PATCH 16/46] Update examples --- .../EXOAtpProtectionPolicyRule/1-Create.ps1 | 15 ++++++++++++++- .../EXOAtpProtectionPolicyRule/2-Update.ps1 | 15 ++++++++++++++- .../EXOAtpProtectionPolicyRule/3-Remove.ps1 | 15 ++++++++++++++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/1-Create.ps1 index b516274848..747e49a549 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/1-Create.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/1-Create.ps1 @@ -21,6 +21,19 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + EXOATPProtectionPolicyRule "EXOATPProtectionPolicyRule-Strict Preset Security Policy" + { + Comments = "Built-in Strict Preset Security Policy"; + Enabled = $False; + Identity = "Strict Preset Security Policy"; + Name = "Strict Preset Security Policy"; + Priority = 0; + SafeAttachmentPolicy = "Strict Preset Security Policy1725468967835"; + SafeLinksPolicy = "Strict Preset Security Policy1725468969412"; + Ensure = "Present" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } } } diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/2-Update.ps1 index b516274848..1cd2df9588 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/2-Update.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/2-Update.ps1 @@ -21,6 +21,19 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + EXOATPProtectionPolicyRule "EXOATPProtectionPolicyRule-Strict Preset Security Policy" + { + Comments = "Built-in Strict Preset Security Policy with comments"; # Changed value + Enabled = $True; # Changed value + Identity = "Strict Preset Security Policy"; + Name = "Strict Preset Security Policy"; + Priority = 0; + SafeAttachmentPolicy = "Strict Preset Security Policy1725468967835"; + SafeLinksPolicy = "Strict Preset Security Policy1725468969412"; + Ensure = "Present" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } } } diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/3-Remove.ps1 index b516274848..2052e05050 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/3-Remove.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOAtpProtectionPolicyRule/3-Remove.ps1 @@ -21,6 +21,19 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + EXOATPProtectionPolicyRule "EXOATPProtectionPolicyRule-Strict Preset Security Policy" + { + Comments = "Built-in Strict Preset Security Policy"; + Enabled = $False; + Identity = "Strict Preset Security Policy"; + Name = "Strict Preset Security Policy"; + Priority = 0; + SafeAttachmentPolicy = "Strict Preset Security Policy1725468967835"; + SafeLinksPolicy = "Strict Preset Security Policy1725468969412"; + Ensure = "Absent" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } } } From a42a311b9f6be118a78555fc949cc02a333194a0 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 17 Sep 2024 16:47:31 -0400 Subject: [PATCH 17/46] 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 18/46] 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 19/46] 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 20/46] 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 21/46] 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 22/46] 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 9d3a481475e63954657da839bc8ba81b99812d05 Mon Sep 17 00:00:00 2001 From: sasistla Date: Thu, 19 Sep 2024 15:24:53 +0530 Subject: [PATCH 23/46] Update 2-Update.ps1 --- .../Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 index 59e7cd4060..bb4c462947 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOEmailTenantSettings/2-Update.ps1 @@ -28,7 +28,6 @@ Configuration Example Identity = $TenantId; IsValid = $True; ObjectState = "Unchanged" - Ensure = "Present"; Name = "Default" TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint From 84e8d2b2baef7e4e78407fc3d7f86ca8a28de32b Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Thu, 19 Sep 2024 07:39:19 -0400 Subject: [PATCH 24/46] 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' + } + ) } } } From 13334d30cd75b865815421b613f84aaf8970be51 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 19 Sep 2024 11:51:44 +0000 Subject: [PATCH 25/46] Updated Resources and Cmdlet documentation pages --- .../SCInsiderRiskEntityList.md | 208 ++++++++++++++++++ .../cmdlets/Get-M365DSCCIMInstanceKey.md | 18 ++ 2 files changed, 226 insertions(+) create mode 100644 docs/docs/resources/security-compliance/SCInsiderRiskEntityList.md create mode 100644 docs/docs/user-guide/cmdlets/Get-M365DSCCIMInstanceKey.md diff --git a/docs/docs/resources/security-compliance/SCInsiderRiskEntityList.md b/docs/docs/resources/security-compliance/SCInsiderRiskEntityList.md new file mode 100644 index 0000000000..4c0e22601d --- /dev/null +++ b/docs/docs/resources/security-compliance/SCInsiderRiskEntityList.md @@ -0,0 +1,208 @@ +# SCInsiderRiskEntityList + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **Name** | Key | String | The name of the group or setting. | | +| **ListType** | Required | String | The setting type. | | +| **Description** | Write | String | Description for the group or setting. | | +| **DisplayName** | Write | String | The display name of the group or setting. | | +| **Domains** | Write | MSFT_SCInsiderRiskEntityListDomain[] | List of domains | | +| **FilePaths** | Write | StringArray[] | List of file paths. | | +| **FileTypes** | Write | StringArray[] | List of file types. | | +| **Keywords** | Write | StringArray[] | List of keywords. | | +| **SensitiveInformationTypes** | Write | StringArray[] | List of sensitive information types. | | +| **Sites** | Write | MSFT_SCInsiderRiskEntityListSite[] | List of sites. | | +| **TrainableClassifiers** | Write | StringArray[] | List of trainable classifiers. | | +| **ExceptionKeyworkGroups** | Write | StringArray[] | List of keywords for exception. | | +| **ExcludedClassifierGroups** | Write | StringArray[] | List of excluded trainable classifiers. | | +| **ExcludedDomainGroups** | Write | StringArray[] | List of excluded domains. | | +| **ExcludedFilePathGroups** | Write | StringArray[] | List of excluded file paths. | | +| **ExcludedFileTypeGroups** | Write | StringArray[] | List of excluded file types. | | +| **ExcludedKeyworkGroups** | Write | StringArray[] | List of excluded keywords. | | +| **ExcludedSensitiveInformationTypeGroups** | Write | StringArray[] | List of excluded sensitive information types. | | +| **ExcludedSiteGroups** | Write | StringArray[] | List of excluded sites. | | +| **Ensure** | Write | String | Specify if this entity should exist or not. | `Present`, `Absent` | +| **Credential** | Write | PSCredential | Credentials of the workload's Admin | | +| **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | +| **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | +| **CertificateThumbprint** | Write | String | Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication. | | +| **ManagedIdentity** | Write | Boolean | Managed ID being used for authentication. | | +| **AccessTokens** | Write | StringArray[] | Access token used for authentication. | | + +### MSFT_SCInsiderRiskEntityListDomain + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **Dmn** | Required | String | Domain name. | | +| **isMLSubDmn** | Write | Boolean | Defines if the entry should include multi-level subdomains or not. | | + +### MSFT_SCInsiderRiskEntityListSite + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **Url** | Required | String | Url of the site. | | +| **Name** | Write | String | Name of the site. | | +| **Guid** | Write | String | Unique identifier of the site. | | + + +## Description + +Configures settings for Insider Risk in Purview. + +## Permissions + +### Microsoft Graph + +To authenticate with the Microsoft Graph API, this resource required the following permissions: + +#### Delegated permissions + +- **Read** + + - None + +- **Update** + + - None + +#### Application permissions + +- **Read** + + - None + +- **Update** + + - None + +## Examples + +### Example 1 + +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. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + 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; + } + } +} +``` + +### Example 2 + +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. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + 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; + } + } +} +``` + +### Example 3 + +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. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + 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/docs/docs/user-guide/cmdlets/Get-M365DSCCIMInstanceKey.md b/docs/docs/user-guide/cmdlets/Get-M365DSCCIMInstanceKey.md new file mode 100644 index 0000000000..f4b2a95b4d --- /dev/null +++ b/docs/docs/user-guide/cmdlets/Get-M365DSCCIMInstanceKey.md @@ -0,0 +1,18 @@ +# Get-M365DSCCIMInstanceKey + +## Description + +This function gets the key parameter for the specified CIMInstance + +## Output + +This function outputs information as the following type: +**System.String** + +## Parameters + +| Parameter | Required | DataType | Default Value | Allowed Values | Description | +| --- | --- | --- | --- | --- | --- | +| CIMInstance | True | Hashtable | | | | + + From 30e267d5db9f32f5cdb7ec4ad9e92edea69c90e0 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 19 Sep 2024 11:53:38 +0000 Subject: [PATCH 26/46] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index a1e5c97de8..699d440d56 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -38814,6 +38814,176 @@ } ] }, + { + "ClassName": "MSFT_SCInsiderRiskEntityListDomain", + "Parameters": [ + { + "CIMType": "String", + "Name": "Dmn", + "Option": "Required" + }, + { + "CIMType": "Boolean", + "Name": "isMLSubDmn", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_SCInsiderRiskEntityListSite", + "Parameters": [ + { + "CIMType": "String", + "Name": "Url", + "Option": "Required" + }, + { + "CIMType": "String", + "Name": "Name", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Guid", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_SCInsiderRiskEntityList", + "Parameters": [ + { + "CIMType": "String", + "Name": "Name", + "Option": "Key" + }, + { + "CIMType": "String", + "Name": "ListType", + "Option": "Required" + }, + { + "CIMType": "String", + "Name": "Description", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "DisplayName", + "Option": "Write" + }, + { + "CIMType": "MSFT_SCInsiderRiskEntityListDomain[]", + "Name": "Domains", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "FilePaths", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "FileTypes", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "Keywords", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "SensitiveInformationTypes", + "Option": "Write" + }, + { + "CIMType": "MSFT_SCInsiderRiskEntityListSite[]", + "Name": "Sites", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "TrainableClassifiers", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "ExceptionKeyworkGroups", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "ExcludedClassifierGroups", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "ExcludedDomainGroups", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "ExcludedFilePathGroups", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "ExcludedFileTypeGroups", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "ExcludedKeyworkGroups", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "ExcludedSensitiveInformationTypeGroups", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "ExcludedSiteGroups", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Ensure", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "Credential", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ApplicationId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "TenantId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "CertificateThumbprint", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "ManagedIdentity", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "AccessTokens", + "Option": "Write" + } + ] + }, { "ClassName": "MSFT_SCLabelSetting", "Parameters": [ From 00fde30e0185e2c4ed265cc485687a4eed4a856e Mon Sep 17 00:00:00 2001 From: Fabien Tschanz Date: Thu, 19 Sep 2024 16:44:32 +0200 Subject: [PATCH 27/46] Add WinRM requirement to prerequisite instructions --- docs/docs/user-guide/get-started/prerequisites.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/docs/user-guide/get-started/prerequisites.md b/docs/docs/user-guide/get-started/prerequisites.md index 0ff30b9b23..243cd7322f 100644 --- a/docs/docs/user-guide/get-started/prerequisites.md +++ b/docs/docs/user-guide/get-started/prerequisites.md @@ -6,6 +6,10 @@ For Microsoft365DSC to function, you need to arrange the following components: Microsoft365DSC is supported for PowerShell version 5.1 and 7.3+. For additional details on how to leverage it with PowerShell 7, please refer to our [PowerShell 7+ Guide for Microsoft365DSC](https://microsoft365dsc.com/user-guide/get-started/powershell7-support/). +### Windows Remote Management (WinRM) + +Microsoft365DSC uses the Local Configuration Manager (LCM). This requires PowerShell Remoting to be enabled. Please run either `winrm quickconfig -force` or `Enable-PSRemoting -Force -SkipNetworkProfileCheck` to enable it. + ### Tooling To get the best experience running Microsoft365DSC cmdlets, it is recommended that you use the Windows Terminal. All screenshots provided in this article are using the Windows Terminal. This tool allows you to quickly switch between PowerShell versions and provide better support for icons and symbols that are used throughout Microsoft365DSC’s experience. From 148e7cd300bb717ddfcf86c4fb6ea9115482fecb Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 19 Sep 2024 17:14:26 +0000 Subject: [PATCH 28/46] Updated Resources and Cmdlet documentation pages --- .../exchange/EXOAtpProtectionPolicyRule.md | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 docs/docs/resources/exchange/EXOAtpProtectionPolicyRule.md diff --git a/docs/docs/resources/exchange/EXOAtpProtectionPolicyRule.md b/docs/docs/resources/exchange/EXOAtpProtectionPolicyRule.md new file mode 100644 index 0000000000..59c88250cd --- /dev/null +++ b/docs/docs/resources/exchange/EXOAtpProtectionPolicyRule.md @@ -0,0 +1,174 @@ +# EXOAtpProtectionPolicyRule + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **Identity** | Key | String | Identifier for the rule | | +| **Enabled** | Write | Boolean | Specifies whether the rule is enabled | | +| **Comments** | Write | String | Informative comments for the rule, such as what the rule is used for or how it has changed over time. The length of the comment can't exceed 1024 characters. | | +| **ExceptIfRecipientDomainIs** | Write | StringArray[] | Specifies an exception that looks for recipients with email addresses in the specified domains. | | +| **ExceptIfSentTo** | Write | StringArray[] | Specifies an exception that looks for recipients in messages. You can use any value that uniquely identifies the recipient | | +| **ExceptIfSentToMemberOf** | Write | StringArray[] | Specifies an exception that looks for messages sent to members of groups. You can use any value that uniquely identifies the group. | | +| **Name** | Write | String | Unique name for the rule. The maximum length is 64 characters. | | +| **Priority** | Write | UInt32 | Specifies a priority value for the rule that determines the order of rule processing. A lower integer value indicates a higher priority, the value 0 is the highest priority, and rules can't have the same priority value. | | +| **RecipientDomainIs** | Write | StringArray[] | Specifies a condition that looks for recipients with email addresses in the specified domains. | | +| **SafeAttachmentPolicy** | Write | String | Specifies the existing Safe Attachments policy that's associated with the preset security policy. | | +| **SafeLinksPolicy** | Write | String | Specifies the existing Safe Links policy that's associated with the preset security policy. | | +| **SentTo** | Write | StringArray[] | Specifies a condition that looks for recipients in messages. You can use any value that uniquely identifies the recipient. | | +| **SentToMemberOf** | Write | StringArray[] | Specifies a condition that looks for messages sent to members of distribution groups, dynamic distribution groups, or mail-enabled security groups. | | +| **Ensure** | Write | String | Present ensures the instance exists, absent ensures it is removed. | `Present`, `Absent` | +| **Credential** | Write | PSCredential | Credentials of the workload's Admin | | +| **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | +| **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | +| **CertificateThumbprint** | Write | String | Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication. | | +| **ManagedIdentity** | Write | Boolean | Managed ID being used for authentication. | | +| **AccessTokens** | Write | StringArray[] | Access token used for authentication. | | + + +## Description + +Manage ATP Protection policy rules that are associated with Microsoft Defender for Office 365 protections in preset security policies. + +## Permissions + +### Exchange + +To authenticate with Microsoft Exchange, this resource required the following permissions: + +#### Roles + +- Transport Hygiene, Security Admin, View-Only Configuration, Security Reader + +#### Role Groups + +- Organization Management + +## Examples + +### Example 1 + +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. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + EXOATPProtectionPolicyRule "EXOATPProtectionPolicyRule-Strict Preset Security Policy" + { + Comments = "Built-in Strict Preset Security Policy"; + Enabled = $False; + Identity = "Strict Preset Security Policy"; + Name = "Strict Preset Security Policy"; + Priority = 0; + SafeAttachmentPolicy = "Strict Preset Security Policy1725468967835"; + SafeLinksPolicy = "Strict Preset Security Policy1725468969412"; + Ensure = "Present" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + } +} +``` + +### Example 2 + +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. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + EXOATPProtectionPolicyRule "EXOATPProtectionPolicyRule-Strict Preset Security Policy" + { + Comments = "Built-in Strict Preset Security Policy with comments"; # Changed value + Enabled = $True; # Changed value + Identity = "Strict Preset Security Policy"; + Name = "Strict Preset Security Policy"; + Priority = 0; + SafeAttachmentPolicy = "Strict Preset Security Policy1725468967835"; + SafeLinksPolicy = "Strict Preset Security Policy1725468969412"; + Ensure = "Present" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + } +} +``` + +### Example 3 + +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. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + EXOATPProtectionPolicyRule "EXOATPProtectionPolicyRule-Strict Preset Security Policy" + { + Comments = "Built-in Strict Preset Security Policy"; + Enabled = $False; + Identity = "Strict Preset Security Policy"; + Name = "Strict Preset Security Policy"; + Priority = 0; + SafeAttachmentPolicy = "Strict Preset Security Policy1725468967835"; + SafeLinksPolicy = "Strict Preset Security Policy1725468969412"; + Ensure = "Absent" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + } +} +``` + From b9a1b122d57af424ee1c17795b1a675166bebfee Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 19 Sep 2024 17:16:09 +0000 Subject: [PATCH 29/46] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index 699d440d56..239087b5ff 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -6239,6 +6239,111 @@ } ] }, + { + "ClassName": "MSFT_EXOAtpProtectionPolicyRule", + "Parameters": [ + { + "CIMType": "String", + "Name": "Identity", + "Option": "Key" + }, + { + "CIMType": "Boolean", + "Name": "Enabled", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Comments", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "ExceptIfRecipientDomainIs", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "ExceptIfSentTo", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "ExceptIfSentToMemberOf", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Name", + "Option": "Write" + }, + { + "CIMType": "UInt32", + "Name": "Priority", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "RecipientDomainIs", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "SafeAttachmentPolicy", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "SafeLinksPolicy", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "SentTo", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "SentToMemberOf", + "Option": "Write" + }, + { + "CIMType": "string", + "Name": "Ensure", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "Credential", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ApplicationId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "TenantId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "CertificateThumbprint", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "ManagedIdentity", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "AccessTokens", + "Option": "Write" + } + ] + }, { "ClassName": "MSFT_EXOAuthenticationPolicy", "Parameters": [ From 4b671c43315533c9960338320310f6011b329178 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 19 Sep 2024 17:18:00 +0000 Subject: [PATCH 30/46] Updated {Create} EXO Integration Tests --- .../M365DSCIntegration.EXO.Create.Tests.ps1 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 index d96a63f694..907e37db8e 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 @@ -129,6 +129,20 @@ TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint } + EXOATPProtectionPolicyRule 'EXOATPProtectionPolicyRule-Strict Preset Security Policy' + { + Comments = "Built-in Strict Preset Security Policy"; + Enabled = $False; + Identity = "Strict Preset Security Policy"; + Name = "Strict Preset Security Policy"; + Priority = 0; + SafeAttachmentPolicy = "Strict Preset Security Policy1725468967835"; + SafeLinksPolicy = "Strict Preset Security Policy1725468969412"; + Ensure = "Present" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } EXOAuthenticationPolicy 'ConfigureAuthenticationPolicy' { Identity = "Block Basic Auth" From 5e066597bf10b185ca85002f88e5f6bf40574b27 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 19 Sep 2024 17:18:28 +0000 Subject: [PATCH 31/46] Updated {Update} EXO Integration Tests --- .../M365DSCIntegration.EXO.Update.Tests.ps1 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 index b1c89d5d9f..200455c0ad 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 @@ -147,6 +147,20 @@ TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint } + EXOATPProtectionPolicyRule 'EXOATPProtectionPolicyRule-Strict Preset Security Policy' + { + Comments = "Built-in Strict Preset Security Policy with comments"; # Changed value + Enabled = $True; # Changed value + Identity = "Strict Preset Security Policy"; + Name = "Strict Preset Security Policy"; + Priority = 0; + SafeAttachmentPolicy = "Strict Preset Security Policy1725468967835"; + SafeLinksPolicy = "Strict Preset Security Policy1725468969412"; + Ensure = "Present" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } EXOAuthenticationPolicy 'ConfigureAuthenticationPolicy' { Identity = "Block Basic Auth" From af28151061c9c2c2e4a8de7d03df3c33d84a993c Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 19 Sep 2024 17:19:03 +0000 Subject: [PATCH 32/46] Updated {Update} EXO Integration Tests --- .../M365DSCIntegration.EXO.Remove.Tests.ps1 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 index 9561195d84..065fa1f57d 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 @@ -115,6 +115,20 @@ TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint } + EXOATPProtectionPolicyRule 'EXOATPProtectionPolicyRule-Strict Preset Security Policy' + { + Comments = "Built-in Strict Preset Security Policy"; + Enabled = $False; + Identity = "Strict Preset Security Policy"; + Name = "Strict Preset Security Policy"; + Priority = 0; + SafeAttachmentPolicy = "Strict Preset Security Policy1725468967835"; + SafeLinksPolicy = "Strict Preset Security Policy1725468969412"; + Ensure = "Absent" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } EXOAuthenticationPolicy 'ConfigureAuthenticationPolicy' { Identity = "Block Basic Auth" From aa6466fe0fa5f1cfdbdbf537de46593407445959 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Thu, 19 Sep 2024 15:36:34 -0400 Subject: [PATCH 33/46] ADOPermissionGroup - Initial Release --- .../MSFT_ADOPermissionGroup.psm1 | 596 ++++++++++++++++++ .../MSFT_ADOPermissionGroup.schema.mof | 19 + .../MSFT_ADOPermissionGroup/readme.md | 6 + .../MSFT_ADOPermissionGroup/settings.json | 20 + .../Resources/ADOPermissionGroup/1-Create.ps1 | 38 ++ .../Resources/ADOPermissionGroup/2-Update.ps1 | 38 ++ .../Resources/ADOPermissionGroup/3-Remove.ps1 | 38 ++ .../M365DSCAzureDevOPSHelper.psm1 | 8 +- ...crosoft365DSC.ADOPermissionGroup.Tests.ps1 | 290 +++++++++ 9 files changed, 1051 insertions(+), 2 deletions(-) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/MSFT_ADOPermissionGroup.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/MSFT_ADOPermissionGroup.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/ADOPermissionGroup/1-Create.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/ADOPermissionGroup/2-Update.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/ADOPermissionGroup/3-Remove.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.ADOPermissionGroup.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/MSFT_ADOPermissionGroup.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/MSFT_ADOPermissionGroup.psm1 new file mode 100644 index 0000000000..ede45030e4 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/MSFT_ADOPermissionGroup.psm1 @@ -0,0 +1,596 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $OrganizationName, + + [Parameter(Mandatory = $true)] + [System.String] + $PrincipalName, + + [Parameter()] + [System.String] + $Descriptor, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [ValidateSet('Project', 'Organization')] + [System.String] + $Level, + + [Parameter()] + [System.String[]] + $Members, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + New-M365DSCConnection -Workload 'AzureDevOPS' ` + -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) + { + if (-not [System.String]::IsNullOrEmpty($Descriptor)) + { + $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.descriptor -eq $Descriptor} + } + + if ($null -eq $instance) + { + $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.principalName -eq $PrincipalName} + } + } + else + { + $uri = "https://vssps.dev.azure.com/$OrganizationName/_apis/graph/groups?api-version=7.1-preview.1" + $allInstances = (Invoke-M365DSCAzureDevOPSWebRequest -Uri $uri).value + if (-not [System.String]::IsNullOrEmpty($Descriptor)) + { + $instance = $allInstances | Where-Object -FilterScript {$_.descriptor -eq $Descriptor} + } + if ($null -eq $instance) + { + $instance = $allInstances | Where-Object -FilterScript {$_.principalName -eq $PrincipalName} + } + } + if ($null -eq $instance) + { + return $nullResult + } + + # Level + $LevelValue = 'Project' + if ($instance.domain.StartsWith('vstfs:///Framework/IdentityDomain/')) + { + $LevelValue = 'Organization' + } + + # Membership + $MembersValue = @() + $uri = "https://vsaex.dev.azure.com/$($OrganizationName)/_apis/GroupEntitlements/$($instance.originId)/members?api-version=7.1" + $membership = (Invoke-M365DSCAzureDevOPSWebRequest -Uri $uri).members + + foreach ($member in $membership) + { + $MembersValue += $member.user.principalName + } + + $results = @{ + OrganizationName = $OrganizationName + PrincipalName = $instance.principalName + Description = $instance.description + DisplayName = $instance.displayName + Descriptor = $instance.descriptor + Level = $LevelValue + Id = $instance.originId + Members = $MembersValue + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + return [System.Collections.Hashtable] $results + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullResult + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $OrganizationName, + + [Parameter(Mandatory = $true)] + [System.String] + $PrincipalName, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Descriptor, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [ValidateSet('Project', 'Organization')] + [System.String] + $Level, + + [Parameter()] + [System.String[]] + $Members, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + New-M365DSCConnection -Workload 'AzureDevOPS' ` + -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 + + $currentInstance = Get-TargetResource @PSBoundParameters + + # CREATE + if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') + { + $newGroup = $null + if ($Level -eq 'Organization') + { + $uri = "https://vssps.dev.azure.com/$OrganizationName/_apis/graph/groups?api-version=7.1-preview.1" + $body = '{"displayName": "' + $DisplayName + '","description": "' + $Description + '"}' + $newGroup = Invoke-M365DSCAzureDevOPSWebRequest -Uri $uri -Method POST -Body $body -ContentType 'application/json' + } + elseif ($Level -eq 'Project') + { + $projectName = $PrincipalName.Split(']')[0] + $projectName = $projectName.Substring(1, $projectName.Length -1) + $uri = "https://dev.azure.com/$($OrganizationName)/_apis/projects/$($ProjectName)?api-version=7.1" + $response = Invoke-M365DSCAzureDevOPSWebRequest -Uri $uri + $projectId = $response.id + + $uri = "https://vssps.dev.azure.com/$($OrganizationName)/_apis/graph/descriptors/$($projectId)?api-version=7.1-preview.1" + $response = Invoke-M365DSCAzureDevOPSWebRequest -Uri $uri + $scope = $response.value + + $uri = "https://vssps.dev.azure.com/$($OrganizationName)/_apis/graph/groups?scopeDescriptor=$($scope)&api-version=7.1-preview.1" + $body = '{"displayName": "' + $DisplayName + '","description": "' + $Description + '"}' + $newGroup = Invoke-M365DSCAzureDevOPSWebRequest -Uri $uri -Method POST -Body $body -ContentType 'application/json' + } + + Write-Host "NEWGROUP::: $($newGroup | fl * | Out-String)" + foreach ($member in $Members) + { + Write-Verbose -Message "Adding Member {$member} to group ${$PrincipalName}" + Set-M365DSCADOPermissionGroupMember -OrganizationName $OrganizationName ` + -GroupId $newGroup.originId ` + -PrincipalName $member + } + } + # UPDATE + elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + { + if ($Description -ne $currentInstance.Description) + { + Write-Verbose -Message "Updating group {$PrincipalName} description to {$Description}" + $uri = "https://vssps.dev.azure.com/$($OrganizationName)/_apis/graph/groups/$($currentInstance.Descriptor)?api-version=7.1-preview.1" + $body = '[{"op": "replace", "path": "/description", "from": null, "value": "' + $Description + '"}]' + } + + $membershipChanges = Compare-Object -ReferenceObject $currentInstance.Members -DifferenceObject $Members + foreach ($diff in $membershipChanges) + { + if ($diff.SideIndicator -eq '=>') + { + Write-Verbose -Message "Adding Member {$($diff.InputObject)} to group ${$PrincipalName}" + Set-M365DSCADOPermissionGroupMember -OrganizationName $OrganizationName ` + -GroupId $currentInstance.Id ` + -PrincipalName $diff.InputObject ` + -Method 'PUT' + } + else + { + Write-Verbose -Message "Removing Member {$($diff.InputObject)} to group ${$PrincipalName}" + Set-M365DSCADOPermissionGroupMember -OrganizationName $OrganizationName ` + -GroupId $currentInstance.Id ` + -PrincipalName $diff.InputObject ` + -Method 'DELETE' + } + } + } + # REMOVE + elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') + { + Write-Verbose -Message "Removing group {$principalName} with Descriptor {$($currentInstance.Descriptor)}" + $uri = "https://vssps.dev.azure.com/$($OrganizationName)/_apis/graph/groups/$($currentInstance.Descriptor)?api-version=7.1-preview.1" + Invoke-M365DSCAzureDevOPSWebRequest -Uri $uri -Method 'DELETE' -ContentType 'application/json' | Out-Null + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $OrganizationName, + + [Parameter(Mandatory = $true)] + [System.String] + $PrincipalName, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Descriptor, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [ValidateSet('Project', 'Organization')] + [System.String] + $Level, + + [Parameter()] + [System.String[]] + $Members, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" + + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + Write-Verbose -Message "Test-TargetResource returned $testResult" + + return $testResult +} + +function Export-TargetResource +{ + [CmdletBinding()] + [OutputType([System.String])] + param + ( + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + ##TODO - Replace workload + $ConnectionMode = New-M365DSCConnection -Workload 'AzureDevOPS' ` + -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 + + $profile = Invoke-M365DSCAzureDevOPSWebRequest -Uri 'https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=5.1' + $accounts = Invoke-M365DSCAzureDevOPSWebRequest -Uri "https://app.vssps.visualstudio.com/_apis/accounts?api-version=7.1-preview.1&memberId=$($profile.id)" + + $i = 1 + $dscContent = '' + if ($accounts.Length -eq 0) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + foreach ($account in $accounts) + { + $organization = $account.Value.accountName + $uri = "https://vssps.dev.azure.com/$organization/_apis/graph/groups?api-version=7.1-preview.1" + + [array] $Script:exportedInstances = (Invoke-M365DSCAzureDevOPSWebRequest -Uri $uri).Value + + $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.principalName + if ($null -ne $Global:M365DSCExportResourceInstancesCount) + { + $Global:M365DSCExportResourceInstancesCount++ + } + Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline + $params = @{ + OrganizationName = $Organization + PrincipalName = $config.principalName + Descriptor = $config.descriptor + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + + if (-not $config.principalName.StartsWith("[TEAM FOUNDATION]")) + { + $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 '' + } +} + +function Set-M365DSCADOPermissionGroupMember +{ + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [System.String] + $OrganizationName, + + [Parameter(Mandatory = $true)] + [System.String] + $GroupId, + + [Parameter(Mandatory = $true)] + [System.String] + $PrincipalName, + + [Parameter()] + [System.String] + $Method = 'Put' + ) + if ($null -eq $Script:allUsers) + { + $uri = "https://vsaex.dev.azure.com/$($OrganizationName)/_apis/userentitlements?api-version=7.2-preview.4" + $Script:allUsers = Invoke-M365DSCAzureDevOPSWebRequest -Uri $uri + } + $user = $Script:allUsers.items | Where-Object -FilterScript {$_.user.principalName -eq $PrincipalName} + $UserId = $user.id + $uri = "https://vsaex.dev.azure.com/$($OrganizationName)/_apis/GroupEntitlements/$($GroupId)/members/$($UserId)?api-version=5.0-preview.1" + Invoke-M365DSCAzureDevOPSWebRequest -Uri $uri -Method $Method | Out-Null +} + +Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/MSFT_ADOPermissionGroup.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/MSFT_ADOPermissionGroup.schema.mof new file mode 100644 index 0000000000..43ef224474 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/MSFT_ADOPermissionGroup.schema.mof @@ -0,0 +1,19 @@ +[ClassVersion("1.0.0.0"), FriendlyName("ADOPermissionGroup")] +class MSFT_ADOPermissionGroup : OMI_BaseResource +{ + [Key, Description("The name of the Azure DevOPS Organization.")] String OrganizationName; + [Key, Description("Principal name to identify the group.")] String PrincipalName; + [Write, Description("Display name for the group.")] String DisplayName; + [Write, Description("Description of the group.")] String Description; + [Write, Description("List of principal names of the members of the group.")] String Members[]; + [Write, Description("Unique identifier for the group.")] String Id; + [Write, Description("Unique descriptor for the group.")] String Descriptor; + [Write, Description("Determines at what level in the hierarchy the group exists. Valid values are Project or Organization."), ValueMap{"Organization", "Project"}, Values{"Organization", "Project"}] String Level; + [Write, Description("Present ensures the instance exists, absent ensures it is removed."), 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; + [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_ADOPermissionGroup/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/readme.md new file mode 100644 index 0000000000..d460ee4d90 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/readme.md @@ -0,0 +1,6 @@ + +# ADOPermissionGroup + +## Description + +Manages Azure DevOPS permission groups. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/settings.json new file mode 100644 index 0000000000..9157b5dcfc --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_ADOPermissionGroup/settings.json @@ -0,0 +1,20 @@ +{ + "resourceName": "ADOPermissionGroup", + "description": "Manages Azure DevOPS permission groups.", + "roles": { + "read": [], + "update": [] + }, + "permissions": { + "graph": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [], + "update": [] + } + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/ADOPermissionGroup/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/ADOPermissionGroup/1-Create.ps1 new file mode 100644 index 0000000000..053c8b5b43 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/ADOPermissionGroup/1-Create.ps1 @@ -0,0 +1,38 @@ +<# +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 + { + ADOPermissionGroup "TestPermissionGroup" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Description = "My Description"; + DisplayName = "TestGroup"; + Ensure = "Present"; + Level = "Organization"; + Members = @("AdeleV@$TenantId"); + OrganizationName = "O365DSC-Dev"; + PrincipalName = "[O365DSC-DEV]\TestGroup"; + TenantId = $TenantId; + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/ADOPermissionGroup/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/ADOPermissionGroup/2-Update.ps1 new file mode 100644 index 0000000000..f4bf481b12 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/ADOPermissionGroup/2-Update.ps1 @@ -0,0 +1,38 @@ +<# +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 + { + ADOPermissionGroup "TestPermissionGroup" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Description = "My Description"; + DisplayName = "TestGroup"; + Ensure = "Present"; + Level = "Organization"; + Members = @("AdeleV@$TenantId", "admin@$TenantId"); #Drift + OrganizationName = "O365DSC-Dev"; + PrincipalName = "[O365DSC-DEV]\TestGroup"; + TenantId = $TenantId; + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/ADOPermissionGroup/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/ADOPermissionGroup/3-Remove.ps1 new file mode 100644 index 0000000000..57f2071f19 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/ADOPermissionGroup/3-Remove.ps1 @@ -0,0 +1,38 @@ +<# +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 + { + ADOPermissionGroup "TestPermissionGroup" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Description = "My Description"; + DisplayName = "TestGroup"; + Ensure = "Absent"; + Level = "Organization"; + Members = @("AdeleV@$TenantId"); + OrganizationName = "O365DSC-Dev"; + PrincipalName = "[O365DSC-DEV]\TestGroup"; + TenantId = $TenantId; + } + } +} diff --git a/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCAzureDevOPSHelper.psm1 b/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCAzureDevOPSHelper.psm1 index 19f3105cf7..92375e9444 100644 --- a/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCAzureDevOPSHelper.psm1 +++ b/Modules/Microsoft365DSC/Modules/WorkloadHelpers/M365DSCAzureDevOPSHelper.psm1 @@ -13,12 +13,16 @@ function Invoke-M365DSCAzureDevOPSWebRequest [Parameter()] [System.String] - $Body + $Body, + + [Parameter()] + [System.String] + $ContentType = 'application/json-patch+json' ) $headers = @{ Authorization = $global:MsCloudLoginConnectionProfile.AzureDevOPS.AccessToken - 'Content-Type' = 'application/json-patch+json' + 'Content-Type' = $ContentType } $params = @{ diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ADOPermissionGroup.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ADOPermissionGroup.Tests.ps1 new file mode 100644 index 0000000000..eab6c2dfc5 --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ADOPermissionGroup.Tests.ps1 @@ -0,0 +1,290 @@ +[CmdletBinding()] +param( +) +$M365DSCTestFolder = Join-Path -Path $PSScriptRoot ` + -ChildPath '..\..\Unit' ` + -Resolve +$CmdletModule = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Microsoft365.psm1' ` + -Resolve) +$GenericStubPath = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Generic.psm1' ` + -Resolve) +Import-Module -Name (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\UnitTestHelper.psm1' ` + -Resolve) + +$CurrentScriptPath = $PSCommandPath.Split('\') +$CurrentScriptName = $CurrentScriptPath[$CurrentScriptPath.Length -1] +$ResourceName = $CurrentScriptName.Split('.')[1] +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource $ResourceName -GenericStubModule $GenericStubPath + +Describe -Name $Global:DscHelper.DescribeHeader -Fixture { + InModuleScope -ModuleName $Global:DscHelper.ModuleName -ScriptBlock { + Invoke-Command -ScriptBlock $Global:DscHelper.InitializeScript -NoNewScope + BeforeAll { + + $secpasswd = ConvertTo-SecureString (New-Guid | Out-String) -AsPlainText -Force + $Credential = New-Object System.Management.Automation.PSCredential ('tenantadmin@mydomain.com', $secpasswd) + + Mock -CommandName Confirm-M365DSCDependencies -MockWith { + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credentials" + } + + # Mock 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 = @{ + Description = "Test Description"; + DisplayName = "TestGroup"; + Level = "Organization"; + Members = @("john.smith@contoso.com"); + OrganizationName = "TestOrg"; + PrincipalName = "[TestOrg]\TestGroup"; + Ensure = 'Present' + Credential = $Credential; + } + + Mock -CommandName Invoke-M365DSCAzureDevOPSWebRequest -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 + } + } + + Context -Name "The instance exists but it SHOULD NOT" -Fixture { + BeforeAll { + $testParams = @{ + Description = "Test Description"; + DisplayName = "TestGroup"; + Level = "Organization"; + Members = @("john.smith@contoso.com"); + OrganizationName = "TestOrg"; + PrincipalName = "[TestOrg]\TestGroup"; + Ensure = 'Absent' + Credential = $Credential; + } + + Mock -CommandName Invoke-M365DSCAzureDevOPSWebRequest -MockWith { + if ($Script:count -eq 0) + { + $Script:count++ + return @{ + Value = @{ + PrincipalName = '[TestOrg]\TestGroup' + Domain = 'vstfs:///Framework/IdentityDomain/' + OriginId = '12345-12345-12345-12345-12345' + } + } + } + elseif ($Script:count -eq 1) + { + $Script:count++ + return @{ + Members = @( + @{ + User = @{ + principalName = "john.smith@contoso.com" + } + } + ) + } + } + } + } + It 'Should return Values from the Get method' { + $Script:count = 0 + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + } + It 'Should return false from the Test method' { + $Script:count = 0 + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should remove the instance from the Set method' { + Set-TargetResource @testParams + } + } + + Context -Name "The instance exists and values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + Description = "Test Description"; + DisplayName = "TestGroup"; + Level = "Organization"; + Members = @("john.smith@contoso.com"); + OrganizationName = "TestOrg"; + PrincipalName = "[TestOrg]\TestGroup"; + Ensure = 'Present' + Credential = $Credential; + } + + $Script:count = 0 + Mock -CommandName Invoke-M365DSCAzureDevOPSWebRequest -MockWith { + if ($Script:count -eq 0) + { + $Script:count++ + return @{ + Value = @{ + PrincipalName = '[TestOrg]\TestGroup' + Domain = 'vstfs:///Framework/IdentityDomain/' + OriginId = '12345-12345-12345-12345-12345' + Description = 'Test Description' + DisplayName = 'TestGroup' + } + } + } + elseif ($Script:count -eq 1) + { + $Script:count++ + return @{ + Members = @( + @{ + User = @{ + principalName = "john.smith@contoso.com" + } + } + ) + } + } + } + } + + 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 = @{ + Description = "Test Description"; + DisplayName = "TestGroup"; + Level = "Organization"; + Members = @("john.smith@contoso.com"); + OrganizationName = "TestOrg"; + PrincipalName = "[TestOrg]\TestGroup"; + Ensure = 'Present' + Credential = $Credential; + } + + $Script:count = 0 + Mock -CommandName Invoke-M365DSCAzureDevOPSWebRequest -MockWith { + if ($Script:count -eq 0) + { + $Script:count++ + return @{ + Value = @{ + PrincipalName = '[TestOrg]\TestGroup' + Domain = 'vstfs:///Framework/IdentityDomain/' + OriginId = '12345-12345-12345-12345-12345' + Description = 'Test Description' + DisplayName = 'TestGroup' + } + } + } + elseif ($Script:count -eq 1) + { + $Script:count++ + return @{ + Members = @( + @{ + User = @{ + principalName = "bob.houle@contoso.com" # drift + } + } + ) + } + } + } + } + + 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 + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential; + } + + $Script:count = 0 + Mock -CommandName Invoke-M365DSCAzureDevOPSWebRequest -MockWith { + if ($Script:count -eq 0) + { + $Script:count++ + return @{ + id = '12345-12345-12345-12345-12346' + } + } + elseif ($Script:count -eq 1) + { + $Script:count++ + return @( + @{ + Value = @{ + accountName = 'MyOrg' + } + } + ) + } + elseif ($Script:count -eq 2) + { + $Script:count++ + return @{ + Value = @{ + PrincipalName = '[TestOrg]\TestGroup' + Domain = 'vstfs:///Framework/IdentityDomain/' + OriginId = '12345-12345-12345-12345-12345' + Description = 'Test Description' + DisplayName = 'TestGroup' + } + } + } + elseif ($Script:count -eq 3) + { + $Script:count++ + return @{ + Members = @( + @{ + User = @{ + principalName = "john.smith@contoso.com" + } + } + ) + } + } + } + } + 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 66930b7a8d59e3a6f6fdb0bf42a3bb212beee891 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Thu, 19 Sep 2024 15:37:35 -0400 Subject: [PATCH 34/46] Update CHANGELOG.md --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b25d25321c..a2fc9290a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ * Initial release * ADOOrganizationOwner * Initial release. -* ADOOrganizationOwner +* ADOPermissionGroup * Initial release. * ADOSecurityPolicy * Initial release. @@ -22,8 +22,6 @@ * Initial Release. * EXOManagementScope * Initial Release. -* EXOManagementScope - * Initial Release. * EXORetenionPolicy * Initial Release. * EXOPhishSimOverrideRule From 5d47b69dd86f9edd562af3d9e7056cdb8a89b326 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 19 Sep 2024 19:58:58 +0000 Subject: [PATCH 35/46] Updated Resources and Cmdlet documentation pages --- .../resources/azure-ad/ADOPermissionGroup.md | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 docs/docs/resources/azure-ad/ADOPermissionGroup.md diff --git a/docs/docs/resources/azure-ad/ADOPermissionGroup.md b/docs/docs/resources/azure-ad/ADOPermissionGroup.md new file mode 100644 index 0000000000..f05b158e0d --- /dev/null +++ b/docs/docs/resources/azure-ad/ADOPermissionGroup.md @@ -0,0 +1,178 @@ +# ADOPermissionGroup + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **OrganizationName** | Key | String | The name of the Azure DevOPS Organization. | | +| **PrincipalName** | Key | String | Principal name to identify the group. | | +| **DisplayName** | Write | String | Display name for the group. | | +| **Description** | Write | String | Description of the group. | | +| **Members** | Write | StringArray[] | List of principal names of the members of the group. | | +| **Id** | Write | String | Unique identifier for the group. | | +| **Descriptor** | Write | String | Unique descriptor for the group. | | +| **Level** | Write | String | Determines at what level in the hierarchy the group exists. Valid values are Project or Organization. | `Organization`, `Project` | +| **Ensure** | Write | String | Present ensures the instance exists, absent ensures it is removed. | `Present`, `Absent` | +| **Credential** | Write | PSCredential | Credentials of the workload's Admin | | +| **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | +| **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | +| **CertificateThumbprint** | Write | String | Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication. | | +| **ManagedIdentity** | Write | Boolean | Managed ID being used for authentication. | | +| **AccessTokens** | Write | StringArray[] | Access token used for authentication. | | + + +## Description + +Manages Azure DevOPS permission groups. + +## Permissions + +### Microsoft Graph + +To authenticate with the Microsoft Graph API, this resource required the following permissions: + +#### Delegated permissions + +- **Read** + + - None + +- **Update** + + - None + +#### Application permissions + +- **Read** + + - None + +- **Update** + + - None + +## Examples + +### Example 1 + +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. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + ADOPermissionGroup "TestPermissionGroup" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Description = "My Description"; + DisplayName = "TestGroup"; + Ensure = "Present"; + Level = "Organization"; + Members = @("AdeleV@$TenantId"); + OrganizationName = "O365DSC-Dev"; + PrincipalName = "[O365DSC-DEV]\TestGroup"; + TenantId = $TenantId; + } + } +} +``` + +### Example 2 + +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. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + ADOPermissionGroup "TestPermissionGroup" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Description = "My Description"; + DisplayName = "TestGroup"; + Ensure = "Present"; + Level = "Organization"; + Members = @("AdeleV@$TenantId", "admin@$TenantId"); #Drift + OrganizationName = "O365DSC-Dev"; + PrincipalName = "[O365DSC-DEV]\TestGroup"; + TenantId = $TenantId; + } + } +} +``` + +### Example 3 + +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. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + ADOPermissionGroup "TestPermissionGroup" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Description = "My Description"; + DisplayName = "TestGroup"; + Ensure = "Absent"; + Level = "Organization"; + Members = @("AdeleV@$TenantId"); + OrganizationName = "O365DSC-Dev"; + PrincipalName = "[O365DSC-DEV]\TestGroup"; + TenantId = $TenantId; + } + } +} +``` + From 73d969390b6ba246212afda667c6db95caa6f172 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 19 Sep 2024 20:01:16 +0000 Subject: [PATCH 36/46] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index 239087b5ff..7a8f7aec70 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -5199,6 +5199,86 @@ } ] }, + { + "ClassName": "MSFT_ADOPermissionGroup", + "Parameters": [ + { + "CIMType": "String", + "Name": "OrganizationName", + "Option": "Key" + }, + { + "CIMType": "String", + "Name": "PrincipalName", + "Option": "Key" + }, + { + "CIMType": "String", + "Name": "DisplayName", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Description", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "Members", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Id", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Descriptor", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Level", + "Option": "Write" + }, + { + "CIMType": "string", + "Name": "Ensure", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "Credential", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ApplicationId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "TenantId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "CertificateThumbprint", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "ManagedIdentity", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "AccessTokens", + "Option": "Write" + } + ] + }, { "ClassName": "MSFT_ADOSecurityPolicy", "Parameters": [ From 04f7136e907a9b78e482e96f4f74b1ec27fa3911 Mon Sep 17 00:00:00 2001 From: Peter Richards Date: Thu, 19 Sep 2024 15:24:59 -0700 Subject: [PATCH 37/46] Implement support for DmarcQuarantineAction and DmarcRejectAction on EXOAntiPhishPolicy --- .../MSFT_EXOAntiPhishPolicy.psm1 | 32 +++++++++++++++++++ .../MSFT_EXOAntiPhishPolicy.schema.mof | 2 ++ .../Resources/EXOAntiPhishPolicy/1-Create.ps1 | 2 ++ .../Resources/EXOAntiPhishPolicy/2-Update.ps1 | 2 ++ .../Resources/EXOAntiPhishPolicy/3-Remove.ps1 | 2 ++ .../M365DSCIntegration.EXO.Create.Tests.ps1 | 2 ++ .../M365DSCIntegration.EXO.Remove.Tests.ps1 | 4 ++- .../M365DSCIntegration.EXO.Update.Tests.ps1 | 2 ++ 8 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAntiPhishPolicy/MSFT_EXOAntiPhishPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAntiPhishPolicy/MSFT_EXOAntiPhishPolicy.psm1 index d849dc40ff..2fab25e233 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAntiPhishPolicy/MSFT_EXOAntiPhishPolicy.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAntiPhishPolicy/MSFT_EXOAntiPhishPolicy.psm1 @@ -151,6 +151,16 @@ function Get-TargetResource [System.String] $TargetedUserQuarantineTag, + [Parameter()] + [System.String] + [ValidateSet('MoveToJmf', 'Quarantine')] + $DmarcQuarantineAction, + + [Parameter()] + [System.String] + [ValidateSet('Quarantine', 'Reject')] + $DmarcRejectAction, + [Parameter()] [System.Management.Automation.PSCredential] $Credential, @@ -277,6 +287,8 @@ function Get-TargetResource TargetedUserProtectionAction = $TargetedUserProtectionActionValue TargetedUsersToProtect = $AntiPhishPolicy.TargetedUsersToProtect TargetedUserQuarantineTag = $AntiPhishPolicy.TargetedUserQuarantineTag + DmarcQuarantineAction = $AntiPhishPolicy.DmarcQuarantineAction + DmarcRejectAction = $AntiPhishPolicy.DmarcRejectAction Credential = $Credential Ensure = 'Present' ApplicationId = $ApplicationId @@ -457,6 +469,16 @@ function Set-TargetResource [System.String] $TargetedUserQuarantineTag, + [Parameter()] + [System.String] + [ValidateSet('MoveToJmf', 'Quarantine')] + $DmarcQuarantineAction, + + [Parameter()] + [System.String] + [ValidateSet('Quarantine', 'Reject')] + $DmarcRejectAction, + [Parameter()] [System.Management.Automation.PSCredential] $Credential, @@ -694,6 +716,16 @@ function Test-TargetResource [System.String] $TargetedUserQuarantineTag, + [Parameter()] + [System.String] + [ValidateSet('MoveToJmf', 'Quarantine')] + $DmarcQuarantineAction, + + [Parameter()] + [System.String] + [ValidateSet('Quarantine', 'Reject')] + $DmarcRejectAction, + [Parameter()] [System.Management.Automation.PSCredential] $Credential, diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAntiPhishPolicy/MSFT_EXOAntiPhishPolicy.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAntiPhishPolicy/MSFT_EXOAntiPhishPolicy.schema.mof index 42d4410650..39594f64e1 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAntiPhishPolicy/MSFT_EXOAntiPhishPolicy.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOAntiPhishPolicy/MSFT_EXOAntiPhishPolicy.schema.mof @@ -36,6 +36,8 @@ class MSFT_EXOAntiPhishPolicy : OMI_BaseResource [Write, Description("The TargetedUserActionRecipients parameter specifies the replacement or additional recipients for detected user impersonation messages when the TargetedUserProtectionAction parameter is set to the value Redirect or BccMessage. A valid value for this parameter is an email address. You can specify multiple email addresses separated by commas.")] String TargetedUserActionRecipients[]; [Write, Description("The TargetedUsersToProtect parameter specifies the users that are included in user impersonation protection when the EnableTargetedUserProtection parameter is set to $true.")] String TargetedUsersToProtect[]; [Write, Description("The TargetedUserQuarantineTag specifies the quarantine policy that's used on messages that are quarantined by user impersonation protection.")] String TargetedUserQuarantineTag; + [Write, Description("The DmarcQuarantineAction parameter specifies the action to take when a message fails DMARC checks and the sender's DMARC policy is p=quarantine"), ValueMap{"MoveToJmf","Quarantine"}, Values{"MoveToJmf","Quarantine"}] String DmarcQuarantineAction; + [Write, Description("The DmarcRejectAction parameter specifies the action to take when a message fails DMARC checks and the sender's DMARC policy is p=reject."), ValueMap{"Quarantine","Reject"}, Values{"Quarantine","Reject"}] String DmarcRejectAction; [Write, Description("Credentials of the Exchange Global 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/Examples/Resources/EXOAntiPhishPolicy/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOAntiPhishPolicy/1-Create.ps1 index a65fc01916..2166373c7b 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/EXOAntiPhishPolicy/1-Create.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOAntiPhishPolicy/1-Create.ps1 @@ -45,6 +45,8 @@ Configuration Example EnableUnusualCharactersSafetyTips = $null TargetedUserActionRecipients = $null Ensure = "Present" + DmarcQuarantineAction = "Quarantine" + DmarcRejectAction = "Reject" ApplicationId = $ApplicationId TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOAntiPhishPolicy/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOAntiPhishPolicy/2-Update.ps1 index b5fa497a46..5a606beeaa 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/EXOAntiPhishPolicy/2-Update.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOAntiPhishPolicy/2-Update.ps1 @@ -45,6 +45,8 @@ Configuration Example EnableUnusualCharactersSafetyTips = $null TargetedUserActionRecipients = $null Ensure = "Present" + DmarcQuarantineAction = "Quarantine" + DmarcRejectAction = "Reject" ApplicationId = $ApplicationId TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOAntiPhishPolicy/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOAntiPhishPolicy/3-Remove.ps1 index b5fa497a46..7bf9e68c43 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/EXOAntiPhishPolicy/3-Remove.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOAntiPhishPolicy/3-Remove.ps1 @@ -44,6 +44,8 @@ Configuration Example EnableOrganizationDomainsProtection = $null EnableUnusualCharactersSafetyTips = $null TargetedUserActionRecipients = $null + DmarcQuarantineAction = "Quarantine" + DmarcRejectAction = "Reject" Ensure = "Present" ApplicationId = $ApplicationId TenantId = $TenantId diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 index 907e37db8e..5d5ded4159 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 @@ -101,6 +101,8 @@ EnableOrganizationDomainsProtection = $null EnableUnusualCharactersSafetyTips = $null TargetedUserActionRecipients = $null + DmarcQuarantineAction = "Quarantine" + DmarcRejectAction = "Reject" Ensure = "Present" ApplicationId = $ApplicationId TenantId = $TenantId diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 index 065fa1f57d..a1e535e007 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 @@ -92,6 +92,8 @@ EnableOrganizationDomainsProtection = $null EnableUnusualCharactersSafetyTips = $null TargetedUserActionRecipients = $null + DmarcQuarantineAction = "Quarantine" + DmarcRejectAction = "Reject" Ensure = "Present" ApplicationId = $ApplicationId TenantId = $TenantId @@ -469,7 +471,7 @@ } EXORecipientPermission 'AddSendAs' { - + Identity = 'AdeleV@$Domain' Trustee = "admin@$TenantId" Ensure = 'Absent' diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 index 200455c0ad..8b40016f2e 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 @@ -101,6 +101,8 @@ EnableOrganizationDomainsProtection = $null EnableUnusualCharactersSafetyTips = $null TargetedUserActionRecipients = $null + DmarcQuarantineAction = "Quarantine" + DmarcRejectAction = "Reject" Ensure = "Present" ApplicationId = $ApplicationId TenantId = $TenantId From e0b9483d8bb352aba866325ed294024574f9821d Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Fri, 20 Sep 2024 04:46:36 +0000 Subject: [PATCH 38/46] Updated Resources and Cmdlet documentation pages --- .../exchange/EXOEmailTenantSettings.md | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 docs/docs/resources/exchange/EXOEmailTenantSettings.md diff --git a/docs/docs/resources/exchange/EXOEmailTenantSettings.md b/docs/docs/resources/exchange/EXOEmailTenantSettings.md new file mode 100644 index 0000000000..0de767d147 --- /dev/null +++ b/docs/docs/resources/exchange/EXOEmailTenantSettings.md @@ -0,0 +1,83 @@ +# EXOEmailTenantSettings + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **IsSingleInstance** | Key | String | Only valid value is 'Yes'. | `Yes` | +| **Identity** | Write | String | Identity which indicates the organization name. | | +| **EnablePriorityAccountProtection** | Write | Boolean | Specifies whether priority account protection is enabled. | | +| **IsValid** | Write | Boolean | Specifies whether the migration configuration is valid. | | +| **ObjectState** | Write | String | Specifies the state of the object. | | +| **Name** | Write | String | Specifies the name of the object. | | +| **Credential** | Write | PSCredential | Credentials of the Exchange Global Admin | | +| **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | +| **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | +| **CertificateThumbprint** | Write | String | Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication. | | +| **CertificatePath** | Write | String | Path to certificate used in service principal usually a PFX file. | | +| **CertificatePassword** | Write | PSCredential | Username can be made up to anything but password will be used for CertificatePassword | | +| **ManagedIdentity** | Write | Boolean | Managed ID being used for authentication. | | +| **AccessTokens** | Write | StringArray[] | Access token used for authentication. | | + +EXOEmailTenantSettings + +## Description + +This resource allows users to manage email tenant settings. + +## Permissions + +### Exchange + +To authenticate with Microsoft Exchange, this resource required the following permissions: + +#### Roles + +- Organization Management, Security Reader + +#### Role Groups + +- Organization Management, Security Administrator + +## Examples + +### Example 1 + +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. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + EXOEmailTenantSettings "EXOEmailTenantSettings-Test" + { + IsSingleInstance = "Yes" + EnablePriorityAccountProtection = $True; + Identity = $TenantId; + IsValid = $True; + ObjectState = "Unchanged" + Name = "Default" + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ApplicationId = $ApplicationId + } + } +} +``` + From 5b1e1f5f3c78ee187621ee5f4a9c80322ff8939f Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Fri, 20 Sep 2024 04:48:34 +0000 Subject: [PATCH 39/46] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index 7a8f7aec70..3c139d61a7 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -7994,6 +7994,81 @@ } ] }, + { + "ClassName": "MSFT_EXOEmailTenantSettings", + "Parameters": [ + { + "CIMType": "String", + "Name": "IsSingleInstance", + "Option": "Key" + }, + { + "CIMType": "String", + "Name": "Identity", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "EnablePriorityAccountProtection", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "IsValid", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ObjectState", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Name", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "Credential", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ApplicationId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "TenantId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "CertificateThumbprint", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "CertificatePath", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "CertificatePassword", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "ManagedIdentity", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "AccessTokens", + "Option": "Write" + } + ] + }, { "ClassName": "MSFT_EXOEOPProtectionPolicyRule", "Parameters": [ From 581d15723d077abc1b7a20ea2c25afb5086536aa Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Fri, 20 Sep 2024 04:49:22 +0000 Subject: [PATCH 40/46] Updated {Update} EXO Integration Tests --- .../M365DSCIntegration.EXO.Update.Tests.ps1 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 index 200455c0ad..964a5ffcdf 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 @@ -370,6 +370,18 @@ TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint } + EXOEmailTenantSettings 'EXOEmailTenantSettings-Test' + { + IsSingleInstance = "Yes" + EnablePriorityAccountProtection = $True; + Identity = $TenantId; + IsValid = $True; + ObjectState = "Unchanged" + Name = "Default" + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ApplicationId = $ApplicationId + } EXOFocusedInbox 'EXOFocusedInbox-Test' { Ensure = "Present"; From 6b790ba5ea545cde63fc35fc25f258c2ee6d4c02 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Fri, 20 Sep 2024 17:45:15 +0000 Subject: [PATCH 41/46] Updated Resources and Cmdlet documentation pages --- docs/docs/resources/exchange/EXOAntiPhishPolicy.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/docs/resources/exchange/EXOAntiPhishPolicy.md b/docs/docs/resources/exchange/EXOAntiPhishPolicy.md index d0859b5a13..5c09c05326 100644 --- a/docs/docs/resources/exchange/EXOAntiPhishPolicy.md +++ b/docs/docs/resources/exchange/EXOAntiPhishPolicy.md @@ -39,6 +39,8 @@ | **TargetedUserActionRecipients** | Write | StringArray[] | The TargetedUserActionRecipients parameter specifies the replacement or additional recipients for detected user impersonation messages when the TargetedUserProtectionAction parameter is set to the value Redirect or BccMessage. A valid value for this parameter is an email address. You can specify multiple email addresses separated by commas. | | | **TargetedUsersToProtect** | Write | StringArray[] | The TargetedUsersToProtect parameter specifies the users that are included in user impersonation protection when the EnableTargetedUserProtection parameter is set to $true. | | | **TargetedUserQuarantineTag** | Write | String | The TargetedUserQuarantineTag specifies the quarantine policy that's used on messages that are quarantined by user impersonation protection. | | +| **DmarcQuarantineAction** | Write | String | The DmarcQuarantineAction parameter specifies the action to take when a message fails DMARC checks and the sender's DMARC policy is p=quarantine | `MoveToJmf`, `Quarantine` | +| **DmarcRejectAction** | Write | String | The DmarcRejectAction parameter specifies the action to take when a message fails DMARC checks and the sender's DMARC policy is p=reject. | `Quarantine`, `Reject` | | **Credential** | Write | PSCredential | Credentials of the Exchange Global Admin | | | **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | | **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | @@ -117,6 +119,8 @@ Configuration Example EnableUnusualCharactersSafetyTips = $null TargetedUserActionRecipients = $null Ensure = "Present" + DmarcQuarantineAction = "Quarantine" + DmarcRejectAction = "Reject" ApplicationId = $ApplicationId TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint @@ -173,6 +177,8 @@ Configuration Example EnableUnusualCharactersSafetyTips = $null TargetedUserActionRecipients = $null Ensure = "Present" + DmarcQuarantineAction = "Quarantine" + DmarcRejectAction = "Reject" ApplicationId = $ApplicationId TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint @@ -228,6 +234,8 @@ Configuration Example EnableOrganizationDomainsProtection = $null EnableUnusualCharactersSafetyTips = $null TargetedUserActionRecipients = $null + DmarcQuarantineAction = "Quarantine" + DmarcRejectAction = "Reject" Ensure = "Present" ApplicationId = $ApplicationId TenantId = $TenantId From 4f7ca1a78ff5d06af75ce8e820270d1b7b2ac96d Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Fri, 20 Sep 2024 17:47:07 +0000 Subject: [PATCH 42/46] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index 3c139d61a7..7acf32a9b7 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -5972,6 +5972,16 @@ "Name": "TargetedUserQuarantineTag", "Option": "Write" }, + { + "CIMType": "String", + "Name": "DmarcQuarantineAction", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "DmarcRejectAction", + "Option": "Write" + }, { "CIMType": "MSFT_Credential", "Name": "Credential", From 83252db2ae4d5507e1b15be84338c41c0a0e6de1 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Fri, 20 Sep 2024 17:47:22 +0000 Subject: [PATCH 43/46] Updated {Create} EXO Integration Tests --- .../Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 index 5d5ded4159..e6ed079a01 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Create.Tests.ps1 @@ -101,9 +101,9 @@ EnableOrganizationDomainsProtection = $null EnableUnusualCharactersSafetyTips = $null TargetedUserActionRecipients = $null + Ensure = "Present" DmarcQuarantineAction = "Quarantine" DmarcRejectAction = "Reject" - Ensure = "Present" ApplicationId = $ApplicationId TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint From a7ad97826b9c87921b3641c1e2bb1322d656839a Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Fri, 20 Sep 2024 17:47:49 +0000 Subject: [PATCH 44/46] Updated {Update} EXO Integration Tests --- .../Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 index edc270afb6..4b112d0d83 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 @@ -101,9 +101,9 @@ EnableOrganizationDomainsProtection = $null EnableUnusualCharactersSafetyTips = $null TargetedUserActionRecipients = $null + Ensure = "Present" DmarcQuarantineAction = "Quarantine" DmarcRejectAction = "Reject" - Ensure = "Present" ApplicationId = $ApplicationId TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint From e5f347a74305f3e763ef65d854b5d98845d19cd9 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Fri, 20 Sep 2024 17:48:11 +0000 Subject: [PATCH 45/46] Updated {Update} EXO Integration Tests --- .../Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 index a1e535e007..d309331b25 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Remove.Tests.ps1 @@ -471,7 +471,7 @@ } EXORecipientPermission 'AddSendAs' { - + Identity = 'AdeleV@$Domain' Trustee = "admin@$TenantId" Ensure = 'Absent' From c5de2da5d7fe1f5b57a90bfc102bf2f262c2e114 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Fri, 20 Sep 2024 14:10:21 -0400 Subject: [PATCH 46/46] Updated DSCPArser --- CHANGELOG.md | 1 + Modules/Microsoft365DSC/Dependencies/Manifest.psd1 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea8a75523f..4bf8282a0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ * SPOAccessControlSettings * Added support for property EnableRestrictedAccessControl. * DEPENDENCIES + * Updated DSCParser to version 2.0.0.10. * Updated Microsoft.Graph to version 2.23.0. * Added dependencies on Az.Accounts, Az.Resources and Az.SecurityInsights * Updated DSCParser to version 2.0.0.9. diff --git a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 index 1b2afb7550..85952adab5 100644 --- a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 +++ b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 @@ -14,7 +14,7 @@ }, @{ ModuleName = 'DSCParser' - RequiredVersion = '2.0.0.9' + RequiredVersion = '2.0.0.10' }, @{ ModuleName = 'ExchangeOnlineManagement'