DSCResources/cMsmqQueue/cMsmqQueue.psm1
<#
Author : Serge Nikalaichyk (https://www.linkedin.com/in/nikalaichyk) Version : 1.0.2 Date : 2015-10-15 #> function Get-TargetResource { [CmdletBinding()] [OutputType([Hashtable])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $Name ) begin { try { $Service = Get-Service -Name MSMQ -ErrorAction Stop if ($Service.Status -ne 'Running') { throw "Please ensure that the Message Queuing (MSMQ) service is running." } } catch { throw $_.Exception.Message } Initialize-cMsmqType } process { $cMsmqQueue = Get-cMsmqQueue -Name $Name -ErrorAction SilentlyContinue if ($cMsmqQueue) { Write-Verbose -Message "Queue '$Name' was found." $EnsureResult = 'Present' } else { Write-Verbose -Message "Queue '$Name' could not be found." $EnsureResult = 'Absent' } $ReturnValue = @{ Ensure = $EnsureResult Name = $Name Transactional = $cMsmqQueue.Transactional Authenticate = $cMsmqQueue.Authenticate Journaling = $cMsmqQueue.Journaling JournalQuota = $cMsmqQueue.JournalQuota Label = $cMsmqQueue.Label PrivacyLevel = $cMsmqQueue.PrivacyLevel QueueQuota = $cMsmqQueue.QueueQuota } return $ReturnValue } } function Test-TargetResource { [CmdletBinding()] [OutputType([Boolean])] param ( [Parameter(Mandatory = $false)] [ValidateSet('Absent', 'Present')] [String] $Ensure = 'Present', [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $Name, [Parameter(Mandatory = $false)] [Boolean] $Transactional = $false, [Parameter(Mandatory = $false)] [Boolean] $Authenticate = $false, [Parameter(Mandatory = $false)] [Boolean] $Journaling = $false, [Parameter(Mandatory = $false)] [UInt32] $JournalQuota = [UInt32]::MaxValue, [Parameter(Mandatory = $false)] [String] $Label = $null, [Parameter(Mandatory = $false)] [ValidateSet('None', 'Optional', 'Body')] [String] $PrivacyLevel = 'Optional', [Parameter(Mandatory = $false)] [UInt32] $QueueQuota = [UInt32]::MaxValue ) $PSBoundParameters.GetEnumerator() | ForEach-Object -Begin { $Width = $PSBoundParameters.Keys.Length | Sort-Object -Descending | Select-Object -First 1 } -Process { "{0,-$($Width)} : '{1}'" -f $_.Key, ($_.Value -join ', ') | Write-Verbose } $TargetResource = Get-TargetResource -Name $Name if ($Ensure -eq 'Absent') { if ($TargetResource.Ensure -eq 'Absent') { $InDesiredState = $true } else { $InDesiredState = $false } } else { if ($TargetResource.Ensure -eq 'Absent') { $InDesiredState = $false } else { $InDesiredState = $true if ($PSBoundParameters.ContainsKey('Transactional')) { if ($TargetResource.Transactional -ne $Transactional) { $InDesiredState = $false if ($TargetResource.Transactional -eq $true) { $CurrentQueueTypeString = 'transactional' } else { $CurrentQueueTypeString = 'non-transactional' } if ($Transactional -eq $true) { $DesiredQueueTypeString = 'transactional' } else { $DesiredQueueTypeString = 'non-transactional' } $ErrorMessage = "Queue '{0}' is {1} and cannot be converted to {2}." -f $Name, $CurrentQueueTypeString, $DesiredQueueTypeString throw $ErrorMessage } } $PSBoundParameters.GetEnumerator() | Where-Object {$_.Key -in @('Authenticate', 'Journaling', 'JournalQuota', 'Label', 'PrivacyLevel', 'QueueQuota')} | ForEach-Object { $PropertyName = $_.Key if ($TargetResource."$PropertyName" -cne $_.Value) { $InDesiredState = $false "Property '{0}': Current value '{1}'; Desired value: '{2}'." -f $PropertyName, $TargetResource."$PropertyName", $_.Value | Write-Verbose } } } } if ($InDesiredState -eq $true) { Write-Verbose -Message "The target resource is already in the desired state. No action is required." } else { Write-Verbose -Message "The target resource is not in the desired state." } return $InDesiredState } function Set-TargetResource { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $false)] [ValidateSet('Absent', 'Present')] [String] $Ensure = 'Present', [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $Name, [Parameter(Mandatory = $false)] [Boolean] $Transactional = $false, [Parameter(Mandatory = $false)] [Boolean] $Authenticate = $false, [Parameter(Mandatory = $false)] [Boolean] $Journaling = $false, [Parameter(Mandatory = $false)] [UInt32] $JournalQuota = [UInt32]::MaxValue, [Parameter(Mandatory = $false)] [String] $Label = $null, [Parameter(Mandatory = $false)] [ValidateSet('None', 'Optional', 'Body')] [String] $PrivacyLevel = 'Optional', [Parameter(Mandatory = $false)] [UInt32] $QueueQuota = [UInt32]::MaxValue ) if (-not $PSCmdlet.ShouldProcess($Name)) { return } $CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name if ($Ensure -eq 'Absent') { Write-Verbose -Message "Testing if the current user has the permission necessary to perform the operation." $CurrentUserPermission = Get-cMsmqQueuePermission -Name $Name -Principal $CurrentUser -ErrorAction SilentlyContinue $PermissionToTest = [System.Messaging.MessageQueueAccessRights]::DeleteQueue if (-not $CurrentUserPermission -or -not $CurrentUserPermission.HasFlag($PermissionToTest)) { "User '{0}' does not have the '{1}' permission on queue '{2}'." -f $CurrentUser, $PermissionToTest, $Name | Write-Verbose Reset-cMsmqQueueSecurity -Name $Name -Confirm:$false -Verbose:$VerbosePreference } $PSBoundParameters.GetEnumerator() | Where-Object {$_.Key -in (Get-Command -Name Remove-cMsmqQueue).Parameters.Keys} | ForEach-Object -Begin {$RemoveParameters = @{}} -Process {$RemoveParameters.Add($_.Key, $_.Value)} Remove-cMsmqQueue @RemoveParameters -Confirm:$false } else { $TargetResource = Get-TargetResource -Name $Name if ($TargetResource.Ensure -eq 'Absent') { $PSBoundParameters.GetEnumerator() | Where-Object {$_.Key -in (Get-Command -Name New-cMsmqQueue).Parameters.Keys} | ForEach-Object -Begin {$NewParameters = @{}} -Process {$NewParameters.Add($_.Key, $_.Value)} New-cMsmqQueue @NewParameters } else { Write-Verbose -Message "Testing if the current user has the permission necessary to perform the operation." $CurrentUserPermission = Get-cMsmqQueuePermission -Name $Name -Principal $CurrentUser -ErrorAction SilentlyContinue $PermissionToTest = [System.Messaging.MessageQueueAccessRights]::SetQueueProperties if (-not $CurrentUserPermission -or -not $CurrentUserPermission.HasFlag($PermissionToTest)) { "User '{0}' does not have the '{1}' permission on queue '{2}'." -f $CurrentUser, $PermissionToTest, $Name | Write-Verbose Reset-cMsmqQueueSecurity -Name $Name -Confirm:$false -Verbose:$VerbosePreference } $PSBoundParameters.GetEnumerator() | Where-Object {$_.Key -in (Get-Command -Name Set-cMsmqQueue).Parameters.Keys} | ForEach-Object -Begin {$SetParameters = @{}} -Process {$SetParameters.Add($_.Key, $_.Value)} Set-cMsmqQueue @SetParameters } } } Export-ModuleMember -Function Get-TargetResource, Set-TargetResource, Test-TargetResource #region Helper Functions function Initialize-cMsmqType { <# .SYNOPSIS Initializes custom and native MSMQ types. .DESCRIPTION The Initialize-cMsmqType function initializes custom and native MSMQ types. #> $DllFilePath = Split-Path -Path $PSScriptRoot -Parent | Split-Path -Parent | Join-Path -ChildPath 'cMsmq.dll' if ([AppDomain]::CurrentDomain.GetAssemblies().Location -notcontains $DllFilePath) { Add-Type -Path $DllFilePath -ErrorAction Stop } if ([AppDomain]::CurrentDomain.GetAssemblies().ManifestModule.Name -notcontains 'System.Messaging.dll') { Add-Type -AssemblyName System.Messaging -ErrorAction Stop } } Initialize-cMsmqType function Get-cMsmqQueue { <# .SYNOPSIS Gets the specified private MSMQ queue by its name. .DESCRIPTION The Get-cMsmqQueue function gets the specified private MSMQ queue by its name. .PARAMETER Name Specifies the name of the queue. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [String] $Name ) begin { Initialize-cMsmqType } process { $QueuePath = '.\private$\{0}' -f $Name if (-not [System.Messaging.MessageQueue]::Exists($QueuePath)) { Write-Error -Message "Queue '$Name' could not be found at the specified path: '$QueuePath'." return } $Queue = New-Object -TypeName System.Messaging.MessageQueue -ArgumentList $QueuePath $OutputObject = [PSCustomObject]@{ Name = $Name Path = $Queue.Path Transactional = $Queue.Transactional Authenticate = $Queue.Authenticate Journaling= $Queue.UseJournalQueue JournalQuota = [UInt32]$Queue.MaximumJournalSize Label = $Queue.Label PrivacyLevel = [String]$Queue.EncryptionRequired QueueQuota = [UInt32]$Queue.MaximumQueueSize } return $OutputObject } } function Get-cMsmqQueuePermission { <# .SYNOPSIS Gets the access rights of the specified principal on the specified private MSMQ queue. .DESCRIPTION The Get-cMsmqQueuePermission function gets the access rights that have been granted to the specified security principal on the specified private MSMQ queue. .PARAMETER Name Specifies the name of the queue. .PARAMETER Principal Specifies the identity of the principal. #> [CmdletBinding()] [OutputType([System.Messaging.MessageQueueAccessRights])] param ( [Parameter(Mandatory = $true)] [String] $Name, [Parameter(Mandatory = $true)] [String] $Principal ) begin { Initialize-cMsmqType } process { try { Write-Verbose -Message "Getting permissions for principal '$Principal' on queue '$Name'." $AccessMask = [cMsmq.Security]::GetAccessMask($Name, $Principal) $OutputObject = [System.Messaging.MessageQueueAccessRights]$AccessMask.value__ return $OutputObject } catch { Write-Error -Message $_.Exception.Message return } } } function New-cMsmqQueue { <# .SYNOPSIS Creates a new private MSMQ queue. .DESCRIPTION The New-cMsmqQueue function creates a new private MSMQ queue. .PARAMETER Name Specifies the name of the queue. .PARAMETER Transactional Specifies whether the queue is a transactional queue. .PARAMETER Authenticate Sets a value that indicates whether the queue accepts only authenticated messages. .PARAMETER Journaling Sets a value that indicates whether received messages are copied to the journal queue. .PARAMETER JournalQuota Sets the maximum size of the journal queue in KB. .PARAMETER Label Sets the queue description. .PARAMETER PrivacyLevel Sets the privacy level associated with the queue. .PARAMETER QueueQuota Sets the maximum size of the queue in KB. #> [CmdletBinding(ConfirmImpact = 'Medium', SupportsShouldProcess = $true)] param ( [Parameter( Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [String] $Name, [Parameter(Mandatory = $false)] [Boolean] $Transactional = $false, [Parameter(Mandatory = $false)] [Boolean] $Authenticate = $false, [Parameter(Mandatory = $false)] [Boolean] $Journaling = $false, [Parameter(Mandatory = $false)] [UInt32] $JournalQuota = [UInt32]::MaxValue, [Parameter(Mandatory = $false)] [String] $Label = $null, [Parameter(Mandatory = $false)] [ValidateSet('None', 'Optional', 'Body')] [String] $PrivacyLevel = 'Optional', [Parameter(Mandatory = $false)] [UInt32] $QueueQuota = [UInt32]::MaxValue ) begin { Initialize-cMsmqType $PropertyNames = @{ Authenticate = 'Authenticate' Journaling = 'UseJournalQueue' JournalQuota = 'MaximumJournalSize' Label = 'Label' PrivacyLevel = 'EncryptionRequired' QueueQuota = 'MaximumQueueSize' } } process { if (-not $PSCmdlet.ShouldProcess($Name, 'Create Queue')) { return } $QueuePath = '.\private$\{0}' -f $Name try { $Queue = [System.Messaging.MessageQueue]::Create($QueuePath, $Transactional) } catch { Write-Error -Message $_.Exception.Message return } $PSBoundParameters.GetEnumerator() | Where-Object {$_.Key -in $PropertyNames.Keys} | ForEach-Object { $PropertyName = $PropertyNames.Item($_.Key) if ($Queue."$PropertyName" -cne $_.Value) { "Setting property '{0}' to value '{1}'." -f $PropertyName, $_.Value | Write-Verbose $Queue."$PropertyName" = $_.Value } } } } function Remove-cMsmqQueue { <# .SYNOPSIS Removes the specified private MSMQ queue. .DESCRIPTION The Remove-cMsmqQueue function the specified private MSMQ queue. .PARAMETER Name Specifies the name of the queue. #> [CmdletBinding(ConfirmImpact = 'High', SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [String] $Name ) begin { Initialize-cMsmqType } process { if (-not $PSCmdlet.ShouldProcess($Name, 'Remove Queue')) { return } $QueuePath = '.\private$\{0}' -f $Name try { [Void][System.Messaging.MessageQueue]::Delete($QueuePath) } catch { Write-Error -Message $_.Exception.Message return } } } function Reset-cMsmqQueueSecurity { <# .SYNOPSIS Resets the security settings on the specified private MSMQ queue. .DESCRIPTION The Reset-cMsmqQueueSecurity function performs the following actions: - Grants ownership of the queue to the SYSTEM account (DSC runs as SYSTEM); - Resets the permission list to the operating system's default values. .PARAMETER Name Specifies the name of the queue. #> [CmdletBinding(ConfirmImpact = 'High', SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [String] $Name ) begin { Initialize-cMsmqType $DefaultSecurity = 'Security=010007801c0000002800000000000000140000000200080000000000' + '010100000000000512000000010500000000000515000000e611610036157811027bc60001020000' } process { if (-not $PSCmdlet.ShouldProcess($Name, 'Reset Queue Security')) { return } $QueuePath = '.\private$\{0}' -f $Name $CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name $QueueOwner = [cMsmq.Security]::GetOwner($Name) Write-Verbose -Message "Queue Owner : '$QueueOwner'" if ($CurrentUser -ne $QueueOwner) { Write-Verbose -Message "Taking ownership of queue '$Name'." $FilePath = Get-ChildItem -Path "$Env:SystemRoot\System32\msmq\storage\lqs" -Force | Select-String -Pattern "Label=private`$\$($Name)" -SimpleMatch | Select-Object -ExpandProperty Path if (-not $FilePath) { Write-Error -Message "Could not find a corresponding .INI file for queue '$Name'." return } (Get-Content -Path $FilePath) | ForEach-Object {$_ -replace '^Security=.+', $DefaultSecurity} | Set-Content -Path $FilePath } Write-Verbose -Message "Resetting permissions on queue '$Name'." $Queue = New-Object -TypeName System.Messaging.MessageQueue $Queue.Path = $QueuePath $Queue.ResetPermissions() } } function Set-cMsmqQueue { <# .SYNOPSIS Sets properties on the specified private MSMQ queue. .DESCRIPTION The Set-cMsmqQueue function sets properties on the specified private MSMQ queue. .PARAMETER Name Specifies the name of the queue. .PARAMETER Authenticate Sets a value that indicates whether the queue accepts only authenticated messages. .PARAMETER Journaling Sets a value that indicates whether received messages are copied to the journal queue. .PARAMETER JournalQuota Sets the maximum size of the journal queue in KB. .PARAMETER Label Sets the queue description. .PARAMETER PrivacyLevel Sets the privacy level associated with the queue. .PARAMETER QueueQuota Sets the maximum size of the queue in KB. #> [CmdletBinding(ConfirmImpact = 'Medium', SupportsShouldProcess = $true)] param ( [Parameter( Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [String] $Name, [Parameter(Mandatory = $false)] [Boolean] $Authenticate = $false, [Parameter(Mandatory = $false)] [Boolean] $Journaling = $false, [Parameter(Mandatory = $false)] [UInt32] $JournalQuota = [UInt32]::MaxValue, [Parameter(Mandatory = $false)] [String] $Label = $null, [Parameter(Mandatory = $false)] [ValidateSet('None', 'Optional', 'Body')] [String] $PrivacyLevel = 'Optional', [Parameter(Mandatory = $false)] [UInt32] $QueueQuota = [UInt32]::MaxValue ) begin { Initialize-cMsmqType $PropertyNames = @{ Authenticate = 'Authenticate' Journaling = 'UseJournalQueue' JournalQuota = 'MaximumJournalSize' Label = 'Label' PrivacyLevel = 'EncryptionRequired' QueueQuota = 'MaximumQueueSize' } } process { if (-not $PSCmdlet.ShouldProcess($Name, 'Set Queue')) { return } $QueuePath = '.\private$\{0}' -f $Name if (-not [System.Messaging.MessageQueue]::Exists($QueuePath)) { Write-Error -Message "Queue '$Name' could not be found at the specified path: '$QueuePath'." return } $Queue = New-Object -TypeName System.Messaging.MessageQueue -ArgumentList $QueuePath $PSBoundParameters.GetEnumerator() | Where-Object {$_.Key -in $PropertyNames.Keys} | ForEach-Object { $PropertyName = $PropertyNames.Item($_.Key) if ($Queue."$PropertyName" -ne $_.Value) { "Setting property '{0}' to value '{1}'." -f $PropertyName, $_.Value | Write-Verbose $Queue."$PropertyName" = $_.Value } } } } #endregion |