AWS Config マネージドルールの CloudFormation をスクレイピングする
2022.6.6
はじめに
AWS Config にはマネージドルールがあります。
AWS Config は、AWS マネージドルールを提供します。マネージドルールは、定義済みのカスタマイズ可能なルールであり、AWS リソースが一般的なベストプラクティスに準拠しているかどうかを評価するために AWS Config で使用します。
AWS Config マネージドルール – AWS Config
一つ一つコンソールで設定しても良いですが、大変なので CloudFormation を使うと楽になります。
更に便利な点として、Config マネージドルールの CloudFormation テンプレートを AWS が公開しています。
そちらを利用すれば、1 から CloudFormation テンプレートを書かなくて済みます。
Amazon S3 URL を選択し、テンプレート URL http://s3.amazonaws.com/aws-configservice-us-east-1/cloudformation-templates-for-managed-rules/THE_RULE_IDENTIFIER.template を入力します。
AWS CloudFormation テンプレートを使用した AWS Config マネージドルールの作成 – AWS Config
本記事では、その AWS が公開している CloudFormation テンプレートをスクレイピングする内容となっております。
スクレイピングするメリット
コンソールや CloudFormation で AWS のテンプレートを使ってマネージドルールを設定しても良いですが、ひとつ問題点があります。
「ルールを一つづつしか設定できない」ということです。
(Config 有効時に設定する場合はその限りではないです)
現在、Config のマネージドルールは 200 個を超えております。
さらに、Config は複数リージョンや複数アカウントに適用する場合が多いため、CloudFormation でまとめて設定しておくと導入の時間削減になります。
そのため、200 個を超えるマネージドルールの CloudFormation テンプレートダウンロード作業を短縮するためスクレイピング用いて自動化します。
マネージドルールの CloudFormation をスクレイピングする
バージョン
c:\Users>python -V Python 3.9.5
ディレクトリ
Sample
ディレクトリに、スクレイピングした CloudFormation(以下、CFn)テンプレートが格納されます。
C:. │ ├─Doc │ └─Sample │ └─Src └─main.py
プログラム実行後のディレクトリ構造
C:. │ README.md │ ├─Doc │ └─Sample │ access-keys-rotated.yaml │ account-part-of-organizations.yaml │ acm-certificate-expiration-check.yaml │ alb-desync-mode-check.yaml │ alb-http-drop-invalid-header-enabled.yaml │ alb-http-to-https-redirection-check.yaml │ alb-waf-enabled.yaml │ api-gw-associated-with-waf.yaml │ api-gw-cache-enabled-and-encrypted.yaml │ api-gw-endpoint-type-check.yaml │ └─Src main.py
プログラムコード
import glob import logging import os import re import requests from bs4 import BeautifulSoup from cfn_flip import flip, to_yaml, to_json from logging import getLogger import sys logger = getLogger(__name__) def ScrapingAWSDocument(): logger.debug(sys._getframe().f_code.co_name) re_delete_indent = re.compile(r'\n +') base_url = "https://docs.aws.amazon.com/config/latest/developerguide/" target_url = "https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html" r = requests.get(target_url) r.encoding = 'utf-8' soup = BeautifulSoup(r.text, 'lxml') link_list = soup.find(id="main-col-body").find_all("li") rules = [] for child in link_list: child_url = base_url + child.find("a").get('href')[2:] r2 = requests.get(child_url) r2.encoding = 'utf-8' soup2 = BeautifulSoup(r2.text, 'lxml') detail = soup2.find(id="main-col-body").p.text.strip() Identifier = soup2.find(id="main-col-body").b.parent.text.strip() detail = re_delete_indent.sub(' ', detail) logger.info(MoldingIdentifier(Identifier)) rules.append( { 'rule_name': soup2.h1.text, 'rule_detail': detail, 'url': child_url, 'Identifier': MoldingIdentifier(Identifier) } ) return rules def MoldingIdentifier(data): logger.debug(sys._getframe().f_code.co_name) return data[data.rfind(":")+1:].strip() def ScriptAWSConfigRule(Identifier): logger.debug(sys._getframe().f_code.co_name) target_url = f"http://s3.amazonaws.com/aws-configservice-us-east-1/cloudformation-templates-for-managed-rules/{Identifier}.template" r = requests.get(target_url) r.encoding = 'utf-8' return r.text def ConvertJSONToYAML(json_text): logger.debug(sys._getframe().f_code.co_name) return to_yaml(to_json(json_text)) def cd(): logger.debug(sys._getframe().f_code.co_name) os.chdir(os.path.dirname(__file__)) def main(): logger.debug(sys._getframe().f_code.co_name) rules = ScrapingAWSDocument() for i in rules: json = ScriptAWSConfigRule(i["Identifier"]) yaml = ConvertJSONToYAML(json) OutPutYamlRules(yaml, i["rule_name"]) def OutPutYamlRules(yaml, rule_name): logger.debug(sys._getframe().f_code.co_name) logger.info('output file' + rule_name) with open(f"../Doc/Sample/{rule_name}.yaml", mode="w", encoding="utf-8") as target: target.writelines(yaml) def ResultCheck(): logger.debug(sys._getframe().f_code.co_name) files = glob.glob("../Doc/Sample/*") print("CFn File Count "+str(len(files))) if __name__ == "__main__": logging.basicConfig(level=logging.INFO) cd() logger.debug(sys._getframe().f_code.co_name) main() ResultCheck()
解説
マネージドルールの一覧を取得する
今回は、マネージドルールで定義してあるものすべての CFn を取得するのでまずはマネージドルールの一覧情報をスクレイピングします。
現時点(2022/06/02)で実行すると 276 個のファイルが作成されます。
def ScrapingAWSDocument(): logger.debug(sys._getframe().f_code.co_name) re_delete_indent = re.compile(r'\n +') base_url = "https://docs.aws.amazon.com/config/latest/developerguide/" target_url = "https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html" r = requests.get(target_url) r.encoding = 'utf-8' soup = BeautifulSoup(r.text, 'lxml') link_list = soup.find(id="main-col-body").find_all("li") rules = [] for child in link_list: child_url = base_url + child.find("a").get('href')[2:] r2 = requests.get(child_url) r2.encoding = 'utf-8' soup2 = BeautifulSoup(r2.text, 'lxml') detail = soup2.find(id="main-col-body").p.text.strip() Identifier = soup2.find(id="main-col-body").b.parent.text.strip() detail = re_delete_indent.sub(' ', detail) logger.info(MoldingIdentifier(Identifier)) rules.append( { 'rule_name': soup2.h1.text, 'rule_detail': detail, 'url': child_url, 'Identifier': MoldingIdentifier(Identifier) } ) return rules def MoldingIdentifier(data): logger.debug(sys._getframe().f_code.co_name) return data[data.rfind(":")+1:].strip()
コード行番号 | 解説 |
---|---|
4 | マネージドルールの詳細があるページです、これにルール名前を URL に追加してスクレイピングします |
5 | マネージドルールの一覧があるページです、マネージドルール一覧をスクレイピングするために使用します |
12 | マネージドルールの名前を取得して URL を結合します |
25 | マネージドルールの識別子を取得して格納します |
32 | マネージドルールの詳細ページの識別子のみ取得します |
target_url
を、英語ページにしています。
理由としては、日本語ページだとルール名や識別子のフォーマットにばらつきが多いためです。
CloudFormation テンプレートを取得する
マネージドルールの識別子(Identifier)を対象 URL に変換してスクレイピングします。
テンプレートのページはシンプルなので抽出処理などは不要です。
サンプルページ:ACCESS_KEYS_ROTATED
def ScriptAWSConfigRule(Identifier): logger.debug(sys._getframe().f_code.co_name) target_url = f"http://s3.amazonaws.com/aws-configservice-us-east-1/cloudformation-templates-for-managed-rules/{Identifier}.template" r = requests.get(target_url) r.encoding = 'utf-8' return r.text
JSON 形式から YAML 形式に変換
JSON 形式が良い方は、本関数を削除して下さい。
合わせてファイル保存時の拡張子も変更してください。
YAML 形式のほうが読みやすいので変換処理を実施しています。
def ConvertJSONToYAML(json_text): logger.debug(sys._getframe().f_code.co_name) return to_yaml(to_json(json_text))
CFn ファイルを一つにまとめる場合は JSON 形式のまま結合処理を実施してから YAML 形式に変換する方が楽だと思います。
まとめ
手作業でやるのは大変なので誰かの参考になれば幸いです。
テックブログ新着情報のほか、AWSやGoogle Cloudに関するお役立ち情報を配信中!
Follow @twitter2021年新卒入社。インフラエンジニアです。RDBが三度の飯より好きです。 主にデータベースやAWSのサーバレスについて書く予定です。あと寒いのは苦手です。
Recommends
こちらもおすすめ
Special Topics
注目記事はこちら
データ分析入門
これから始めるBigQuery基礎知識
2024.02.28
AWSの料金が 10 %割引になる!
『AWSの請求代行リセールサービス』
2024.07.16