skip to Main Content

I am trying to run a SSM command on more than 50 EC2 instances of my fleet. By using AWS boto3’s SSM client, I am running a specific command on my nodes. My code is given below. After running the code, an unexpected error is showing up.

# running ec2 instances
instances = client.describe_instances()
instance_ids = [inst["InstanceId"] for inst in instances] # might contain more than 50 instances

# run command
run_cmd_resp = ssm_client.send_command(
    Targets=[
        {"Key": "InstanceIds", "Values": inst_ids_all},
    ],
    DocumentName="AWS-RunShellScript",
    DocumentVersion="1",
    Parameters={
        "commands": ["#!/bin/bash", "ls -ltrh", "# some commands"]
    }
)

On executing this, getting below error

An error occurred (ValidationException) when calling the SendCommand operation: 1 validation error detected: Value '[...91 instance IDs...]' at 'targets.1.member.values' failed to satisfy constraint: Member must have length less than or equal to 50.

How do I run the SSM command my whole fleet?

2

Answers


  1. Chosen as BEST ANSWER

    Finally after @Rohan Kishibe's answer, I tried to implement below batched execution for the SSM runShellScript.

    import math
    ec2_ids_all = [...] # all instance IDs fetched by pagination.
    PG_START, PG_STOP = 0, 50
    PG_SIZE = 50
    PG_COUNT = math.ceil(len(ec2_ids_all) / PG_SIZE)
    
    for page in range(PG_COUNT):
        cmd = ssm.send_command(
            Targets=[{"Key": "InstanceIds", "Values": ec2_ids_all[PG_START:PG_STOP]}],
            DocumentVersion="AWS-RunShellScript",
            Parameters={"commands": ["ls -ltrh", "# other commands"]}
        }
        PG_START += PG_SIZE
        PG_STOP += PG_SIZE
    
    

    In above way, the total number of instance IDs will be distributed in batches and then executed accordingly. One can also save the Command IDs and batch instance IDs in a mapping for future usage.


  2. As shown in the error message and boto3 documentation (link), the number of instances in one send_command call is limited up to 50. To run the SSM command for all instances, splitting the original list into 50 each could be a solution.

    FYI: If your account has a fair amount of instances, describe_instances() can’t retrieve all instance info in one api call, so it would be better to check whether NextToken is in response.
    ref: How do you use "NextToken" in AWS API calls

    # running ec2 instances
    instances = client.describe_instances()
    instance_ids = [inst["InstanceId"] for inst in instances]
    while "NextToken" in instances:
        instances = client.describe_instances(NextToken=instances["NextToken"])
        instance_ids += [inst["InstanceId"] for inst in instances]
    
    # run command
    for i in range(0, len(instance_ids), 50):
        target_instances = instance_ids[i : i + 50]
        run_cmd_resp = ssm_client.send_command(
            Targets=[
                {"Key": "InstanceIds", "Values": inst_ids_all},
            ],
            DocumentName="AWS-RunShellScript",
            DocumentVersion="1",
            Parameters={
                "commands": ["#!/bin/bash", "ls -ltrh", "# some commands"]
            }
        )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search