0

We patch and reboot a large number of Windows instances via AWS SSM commands currently but there is an issue where the SSM agent doesn't always check in. My thought process was to speed things up I can run a while loop on each of our patch groups that pulls the uptime of each instance in the group. Once all instances had been up for say 2 minutes the next group will be rebooted. I can't seem to get past a simple issue with how to properly create a while loop to accomplish this though.

When running the below commands as a bash script I'm getting an error that mentions I'm trying to use an "invalid arithmetic operator" in the loop. I have tested by outputting the value of $upTime and it was '18'.

This seems like one of those small mess ups that I am just wasting way too much time on and someone will be able to quickly point out what I am doing wrong. If the output of $upTime is a number I assumed the less than in the loop would function. If I just hard set it to 18 it loops properly which makes me think it must be some weird formatting issue from the SSM command but that should be plaintext and from my tests there is not white space coming through.

#!/bin/bash

aws ec2 reboot-instances --instance-ids `aws ec2 describe-instances --filters "Name=tag:RebootGroup,Values=01" --query 'Reservations[].Instances[].InstanceId' --output text`

sleep 30

upTime=1

while [[ $upTime -le 120 ]]
do

ssmID=$(aws ssm send-command --document-name "AWS-RunPowerShellScript" --document-version "1" --targets '[{"Key":"tag:RebootGroup","Values":["01"]}]' --parameters '{"commands":["$wmi = Get-WmiObject -Class Win32_OperatingSystem ","$uptimeSeconds = ($wmi.ConvertToDateTime($wmi.LocalDateTime)-$wmi.ConvertToDateTime($wmi.LastBootUpTime) | select-object -expandproperty \"TotalSeconds\")","[int]$uptimeSeconds"],"workingDirectory":[""],"executionTimeout":["3600"]}' --timeout-seconds 600 --max-concurrency "50" --max-errors "0" --region us-west-2 --output text --query "Command.CommandId")

echo "Value of ssmID = $ssmID"

echo "Value of upTime before sleep = $upTime"

sleep 15

upTime=$(aws ssm list-command-invocations --command-id "$ssmID" --details --query "CommandInvocations[].CommandPlugins[].{Output:Output}" --output text)

echo "Value of upTime after command invocation = $upTime"

echo "Waiting for instance uptime of 2 minutes before continuing"
done
2
  • 2
    First, what is the exact error messsage you get? Second, the way you're using echo doesn't do a good job of showing nonprinting, whitespace, etc characters; try adding delimiters around the value, and using cat -vt to make weird characters visible. For example: echo "Value of upTime after command invocation = <$upTime>" | | LC_ALL=C cat -vt Commented Nov 14, 2020 at 0:41
  • @GordonDavisson don't have the full error message handy now that I applied fixes based on the findings from your response and Glenn's but it did mention "invalid arithmetic operator". You were both right in that me utilizing echo to ensure there were no weird characters or more specifically any carriage returns. After applying the suggestions the issue was fixed. Thanks for the assist! Commented Nov 16, 2020 at 21:39

1 Answer 1

2

I bet you have a stray carriage return in there:

$ upTime=$'42\r'
$ [[ $upTime -le 120 ]] && echo y || echo n
")syntax error: invalid arithmetic operator (error token is "
n

And because of the error, [[ returns non-zero and the while loop ends too soon.

Try this:

printf "Value of upTime after command invocation = %q\n" "$upTime"

If I'm right, you can remove the CR with a parameter expansion:

upTime=${upTime%$'\r'}

or with sed

upTime=$(aws ... | sed 's/\r$//')
1
  • Glenn, thanks mate. You were exactly correct. I will definitely keep this in my arsenal in the future when it comes to validating variable values in the future. The tip for removing CRs from the variable worked like a charm as well. Commented Nov 16, 2020 at 21:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .