Stating and Stopping EC2 instances across Multiple accounts(ie. in accounts in an Organization)

Cahit Öz
3 min readSep 28, 2019

--

After submitting my First story on Medium, I was confronted with the challenge of enabling starting and stopping instances in multiple accounts in an organization.

Lets assume we have (accountA) as the root account who will initiate the stating and stopping of instanced in (accountB) this can be multiple accounts as B, B1 B2 .. Bn etc.

So to enable this you have to perform 3 things.

  • Create a Role auth in accountB that will give access to EC2 instances, Lambda functions and STS operations
  • Enhance the auth function in accountB so that it will allow accountA to assume that Role.
  • Create a Role lambda_start_stop_ec2 in accountA that will give access to EC2 instances, Lambda functions and STS operations.
  • And finally the changes you need to perform for the code. This is a follow up to my first story

1 - Creating the Role Auth in accountB

In accountB create a Role named Auth.

attach the policies AmazonEC2FullAccess and AWSLambdaFullAccess. Please feel free to limit the rights you grant in a policy ;)

Create a policy named STSFullAccess and also attach it

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "sts:*",
"Resource": "*"
}
]
}

2 - Allow accountA to assume Role Auth in accountB

Please enter the account number of account A in the blurred location

3 - Create the role lambda_start_stop_ec2 in accountA and tell it what role in what account it can assume.

Create the role named lambda_start_stop_ec2

assuming that you want to also start stop your ec2 instances in your root account attach the same policies AmazonEC2FullAccess and AWSLambdaFullAccess.

create a policy that will request to access policies.

{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::[accountB]:role/Auth"
}
}

Relace [accountB] with the account number of accountB

4 - Make the necessary changes to the lambda function we created in my First story.

import boto3
import logging
#setup simple logging for INFO
logger = logging.getLogger()
logger.setLevel(logging.INFO)
prj_maps = {
"root": {
"RoleArn":"",
"RoleSessionName":""
},
"Prj-1": {
"RoleArn":"arn:aws:iam::999999999999:role/Auth",
"RoleSessionName":"cross_acct_lambda"
},
# You can extend the number of accounts to your liking
}
def multiprocessing_func(Proj, Action):

if Proj == 'root' :
ec2 = boto3.resource('ec2')
else :
sts_connection = boto3.client('sts')
acct_b = sts_connection.assume_role(
RoleArn= prj_maps[Proj]['RoleArn'],
RoleSessionName="cross_acct_lambda"
)

ACCESS_KEY = acct_b['Credentials']['AccessKeyId']
SECRET_KEY = acct_b['Credentials']['SecretAccessKey']
SESSION_TOKEN = acct_b['Credentials']['SessionToken']

ec2 = boto3.resource(
'ec2',
aws_access_key_id=ACCESS_KEY,
aws_secret_access_key=SECRET_KEY,
aws_session_token=SESSION_TOKEN
)

if Action == 'stop':
filters = [
{
'Name': 'tag:AutoOff',
'Values': ['True']
},
{
'Name': 'instance-state-name',
'Values': ['running']
}
]
else :
filters = [
{
'Name': 'tag:AutoOff',
'Values': ['True']
},
{
'Name': 'instance-state-name',
'Values': ['stopped']
}
]

instances = ec2.instances.filter(Filters=filters)

instancesFound = 0
for oneInstance in instances:
logger.info('%s Instance ID: %s', Action, oneInstance.id)
instancesFound = 1

if instancesFound == 0:
logger.info('No Instances found to %s....', Action)
else:
#Stop all running instances that are tagged AutoOff=True
if Action == 'stop':
instances.stop()
else :
instances.start()
logger.info('%s order has been sent: %s', Action, Proj)
def lambda_handler(event, context):
logger.info('Action: %s', event['action'])
multiprocessing_func('root', event['action'])
multiprocessing_func('Prj-1', event['action'])

This will toggle over all accounts (identified here as Prj-1) and start and stop them. And you can schedule them at predefined times as mention in my First story.

As the number of accounts increase you may consider multithreading which I will explain in a separate story.

--

--