Skip to content

Commit

Permalink
Initial implementation of CloudWatch agent support for Windows instan…
Browse files Browse the repository at this point in the history
…ces.
  • Loading branch information
gitwater committed Jun 16, 2021
1 parent fc7f802 commit 2b16aa6
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 27 deletions.
33 changes: 20 additions & 13 deletions src/paco/application/ec2_launch_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,20 +228,27 @@ def ec2lm_update_instances_hook(self, hook, bucket_resource):
Key="ec2lm_cache_id.md5"
)
# send SSM command to update existing instances
ssm_client = self.account_ctx.get_aws_client('ssm', aws_region=self.aws_region)
ssm_log_group_name = prefixed_name(resource, 'paco_ssm', self.paco_ctx.legacy_flag)
ssm_client.send_command(
Targets=[{
'Key': 'tag:aws:cloudformation:stack-name',
'Values': [resource.stack.get_name()]
},],
DocumentName='paco_ec2lm_update_instance',
Parameters={ 'CacheId': [cache_id] },
CloudWatchOutputConfig={
'CloudWatchLogGroupName': ssm_log_group_name,
'CloudWatchOutputEnabled': True,
},
ssm_ctl = self.paco_ctx.get_controller('SSM')
ssm_ctl.paco_ec2lm_update_instance(
resource=resource,
account_ctx=self.accout_ctx,
region=self.aws_region,
cache_id=cache_id
)
# ssm_client = self.account_ctx.get_aws_client('ssm', aws_region=self.aws_region)
# ssm_log_group_name = prefixed_name(resource, 'paco_ssm', self.paco_ctx.legacy_flag)
# ssm_client.send_command(
# Targets=[{
# 'Key': 'tag:aws:cloudformation:stack-name',
# 'Values': [resource.stack.get_name()]
# },],
# DocumentName='paco_ec2lm_update_instance',
# Parameters={ 'CacheId': [cache_id] },
# CloudWatchOutputConfig={
# 'CloudWatchLogGroupName': ssm_log_group_name,
# 'CloudWatchOutputEnabled': True,
# },
# )

def ec2lm_update_instances_cache(self, hook, bucket_resource):
"Cache method for EC2LM resource"
Expand Down
141 changes: 128 additions & 13 deletions src/paco/application/reseng_asg.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ def init_resource(self):
cache_method=None,
hook_arg=self.resource
)
# TODO: Make this work with Linux too
self.stack.hooks.add(
name='UpdateCloudWatchAgent.' + self.resource.name,
stack_action=['create', 'update'],
stack_timing='post',
hook_method=self.asg_hook_update_cloudwatch_agent,
cache_method=None,
hook_arg=self.resource
)

# For ECS ASGs add an ECS Hook
if self.resource.ecs != None and self.resource.is_enabled() == True:
Expand All @@ -150,19 +159,125 @@ def get_ec2lm_cache_id(self, hook, hook_arg):
return self.ec2lm_cache_id

def asg_hook_update_ssm_agent(self, hook, asg):
ssm_client = self.account_ctx.get_aws_client('ssm', aws_region=self.aws_region)
ssm_log_group_name = prefixed_name(asg, 'paco_ssm', self.paco_ctx.legacy_flag)
response = ssm_client.send_command(
Targets=[{
'Key': 'tag:aws:cloudformation:stack-name',
'Values': [asg.stack.get_name()]
},],
CloudWatchOutputConfig={
'CloudWatchLogGroupName': ssm_log_group_name,
'CloudWatchOutputEnabled': True,
},
DocumentName='AWS-UpdateSSMAgent',
)
ssm_ctl = self.paco_ctx.get_controller('SSM')
ssm_ctl.command_update_ssm_agent(asg, self.account_ctx, self.aws_region)


