Search code examples
phplaravelamazon-web-servicesamazon-ec2laravel-10

Running a command on AWS EC2 instance using PHP SDK


I would like to run arbitrary commands on my EC2 instance using the PHP SDK, however, when i run the commands nothing seems to be happening.

My workflow is as follows:

  1. Run the command:
 return $this->awsSdk
            ->createSsm()
            ->sendCommand([
                'DocumentName' => 'AWS-RunShellScript',
                'Targets' => [
                    [
                        'Key' => 'InstanceIds',
                        'Values' => [$this->getInstance($instance)] //Returns a value such as i-012345345h30 as an example
                    ]
                ],
                'Commands' => $commands, //An array of commands, (For testing this is ['touch testing.txt']
                'Output' => 'text'
            ])->toArray();

Result of this is as follows:

array:2 [
  "Command" => array:26 [
    "CommandId" => "MY_COMMAND_ID"
    "DocumentName" => "AWS-RunShellScript"
    "DocumentVersion" => "$DEFAULT"
    "Comment" => ""
    "ExpiresAfter" => Aws\Api\DateTimeResult @1704816674^ {#910
      date: 2024-01-09 16:11:14.150 UTC (+00:00)
    }
    "Parameters" => []
    "InstanceIds" => []
    "Targets" => array:1 [
      0 => array:2 [
        "Key" => "InstanceIds"
        "Values" => array:1 [
          0 => "MY_INSTANCE_ID"
        ]
      ]
    ]
    "RequestedDateTime" => Aws\Api\DateTimeResult @1704809474^ {#913
      date: 2024-01-09 14:11:14.150 UTC (+00:00)
    }
    "Status" => "Pending"
    "StatusDetails" => "Pending"
    "OutputS3Region" => "eu-west-1"
    "OutputS3BucketName" => ""
    "OutputS3KeyPrefix" => ""
    "MaxConcurrency" => "50"
    "MaxErrors" => "0"
    "TargetCount" => 0
    "CompletedCount" => 0
    "ErrorCount" => 0
    "DeliveryTimedOutCount" => 0
    "ServiceRole" => ""
    "NotificationConfig" => array:3 [
      "NotificationArn" => ""
      "NotificationEvents" => []
      "NotificationType" => ""
    ]
    "CloudWatchOutputConfig" => array:2 [
      "CloudWatchLogGroupName" => ""
      "CloudWatchOutputEnabled" => false
    ]
    "TimeoutSeconds" => 3600
    "AlarmConfiguration" => array:2 [
      "IgnorePollAlarmFailure" => false
      "Alarms" => []
    ]
    "TriggeredAlarms" => []
  ]
  "@metadata" => array:4 [
    "statusCode" => 200
    "effectiveUri" => "https://ssm.eu-west-1.amazonaws.com"
    "headers" => array:6 [
      "server" => "Server"
      "date" => "Tue, 09 Jan 2024 14:11:14 GMT"
      "content-type" => "application/x-amz-json-1.1"
      "content-length" => "903"
      "connection" => "keep-alive"
      "x-amzn-requestid" => "SOME_ID"
    ]
    "transferStats" => array:1 [
      "http" => array:1 [
        0 => []
      ]
    ]
  ]
] // app/Console/Commands/GenerateSslCertificate.php:45
  1. Get the result of the command in a different call
return $this->awsSdk
            ->createSsm()
            ->getCommandInvocation([
                'CommandId' => $id,
                'InstanceId' => $this->getInstance($instance),
            ]);

And the result of this is:

Aws\Result^ {#892
  -data: array:18 [
    "CommandId" => "MY_COMMAND_ID"
    "InstanceId" => "MY_INSTANCE_ID"
    "Comment" => ""
    "DocumentName" => "AWS-RunShellScript"
    "DocumentVersion" => "$DEFAULT"
    "PluginName" => "aws:runShellScript"
    "ResponseCode" => 0
    "ExecutionStartDateTime" => "2024-01-09T14:11:14.371Z"
    "ExecutionElapsedTime" => "PT0.017S"
    "ExecutionEndDateTime" => "2024-01-09T14:11:14.371Z"
    "Status" => "Success"
    "StatusDetails" => "Success"
    "StandardOutputContent" => ""
    "StandardOutputUrl" => ""
    "StandardErrorContent" => ""
    "StandardErrorUrl" => ""
    "CloudWatchOutputConfig" => array:2 [
      "CloudWatchLogGroupName" => ""
      "CloudWatchOutputEnabled" => false
    ]
    "@metadata" => array:4 [
      "statusCode" => 200
      "effectiveUri" => "https://ssm.eu-west-1.amazonaws.com"
      "headers" => array:6 [
        "server" => "Server"
        "date" => "Tue, 09 Jan 2024 14:12:44 GMT"
        "content-type" => "application/x-amz-json-1.1"
        "content-length" => "582"
        "connection" => "keep-alive"
        "x-amzn-requestid" => "SOME_ID"
      ]
      "transferStats" => array:1 [
        "http" => array:1 [
          0 => []
        ]
      ]
    ]
  ]
  -monitoringEvents: []

However, when I log in to the instance, I cant find anywhere where the file testing.txt is created.

Can anyone point me in the right direction of how I can get this to work please?


*** UPDATE ***

After some digging, I have restructured the command to the following:

return $this->awsSdk
            ->createSsm()
            ->sendCommand([
                'InstanceIds' => ['MY INSTANCE ID'],
                'DocumentName' => 'AWS-RunShellScript',
                'Comment' => 'Run Script',
                'Parameters' => [
                    'Commands' => ['pwd'],
                ],
                'Output' => 'text'
            ])->toArray();

Its also worth noting that the equivalent CLI command seems to work without issue using the AWS-RunShellScript document:

aws ssm send-command \                                                                                                   
    --instance-ids "MY_INSTANCE_ID" \
    --document-name "AWS-RunShellScript" \
    --comment "IP config" \
    --parameters commands=ifconfig \
    --output text

Solution

  • Having scraped the AWS docs for some hours, I found my issue.

    After instantiating the SDK:

    $this->awsSdk = new Sdk([
                'region'      => 'eu-west-1',
                'credentials' => [
                    'key'    => 'YOUR_ACCESS_KEY',
                    'secret' => 'YOUR_SECRET_KEY'
                ]
            ]);
    

    I then created a function which allows me to send commands, like so:

    public function sendCommands(string $instance, array $commands)
        {
            return $this->awsSdk
                ->createSsm()
                ->sendCommand([
                    'InstanceIds' => [$this->getInstance($instance)],
                    'DocumentName' => 'AWS-RunShellScript',
                    'Comment' => 'Run Script',
                    'Parameters' => [
                        'commands' => $commands,
                    ],
                    'Output' => 'text'
                ])->toArray();
        }
    

    The only difference here is the lower case C in commands which seemed to fix my issue. However, I thought it best to include my whole solution.