kakakakakku blog

kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Step Functions の新機能「変数」と「JSONata」を AWS と LocalStack で試す

2024年11月22日に AWS Step Functions で新機能「変数 (variables)」「JSONata」がサポートされた❗️

aws.amazon.com

特に「変数」は待望の機能なんじゃないかな〜と思う.今までは AWS Step Functions でタスク結果を引き回すときにどうしてもバケツリレーが必要で,正直 ResultSelector / ResultPath という構文も難しく(何度書いても忘れてしまう😇)ある程度複雑なワークフローを実装するときはデータシュミレーターがないと無理に思えるほどだった.個人的には AWS Step Functions は動くようになったら最高に便利だけどイメージ通りに実装するまでの過程が結構大変〜というイメージがある💨

変数と JSONata を試す

さっそく変数と JSONata を試してみる❗️今回は AWS Lambda 関数から出力された数値同士を「掛け算」するサンプルにする.実は今までの AWS Step Functions 組み込み関数では States.MathAdd を使った「足し算」しかサポートされてなく,過去に困った経験があった.

docs.aws.amazon.com

AWS Step Functions ワークフロー

以下のようなワークフローを作った.AWS Lambda 関数 returnXreturnY はランダムな整数を返して結果を「変数」設定し,最後の Pass では「JSONata」を使って returnXreturnY の値を計算(掛け算)する👌

ステートマシンは必要最低限にしていて,ポイントをザッと以下に挙げる📝

  • QueryLanguageJSONata を指定する
  • Assign を使って AWS Lambda 関数 returnXreturnY の結果 {% $states.result.body %} を変数に設定する
  • JSONata 式 {% $x * $y %} で計算(掛け算)する
{
  "QueryLanguage": "JSONata",
  "StartAt": "returnX",
  "States": {
    "returnX": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:000000000000:function:returnX",
      "Assign": {
        "x": "{% $states.result.body %}"
      },
      "Next": "returnY"
    },
    "returnY": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:000000000000:function:returnY",
      "Assign": {
        "y": "{% $states.result.body %}"
      },
      "Next": "multiplication"
    },
    "multiplication": {
      "Type": "Pass",
      "Output": {
        "multiplication": "{% $x * $y %}"
      },
      "End": true
    }
  }
}

AWS Lambda 関数 returnXreturnY の実装はシンプル💡

import json
import random

def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'body': random.randint(1, 100)
    }

ちなみに 0 ≤ n < 1 の範囲でランダムな値を取得するなら JSONata の $random() 関数で表現できてしまう.あくまで今回は AWS Lambda 関数で何かしらの処理を実行するサンプルとして見てもらえればと👌

docs.jsonata.org

実行結果

AWS Step Functions ワークフローを実行すると期待通りに「変数」「JSONata」を使えた❗️

注意点

今回のリリースと同時に今まで何度もお世話になったデータフローシュミレーターがメンテナンスされなくなっていた😇

LocalStack で変数と JSONata を試す

今回さらにスゴイのは LocalStack も同時に変数と JSONata をサポートしている点で,リリースノートにも LocalStack の紹介文が載っていた❗️

We have also partnered with LocalStack and Datadog to ensure that their local emulation and observability experiences are updated to support Variables and JSONata.

そして LocalStack 側のブログにも記事が出ている📝

blog.localstack.cloud

正確には LocalStack v4.0.1 でサポートされているため,今回は最新の LocalStack v4.0.2 で試す.

github.com

AWS Step Functions ワークフロー

以下の AWS SAM テンプレートを準備した.マネジメントコンソールで試した構成とほぼ同じ👌

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:
  FunctionReturnX:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: returnX
      CodeUri: ./src
      Handler: app.lambda_handler
      Runtime: python3.12
      Architectures:
        - x86_64
  FunctionReturnY:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: returnY
      CodeUri: ./src
      Handler: app.lambda_handler
      Runtime: python3.12
      Architectures:
        - x86_64
  StateMachine:
    Type: AWS::Serverless::StateMachine
    Properties:
      Name: sandbox
      DefinitionUri: ./sandbox.asl.json
      DefinitionSubstitutions:
        FunctionXArn: !GetAtt FunctionReturnX.Arn
        FunctionYArn: !GetAtt FunctionReturnY.Arn

ステートマシン sandbox.asl.json もほぼ同じで,AWS Lambda 関数名の ARN を AWS SAM テンプレートから渡すところが違う👌

