【Scala】Scala ~ AWS SDK / Secrets Managerサンプル ~

https://dk521123.hatenablog.com/entry/2023/03/24/211033
https://dk521123.hatenablog.com/entry/2023/04/01/002005

の続き。

今回は、Scalaで、AWS SDK for Java を使ったSecrets Managerのサンプル集

目次

【0】build.sbt
【1】Secrets Managerをハンドリングする共通処理

【0】build.sbt

scalaVersion := "2.12.17"

name := "hello-world"
organization := "ch.epfl.scala"
version := "1.0"

libraryDependencies ++= Seq(
  "org.json4s" %% "json4s-jackson" % "4.1.0-M2",
  "org.yaml" % "snakeyaml" % "1.33",
  "org.scala-lang.modules" %% "scala-parser-combinators" % "2.1.1",
  "software.amazon.awssdk" % "s3" % "2.20.30",
  "software.amazon.awssdk" % "secretsmanager" % "2.20.30",
  "software.amazon.awssdk" % "ses" % "2.20.30"
)

【1】Secrets Managerをハンドリングする共通処理

import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse
import software.amazon.awssdk.services.secretsmanager.model.SecretsManagerException

import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider

import org.json4s._
import org.json4s.jackson.JsonMethods._

import java.net.URI

final case class SecretsManagerUtils(
  region: Region = Region.US_WEST_2,
  isDev: Boolean = false
) {
  val secretsClient = getClient(region, isDev)

  private def getClient(
    region: Region = Region.US_WEST_2,
    isDev: Boolean = false
  ): SecretsManagerClient = {
    if (isDev) {
      val accessKey = "dummy"
      val secretAccessKey = "dummy"
      val endpoint = "http://localhost:4566"
      val credentials = AwsBasicCredentials.create(accessKey, secretAccessKey)

      SecretsManagerClient.builder()
        .region(region)
        .credentialsProvider(StaticCredentialsProvider.create(credentials))
        .endpointOverride(new URI(endpoint))
        .build()
    } else {
      SecretsManagerClient.builder()
        .region(region)
        .build();
    }
  }

  def getSecretValue(secretName: String): String = {
    val secretValueResponse = getSecretValueResponse(secretName)
    if(secretValueResponse == null) {
      return ""
    }

    val secretValue = secretValueResponse.secretString()
    if (secretValue == null) "" else secretValue
  }

  def getSecretValueAsMap(secretName: String): Map[String, Any] = {
    val secretValue = getSecretValue(secretName)
    val secretJsonValue = parse(secretValue)
    // To Map. See https://www.javadoc.io/doc/org.json4s/json4s-core_2.9.1/3.0.0/org/json4s/JsonAST$$JObject.html
    secretJsonValue.asInstanceOf[JObject].values
  }

  def getSecretValueResponse(secretName: String): GetSecretValueResponse = {
    val secretValueRequest = GetSecretValueRequest.builder()
      .secretId(secretName)
      .build()
    secretsClient.getSecretValue(secretValueRequest)
  }
}

呼び出し側

import scala.collection.JavaConversions._
import scala.reflect._

import software.amazon.awssdk.regions.Region

object Main extends App {
  val region = Region.US_WEST_1
  val secretsManager = SecretsManagerUtils(region, true)
  val result = secretsManager.getSecretValue("app/local/DemoSecrets")
  println(s"result = ${result}") // result = {"CLIENT_SECRET_VALUE1":"Hello", "CLIENT_SECRET_VALUE2":"World" }
  val mapValues = secretsManager.getSecretValueAsMap("app/local/DemoSecrets")
  println(mapValues.get("CLIENT_SECRET_VALUE1").getOrElse("Other")) // Hello
  println(mapValues.get("CLIENT_SECRET_VALUE2").getOrElse("Other")) // World
  println(mapValues.get("CLIENT_SECRET_VALUE3").getOrElse("Other")) // Other
}

動作確認用データ

# Create Secret
$ aws --endpoint-url=http://localhost:4566 secretsmanager create-secret \
 --name "app/local/DemoSecrets" \
 --secret-string '{"CLIENT_SECRET_VALUE1":"Hello", "CLIENT_SECRET_VALUE2":"World" }'

# Get Secret
$ aws --endpoint-url=http://localhost:4566 secretsmanager get-secret-value \
    --secret-id app/local/DemoSecrets

