Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
11
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

Organization

AWSの使用料金をSlackに投稿するLambdaファンクション

スクリーンショット 2017-09-08 22.33.48.png

概要

  • CloudWatchのapiのListMetricsで課金情報が取得できるAWSのサービス一覧を取得して、それを元にapiのGetMetricStatisticsを呼ぶことで各AWSのサービス毎の料金を取得できる
  • AWSの管理画面から請求アラートを受け取るを有効にしておく必要がある

スクリーンショット 2017-09-07 1.01.11.png

  • Promiseを使ってるため、nodejsのバージョンは6.10を選択する

許可が必要なIAMポリシー

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": ["cloudwatch:ListMetrics", "cloudwatch:GetMetricStatistics"],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

ソースコード

  • 環境編SLACK_API_TOKENとPOST_CHANNELは任意の値を設定する
package.json
{
  "name": "estimated-charges",
  "version": "1.0.0",
  "main": "index.js"
  "license": "MIT",
  "dependencies": {
    "@slack/client": "^3.10.0",
    "aws-sdk": "^2.85.0",
    "moment": "^2.18.1"
  }
}
index.js
'use strict';

const WebClient = require('@slack/client').WebClient
const AWS = require('aws-sdk')
const moment = require('moment')

const cloudwatch = new AWS.CloudWatch({
  region: 'us-east-1',
  endpoint: 'http://monitoring.us-east-1.amazonaws.com'
})

const listMetrics = () => {
  return new Promise((resolve, reject) => {
    cloudwatch.listMetrics({ MetricName: 'EstimatedCharges' }, (error, data) => {
      if (error) {
        reject(error)
      } else {
        resolve(data)
      }
    })
  })
}

const getMetricStatistics = (serviceName, startTime, endTime) => {
  const dimensions = [
    { Name: 'Currency', Value: 'USD' }
  ]
  if (serviceName) {
    dimensions.push({ Name: 'ServiceName', Value: serviceName })
  }
  return new Promise((resolve, reject) => {
    const params = {
      MetricName: 'EstimatedCharges',
      Namespace: 'AWS/Billing',
      Period: 86400,
      StartTime: startTime,
      EndTime: endTime,
      Statistics: ['Maximum'],
      Dimensions: dimensions
    }
    cloudwatch.getMetricStatistics(params, (error, data) => {
      if (error) {
        reject(error)
      } else {
        resolve({ name: serviceName, data: data })
      }
    })
  })
}

exports.handler = (event, context, callback) => {
  listMetrics().then(data => {
    const now = moment().toISOString()
    const yesterday = moment(now).subtract(1, 'd').toISOString()
    const promises = data['Metrics'].map(metric => {
      return metric['Dimensions'][0]
    }).filter(dimension => {
      return dimension['Name'] == 'ServiceName'
    }).map(dimension => {    
      return dimension['Value']
    }).filter((serviceName, index, self) => {
      return self.indexOf(serviceName) === index
    }).map(serviceName => {
      return getMetricStatistics(serviceName, yesterday, now)
    })
    promises.unshift(getMetricStatistics(null, yesterday, now))
    Promise.all(promises).then(data => {
      const results = data.filter(result => {
        const datapoints = result.data['Datapoints']
        return (datapoints.length != 0 && datapoints[0]['Maximum'] != 0)
      })
      if (results.length == 0) {
        return
      }
      const fields = results.filter(result => {
        return result.name != null
      }).sort((result1, result2) => {
        const charge1 = result1.data['Datapoints'][0]['Maximum']
        const charge2 = result2.data['Datapoints'][0]['Maximum']
        return (charge2 - charge1)
      }).map(result => {
        const datapoint = result.data['Datapoints'][0]
        return { title: result.name, value: `$${datapoint['Maximum']}`, short: true }
      })
      const datapoint = results[0].data['Datapoints'][0]
      const client = new WebClient(process.env.SLACK_API_TOKEN)
      client.chat.postMessage(process.env.POST_CHANNEL, `AWS料金 $${datapoint['Maximum']}`, { attachments: [{ color: 'good', fields: fields }] }, (error, response) => {
        if (error) { console.error(error) }
      })
    }, reason => {
      console.error(reason)
    })
  }, reason => {
    console.error(reason)
  })
}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
11
Help us understand the problem. What are the problem?