Last active
April 19, 2024 12:11
-
-
Save ramonsmits/3af6623436a443eb28f8a54fdd7688cf to your computer and use it in GitHub Desktop.
Powershell script to move MSMQ messages between queues with support for transactions and partial sets based on size.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Moves messages between queues. | |
.DESCRIPTION | |
Moves messages between queues, move isn't really the right word as moving can only occur | |
between a queue and its sub-queues but its the Verb allowed by Powershell. | |
Is allows moving messages between queues of different transaction type. | |
It also supports to move messages in a single transaction. It does not prevent to send | |
non-transactional to a transactional queue in a transactional. This means that the | |
receives are not participating in the transaction and are lost if the transaction commit | |
would fail! | |
.PARAMETER SourcePath | |
The location of the source queue. | |
.PARAMETER DestinationPath | |
The location of the destination queue. | |
.PARAMETER Size | |
Moves a maximum number messages. | |
.EXAMPLE | |
# Move all messages | |
Move-MsmqMessages ".\PRIVATE$\queue-a" ".\PRIVATE$\queue-b" | |
.EXAMPLE | |
# Move all messages in a transaction, only safe if both queues are transactional! | |
Move-MsmqMessages ".\PRIVATE$\queue-a" ".\PRIVATE$\queue-b" -Transactional | |
.EXAMPLE | |
# Move 10 messages | |
Move-MsmqMessages ".\PRIVATE$\queue-a" ".\PRIVATE$\queue-b" -BatchSize 10 | |
.EXAMPLE | |
# Move 10 messages in a transaction, only safe if both queues are transactional! | |
Move-MsmqMessages ".\PRIVATE$\queue-a" ".\PRIVATE$\queue-b" -BatchSize 10 -Transactional | |
#> | |
Set-StrictMode -Version Latest | |
Add-Type -AssemblyName System.Messaging | |
Add-Type -AssemblyName System.Transactions | |
Function Move-MsmqMessages { | |
param( | |
[Parameter(Mandatory = $true)] | |
[ValidateNotNullOrEmpty()] | |
[string] $SourcePath, | |
[Parameter(Mandatory = $true)] | |
[ValidateNotNullOrEmpty()] | |
[string] $DestinationPath, | |
[switch] $Transactional, | |
[int] $Size | |
) | |
$sourceQueue = New-Object System.Messaging.MessageQueue -Argumentlist $sourcePath, [System.Messaging.QueueAccessMode]::Receive | |
$destinationQueue = New-Object System.Messaging.MessageQueue -Argumentlist $destinationPath, [System.Messaging.QueueAccessMode]::Send | |
$scope = New-Object System.Transactions.TransactionScope | |
$transactionType = If ($Transactional) { [System.Messaging.MessageQueueTransactionType]::Automatic } Else { [System.Messaging.MessageQueueTransactionType]::Single } | |
try { | |
$count = 0; | |
$msgEnum = $sourceQueue.GetMessageEnumerator2() | |
foreach ($item in $msgEnum) { | |
$count++ | |
if ($sourceQueue.Transactional) { | |
$msg = $sourceQueue.Receive($transactionType) # Not using `ReceiveById(id)` or `$sourceQueue.RemoveCurrent($transactionType)` | |
} | |
else { | |
$msg = $sourceQueue.Receive() # Not using `$item.RemoveCurrent() as that breaks the foreach "When you remove the current message, the cursor is moved to the next message". | |
} | |
if ($destinationQueue.Transactional) { | |
$destinationQueue.Send($msg, $transactionType) | |
} | |
else { | |
$destinationQueue.Send($msg) | |
} | |
if (($Size -gt 0) -and ($count -ge $Size)) { | |
break | |
} | |
} | |
if ($transactional) { | |
$scope.Complete(); | |
} | |
return $count; | |
} | |
finally { | |
$msgEnum.Dispose() | |
$scope.Dispose() | |
$sourceQueue.Dispose() | |
$destinationQueue.Dispose() | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Excellent! I was browsing and stumbled upon your fine script. It really saved me some hours of scripting. Thank you @ramonsmits!