AWS re:invent参加にあたって知っておいたほうが少し快適になること

本稿は、ZOZO Advent Calendar 2023 シリーズ 6 2日目の記事です。

AWS re:invent 2023に参加してきました。 弊社からは7名が参加し、うち2人は昨年のre:invent2022にも参加したメンバーでした。 私も2回目の参加であり、初参加メンバーの疑問点に答えたりアドバイスをする機会が多々ありました。

会社としての参加ブログは別途投稿されますが、今回はre:inventに関して社内で一緒に参加したメンバーにしたアドバイスや個人的に知っておいたほうがいいと感じた内容をまとめます。 なにも知らない状態でre:inventに参加すると知らないワードがいろいろ出てきたりとハードルが高いので、もし参加する機会があればこの記事を参考にしてもらえればと思います。

この記事はre:invnentに参加するまでの工程すべてを網羅しているわけではないです。 要所要所で使えるtipsなどをまとめた記事であるため、実際に行かれる場合はほかの方の記事も参考にしていただければと思います。

そもそもre:invnetとはなにか

AWS re:InventはAWSが主催するクラウドコンピューティングに関連するカンファレンスです。 AWSの製品発表などを含む基調講演や、AWSのサービスを使ったハンズオンラボ、AWSのパートナー企業による展示ブースなどがあります。 2012年から毎年ラスベガスで開催されており、今年で12回目を迎えます。

reinvent.awsevents.com

事前に知っておいたほうがいい、調べておいたほうがいい編

初めてで海外に不安があるならばツアーに参加するのが良い

re:inventはラスベガスの会場で開催されます。そもそも日本国外であり、旅慣れていない場合にはかなりハードルが高いです。 近畿日本ツーリストさんが例年ツアーを開催しており、ツアーに参加すると飛行機の手配やホテルの手配などがすべてセットになっているので、初めての参加者にはおすすめです。 移動の際にも添乗員さんがついてくださり空港などでのアクシデントの際にも安心です。

knt.reinventjapantour.com

昨年はツアーを利用してre:inventに参加し、ある程度土地勘がついたため今年は個人手配で参加しました。

事前勉強会が開催される

Connpassなどで調べると事前勉強会が多数開催されています。 事前に今までのアップデートのインプットができるだけでなく、参加者同士の交流の場にもなります。

都合がつけば行くのをおすすめします。

セッションの予約

re:inventではたくさんのセッションが開催されます。 セッションは事前に公開され、一覧が申し込んだポータルサイトから見れるようになっています。 予約開始は適宜メールで連絡が来ますが10月末ごろから始まります。 気になるセッションはお気に入り登録して予約開始を待ちましょう。

またここでセッション以外に「breakfast」「lunch」と検索して出てくる「Monday lunch」などのセッションもお気に入り登録しておくと良いです。 後ほどセッションと一緒にカレンダーでお気に入りしたセッション一覧が表示されるためランチなどの時間を確認できます。

セッションの予約開始は深夜になるので本気で参加したいセッションがある場合は頑張って予約するほうがいいです。 予約の埋まってしまったセッションは「walk-up」という形で当日参加できる場合がありますが、セッション開始前に会場へ行って並ぶ必要があります。 大半のセッションはwalk-upに並ぶことで入れるので予約できなかったからといって落ち込む必要はありません。

今回自分は朝起きてから予約をしようとしたら予約が埋まっていたセッションが多かったのであまり予約できていない状態で参加しましたが、walk-upで大半のセッションに参加できたので問題なかったです。

情報が集まるSNSをフォローする

re:inventに関する情報はいろいろな方が発信してくださっています。 自分は以下のFacebookのグループに参加し、様々な情報を得ていました。

www.facebook.com

イベント情報やセッション予約の開始情報、ラスベガスの情報などが集まっているので、参加するならフォローしておくと良いと思います。 今年もとてもお世話になりました。

準備編

現地でのインターネット

会場にはWi-Fiがあり、かなり早いためインターネットには困りません。 しかしながら会場外ではWi-Fiがないため個人で調達する必要があります。

いつの間にか空港からSIMの自販機はなくなっており、インターネットを利用するには現地のSIMを契約するか、日本でモバイルルーターをレンタルするかのどちらかになります。 端末がeSIMに対応しているのであれば個人的にはeSIMの購入をおすすめしています。 eSIMとはインターネット経由でSIMが買える機能であり、最近の端末であれば物理のSIMとは別に設定できます。

自分は会社の先輩に教えてもらったAiraloというサービスを利用してeSIMを購入しました。 eSIMであればモバイルルータを持ち歩く必要もなく、かなり安価にインターネットを利用できるのでおすすめです。

www.airalo.com

今回は30日5GBで$16の契約にしました。 リファラルコードがあるのでもしよければ使ってもらえると嬉しいです。

KAZUYO7955

スーツケースには余裕を持たせる

re:inventに参加するとたくさんのお土産をもらえます。 持ち帰るのが大変なほどもらえるので、スーツケースには余裕を持たせておくと良いです。 自分は半分開けてたスーツケースが帰りには埋まっていました。

ホテルに給湯器はない

ラスベガスのホテルには共通して部屋に給湯器がありません。 聞いたところの話によるとできるかぎり部屋からでてカジノで楽しんでほしいから、などの戦略のようです。

部屋で持参のインスタント食品やコーヒーを飲みたい場合にはお湯を沸かす手段がないためロビーに電話して持ってきてもらうか給湯器を持っていくの2択になります。 ロビーに頼むとチップなどもありすこし大変なため自分は折り畳みの給湯器を購入して持っていきました。

購入する際には日本と違って120Vの電圧に対応できるものを購入する必要があるので注意してください。

AWSイベントアプリを入れる

AWSが公式で出しているAWSのイベントアプリがあります。 アプリを利用することでセッション情報の確認、予約だけでなく、会場内の案内やスケジュールの確認などができます。

様々な情報が集まっているので、参加するならば必須のアプリです。

移動編

日本を出てからラスベガスに着くまでの移動の間で知っておいたほうがいいことをまとめています。

入国審査について

多くの方が入国審査に関して英語で何を聞かれるか不安に思っていると思います。 re:invnentに参加する際はかなり簡単です。 「何の目的で来たのか」を聞かれた場合には「AWSのカンファレンスに参加するため」と答えればOKです。 入国審査官が「AWS ?」って聞いてくるぐらいにはre:invent参加者が空港に溢れているため「I'll go to AWS conference」で通じます。

バッジのピックアップ

空港でre:inventに参加するための名札をピックアップできます。 ピックアップには「AWSイベントアプリに表示されるQRまたは参加メアドの入力」と「パスポート」が必要になります。 事前にイベント申し込みページから顔写真をアップロードしていない場合はここで撮影された写真を名札に印刷してくれます。 飛行機の長時間移動後の疲れ切った写真になってしまうため可能ならば事前にアップロードしておくのがおすすめです。

re:invent現地編

会場について

会場は複数のホテルにまたがっています。 会場の移動はシャトルバス、または歩きの移動がメインとなり、どちらもかなり時間がかかります。 セッションの都合でホテルを移動する必要がある場合はかなり余裕を持って移動する必要があります。

会場が広いためシャトルバスでも歩きでも移動には40分程度かかるものとして自分は計算していました。

シャトルバスの時間はアプリで確認できます。 行くべき部屋等々は「Ask me」と書かれたシャツを着ているスタッフさんに聞くと助けてくれます。

Keynoteについて

