Skip to content

Instantly share code, notes, and snippets.

@junichim
Last active December 7, 2019 13:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save junichim/4620049b6ce4c96ba36606c151b3af5b to your computer and use it in GitHub Desktop.
Save junichim/4620049b6ce4c96ba36606c151b3af5b to your computer and use it in GitHub Desktop.
リザーブドインスタンスの有効期限切れを通知するためのスクリプト
'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);
}
});
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment