Skip to content

zAbuQasem/ssm-run-command

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

ssm-run-command

CI Lint License: MIT

Run shell commands on AWS EC2 instances via SSM — no SSH required. Supports OIDC, static credentials, multi-line commands, and tag-based targeting.

Credits: Based on the original work by Donghyun Peter Kim (peterkimzz). Rewritten with AWS SDK v3, Node 24, OIDC, and tag targeting.

Contents

Requirements

  1. GitHub OIDC configured in AWS. Follow the GitHub guide or the AWS IAM OIDC provider docs. Static credentials also work if you prefer.
  2. IAM permissions on the role or user (minimum policy below).
  3. EC2 instance role with AmazonSSMManagedInstanceCore attached so the SSM agent can register.

Minimum IAM policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "ssm:SendCommand",
      "Resource": [
        "arn:aws:ec2:*:*:instance/*",
        "arn:aws:ssm:*:*:document/AWS-RunShellScript"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "ssm:GetCommandInvocation",
        "ssm:ListCommandInvocations"
      ],
      "Resource": "*"
    }
  ]
}

Need broader SSM access (Parameter Store, Session Manager, Patch Manager)? See AmazonSSMFullAccess — it grants significantly more than this action requires.

Quick start

With OIDC (recommended)

name: Deploy via SSM

on:
  push:
    branches: [main]

concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: ubuntu-latest

    permissions:          # job-level, not workflow-level
      id-token: write
      contents: read

    steps:
      # No checkout needed — the command runs on the EC2 instance, not this runner.

      - name: Configure AWS credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0
        with:
          role-to-assume: arn:aws:iam::123456789012:role/my-deploy-role
          aws-region: us-east-1

      - name: Run commands on EC2
        id: ssm
        uses: zAbuQasem/ssm-run-command@v2
        with:
          aws-region: us-east-1
          instance-ids: ${{ secrets.INSTANCE_ID }}
          working-directory: /home/ubuntu/app
          wait-for-output: true
          comment: Deploy ${{ github.sha }}
          command: |
            git pull origin main
            npm install --production
            pm2 restart all

      - run: echo "${{ steps.ssm.outputs.output }}"

For stricter supply-chain security, pin to a commit SHA instead of @v2 (see examples/ for SHA-pinned, production-ready workflows).

Examples

Ready-to-use workflow files are in the examples/ directory:

File Description
oidc-deploy.yml Standard OIDC deploy — no long-lived credentials
tag-targeting.yml Target instances by EC2 tag instead of instance IDs
multi-instance.yml Dispatch to multiple instances in parallel
windows-powershell.yml Run PowerShell on Windows EC2 targets
static-credentials.yml Use IAM access keys instead of OIDC

Targeting instances

You must provide exactly one of instance-ids or targets — not both.

By instance ID

Newline-separated, up to 50 IDs per call.

with:
  instance-ids: |
    i-0b1f8b18a1d450000
    i-0b1f8b18a1d450001

By tags (or resource groups)

targets follows the AWS CLI --targets format: Key=<key>,Values=<v1>[,<v2>...]. One target per line, up to 5 targets per call. When multiple targets are given, an instance must match all of them.

Valid keys:

  • tag:<tag-name> — filter by EC2 tag
  • InstanceIds — equivalent to instance-ids but via the targets API
  • resource-groups:Name — filter by AWS Resource Group name
  • resource-groups:ResourceTypeFilters — filter by resource type within a group

Single tag:

with:
  aws-region: us-east-1
  targets: |
    Key=tag:env,Values=${{ inputs.server }}
  command: cd /home/ubuntu/ga-pl && git pull origin main
  comment: Deploy ${{ github.sha }} from GitHub Actions

Multiple tags (AND):

with:
  targets: |
    Key=tag:env,Values=prod
    Key=tag:role,Values=web,api

Running on Windows targets

Set document-name to AWS-RunPowerShellScript and pass PowerShell in command:

- uses: zAbuQasem/ssm-run-command@10be2ff3863ef31e5459092c0be8538fbc0907f8 # v2.0.0
  with:
    aws-region: us-east-1
    instance-ids: ${{ secrets.WINDOWS_INSTANCE_ID }}
    document-name: AWS-RunPowerShellScript
    working-directory: C:\inetpub\wwwroot\app
    command: |
      git pull origin main
      iisreset

Custom SSM documents also work — pass either the document name or its full ARN (e.g. arn:aws:ssm:us-east-1:123456789012:document/MyDeployDoc). The document must accept commands and workingDirectory parameters.

Inputs

Input Required Default Description
aws-region Yes AWS region (e.g. us-east-1).
instance-ids One of Newline-separated EC2 instance IDs (max 50). Mutually exclusive with targets.
targets One of Newline-separated Key=<k>,Values=<v1>[,<v2>...] lines (max 5). See Targeting instances. Mutually exclusive with instance-ids.
document-name No AWS-RunShellScript SSM document to execute. Use AWS-RunPowerShellScript for Windows, or provide a custom document name or ARN. The document must accept commands and workingDirectory parameters.
command No echo $(date) >> logs.txt Shell command(s). Each non-empty line runs as a separate step.
working-directory No /home/ubuntu Absolute path where commands execute.
comment No Executed by Github Actions Comment on the SSM Run Command entry (max 100 chars).
wait-for-output No false Wait for the command to finish and stream stdout/stderr into the Actions log. Sets the output action output.
wait-timeout No 60 Seconds to wait before giving up (1–3600). Only relevant when wait-for-output is true.
aws-access-key-id No IAM access key. Not needed when using OIDC.
aws-secret-access-key No IAM secret key. Not needed when using OIDC.
aws-session-token No STS session token for temporary credentials.

Outputs

Output Description
command-id SSM Run Command UUID. Use with aws ssm get-command-invocation --command-id <id> --instance-id <id> to inspect status and output.
output Combined stdout from all targeted instances. Only set when wait-for-output: true. SSM truncates per-invocation content at 24 000 bytes.

Viewing command output

Set wait-for-output: true to have the action poll until the command finishes and stream the results into the Actions log:

- name: Deploy
  id: deploy
  uses: zAbuQasem/ssm-run-command@10be2ff3863ef31e5459092c0be8538fbc0907f8 # v2.0.0
  with:
    aws-region: us-east-1
    instance-ids: ${{ secrets.INSTANCE_ID }}
    wait-for-output: true
    wait-timeout: 120
    command: |
      cd /home/ubuntu/app
      git pull origin main
      npm install --production
      pm2 restart all

- name: Print output
  run: echo "${{ steps.deploy.outputs.output }}"

Each instance's stdout and stderr appear in a collapsible group labelled with the instance ID and final status. The action fails if any instance exits with a non-success status or if the timeout is reached before all instances complete.

Note: When using wait-for-output: true with targets (tag-based), the ssm:ListCommandInvocations permission is also required to discover which instances were matched. See the IAM policy below.

Troubleshooting

AccessDeniedException — the IAM principal lacks ssm:SendCommand. Attach the minimum policy.

InvalidInstanceId: null — the EC2 instance has no AmazonSSMManagedInstanceCore role, or the SSM agent isn't running / reachable.

TargetNotConnected — the instance is tagged/matched correctly but offline from the SSM service. Check the agent and the instance's egress to the SSM endpoints.

Either 'instance-ids' or 'targets' must be provided — you passed neither (or both). Pick exactly one.

About

A GitHub Action to execute commands on AWS EC2 instances via SSM

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Contributors