Keynoteは基調講演のことであり、月曜日のみ夜、火水木は朝に開催されます。 基調講演では新製品の発表などなど盛りだくさんの内容であり、とても盛り上がるためせっかくreinventに来たのであれば参加することをおすすめします。 基調講演は席の予約ができないため、いい席へ行きたい場合は早めに会場に行って並ぶ必要があります。

遅く行ったとしてもいい席に案内されることもある
基調講演のみ同時通訳が行われています。会場を入って右手に同時通訳レシーバを受け取る場所があるため受け取るのをおすすめします。

SWAG

re:inventに参加するとたくさんのお土産をもらえます。 このお土産のことをSWAGと呼びます。 SWAGはメインホールの交換所でもらえるものと、各会場のブース、イベントでもらえるものがあります。 例年確実にもらえるものとして以下のようなものがあります。

| SWAG | 条件 | | --- | --- | | パーカー | メインホールの交換所に行く | | 水筒 | メインホールの交換所に行く | | 認定者Tシャツ | AWSの資格を持った状態でメインホールの交換所に行く |

全員がもらえるパーカー

EXPO

EXPOは展示ブースのことであり、AWSのパートナー企業がブースを出しています。 SWAGをもらえるホールの隣で開催されます。入れる時間が決まっているのでイベントアプリで時間を調べていくようにしましょう。 各社の紹介を聞いたりSWAGをもらえたりできます。

個人的におすすめのEXPOブースはAWSのブースです。直接各サービスを作っているエンジニアと話して質問できたりオリジナルアイテム、AWSのバウチャーをもらえたりします。

セッションの参加について

アプリで会場を調べて行くことになります。 予約していた人はセッション開始の10分前まで優先的に入場でき、10分前を過ぎると予約が解放され、walk-upの人も入場できるようになります。 自分は予約していればぎりぎりでも大丈夫だろうと思っていたのですが予約が解放されてwalk-upの方で埋まってしまい参加できなかったセッションがあったため同じミスをされる方が今後この記事を機に減ることを願います。

walk-upに並ぶ風景

ご飯について

re:invent会場では朝ごはんとお昼御飯がでます。 セッションの予約の欄にて「lunch」などのセッションをお気に入り登録したほうがよいと書きましたが、ここで表示される時間内にMeal会場へ行くとご飯をもらえます。

その場で食べる形式と「Grab and Go」というお弁当形式の2種類があります。 Grab and Goはお弁当をもらって会場外で食べることができます。セッションが詰まっている際などにはこちらがおすすめです。

各会場のホテルごとに食事の内容が少し違うらしいのです。気になった方はいろいろな会場に行ってみると良いと思います。

朝ごはん@wynn
ごはん会場風景

re:play とは

会場でちらほら聞くことになる「re:play」とは何なのか。 re:inventの実質最終日である木曜日の夜に行われるイベントの名前です。 アーティストの生ライブやDJを見ることができたりご飯が食べられたりします。

replay会場
replayイベント風景
replayごはん
注意点として「かばんの持ち込みが不可」「屋外なので寒い」などあります。 詳細な注意事項は会場内に水曜日あたりから出現する立て看板にあるのでそちらを参考にしてください。 勉強抜きで思いっきり楽しむイベントです。会場への移動はシャトルバスになり、各ホテルから運行されています。 運行時間等はこれもAWSイベントアプリで確認できます。
replay注意書きの看板
また参加するとTシャツのSWAGが受け取れるので忘れずに受け取るようにしましょう。

あとがき

AWS re:inventに2回目の参加をしていろいろと思ったTipsのようなものをまとめました。 最初に書いたように要所要所で使えるtipsなどをまとめた記事であるため、実際に行かれる場合はほかの方の記事も参考にしていただければと思います。

また思い出すこと等あれば適宜アップデートしていきます。 ここまでご覧いただきありがとうございました。

更新履歴

日付 内容
2023/12/02 初版公開

Datadogで使用量情報をAdmin権限以外でも見れるようにする

Datadogを使用していた際にメンバーの「どれくらい使用しているかわからないんだよね〜」というつぶやきを見かけました。使用量がわからないと思わぬ課金が発生していてもすぐ気づけ無い等の問題があるためDatadogのカスタムロールを用いてUsageの見れる権限を作成して対応しました。

本記事ではDatadogno使用量(Plan & Usage)を見れるロールを作ることを題材にDatadogのカスタムロールの作成方法を紹介します。また自分の利用方法では複数のDatadog Organizationがあり、いちいち手作業でカスタムロールを作成するのは大変だったのでPythonでカスタムロールの作成をスクリプト化し、自動化をしました。こちらのスクリプトについても紹介します。

今回使用する「カスタムロール」機能はEnterprise版の機能になりますのでご注意ください。

カスタムロールの作成と変更は、オプトイン Enterprise 機能です。ご使用のアカウントでこの機能を有効にしたい場合は、Datadog のサポートチームにお問い合わせください。*1

Datadogの権限について

Datadogの権限については以下のページにまとまっています。デフォルトで存在する権限は「Datadog 管理者ロール」「Datadog 標準ロール」「Datadog 読み取り専用ロール」の3つです。これらの権限をメンバーに付与することで権限管理をできますが、より柔軟に権限を振りたい場合には「カスタムロール」を用いて権限を作る必要があります。

docs.datadoghq.com

「管理者」しかPlan & Usageページに表示される「使用状況レポート」にアクセスできず、メンバーがDatadogの使用量を意識しにくいという課題がありました。

docs.datadoghq.com

ここでカスタムロールを作成してメンバー誰でもPlan & Usageページを見れるようにしてハッピーになろうぜ、というのが今回の記事になります。

カスタムロール

datadogの提供するアクセス許可を組み合わせてオリジナルのアクセス権限を作成することができます。アクセス許可の種類については以下のページにまとまっています。

docs.datadoghq.com

カスタムロールの作り方

カスタムロールの作成は以下のページを参考に作成しました。

docs.datadoghq.com

カスタムロールの有効化

カスタムロールはEnterprise版のオプトイン機能になります。 事前にカスタムロールの機能を有効化するために問い合わせページやサポートの方にカスタムロールを有効化したい旨を伝え有効化してもらいましょう。

手動のロールの作成

  1. Datadog Roles ページに移動
  2. ページ右上隅 New Role を選択
  3. ロールに名前をつける
    • 今回は「BillingReadRole」という名前にしました
  4. 権限の編集
    • 名前の下に付与される権限一覧が表示されます
      • デフォルトでReadに関する権限がいくつか付与される
    • 追加でBillingとUsageを見るための権限にチェック
      • Billing Read
      • Usage Read
  5. 保存

これで無事にカスタムロールが作成できました🙌

ロール作成の自動化 with Python

何個かDatadogのOrganizationを持っていると同一のロールをすべてのOrganizationで作りたいなどの要件が出てきます。 そんなときには何かしらの自動化手段を考えたいものです。

DatadogのRoleを管理する際には「terraformを用いて管理する案」や「API経由で作成する案」があります。今回はPythonAPI経由で作成に挑戦しましたのでそちらを共有します。

1. 操作するアカウントのAPIキーを取得する

PythonからDatadogを操作するには API Key と Application Key が必要になります。

以下の手順でそれぞれ取得し、どこかにメモします。これらの情報が流出してしまうと自由にDatadogを操作できてしまうため管理には気をつけてください。

  1. Organization Setting を開く
  2. ACCESS -> API Keys からキーを作成し保存
  3. ACCESS -> Application Keys からキーを作成し保存
2. Python実行用の環境作成

