Skip to content

How do I change the capacity mode for multiple Amazon DynamoDB tables at the same time?

12 minute read
0

I want to change the capacity mode for multiple Amazon DynamoDB tables at the same time.

Short description

When you change the capacity mode of multiple DynamoDB tables, you must specify either provisioned capacity mode or on-demand capacity mode. Before you change the capacity mode, see Considerations when switching capacity modes in DynamoDB.

To change the capacity mode of multiple DynamoDB tables at the same time, use one of the following methods:

  • AWS Command Line Interface (AWS CLI)
  • AWS CloudFormation
  • Python

Resolution

Best practices

When you change the capacity mode of multiple DynamoDB tables, use the following best practices:

  • Before you initiate the change, configure the appropriate AWS CLI credentials.
  • Make sure that you have appropriate AWS Identity and Access Management (IAM) permissions.
  • Deploy your changes to a non-production environment first.
  • Allow several minutes for each table to complete the switch.
  • Switch capacity modes only once every 24 hours for each table.
  • Analyze usage patterns to select the appropriate capacity mode, and adjust for AWS Regional requirements.
  • Monitor costs after the switch to make sure that you have the appropriate provisioned capacity.

AWS CLI

Note: If you receive errors when you run AWS Command Line Interface (AWS CLI) commands, then see Troubleshooting errors for the AWS CLI. Also, make sure that you're using the most recent AWS CLI version.

Provisioned mode

To use the AWS CLI to change the capacity mode of multiple DynamoDB tables to provisioned mode, complete the following steps:

  1. Open your text editor, and enter the following code to create a new shell script:

    #!/bin/bash
    
    # Set the AWS region
    AWS_REGION=[REGION] # Change this to your desired region
    
    # OPTION1: List of table names you want to switch
    TABLES=("table1" "table2" "table3")
    
    # OPTION2: Get all table names in the account
    TABLES=$(aws dynamodb list-tables —region $AWS_REGION —query 'TableNames[]' —output text)
    
    # Default provisioned capacity units
    READ_CAPACITY=READ_CAPACITY_VALUE
    WRITE_CAPACITY=WRITE_CAPACITY_VALUE
    
    echo "Using AWS Region: $AWS_REGION"
    
    for TABLE_NAME in $TABLES
    do
    # Check current billing mode
    CURRENT_MODE=$(aws dynamodb describe-table —region $AWS_REGION —table-name $TABLE_NAME —query 'Table.BillingModeSummary.BillingMode' —output text)
    
    if [ "$CURRENT_MODE" = "PAY_PER_REQUEST" ]; then
    echo "Processing table: $TABLE_NAME"
    
    # Get GSI configurations
    GSI_CONFIG=""
    GSI_LIST=$(aws dynamodb describe-table —region $AWS_REGION —table-name $TABLE_NAME —query 'Table.GlobalSecondaryIndexes[*].IndexName' —output text)
    
    if [ ! -z "$GSI_LIST" ]; then
    echo "Found GSIs: $GSI_LIST"
    
    # Build GSI provisioned throughput configuration
    GSI_CONFIG="—global-secondary-index-updates“
    for GSI_NAME in $GSI_LIST
    do
    if [ -z "$FIRST_GSI" ]; then
    GSI_CONFIG="$GSI_CONFIG [{\"Update\":{\"IndexName\":\"$GSI_NAME\",\"ProvisionedThroughput\":{\"ReadCapacityUnits\":$READ_CAPACITY,\"WriteCapacityUnits\":$WRITE_CAPACITY}}}"
    FIRST_GSI="false"
    else
    GSI_CONFIG="$GSI_CONFIG,{\"Update\":{\"IndexName\":\"$GSI_NAME\",\"ProvisionedThroughput\":{\"ReadCapacityUnits\":$READ_CAPACITY,\"WriteCapacityUnits\":$WRITE_CAPACITY}}}"
    fi
    done
    GSI_CONFIG="$GSI_CONFIG]"
    fi
    
    # Update table and GSIs
    if [ ! -z "$GSI_CONFIG" ]; then
    echo "Updating table and GSIs..."
    aws dynamodb update-table \
    --region $AWS_REGION \
    --table-name $TABLE_NAME \
    --billing-mode PROVISIONED \
    --provisioned-throughput ReadCapacityUnits=$READ_CAPACITY,WriteCapacityUnits=$WRITE_CAPACITY \
    $GSI_CONFIG
    else
    echo "Updating table (no GSIs)..."
    aws dynamodb update-table \
    --region $AWS_REGION \
    --table-name $TABLE_NAME \
    --billing-mode PROVISIONED \
    --provisioned-throughput ReadCapacityUnits=$READ_CAPACITY,WriteCapacityUnits=$WRITE_CAPACITY
    fi
    
    echo "Request submitted for $TABLE_NAME"
    else
    echo "Skipping $TABLE_NAME - already in PROVISIONED mode"
    fi
    
    # Reset GSI tracking for next table
    FIRST_GSI=""
    echo "----------------------------------------"
    
    done

    To change the capacity mode of all DynamoDB tables to on-demand mode, remove the following section of code:

    #OPTION1: List of table names you want to switch  
    TABLES=("table1" "table2" "table3")

    To change the capacity mode of specific DynamoDB tables to on-demand mode, replace "table1" "table2" "table3" with your table names. Then, remove the following section of code:

    #OPTION2: Get all table names in the account  
    TABLES=$(aws dynamodb list-tables —query 'TableNames[]' —output text)

    Note: Replace READ_CAPACITY_VALUE and WRITE_CAPACITY_VALUE with your read and write capacity values.

  2. Save the file with the name, switch-all-tables-with-gsi-to-provisioned.sh.

  3. To make the file executable, open the terminal and run the following command:

    chmod +x switch-all-tables-with-gsi-to-provisioned.sh
  4. To run the shell script in the terminal, run the following command:

    ./switch-all-tables-with-gsi-to-provisioned.sh

