Kanji
・雲端架構工程師 / 自由職業 ・1993年出生 ・愛媛縣出身 / 現居東京都澀谷區 ・5年 AWS 實戰經驗 個人檔案詳情
目錄
自動化是 AWS Systems Manager 的一項功能,可讓您自動執行操作任務。
您可以使用 Runbook 自動執行各種操作,該 Runbook 定義了自動化執行的任務。
AWS 提供了許多 Runbook,因此您只需傳遞所需引數即可執行任務。
參考: Systems Manager Automation Runbook 參考 - AWS Systems Manager Automation Runbook 參考
您還可以定義自己的 Runbook,透過組合操作實施自定義處理,或呼叫多個 AWS 提供的 Runbook 來建立操作工作流程。
參考: Systems Manager 自動化操作參考 - AWS Systems Manager
AWS_DATA_PATH
/botocore/data
import os os.environ["AWS_DATA_PATH"] = f"{os.getcwd()}/botocore/data"
import os import sys import boto3 import botocore sys.path.insert(0, os.getcwd()) reload(botocore) reload(boto3)
/opt/python/botocore/data
import os os.environ["AWS_DATA_PATH"] = "/opt/python/botocore/data"
/opt/python
如下面的類方法所示,當在 boto3 中建立客戶端時,botocore 從 ${botocore 根目錄}/data 、 ~/.aws/models 和 AWS_DATA_PATH 載入模型檔案。
${botocore 根目錄}/data
~/.aws/models
參考:【如何將新的AWS服務與舊的boto3一起使用|DevelopersIO](https://dev.classmethod.jp/articles/use-new-aws-service-with-old-boto3)
botocore的安裝目錄具有以下模型檔案檔案結構:
# 【提示/解決方法】如何將最新的Boto3與SSM Automation (或Lambda )配合使用 botocore/data └── controltower └── 2018-05-10 ├── endpoint-rule-set-1.json.gz ├── paginators-1.json # List of available APIs └── service-2.json.gz
如果您設定 AWS_DATA_PATH 環境變數,則以最高優先順序載入最新的模型檔案。
參考: 配置 - Boto3 1.34.108 文件
但是,即使您設定此環境變數,boto3 和 botocore 本身也不是 SSM Automation 中的最新版本。
在 Lambda 中,Lambda 層中的模組優先,但在 SSM Automation 中,zip 檔案(附件)中的模組不具有優先順序。
在 SSM Automation 中,附件檔案被提取到“/tmp/${random string}”。以下輸出顯示了“sys.path”的 JSON 格式,其中附件中的檔案具有最低優先順序。
{ "sys path": [ "/var/task", "/opt/python/lib/python3.8/site-packages", "/opt/python", "/var/runtime", "/var/lang/lib/python38.zip", "/var/lang/lib/python3.8", "/var/lang/lib/python3.8/lib-dynload", "/var/lang/lib/python3.8/site-packages", "/opt/python/lib/python3.8/site-packages", "/opt/python", "/tmp/4aa790cf-cb0b-48b6-80f0-3ed7f82f228c-2024-03-17-07-15-31" ] }
sys.path
├── src │ ├── customer_script.py │ └── modules │ └── requirements.txt └── ssm.tf
ListHandler
import logging import json import os import sys from importlib import reload import boto3 import botocore AUTOMATION_NAME = "sample-automation" logger = logging.getLogger(__name__) class ListHandler(logging.Handler): def __init__(self): super().__init__() self.log_messages = [] log_format = json.dumps( { "logLevel": "%(levelname)s", "time": "%(asctime)s", "line": "%(filename)s: %(lineno)d", "message": "%(message)s", }, indent=2, ensure_ascii=False, ) self.formatter = logging.Formatter(log_format) def emit(self, record): self.log_messages.append(json.loads(self.format(record))) def handler(event, context): list_handler = ListHandler() logger.addHandler(list_handler) logger.setLevel(logging.DEBUG) os.environ["AWS_DATA_PATH"] = f"{os.getcwd()}/botocore/data" session = boto3.Session() logger.debug({"botocore paths": session._loader.search_paths}) sys.path.insert(0, os.getcwd()) reload(botocore) reload(boto3) logger.debug({"sys path": sys.path}) logger.debug({"current directory": os.getcwd()}) logger.debug({"child directory": os.listdir()}) logger.debug({"botocore path": os.path.dirname(botocore.__file__)}) logger.debug({"boto3 path": boto3.__file__}) logger.debug({"boto3 version": boto3.__version__}) logger.debug({"botocore path": botocore.__file__}) logger.debug({"botocore version": botocore.__version__}) try: client = session.client("controltower") logger.info(client.list_baselines()) except Exception as e: logger.error(f"Failed to list_baselines: {e}") raise Exception(list_handler.log_messages) # The max length of SSM Automation output is 100 KB (102400 characters) return list_handler.log_messages if __name__ == "__main__": handler(event={}, context={})
boto3 botocore
data "aws_caller_identity" "current" {} #tfsec:ignore:aws-s3-encryption-customer-key #tfsec:ignore:aws-s3-enable-bucket-encryption #tfsec:ignore:aws-s3-enable-bucket-logging resource "aws_s3_bucket" "bucket" { bucket = "sample-automation-${data.aws_caller_identity.current.account_id}" } resource "aws_s3_bucket_versioning" "bucket_versioning" { bucket = aws_s3_bucket.bucket.id versioning_configuration { status = "Enabled" } } resource "aws_s3_bucket_public_access_block" "bucket_public_access_block" { bucket = aws_s3_bucket.bucket.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } resource "null_resource" "install_requirements" { triggers = { always_run = "${timestamp()}" } provisioner "local-exec" { command = "pip install -r ${path.root}/src/modules/requirements.txt -t ${path.root}/src/modules" } } data "archive_file" "file" { depends_on = [ null_resource.install_requirements ] type = "zip" source_dir = "${path.root}/src/modules" output_path = "${path.root}/src/modules.zip" } resource "aws_s3_object" "object" { bucket = aws_s3_bucket.bucket.id key = "modules.zip" source = data.archive_file.file.output_path } resource "aws_ssm_document" "runbook" { depends_on = [ aws_s3_object.object ] name = "sample-automation" document_type = "Automation" document_format = "JSON" attachments_source { key = "S3FileUrl" name = "modules.zip" values = [ "https://${aws_s3_bucket.bucket.bucket}.s3.amazonaws.com/modules.zip" ] } content = jsonencode({ assumeRole = aws_iam_role.role.arn schemaVersion = "0.3" description = "TestBoto3VersionUp" mainSteps = [ { name = "TestBoto3VersionUp" action = "aws:executeScript" inputs = { Runtime = "python3.8" Handler = "handler" Script = "${file("${path.root}/src/customer_script.py")}" Attachment = "modules.zip" } } ] files = { "modules.zip" = { checksums = { sha256 = filesha256(data.archive_file.file.output_path) } } } }) } resource "aws_iam_role" "role" { name = "sample-automation-role" assume_role_policy = jsonencode({ Version = "2012-10-17", Statement = [ { Action = "sts:AssumeRole", Effect = "Allow", Principal = { Service = "ssm.amazonaws.com" } } ] }) managed_policy_arns = [ "arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole", "arn:aws:iam::aws:policy/AdministratorAccess" ] }
├── src │ ├── function │ │ └── main.py │ └── layer │ │ ├── python # Store the package file of Boto3 / Botocore by processing Terraform under this directory │ │ └── requirements.txt └── lambda.tf
import logging import os import sys import boto3 import botocore logger = logging.getLogger(__name__) def handler(event, context): logger.setLevel(logging.DEBUG) os.environ["AWS_DATA_PATH"] = "/opt/python/botocore/data" session = boto3.Session() logger.debug({"botocore paths": session._loader.search_paths}) logger.debug({"sys path": sys.path}) logger.debug({"botocore path": os.path.dirname(botocore.__file__)}) logger.debug({"boto3 path": boto3.__file__}) logger.debug({"boto3 version": boto3.__version__}) logger.debug({"botocore path": botocore.__file__}) logger.debug({"botocore version": botocore.__version__}) client = session.client("controltower") logger.info(client.list_baselines()) if __name__ == "__main__": handler(event={}, context={})
null_resource
data.archive_file
data "archive_file" "function" { depends_on = [null_resource.install_requirements] type = "zip" source_dir = "${path.root}/src/function" output_path = "${path.root}/src/function.zip" } resource "aws_lambda_function" "function" { function_name = "sample-function" filename = data.archive_file.function.output_path source_code_hash = data.archive_file.function.output_base64sha256 handler = "main.handler" runtime = "python3.11" role = aws_iam_role.role.arn # os.environ["AWS_DATA_PATH"] is OK even if you set the following environment variables # environment { # variables = { # AWS_DATA_PATH = "/opt/python/botocore/data" # } # } layers = [aws_lambda_layer_version.layer_version.arn] } resource "null_resource" "install_requirements" { triggers = { always_run = "${timestamp()}" } provisioner "local-exec" { command = "pip install -r ${path.root}/src/layer/requirements.txt -t ${path.root}/src/layer/python" } } data "archive_file" "layer" { depends_on = [null_resource.install_requirements] type = "zip" source_dir = "${path.root}/src/layer" output_path = "${path.root}/src/layer.zip" } resource "aws_lambda_layer_version" "layer_version" { layer_name = "sample-layer" filename = data.archive_file.layer.output_path compatible_runtimes = ["python3.11"] source_code_hash = data.archive_file.layer.output_base64sha256 } resource "aws_iam_role" "role" { name = "sample-lambda-role" assume_role_policy = jsonencode({ Version = "2012-10-17", Statement = [ { Action = "sts:AssumeRole", Effect = "Allow", Principal = { Service = "lambda.amazonaws.com" } } ] }) managed_policy_arns = [ "arn:aws:iam::aws:policy/AdministratorAccess" ] }