{
  "QueryLanguage": "JSONata",
  "StartAt": "returnX",
  "States": {
    "returnX": {
      "Type": "Task",
      "Resource": "${FunctionXArn}",
      "Assign": {
        "x": "{% $states.result.body %}"
      },
      "Next": "returnY"
    },
    "returnY": {
      "Type": "Task",
      "Resource": "${FunctionYArn}",
      "Assign": {
        "y": "{% $states.result.body %}"
      },
      "Next": "multiplication"
    },
    "multiplication": {
      "Type": "Pass",
      "Output": {
        "multiplication": "{% $x * $y %}"
      },
      "End": true
    }
  }
}

次に LocalStack にデプロイすると LocalStack Resource Browser で確認できる❗️

$ samlocal build 
$ samlocal deploy

そして LocalStack 上の AWS Step Functions ワークフローを実行すると,{\"multiplication\":1344} と出力されていて期待通りに計算(掛け算)できている👏

最高だ〜 \( 'ω')/

$ awslocal stepfunctions start-execution --state-machine-arn arn:aws:states:ap-northeast-1:000000000000:stateMachine:sandbox
{
    "executionArn": "arn:aws:states:ap-northeast-1:000000000000:execution:sandbox:ac69989b-6569-4881-aea7-23de22d124ba",
    "startDate": "2024-11-28T00:00:00.000000+09:00"
}

$ awslocal stepfunctions describe-execution --execution-arn arn:aws:states:ap-northeast-1:000000000000:execution:sandbox:ac69989b-6569-4881-aea7-23de22d124ba
{
    "executionArn": "arn:aws:states:ap-northeast-1:000000000000:execution:sandbox:ac69989b-6569-4881-aea7-23de22d124ba",
    "stateMachineArn": "arn:aws:states:ap-northeast-1:000000000000:stateMachine:sandbox",
    "name": "ac69989b-6569-4881-aea7-23de22d124ba",
    "status": "SUCCEEDED",
    "startDate": "2024-11-28T00:00:00.000000+09:00",
    "stopDate": "2024-11-28T00:00:00.000000+09:00",
    "input": "{}",
    "inputDetails": {
        "included": true
    },
    "output": "{\"multiplication\":1344}",
    "outputDetails": {
        "included": true
    }
}

LocalStack Resource Browser でも実行結果を確認できた❗️(今のところ変数は表示されていなさそうだった)

関連記事

aws.amazon.com

関連ドキュメント

docs.jsonata.org

docs.aws.amazon.com

Terraform で Service-Linked Role(サービスにリンクされたロール)を作る

マネジメントコンソールを使ってリソースを設定していると自動的に「Service-Linked Role(サービスにリンクされたロール)」が作られていることがある💡実は AWS CloudFormation や Terraform を使って Service-Linked Role を IaC (Infrastructure as Code) で管理することもできて,今回はお試しとして Terraform の aws_iam_service_linked_role リソースを使って AWS Config の Service-Linked Role を作ってみる👌

👾 iam.tf

aws_iam_service_linked_role リソースの aws_service_name に AWS Config を表す config.amazonaws.com を設定しておけば OK👌 簡単❗️

resource "aws_iam_service_linked_role" "config" {
  aws_service_name = "config.amazonaws.com"
}

docs.aws.amazon.com

ちなみに Service-Linked Role をサポートしているサービス一覧は以下のドキュメントで確認できる📝

docs.aws.amazon.com

デプロイ確認

Terraform コードを apply すると Service-Linked Role AWSServiceRoleForConfig を追加できていた \( 'ω')/

AWS Security Hub と AWS Config

AWS Config には Service-Linked Role AWSServiceRoleForConfig もしくは マネージドポリシー AWS_ConfigRoleAWSConfigRole ではなく)をアタッチした IAM Role を使うとドキュメントには書いてある📝実際にポリシーを比較しても基本的には同じ設定になっていた.

docs.aws.amazon.com

しかし AWS Security Hub のコントロール [Config.1] AWS Config should be enabled and use the service-linked role for resource recording はデフォルトで Service-Linked Role AWSServiceRoleForConfig を使うことを要求していて,マネージドポリシー AWS_ConfigRole をアタッチした IAM Role を使っていると警告が出てしまう(includeConfigServiceLinkedRoleCheck パラメータで無効化することは可能).

よって,特殊な要件がなく AWS Security Hub に準拠するのであれば Service-Linked Role を使っておくのが良さそうだな〜と思った🤔

docs.aws.amazon.com

