{"id":133,"date":"2017-04-02T13:35:21","date_gmt":"2017-04-02T17:35:21","guid":{"rendered":"https:\/\/packetlost.com\/blog\/?p=133"},"modified":"2017-04-04T21:35:52","modified_gmt":"2017-04-05T01:35:52","slug":"using-sqs-queues-with-powershell","status":"publish","type":"post","link":"https:\/\/packetlost.com\/blog\/2017\/04\/02\/using-sqs-queues-with-powershell\/","title":{"rendered":"Using SQS Queues with PowerShell"},"content":{"rendered":"<p>I wanted to look at connecting two disparate systems for a recent project. The goal was to be able to enter information into one system and have information processed by another system. The systems have no direct authentication trusts between them but they are both running on Amazon Web Services EC2 platform. This was a perfect use for the decoupling nature of the <a href=\"http:\/\/docs.aws.amazon.com\/AWSSimpleQueueService\/latest\/SQSDeveloperGuide\/Welcome.html\">Amazon Simple Queue Service<\/a> and I wanted to come up with a proof of concept, which is outlined below.<\/p>\n<p>Before getting into any details, I want to make clear that this is not a best practice use case of SQS. For most uses of SQS there is a need to keep track of the messages being processed in some kind of permanent state such as a database. With a persistent data store containing the processed messages, the queue workers can more effectively process messages if messages are delivered one or more times. That being said lets go over this proof of concept.<\/p>\n<p>Assuming AWS keys with correct permissions are configured and the AWSPowerShell module is loaded, the below command will create a new SQS queue with PowerShell. The command returns the created queue url which will be stored in a variable $NewSQSQueueUrl for future use.<\/p>\n<pre class=\"lang:ps decode:true \" title=\"Create the SQS Queue\">$NewSQSQueueUrl = New-SQSQueue -QueueName ExampleBlogPostQueue -Region us-east-1<\/pre>\n<p>A quick peek at the SQS console to ensure the queue was created.<\/p>\n<p><a href=\"https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/NewQueue.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-134\" src=\"https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/NewQueue.png\" alt=\"\" width=\"989\" height=\"246\" srcset=\"https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/NewQueue.png 989w, https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/NewQueue-300x75.png 300w, https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/NewQueue-768x191.png 768w\" sizes=\"auto, (max-width: 989px) 100vw, 989px\" \/><\/a><\/p>\n<p>This next bit creates an array of strings which will serve as some example information to share between the systems. For this proof of concept I am sending example PowerShell parameters into the SQS queue.<\/p>\n<pre class=\"lang:ps decode:true \">$exampleparams = @(\"All The Things\",\"Another Example Parameter\")<\/pre>\n<p>I have written the POC functions which are also uploaded to <a href=\"https:\/\/github.com\/Packet-Lost\/PowerShellScripts\/tree\/master\/AWSFunctions\">my GitHub PowerShell repo<\/a> that get dot sourced. These functions put the information (example parameters) into the new SQS queue as <a href=\"http:\/\/docs.aws.amazon.com\/AWSSimpleQueueService\/latest\/SQSDeveloperGuide\/sqs-message-attributes.html\">message attributes<\/a> of the newly created SQS message.<\/p>\n<pre class=\"lang:ps decode:true\">. \"$env:userprofile\\Documents\\PowerShell\\WIP\\blog\\Start-SQSQueueProcessing.ps1\"\r\n. \"$env:userprofile\\Documents\\PowerShell\\WIP\\blog\\Send-PowerShellParameterToSQS.ps1\"\r\n\r\nSend-PowerShellParameterToSQS -Parameters $exampleparams -SQSQueueUrl $NewSQSQueueUrl -RequestType Enable<\/pre>\n<p>After running these functions the message ids are returned to the PowerShell host indicating the messages have been inserted into the SQS queue successfully.<a href=\"https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/MessagesUploaded.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-135\" src=\"https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/MessagesUploaded.png\" alt=\"\" width=\"1137\" height=\"112\" srcset=\"https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/MessagesUploaded.png 1137w, https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/MessagesUploaded-300x30.png 300w, https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/MessagesUploaded-768x76.png 768w, https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/MessagesUploaded-1024x101.png 1024w\" sizes=\"auto, (max-width: 1137px) 100vw, 1137px\" \/><\/a><\/p>\n<p>Below is the function that was dot sourced that did the uploading. You could customize this to fit your use case with some help from the <a href=\"http:\/\/docs.aws.amazon.com\/powershell\/latest\/reference\/items\/Send-SQSMessage.html\">AWS Send-SQSMessage cmdlet documentation<\/a>.<\/p>\n<pre class=\"lang:ps decode:true \" title=\"Send-PowerShellParameterToSQS\">function Send-PowerShellParameterToSQS\r\n{\r\n    [CmdletBinding()]\r\n    [Alias()]\r\n    [OutputType([int])]\r\n    Param\r\n    (\r\n        [Parameter(Mandatory=$true,\r\n                   ValueFromPipelineByPropertyName=$true,\r\n                   Position=0)]\r\n        [String[]]$Parameters,\r\n        [Parameter(Mandatory=$true,\r\n                   ValueFromPipelineByPropertyName=$true,\r\n                   Position=1)]\r\n        [String]$SQSQueueUrl,\r\n        [Parameter(Mandatory=$true,\r\n                   ValueFromPipelineByPropertyName=$true,\r\n                   Position=2)]\r\n        [ValidateSet(\"Enable\",\"Disable\")]\r\n        [String]$RequestType\r\n    )\r\n\r\n    Process\r\n    {\r\n        try\r\n        {\r\n\r\n            ForEach ($Parameter in $Parameters){\r\n                $messageParameter = New-Object Amazon.SQS.Model.MessageAttributeValue\r\n                $messageParameter.DataType = \"String\"\r\n                $messageParameter.StringValue = $Parameter\r\n\r\n                $messageAttributes = New-Object System.Collections.Hashtable\r\n                $messageAttributes.Add($RequestType, $messageParameter)\r\n\r\n                Send-SQSMessage -QueueUrl $SQSQueueUrl -MessageAttributes $messageAttributes -MessageBody \"Request generated by $($env:Username) at $(Get-Date -Format u)\"\r\n\r\n            }\r\n        }\r\n        catch {\r\n            Write-Error $_\r\n        }\r\n\r\n    }\r\n\r\n}<\/pre>\n<p>With messages being put into the queue, I need a function to pull down the messages and process them on the queue worker system (aka the SQS message receiver). My goal is to take different actions on the queue worker system based on the message attributes of the SQS messages pulled out of the queue. That function looks something like this.<\/p>\n<pre class=\"lang:ps decode:true\" title=\"Start-SQSQueueProccessing\">function Start-SQSQueueProccessing\r\n{\r\n    [CmdletBinding()]\r\n    [Alias()]\r\n    [OutputType([int])]\r\n    Param\r\n    (\r\n        [Parameter(Mandatory=$true,\r\n                   ValueFromPipelineByPropertyName=$true,\r\n                   Position=0)]\r\n        [String]$SQSQueueUrl\r\n    )\r\n\r\n    Process\r\n    {\r\n        try\r\n        {\r\n        Write-Verbose \"$(Get-Date -Format u) Polling for $SQSQueueUrl messages\"\r\n        $recmsg = Receive-SQSMessage -QueueUrl $SQSQueueUrl -MessageCount 10 -MessageAttributeName All\r\n        if ($recmsg) {\r\n            Write-Verbose \"$(Get-Date -Format u) ... Messages found in queue...starting to process them\"\r\n            $recmsg | ForEach-Object {\r\n                Write-Verbose \"$(Get-Date -Format u) ... Processing message $($_.MessageId) that was $($_.Body)\" \r\n                $_.MessageAttributes | ForEach-Object {\r\n                ###This is where you can take action depending on the Key value of the message attribute###\r\n                    if ($_.Keys -eq \"Enable\") { Write-Output \"We could do a Enable-Noun powershell cmdlet with the -parameter $($_.Enable.StringValue) here\" }\r\n                    elseif ($_.Keys -eq \"Disable\") { Write-Output \"We could do a Disable-Noun powershell cmdlet with the -parameter $($_.Disable.StringValue) here\" }\r\n                    else { Write-Warning \"Thats odd, the key of the message attribute was not what was expected so no action was taken\" }\r\n                    }\r\n                Write-Verbose \"$(Get-Date -Format u) Done processing $($_.MessageId) that was $($_.Body) attempting message deletion\"  \r\n                Remove-SQSMessage -QueueUrl $SQSQueueUrl -ReceiptHandle $_.ReceiptHandle -Force\r\n                }\r\n\r\n        }else{ Write-Verbose \"$(Get-Date -Format u) ... No messages were pulled from the queue...nothing to do right now\"}\r\n\r\n        }\r\n        catch {\r\n            Write-Error $_\r\n        }\r\n\r\n    }\r\n\r\n}<\/pre>\n<p>This function isn&#8217;t actually doing anything interesting with the messages other than generating some output to the PowerShell streams but this is a proof of concept after all :).<\/p>\n<h4>Considerations when using SQS<\/h4>\n<p>As SQS is designed to decouple distributed systems, SQS does not assume every message pulled from the queue has been processed successfully. Messages that are pulled from the queue are hidden from the queue until the message <a href=\"http:\/\/docs.aws.amazon.com\/AWSSimpleQueueService\/latest\/SQSDeveloperGuide\/sqs-visibility-timeout.html\">visibility timeout<\/a> period has passed. It is up to the queue workers to delete the messages from the queue after the message has been processed. This is why at the end of the function above, messages are deleted from the queue with the <a href=\"http:\/\/docs.aws.amazon.com\/powershell\/latest\/reference\/Index.html\">Remove-SQSMessage cmdlet<\/a>.<\/p>\n<p>After working with SQS a bit, I noticed that the behavior surrounding the delivery of messages sitting in the queue is a little unintuitive. For example, say there are 8 messages in a queue and I request for up to 10 messages to be received with <a href=\"http:\/\/docs.aws.amazon.com\/powershell\/latest\/reference\/items\/Receive-SQSMessage.html\">Receive-SQSMessage<\/a>. A logical assumption would be that all 8 messages are returned but that is rarely the case. After working with some messages in queues it becomes quite apparent that SQS will return a random number of messages. Additionally without using <a href=\"http:\/\/docs.aws.amazon.com\/AWSSimpleQueueService\/latest\/SQSDeveloperGuide\/FIFO-queues.html\">FIFO (First-In First-Out) queues<\/a>, the messages will often be delivered out of order.<\/p>\n<p>Another bit of a gotcha I ran into at first was that by default, Recieve-SQSMessage will not return any message attributes from SQS. The resulting <a href=\"http:\/\/docs.aws.amazon.com\/sdkfornet\/v3\/apidocs\/index.html?page=SQS\/TSQSMessage.html&amp;tocid=Amazon_SQS_Model_Message\">Amazon.SQS.Model.Message<\/a> object that was returned had blank MessageAttributeValues until I specified &#8220;-MessageAttributeName All&#8221; parameter.<\/p>\n<p>Hopefully the above considerations will shed some light on the way the function is written. I wrote it so that it could be run repeatedly from a parent polling script and that it could handle one more more message objects being returned from each poll of SQS.<\/p>\n<h4>Back to the functions<\/h4>\n<p>Finally, we get to the polling function portions of the script which could run on scheduled intervals via task scheduler. This function first checks a queue for the existence of messages using the <a href=\"http:\/\/docs.aws.amazon.com\/powershell\/latest\/reference\/items\/Get-SQSQueueAttribute.html\">Get-SQSQueueAttribute<\/a> cmdlet. If messages are found in the queue, it will invoke the Start-SQSQueueProcessing function referenced above to handle the messages. I make use PowerShell transcription to keep a log for now. If this ever moves out of proof of concept, logging could be improved quite a bit to make it cleaner.<\/p>\n<p>This is the POC Queue polling function.<\/p>\n<pre class=\"lang:ps decode:true\" title=\"Start-SQSQueuePolling\">Function Start-SQSQueuePolling{\r\n    Param\r\n    (\r\n        [Parameter(Mandatory=$true,\r\n                   ValueFromPipelineByPropertyName=$true,\r\n                   Position=0)]\r\n        [String]$QueueUrl\r\n    )\r\n\r\n$numofmsg = Get-SQSQueueAttribute -QueueUrl $QueueUrl -AttributeName ApproximateNumberOfMessages\r\n\r\n    if ($numofmsg.ApproximateNumberOfMessages -gt 0){\r\n\r\n            Write-Output \"Number of messages in the queue to process is approximately $($numofmsg.ApproximateNumberOfMessages)\"\r\n\r\n            While ($numofmsg.ApproximateNumberOfMessages -gt 0) {\r\n            Start-SQSQueueProccessing -SQSQueueUrl $QueueUrl -Verbose\r\n            Start-Sleep -Seconds 5\r\n            $numofmsg = Get-SQSQueueAttribute -QueueUrl $QueueUrl -AttributeName ApproximateNumberOfMessages\r\n            Write-Output \"Number of messages is $($numofmsg.ApproximateNumberOfMessages)\"\r\n            }\r\n        }else{\r\n            Write-Output \"No messages found in the queue to process\"\r\n        }\r\n    Write-Output \"Done processing SQS Messages in the Queue\"\r\n}<\/pre>\n<p>How I envision the script being called ala cron or task scheduler for regular execution would be something like&#8230;<\/p>\n<pre class=\"lang:ps decode:true \" title=\"Calling the polling function\">Import-Module AWSPowerShell\r\n. \"$env:userprofile\\Documents\\PowerShell\\WIP\\blog\\Start-SQSQueuePolling.ps1\"\r\nStart-Transcript -Path \"$env:userprofile\\Documents\\PowerShell\\WIP\\blog\\OurSQSPollingLog.txt\" -NoClobber -Append\r\nStart-SQSQueuePolling -QueueUrl https:\/\/sqs.us-east-1.amazonaws.com\/1234567890\/ExampleBlogPostQueue<\/pre>\n<p>What does it look like when ran you may be wondering?<\/p>\n<p><a href=\"https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/PollingAndProcessingTheQueue-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-139\" src=\"https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/PollingAndProcessingTheQueue-1.png\" alt=\"\" width=\"1245\" height=\"180\" srcset=\"https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/PollingAndProcessingTheQueue-1.png 1245w, https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/PollingAndProcessingTheQueue-1-300x43.png 300w, https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/PollingAndProcessingTheQueue-1-768x111.png 768w, https:\/\/packetlost.com\/blog\/wp-content\/uploads\/2017\/04\/PollingAndProcessingTheQueue-1-1024x148.png 1024w\" sizes=\"auto, (max-width: 1245px) 100vw, 1245px\" \/><\/a><\/p>\n<p>The PowerShell transcript output captures the same information. As you may have noticed, the body of the generated SQS messages contains information on who created the SQS message and when it was created which could help for audit trails.<\/p>\n<p>Thanks for following along and happy scripting!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I wanted to look at connecting two disparate systems for a recent project. The goal was to be able to enter information into one system and have information processed by another system. The systems have no direct authentication trusts between &hellip;<\/p>\n<p class=\"read-more\"><a href=\"https:\/\/packetlost.com\/blog\/2017\/04\/02\/using-sqs-queues-with-powershell\/\">Read more &raquo;<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,2,42],"tags":[7,3,43],"class_list":["post-133","post","type-post","status-publish","format-standard","hentry","category-amazon-web-services","category-powershell","category-sqs","tag-aws","tag-powershell","tag-sqs"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/packetlost.com\/blog\/wp-json\/wp\/v2\/posts\/133","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/packetlost.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/packetlost.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/packetlost.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/packetlost.com\/blog\/wp-json\/wp\/v2\/comments?post=133"}],"version-history":[{"count":12,"href":"https:\/\/packetlost.com\/blog\/wp-json\/wp\/v2\/posts\/133\/revisions"}],"predecessor-version":[{"id":150,"href":"https:\/\/packetlost.com\/blog\/wp-json\/wp\/v2\/posts\/133\/revisions\/150"}],"wp:attachment":[{"href":"https:\/\/packetlost.com\/blog\/wp-json\/wp\/v2\/media?parent=133"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/packetlost.com\/blog\/wp-json\/wp\/v2\/categories?post=133"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/packetlost.com\/blog\/wp-json\/wp\/v2\/tags?post=133"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}