I am implementing a custom cloudwatch metric for a windows ec2 instance and here's my script for the same. This metric should report cloudwatch regarding the memory and pagefile details.
[CmdletBinding(DefaultParametersetName="credsfromfile", supportsshouldprocess = $true) ]
param(
[switch]$mem_util,
[switch]$mem_used ,
[switch]$mem_avail,
[switch]$page_used,
[switch]$page_avail,
[switch]$page_util,
[switch]$auto_scale,
[ValidateSet("bytes","kilobytes","megabytes","gigabytes" )]
[string]$memory_units = "none",
[switch]$from_scheduler,
[Parameter(Parametersetname ="credsinline",mandatory=$true)]
[string]$aws_access_id = "",
[Parameter(Parametersetname ="credsinline",mandatory=$true)]
[string]$aws_secret_key = "",
[Parameter(Parametersetname ="credsfromfile")]
[string]$aws_credential_file = [Environment]::GetEnvironmentVariable("AWS_CREDENTIAL_FILE"),
[string]$logfile = $null,
[Switch]$version
)
$ErrorActionPreference = 'Stop'
### Initliaze common variables ###
$accountinfo = New-Object psobject
$wc = New-Object Net.WebClient
$time = Get-Date
[string]$aaid =""
[string]$ask =""
$invoc = (Get-Variable myinvocation -Scope 0).value
$currdirectory = Split-Path $invoc.mycommand.path
$scriptname = $invoc.mycommand.Name
$ver = '1.0.0'
$client_name = 'CloudWatch-PutInstanceDataWindows'
$useragent = "$client_name/$ver"
### Logs all messages to file or prints to console based on from_scheduler setting. ###
function report_message ([string]$message)
{
if($from_scheduler)
{ if ($logfile.Length -eq 0 )
{
$logfile = $currdirectory +"\" +$scriptname.replace('.ps1','.log')
}
$message | Out-File -Append -FilePath $logfile
}
else
{
Write-Host $message
}
}
### Global trap for all exceptions for this script. All exceptions will exit the script.###
trap [Exception] {
report_message ($_.Exception.Message)
Exit
}
if ($version)
{
report_message "$scriptname version $ver"
exit
}
####Test and load AWS sdk
$ProgFilesLoc = (${env:ProgramFiles(x86)}, ${env:ProgramFiles} -ne $null)[0]
$SDKLoc = "$ProgFilesLoc\AWS SDK for .NET\bin\Net35"
if ((Test-Path -PathType Container -Path $SDKLoc) -eq $false) {
$SDKLoc = "C:\Windows\Assembly"
}
$SDKLibraryLocation = dir C:\Windows\Assembly -Recurse -Filter "AWSSDK.dll"
if ($SDKLibraryLocation -eq $null)
{
throw "Please Install .NET sdk for this script to work."
}
else
{
$SDKLibraryLocation = $SDKLibraryLocation.FullName
Add-Type -Path $SDKLibraryLocation
Write-Verbose "Assembly Loaded"
}
### Process parameterset for credentials and adds them to a powershell object ###
switch ($PSCmdlet.Parametersetname)
{
"credsinline" {
Write-Verbose "Using credentials passed as arguments"
if (!($aws_access_id.Length -eq 0 ))
{
$aaid = $aws_access_id
}
else
{
throw ("Value of AWS access key id is not specified.")
}
if (!($aws_secret_key.Length -eq 0 ))
{
$ask = $aws_secret_key
}
else
{
throw "Value of AWS secret key is not specified."
}
}
"credsfromfile"{
if ( Test-Path $aws_credential_file)
{
Write-Verbose "Using AWS credentials file $aws_credential_file"
Get-Content $aws_credential_file | ForEach-Object {
if($_ -match '.*=.*'){$text = $_.split("=");
switch ($text[0].trim())
{
"AWSAccessKeyId" {$aaid= $text[1].trim()}
"AWSSecretKey" { $ask = $text[1].trim()}
}}}
}
else {throw "Failed to open AWS credentials file $aws_credential_file"}
}
}
if (($aaid.length -eq 0) -or ($ask.length -eq 0))
{
throw "Provided incomplete AWS credential set"
}
else
{
Add-Member -membertype noteproperty -inputobject $accountinfo -name "AWSSecretKey" -value $ask
Add-Member -membertype noteproperty -inputobject $accountinfo -name "AWSAccessKeyId" -value $aaid
Remove-Variable ask; Remove-Variable aaid
}
### Check if atleast one metric is requested to report.###
if ( !$mem_avail -and !$mem_used -and !$mem_util -and !$page_avail -and !$page_used -and !$page_util)
{
throw "Please specify a metric to report exiting script"
}
### Avoid a storm of calls at the beginning of a minute.###
if ($from_scheduler)
{
$rand = new-object system.random
start-sleep -Seconds $rand.Next(20)
}
### Functions that interact with metadata to get data required for dimenstion calculation and endpoint for cloudwatch api. ###
function get-metadata {
$extendurl = $args
$baseurl = "http://ift.tt/1MeYIsc"
$fullurl = $baseurl + $extendurl
return ($wc.DownloadString($fullurl))
}
function get-region {
$az = get-metadata("/placement/availability-zone")
return ($az.Substring(0, ($az.Length -1)))
}
function get-endpoint {
$region = get-region
return "https://monitoring." + $region + ".amazonaws.com/"
}
### Function that creates metric data which will be added to metric list that will be finally pushed to cloudwatch. ###
function append_metric {
$metricdata = New-Object Amazon.Cloudwatch.Model.MetricDatum
$metricdata.metricname, $metricdata.Unit, $metricdata.value, $metricdata.Dimensions = $args
$metricdata.Timestamp = $time.ToUniversalTime()
return $metricdata
}
### Function that validates units passed. Default value of Megabytes is used###
function parse-units {
param ([string]$mem_units,
[long]$mem_unit_div)
$units = New-Object psobject
switch ($memory_units.ToLower())
{
"bytes" { $mem_units = "Bytes"; $mem_unit_div = 1}
"kilobytes" { $mem_units = "Kilobytes"; $mem_unit_div = 1kb}
"megabytes" { $mem_units = "Megabytes"; $mem_unit_div = 1mb}
"gigabytes" { $mem_units = "Gigabytes"; $mem_unit_div = 1gb}
default { $mem_units = "Megabytes"; $mem_unit_div = 1mb}
}
Add-Member -InputObject $units -Name "mem_units" -MemberType NoteProperty -Value $mem_units
Add-Member -InputObject $units -Name "mem_unit_div" -MemberType NoteProperty -Value $mem_unit_div
return $units
}
### Function that gets memory stats using WMI###
function get-memory {
begin {}
process {
$mem = New-Object psobject
$units = parse-units
[long]$mem_avail_wmi = (get-WmiObject Win32_OperatingSystem | select -expandproperty FreePhysicalMemory) * 1kb
[long]$total_phy_mem_wmi = get-WmiObject Win32_ComputerSystem | select -expandproperty TotalPhysicalMemory
[long]$mem_used_wmi = $total_phy_mem_wmi - $mem_avail_wmi
Add-Member -InputObject $mem -Name "mem_avail_wmi" -MemberType NoteProperty -Value $mem_avail_wmi
Add-Member -InputObject $mem -Name "total_phy_mem_wmi" -MemberType NoteProperty -Value $total_phy_mem_wmi
Add-Member -InputObject $mem -Name "mem_used_wmi" -MemberType NoteProperty -Value $mem_used_wmi
Add-Member -InputObject $mem -Name "mem_units" -MemberType NoteProperty -Value $units.mem_units
Add-Member -InputObject $mem -Name "mem_unit_div" -MemberType NoteProperty -Value $units.mem_unit_div
write $mem
}
end{}
}
### Function that writes metrics to be piped to next fucntion to push to cloudwatch.###
function create-metriclist {
param (
[parameter(Valuefrompipeline=$true)] $mem_info)
begin{
$group_name = ""
$instance_id = get-metadata("/instance-id")
$auto_scale_group = Get-ASAutoScalingGroup
foreach($as in $auto_scale_group)
{
$flag = 0
$group_name = $as.AutoScalingGroupName
foreach($ins in $as.Instances)
{
Write-Host $ins.InstanceId
Write-Host "`r`n"
if ($ins.InstanceId -eq $instance_id)
{
$flag = 1
break
}
}
if ($flag -eq 1)
{
break
}
Write-Host "---------------------------`r`n"
}
Write-Host $instance_id
Write-Host "`r`n"
Write-Host $group_name
Write-Host "`r`n"
$dimlist = New-Object Collections.Generic.List[Amazon.Cloudwatch.Model.Dimension]
$dims = New-Object Amazon.Cloudwatch.Model.Dimension
if ( $auto_scale)
{
$dims.Name = "AutoScalingGroupName"
$dims.value = $group_name
}
else
{
$dims.Name = "InstanceId"
$dims.value = $instance_id
}
$dimlist.Add($dims)
$pagefilessize = @{}
$pagefileusage = @{}
gwmi Win32_PageFileSetting | ForEach-Object{$pagefilessize[$_.name]=$_.MaximumSize *1mb}
gwmi Win32_PageFileUsage | ForEach-Object{$pagefileusage[$_.name]=$_.currentusage *1mb}
[string[]]$pagefiles = $pagefilessize.keys
}
process{
if ($mem_util)
{
$percent_mem_util= 0
if ( [long]$mem_info.total_phy_mem_wmi -gt 0 ) { $percent_mem_util = 100 * ([long]$mem_info.mem_used_wmi/[long]$mem_info.total_phy_mem_wmi)}
write (append_metric "MemoryUtilization" "Percent" ("{0:N2}" -f $percent_mem_util) $dimlist)
}
if ($mem_used)
{
write (append_metric "MemoryUsed" $mem_info.mem_units ("{0:N2}" -f ([long]($mem_info.mem_used_wmi/$mem_info.mem_unit_div))) $dimlist)
}
if ( $mem_avail)
{
write (append_metric "MemoryAvailable" $mem_info.mem_units ("{0:N2}" -f ([long]($mem_info.mem_avail_wmi/$mem_info.mem_unit_div))) $dimlist)
}
if ($page_avail)
{
for ($i=0; $i -le ($pagefiles.count - 1);$i++)
{
write (append_metric ("pagefileAvailable("+$pagefiles[$i]+")") $mem_info.mem_units ("{0:N2}" -f ((($pagefilessize[$pagefiles[$i]]- ($pagefileusage[$pagefiles[$i]]))/$mem_info.mem_unit_div))) $dimlist)
}
}
if ($page_used)
{
for ($i = 0; $i -le ($pagefiles.count -1); $i++)
{
write (append_metric ("pagefileUsed("+$pagefiles[$i]+")") $mem_info.mem_units ("{0:N2}" -f (($pagefileusage[$pagefiles[$i]])/$mem_info.mem_unit_div)) $dimlist)
}
}
if ($page_util)
{
for ($i=0; $i -le ($pagefiles.count -1);$i++)
{
if($pagefilessize[$pagefiles[$i]] -gt 0 )
{
write (append_metric ("pagefileUtilization("+$pagefiles[$i]+")") "Percent" ("{0:N2}" -f((($pagefileusage[$pagefiles[$i]])*100)/$pagefilessize[$pagefiles[$i]])) $dimlist)
}
}
}
}
end{}
}
### Uses AWS sdk to push metrics to cloudwathc. This finally prints a requestid.###
function put-instancemem {
param (
[parameter(Valuefrompipeline=$true)] $metlist)
begin{
$cwconfig = New-Object Amazon.CloudWatch.AmazonCloudWatchConfig
$cwconfig.serviceURL = get-endpoint
$cwconfig.UserAgent = $useragent
$monputrequest = new-object Amazon.Cloudwatch.Model.PutMetricDataRequest
$response = New-Object psobject
$metricdatalist = New-Object Collections.Generic.List[Amazon.Cloudwatch.Model.MetricDatum]
}
process{
if ($PSCmdlet.shouldprocess($metlist.metricname,"The metric data "+$metlist.value.tostring() +" "+ $metlist.unit.tostring()+" will be pushed to cloudwatch")){
$metricdatalist.add($metlist)
Write-Verbose ("Metricname= " +$metlist.metricname+" Metric Value= "+ $metlist.value.tostring()+" Metric Units= "+$metlist.unit.tostring())
}
}
end{
$monputrequest.namespace = "System/Windows"
if ($metricdatalist.count -gt 0 ) {
$cwclient = New-Object Amazon.Cloudwatch.AmazonCloudWatchClient($accountinfo.AWSAccessKeyId,$accountinfo.AWSSecretKey,$cwconfig)
$monputrequest.metricdata = $metricdatalist
$monresp = $cwclient.PutMetricData($monputrequest)
Add-Member -Name "RequestId" -MemberType NoteProperty -Value $monresp.ResponseMetadata.RequestId -InputObject $response
}
else {throw "No metric data to push to CloudWatch exiting script" }
Write-Verbose ("RequestID: " + $response.RequestId)
}
}
### Pipelined call of fucntions that pushs metrics to cloudwatch.
get-memory | create-metriclist | put-instancemem
When I execute this manually, I am getting an output as shown below:
'UserAgent' is a ReadOnly property.
But when I try get-memory | create-metriclist
it is giving me the full details of memory and pagefile. Which means something is going wrong while trying to push to cloudwatch.
Can someone help me?
Aucun commentaire:
Enregistrer un commentaire