生成 AI パスポート試験(2024年 第3回)を受験してみた

2024年10月18日(受験可能期間は10月1日〜10月31日)に「生成 AI パスポート試験(2024年 第3回)」を受験して合格した👌試験自体は IBT (Internet Based Testing) だけど結果はすぐに出ず,2024年11月18日に発表された.個人的に結構イイ試験だな〜と感じたので「試験の紹介を目的として」出題内容には触れず簡単に振り返っておこうと思う.

guga.or.jp

試験概要

「生成 AI パスポート試験」はその名前の通り,最近よく話題になる「生成 AI」の基礎知識を問う試験になっていて,ウェブサイトには「生成 AI リスクを予防する資格試験」と表現されていたりもする.よって,少なくともこのぐらいは知っておくと良いぞ〜というポイント(歴史・技術・活用テクニックなど)を理解していることを証明できる💡

出題範囲(2024年 第3回 前提)

出題範囲はシラバス(生成 AI パスポート シラバス 8.03 最新確定版)に詳しく載っていて,ある程度理解していればシラバスを見ると「あ〜こういう感じか〜」と把握できると思う.ちなみにシラバスを見て「ちょっと古いのでは...?」と感じる人もいると思っていて,実は次回開催からはシラバスが改訂されるため,それに関しては記事の最後に紹介する❗️

  • 第1章: AI(人工知能)
  • 第2章: 生成 AI(ジェネレーティブ AI)
  • 第3章: 現在の生成 AI(ジェネレーティブ AI)の動向
  • 第4章: 情報リテラシー・基本理念とAI社会原則
  • 第5章: テキスト生成 AI のプロンプト制作と実例

あと試験としては合格率が高く難易度は低めだと思う.

  • 2024年 第2回(合格率 78.76%)
  • 2024年 第3回(合格率 75.76%)

guga.or.jp

guga.or.jp

なぜ受験したか

自分自身はソフトウェアエンジニアとして日々生成 AI を活用していて,ある程度の理解と体験があるためシラバスを見て基本的にはわかるかな〜という感じだった.よって,資格試験の受験者層とは少し外れている可能性はあるけど,今後もっと生成 AI が普及するためには重要な資格なのかと考えて,どんな資格試験なのか実際に体験しておきたいというモチベーションがあった💪

試験対策

書籍(テキスト・問題集)や研修・試験対策コンテンツなど十分に揃っていると思う.

今回は「生成 AI パスポート公式テキスト 第2版(現在 Amazon で買えるのは第3版だけど読んだのは第2版)」「生成 AI パスポート テキスト & 問題集」をザッと読んだ📕内容的には「公式テキスト」の方が詳しく解説されているけど,あくまでテキスト(教科書)なので,模擬テストを解きながら勉強するなら「テキスト & 問題集」の方が良いと思う.内容的には似ていて,両方読む必要はないかな〜という印象だった.

生成AIパスポート テキスト&問題集

生成AIパスポート テキスト&問題集

  • 日本能率協会マネジメントセンター
Amazon

あとは「おとなもこどもも知りたい 生成 AI の教室」も読んだ📕たまたま子供がこの本を読んでいて,読ませてもらったけど,結構「生成 AI パスポート試験」の出題範囲とカブっていておすすめ❗️小学生でも読める本だからイメージしやすく,本格的な試験対策に入る前に読んでおくと良いと思う.

他に Transformer / GAN / LSTM などの理解は YouTube「スタビジ」の解説動画を観た😀

www.youtube.com

さらにポッドキャスト「Misreading Chat」は普段から聴いていて,そういえば過去に GAN / VAE / Stable Diffusion 関連の論文紹介エピソードがあったな〜と思い出して,改めて聴き直したりもした👂️

misreading.chat

misreading.chat

misreading.chat

ザッとこんな感じ❗️よって,直接的に試験対策をするというよりは,出題範囲で理解が浅いな〜と感じた部分を中心に学び直していた感じだった \( 'ω')/

試験当日

試験自体は IBT で自宅から受験できる👌受験可能期間が1ヶ月間もあって,時間調整しやすいのは良かった.「IBT 受験に必要な機器および受験環境」「禁止事項」はウェブサイトに載っている📝

guga.or.jp

僕はよく AWS 認定試験・Kubernetes 認定資格・Terraform 認定試験などを受験していて,オンライン受験には慣れているため,普段と同じ環境で受験した.オペレーターに監視されることはなかった.

出題範囲(2025年2月以降)

