2
0

AWS LambdaからOCIリソースのAPI操作(Secrets Managerを使用)

Last updated at Posted at 2023-09-27

概要

AWS Lambdaを使ってOCIリソースのAPI操作(Computeの開始操作)を行いました。
サーバレスにすることによってEventbridge等を使った時刻起動やイベント起動等でOCIリソース操作可能になります。
また、OCI資格情報をSecret Managerを使用することによって資格情報をコード外に保存しています。

EC2を使った操作はこちらを参照ください。
AWS環境からOCIリソースのAPI操作(Secrets Managerを使用)

(トークンベース認証方法から通常のAPI認証に変更しました)

構成

image.png

設定手順

  • OCI資格情報をSecrets Managerへ配置
    以下の手順についてはAWS環境からOCIリソースのAPI操作(Secrets Managerを使用)を参照下さい。
    -OCI資格情報取得
    -OCI Compute OCID
    -Secrets Managerに資格情報を配置するための準備
    -Secrets Managerに資格情報を配置

  • Container Image作成
    Lambdaは250MBまでのサイズ制限があり、OCIの外部モジュールだけで250MBを超えます。
    そのため、EC2にてContainer Imageを作成、ECRにPushして使用します。

    • EC2にSecret Managerアクセス権限追加
      操作するEC2にアタッチされたIAMロールに対してポリシー"SecretsManagerReadWrite"を追加します。この作業は必須ではありませんが、ECR登録前にContainer Imageのテストを行う場合は必要です。
    • EC2内に以下のディレクトリ構造でファイルを配置します。
work directory
├── Dockerfile_ocivmstart
└── app
    ├── app.py
    └── requirements.txt
requirements.txt
boto3
oci
app.py
import oci
import boto3
from botocore.exceptions import ClientError
import ast

# Secretmanager
session = boto3.session.Session()
client = session.client(
    service_name='secretsmanager',
    region_name='ap-northeast-1'
    )

def handler(event, context):
    # Resource Request
    instance_ocid = event['instance_ocid']
    instance_action = event['instance_action']

    #OCI config
    get_secret_value_response = client.get_secret_value(
      SecretId= 'OCIuser'
      )
    secret_data = get_secret_value_response['SecretString']
    secret = ast.literal_eval(secret_data)
    fingerprint = secret['fingerprint']
    oci_api_key = secret['oci_api_key']
    user = secret['user']
    tenancy = secret['tenancy']
    region = secret['region']

    pem_prefix = '-----BEGIN RSA PRIVATE KEY-----\n'
    pem_suffix = '\n-----END RSA PRIVATE KEY-----'
    key_content = '{}{}{}'.format(pem_prefix, oci_api_key, pem_suffix)

    config = {
        "user": user,
        "key_content": key_content,
        "fingerprint": fingerprint,
        "tenancy": tenancy,
        "region": region
        }

    oci.config.validate_config(config)

    # VM action
    core_client = oci.core.ComputeClient(config, service_endpoint='https://iaas.【リージョン】.oraclecloud.com')

    instance_action_response = core_client.instance_action(
        instance_id=instance_ocid,
        action=instance_action)
    print(instance_action_response.data)

参考
Docs » API Reference » Core Services » ComputeClient
oci-python-sdk/examples/configuration_example.py

Dockerfile_ocivmstart.
FROM public.ecr.aws/lambda/python:3.8

COPY app/requirements.txt ${LAMBDA_TASK_ROOT}
RUN pip install -r requirements.txt

COPY app/app.py ${LAMBDA_TASK_ROOT}

CMD [ "app.handler" ]

準備できたらイメージ作成と実行を行います。

docker build -t ocivmstart . -f Dockerfile_ocivmstart
docker run --rm -p 9000:8080 ocivmstart

確認コマンドを実行して、OCI Computeが起動できたら成功です

curl -d '{"instance_ocid":"【ComputeのOCID】","instance_action":"START"}' http://localhost:9000/2015-03-31/functions/function/invocations

参考:
Lambda コンテナイメージをローカルでテストする

  • 作成したContainer ImageをECRにPush
    AWSコンソールからAmazon>リポジトリでリポジトリを作成を押下
    image.png
    リポジトリ名を入力してリポジトリを作成を押下
    image.png
    作成後プッシュコマンドの表示を押下
    image.png

表示されたコマンドをEC2で実施してContainer ImageをPush

Pushに成功すれば以下のようにイメージが表示されます。
image.png

  • Lambda作成
    作成前にLambdaにアタッチするロールを作成します。
    IAM>ロールの作成を押下
    image.png
    以下のポリシーを含むロールを作成
    AmazonEC2ContainerRegistryReadOnly
    AWSLambdaBasicExecutionRole
    SecretsManagerReadWrite
    image.png

Lambda>関数に移動し、関数の作成を押下
コンテナイメージを選択し、コンテナの場所を指定します。
また、実行ロールを先ほど作成したロールを指定して関数の作成を押下します。
image.png
作成完了しましたら基本設定の編集でタイムアウトを3秒から長い時間に変更します。
image.png

実行結果

正常に実行できるかLambdaのテスト機能を使って確認します。
image.png
イベントJSONに以下を入力してテストボタンを押下します。