Pythonで操作するにあたり、Datadog社の公開している datadog-api-client-python を利用していきます。

  1. ライブラリのインストール
    pip install datadog-api-client
  2. アクセス情報を環境変数にセット
    • デフォルトでこのライブラリは DD_API_KEYDD_APP_KEY に書かれたAPI Key とApplication Keyを用いてDatadogにアクセスします
      1. で保存したキーを環境変数に保存しましょう
    • また時折 DD_SITE なる環境変数を求められることがありました。こちらもセットしておきましょう
export DD_API_KEY=<取得したAPIキー>export DD_APP_KEY=<取得したAPPキー>export DD_SITE=datadoghq.com
3. 付与する権限のIDを調べる

APIリファレンス > ロール: List roles のサンプルコードを用いて手動で作成した BillingReadRoleにはどのような権限がついているか確認していきます。Python実行用の環境作成でセットした認証情報をもとにDatadogにログインされます。list_role を実行することで ROLE_DATA_ATTRIBUTES_NAME のロール名についているPermissionを見ることができます。

サンプルがそのままだと動かなかったので少しだけ修正しています。

# from os import environ
from datadog_api_client.v2 import ApiClient, Configuration
from datadog_api_client.v2.api.roles_api import RolesApi

# there is a valid "role" in the system
ROLE_DATA_ATTRIBUTES_NAME = "<BillingReadRole 作成したロール名>"

configuration = Configuration()
with ApiClient(configuration) as api_client:
    api_instance = RolesApi(api_client)
    response = api_instance.list_roles(
        filter=ROLE_DATA_ATTRIBUTES_NAME,
    )

    print(response)