実は次回開催(2025年2月)から出題範囲が改訂されるとアナウンスされている💡シラバス(シラバス改定 2025年2月試験版)も公開されていて,例えば GPT-4o / Claude / Gemini / Sora なども出題範囲になっていたり,AI 事業者ガイドライン(第1.0版)前提で改訂されていてイイ❗️試験自体の鮮度が保たれていることは重要だと思う.

www.meti.go.jp

さらに「生成 AI パスポート試験」の良いところに「生成 AI パスポート 資格更新テスト」という仕組みがあって,新シラバスに沿った知識を証明するために問題数を減らして再受験することができる👌

guga.or.jp

まとめ

個人的にイイ試験だと思う❗️受験して良かった.もし興味あるぞ〜って思ったら次回開催(2025年2月)を申し込んでみると良いじゃないでしょうか😀(申し込み期限は2025年1月31日📅)

1点改善点を挙げるなら IBT なのに合否発表まで遅すぎるのでもっと早く発表されると良いな〜とは思う.

AWS CDK で Cognito User Pools の「メッセージテンプレート」を設定する

Amazon Cognito User Pools から自動的に送信される「招待メッセージ」はデフォルトでは以下のようにシンプルな設定になっていて,実運用では使いにくさがある😇 あと末尾の . までコピーしてログインしようとして「エラーになります」って言われることもよくあったりする🔥

emailSubject:
Your temporary password
emailBody:
Your username is {username} and temporary password is {####}.

そこで「メッセージテンプレート」を使うとある程度ならメールテンプレートをカスタマイズできる✉️

docs.aws.amazon.com

今回は AWS CDK でメッセージテンプレートを実装してみた❗️aws_cognito.UserPooluserInvitation を設定すれば OK👌

docs.aws.amazon.com

ちなみにメール本文はマークアップに対応してて,改行する場合は <br /> を使う.

import {
    Stack,
    StackProps,
    aws_cognito,
} from 'aws-cdk-lib'
import { Construct } from 'constructs'

export class CognitoMessageTemplateStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props)

        new aws_cognito.UserPool(this, 'UserPool', {
            userPoolName: 'sandbox',
            signInAliases: {
                email: true,
            },
            userInvitation: {
                emailSubject: 'kakakakakku sandbox: Your temporary password',
                emailBody: `Thank you for signing up to kakakakakku sandbox \( 'ω')/<br />
Your username is {username} and temporary password is {####}`,
            }
        })
    }
}

デプロイして再度確認するとカスタマイズしたメッセージテンプレートになっていた👏

crossRegionReferences: AWS CDK で Cognito User Pools にカスタムドメインを設定する

Amazon Cognito User Pools にカスタムドメインを設定する場合,内部的に追加される Amazon CloudFront で「バージニア北部リージョン (us-east-1)」の AWS Certificate Manager (ACM) 証明書が必要になる📝

しかし AWS CDK(もしくは AWS CloudFormation)ではクロスリージョンスタックリファレンス(リージョン間のクロススタックリファレンス)ができず,実装するときに AWS Systems Manager Parameter Store を使ったり,Props に静的に ARN (Amazon Resource Names) を設定したり,何かしら考慮が必要になるという課題がある.

docs.aws.amazon.com

ちなみに Amazon Cognito User Pools のカスタムドメインに限った話ではなく,例えば Amazon CloudFront と Amazon S3 を組み合わせる場合にも出てくる仕様になる.

docs.aws.amazon.com

実際に試すと以下のエラーが出る🔥

Cross stack references are only supported for stacks deployed to the same environment or between nested stacks and their parent stack.

crossRegionReferences プロパティ

現在まだ実験的機能 (experimental) ではあるけど,AWS CDK の crossRegionReferences プロパティを使うとイイ感じにクロスリージョンスタックリファレンス(リージョン間のクロススタックリファレンス)を実現できる👌

仕組みをザックリと説明すると,AWS CloudFormation のカスタムリソース (AWS Lambda 関数) で us-east-1 リージョンの AWS Certificate Manager 証明書 ARN を ap-northeast-1 リージョンの AWS Systems Manager Parameter Store に登録して,ap-northeast-1 リージョンの AWS CloudFormation スタックでは AWS Systems Manager Parameter Store の動的参照を使って AWS Certificate Manager 証明書 ARN を取得する.ザックリではあるけどアーキテクチャ図も描いておいた❗️

詳細な仕組みは AWS CDK ドキュメントを参照してもらえればと📝

docs.aws.amazon.com