{
    "ARN": "arn:aws:secretsmanager:us-west-1:000000000000:secret:app/local/DemoSecrets-TNEKYE",
    "Name": "app/local/DemoSecrets",
    "VersionId": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx",
    "SecretString": "{\"CLIENT_SECRET_VALUE1\":\"Hello\", \"CLIENT_SECRET_VALUE2\":\"World\" }",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2023-04-02T23:44:48.267328+09:00"
}

補足:JSONの処理

* 以下の関連記事を参照のこと。

ScalaJSON
https://dk521123.hatenablog.com/entry/2023/04/04/000733

関連記事

ScalaAWS SDK
https://dk521123.hatenablog.com/entry/2023/03/24/211033
ScalaAWS SDK / S3サンプル ~
https://dk521123.hatenablog.com/entry/2023/04/01/002005
ScalaAWS SDK / SESサンプル ~
https://dk521123.hatenablog.com/entry/2023/04/16/003338
Scala ~ 環境構築編 ~
https://dk521123.hatenablog.com/entry/2023/03/10/193805
Scala ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/12/184331
Scala ~ 基本編 / 繰り返し ~
https://dk521123.hatenablog.com/entry/2023/01/24/000000
Scala ~ 基本編 / Option型 ~
https://dk521123.hatenablog.com/entry/2023/03/09/000000
Scala ~ 基本編 / メソッド ~
https://dk521123.hatenablog.com/entry/2023/03/03/000000
Scala ~ 基本編 / クラス ~
https://dk521123.hatenablog.com/entry/2023/03/14/000857
Scala ~ 基本編 / コレクション ~
https://dk521123.hatenablog.com/entry/2023/03/13/000345
Scala ~ 基本編 / 日付・日時 ~
https://dk521123.hatenablog.com/entry/2023/03/08/000000
Scala ~ 基本編 / 正規表現
https://dk521123.hatenablog.com/entry/2023/03/18/034704
Scala ~ 基本編 / ジェネリック
https://dk521123.hatenablog.com/entry/2023/03/21/003817
Scala ~ ファイル名・パスの扱い ~
https://dk521123.hatenablog.com/entry/2023/03/11/000000
Scala ~ ファイルハンドリング ~
https://dk521123.hatenablog.com/entry/2023/01/03/000000
ScalaYAML
https://dk521123.hatenablog.com/entry/2023/03/16/012034
ScalaJSON
https://dk521123.hatenablog.com/entry/2023/04/04/000733
JavaでEmail ~ JavaMail / Text ~
https://dk521123.hatenablog.com/entry/2016/07/16/222422
JavaでEmail ~ JavaMail / 添付ファイル ~
https://dk521123.hatenablog.com/entry/2016/07/17/023459
JavaでEmail ~ SMTP認証 ~
https://dk521123.hatenablog.com/entry/2016/11/07/215251
JavaでEmail ~ SMTP認証 / DIGEST-MD5
https://dk521123.hatenablog.com/entry/2016/12/07/222229
JavaでEmail ~ JavaMail / TLS
https://dk521123.hatenablog.com/entry/2017/05/03/163219
JavaでEmail ~ JavaMail / Return-Path・Errors-To ~
https://dk521123.hatenablog.com/entry/2017/05/07/000344
Amazon SES ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2017/04/28/234103
Amazon S3 ~ Boto3編 ~
https://dk521123.hatenablog.com/entry/2019/10/21/230004
Amazon S3 ~ Boto3でファイル存在チェック ~
https://dk521123.hatenablog.com/entry/2022/02/26/182526
AWS Glue ~ Scalaでの実装 ~
https://dk521123.hatenablog.com/entry/2023/03/17/000000
Docker compose ~ LocalStack/Glue4.0 ~
https://dk521123.hatenablog.com/entry/2023/03/25/021432
AWS Glue ~ ローカル環境を作成する / Glue v3.0版 ~
https://dk521123.hatenablog.com/entry/2022/01/31/165650
LocalStack ~ ローカルで疑似AWSを作成する ~
https://dk521123.hatenablog.com/entry/2019/12/14/010524
LocalStack ~ ローカルで疑似Lambda/S3/DynamoDBを作成する ~
https://dk521123.hatenablog.com/entry/2019/12/16/231149