diff --git a/Modules/Office365DSC/DSCResources/MSFT_SCRetentionComplianceTag/MSFT_SCRetentionComplianceTag.psm1 b/Modules/Office365DSC/DSCResources/MSFT_SCComplianceTag/MSFT_SCComplianceTag.psm1 similarity index 53% rename from Modules/Office365DSC/DSCResources/MSFT_SCRetentionComplianceTag/MSFT_SCRetentionComplianceTag.psm1 rename to Modules/Office365DSC/DSCResources/MSFT_SCComplianceTag/MSFT_SCComplianceTag.psm1 index 48387656c6..6af706fbc7 100644 --- a/Modules/Office365DSC/DSCResources/MSFT_SCRetentionComplianceTag/MSFT_SCRetentionComplianceTag.psm1 +++ b/Modules/Office365DSC/DSCResources/MSFT_SCComplianceTag/MSFT_SCComplianceTag.psm1 @@ -50,7 +50,6 @@ function Get-TargetResource [System.String] $RetentionType, - [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] @@ -61,47 +60,41 @@ function Get-TargetResource $GlobalAdminAccount ) - Write-Verbose -Message "Getting configuration of RetentionComplianceTag for $Name" + Write-Verbose -Message "Getting configuration of ComplianceTag for $Name" Write-Verbose -Message "Calling Test-SecurityAndComplianceConnection function:" Test-SecurityAndComplianceConnection -GlobalAdminAccount $GlobalAdminAccount - #Testing only - Write-Verbose -Message "FilePlane $($FilePlan.FilePlanPropertyDepartment)" - $tagObjects = Get-ComplianceTag - $tagObjects = $tagObjects | Where-Object { $_.Name -eq $Name } + $tagObject = $tagObjects | Where-Object { $_.Name -eq $Name } - if ($null -eq $tagObjects) + if ($null -eq $tagObject) { - Write-Verbose -Message "RetentionComplianceTag $($Name) does not exist." + Write-Verbose -Message "ComplianceTag $($Name) does not exist." $result = $PSBoundParameters $result.Ensure = 'Absent' return $result } else { - Write-Verbose "Found existing RetentionComplianceTag $($Name)" + Write-Verbose "Found existing ComplianceTag $($Name)" + $ConvertedFilePlanProperty = Get-SCFilePlanProperty $tagObject.FilePlanMetadata $result = @{ - Ensure = 'Present' - } - foreach ($KeyName in ($PSBoundParameters.Keys | Where-Object -FilterScript { $_ -ne 'Ensure' })) - { - if ($null -ne $tagObjects.$KeyName) - { - $result += @{ - $KeyName = $tagObjects.$KeyName - } - } - else - { - $result += @{ - $KeyName = $PSBoundParameters[$KeyName] - } - } + Name = $tagObject.Name + Comment = $tagObject.Comment + FilePlanProperty = $ConvertedFilePlanProperty + RetentionDuration = $tagObject.RetentionDuration + IsRecordLabel = $tagObject.IsRecordLabel + Regulatory = $tagObject.Regulatory + Notes = $tagObject.Notes + ReviewerEmail = $tagObject.ReviewerEmail + RetentionAction = $tagObject.RetentionAction + EventType = $tagObject.EventType + RetentionType = $tagObject.RetentionType + GlobalAdminAccount = $GlobalAdminAccount + Ensure = 'Present' } - Write-Verbose -Message "Found RetentionComplianceTag $($Name)" Write-Verbose -Message "Get-TargetResource Result: `n $(Convert-O365DscHashtableToString -Hashtable $result)" return $result } @@ -168,7 +161,7 @@ function Set-TargetResource $GlobalAdminAccount ) - Write-Verbose -Message "Setting configuration of RetentionComplianceTag for $Name" + Write-Verbose -Message "Setting configuration of ComplianceTag for $Name" Test-SecurityAndComplianceConnection -GlobalAdminAccount $GlobalAdminAccount $CurrentTag = Get-TargetResource @PSBoundParameters @@ -176,21 +169,24 @@ function Set-TargetResource if (('Present' -eq $Ensure) -and ('Absent' -eq $CurrentTag.Ensure)) { $CreationParams = $PSBoundParameters + $CreationParams.Remove("GlobalAdminAccount") + $CreationParams.Remove("Ensure") + #Convert File plan to JSON before Set if ($FilePlanProperty) { - Write-Verbose -Message "Converting fileplan to JSON" - $FilePlanPropertyJSON = Get-SCFilePlanProperty $FilePlanProperty - $CreationParams["FilePlanProperty"] = $FilePlanPropertyJSON + Write-Verbose -Message "Converting FilePlan to JSON" + $FilePlanPropertyJSON = ConvertTo-JSON (Get-SCFilePlanPropertyObject $FilePlanProperty) + $CreationParams.FilePlanProperty = $FilePlanPropertyJSON } - $CreationParams.Remove("GlobalAdminAccount") - $CreationParams.Remove("Ensure") + Write-Verbose "Creating new Compliance Tag $Name calling the New-ComplianceTag cmdlet." New-ComplianceTag @CreationParams } elseif (('Present' -eq $Ensure) -and ('Present' -eq $CurrentTag.Ensure)) { $SetParams = $PSBoundParameters - #Remove unsed parameters for Set-ComplianceTag cmdlet + + #Remove unused parameters for Set-ComplianceTag cmdlet $SetParams.Remove("GlobalAdminAccount") $SetParams.Remove("Ensure") $SetParams.Remove("Name") @@ -198,11 +194,41 @@ function Set-TargetResource $SetParams.Remove("Regulatory") $SetParams.Remove("RetentionAction") $SetParams.Remove("RetentionType") + + # Once set, a label can't be removed; + if ($SetParams.IsRecordLabel -eq $false -and $CurrentTag.IsRecordLabel -eq $true) + { + throw "Can't remove label on the existing Compliance Tag {$Name}. " + ` + "You will need to delete the tag and recreate it." + } + + if ($null -ne $PsBoundParameters["Regulatory"] -and + $Regulatory -ne $CurrentTag.Regulatory) + { + throw "SPComplianceTag can't change the Regulatory property on " + ` + "existing tags {$Name} from $Regulatory to $($CurrentTag.Regulatory)." + ` + " You will need to delete the tag and recreate it." + } + + if ($RetentionAction -ne $CurrentTag.RetentionAction) + { + throw "SPComplianceTag can't change the RetentionAction property on " + ` + "existing tags {$Name} from $RetentionAction to $($CurrentTag.RetentionAction)." + ` + " You will need to delete the tag and recreate it." + } + + if ($RetentionType -ne $CurrentTag.RetentionType) + { + throw "SPComplianceTag can't change the RetentionType property on " + ` + "existing tags {$Name} from $RetentionType to $($CurrentTag.RetentionType)." + ` + " You will need to delete the tag and recreate it." + } + #Convert File plan to JSON before Set if ($FilePlanProperty) { - Write-Verbose -Message "Converting fileplan to JSON" - $FilePlanPropertyJSON = Get-SCFilePlanProperty $FilePlanProperty + Write-Verbose -Message "Converting FilePlan properties to JSON" + $FilePlanPropertyJSON = ConvertTo-JSON (Get-SCFilePlanPropertyObject $FilePlanProperty) $SetParams["FilePlanProperty"] = $FilePlanPropertyJSON } Set-ComplianceTag @SetParams -Identity $Name @@ -276,7 +302,7 @@ function Test-TargetResource $GlobalAdminAccount ) - Write-Verbose -Message "Testing configuration of RetentionComplianceTag for $Name" + Write-Verbose -Message "Testing configuration of ComplianceTag for $Name" $CurrentValues = Get-TargetResource @PSBoundParameters Write-Verbose -Message "Current Values: $(Convert-O365DscHashtableToString -Hashtable $CurrentValues)" @@ -284,6 +310,15 @@ function Test-TargetResource $ValuesToCheck = $PSBoundParameters $ValuesToCheck.Remove('GlobalAdminAccount') | Out-Null + $ValuesToCheck.Remove("FilePlanProperty") | Out-Null + + $TestFilePlanProperties = Test-SCFilePlanProperties -CurrentProperty $CurrentValues ` + -DesiredProperty $PSBoundParameters + + if ($false -eq $TestFilePlanProperties) + { + return $false + } $TestResult = Test-Office365DSCParameterState -CurrentValues $CurrentValues ` -DesiredValues $PSBoundParameters ` @@ -310,7 +345,7 @@ function Export-TargetResource ) $result = Get-TargetResource @PSBoundParameters $result.GlobalAdminAccount = Resolve-Credentials -UserName "globaladmin" - $content = " SCRetentionComplianceTag " + (New-GUID).ToString() + "`r`n" + $content = " SCComplianceTag " + (New-GUID).ToString() + "`r`n" $content += " {`r`n" $currentDSCBlock = Get-DSCBlock -Params $result -ModulePath $PSScriptRoot $content += Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName "GlobalAdminAccount" @@ -318,4 +353,80 @@ function Export-TargetResource return $content } +function Get-SCFilePlanPropertyObject +{ + [CmdletBinding()] + [OutputType([PSCustomObject])] + param( + [Parameter()] + $Properties + ) + + if ($null -eq $Properties) + { + return $null + } + + $result = [PSCustomObject]@{ + Settings=@( + @{Key="FilePlanPropertyDepartment"; Value=$properties.FilePlanPropertyDepartment}, + @{Key="FilePlanPropertyCategory"; Value=$properties.FilePlanPropertyCategory}, + @{Key="FilePlanPropertySubcategory";Value=$properties.FilePlanPropertySubcategory}, + @{Key="FilePlanPropertyCitation"; Value=$properties.FilePlanPropertyCitation}, + @{Key="FilePlanPropertyReferenceId";Value=$properties.FilePlanPropertyReferenceId}, + @{Key="FilePlanPropertyAuthority"; Value=$properties.FilePlanPropertyAuthority} + )} + + return $result +} + +function Get-SCFilePlanProperty +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param( + [Parameter(Mandatory=$true)] + [System.String] + $Metadata + ) + + if ($null -eq $Metadata) + { + return $null + } + $JSONObject = ConvertFrom-JSON $Metadata + $result = @{ + FilePlanPropertyDepartment = $JSONObject.Settings["FilePlanPropertyDepartment"].Value + FilePlanPropertyCategory = $JSONObject.Settings["FilePlanPropertyCategory"].Value + FilePlanPropertySubcategory = $JSONObject.Settings["FilePlanPropertySubcategory"].Value + FilePlanPropertyCitation = $JSONObject.Settings["FilePlanPropertyCitation"].Value + FilePlanPropertyReferenceId = $JSONObject.Settings["FilePlanPropertyReferenceId"].Value + FilePlanPropertyAuthority = $JSONObject.Settings["FilePlanPropertyAuthority"].Value + } + + return $result +} + +function Test-SCFilePlanProperties +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param( + [Parameter(Mandatory = $true)] $CurrentProperty, + [Parameter(Mandatory = $true)] $DesiredProperty + ) + + if ($CurrentProperty.FilePlanPropertyDepartment -ne $DesiredProperty.FilePlanPropertyDepartment -or ` + $CurrentProperty.FilePlanPropertyCategory -ne $DesiredProperty.FilePlanPropertyCategory -or ` + $CurrentProperty.FilePlanPropertySubcategory -ne $DesiredProperty.FilePlanPropertySubcategory -or ` + $CurrentProperty.FilePlanPropertyCitation -ne $DesiredProperty.FilePlanPropertyCitation -or ` + $CurrentProperty.FilePlanPropertyReferenceId -ne $DesiredProperty.FilePlanPropertyReferenceId -or ` + $CurrentProperty.FilePlanPropertyAuthority -ne $DesiredProperty.FilePlanPropertyAuthority) + { + return $false + } + + return $true +} + Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Office365DSC/DSCResources/MSFT_SCRetentionComplianceTag/MSFT_SCRetentionComplianceTag.schema.mof b/Modules/Office365DSC/DSCResources/MSFT_SCComplianceTag/MSFT_SCComplianceTag.schema.mof similarity index 93% rename from Modules/Office365DSC/DSCResources/MSFT_SCRetentionComplianceTag/MSFT_SCRetentionComplianceTag.schema.mof rename to Modules/Office365DSC/DSCResources/MSFT_SCComplianceTag/MSFT_SCComplianceTag.schema.mof index f0f3050691..e67921b3f8 100644 --- a/Modules/Office365DSC/DSCResources/MSFT_SCRetentionComplianceTag/MSFT_SCRetentionComplianceTag.schema.mof +++ b/Modules/Office365DSC/DSCResources/MSFT_SCComplianceTag/MSFT_SCComplianceTag.schema.mof @@ -1,5 +1,5 @@ [ClassVersion("1.0.0")] -Class MSFT_FilePlanProperty +Class MSFT_SCFilePlanProperty { [Write, Description("File plan department. Can get list by running Get-FilePlanPropertyDepartment.")] String FilePlanPropertyDepartment; [Write, Description("File plan Authority. Can get list by running Get-FilePlanPropertyAuthority.")] String FilePlanPropertyAuthority; @@ -8,8 +8,8 @@ Class MSFT_FilePlanProperty [Write, Description("File plan reference id. Can get a list by running Get-FilePlanPropertyReferenceId.")] String FilePlanPropertyReferenceId; [Write, Description("File plan subcategory. Can get a list by running Get-FilePlanPropertySubCategory.")] String FilePlanPropertySubCategory; }; -[ClassVersion("1.0.0.0"), FriendlyName("SCRetentionComplianceTag")] -class MSFT_SCRetentionComplianceTag : OMI_BaseResource +[ClassVersion("1.0.0.0"), FriendlyName("SCComplianceTag")] +class MSFT_SCComplianceTag : OMI_BaseResource { [Key, Description("The Name parameter specifies the unique name of the complaiance tag.")] String Name; [Write, Description("Specify if this rule should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; @@ -18,7 +18,7 @@ class MSFT_SCRetentionComplianceTag : OMI_BaseResource [Write, Description("The IsRecordLabel parameter specifies whether the label is a record label.")] Boolean IsRecordLabel; [Write, Description("The Notes parameter specifies an optional note. If you specify a value that contains spaces, enclose the value in quotation marks, for example: 'This is a user note'")] String Notes; [Write, Description("Regulatory description")] Boolean Regulatory; - [Write, Description("The FilePlanProperty parameter specifies the file plan properties to include in the label."),EmbeddedInstance("MSFT_FilePlanProperty")] String FilePlanProperty; + [Write, Description("The FilePlanProperty parameter specifies the file plan properties to include in the label."),EmbeddedInstance("MSFT_SCFilePlanProperty")] String FilePlanProperty; [Write, Description("The ReviewerEmail parameter specifies the email address of a reviewer for Delete and KeepAndDelete retention actions. You can specify multiple email addresses separated by commas.")] String ReviewerEmail; [Write, Description("The RetentionDuration parameter specifies the hold duration for the retention rule. Valid values are: An integer - The hold duration in days, Unlimited - The content is held indefinitely.")] String RetentionDuration; [Write, Description("The RetentionAction parameter specifies the action for the label. Valid values are: Delete, Keep or KeepAndDelete."), ValueMap{"Delete","Keep","KeepAndDelete"}, Values{"Delete","Keep","KeepAndDelete"}] String RetentionAction; diff --git a/Modules/Office365DSC/DSCResources/MSFT_SCComplianceTag/readme.md b/Modules/Office365DSC/DSCResources/MSFT_SCComplianceTag/readme.md new file mode 100644 index 0000000000..b187259028 --- /dev/null +++ b/Modules/Office365DSC/DSCResources/MSFT_SCComplianceTag/readme.md @@ -0,0 +1,6 @@ +# SCComplianceTag + + +## Description + +This resource configures a Compliance Tag in Security and Compliance. diff --git a/Modules/Office365DSC/DSCResources/MSFT_SCRetentionComplianceTag/readme.md b/Modules/Office365DSC/DSCResources/MSFT_SCRetentionComplianceTag/readme.md deleted file mode 100644 index d286c347d4..0000000000 --- a/Modules/Office365DSC/DSCResources/MSFT_SCRetentionComplianceTag/readme.md +++ /dev/null @@ -1,6 +0,0 @@ -# SCRetentionComplianceTag - - -## Description - -This resource configures a Retention Compliance Rule in Security and Compliance. diff --git a/Modules/Office365DSC/Examples/Resources/SCRetentionComplianceTag/1-AddingNewRetentionComplianceTag.ps1 b/Modules/Office365DSC/Examples/Resources/SCComplianceTag/1-AddingNewComplianceTag.ps1 similarity index 100% rename from Modules/Office365DSC/Examples/Resources/SCRetentionComplianceTag/1-AddingNewRetentionComplianceTag.ps1 rename to Modules/Office365DSC/Examples/Resources/SCComplianceTag/1-AddingNewComplianceTag.ps1 diff --git a/Modules/Office365DSC/Modules/Office365DSCUtil.psm1 b/Modules/Office365DSC/Modules/Office365DSCUtil.psm1 index fc84191fce..0038885c65 100644 --- a/Modules/Office365DSC/Modules/Office365DSCUtil.psm1 +++ b/Modules/Office365DSC/Modules/Office365DSCUtil.psm1 @@ -1839,20 +1839,3 @@ function Get-SPOAdministrationUrl Write-Verbose -Message "SharePoint Online admin URL is $global:AdminUrl" return $global:AdminUrl } -function Get-SCFilePlanProperty -{ - [CmdletBinding()] - param( - [Parameter()] - $FilePlan - ) - - if ($null -eq $FilePlan) - { - return $null - } - - Write-Verbose -Message "FilePlan Property: $($FilePlan.FilePlanPropertyDepartment)" - - return $result -} diff --git a/Tests/9660-O365DSC-ErrorLog.log b/Tests/9660-O365DSC-ErrorLog.log deleted file mode 100644 index bb31909b0a..0000000000 Binary files a/Tests/9660-O365DSC-ErrorLog.log and /dev/null differ diff --git a/Tests/Unit/Office365DSC/Office365DSC.SCRetentionComplianceTag.Tests.ps1 b/Tests/Unit/Office365DSC/Office365DSC.SCComplianceTag.Tests.ps1 similarity index 73% rename from Tests/Unit/Office365DSC/Office365DSC.SCRetentionComplianceTag.Tests.ps1 rename to Tests/Unit/Office365DSC/Office365DSC.SCComplianceTag.Tests.ps1 index 5bf472e3f2..bdab09b548 100644 --- a/Tests/Unit/Office365DSC/Office365DSC.SCRetentionComplianceTag.Tests.ps1 +++ b/Tests/Unit/Office365DSC/Office365DSC.SCComplianceTag.Tests.ps1 @@ -12,7 +12,7 @@ Import-Module -Name (Join-Path -Path $PSScriptRoot ` -Resolve) $Global:DscHelper = New-O365DscUnitTestHelper -StubModule $CmdletModule ` - -DscResource "SCRetentionComplianceTag" + -DscResource "SCComplianceTag" Describe -Name $Global:DscHelper.DescribeHeader -Fixture { InModuleScope -ModuleName $Global:DscHelper.ModuleName -ScriptBlock { Invoke-Command -ScriptBlock $Global:DscHelper.InitializeScript -NoNewScope @@ -57,9 +57,9 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Comment = "This is a test Rule" RetentionAction = "Keep" RetentionDuration = "1025" - FilePlanProperty = (New-CimInstance -ClassName MSFT_FilePlanProperty -Property @{ + FilePlanProperty = (New-CimInstance -ClassName MSFT_SCFilePlanProperty -Property @{ FilePlanPropertyDepartment = "Legal" - }-ClientOnly) + } -ClientOnly) GlobalAdminAccount = $GlobalAdminAccount RetentionType = "ModificationAgeInDays" Ensure = "Present" @@ -84,13 +84,18 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Context -Name "Rule already exists" -Fixture { $testParams = @{ - Name = "TestRule" + Name = "TestRule" Comment = "This is a test Rule" RetentionAction = "Keep" RetentionDuration = "1025" - FilePlanProperty = (New-CimInstance -ClassName MSFT_FilePlanProperty -Property @{ - FilePlanPropertyDepartment = "Legal" - }-ClientOnly) + FilePlanProperty = (New-CimInstance -ClassName MSFT_SCFilePlanProperty -Property @{ + FilePlanPropertyDepartment = "DemoDept" + FilePlanPropertyCitation = "DemoCit" + FilePlanPropertyReferenceId = "DemoRef" + FilePlanPropertyAuthority = "DemoAuth" + FilePlanPropertyCategory = "DemoCat" + FilePlanPropertySubcategory = "DemoSub" + } -ClientOnly) GlobalAdminAccount = $GlobalAdminAccount RetentionType = "ModificationAgeInDays" Ensure = "Present" @@ -98,7 +103,18 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Mock -CommandName Get-ComplianceTag -MockWith { return @{ - Name = "TestRule" + Name = "TestRule" + Comment = "This is a test Rule" + RetentionAction = "Keep" + RetentionDuration = "1025" + FilePlanMetadata = '{"Settings":[ + {"Key":"FilePlanPropertyDepartment","Value":"DemoDept"}, + {"Key":"FilePlanPropertyCitation","Value":"DemoCit"}, + {"Key":"FilePlanPropertyReferenceId","Value":"DemoRef"}, + {"Key":"FilePlanPropertyAuthority","Value":"DemoAuth"}, + {"Key":"FilePlanPropertyCategory","Value":"DemoCat"}, + {"Key":"FilePlanPropertySubcategory","Value":"DemoSub"}]}' + RetentionType = "ModificationAgeInDays" } } @@ -106,7 +122,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Test-TargetResource @testParams | Should Be $true } - It 'Should recreate from the Set method' { + It 'Should update from the Set method' { Set-TargetResource @testParams } @@ -121,9 +137,9 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Name = "TestRule" Comment = "This is a test Rule" RetentionAction = "Keep" - FilePlanProperty = (New-CimInstance -ClassName MSFT_FilePlanProperty -Property @{ + FilePlanProperty = (New-CimInstance -ClassName MSFT_SCFilePlanProperty -Property @{ FilePlanPropertyDepartment = "Legal" - }-ClientOnly) + } -ClientOnly) RetentionDuration = "1025" GlobalAdminAccount = $GlobalAdminAccount RetentionType = "ModificationAgeInDays" diff --git a/Tests/Unit/Stubs/Office365.psm1 b/Tests/Unit/Stubs/Office365.psm1 index ea057844aa..4b28a679c0 100644 --- a/Tests/Unit/Stubs/Office365.psm1 +++ b/Tests/Unit/Stubs/Office365.psm1 @@ -12121,7 +12121,7 @@ function New-ComplianceTag $Comment, [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance] + [System.String] $FilePlanProperty, [Parameter()] @@ -12173,7 +12173,7 @@ function Set-ComplianceTag $Comment, [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance] + [System.String] $FilePlanProperty, [Parameter()]