On-demand mode

To use the AWS CLI to change the capacity mode of multiple DynamoDB tables to on-demand mode, complete the following steps:

  1. Open your text editor, and then enter the following code to create a new shell script:

    #!/bin/bash
    
    # Set the AWS region
    AWS_REGION=[REGION] # Change this to your desired region
    
    # OPTION1: List of table names you want to switch
    TABLES=("table1" "table2" "table3")
    
    # OPTION2: Get all table names in the account
    #TABLES=$(aws dynamodb list-tables --region $AWS_REGION --query 'TableNames[]' --output text)
    
    for TABLE_NAME in $TABLES
    do
        # Check current billing mode
        CURRENT_MODE=$(aws dynamodb describe-table --region $AWS_REGION --table-name $TABLE_NAME --query 'Table.BillingModeSummary.BillingMode' --output text)
        
        if [ "$CURRENT_MODE" = "PROVISIONED" ]; then
            echo "Processing table: $TABLE_NAME"
            # Check if table has any GSIs
            GSI_LIST=$(aws dynamodb describe-table --region $AWS_REGION --table-name $TABLE_NAME --query 'Table.GlobalSecondaryIndexes[*].IndexName' --output text)
            
            if [ ! -z "$GSI_LIST" ]; then
                echo "Table has GSIs: $GSI_LIST"
                echo "Note: GSIs will automatically switch to On-Demand with the table"
            fi
    
            # Update table to On-Demand
            echo "Switching $TABLE_NAME to PAY_PER_REQUEST mode..."
            aws dynamodb update-table \
                --region $AWS_REGION \
                --table-name $TABLE_NAME \
                --billing-mode PAY_PER_REQUEST
            
            echo "Request submitted for $TABLE_NAME"
        else
            echo "Skipping $TABLE_NAME - already in PAY_PER_REQUEST mode"
        fi
        echo "----------------------------------------"
    done

    To change the capacity mode of all DynamoDB tables to on-demand mode, remove the following section of code:

    #OPTION1: List of table names you want to switch  
    TABLES=("table1" "table2" "table3")

    To change the capacity mode of specific DynamoDB tables to on-demand mode, replace "table1" "table2" "table3" with your table names. Then, remove the following section of code:

    `#OPTION2: Get all table names in the account`  
    TABLES=$(aws dynamodb list-tables —region $AWS\_REGION —query 'TableNames\[\]' —output text)

    Note: Replace REGION with your Region. Use the Region's code, such as us-east-1.

  2. To make the file executable, open the terminal and run the command:

    chmod +x switch-all-tables-with-gsi-to-ondemand.sh
  3. To run the shell script in the terminal, run the command:

    ./switch-all-tables-with-gsi-to-ondemand.sh