{
  "instance_ocid":"【ComputeのOCID】",
  "instance_action":"START"
}

参考:
Python の Lambda 関数ハンドラー

正常完了しました。
image.png

OCIコンソールも確認して対象Computeが正常にスタートしたことを確認します。
Lambdaテスト実行前
image.png
Lambdaテスト実行後
Computeスタートしました。
image.png

サンプル2

OCIのVM以外にAutonomousDBやMySQL Database Serviceのリソースを開始/停止を行うサンプルです。

work directory
├── Dockerfile_ocivmstart
└── app
    ├── app.py
    └── requirements.txt
requirements.txt
boto3
oci
app.py
import oci
import boto3
from botocore.exceptions import ClientError
import ast

# Secretmanager
session = boto3.session.Session()
client = session.client(
    service_name='secretsmanager',
    region_name='ap-northeast-1'
    )

def handler(event, context):
    # Resource Request
    instance_type = event['instance_type']
    instance_ocid = event['instance_ocid']
    instance_action = event['instance_action']

    #OCI config
    get_secret_value_response = client.get_secret_value(
        SecretId= 'OCIuser'
        )
    secret_data = get_secret_value_response['SecretString']
    secret = ast.literal_eval(secret_data)
    fingerprint = secret['fingerprint']
    oci_api_key = secret['oci_api_key']
    user = secret['user']
    tenancy = secret['tenancy']
    region = secret['region']

    pem_prefix = '-----BEGIN RSA PRIVATE KEY-----\n'
    pem_suffix = '\n-----END RSA PRIVATE KEY-----'
    key_content = '{}{}{}'.format(pem_prefix, oci_api_key, pem_suffix)

    config = {
        "user": user,
        "key_content": key_content,
        "fingerprint": fingerprint,
        "tenancy": tenancy,
        "region": region
        }

    oci.config.validate_config(config)


    # VM action
    if instance_type == 'VM':
        core_client = oci.core.ComputeClient(config, service_endpoint='https://iaas.【リージョン】.oraclecloud.com')

        if instance_action == 'START':
            get_instance_response = core_client.get_instance(instance_ocid)

            if get_instance_response.data.lifecycle_state == 'STOPPED':
                instance_action_response = core_client.instance_action(
                    instance_id=instance_ocid,
                    action=instance_action)
                print(instance_action_response.data)

            else:
                print('VM START aborted')

        elif instance_action == 'STOP':
            get_instance_response = core_client.get_instance(instance_ocid)

            if get_instance_response.data.lifecycle_state == 'RUNNING':
                instance_action_response = core_client.instance_action(
                    instance_id=instance_ocid,
                    action=instance_action)
                print(instance_action_response.data)

            else:
                print('VM STOP aborted')

    # ADB action
    elif instance_type == 'ADB':
        database_client = oci.database.DatabaseClient(config, service_endpoint='https://database.【リージョン】.oraclecloud.com')

        if instance_action == 'START':
            get_autonomous_database_response = database_client.get_autonomous_database(
                autonomous_database_id=instance_ocid)

            if get_autonomous_database_response.data.lifecycle_state == 'STOPPED':
                start_autonomous_database_response = database_client.start_autonomous_database(
                    autonomous_database_id=instance_ocid)
                print(start_autonomous_database_response.data)

            else:
                print('ADB START aborted')

        elif instance_action == 'STOP':
            get_autonomous_database_response = database_client.get_autonomous_database(
                autonomous_database_id=instance_ocid)

            if get_autonomous_database_response.data.lifecycle_state == 'AVAILABLE':
                stop_autonomous_database_response = database_client.stop_autonomous_database(
                    autonomous_database_id=instance_ocid)
                print(stop_autonomous_database_response.data)

            else:
                print('ADB STOP aborted')

        else:
            pass

    # MDS action
    elif instance_type == 'MDS':
        mysql_client = oci.mysql.DbSystemClient(config, service_endpoint='https://mysql.【リージョン】.ocp.oraclecloud.com')

        if instance_action == 'START':
            get_db_system_response = mysql_client.get_db_system(
                db_system_id=instance_ocid)
            if get_db_system_response.data.lifecycle_state == 'INACTIVE':
                start_db_system_response = mysql_client.start_db_system(
                    db_system_id=instance_ocid)
                print(start_db_system_response.headers)

            else:
                print('MDS START aborted')

        elif instance_action == 'STOP':
            get_db_system_response = mysql_client.get_db_system(
                db_system_id=instance_ocid)
            if get_db_system_response.data.lifecycle_state == 'ACTIVE':
                stop_db_system_response = mysql_client.stop_db_system(
                    db_system_id=instance_ocid,
                    stop_db_system_details=oci.mysql.models.StopDbSystemDetails(
                        shutdown_type="IMMEDIATE")
                )
                print(stop_db_system_response.headers)

            else:
                print('MDS STOP aborted')

        else:
            pass

    # Unexpected Resource action
    else:
        pass

イベントJSONは以下のとおりとなります。

  • instance_type : VM/ADB/MDS
  • instance_ocid : リソースのOCID
  • instance_action : START/STOP

ADBを開始する場合

{
    "instance_type":"ADB",
    "instance_ocid":"【OCID】",
    "instance_action":"START"
}
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0