Reputation: 23
hope I did not overlook an already existing answer .. I would like to create an aws-cdk python stack containing an event that is starting an ssm-document when triggered. I got all the wanted stuff going in my aws test account, event is triggering on the desired actions and starts the ssm-document/run command with the correct targets (ec2 instances identified by some tags). Now when it comes to Iac using aws-cdk python, I came to the boundary that it seems as if the aws_events_targets class (https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_events_targets.html) does not allow to set the diesired target for an event .. does anyone has a suggestion how to best work around this or the correct example for a blind man?
Upvotes: 1
Views: 1179
Reputation: 543
In short, it is not a valid approach because ssm-document is just a set of commands, not a system(application) or computing environment.
I would decompose this process as follows:
According to this, I would stick to the design pattern for event-based business logic execution:
Event source sends event to event Bus. Then EventBridge Rule catches it based on predefined pattern and activates Lambda which is going to execute a business logic (in our case ssm-document).
So, let's get to code:
Step 0
I assume you have AWS CLI/CDK installed, project is set and you are already logged in.
Step 1 - Declare and define Lambda and its Role
import aws_cdk.aws_iam as _iam
lambda_role = _iam.Role(self, "lambda_role",
role_name="lambda_role",
assumed_by=_iam.ServicePrincipal("lambda.amazonaws.com"),
managed_policies=[
_iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaBasicExecutionRole"),
_iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSServiceRoleX"),
_iam.ManagedPolicy.from_aws_managed_policy_name("AWSRoleY")],
inline_policies={
"My_Custom_Name_For_Inline_Policy": _iam.PolicyDocument(statements=[
_iam.PolicyStatement(
effect=_iam.Effect.ALLOW,
actions=["ssm:*"],
resources=["RESOURCE_ARN1", "RESOURCE_ARN1"])])})
As I do not have enough information to help you correctly define the role, I proposed all possible option how you can allow your Lambda function to run ssm-document. Here are some details:
service-role/AWSLambdaBasicExecutionRole
is mandatoryservice-role/AWSServiceRoleX
is meant for any service-based AWS managed roles. Replace it with the correct one(s) in case of need.AWSRoleY
is meant for general AWS-managed roles. Replace it with the correct one(s) in case of need.inline_policies
in case you need to granularly define what exactly you want to allow your Lambda role to do.import aws_cdk.aws_lambda as _lambda
lambda_ = _lambda.Function(self, "my_lambda",
function_name="my_lambda",
architecture=_lambda.Architecture.ARM_64,
runtime=_lambda.Runtime.PYTHON_3_9,
role=lambda_role,
log_retention=_logs.RetentionDays.THREE_MONTHS,
timeout=cdk.Duration.minutes(3),
retry_attempts=0,
code=_lambda.Code.from_asset("./assets/lambda_code/"),
handler="lambda_main.lambda_handler")
Again, In this case I am lacking some information but I assume this might be a strucutre of your Lambda function:
lambda_role
refers to previously defined role.log_retention
for 3 months, removed retry_attempts
and set 3 min timeout
. These are optional and should be used/adjusted based on your preference.code
refers to block which I will provide in the next block. If you want to following the proposed structure then in the root of the project you will need to create dir called lambda_code
, inside it you will script named lambda_main.py
lambda_handler
.Lambda code which hold business logic (run ssm-document):
import boto3
_ssm = boto3.client('ssm')
def lambda_handler(event, context):
print(event)
response = ssm_client.send_command(
InstanceIds=['i-01234567890'],
DocumentName="AWS-RunShellScript",
Parameters={'commands': ['whoami']})
command_id = response['Command']['CommandId']
return ssm_client.get_command_invocation(CommandId=command_id, InstanceId='i-01234567890')
In this example I decided to use AWS-RunShellScript
document, which you should substitute with the one you need. Be default, parameters also should be changed.
Step 2 - Declare and define EventBridge Rule
import aws_cdk.aws_events as _events
import aws_cdk.aws_events_targets as _targets
# Based on CRON
_events.Rule(self, "trigger_rule_sync",
rule_name="my_rule_name",
enabled=True,
schedule=_events.Schedule.cron(minute="0", hour="8"),
targets=[_targets.LambdaFunction(handler=lambda_tableau_deployment)])
# Based on EVENT PATTERN
_events.Rule(self, "trigger_rule_sync",
rule_name="my_rule_name",
enabled=True,
event_pattern=_events.EventPattern(
resources=["ARN_OF_MY_CODECOMMIT_REPO"],
detail={"event": ["referenceUpdated"], "referenceName": ["prod", "dev"]}),
targets=[_targets.LambdaFunction(handler=lambda_tableau_deployment)])
In this snippet I provided both triggering by schedule and triggering by event. As an event I assumed that I want to trigger my lambda when I commit something to by prod or dev branch in AWS CodeCommit. (weird, but why not?). Details of code can be found in boto3 documentation.
Step 3 - Sync, deploy and check
When everything is ready, give it a try and verify that final result matches the expected one.
Upvotes: 1