GASでAWS APIを呼び出して、セキュリティグループをシートに反映しよう
この記事の概要
これを待ってたんだろ?
参考にした記事
こちらの記事を参考に、セキュリティグループ版を作りました。
必要なもの
AWSのアクセスキー・シークレットアクセスキー
→ IAMユーザーに適切な権限をつけて発行して下さい。
やってみよう
1. スプレッドシートを作成・シートの名前決め
結果を出力するためのシート名もつけておきましょう。
2. コードのコピペ
シートメニュー
> 拡張機能
> Apps Script
を開いてファイルを作成していきます。
ファイル名は任意ですが、一応載せておきます。
- aws.gs
下のサイトaws.js
をコピーして貼り付けて下さい。
- credentials.gs
コード(クリックして開く)
// Grobal Vars
let AWS_ACCESS_KEY_ID
let AWS_SECRET_ACCESS_KEY
// AWS Credentials
const keypair = {
'demo': {
'id': 'AKIAXXXXXXXXXXXXXXXXXX',
'secret': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
},
'prod': {
'id': 'AKIAXXXXXXXXXXXXXXXXXX',
'secret': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
}
}
// set credentials
function setAwsCredentials(env){
console.log('env name -> '+env)
AWS_ACCESS_KEY_ID = keypair[env].id
AWS_SECRET_ACCESS_KEY = keypair[env].secret
AWS.init(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
}
- awsresourcectl.gs
コード(クリックして開く)
// セキュリティグループの情報をlistで返却する。
function EC2DescribeSecurityGroups(envname) {
setAwsCredentials(envname)
const res = AWS.request(
'ec2',
'ap-northeast-1',
'DescribeSecurityGroups',
{ Version: '2016-11-15' },
)
// ステータスコードと返却されたxmlを格納
const code = res.getResponseCode()
const text = res.getContentText()
// xmlのパース
if (code < 200 || code >= 300) throw Error(`AWS.request failed: ${code} - ${text}`)
const root = XmlService.parse(text).getRootElement()
const ns = root.getNamespace()
const securitygroups = root.getChild('securityGroupInfo', ns).getChildren()
const resultlist = []
securitygroups.forEach(securitygroup => {
const groupId = securitygroup.getChild('groupId', ns).getText()
const groupName = securitygroup.getChild('groupName', ns).getText()
ippermissions = securitygroup.getChild('ipPermissions', ns).getChildren()
ippermissions.forEach(ippermission => {
const ipProtocol = (ippermission.getChild('ipProtocol', ns) ? ippermission.getChild('ipProtocol', ns).getText() : 'ー')
const fromPort = (ippermission.getChild('fromPort', ns) ? ippermission.getChild('fromPort', ns).getText() : 'ー')
const toPort = (ippermission.getChild('toPort', ns) ? ippermission.getChild('toPort', ns).getText() : 'ー')
// 許可sg群
const groups = ippermission.getChild('groups', ns).getChildren()
// 許可ip群
const ipranges = ippermission.getChild('ipRanges', ns).getChildren()
// sgを許可しているパターン
groups.forEach(group => {
const srcgroupId = (group.getChild('groupId', ns) ? group.getChild('groupId', ns).getText() : 'ー')
const description = (group.getChild('description', ns) ? group.getChild('description', ns).getText() : 'ー')
resultlist.push([groupId, groupName, ipProtocol, fromPort, toPort, srcgroupId, description])
})
// ipを許可しているパターン
ipranges.forEach(iprange => {
const cidrIp = (iprange.getChild('cidrIp', ns) ? iprange.getChild('cidrIp', ns).getText() : 'ー')
const description = (iprange.getChild('description', ns) ? iprange.getChild('description', ns).getText() : 'ー')
resultlist.push([groupId, groupName, ipProtocol, fromPort, toPort, cidrIp, description])
})
})
})
return {'list':resultlist, 'collength':resultlist[0].length, 'rowlength':resultlist.length}
}
- spreadsheetctl.gs
コード(クリックして開く)
// CONST VALUE
const STARTROW = 1
const STARTCOL = 2
const COLUMNNAMES = ['Security Group ID','Security Group Name','Protocol','from Port','to Port','Access Source','Description']
// シートにセキュリティグループの情報を反映する。
function dispSecurityGroupRuletoSheet(sheetname, env){
// INIT
const sheet = SpreadsheetApp.getActive().getSheetByName(sheetname)
// 既存情報のクリア + カラム名のセット
sheet.clearContents()
sheet.getRange(STARTROW,STARTCOL,1,COLUMNNAMES.length).setValues([COLUMNNAMES])
// 情報取得 + 反映
const sginfo = EC2DescribeSecurityGroups(env)
sheet.getRange(STARTROW+1, STARTCOL, sginfo.rowlength, sginfo.collength).setValues(sginfo.list)
}
- main.gs
コード(クリックして開く)
// シートにカスタムメニュー追加
function onOpen() {
customMenulist = []
customMenulist.push({name: 'デモ:sg情報をシートに反映', functionName: 'dispDemoSecurityGroupRuletoSheet'})
customMenulist.push({name: '本番:sg情報をシートに反映', functionName: 'dispProdSecurityGroupRuletoSheet'})
SpreadsheetApp.getActiveSpreadsheet().addMenu('カスタム',customMenulist)
}
// シートにセキュリティグループの情報を反映(デモ)
function dispDemoSecurityGroupRuletoSheet(){
dispSecurityGroupRuletoSheet('demo_sg','demo')
}
// シートにセキュリティグループの情報を反映(本番)
function dispProdSecurityGroupRuletoSheet(){
dispSecurityGroupRuletoSheet('prod_sg','prod')
}
3. 実行してみよう
スプレッドシートをリロードして再読み込みすると
カスタムメニューが表示されているはずです。
メニューを選択すると、初回のみ "承認が必要" 的な文言が出ます。
アカウントを選択して、許可を押せばOKです。これ以降は聞かれません。
コードのちょっとした説明
xmlのパースとテストのやり方
xmlのパースを理解すれば、改変や応用は容易だと思います。
// ステータスコードと返却されたxmlを格納
const code = res.getResponseCode()
const text = res.getContentText()
このtextを一旦セル等に吐き出して構造を理解する
→ <item>
のブロックをforEachで回す、値をgetChild('値', ns).getText()
で取ればだいたい行けます。
あまり本番のデータをいじりたくない場合、は別リージョンにテスト用のsgを作ると良いです。
その場合、awsresourcectl.gs
で指定したap-northeast-1
を別リージョンのものに指定するのをお忘れなく。
nullの場合だけデフォルト値を代入
また、nullの場合は別の値を入れたい という場合
↓のような書き方が使えるようです。
// valがnullだったらnone、nullじゃなかったらvalをresultに入れる
const result = ( val ? val : "none" )
今回はdescriptionがnullの場合がありえると思って使いました。
途中からヤケクソで連発していますが。。
データの一括挿入
gasはセルの範囲を二次元配列として扱います。
その特性上、setValues()
で二次元配列を渡せば一発でデータの挿入が出来ます。
但し、getRange()
の大きさと二次元配列の大きさが一致している必要があるので
適宜length
などで動的に範囲を取るようにしています。
// 情報取得 + 反映
const sginfo = EC2DescribeSecurityGroups(env)
sheet.getRange(STARTROW+1, STARTCOL, sginfo.rowlength, sginfo.collength).setValues(sginfo.list)
Discussion