LoginSignup
13
11

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-09-06

スクリーンショット 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)
  })
}
13
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
11