So i'm trying to create a SSM document with the instance ID below, so i can then link it to event bridge to trigger a powershell script based on a cloud watch alarm. I have selected Target type: /AWS::EC2::instance and then the YAML below. But it isn't letting me create the document?
---
schemaVersion: "2.2"
description: "Command Document Example JSON Template"
InstanceId: "i-0bbec63d8fee3d6e3"
description: "Instance ID"
mainSteps:
- action: "aws:runPowerShellScript"
name: "RunCommands"
inputs:
runCommand:
- "Restart-Service -Name ColdFusion 2018 Application Server"
Trying to create a SSM document, which will target this specific instance, based on this instance ID
It is not a valid SSM document file.
I believe that bellow example should help.
---
schemaVersion: "2.2"
description: "Example document"
parameters:
Message:
type: "String"
description: "Example parameter"
default: "Hello World"
mainSteps:
- action: "aws:runPowerShellScript"
name: "example"
inputs:
timeoutSeconds: '60'
runCommand:
- "Write-Output {{Message}}"
Source: https://docs.aws.amazon.com/systems-manager/latest/userguide/document-schemas-features.html#documents-schema-twox
Related
I am writing a cloud-formation template where I am running two powershell scripts. Now I want to fetch the output of both the scripts and want to send that to an email which is already mentioned in cloud-formation parameter.
here is the code:-
AWSTemplateFormatVersion: '2010-09-09'
Description: Test Document
Resources:
Type: AWS::SSM::Document
Properties:
DocumentType: Command
Name: "Test Upgrade"
Content:
schemaVersion: '2.2'
description: "Test Upgrade"
parameters:
Emails:
type: String
description: |-
enter the email address to send the overall output
mainSteps:
- action: "aws:runPowerShellScript"
name: "DriverUpgrade"
precondition:
StringEquals: ["platformType", "Windows"]
inputs:
runCommand:
[]
timeoutSeconds: 3600
onFailure: Continue
maxAttempts: 1
isCritical: False
nextStep: Second Driver
- action: "aws:runPowerShellScript"
name: "SecondDriverUpgrade"
precondition:
StringEquals: ["platformType", "Windows"]
inputs:
runCommand:
[]
timeoutSeconds: 3600
onFailure: Continue
isCritical: False
Could you use AWS CLI command 'ses send-email' inside the Powershell script or as a part of Run Command parameters?
Certainly you have to configure the SES service beforehand in cfn template or console.
You could retrieve the email address from parameter store by using AWS CLI command ssm get-parameters.
To store the output of the scripts you could use a local file, parameter store, or, for example, dynamodb table etc.
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ses/send-email.html
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ssm/get-parameters.html
I want to trigger a lambda whenever a new EC2 instance is registred in SSM's Fleet Manager (meaning the instance can be connected to using SSM), however I can't find what pattern to use in EventBridge.
Within EventBridge, I tried using the following pattern I found in the docs (so far its looks like the closest thing to my goal):
{
"source": ["aws.ssm"],
"detail-type": ["Inventory Resource State Change"]
}
However when I create a new EC2 and wait for its SSM agent to become active, it still doesn't trigger the above pattern.
Any idea how to catch this kind of event?
I think you have to go through CloudTrail API call.
Please find below a CloudFormation template I used in the past that was working. Please note that it just provides the SSM resources. You need to add your own SQS queue as well (see SQS.ARN) and I've used the association with the tag registration set to enabled. So that if you have a lambda function connected, you can set it to false so if the instance connect again, it won't go to the same process again.
AWSTemplateFormatVersion: "2010-09-09"
Description: >
SSM Registration event
# Description of the resources to be created.
Resources:
RegistrationDocument:
Type: AWS::SSM::Document
Properties:
DocumentType: Command
Content:
schemaVersion: "2.2"
description: >
An Automation Document ran by registered instances that gathers their software inventory
and automatically updates their AWS SSM Agent to the latest version.
mainSteps:
- name: GatherSoftware
action: aws:softwareInventory
- name: Sleep
action: aws:runShellScript
inputs:
runCommand:
- sleep 20 || true
- name: UpdateAgent
action: aws:updateSsmAgent
inputs:
agentName: amazon-ssm-agent
source: https://s3.{Region}.amazonaws.com/amazon-ssm-{Region}/ssm-agent-manifest.json
allowDowngrade: "false"
RegistrationDocumentAssociation:
Type: AWS::SSM::Association
Properties:
AssociationName: !Sub registration-association-${AWS::StackName}
Name: !Ref RegistrationDocument
Targets:
- Key: tag:registration
Values:
- enabled
RegistrationEventRule:
Type: AWS::Events::Rule
Properties:
Description: >
Events Rule that monitors registration of AWS SSM instances
and logs them to an SQS queue.
EventPattern:
source:
- aws.ssm
detail-type:
- AWS API Call via CloudTrail
detail:
eventName:
- UpdateInstanceAssociationStatus
requestParameters:
associationId:
- !Ref RegistrationDocumentAssociation
executionResult:
status:
- Success
State: ENABLED
Targets:
- Arn: SQS.ARN
Id: SqsRegistrationSubscription
SqsParameters:
MessageGroupId: registration.events
The task is simple: whenever an EC2 instance is launched with tag key:value I want it to install a specific software. Whenever an EC2 instance is launched with a different tag key:value I want it to install a different software.
I understand that I can create 2 different associations in State Manager that uses runCommand RuneRemoteScript to install software based on the tags, but the goal is to have 1 composite document that can do this.
Any help / guidance would be appreciated!
You can achieve that using SSM Automation documents - https://docs.aws.amazon.com/systems-manager/latest/userguide/automation-branchdocs.html
However, probably you will need to do something like this:
In the State Manager use AWS-RunDocument,
This document should execute SSM Automation document (your Composite document)
Your Composite document should look like this:
I didn't validate this template, and I assume It shouldn't work without a few days of debugging
schemaVersion: '0.3'
parameters:
InstanceId:
type: String
mainSteps:
- name: DescribeEc2
action: 'aws:executeScript'
inputs:
Runtime: python3.7
Handler: script_handler
Script: |
import json
import boto3
def script_handler(events):
ec2_instance = boto3.client('ec2').describe_instances(
InstanceIds=events["instance_id"],
)["Reservations"][0]["Instances"][0]
# thread it like an example,
# Here you should parse your tags and decide what software you
# want to install on the provided instance
return json.dumps(
{
"to_be_installed": "result"
},
sort_keys=True,
default=str
)
InputPayload:
instance_id: '{{ InstanceId }}'
Outputs:
- Name: result
Selector: "$.to_be_installed"
- name: WhatToInstall
action: aws:branch
inputs:
Choices:
- NextStep: InstallSoft1
Variable: "{{DescribeEc2.result}}"
StringEquals: soft_1
- NextStep: InstallSoft1
Variable: "{{DescribeEc2.result}}"
StringEquals: soft_2
- name: InstallSoft1
action: aws:runCommand
inputs:
DocumentName: AWS-RunShellScript
InstanceIds:
- '{{ InstanceId }}'
Parameters:
commands:
...
- name: InstallSoft2
action: aws:runCommand
inputs:
DocumentName: AWS-RunShellScript
InstanceIds:
- '{{ InstanceId }}'
Parameters:
commands:
...
Tbh, you will find a lot of troubles with such solution (IAM and SSM specific issues), so I will recommend using Event Bridge -> Lambda Function(that decides which Document/Automation should be run) -> SSM-RunDocument (executed directly in the Lambda Function).
I have the following resource in my SAM template.yml:
MetricsRule:
Type: AWS::Events::Rule
Properties:
Name: MetricsRule
Description: Puts metrics to the CloudWatch log group
EventBusName: !FindInMap [LambdaConfig, !Ref stage, eventBusName]
EventPattern:
detail:
status:
- GENERATED
State: !FindInMap [LambdaConfig, !Ref stage, eventEnabled]
Targets:
- Id: LogGroupTarget
Arn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${LogGroup}"
InputTransformer:
InputPathsMap:
customer-id: $.detail.customerId
destination-type: $.detail.destinationType
provider-id: $.detail.providerId
InputTemplate: '"cusId = <customer-id> - <destination-type> for <provider-id>"'
If I try to deploy this stack it fails with the exception Input for target LogGroupTarget is not a valid JSON text.
UPD:
If I put InputTemplate as '{"message":"cusId = <customer-id> - <destination-type> for <provider-id>"}' it will work. But, in the documentation, examples and even in the placeholder of the field in UI it stands that Input Template: A string containing placeholders which will be filled with values defined in Input Paths e.g. "The state of Instance <instance> is <state>"
Is there any option to specify InputTemplate as a string?
Thanks.
I hope author found answer.
But just to have answer here too.
This should work:
InputTemplate: |
"cusId = <customer-id> - <destination-type> for <provider-id>"
More information is here: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-events-rule-inputtransformer.html
I have a CloudFormation template that creates an AWS::Events::Rule and an AWS::SSM::Document. I need to provide a list of Targets for the SSM::Rule, but each target expects an ARN:
mySSMDocument:
Type: AWS::SSM::Document
Properties:
DocumentType: 'Command'
Content:
schemaVersion: '2.2'
description: "Code that will be run on EC2"
mainSteps:
- action: "aws:runShellScript"
name: runShellScript
inputs:
runCommand:
- 'Some command to execute'
myEventRule:
Type: AWS::Events::Rule
Properties:
Description: "A description for the Rule."
EventPattern:
source:
- "aws.autoscaling"
detail-type:
- "EC2 Instance-terminate Lifecycle Action"
detail:
AutoScalingGroupName:
- !Ref 'someAutoScalingGroupInThisTemplate'
RoleArn: 'some role ARN'
State: "ENABLED"
Targets:
- Id: "some-unique-id"
Arn: <-- This is the value that I need to fill in.
RunCommandParameters:
RunCommandTargets:
- Key: "tag: Name"
Values:
- 'The name of the EC2 machine'
I think that I need to replace the <-- This is the value that I need to fill in. with the ARN of mySSMDocument, but I don't see any way to retrieve this value from within the template itself. The documentation does not specify any GetAtt functionality on SSM::Document that allows to get the ARN. Anyone know how to solve this issue?
This is ARN pattern of Document
arn:${Partition}:ssm:${Region}:${Account}:document/${DocumentName}
example:
arn:aws:ssm:us-east-2:12345678912:document/demoooo
You can use Ref function to get name of document, then Sub to create final ARN
refer: https://docs.aws.amazon.com/IAM/latest/UserGuide/list_awssystemsmanager.html#awssystemsmanager-resources-for-iam-policies
!Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:document/${mySSMDocument}
You can produce the ARN format for AWS::SSM::Document using the return Value for AWS::SSM::Document, the Pseudo Parameters for Partition, Region, and AccountId, and the Sub intrinsic function