CloudFormation

Use the following best practices:

  • Set AWS Lambda templates to use the Python 3.9 runtime.
  • Monitor Amazon CloudWatch Logs to track the Lambda function's progress.

Note: Before you start, configure your AWS credentials. Run the following configure AWS CLI command:

aws configure

Provisioned mode

To use CloudFormation to change the capacity mode of multiple DynamoDB tables to provisioned mode, complete the following steps:

  1. Open your text editor and enter the following code to create a new YAML file:

    AWSTemplateFormatVersion: '2010-09-09'
    Description: 'Switch specific DynamoDB tables from On-Demand to Provisioned capacity mode'
    
    Parameters:
      ReadCapacityUnits:
        Type: Number
        Default: 5
        Description: Read Capacity Units for tables and GSIs
      
      WriteCapacityUnits:
        Type: Number
        Default: 5
        Description: Write Capacity Units for tables and GSIs
      
      TableNames:
        Type: CommaDelimitedList
        Description: Comma-separated list of DynamoDB table names to update
    
    Resources:
      DynamoDBTableUpdates:
        Type: Custom::DynamoDBTableUpdates
        Properties:
          ServiceToken: !GetAtt UpdateTablesFunction.Arn
          ReadCapacityUnits: !Ref ReadCapacityUnits
          WriteCapacityUnits: !Ref WriteCapacityUnits
          TableNames: !Ref TableNames
    
      UpdateTablesFunction:
        Type: AWS::Lambda::Function
        Properties:
          Runtime: python3.9
          Handler: index.handler
          Role: !GetAtt LambdaExecutionRole.Arn
          Code:
            ZipFile: |
              import boto3
              import cfnresponse
              
              def handler(event, context):
                  try:
                      if event['RequestType'] in ['Create', 'Update']:
                          dynamodb = boto3.client('dynamodb')
                          
                          # Get parameters
                          read_capacity = event['ResourceProperties']['ReadCapacityUnits']
                          write_capacity = event['ResourceProperties']['WriteCapacityUnits']
                          table_names = event['ResourceProperties']['TableNames']
                          
                          for table_name in table_names:
                              try:
                                  # Get table details
                                  table = dynamodb.describe_table(TableName=table_name)['Table']
                                  current_mode = table.get('BillingModeSummary', {}).get('BillingMode', '')
                                  
                                  if current_mode == 'PAY_PER_REQUEST':
                                      # Prepare GSI updates if any
                                      gsi_updates = []
                                      if 'GlobalSecondaryIndexes' in table:
                                          for gsi in table['GlobalSecondaryIndexes']:
                                              gsi_updates.append({
                                                  'Update': {
                                                      'IndexName': gsi['IndexName'],
                                                      'ProvisionedThroughput': {
                                                          'ReadCapacityUnits': int(read_capacity),
                                                          'WriteCapacityUnits': int(write_capacity)
                                                      }
                                                  }
                                              })
                                      
                                      # Update table
                                      update_params = {
                                          'TableName': table_name,
                                          'BillingMode': 'PROVISIONED',
                                          'ProvisionedThroughput': {
                                              'ReadCapacityUnits': int(read_capacity),
                                              'WriteCapacityUnits': int(write_capacity)
                                          }
                                      }
                                      
                                      if gsi_updates:
                                          update_params['GlobalSecondaryIndexUpdates'] = gsi_updates
                                          
                                      dynamodb.update_table(**update_params)
                                      print(f"Switching {table_name} to PROVISIONED mode")
                                  else:
                                      print(f"Table {table_name} is not in PAY_PER_REQUEST mode. Skipping.")
                                  
                              except Exception as e:
                                  print(f"Error processing table {table_name}: {str(e)}")
                                  continue
                                  
                          cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
                      else:
                          cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
                          
                  except Exception as e:
                      print(f"Error: {str(e)}")
                      cfnresponse.send(event, context, cfnresponse.FAILED, {})
    
      LambdaExecutionRole:
        Type: AWS::IAM::Role
        Properties:
          AssumeRolePolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Principal:
                  Service: lambda.amazonaws.com
                Action: sts:AssumeRole
          ManagedPolicyArns:
            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
          Policies:
            - PolicyName: DynamoDBAccess
              PolicyDocument:
                Version: '2012-10-17'
                Statement:
                  - Effect: Allow
                    Action:
                      - dynamodb:DescribeTable
                      - dynamodb:UpdateTable
                    Resource: '*'
  2. Save the file with the name, switch-to-provisioned.yaml.

  3. Run the following create-stack AWS CLI command:

    # For Provisioned mode
    aws cloudformation create-stack \
      --stack-name switch-to-provisioned \
      --template-body file://switch-to-provisioned.yaml \
      --capabilities CAPABILITY_IAM \
      --region [REGION] \ 
      --parameters  ParameterKey=TableNames,ParameterValue="Table1,Table2,Table3" \ 
                  ParameterKey=ReadCapacityUnits,ParameterValue=[RCU_VALUE] \
                  ParameterKey=WriteCapacityUnits,ParameterValue=[WCU_VALUE]

    Note: Replace "Table1,Table2,Table3" with your table names, RCU_VALUE and WCU_VALUE with your RCU and WCU values, and REGION with your Region, such as us-east-1.

