ブログ記事
で紹介している Lambda 関数
ブログ記事
で紹介している Lambda 関数
'use strict'; | |
const AWS=require('aws-sdk'); | |
const targetAccounts = [{ | |
accountName: "アカウント名", | |
accountId: "アカウントID", | |
remoteRole: "ロール名" | |
}]; | |
const NOTIFY_TO_SNS_TOPIC = "SNSトピックのARN"; | |
const MY_ACCOUNT_NAME = "自分のアカウント名"; | |
const ALERT_DAYS = 14; // アラート発生日, 終了日の何日前に警告するかを指定 | |
const S2MS = 1000; // sec -> mill sec への変換係数 | |
const SEC_OF_DAY = 24 * 60 * 60; // 1日の秒数 | |
/** | |
* リザーブドインスタンス期限切れの確認 | |
*/ | |
exports.handler = async (event) => { | |
console.log("start"); | |
console.log(JSON.stringify(event)); | |
// このアカウント | |
let ec2 = new AWS.EC2(); | |
let rds = new AWS.RDS(); | |
try { | |
console.log("check for own account"); | |
await checkReserveInstance(MY_ACCOUNT_NAME, ec2, rds); | |
} catch(err) { | |
console.error("error occured: " + JSON.stringify(err)); | |
console.log("continue..."); | |
} | |
// リモートアカウント | |
for (let accountInfo of targetAccounts) { | |
console.log("check for account " + accountInfo.accountName); | |
try { | |
// STS 経由でcredentialsを取得 | |
let credentials = await getSTSCredential(accountInfo); | |
let param = { | |
accessKeyId : credentials.AccessKeyId, | |
secretAccessKey : credentials.SecretAccessKey, | |
sessionToken: credentials.SessionToken, | |
}; | |
console.log("credentials from STS: " + JSON.stringify(param)); | |
ec2 = new AWS.EC2(param); | |
rds = new AWS.RDS(param); | |
await checkReserveInstance(accountInfo.accountName, ec2, rds); | |
} catch(err) { | |
console.error("error occured: " + JSON.stringify(err)); | |
console.log("continue..."); | |
} | |
} | |
const response = { | |
statusCode: 200, | |
body: JSON.stringify('success'), | |
}; | |
return response; | |
}; | |
async function getSTSCredential(accountInfo) { | |
const sts = new AWS.STS(); | |
let param = { | |
RoleArn: "arn:aws:iam::" + accountInfo.accountId + ":role/" + accountInfo.remoteRole, | |
RoleSessionName: "cross_account_by_lambda", | |
}; | |
console.log("getSTSCredential param: " + JSON.stringify(param)); | |
return new Promise(function(resolve, reject) { | |
sts.assumeRole(param, function(err, data) { | |
if (err) { | |
console.error("getSTSCredential: " + JSON.stringify(err)); | |
reject(err); | |
} else { | |
resolve(data.Credentials); | |
} | |
}); | |
}); | |
} | |
async function checkReserveInstance(accountName, ec2, rds) { | |
// EC2 | |
console.log("check EC2 RI"); | |
let endDates; | |
try { | |
endDates = await getEC2ReserveInstancesExpireDate(ec2); | |
console.log("EC2 end dates: " + JSON.stringify(endDates)); | |
await notifyReservedInstanceExpire(accountName, endDates); | |
} catch(err) { | |
console.error("error occured for EC2 expire check: " + JSON.stringify(err)); | |
} | |
// RDS | |
console.log("check RDS RI"); | |
try { | |
endDates = await getRDSReserveInstancesExpireDate(rds); | |
console.log("RDS end dates: " + JSON.stringify(endDates)); | |
await notifyReservedInstanceExpire(accountName, endDates); | |
} catch(err) { | |
console.error("error occured for RDS expire check: " + JSON.stringify(err)); | |
} | |
} | |
async function notifyReservedInstanceExpire(accountName, endDates) { | |
let nwDt = new Date(); | |
for (let cand of endDates) { | |
let diffDays = (cand.endDate - nwDt) / S2MS / SEC_OF_DAY; | |
if (diffDays <= ALERT_DAYS) { | |
console.log("will expire, id: " + JSON.stringify(cand)); | |
await notifyViaSNS(accountName, cand); | |
} | |
} | |
} | |
async function getEC2ReserveInstancesExpireDate(ec2) { | |
let result = []; | |
let instances = await getActiveEC2ReserveInstances(ec2); | |
console.log("active EC2 RIs:" + JSON.stringify(instances)); | |
for (let instance of instances.ReservedInstances) { | |
let res = { | |
resoruceType: "EC2", | |
reservedInstanceId: instance.ReservedInstancesId, | |
endDate: instance.End | |
} | |
result.push(res); | |
} | |
return result; | |
} | |
async function getActiveEC2ReserveInstances(ec2) { | |
let param = { | |
Filters: [{ | |
Name: "state", | |
Values: ["active"] | |
}], | |
}; | |
console.log("getActiveEC2ReserveInstances param: " + JSON.stringify(param)); | |
return new Promise(function(resolve, reject) { | |
ec2.describeReservedInstances(param, function(err, data){ | |
if (err) { | |
console.error("describeReservedInstances: " + JSON.stringify(err)); | |
reject(err); | |
} else { | |
resolve(data); | |
} | |
}); | |
}); | |
} | |
async function getRDSReserveInstancesExpireDate(rds) { | |
let result = []; | |
let instances = await getActiveRDSReserveInstances(rds); | |
console.log("active RDS RIs:" + JSON.stringify(instances)); | |
for (let instance of instances) { | |
let edDt = new Date(instance.StartTime); | |
edDt.setDate(edDt.getDate() + (instance.Duration / SEC_OF_DAY)); | |
let res = { | |
resoruceType: "RDS", | |
reservedInstanceId: instance.ReservedDBInstanceId, | |
endDate: edDt, | |
} | |
result.push(res); | |
} | |
return result; | |
} | |
async function getActiveRDSReserveInstances(rds) { | |
let result = []; | |
let data = await getRDSReserveInstances(rds); | |
for (let instance of data.ReservedDBInstances ) { | |
if (instance.State === "active") { | |
result.push(instance); | |
} | |
} | |
return result; | |
} | |
async function getRDSReserveInstances(rds) { | |
return new Promise(function(resolve, reject) { | |
rds.describeReservedDBInstances({}, function(err, data){ | |
if (err) { | |
console.error("describeReservedDBInstances: " + JSON.stringify(err)); | |
reject(err); | |
} else { | |
resolve(data); | |
} | |
}); | |
}); | |
} | |
async function notifyViaSNS(accountName, cand) { | |
const sns = new AWS.SNS(); | |
let param = { | |
Subject: "Reserved Instance will be expired soon.", | |
Message: | |
"Account Name: " + accountName + | |
"\nResource Type: " + cand.resoruceType + | |
"\nReserved Instance Id: " + cand.reservedInstanceId + | |
"\nEnd date: " + cand.endDate, | |
TopicArn: NOTIFY_TO_SNS_TOPIC, | |
}; | |
console.log("notifyViaSNS param: " + JSON.stringify(param)); | |
return new Promise(function(resolve, reject) { | |
sns.publish(param, function(err, data){ | |
if (err) { | |
console.error("publish: " + JSON.stringify(err)); | |
reject(err); | |
} else { | |
resolve(data); | |
} | |
}); | |
}); | |
} |