以下の内容はhttps://tech.enechange.co.jp/entry/2025/05/15/213951より取得しました。


AWSのChatOps、Lambdaなしでも組める、組めるぞ

CTO室の岩本 (iwamot) です。

AWSでChatOpsしたく先行事例を調べたら、Lambdaを使う例がヒットしました。

しかし今なら、Amazon Q Developer in chat applications(旧称:AWS Chatbot)のカスタムアクションでLambdaをなくせそうな気がします。2023年11月に発表された機能です。

実際に試したところ、おおむね実用的な仕組みができました。

そこで今回は、AWSのChatOpsがLambdaなしで組めた話をご紹介します。

Lambdaを避ける理由

本題に入る前に、Lambdaを避ける理由に触れておきます。

ひとことで言うと「運用が楽になるから」です。Lambdaを使わなければ、ランタイムのEOLを気にせずに済みます。ChatOpsの対象が増えるほど、EOL対応の有無が作業コストに響いてくるはずです。

使用したサービスと全体構成

さて、今回実装した仕組みをご説明します。

使用した主なサービスは以下の3つです。

  • Amazon EventBridge:イベント検出とルーティング
  • Amazon SNS:通知の配信
  • Amazon Q Developer in chat applications:Slack通知とカスタムアクション実行

全体構成は以下のようになります。

  1. CodeDeployのデプロイ状態変更イベントをEventBridgeルールで検出
  2. 入力トランスフォーマーでイベントを変換し、SNSトピックにルーティング
  3. Amazon Q Developer in chat applicationsのSlackチャンネルに配信
  4. メッセージとカスタムアクションボタンをSlackに表示
  5. カスタムアクションを選択
  6. CodeDeployのコマンドを実行(例:トラフィックの再ルーティング、デプロイを停止してロールバック、タスクセット終了)

カスタムアクションの作成・紐づけ

Slackなどのチャットアプリケーションにカスタムアクションボタンを表示するには、カスタムアクションを作成する必要があります。

方法は以下の2つです。

ぼくは、IaCで管理したかったのでAPIを選びました。チャットアプリケーション上でカスタムアクションを作ると、AWS所有のリソースとなってしまうようです(ユーザー側のAWSアカウントには作られない)。

APIで作る場合は、カスタムアクションをチャットチャンネルに関連づける必要があります。

具体的には以下の通りです。

  1. CreateCustomAction API でカスタムアクションを作成
  2. AssociateToConfiguration API でチャットチャンネルに紐づけ

今回はTerraformで以下のように実装しました(長いので折りたたんでいます)。

カスタムアクションの定義

locals {
  custom_actions = {
    SwitchTasks = {
      button_text = "🔁 タスク入れ替え"
      criteria = [
        {
          operator      = "EQUALS"
          value         = "blue-green-deployment"
          variable_name = "ActionGroup"
        },
      ]
      variables = {
        "ActionGroup"  = "event.metadata.additionalContext.ActionGroup"
        "DeploymentId" = "event.metadata.additionalContext.DeploymentId"
        "Region"       = "event.metadata.additionalContext.Region"
      }
      command_text = "codedeploy continue-deployment --deployment-id $DeploymentId --region $Region --deployment-wait-type READY_WAIT"
    }

    RollbackDeployment = {
      button_text = "🔙 ロールバック"
      criteria = [
        {
          operator      = "EQUALS"
          value         = "blue-green-deployment"
          variable_name = "ActionGroup"
        },
      ]
      variables = {
        "ActionGroup"  = "event.metadata.additionalContext.ActionGroup"
        "DeploymentId" = "event.metadata.additionalContext.DeploymentId"
        "Region"       = "event.metadata.additionalContext.Region"
      }
      command_text = "codedeploy stop-deployment --deployment-id $DeploymentId --region $Region --auto-rollback-enabled true"
    }

    TerminateOldTask = {
      button_text = "🧹 元タスク終了"
      criteria = [
        {
          operator      = "EQUALS"
          value         = "blue-green-deployment"
          variable_name = "ActionGroup"
        },
      ]
      variables = {
        "ActionGroup"  = "event.metadata.additionalContext.ActionGroup"
        "DeploymentId" = "event.metadata.additionalContext.DeploymentId"
        "Region"       = "event.metadata.additionalContext.Region"
      }
      command_text = "codedeploy continue-deployment --deployment-id $DeploymentId --region $Region --deployment-wait-type TERMINATION_WAIT"
    }
  }
}

resource "awscc_chatbot_custom_action" "this" {
  for_each = local.custom_actions

  action_name = each.key
  attachments = [
    {
      button_text       = each.value.button_text
      criteria          = each.value.criteria
      notification_type = "Custom"
      variables         = each.value.variables
    },
  ]
  definition = {
    command_text = each.value.command_text
  }
  tags = [
    for key, value in merge(data.aws_default_tags.this.tags, { "Name" = each.key }) : {
      key   = key
      value = value
    }
  ]
}

チャットチャンネルへの紐づけ(null_resourceで実装)

# 今回は custom_action_groups = ["blue-green-deployment"] とする
variable "custom_action_groups" {
  description = "A list of custom action group names to associate specific AWS Chatbot actions with the specified Slack channel configuration."
  type        = list(string)
  default     = []
}