On-demand mode

To use CloudFormation to change the capacity mode of multiple DynamoDB tables to on-demand mode, complete the following steps:

  1. Open your text editor and enter the following code to create a new YAML file:

    AWSTemplateFormatVersion: '2010-09-09'
    Description: 'Switch specific DynamoDB tables from Provisioned to On-Demand capacity mode'
    
    Parameters:
      TableNames:
        Type: CommaDelimitedList
        Description: Comma-separated list of DynamoDB table names to update
    
    Resources:
      DynamoDBTableUpdates:
        Type: Custom::DynamoDBTableUpdates
        Properties:
          ServiceToken: !GetAtt UpdateTablesFunction.Arn
          TableNames: !Ref TableNames
    
      UpdateTablesFunction:
        Type: AWS::Lambda::Function
        Properties:
          Runtime: python3.9
          Handler: index.handler
          Role: !GetAtt LambdaExecutionRole.Arn
          Code:
            ZipFile: |
              import boto3
              import cfnresponse
              
              def handler(event, context):
                  try:
                      if event['RequestType'] in ['Create', 'Update']:
                          dynamodb = boto3.client('dynamodb')
                          
                          # Get table names from the event
                          table_names = event['ResourceProperties']['TableNames']
                          
                          for table_name in table_names:
                              try:
                                  # Get table details
                                  table = dynamodb.describe_table(TableName=table_name)['Table']
                                  current_mode = table.get('BillingModeSummary', {}).get('BillingMode', '')
                                  
                                  if current_mode == 'PROVISIONED':
                                      # Update table to On-Demand
                                      dynamodb.update_table(
                                          TableName=table_name,
                                          BillingMode='PAY_PER_REQUEST'
                                      )
                                      print(f"Switching {table_name} to PAY_PER_REQUEST mode")
                                  else:
                                      print(f"Table {table_name} is not in PROVISIONED mode. Skipping.")
                                  
                              except Exception as e:
                                  print(f"Error processing table {table_name}: {str(e)}")
                                  continue
                                  
                          cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
                      else:
                          cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
                          
                  except Exception as e:
                      print(f"Error: {str(e)}")
                      cfnresponse.send(event, context, cfnresponse.FAILED, {})
    
      LambdaExecutionRole:
        Type: AWS::IAM::Role
        Properties:
          AssumeRolePolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Principal:
                  Service: lambda.amazonaws.com
                Action: sts:AssumeRole
          ManagedPolicyArns:
            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
          Policies:
            - PolicyName: DynamoDBAccess
              PolicyDocument:
                Version: '2012-10-17'
                Statement:
                  - Effect: Allow
                    Action:
                      - dynamodb:DescribeTable
                      - dynamodb:UpdateTable
                    Resource: '*'
  2. Save the file, with the name, switch-to-ondemand.yaml.

  3. Run the following create-stack AWS CLI command:

    # For On-Demand mode
    aws cloudformation create-stack \
      --stack-name switch-to-ondemand \
      --template-body file://switch-to-ondemand.yaml \
      --capabilities CAPABILITY_IAM \
      --region [REGION] \ 
      --parameters ParameterKey=TableNames,ParameterValue="Table1,Table2,Table3" 

    Note: Replace "Table1,Table2,Table3" with your table names and REGION with your Region.