❯ python3 ./list_roles.py
{'data': [
  {'attributes':
    {
      'created_at': datetime.datetime(2022, 2, 18, 11, 51, 54, 42646, tzinfo=tzutc()),
      'modified_at': datetime.datetime(2022, 2, 18, 11, 51, 54, 89124, tzinfo=tzutc()),
      'name': 'BillingReadRole',
      'user_count': 1
    },
    'id': '01234567-hoge...',
    'relationships': {
      'permissions': {
        'data': [
          {'id': 'd90f6830-d3d8-11e9-a77a-b3404e5e9ee2', 'type': 'permissions'},
          {'id': '46a301df-ec5c-11ea-aa9f-970a9ae645e5', 'type': 'permissions'},
          ....

data として id が出ていますがこのままではなんのPermissionなのかわかりません... 別途Permissionの内容を見るAPIリファレンス > ロール: List Permissionを実行してみましょう。

from datadog_api_client.v2 import ApiClient, Configuration
from datadog_api_client.v2.api.roles_api import RolesApi

configuration = Configuration()
with ApiClient(configuration) as api_client:
    api_instance = RolesApi(api_client)
    response = api_instance.list_permissions()

    print(response)
❯ python3 ./list_permission.py 
{'data': [
    {
        'attributes': {
            'created': datetime.datetime(2018, 10, 19, 15, 35, 23, 734317, tzinfo=tzutc()),
            'description': 'Deprecated. Privileged Access (also '
                'known as Admin permission) has been '
                'replaced by more specific '
                'permissions: Access Management, Org '
                'Management, Billing Read/Write, '
                'Usage Read/Write.',
            'display_name': 'Privileged Access',
            'display_type': 'other',
            'group_name': 'General',
            'name': 'admin',
            'restricted': False
        },
        'id': '984a2bd4-d3b4-11e8-a1ff-a7f660d43029',
        'type': 'permissions'
    },
    {
        'attributes': {
            'created': datetime.datetime(2018, 10, 19, 15, 35, 23, 756736, tzinfo=tzutc()),
            'description': 'View and edit components in your '
                'Datadog organization that do not '
                'have explicitly defined permissions. '
                'This includes configuring events, '
                'facets (except logs), and saved '
                'views.',
            'display_name': 'Standard Access',
            'display_type': 'other',
            'group_name': 'General',
            'name': 'standard',
            'restricted': False
            },
        'id': '984d2f00-d3b4-11e8-a200-bb47109e9987',
        'type': 'permissions'
    },
    .... 省略
}

どうやら id と紐づく attributes が存在し、権限の名前や説明を設定しているようです。BillingReadRole についていた 46a301df-ec5c-11ea-aa9f-970a9ae645e5 のidを出力されたjsonから検索すると以下が見つかりました。

description: View your organization's subscription and payment method but not make edits.
display_name: Billing Read

カスタムロールを作成し、付与したい権限のIDを与えることで目的の権限を作成することができそうです。idについては github 上で調べたところ同じ値が数多く見つかり、Organization毎に異なるものでは無いようでした。

4. ロールの作成と権限の付与

APIリファレンス > ロール: Create role を用いてカスタムロールを作成していきます。リンク先のサンプルコードでは権限の何もついていないロールを作成していますが、やってみたところ権限付与もCreateRoleと同時に実行可能でした。

以下のサンプルでは「Billing Read」「Usage Read」の権限を付けた権限を「billing-read-role」の名前でカスタムロールを作成しています。実行後には作成したカスタムロールのidを表示しています。

from datadog_api_client.v2 import ApiClient, Configuration
from datadog_api_client.v2.api.roles_api import RolesApi
from datadog_api_client.v2.model.permissions_type import PermissionsType
from datadog_api_client.v2.model.relationship_to_permission_data import \
  RelationshipToPermissionData
from datadog_api_client.v2.model.relationship_to_permissions import \
  RelationshipToPermissions
from datadog_api_client.v2.model.role_create_attributes import \
  RoleCreateAttributes
from datadog_api_client.v2.model.role_create_data import RoleCreateData
from datadog_api_client.v2.model.role_create_request import RoleCreateRequest
from datadog_api_client.v2.model.role_relationships import RoleRelationships
from datadog_api_client.v2.model.roles_type import RolesType

createRoleBody = RoleCreateRequest(
  data=RoleCreateData(
    type=RolesType("roles"),
    attributes=RoleCreateAttributes(
      name="billing-read-role"
    ),
    relationships=RoleRelationships(
      users=None,
      permissions=RelationshipToPermissions(
        data=[
          RelationshipToPermissionData(
            # Billing Read
            id="46a301df-ec5c-11ea-aa9f-970a9ae645e5",
            type=PermissionsType("permissions")
          ),
          RelationshipToPermissionData(
            # Usage Read
            id="46a301e1-ec5c-11ea-aa9f-afa39f6f3e36",
            type=PermissionsType("permissions")
          ),
        ]
      )
    ),
  )
)

def main():
  configuration = Configuration()
  with ApiClient(configuration) as api_client:
    api_instance = RolesApi(api_client)
    try:
      response = api_instance.create_role(body=createRoleBody)
    except Exception as e:
      print(e)
      return
    print(response["data"]["id"])

if __name__ == '__main__':
  main()

実行することでRelationshipToPermissionDataとして記載した権限のついたカスタムロールを作成することができます。API KeyとAPP Keyを入れ替えることで各アカウントに同一のロールを作成することができます。

まとめ

作成したカスタムロールをユーザに付与することで適切に権限を管理することができます。 今回はPlan & Usage を管理者以外が見れないことを不便に感じてカスタムロールを作成しました。 この記事を読んだ方でこんな感じでカスタムロール使用しているよ〜、こんな感じでロールの管理しているよ〜などあればコメントでご紹介いただきたいです。

ここまで読んでくださりありがとうございました!

AWS Cost Explorer を利用してコストを可視化する

本記事ではAWSのCost Explorerの使い方を触ったことがない方向けに画像多めに紹介します。

クラウドを利用しているとどうしても月々お金がかかってしまいます。 会社で利用しているとなるとお金の報告やら予算の策定やらなにやら、 開発とは別のところで苦戦することがあるかもしれません。

私自身も苦戦する中でAWSの費用可視化ツールである Cost Explorer を手探りで利用し、かなり仲良くなりました。 せっかく仲良くなったので利用方法をまとめて社内でも使い方勉強会を開催しましたが、勉強会の直後にCost ExplorerのUIにアップデートが入り、資料の画像が古いものとなってしまいました。

本記事では勉強会で行った内容を新UIに対応させて紹介します。

Cost Explorer とは

AWS Cost Explorer の使いやすいインターフェイスでは、AWS のコストと使用量の経時的変化を可視化し、理解しやすい状態で管理できます。すぐに使用開始し、カスタムレポートを作成してコストと使用量のデータを分析できます。大まかにデータを分析することや (例: すべてのアカウントの合計コストと使用量)、コストと使用量のデータを詳細に分析して傾向、コスト要因、異常を特定できます。

aws.amazon.com

Cost Explorer ではAWSのコストを可視化し、簡単な分析まで行うことができます。 やれることの幅が広く、最初はどこから触ればいいのか戸惑うかと思います。 1つずつ順番に見ていきましょう。

Cost Explorer へのアクセス方法

まずはCost Explorer にアクセスするところからです。 2種類のアクセス方法があります。

アカウントメニューからアクセス

  1. 右上のアカウントメニューから請求ダッシュボードを選択
  2. Cost Management の中から Cost Explorer を選択
  3. Cost Explorerを起動

サーチバーからアクセス

サーチバーに「Cost Explorer」と入れることでアクセスができます。

アクセスできないときは...

Cost Explorer に権限周りでアクセスができない可能性があります。 Cost Explorer にIAMユーザ等でアクセスする場合にはRoot アカウント側でIAM アカウントにアクセスする許可をする必要があります。

詳細なアクセス制御については以下を御覧ください。

docs.aws.amazon.com

  1. アカウントメニューから「アカウント」を選択
  2. IAM ユーザー/ロールによる請求情報へのアクセスの「編集」
  3. 「IAM アクセスのアクティブ化」にチェックを入れ「更新」 

これでIAM ユーザからもCost Explorer の画面が開けるようになりました。

トップ画面の見方

サマリーのグラフ、金額等が表示されています。 下図は何もお金が発生していないアカウントの内容を表示していますが、 実際に費用がかかっている場合にはグラフや金額が出てきます。

① Cost Explorer で表示

こちらのリンクからより詳細な分析のできるCost Explorer画面へ移動します。 移動先の使い方は後ほど紹介します。

② 傾向

特定のインスタンス、サービスがいくら増えた、全体の金額がいくら増えた、などの情報を表示してくれます。 先月との比較が表示されるため月イチで見るのをおすすめします。

③ レポートの表示

Cost Explorerで分析した内容をテンプレートとして保存した「レポート」を作成した際にここに一覧がでてきます。 今回の記事ではレポートの作成については触れません。

Cost Explorerの使い方

本編のCost Explorer画面の使い方です。 Cost Explorer の使い方のポイントは大きく分けて下の3つです。

  1. 日付範囲の指定
  2. グループ化
  3. フィルタ

これらの項目は「レポートパラメータ」としてグラフの右側に設定項目が表示されています。

① 日付範囲の指定

ここで設定できるのは「日付の粒度」「日付範囲」の2つです。 日付の粒度を「月別」として日付範囲を「3か月」とすれば直近3ヶ月の費用変化をグラフとして見れます。

② グループ化

グループ化はとても便利で私のお気に入りの機能です。 対象を決めてグラフを詳細化してくれます。 グループ化の対象を「グループ化を選択」から選びます。

以下の写真では例として「サービス」でグループ化しました。 この場合、月々の利用料のうち、何にいくらかかっているかが詳細にみることができるようになります。 大半の費用をこのアカウントではWAFが占めていることがわかります。

他にもリージョンや料金タイプ、インスタンスタイプ、タグなど様々なグループ化が行なえます。

  • 料金タイプでグループ化
    • 使用量と税金、その他割引がどれだけ入っていたか確認できる
  • タグでグループ化
    • 特定のタグがついたリソースでグループ化できる
    • AWS Organizationsを有効化している場合、親アカウントからグループ化できるタグを指定する必要があります

③ フィルタ

特定のサービスやタグ、インスタンスタイプ等で絞った検索ができます。 指定した条件のみを表示、非表示の両パターンでフィルタが可能です。

  1. フィルタタブより「新しいフィルターを追加」
  2. ディメンションにフィルタの対象を指定、
  3. オペレータで表示、非表示を選択
  4. 値に何をフィルタするか選択
  5. フィルタを適用

フィルタ機能を用いてグループ化の例で費用の大半を占めていた「WAF」の金額表示を消してみました。

ディメンション オペレータ
サービス Exclude WAF

WAF以外の料金がわかるようになりました。 特定のサービスのみを見たい場合にはオペレータをInclude にすることで可能です。

料金タイプのフィルタについて

デフォルトで「クレジット」「払い戻し」が除外されています。 使用量や支払う金額を見る際には不要なため基本的にはつけたままでOK。

  • クレジット
    • キャンペーン等で得たクレジット分の使用料金
    • つい先日AWS re:invent に現地で参加してきてプロモコードを頂いたのでクレジットに金額が表示されるのが楽しみです✌
  • 払い戻し
    • 何かしらの理由で返金された分の金額

Cost Explorer クイズ

ここまででCost Explorer マスターとなった皆さんにクイズです。

Cost Explorer上で日毎に見たとき、月初だけやけに費用が高くなっていることがわかります。 この原因は何でしょう。

答え

白文字で書いています。 選択して読んでください。

月初に税金とリザーブインスタンスの料金がかかっています。 CostExplorer上は1日にこれら2つの料金が表示されるので月初だけ費用が高く見えます。

  • 確認方法
    1. 日付の粒度を「日別」に設定
    2. グループ化を「料金タイプ」に設定
    3. 「定期的な予約料金」と「Tax」が1日のみにかかっていることがわかります

グループ化を「サービス」にすることでTaxが1日にかかっていることがわかりますが リザーブインスタンスの料金は「料金タイプ」でグループ化しないとわかりません。

まとめ

簡単にCost Explorerの使い方とポイントとなる3つの機能を紹介しました。 Cost Explorerは機能も多く見え、なかなか取っつきにくい(と自分は思った)ので本記事を参考に触ってみたいと思っていただけたら幸いです。

AWSのコスト管理に手を付けた際、以下の特集がとてもためになったため共有します。 Cost Explorerの使い方以外にも費用体系などまとまっておりおすすめです。

gihyo.jp

GitHubActionsでプルリクについたコメントを取得しワークフローを分岐させる

CIの実行結果をGitHubのプルリクに対してコメントとしてつけると便利です。 CIを走らせるたびに新しいコメントを追記されると大量のコメントが出てきて邪魔なので、marocchino/sticky-pull-request-commentを用いてコメントをアップデートしています。

github.com

やんごとなき理由ですでにコメントされているか、されていないかをもとに分岐したい処理が発生したため特定の文字列を含むコメントがあるかを調べるGitHubActionsのStepを作成しました。

作成

自分がコメントの追加、更新に使用している marocchino/sticky-pull-request-comment はコメントの管理のためにコメントアウトした形で制御用のHeaderをつけます。 <!-- Sticky Pull Request Comment'${HEADER}' -->がHeaderとして加えられた文字列です。

この文字列を含むコメントがあるか調べるステップを追加します。 まずは実際に作成したGitHubActionsファイルを示します。

name: Comment Exist
on: push

jobs:
  CreateComment:
    name: createComment
    runs-on: ubuntu-20.04
    steps:
      - name: Create Comment Hoge
        uses: marocchino/sticky-pull-request-comment@v1
        with:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          header: Hoge
          message: 'Hoge'

      - name: Check Comment Exist
        id: comment_exist
        env:
          HEADER: Hoge
        run: |
          PR_NUM=$(echo ${{ github.ref }} | sed -e 's/[^0-9]//g')
          COMMENT_LISTS=$(curl --request GET \
            --url https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUM/comments \
            --header 'authorization: token ${{ secrets.GITHUB_TOKEN }}' \
            --header 'content-type: application/json' \
            --header 'Accept: application/vnd.github.v3+json')
          EXTRACT=$(echo -E $COMMENT_LISTS \
            | jq -r '.[] | select(.body | contains("<!-- Sticky Pull Request Comment'${HEADER}' -->"))')
          if [ -z "$EXTRACT" ]; then
            echo ::set-output name=exist::false
          else
            echo ::set-output name=exist::true
          fi

      - name: Test
        if: steps.comment_exist.outputs.exist == 'true'
        run: |
          echo 'YES!'

解説

GitHubAPIを叩いてプルリクについているコメントを一覧で取得、ほしいコメント文字列が含まれているかを調べることで実現しています。

PR_NUM=$(echo ${{ github.ref }} | sed -e 's/[^0-9]//g')で現在のプルリク番号を取得します。 github.refはワークフローを実行するホスト上にデフォルトで設定されている変数でトリガーとなったブランチやタグが記録されます。 プルリクをトリガーにした場合プルリク番号は直接入っておらず、refs/pull/2/mergeのような形のためsedコマンドで変換する必要があります。

プルリク番号を取得することでプルリクに紐づくコメントを取得するAPIを利用できます。 GitHubActionsからの実行ならばトークンはデフォルトでsecretsに登録されているためそのまま利用できます。 ローカルからcurlする際には設定からトークンを発行しましょう。

curl --request GET \
    --url https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUM/comments \
    --header 'authorization: token ${{ secrets.GITHUB_TOKEN }}' \
    --header 'content-type: application/json' \
    --header 'Accept: application/vnd.github.v3+json'

docs.github.com

取得したコメント一覧のJsonからbodyに目的の文字列が含まれるか調べます。 jq -r '.[] | select(.body | contains("<検索対象文字列>"))' とすることでBodyに目的の文字列が含まれているJsonが抽出できます。 このJsonが空か空でないかをもとにecho ::set-output name=exist::trueで出力を作成し、 別のステップで出力結果をもとに分岐させます。

正常に動作しているかコメントの存在を確認するステップを増やして確認しました。

まとめ

GitHubActionsかなりいろいろなことができて便利です。 もっといいやり方あるよ〜等あればコメントいただけると幸いです。

この記事がどなたかの開発効率の向上につながったら嬉しいです。

VSCodeの拡張機能が動かなくなった

Visual Studio Code拡張機能が動かなくなった。 原因としては markdown-all-in-one という拡張機能がうまくアンインストールできていなかったから。 調査の記録を記事として書きました。

f:id:itib:20220121145438p:plain

現象

拡張機能を使おうとすると Command 'Live Share: Join Collaboration Session' resulted in an error (command 'liveshare.join not found) と表示されて拡張機能が動作しない。 LiveShareだけかと思ったらその他の拡張機能も同様に開くことができなくなっていた。

調査

拡張機能が存在するか調査

code --list-extentions コマンドでVSCodeに入っている拡張機能一覧を取得する。

ref: Managing Extensions in Visual Studio Code

❯ code --list-extensions
4ops.terraform
aws-scripting-guy.cform
christian-kohler.path-intellisense
CoenraadS.bracket-pair-colorizer
Darfka.vbscript
DavidAnson.vscode-markdownlint
donjayamanne.githistory
eamodio.gitlens
esbenp.prettier-vscode
funkyremi.vscode-google-translate
GitHub.vscode-pull-request-github
Gruntfuggly.todo-tree
hashicorp.terraform
hediet.vscode-drawio
jebbs.plantuml
ms-azuretools.vscode-docker
ms-python.python
ms-python.vscode-pylance
ms-toolsai.jupyter
ms-toolsai.jupyter-keymap
ms-toolsai.jupyter-renderers
ms-vscode-remote.remote-containers
ms-vscode-remote.remote-ssh
ms-vscode-remote.remote-ssh-edit
ms-vscode-remote.remote-wsl
ms-vscode-remote.vscode-remote-extensionpack
ms-vscode.powershell
ms-vsliveshare.vsliveshare
ms-vsliveshare.vsliveshare-audio
ms-vsliveshare.vsliveshare-pack
redhat.java
redhat.vscode-commons
redhat.vscode-yaml
streetsidesoftware.code-spell-checker
Tyriar.sort-lines
VisualStudioExptTeam.vscodeintellicode
vscjava.vscode-java-debug
vscjava.vscode-java-dependency
vscjava.vscode-java-pack
vscjava.vscode-java-test
vscjava.vscode-maven

開けなかった拡張機能は存在していることになっている...

拡張機能ディレクトリにもしっかりあるか確認

❯ ls ~/.vscode/extensions
4ops.terraform-0.2.2
aws-scripting-guy.cform-0.0.24
christian-kohler.path-intellisense-2.6.0
christian-kohler.path-intellisense-2.6.1
coenraads.bracket-pair-colorizer-1.0.62
darfka.vbscript-1.0.4
davidanson.vscode-markdownlint-0.45.0
donjayamanne.githistory-0.6.19
eamodio.gitlens-11.7.0
esbenp.prettier-vscode-9.1.0
funkyremi.vscode-google-translate-1.4.13
github.vscode-pull-request-github-0.34.3
gruntfuggly.todo-tree-0.0.214
gruntfuggly.todo-tree-0.0.215
hashicorp.terraform-2.18.0
hashicorp.terraform-2.19.0
hediet.vscode-drawio-1.6.4
jebbs.plantuml-2.17.0
jebbs.plantuml-2.17.1
jebbs.plantuml-2.17.2
ms-azuretools.vscode-docker-1.18.0
ms-azuretools.vscode-docker-1.19.0
ms-python.python-2021.12.1559732655
ms-python.vscode-pylance-2021.11.0
ms-python.vscode-pylance-2022.1.0
ms-python.vscode-pylance-2022.1.1
ms-toolsai.jupyter-2021.10.1001414422
ms-toolsai.jupyter-2021.11.1001550889
ms-toolsai.jupyter-2021.9.1101343141
ms-toolsai.jupyter-keymap-1.0.0
ms-toolsai.jupyter-renderers-1.0.4
ms-vscode-remote.remote-containers-0.202.5
ms-vscode-remote.remote-containers-0.209.6
ms-vscode-remote.remote-ssh-0.70.0
ms-vscode-remote.remote-ssh-edit-0.70.0
ms-vscode-remote.remote-wsl-0.63.13
ms-vscode-remote.vscode-remote-extensionpack-0.21.0
ms-vscode.powershell-2021.12.0
ms-vsliveshare.vsliveshare-1.0.5065
ms-vsliveshare.vsliveshare-1.0.5242
ms-vsliveshare.vsliveshare-1.0.5273
ms-vsliveshare.vsliveshare-audio-0.1.91
ms-vsliveshare.vsliveshare-pack-0.4.0
redhat.java-1.2.0
redhat.vscode-commons-0.0.6
redhat.vscode-yaml-1.2.2
redhat.vscode-yaml-1.3.0
streetsidesoftware.code-spell-checker-2.0.14
streetsidesoftware.code-spell-checker-2.1.3
streetsidesoftware.code-spell-checker-2.1.4
tyriar.sort-lines-1.9.1
visualstudioexptteam.vscodeintellicode-1.2.15
vscjava.vscode-java-debug-0.37.0
vscjava.vscode-java-dependency-0.18.9
vscjava.vscode-java-pack-0.20.0
vscjava.vscode-java-test-0.33.1
vscjava.vscode-maven-0.34.2
yzhang.markdown-all-in-one-3.4.0

一覧表示してもわかりにくいので表示のdiffを取ります。

❯ diff -i <(ls ~/.vscode/extensions | sed 's/-[0-9].*$//g' | uniq) <(code --list-extensions)
42d41
< yzhang.markdown-all-in-one

なんかフォルダだけに残っている拡張機能がありますね👀

コマンドの解説

diffコマンド

対象のファイルの差分を出力します。 比較するファイルとしてコマンドの実行結果を渡すには <() を用いて名前付きパイプとするとあたかもファイルのように比較することができます。

まだ diff -i で大文字と小文字の変化を無視することができます。

qiita.com

lsとsedコマンド

みんなの強い味方 sed

-[数字] を空白に置換することで vscode/extentions の表示に含まれるバージョン情報を削除しています。

ls ~/.vscode/extensions | sed 's/-[0-9].*$//g'

~ ❯ ls ~/.vscode/extensions | tail -n 3
vscjava.vscode-maven-0.34.2
yzhang.markdown-all-in-one-3.4.0
~ ❯ ls ~/.vscode/extensions | sed 's/-[0-9].*$//g' | tail -n 3
vscjava.vscode-java-test
vscjava.vscode-maven
yzhang.markdown-all-in-one

uniq コマンド

表示行で重複するものを消せる。 複数バージョンの拡張機能があると複数フォルダが表示されて嶋う。uniq コマンドで重複を消している

検証

ファイルはあるけどVSCode側にインストールされていない拡張機能を削除。

❯ rm -rf ~/.vscode/extensions/yzhang.markdown-all-in-one-3.4.0

うまく動くようになった! f:id:itib:20220121153845p:plain

どうやら Markdown all in one を過去にアンインストールした際にファイルが残り続けてしまったことが原因らしい。

探してみるとネット上にもちらほらそんな記事が存在した。

qiita.com

最後に

無事に拡張機能が使えるようになってよかったです。 最近読んだシェルノック本がとても役立ちました。

いままでsedでどうやるんだっけ...とか悩むことがありましたがその頻度も減ってきていい感じです。 おすすめ。

Windowsイベントログ解析ツール「Hayabusa」を使ってみる

こんにちは、いちび( @itiB_S144 )です。

2021年12月25日にWindowsイベントログ解析ツールとして「Hayabusa」がリリースされました🎉

Hayabusaは事前に作成したルールに則ってWindowsイベントログを調査し、インシデントや何かしらのイベントが発生していないか高速に検知することができるツールです。 私も開発を微力ながらお手伝いさせていただきました。 本記事では多くの方にHayabusaを触っていただきたいため、簡単な使い方を紹介していきます。

github.com

詳細なHayabusaの実装についてはコミッターのKazuminさんの記事が参考になります、 ぜひご参照ください!

kazuminkun.hatenablog.com

Hayabusaとは

HayabusaはWindowsイベントログの解析ツールです。 事前に作成したルールに則って解析を行い、ルールに引っかかるものがあれば検知します。

Hayabusaは、日本のYamato Securityグループによって作られたWindowsイベントログのファストフォレンジックタイムライン生成およびスレットハンティングツールです。

hayabusa/README-Japanese.md at main · Yamato-Security/hayabusa · GitHub

ルールは独自に作成することも可能ですが、1000以上のルールがプリセットとして用意されており、手軽に試すことができます。 また日本のコミュニティが作っているため日本語のREADMEが充実しています。英語が苦手な方でも安心

ルールの例

Hayabusaリポジトリの中には独自に作られたルールと多くの研究者が作成したルールである Sigma をベースに変換したルールが配置されています。 上にあげた例だけでなく、マルウェア検知など様々なルールが用意されています。 自分でルールを作るのが難しいという方でも十二分にHayabusaを利用することができます。

Hayabusaを使ってみる

以下に用意されたサンプルWindowsイベントログファイルに対してHayabusaを実行してみます。 ルールファイルはHayabusaで用意されたものを用います。 github.com

環境

手順

1. GitHubから必要なファイルを入手

Hayabusaの実行ファイルとサンプルWindowsイベントログファイルを取得します。

Windows環境なので hayabusa-1.0.0-windows-x64-evtx-0.7.2.zip をダウンロードしました。evtx以降の 0.6.70.7.2 の違いは中で使用しているライブラリのバージョンの違いです。とりあえず新しいほうを選んでいます。

f:id:itib:20211231172724p:plain

ファイルを解凍して準備OKです。

2. フォルダの確認

基本的に変更が発生するのは config フォルダになります。 大量発生してログを埋め尽くすようなルールをミュートしたり等々の設定ができます。

ひとまず今回はデフォルトのままで進めます。

├── hayabusa-1.0.0-windows-x64-evtx-0.7.2
│   ├── README.md
│   ├── hayabusa.exe
│   ├── config                // 設定ファイル
│   ├── doc                   // ドキュメントたち
│   ├── logs                  // hayabusa.exeの実行ログ
│   ├── rules                 // 検知ルールファイルたち
│   ├── sample-results
│   └── tools
└── hayabusa-sample-evtx-main // サンプルイベントファイルたち
    ├── DeepBlueCLI
    ├── EVTX-ATTACK-SAMPLES
    ├── EVTX-to-MITRE-Attack
    ├── README.md
    └── YamatoSecurity

3. Hayabusaの実行

コマンドプロンプトからHayabusaを起動します。 エクスプローラーでHayabusaフォルダに移動し、バーに cmd と入力することで簡単に開くことができます。

f:id:itib:20211231183542p:plain

オプションをなにもつけずに hayabusa.exe を実行するとヘルプが出てきます。

f:id:itib:20211231184229p:plain

ファイルに対してHayabusaを実行

hayabusa-sample-evtx に用意されたサンプルのWindowsイベントログファイル(.evtx)に対してHayabusaを実行します。

>hayabusa.exe -f <検査対象WIndowsイベントログ.evtx>

f:id:itib:20211231193000p:plain

検知した内容が表示されたのち、サマリが表示されます。 デフォルトのHayabusaが使用しているルールは /rules フォルダ下に配置されたすべてのルールです。 オプションから使用するルールを指定することも可能です。

フォルダに対してHayabusaを実行

windowsイベントファイルを置いたフォルダに対してまとめてHayabusaを実行することができます。

>hayabusa.exe -d ..\hayabusa-sample-evtx-main

その他

基本的な使い方はREADMEにすべて書かれています。 検出結果のファイルへの出力方法等は以下に記載されていますので参考にしてください。

hayabusa/README-Japanese.md at main · Yamato-Security/hayabusa · GitHub

自分のWindowsに対してHayabusa実行

サンプルのWindowsイベントログファイルにHayabusaを実行した後は自分のWindowsに対して実行してみたくなりますね? 早速試していきましょう。

新規Windowsユーザの作成

今回はログに残る動作として新たなユーザをWindowsに追加します。

設定 → アカウント → 他のユーザ からユーザを追加します。 f:id:itib:20211231193851p:plain

ログの取得

Windowsのイベントログファイルは C:\Windows\System32\winevt\Logs 下に保存されます。 管理者権限が必要となるため Hayabusa を管理者権限のあるコマンドプロンプトで開くかログファイルを管理者権限のいらないフォルダ下にコピーしましょう。

ユーザ追加のログは Security.evtx に保存されます。

f:id:itib:20211231194230p:plain

Hayabusaで調査する

コピーした Security.evtx に対してHayabusaをかけてみました。 User: test が作られたことを検知できていることがわかります。

f:id:itib:20211231195943p:plain

オプション -o をつけるとCSVファイルに検知結果を出力することができます。

>hayabusa.exe -f ..\Security.evtx -o detect.csv

detect.csv

Timestamp,Computer,EventID,Level,RuleTitle,Details,RulePath,FilePath
2021-12-31 19:38:37.370 +09:00,DESKTOP-ITIB,4728,low,User added to local security group,User: itib  :  Group: なし  :  LogonID: 0x4a111111,rules\hayabusa\default\alerts\Security\4728-AccountManipulation_UserAddedToLocalSecurityGroup.yml,..\Security.evtx
2021-12-31 19:38:37.370 +09:00,DESKTOP-ITIB,4728,medium,User added to global security group,User: itib  :  Group: なし  :  LogonID: 0x4a111111,rules\hayabusa\default\alerts\Security\4728_AccountManipulation_UserAddedToGlobalSecurityGroup.yml,..\Security.evtx
2021-12-31 19:38:37.372 +09:00,DESKTOP-ITIB,4720,medium,Local user account created,User: test  :  SID: S-1-1-11-111111111-1111111111-1111111111-1111,rules\hayabusa\default\alerts\Security\4720_CreateAccount-LocalAccount_UserAccountCreated.yml,..\Security.evtx

csvには検知したルールファイルへのパスが表示されています。

ルールファイルを見てみる

Hayabusaで調査するで検知したイベントログとルールファイルを見てみましょう。

Security.evtxをイベントビューアで開き、検索を駆使して検知したイベントを見つけました。 イベントログの EventID4720ChannelSecurity、作られたTargetUserNametestであることがわかります。後々のためにXML形式で見ています。

f:id:itib:20211231204223p:plain

上記イベントを検知した先ほどルールファイルは 4720_CreateAccount-LocalAccount_UserAccountCreated.yml でした。

author: Eric Conrad, Yamato Security
creation_date: 2020/11/08
uodated_date:  2021/12/22

title: Local user account created
title_jp: ローカルユーザアカウントが作成された
details: 'User: %TargetUserName%  :  SID: %TargetSid%'
details_jp: 'ユーザ名: %TargetUserName%  :  SID: %TargetSid%'
description: A local user account was created.
description_jp: ローカルユーザアカウントが作成された.

id: 13edce80-2b02-4469-8de4-a3e37271dcdb
level: medium
status: stable
detection:
    selection:
        Channel: Security
        EventID: 4720
    filter:
        TargetUserName|endswith: "$" #Filter out machine/computer accounts
    condition: selection and not filter
falsepositives:
    - system administrator
tags:
    - attack.persistence
    - attack.11136.001
references:
    - https://attack.mitre.org/techniques/T1136/001/
sample-evtx: ./sample-evtx/DeepBlueCLI/new-user-security.evtx
logsource: default
ruletype: Hayabusa

detection下に書かれた条件で検知が行われます。 ChannelSecurity AND EventID4720 であり、filter にマッチしない条件がこのルールで検知されるようです。

検知した場合は title: Local user account createddetails: 'User: %TargetUserName% : SID: %TargetSid%'%~% が埋められた文字列が検知結果として表示されています。

詳細なルールの書き方はこれまたREADMEに詳しく書かれていますので参考にしてください。

hayabusa/AboutRuleCreation-Japanese.md at main · Yamato-Security/hayabusa · GitHub

最後に

ここまで読んでいただきありがとうございました。

簡単なHayabusaの使い方を紹介させていただきました。 Hayabusa、面白そう!触ってみたいと思っていただけたら幸いです。

HayabusaはOSSとして大和セキュリティが開発しています。 Hayabusaの開発気になる、バグ見つけた等あればGithubのissueや下記ConnpassのSlackからコンタクトお願いします。

yamatosecurity.connpass.com

それではよいWindowsログ解析ライフを!

Windowsの音声出力先を変えるショートカット作成

本記事は ZOZO Advent Calendar 2021 の10日目の記事です。

みなさん在宅ワークしていますか? 自分はフルリモートの会社で働いているためどれだけ在宅環境をよくできるか日々考えています。

本記事では在宅ワークの際に手間だと感じていた 「パソコンの音声出力先変更」をショートカット1つでできるようにした方法を解説します。

どのように調べ、実現までもっていったか、参考になれば幸いです。

やりたいこと

パソコンで音楽を流したり好きなラジオ、配信を聞きながらテンションを上げて作業している方は多いと思います。 お昼時など席を離れた際にわざわざスマホに切り替えて、とかするのは大変ですよね?

我が家では普段の作業時には机の上のスピーカ、キッチンに移動したらおいてあるAlexaからBluetooth接続で音楽を再生させていました。 スピーカボタンを押してデバイスの選択という流れで音声の出力先を切り替えています。 f:id:itib:20211209005317g:plain

この作業がなかなかに手間。 移動開始前に数ステップある作業は嬉しくない。
理想は1クリックで移動可能、 なんなら移動したことを検知して勝手にスピーカを切り替えてほしい。

ということで本記事では「1クリックで音声出力先を切り替えられるようなショートカットを作成する」を目標に調査、実装を行っていきます。

調査

Windowsがどのようにデフォルトの音声出力先を決めているのか調査します。 どうやって設定しているかがわかれば対応したコードを書いて実行できるはず!

Windows APIの調査

Windowsの多くの動作はAPIを用いて操作することができます。 「Windows API インデックス」を漁っていきます。 音声出力先の設定に関わりそうなドキュメントとしては「Windows Core Audio API について」がありました。

コア オーディオ API は次のとおりです。
・マルチメディアデバイス(MMDevice)API。クライアントは、このAPIを使用して、システム内のオーディオエンドポイントデバイスを列挙します。
・...

列挙してくれるだけらしい🤔
フォーラムとかを見るとデバイスの切り替えのAPIは提供されていないように見えます。

ref: Programatically setting the default playback device (and recording device)

どこかのレジストリに格納されているのでは?

WIndowsの設定情報はレジストリに格納される。 当然音声出力先の情報もあるはず。

レジストリエディタで探すと \HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render\ 下に接続されたデバイスらしきデータが見つかりました。

f:id:itib:20211209010601p:plain

音声出力先を変更した際にどこの値が変化するか観察してみると出力先に設定したタイミングでUUID({xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}形式の値)下に含まれる Level: 0 ~ Level: 2 の値が変化するようです。

  • 値は出力に設定されるたびに+nされる
  • 別の値が出力先になっても値は変化しない
  • 足し算される値は期間を開けると大きくなる

ここから時間等でLevelに代入される値が決まっており、 値が最新のもの が音声出力先として使われていると考えることができます。

f:id:itib:20211209012429p:plain

レジストリの値は手動で編集することができるため右クリックで修正してみました。 再生先になっていないデバイスの値を現在の音声出力先となっているデバイスの値より大きくすれば出力先が変更されると予測していました。 とりあえず Level: 2 の値を +1 してみました。

変更前 変更後
Level: 0 0x000003cc 0x000003cc
Level: 1 0x000003cc 0x000003cc
Level: 2 0x0000020d 0x0000020e

値を変更した直後からWindows の右下サウンドマークで表示される出力先には値を大きくしたデバイスが表示されるようになったものの 音声の出力先はいままでの出力先のままという状態になりました。

f:id:itib:20211209020101p:plain

デスクトップの表示だけされても意味がないんだ...

既存実装調査

ここまでで自力では手詰まりだったのでGithubで先人たちの書いた実装を調べます。

Audioやいままでの調査で出てきたMMDevices等のキーワードたちでGithubを調べます。検索してみるとやりたいことを実装しているコードが何個も出てきます。以下は実装の際に大きく参考にしたリポジトリです。

先人は偉大。

github.com

github.com

気合で読んで以下のような流れで音声出力先を変更していることがわかりました。

  1. IPolicyConfig インタフェースを実装したPolicyConfigClientを作成
  2. PolicyConfigClientIPolicyConfig に含まれる SetDefaultDevice() を実装
  3. SetDefaultDevice()にデバイスのUUIDとシステムがオーディオエンドポイントデバイスに割り当てた役割を示すERoleを渡して呼び出す
  4. 出力先が変更される

実装する

先人たちのツールを使えば簡単ですが自分でもいろいろいじりたいと思い手を動かしていきます。実装する内容は以下の2つです。

  1. 出力先として利用可能なデバイス一覧を表示
  2. 音声出力先の切り替え

先人たちのコードが C# で書かれていましたが自分は C# の知見があまりないのできっとPowerShellならWindows 関連なんとかなるでしょ!という安直な考えでPowerShellで実装しました。

出力先として利用可能なデバイス一覧を表示

オーディオ関連のレジストリを調査した際に機器情報が格納されている場所がわかりました。指定した箇所のレジストリ内容を出力することでデバイス名を取得します。

$regRoot = "HKLM:\Software\Microsoft\"

function Get-Devices
{
    $regKey = $regRoot + "\Windows\CurrentVersion\MMDevices\Audio\Render\"
    Write-Output "Active Sound devices:"
    Get-ChildItem $regKey | Where-Object { $_.GetValue("DeviceState") -eq 1} |
        Foreach-Object {
            $subKey = $_.OpenSubKey("Properties")
            Write-Output ("  " + $subKey.GetValue("{a45c254e-df1c-4efd-8020-67d146a850e0},2"))
            Write-Output ("    " + $_.Name.Substring($_.Name.LastIndexOf("\")))
        }
}

Get-Devices

実行すると以下のようにデバイス名とUUIDが表示されます。

PS > .\deviceList.ps1
Active Sound devices:
  スピーカー
    \{476a4519-c338-4f9e-9746-294cd6429136}
  1-2
    \{4c884bad-d966-441a-b1f9-4ec0fced2fbc}
  3-4
    \{cd21a3a3-061e-4064-94a6-f5769eda2979}
  ヘッドホン
    \{ee3cf230-46d3-4043-8fc2-7cd4dff984b0}
  Digital Audio (S/PDIF)
    \{fa27ad22-6853-430a-b1e5-10b83f01dbbb}

音声出力先の切り替え

ここは先人たちのC#で書かれたコードをもとに実装しました。

あまりC#の知見がない + デバイス一覧取得をpowershellで書いたためできればpowershellで完結させたい、 と思っていたところpowershellは変数として与えたC#のコードを実行できるらしい。宇宙感じた。

social.technet.microsoft.com

必要となるERole, IPolicyConfig, PolicyConfigClientを実装しPowershellから呼び出すようにします。

$cSharpSourceCode = @"
using System;
using System.Runtime.InteropServices;

using System.Collections.Generic;
internal enum ERole : uint
{
    eConsole         = 0,
    eMultimedia      = 1,
    eCommunications  = 2,
    ERole_enum_count = 3
}
[Guid("F8679F50-850A-41CF-9C72-430F290290C8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IPolicyConfig
{
    [PreserveSig]
    int GetMixFormat();
    [PreserveSig]
    int GetDeviceFormat();
    [PreserveSig]
    int ResetDeviceFormat();
    [PreserveSig]
    int SetDeviceFormat();
    [PreserveSig]
    int GetProcessingPeriod();
    [PreserveSig]
    int SetProcessingPeriod();
    [PreserveSig]
    int GetShareMode();
    [PreserveSig]
    int SetShareMode();
    [PreserveSig]
    int GetPropertyValue();
    [PreserveSig]
    int SetPropertyValue();
    [PreserveSig]
    int SetDefaultEndpoint(
        [In] [MarshalAs(UnmanagedType.LPWStr)] string deviceId, 
        [In] [MarshalAs(UnmanagedType.U4)] ERole role);
    [PreserveSig]
    int SetEndpointVisibility();
}
[ComImport, Guid("870AF99C-171D-4F9E-AF0D-E63DF40C2BC9")]
internal class _CPolicyConfigClient
{
}
public class PolicyConfigClient
{
    public static int SetDefaultDevice(string deviceID)
    {
        IPolicyConfig _policyConfigClient = (new _CPolicyConfigClient() as IPolicyConfig);
        try {
            Marshal.ThrowExceptionForHR(_policyConfigClient.SetDefaultEndpoint(deviceID, ERole.eConsole));
            Marshal.ThrowExceptionForHR(_policyConfigClient.SetDefaultEndpoint(deviceID, ERole.eMultimedia));
            Marshal.ThrowExceptionForHR(_policyConfigClient.SetDefaultEndpoint(deviceID, ERole.eCommunications));
            return 0;
        } catch {
            return 1;
        }
    }
}
"@

add-type -TypeDefinition $cSharpSourceCode

function Set-DefaultAudioDevice
{
    Param
    (
        [parameter(Mandatory=$true)]
        [string[]]
        $deviceId
    )

    If ([PolicyConfigClient]::SetDefaultDevice("{0.0.0.00000000}.$deviceId") -eq 0)
    {
        Write-Host "SUCCESS: The default audio device has been set."
    }
    Else
    {
        Write-Host "ERROR: There has been a problem setting the default audio device."
    }
}

$id = $args[0]
Set-DefaultAudioDevice $id

1つ目の引数として音声出力先に設定したいデバイスのUUIDを渡すとSetDefaultDeviceが実行され音声出力先を変更することができました。

PS > .\deviceSet.ps1 "{4c884bad-d966-441a-b1f9-4ec0fced2fbc}"
SUCCESS: The default audio device has been set.

スクリプト実行用のショートカット作成

作成したPowerShellコードを呼び出すショートカットを作成します。 Windowsのショートカットには引数を与えて作ることができるので出力先に対応したショートカットをデスクトップに作成します。

ショートカットを作成し、そのプロパティからリンク先を powershell.exe <作成したdeviceSet.ps1へのパス> <設定対象のUUID> に編集します。

サンプル: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe C:\Users\guest\deviceSet.ps1 "{4c884bad-d966-441a-b1f9-4ec0fced2fbc}"

f:id:itib:20211209214848p:plain

出来上がったショートカットをデスクトップに置くことで以下のように音声出力先を1クリックで変更できるようになりました。 自分の環境ではアイコン画像も変えてわかりやすくしました。

f:id:itib:20211209212542g:plain

おわりに

目標だった「1クリックでWindowsの音声出力先を変更する」を達成することができました。

手軽に再生先を変えられるようになっただけでなく、 Windowsの音声出力設定方法やシステムについて詳しくなれた、 PowerShellC#のコードを読みだせることを知れた点で挑戦してみてよかったと思います。

つぎはPowerShellから別の言語に書き換えたり、人の部屋移動を検知してこのショートカットを叩かせたりしたいです。