def asg_hook_update_cloudwatch_agent(self, hook, asg):
ssm_ctl = self.paco_ctx.get_controller('SSM')
cloudwatch_config = """{
"logs": {
"logs_collected": {
"windows_events": {
"collect_list": [
{
"event_format": "xml",
"event_levels": [
"VERBOSE",
"INFORMATION",
"WARNING",
"ERROR",
"CRITICAL"
],
"event_name": "System",
"log_group_name": "Paco-Test-Windows-System",
"log_stream_name": "{instance_id}"
},
{
"event_format": "xml",
"event_levels": [
"VERBOSE",
"INFORMATION",
"WARNING",
"ERROR",
"CRITICAL"
],
"event_name": "Security",
"log_group_name": "Paco-Test-Windows-Security",
"log_stream_name": "{instance_id}"
}
]
}
}
},
"metrics": {
"append_dimensions": {
"AutoScalingGroupName": "${aws:AutoScalingGroupName}",
"ImageId": "${aws:ImageId}",
"InstanceId": "${aws:InstanceId}",
"InstanceType": "${aws:InstanceType}"
},
"metrics_collected": {
"LogicalDisk": {
"measurement": [
"% Free Space"
],
"metrics_collection_interval": 60,
"resources": [
"*"
]
},
"Memory": {
"measurement": [
"% Committed Bytes In Use"
],
"metrics_collection_interval": 60
},
"Paging File": {
"measurement": [
"% Usage"
],
"metrics_collection_interval": 60,
"resources": [
"*"
]
},
"PhysicalDisk": {
"measurement": [
"% Disk Time",
"Disk Write Bytes/sec",
"Disk Read Bytes/sec",
"Disk Writes/sec",
"Disk Reads/sec"
],
"metrics_collection_interval": 60,
"resources": [
"*"
]
},
"Processor": {
"measurement": [
"% User Time",
"% Idle Time",
"% Interrupt Time"
],
"metrics_collection_interval": 60,
"resources": [
"*"
]
},
"TCPv4": {
"measurement": [
"Connections Established"
],
"metrics_collection_interval": 60
},
"TCPv6": {
"measurement": [
"Connections Established"
],
"metrics_collection_interval": 60
},
"statsd": {
"metrics_aggregation_interval": 60,
"metrics_collection_interval": 60,
"service_address": ":8125"
}
}
}
}"""
ssm_ctl.command_update_cloudwatch_agent(asg, self.account_ctx, self.aws_region, cloudwatch_config)


def provision_ecs_capacity_provider_cache(self, hook, asg):
"Cache method for ECS ASG"
Expand Down
125 changes: 124 additions & 1 deletion src/paco/controllers/ctl_ssm.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
import json
from paco.aws_api.ssm.document import SSMDocumentClient
from paco.core.exception import StackException
from paco.controllers.controllers import Controller
from paco.aws_api.ssm.document import SSMDocumentClient
from paco.models.resources import SSMDocument
from paco.utils import md5sum, prefixed_name


SSM_DOCUMENT_UPDATE_WINDOWS_CLOUDWATCH_AGENT = {
"schemaVersion": "2.2",
"description": "Windows CloudWatch Agent Configuration and Start",
"parameters": {
"ConfigParamStoreName": {
"type": "String",
"description": "CloudWatch Configuration Parameter Store Name"
},
},
"mainSteps": [
{
"action": "aws:runPowerShellScript",
"name": "WindowsCloudWatchConfigAndStart",
"inputs": {
"runCommand": [
'& "C:\Program Files\Amazon\AmazonCloudWatchAgent\\amazon-cloudwatch-agent-ctl.ps1" -a fetch-config -m ec2 -s -c ssm:{{ConfigParamStoreName}}',
]
}
}
]
}

class SSMController(Controller):
def __init__(self, paco_ctx):
Expand All @@ -10,6 +36,103 @@ def init(self, command=None, model_obj=None):
self.command = command
self.model_obj = model_obj