Python

You can use an Amazon Elastic Compute Cloud (EC2) instance, Lambda, or your own desktop to run a Python script. Before you change the capacity mode, make sure that you installed Python, pip, and boto3.

Before you start, configure your AWS credentials. Run the following configure AWS CLI command:

aws configure

Provisioned mode

To use a Python script to change the capacity mode of all DynamoDB tables in a specific Region to provisioned mode, complete the following steps:

  1. Open Python and enter the following code to create a new file:

    import boto3
    import time
    from botocore.exceptions import ClientError
    
    def switch_to_provisioned(read_capacity=5, write_capacity=5, region=None):
        """
        Switch all DynamoDB tables from On-Demand to Provisioned capacity mode
        """
        # Initialize DynamoDB client
        dynamodb = boto3.client('dynamodb', region_name=region)
        
        # Get all table names
        tables = []
        paginator = dynamodb.get_paginator('list_tables')
        for page in paginator.paginate():
            tables.extend(page['TableNames'])
        
        print(f"Found {len(tables)} tables")
        
        for table_name in tables:
            try:
                # Get table details
                response = dynamodb.describe_table(TableName=table_name)
                table = response['Table']
                current_mode = table.get('BillingModeSummary', {}).get('BillingMode', '')
                
                if current_mode == 'PAY_PER_REQUEST':
                    print(f"\nProcessing table: {table_name}")
                    
                    # Prepare GSI updates if any
                    gsi_updates = []
                    if 'GlobalSecondaryIndexes' in table:
                        print(f"Found GSIs for table {table_name}")
                        for gsi in table['GlobalSecondaryIndexes']:
                            gsi_updates.append({
                                'Update': {
                                    'IndexName': gsi['IndexName'],
                                    'ProvisionedThroughput': {
                                        'ReadCapacityUnits': read_capacity,
                                        'WriteCapacityUnits': write_capacity
                                    }
                                }
                            })
                    
                    # Prepare update parameters
                    update_params = {
                        'TableName': table_name,
                        'BillingMode': 'PROVISIONED',
                        'ProvisionedThroughput': {
                            'ReadCapacityUnits': read_capacity,
                            'WriteCapacityUnits': write_capacity
                        }
                    }
                    
                    if gsi_updates:
                        update_params['GlobalSecondaryIndexUpdates'] = gsi_updates
                    
                    # Update table
                    print(f"Switching {table_name} to PROVISIONED mode...")
                    dynamodb.update_table(**update_params)
                    print(f"Update request submitted for {table_name}")
                    
                else:
                    print(f"\nSkipping {table_name} - already in PROVISIONED mode")
                
            except ClientError as e:
                if e.response['Error']['Code'] == 'LimitExceededException':
                    print(f"\nError: Cannot update {table_name}. You can only switch between billing modes once per 24 hours.")
                else:
                    print(f"\nError processing table {table_name}: {str(e)}")
                continue
            except Exception as e:
                print(f"\nUnexpected error processing table {table_name}: {str(e)}")
                continue
            
            # Small delay to avoid API throttling
            time.sleep(1)
    
    if __name__ == "__main__":
        # You can modify these values
        READ_CAPACITY = [RCU_VALUE]
        WRITE_CAPACITY = [WCU_VALUE]
        REGION = [REGION]  # Change to your desired region
        
        switch_to_provisioned(
            read_capacity=READ_CAPACITY,
            write_capacity=WRITE_CAPACITY,
            region=REGION
        )

    Note: Replace RCU_VALUE and WCU_VALUE with your RCU AND WCU values and REGION with your Region, such as us-east-1.

  2. Save the file with the switch_to_provisioned.py name.

  3. Open the terminal and run the following command to run the Python script:

    python switch_to_provisioned.py