locals {
  action_group_map = {
    "blue-green-deployment" = ["SwitchTasks", "RollbackDeployment", "TerminateOldTask"]
  }

  selected_actions = distinct(flatten([
    for group in var.custom_action_groups :
    lookup(local.action_group_map, group, [])
  ]))

  action_arns = {
    for k, v in {
      for action in local.selected_actions :
      action => try(
        one([
          for id in data.awscc_chatbot_custom_actions.this.ids : id
          if endswith(id, "/${action}")
        ]),
        null
      )
    } : k => v if v != null
  }
}

resource "null_resource" "associate_actions" {
  for_each = local.action_arns

  triggers = {
    action_arn  = each.value
    chatbot_arn = aws_chatbot_slack_channel_configuration.this.chat_configuration_arn
  }

  provisioner "local-exec" {
    command = "aws chatbot associate-to-configuration --resource ${self.triggers.action_arn} --chat-configuration ${self.triggers.chatbot_arn} --region us-west-2"
  }

  provisioner "local-exec" {
    when    = destroy
    command = "aws chatbot disassociate-from-configuration --resource ${self.triggers.action_arn} --chat-configuration ${self.triggers.chatbot_arn} --region us-west-2"
  }
}

EventBridgeルール

variable "sns_topic_arn" {
  description = "The SNS topic to notify when the blue/green deployment is ready for validation."
  type        = string
  nullable    = false
}

variable "deployment_group_name" {
  description = "The name of the deployment group targeted by ChatOps."
  type        = string
  nullable    = false
}

variable "green_url" {
  description = "The URL for validating the green environment."
  type        = string
  nullable    = false
}

resource "aws_cloudwatch_event_rule" "this" {
  description    = null
  event_bus_name = "default"
  event_pattern = jsonencode(
    {
      detail = {
        deploymentGroup = [
          var.deployment_group_name,
        ]
        state = [
          "READY",
        ]
      }
      detail-type = [
        "CodeDeploy Deployment State-change Notification",
      ]
      source = [
        "aws.codedeploy",
      ]
    }
  )
  force_destroy       = false
  name                = "${var.deployment_group_name}-deployment-ready"
  name_prefix         = null
  role_arn            = null
  schedule_expression = null
  state               = "ENABLED"
  tags = {
    "Name" = "${var.deployment_group_name}-deployment-ready"
  }
}

resource "aws_cloudwatch_event_target" "this" {
  arn            = var.sns_topic_arn
  event_bus_name = "default"
  force_destroy  = false
  input          = null
  input_path     = null
  role_arn       = aws_iam_role.this.arn
  rule           = aws_cloudwatch_event_rule.this.name
  target_id      = "sns"

  input_transformer {
    input_paths = {
      "account"         = "$.account"
      "deploymentGroup" = "$.detail.deploymentGroup"
      "deploymentId"    = "$.detail.deploymentId"
      "region"          = "$.region"
      "time"            = "$.time"
    }
    input_template = replace(replace(
      jsonencode(
        {
          content = {
            description = trimspace( # diffが出るのを避ける
              <<-EOT
              *Deployment group:* <deploymentGroup>
              *Deployment ID:* <deploymentId>
              *Account:* <account>
              *Region:* <region>
              *Time:* <time>
            EOT
            )
            nextSteps = [
              "${var.green_url} で動作を確認します",
              "問題なければタスク入れ替えを、問題があればロールバックを実行します",
              "必要に応じて、元のタスクセットを終了します",
            ]
            textType = "client-markdown"
            title    = ":large_blue_circle::large_green_circle: デプロイの動作確認が可能になりました"
          }
          metadata = {
            additionalContext = {
              ActionGroup  = "blue-green-deployment"
              DeploymentId = "<deploymentId>"
              Region       = "<region>"
            }
            enableCustomActions = true
          }
          source  = "custom"
          version = "1.0"
        }
      ),
    "\\u003c", "<"), "\\u003e", ">") # 余計なエスケープを戻す
  }
}

これで、Slackに以下のような通知が表示されるようになりました。Slack上のカスタムアクションボタンだけで、Blue/Greenデプロイの操作が完結します。

残っている課題

ということで結構いい感じのソリューションなのですが、カスタムアクションボタンの並び順が制御できない点が課題です。

「🧹 元タスク終了」「🔙 ロールバック」「🔁 タスク入れ替え」のような微妙な表示順になるケースもあります。前掲のスクリーンショットは、たまたま狙いどおりに並んでいたのでした。

並び順さえ制御できれば、この方法はChatOpsの定番パターンとして十分に通用しそうです。AWSサポートには機能追加要望として伝えました。対応を期待しています。

まとめ:LambdaなしでもChatOpsが組めた

以上、Amazon Q Developer in chat applicationsのカスタムアクションを使ったChatOpsの例をご紹介しました。

Lambdaなしで組むと、運用が楽になります。ボタンの並び順が制御できない課題は残っていますが、許容できるなら実用に足るはずです。

この方法を活用し、ChatOpsの対象を増やしていきたいと考えています。みなさまも是非お試しください。




以上の内容はhttps://tech.enechange.co.jp/entry/2025/05/15/213951より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14