【Pandas】SecurityGroups の情報を Markdownに表示する Pythonスクリプトを書いてみた

2020.02.15

Pandas の 1.0.0から DataFrameを Markdown形式に出力 することができます。

データ処理ライブリのpandas 1.0.0がリリースされました!

今回はこの Markdown出力機能を利用して AWSの SecurityGroup情報を Markdownテーブルに表示するスクリプトを書いてみました。

なお、 SecurityGroup情報の Markdown出力 自体は他のSDKで実装・ブログ化がされているので N番煎じです。

AWS SDKを使ってSecurity GroupをMarkdownのテーブルで出力するスクリプトを書いてみた

目次

  1. 環境
  2. 書いてみた
  3. スクリプト
  4. おわりに
  5. 参考

環境

  • Python: 3.7.3
  • Jupyter Notebook: 6.0.3
  • Boto3: 1.11.9
  • Pandas: 1.0.0

書いてみた

Jupyter Notebook上で作成しました。 .ipynb ファイルとコード部分のみを取り出した .py ファイルを置いています。

python Sg.py を実行すると 以下のような出力になります。

スクリプト

Sg.py の内容をまとめたものを以下に載せます。

# ライブラリ インポート
import boto3
import pandas as pd

# boto3 client
client = boto3.client('ec2')

# 名前タグ取得用関数
def get_name_from_tags(tags):
    tags_filter = [t['Value'] for t in tags if t['Key'] == "Name"]
    if tags_filter:
        return tags_filter[0]
    else:
        return ""

# VPC ID <--> VPC Name 対応関係の情報取得
vpcs = client.describe_vpcs()['Vpcs']
df_vpcs = pd.DataFrame(
    [
        [vpc['VpcId'], get_name_from_tags(vpc['Tags'])]
        for vpc in vpcs
    ],
    columns=['VpcId', 'Name']
)

# VPC Name 取得用関数
def get_vpc_name(vpcid):
    df_filter = df_vpcs[df_vpcs['VpcId'] == vpcid]
    if df_filter.empty:
        return ''
    else:
        return df_filter.iloc[0]['Name']

# Security Groups 情報取得
sgs = client.describe_security_groups()['SecurityGroups']

# ## Security Groups 一覧
sgs.sort(key=lambda sg:sg['VpcId'])
df_sgs = pd.DataFrame(
    [
        [
            sg['GroupName'],
            sg['GroupId'],
            "{} ({})".format(sg['VpcId'], get_vpc_name(sg['VpcId'])),
            sg['Description']
        ]
        for sg in sgs
    ],
    columns=['GroupName', 'GroupId', "VPC", "Description"]
)
df_sgs.index = df_sgs.index + 1

# display
print('## SecurityGroups 一覧')
print(df_sgs.to_markdown())
print('')

# ## 各 Security Group Rules
# Security Group の名前取得用
def get_sg_name(sgid):
    df_filter = df_sgs[df_sgs['GroupId'] == sgid]
    if df_filter.empty:
        return ''
    else:
        return df_filter.iloc[0]['GroupName']

# Ip Protocol 表示用
def parse_ip_protocol(ip_protocol):
    if ip_protocol == '-1':
        return 'ALL'
    else:
        return ip_protocol

# Port Range 表示用
def parse_port_range(ip_protocol, from_port, to_port):
    if ip_protocol == '-1':
        return 'ALL'
    elif ip_protocol == 'tcp' or ip_protocol == 'udp':
        # TCP, UDP
        if from_port == to_port:
            return "{}".format(from_port)
        else:
            return "{} - {}".format(from_port, to_port)
    else:
        # ICMP, ICMPv6
        icmp_type = 'ALL' if from_port == -1 else from_port
        icmp_code = 'ALL' if to_port == -1 else to_port
        return "Type:{} Code:{}".format(icmp_type, icmp_code)

print('## 各 SecurityGroup ルール')
for sg in sgs:
    # get rules
    buffer = []
    for perms in sg['IpPermissions']:
        # ip protocol, port range
        ip_protocol = parse_ip_protocol(perms.get('IpProtocol'))
        port_range = parse_port_range(
            perms.get('IpProtocol'),
            perms.get('FromPort'),
            perms.get('ToPort')
        )
        for ip_range in perms['IpRanges']:
            buffer.append([
                ip_protocol,
                port_range,
                ip_range.get('CidrIp'),
                ip_range.get('Description')
            ])
        for ip_range in perms['Ipv6Ranges']:
            buffer.append([
                ip_protocol,
                port_range,
                ip_range.get('CidrIpv6'),
                ip_range.get('Description')
            ])
        for group in perms['UserIdGroupPairs']:
            group_id = group['GroupId']
            buffer.append([
                ip_protocol,
                port_range,
                "{} ({})".format(group_id, get_sg_name(group_id)),
                group.get('Description')
            ])
    df_rules = pd.DataFrame(
        buffer,
        columns=['IpProtocol', 'PortRange', 'Source', 'Description']
    ).sort_values(by=['IpProtocol', 'PortRange', 'Source']).reset_index(drop=True)
    df_rules.index = df_rules.index + 1
    # display
    print("### {} (vpc:{})".format(sg['GroupName'], get_vpc_name(sg['VpcId'])))    
    if df_rules.empty:
        print('- no rules')
    else:
        print(df_rules.to_markdown())
    print('')

おわりに

SecurityGroupsの現状把握をさくっと行いたいモチベーションで作ってみました。

今回は純粋な Markdownテーブルが欲しかったので print(df.to_markdown()) で出力しましたが、 形式にこだわらなければ、Jupyter Notebook 上で DataFrameを単に置くだけで表示してくれます。

▼ 参考: Jupyter Notebookと Boto3で AWS環境定義書を作成してみる

Jupyter Notebookと Boto3で AWS環境定義書を作成してみる

この記事が少しでもどなたかのお役に立てば幸いです。

参考