def paco_ec2lm_update_instance(self, resource, account_ctx, region, cache_id):
ssm_client = account_ctx.get_aws_client('ssm', aws_region=region)
ssm_log_group_name = prefixed_name(resource, 'paco_ssm', self.paco_ctx.legacy_flag)
ssm_client.send_command(
Targets=[{
'Key': 'tag:aws:cloudformation:stack-name',
'Values': [resource.stack.get_name()]
},],
DocumentName='paco_ec2lm_update_instance',
Parameters={ 'CacheId': [cache_id] },
CloudWatchOutputConfig={
'CloudWatchLogGroupName': ssm_log_group_name,
'CloudWatchOutputEnabled': True,
},
)

def command_update_ssm_agent(self, resource, account_ctx, region):
ssm_client = account_ctx.get_aws_client('ssm', aws_region=region)
ssm_log_group_name = prefixed_name(resource, 'paco_ssm', self.paco_ctx.legacy_flag)
response = ssm_client.send_command(
Targets=[{
'Key': 'tag:aws:cloudformation:stack-name',
'Values': [resource.stack.get_name()]
},],
CloudWatchOutputConfig={
'CloudWatchLogGroupName': ssm_log_group_name,
'CloudWatchOutputEnabled': True,
},
DocumentName='AWS-UpdateSSMAgent',
)

def command_update_cloudwatch_agent(self, resource, account_ctx, region, cloudwatch_config_json):
ssm_documents = self.paco_ctx.project['resource']['ssm'].ssm_documents
if 'paco_windows_cloudwatch_agent_update' not in ssm_documents:
ssm_doc = SSMDocument('paco_windows_cloudwatch_agent_update', ssm_documents)
ssm_doc.add_location(account_ctx.paco_ref, region)
ssm_doc.content = json.dumps(SSM_DOCUMENT_UPDATE_WINDOWS_CLOUDWATCH_AGENT)
ssm_doc.document_type = 'Command'
ssm_doc.enabled = True
ssm_documents['paco_windows_cloudwatch_agent_update'] = ssm_doc
else:
ssm_documents['paco_windows_cloudwatch_agent_update'].add_location(
account_ctx.paco_ref,
region
)

self.provision_ssm_document(ssm_doc, account_ctx, region)

ssm_client = account_ctx.get_aws_client('ssm', aws_region=region)
ssm_log_group_name = prefixed_name(resource, 'paco_ssm', self.paco_ctx.legacy_flag)

# Create Config Parameter Store
cloudwatch_config_param_store_name = f'Paco-CloudWatch-Config-{resource.stack.get_name()}'
response = ssm_client.put_parameter(
Name=cloudwatch_config_param_store_name,
Description='CloudWatch Configuration',
Value=cloudwatch_config_json,
Type='String',
Overwrite=True,
Tier='Advanced'
)


# Install the Agent
response = ssm_client.send_command(
Parameters={
'action': ['Install'],
'name': ["AmazonCloudWatchAgent"],
'version': ["latest"],
},
Targets=[{
'Key': 'tag:aws:cloudformation:stack-name',
'Values': [resource.stack.get_name()]
},],
CloudWatchOutputConfig={
'CloudWatchLogGroupName': ssm_log_group_name,
'CloudWatchOutputEnabled': True,
},
DocumentName='AWS-ConfigureAWSPackage',
)

# Configure and start the agent
response = ssm_client.send_command(
Parameters={
'ConfigParamStoreName': [cloudwatch_config_param_store_name],
},
Targets=[{
'Key': 'tag:aws:cloudformation:stack-name',
'Values': [resource.stack.get_name()]
},],
CloudWatchOutputConfig={
'CloudWatchLogGroupName': ssm_log_group_name,
'CloudWatchOutputEnabled': True,
},
DocumentName='paco_windows_cloudwatch_agent_update'
)

def validate(self):
pass

Expand Down

0 comments on commit 2b16aa6

Please sign in to comment.