On-demand mode

To use a Python script to change the capacity mode of all DynamoDB tables in a specific Region to on-demand mode, complete the following steps:

  1. Open Python and enter the following code to create a new file:

    import boto3
    import time
    from botocore.exceptions import ClientError
    
    def switch_to_ondemand(region=None):
        """
        Switch all DynamoDB tables from Provisioned to On-Demand capacity mode
        """
        # Initialize DynamoDB client
        dynamodb = boto3.client('dynamodb', region_name=region)
        
        # Get all table names
        tables = []
        paginator = dynamodb.get_paginator('list_tables')
        for page in paginator.paginate():
            tables.extend(page['TableNames'])
        
        print(f"Found {len(tables)} tables")
        
        for table_name in tables:
            try:
                # Get table details
                response = dynamodb.describe_table(TableName=table_name)
                table = response['Table']
                current_mode = table.get('BillingModeSummary', {}).get('BillingMode', '')
                
                if current_mode == 'PROVISIONED':
                    print(f"\nProcessing table: {table_name}")
                    
                    # Check for GSIs
                    if 'GlobalSecondaryIndexes' in table:
                        print(f"Table {table_name} has GSIs - they will automatically switch to On-Demand")
                    
                    # Update table
                    print(f"Switching {table_name} to PAY_PER_REQUEST mode...")
                    dynamodb.update_table(
                        TableName=table_name,
                        BillingMode='PAY_PER_REQUEST'
                    )
                    print(f"Update request submitted for {table_name}")
                    
                else:
                    print(f"\nSkipping {table_name} - already in PAY_PER_REQUEST mode")
                
            except ClientError as e:
                if e.response['Error']['Code'] == 'LimitExceededException':
                    print(f"\nError: Cannot update {table_name}. You can only switch between billing modes once per 24 hours.")
                else:
                    print(f"\nError processing table {table_name}: {str(e)}")
                continue
            except Exception as e:
                print(f"\nUnexpected error processing table {table_name}: {str(e)}")
                continue
            
            # Small delay to avoid API throttling
            time.sleep(1)
    
    def check_table_status(table_name, region=None):
        """
        Check the current billing mode of a specific table
        """
        dynamodb = boto3.client('dynamodb', region_name=region)
        try:
            response = dynamodb.describe_table(TableName=table_name)
            mode = response['Table'].get('BillingModeSummary', {}).get('BillingMode', 'Unknown')
            print(f"Table {table_name} is in {mode} mode")
            return mode
        except Exception as e:
            print(f"Error checking table {table_name}: {str(e)}")
            return None
    
    if __name__ == "__main__":
        REGION = [REGION]   # Change to your desired region
        
        switch_to_ondemand(region=REGION)    

    Note: Replace REGION with your Region.

  2. Save the file with the name, switch_to_ondemand.py.

  3. Open the terminal and run the following command to run the Python script:

    python switch_to_ondemand.py

Related information

DynamoDB throughput capacity

AWS OFFICIALUpdated a month ago