サンプルコード

動作確認に使ったサンプルコードを載せておく👌

👾 bin/cdk.ts

ここで crossRegionReferences プロパティを設定している❗️そして AWS Certificate Manager 証明書 ARN を CognitoCustomDomainStack に渡している.

const envVirginia = {
    account: '000000000000',
    region: 'us-east-1',
}

const envTokyo = {
    account: '000000000000',
    region: 'ap-northeast-1',
}

const cognitoCustomDomainAcmStack = new CognitoCustomDomainAcmStack(app, 'CognitoCustomDomainAcmStack', {
    env: envVirginia,
    crossRegionReferences: true,
})

new CognitoCustomDomainStack(app, 'CognitoCustomDomainStack', {
    env: envTokyo,
    crossRegionReferences: true,
    certificate: cognitoCustomDomainAcmStack.certificate,
})

👾 cognito-custom-domain-acm.ts

AWS Certificate Manager 証明書を作ってクロススタックで取得できるようにしておく.今回は userpool.xxxxx.net というカスタムドメインにした📝

import {
    Stack,
    StackProps,
    aws_certificatemanager,
    aws_route53,
} from 'aws-cdk-lib'
import { Construct } from 'constructs'

export class CognitoCustomDomainAcmStack extends Stack {
    public readonly certificate: aws_certificatemanager.Certificate

    constructor(scope: Construct, id: string, props: StackProps) {
        super(scope, id, props)

        const hostedZone = aws_route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
            hostedZoneId: 'XXXXX',
            zoneName: 'xxxxx.net',
        })

        this.certificate = new aws_certificatemanager.Certificate(this, 'Certificate', {
            domainName: 'userpool.xxxxx.net',
            validation: aws_certificatemanager.CertificateValidation.fromDns(hostedZone),
        })
    }
}

👾 cognito-custom-domain.ts

AWS Certificate Manager 証明書を ARN を StackProps から取得して,Amazon Cognito User Pools のカスタムドメインに設定する👌

import {
    Stack,
    StackProps,
    aws_certificatemanager,
    aws_cognito,
    aws_route53,
    aws_route53_targets,
} from 'aws-cdk-lib'
import { Construct } from 'constructs'

interface CustomStackProps extends StackProps {
    certificate: aws_certificatemanager.Certificate
}

export class CognitoCustomDomainStack extends Stack {
    constructor(scope: Construct, id: string, props: CustomStackProps) {
        super(scope, id, props)

        const userPool = new aws_cognito.UserPool(this, 'UserPool', {
            userPoolName: 'sandbox',
        })

        const userPoolDomain = userPool.addDomain('UserPoolDomain', {
            customDomain: {
                domainName: 'userpool.xxxxx.net',
                certificate: props.certificate,
            },
        })

        const hostedZone = aws_route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
            hostedZoneId: 'XXXXX',
            zoneName: 'xxxxx.net',
        })

        new aws_route53.ARecord(this, 'ApexARecod', {
            zone: hostedZone,
            target: aws_route53.RecordTarget.fromIpAddresses('8.8.8.8'),
        })

        new aws_route53.ARecord(this, 'UserPoolARecord', {
            zone: hostedZone,
            recordName: 'userpool',
            target: aws_route53.RecordTarget.fromAlias(new aws_route53_targets.UserPoolDomainTarget(userPoolDomain)),
        })
    }
}

デプロイ確認

実際にデプロイした結果(既にリソースは削除済)を抜粋して載せておく👌

👇️ us-east-1 リージョンに AWS Certificate Manager 証明書を登録できた

👇️ ap-northeast-1 リージョンの AWS Systems Manager Parameter Store に AWS Certificate Manager 証明書 ARN が登録されていた

👇️ ap-northeast-1 リージョンの Amazon Cognito User Pools に us-east-1 リージョンの AWS Certificate Manager 証明書が登録されていた

まとめ

AWS CDK の crossRegionReferences プロパティを使って,Amazon Cognito User Pools にカスタムドメインを設定してみた.イイ感じにクロスリージョンスタックリファレンス(リージョン間のクロススタックリファレンス)を実現できて便利だった❗️

しかし現状ではまだ「実験的機能 (experimental)」でプロダクション環境では採用しにくく,最近似たような構成を仕事で実装したときは crossRegionReferences プロパティは使わずに独自に実装をした.正式リリースを待つぞー \( 'ω')/

参考リンク

AWS CloudFormation のカスタムリソース実装は以下にある🔗

github.com

github.com