TECH PLAY

電通総研

電通総研 の技術ブログ

836

こんにちは、X イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの福山です。 今回は、弊社CSIRTチームの一機能として活動している「 脆弱性 管理チーム」の活動に関する内容となります。 昨今では、Log4shellやSpring4shellなど、影響範囲の広い 脆弱性 が後を絶ちません。 これらの緊急度の高い 脆弱性 情報が公開された場合を想定した、情報収集〜現場対応までのCSIRT目線での模擬訓練を、FutureVulsを含めて実施してみましたので紹介したいと思います。 FutureVulsとは FutureVulsを導入した経緯 事前準備 模擬訓練 情報収集 トリアージ 全社周知 個別周知 対応状況の確認 対応結果 見つかった課題 最後に FutureVulsとは フューチャー株式会社が開発する OSS の 脆弱性 スキャナVulsがベースになっており、組織単位で継続的に 脆弱性 管理するための 脆弱性 管理サービスです。 参考: https://vuls.biz/ FutureVulsの主な特徴についてピックアップします。 スキャンについて 検知精度、検知スピードに優れており、サーバへの負荷が軽い。 スキャナを導入せずに手動でソフトウェア情報を登録し、管理することも可能。 検出された情報について ソフトウェア情報の一括管理や、複数グループを束ねたグループセットでの横断検索が可能。 参考: グループセット 検出された 脆弱性 情報を一覧で一括管理が可能。 自動 トリアージ について 脆弱性 の危険度合いをスコアで示すCVSSによる従来の評価手法では、以下の点が考慮されていません。 脆弱性 の悪用の容易さ 実際の資産が攻撃を受ける環境にあるか 資産価値 攻撃を受けた際の業務影響度合い また、現場でどのように対応すべきかの判断がもう一段階必要となってきます。 そこで2019年12月、これらを解決する新しい評価手法「SSVC(Stakeholder-Specific Vulnerability Categorization)」が米 カーネギーメロン大学 ソフトウェア工学 研究所によって提案されました。 SSVCでは意思決定ツリーを用いて「immediate」「out_of_cycle」「scheduled」「defer」の4段階で対応優先度を導出します。 FutureVulsにはSSVCの機能が早くも導入されており、各 脆弱性 に対して4段階評価で対応優先度が自動で付与されることで、現場の負担を減らすことができます。 参考: SSVC(Stakeholder-Specific Vulnerability Categorization)を活用した脆弱性管理 FutureVulsを導入した経緯 FutureVulsを活用した 脆弱性 管理チームの緊急時運用フローを確認したい 弊社ではNIST NVD、 JVN iPediaから収集した情報と、シートに入力された各プロジェクトのソフトウェア情報を突き合わせ、該当する 脆弱性 情報の個別周知運用を行っていました。 ただし以下の課題があり、その要件を満たすべく2023年7月よりFutureVulsに本番移行することが決まりました。 <FutureVuls導入前の課題> より正確かつ最新のソフトウェア情報を管理したい 該当する 脆弱性 情報をよりタ イムリ ーに周知したい 各プロジェクトに個別に対応判断を委ねず、判断基準を設けたい 対応状況をより正確に把握したい 事前準備 訓練に使用する 脆弱性 はSpring4Shell(CVE-2022-22965)を採用 Spring4Shellの採用に至った条件は以下 仮想の 脆弱性 ではなく、既存の 脆弱性 を利用した方がやりやすい 社内で利用されているソフトウェア 擬似サーバへの CPE 登録が想定されるもの 参考: 擬似サーバとは リモートコード実行が可能な 脆弱性 で、PoCが公開されているもの 公表された時点でCVE未採番で、セキュリティパッチが公開されていないもの 事前にプロジェクトに協力を募り、3プロジェクトからの協力を取り付ける FutureVulsの擬似サーバを利用し、 CPE 形式でソフトウェア情報を登録 プロジェクトA Spring Framework 5.3.17 OracleJDK 9 Apache HTTP Server 2.4.53 Apache Tomcat 9.0.60 プロジェクトB Spring Boot 2.5.0 OpenJDK 9 nginx 1.6.0 Apache Tomcat 9.0.59 プロジェクトC Spring Framework 5.2.0 OracleJDK 8 Apache HTTP Server 2.4.52 Apache Tomcat 9.0.59 模擬訓練 模擬訓練の流れは以下となります。 情報収集 -> トリアージ -> 全社周知 -> 個別周知 -> 対応状況の確認 なお、全社周知と個別周知の役割の違いは以下です。 全社周知 個別周知 ・ゼロデイ 脆弱性 等、CVEが採番される前の段階でも情報をいち早く整理し伝える ・該当プロジェクトに直接周知することで、対応が必要であることを認識していただく ・影響を受ける条件や、PoCの再現結果を掲載する ・該当プロジェクトの対応状況を可視化する ・FutureVulsを利用しない社員向けにも周知する 以上を踏まえて、実際の訓練の内容に移ります。 情報収集 社内で利用されているソフトウェアにおいて、ゼロデイ 脆弱性 や影響範囲の広い 脆弱性 情報が公開されていないか、複数のWebサイトでチェックしています。 よく参考にしている情報発信元を紹介します。 CyberSecurityHelp:独自の 脆弱性 DBを公開しており、リスク評価、パッチの提供有無、Atack Vector 、Public exploit(実際の悪用有無)、Exploit availability(PoC、攻撃コードの有無)、回避策などの情報をタ イムリ ーに提供するサイトで、公開されるタイミングはCVE採番後。 URL: https://www.cybersecurity-help.cz/ Lunasec:米セキュリティ企業。2021年に公開されたLog4Shellの際にもブログをいち早く公開しており、影響を受ける条件、PoC、回避策などをわかりやすくブログにまとめている URL: https://www.lunasec.io/docs/blog/spring-rce-vulnerabilities/ Bleeping Computer: 脆弱性 情報を含む最新のセキュリティニュースをいち早く提供する海外サイト URL: https://www.bleepingcomputer.com/tag/vulnerability/ Security Next: 脆弱性 情報を含む最新のセキュリティニュースを日本語でいち早く提供するサイト URL: https://www.security-next.com/135304 piyolog: 脆弱性 情報を含む最新のセキュリティニュースがわかりやすく日本語で解説されたブログ URL: https://piyolog.hatenadiary.jp/entry/2022/04/01/065946 GitHub Advisory Database: GitHub Security Advisoriesで公開された 脆弱性 や、依存関係を追跡して得られた依存先ソフトウェアの 脆弱性 を一覧表示する URL: https://github.com/advisories ただし、これらはあくまで参考情報ですので、最終的に一次情報源の確認は必須です。 公的機関( JPCERT/CC 、 JVN 、 IPA 、NIST NVD) 開発元(今回はSpring) URL: https://spring.io/security/ トリアージ これまでの情報を整理した上で、全社周知要否( トリアージ )を2つの観点で実施します。 影響範囲の確認 各プロジェクトにおける利用状況を確認するため、FutureVulsのグループセットからソフトウェア名で検索します。 参考: グループセット内のソフトウェア名検索 緊急度合いの確認 脆弱性 管理チームで利用している緊急度 判断表 をもとに、緊急度を確認します。 No RCE PoC 開発元の評価 緊急度 1 可能 有 高以上 緊急 2 可能 無 高以上 高 3 不可 有 高以上 高 4 不可 無 高以上 中 5 可能 有 中以下 高 6 可能 無 中以下 中 7 不可 有 中以下 中 8 不可 無 中以下 中 9 可能 有 未評価 緊急 10 可能 無 未評価 高 11 不可 有 未評価 高 12 不可 無 未評価 中 公表時点では、No.9の「緊急」に該当しました。 トリアージ の結果、全社周知が必要と判断したため次のステップに進みます。 全社周知 全社周知の準備を進めつつ、MITRE社によるCVE採番状況のチェックもしながら状況変化をキャッチアップします。 URL: https://cve.mitre.org/cve/search_cve_list.html PoC、回避策の検証 まずはPoCを探すところからですが、例として以下の リポジトリ では、 GitHub で公開されているPoCを自動収集しています。 URL: https://github.com/nomi-sec/PoC-in-GitHub/tree/master 当時、実際に対応した際は以下のPoCが出回っており、こちらを実施しました。 LunaSec社も改善に加わっているようです。(※中には悪意のあるPoCを公開している リポジトリ もあるため、コードの中身をよく読んで、社内と隔離されたネットワーク上で実行するようにする) URL: https://github.com/reznok/Spring4Shell-POC dockerを利用するため容易に実施できるものになっています。(環境構築に時間が掛かる場合があります) また、Lunasecによると、脆弱パターンの ブラックリスト を記載したInitBinderを挿入することが緩和策になると謳っています。 全社周知(社内 ポータルサイト へのニュース掲載) 内容としては以下の項目をまとめています。 脆弱性 の概要(RCEの可否、PoCの有無も含む) 影響を受けるソフトウェアバージョンおよび条件 根本対策(パッチ公開情報) 回避策(PoCの実施結果) 攻撃の兆候がないかログの確認を依頼(SOCがあれば活用) 参考情報 CVE採番後の対応 MITER社から公開されるCVE採番情報を確認したら、以下のチェックおよび対応します。 公的機関、開発元から情報が公開・更新されているか 情報収集の際にチェックした各発信元の情報が更新されているか パッチのリリース状況 社内 ポータルサイト に投稿した記事に追記 個別周知 次に、 脆弱性 バージョンを利用している該当プロジェクトに対して個別周知、対応期限を設定します。 オーガニゼーショントピック(CVE別にトピックを立てて情報共有できる機能)の投稿 参考: トピックによる情報共有・注意喚起 送信すると、該当するグループの全メンバへメールが発報されます。 また、SlackやTeamsへの連携も可能です。 Slack連携: https://help.vuls.biz/manual/notification/slack_app/ Teams連携: https://help.vuls.biz/manual/notification/teams/ スペシャ ル警戒タグの設定 こちらの機能を簡単にご紹介します。 脆弱性 単位で独自のタグを付けられる 例えば、「全社周知対象(トピック掲載あり)」や「緊急対応必須」として、ユーザへの情報認識を早めたり、CSIRT側で後から状況を見返す時に便利になると思います。 タグは1つのCVE-IDに対して複数設定できます。 対応期限を設定でき、該当タスクの対応期限を一括で上書き更新できる CVE検知済みのグループに対して設定された旨の通知ができる(通知の際、コメントは不可) 参考: スペシャル警戒タグ機能 今回の対応期限は判断日の5営業日後としました。 各プロジェクトの 脆弱性 情報のサマリに警戒タグが入りました。 対応状況の確認 最後に各プロジェクトの対応状況を後追いします。 FutureVulsではタスクステータスを用いて、対応状況を管理できます。 タスク対応の流れは以下です。 引用: ステータス 次のアクションとして、 スペシャ ル警戒タグで設定した対応期限を超過したタスクに対してリマインドします。 対応期限の翌営業日になったら、グループセットのタスクタブから、フィルターでCVE-2022-22965のタスクステータスが「NEW(新規)」と「INVESTIGATING(調査中)」を抽出します。 タスクに一括チェックした上で、タスクを編集からコメントを送信することで、該当グループの所属メンバ全員にメールを送信できます。 その後はタスクのコメント上でのやり取りも可能です。 対応結果 模擬訓練に参加したプロジェクトの対応結果は以下のようになりました。 プロジェクト 結果 ソフトウェア別の対応 プロジェクトA CPE スキャン(*)にて検出され、対応完了 Spring Framework 5.3.17が検出対象となったため、最新バージョンに CPE を更新し、クローズ プロジェクトB CPE スキャンでは検出できず、個別連絡にて対応依頼の上、完了 Spring Boot 2.5.0は Spring Framework に依存しているが、 CPE スキャンでは検出されず。原因と対応策は下記課題3を参照。Spring Bootの最新バージョンに CPE を更新。 プロジェクトC CPE スキャンにて検出され、対応完了 JDK 8は影響を受けないが検出対象となった。原因と対応策は下記課題4を参照。タスクのステータスを「NOT_AFFECTED(影響なし)」に変更し、クローズ。 * CPE スキャン:擬似サーバ上にソフトウェア名、バージョン情報を手動登録し、日次でスキャンさせる機能。 参考: CPEスキャン 見つかった課題 模擬訓練を通して発見した課題についてまとめます。 No 課題 対応策 補足 1 CVE未採番の状態でもユーザへ第一報を通知したい グループセットのソフトウェアタブで「Spring」と検索し、該当するプロジェクトメンバに連絡する FutureVulsのオーガニゼーショントピックや スペシャ ル警戒タグはCVE採番済みの 脆弱性 に対してしか利用できない。現状はチャットなど別の手段で連絡するしかない。 2 対応済みのプロジェクトにもオーガニゼーショントピックが通知されてしまう オーガニゼーショントピックをなるべく早めに発報することで、対応済み案件へ通知される確率を下げる。またトピック内に注意書きを入れる。 オーガニゼーショントピックは 脆弱性 単位で通知する機能となっており、各タスクのステータスまでチェックしないため、既に対応がクローズしている案件にも通知されるため、注意が必要。 3 Spring Bootの脆弱バージョンを CPE スキャンしても、CVE-2022-22965が検出されない 依存ライブラリスキャンを利用する CPE スキャンは登録したライブラリや フレームワーク の依存ライブラリの 脆弱性 までは検知できない。依存ライブラリスキャンの手法の1つとして GitHub Security Alerts連携 がある。PoCで利用したSpring Bootのpom. xml をスキャン対象の GitHub リポジトリ に手動であげるとFutureVuls側で検出されることを確認。 4 JDK 8であれば影響を受けないはずだが検出されてしまう トピック内に影響を受ける条件を記載し、プロジェクト側にステータスを「NOT_AFFECTED(影響なし)」に変更してもらうようにする FutureVulsには外部条件を考慮した検知の仕組みは現状ないため、ステータスを変更してもらう必要がある 最後に 今回、 脆弱性 対応模擬訓練を行うことで、FutureVulsを活用した緊急時の一連のフローを確認することができました。 また、課題はいくつか見つかりましたが、運用でカバーできそうであることもわかりました。 FutureVulsはリリース頻度が高いため、このあたりの課題が機能アップデートによって解消されることに期待したいと思います! 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア 執筆: @fukuyama.kenta ( Shodo で執筆されました )
こんにちは、X イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの福山です。 今回は、弊社CSIRTチームの一機能として活動している「 脆弱性 管理チーム」の活動に関する内容となります。 昨今では、Log4shellやSpring4shellなど、影響範囲の広い 脆弱性 が後を絶ちません。 これらの緊急度の高い 脆弱性 情報が公開された場合を想定した、情報収集〜現場対応までのCSIRT目線での模擬訓練を、FutureVulsを含めて実施してみましたので紹介したいと思います。 FutureVulsとは FutureVulsを導入した経緯 事前準備 模擬訓練 情報収集 トリアージ 全社周知 個別周知 対応状況の確認 対応結果 見つかった課題 最後に FutureVulsとは フューチャー株式会社が開発する OSS の 脆弱性 スキャナVulsがベースになっており、組織単位で継続的に 脆弱性 管理するための 脆弱性 管理サービスです。 参考: https://vuls.biz/ FutureVulsの主な特徴についてピックアップします。 スキャンについて 検知精度、検知スピードに優れており、サーバへの負荷が軽い。 スキャナを導入せずに手動でソフトウェア情報を登録し、管理することも可能。 検出された情報について ソフトウェア情報の一括管理や、複数グループを束ねたグループセットでの横断検索が可能。 参考: グループセット 検出された 脆弱性 情報を一覧で一括管理が可能。 自動 トリアージ について 脆弱性 の危険度合いをスコアで示すCVSSによる従来の評価手法では、以下の点が考慮されていません。 脆弱性 の悪用の容易さ 実際の資産が攻撃を受ける環境にあるか 資産価値 攻撃を受けた際の業務影響度合い また、現場でどのように対応すべきかの判断がもう一段階必要となってきます。 そこで2019年12月、これらを解決する新しい評価手法「SSVC(Stakeholder-Specific Vulnerability Categorization)」が米 カーネギーメロン大学 ソフトウェア工学 研究所によって提案されました。 SSVCでは意思決定ツリーを用いて「immediate」「out_of_cycle」「scheduled」「defer」の4段階で対応優先度を導出します。 FutureVulsにはSSVCの機能が早くも導入されており、各 脆弱性 に対して4段階評価で対応優先度が自動で付与されることで、現場の負担を減らすことができます。 参考: SSVC(Stakeholder-Specific Vulnerability Categorization)を活用した脆弱性管理 FutureVulsを導入した経緯 FutureVulsを活用した 脆弱性 管理チームの緊急時運用フローを確認したい 弊社ではNIST NVD、 JVN iPediaから収集した情報と、シートに入力された各プロジェクトのソフトウェア情報を突き合わせ、該当する 脆弱性 情報の個別周知運用を行っていました。 ただし以下の課題があり、その要件を満たすべく2023年7月よりFutureVulsに本番移行することが決まりました。 <FutureVuls導入前の課題> より正確かつ最新のソフトウェア情報を管理したい 該当する 脆弱性 情報をよりタ イムリ ーに周知したい 各プロジェクトに個別に対応判断を委ねず、判断基準を設けたい 対応状況をより正確に把握したい 事前準備 訓練に使用する 脆弱性 はSpring4Shell(CVE-2022-22965)を採用 Spring4Shellの採用に至った条件は以下 仮想の 脆弱性 ではなく、既存の 脆弱性 を利用した方がやりやすい 社内で利用されているソフトウェア 擬似サーバへの CPE 登録が想定されるもの 参考: 擬似サーバとは リモートコード実行が可能な 脆弱性 で、PoCが公開されているもの 公表された時点でCVE未採番で、セキュリティパッチが公開されていないもの 事前にプロジェクトに協力を募り、3プロジェクトからの協力を取り付ける FutureVulsの擬似サーバを利用し、 CPE 形式でソフトウェア情報を登録 プロジェクトA Spring Framework 5.3.17 OracleJDK 9 Apache HTTP Server 2.4.53 Apache Tomcat 9.0.60 プロジェクトB Spring Boot 2.5.0 OpenJDK 9 nginx 1.6.0 Apache Tomcat 9.0.59 プロジェクトC Spring Framework 5.2.0 OracleJDK 8 Apache HTTP Server 2.4.52 Apache Tomcat 9.0.59 模擬訓練 模擬訓練の流れは以下となります。 情報収集 -> トリアージ -> 全社周知 -> 個別周知 -> 対応状況の確認 なお、全社周知と個別周知の役割の違いは以下です。 全社周知 個別周知 ・ゼロデイ 脆弱性 等、CVEが採番される前の段階でも情報をいち早く整理し伝える ・該当プロジェクトに直接周知することで、対応が必要であることを認識していただく ・影響を受ける条件や、PoCの再現結果を掲載する ・該当プロジェクトの対応状況を可視化する ・FutureVulsを利用しない社員向けにも周知する 以上を踏まえて、実際の訓練の内容に移ります。 情報収集 社内で利用されているソフトウェアにおいて、ゼロデイ 脆弱性 や影響範囲の広い 脆弱性 情報が公開されていないか、複数のWebサイトでチェックしています。 よく参考にしている情報発信元を紹介します。 CyberSecurityHelp:独自の 脆弱性 DBを公開しており、リスク評価、パッチの提供有無、Atack Vector 、Public exploit(実際の悪用有無)、Exploit availability(PoC、攻撃コードの有無)、回避策などの情報をタ イムリ ーに提供するサイトで、公開されるタイミングはCVE採番後。 URL: https://www.cybersecurity-help.cz/ Lunasec:米セキュリティ企業。2021年に公開されたLog4Shellの際にもブログをいち早く公開しており、影響を受ける条件、PoC、回避策などをわかりやすくブログにまとめている URL: https://www.lunasec.io/docs/blog/spring-rce-vulnerabilities/ Bleeping Computer: 脆弱性 情報を含む最新のセキュリティニュースをいち早く提供する海外サイト URL: https://www.bleepingcomputer.com/tag/vulnerability/ Security Next: 脆弱性 情報を含む最新のセキュリティニュースを日本語でいち早く提供するサイト URL: https://www.security-next.com/135304 piyolog: 脆弱性 情報を含む最新のセキュリティニュースがわかりやすく日本語で解説されたブログ URL: https://piyolog.hatenadiary.jp/entry/2022/04/01/065946 GitHub Advisory Database: GitHub Security Advisoriesで公開された 脆弱性 や、依存関係を追跡して得られた依存先ソフトウェアの 脆弱性 を一覧表示する URL: https://github.com/advisories ただし、これらはあくまで参考情報ですので、最終的に一次情報源の確認は必須です。 公的機関( JPCERT/CC 、 JVN 、 IPA 、NIST NVD) 開発元(今回はSpring) URL: https://spring.io/security/ トリアージ これまでの情報を整理した上で、全社周知要否( トリアージ )を2つの観点で実施します。 影響範囲の確認 各プロジェクトにおける利用状況を確認するため、FutureVulsのグループセットからソフトウェア名で検索します。 参考: グループセット内のソフトウェア名検索 緊急度合いの確認 脆弱性 管理チームで利用している緊急度 判断表 をもとに、緊急度を確認します。 No RCE PoC 開発元の評価 緊急度 1 可能 有 高以上 緊急 2 可能 無 高以上 高 3 不可 有 高以上 高 4 不可 無 高以上 中 5 可能 有 中以下 高 6 可能 無 中以下 中 7 不可 有 中以下 中 8 不可 無 中以下 中 9 可能 有 未評価 緊急 10 可能 無 未評価 高 11 不可 有 未評価 高 12 不可 無 未評価 中 公表時点では、No.9の「緊急」に該当しました。 トリアージ の結果、全社周知が必要と判断したため次のステップに進みます。 全社周知 全社周知の準備を進めつつ、MITRE社によるCVE採番状況のチェックもしながら状況変化をキャッチアップします。 URL: https://cve.mitre.org/cve/search_cve_list.html PoC、回避策の検証 まずはPoCを探すところからですが、例として以下の リポジトリ では、 GitHub で公開されているPoCを自動収集しています。 URL: https://github.com/nomi-sec/PoC-in-GitHub/tree/master 当時、実際に対応した際は以下のPoCが出回っており、こちらを実施しました。 LunaSec社も改善に加わっているようです。(※中には悪意のあるPoCを公開している リポジトリ もあるため、コードの中身をよく読んで、社内と隔離されたネットワーク上で実行するようにする) URL: https://github.com/reznok/Spring4Shell-POC dockerを利用するため容易に実施できるものになっています。(環境構築に時間が掛かる場合があります) また、Lunasecによると、脆弱パターンの ブラックリスト を記載したInitBinderを挿入することが緩和策になると謳っています。 全社周知(社内 ポータルサイト へのニュース掲載) 内容としては以下の項目をまとめています。 脆弱性 の概要(RCEの可否、PoCの有無も含む) 影響を受けるソフトウェアバージョンおよび条件 根本対策(パッチ公開情報) 回避策(PoCの実施結果) 攻撃の兆候がないかログの確認を依頼(SOCがあれば活用) 参考情報 CVE採番後の対応 MITER社から公開されるCVE採番情報を確認したら、以下のチェックおよび対応します。 公的機関、開発元から情報が公開・更新されているか 情報収集の際にチェックした各発信元の情報が更新されているか パッチのリリース状況 社内 ポータルサイト に投稿した記事に追記 個別周知 次に、 脆弱性 バージョンを利用している該当プロジェクトに対して個別周知、対応期限を設定します。 オーガニゼーショントピック(CVE別にトピックを立てて情報共有できる機能)の投稿 参考: トピックによる情報共有・注意喚起 送信すると、該当するグループの全メンバへメールが発報されます。 また、SlackやTeamsへの連携も可能です。 Slack連携: https://help.vuls.biz/manual/notification/slack_app/ Teams連携: https://help.vuls.biz/manual/notification/teams/ スペシャ ル警戒タグの設定 こちらの機能を簡単にご紹介します。 脆弱性 単位で独自のタグを付けられる 例えば、「全社周知対象(トピック掲載あり)」や「緊急対応必須」として、ユーザへの情報認識を早めたり、CSIRT側で後から状況を見返す時に便利になると思います。 タグは1つのCVE-IDに対して複数設定できます。 対応期限を設定でき、該当タスクの対応期限を一括で上書き更新できる CVE検知済みのグループに対して設定された旨の通知ができる(通知の際、コメントは不可) 参考: スペシャル警戒タグ機能 今回の対応期限は判断日の5営業日後としました。 各プロジェクトの 脆弱性 情報のサマリに警戒タグが入りました。 対応状況の確認 最後に各プロジェクトの対応状況を後追いします。 FutureVulsではタスクステータスを用いて、対応状況を管理できます。 タスク対応の流れは以下です。 引用: ステータス 次のアクションとして、 スペシャ ル警戒タグで設定した対応期限を超過したタスクに対してリマインドします。 対応期限の翌営業日になったら、グループセットのタスクタブから、フィルターでCVE-2022-22965のタスクステータスが「NEW(新規)」と「INVESTIGATING(調査中)」を抽出します。 タスクに一括チェックした上で、タスクを編集からコメントを送信することで、該当グループの所属メンバ全員にメールを送信できます。 その後はタスクのコメント上でのやり取りも可能です。 対応結果 模擬訓練に参加したプロジェクトの対応結果は以下のようになりました。 プロジェクト 結果 ソフトウェア別の対応 プロジェクトA CPE スキャン(*)にて検出され、対応完了 Spring Framework 5.3.17が検出対象となったため、最新バージョンに CPE を更新し、クローズ プロジェクトB CPE スキャンでは検出できず、個別連絡にて対応依頼の上、完了 Spring Boot 2.5.0は Spring Framework に依存しているが、 CPE スキャンでは検出されず。原因と対応策は下記課題3を参照。Spring Bootの最新バージョンに CPE を更新。 プロジェクトC CPE スキャンにて検出され、対応完了 JDK 8は影響を受けないが検出対象となった。原因と対応策は下記課題4を参照。タスクのステータスを「NOT_AFFECTED(影響なし)」に変更し、クローズ。 * CPE スキャン:擬似サーバ上にソフトウェア名、バージョン情報を手動登録し、日次でスキャンさせる機能。 参考: CPEスキャン 見つかった課題 模擬訓練を通して発見した課題についてまとめます。 No 課題 対応策 補足 1 CVE未採番の状態でもユーザへ第一報を通知したい グループセットのソフトウェアタブで「Spring」と検索し、該当するプロジェクトメンバに連絡する FutureVulsのオーガニゼーショントピックや スペシャ ル警戒タグはCVE採番済みの 脆弱性 に対してしか利用できない。現状はチャットなど別の手段で連絡するしかない。 2 対応済みのプロジェクトにもオーガニゼーショントピックが通知されてしまう オーガニゼーショントピックをなるべく早めに発報することで、対応済み案件へ通知される確率を下げる。またトピック内に注意書きを入れる。 オーガニゼーショントピックは 脆弱性 単位で通知する機能となっており、各タスクのステータスまでチェックしないため、既に対応がクローズしている案件にも通知されるため、注意が必要。 3 Spring Bootの脆弱バージョンを CPE スキャンしても、CVE-2022-22965が検出されない 依存ライブラリスキャンを利用する CPE スキャンは登録したライブラリや フレームワーク の依存ライブラリの 脆弱性 までは検知できない。依存ライブラリスキャンの手法の1つとして GitHub Security Alerts連携 がある。PoCで利用したSpring Bootのpom. xml をスキャン対象の GitHub リポジトリ に手動であげるとFutureVuls側で検出されることを確認。 4 JDK 8であれば影響を受けないはずだが検出されてしまう トピック内に影響を受ける条件を記載し、プロジェクト側にステータスを「NOT_AFFECTED(影響なし)」に変更してもらうようにする FutureVulsには外部条件を考慮した検知の仕組みは現状ないため、ステータスを変更してもらう必要がある 最後に 今回、 脆弱性 対応模擬訓練を行うことで、FutureVulsを活用した緊急時の一連のフローを確認することができました。 また、課題はいくつか見つかりましたが、運用でカバーできそうであることもわかりました。 FutureVulsはリリース頻度が高いため、このあたりの課題が機能アップデートによって解消されることに期待したいと思います! 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア 執筆: @fukuyama.kenta ( Shodo で執筆されました )
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 2023年6月に Amazon Verified Permissions というサービスがGAしましたが、まだ利用している方は少ないのではないでしょうか。 アプリケーションとは切り離して認可ポリシーを管理できるこのサービスですが、ではどうやってポリシーや スキーマ を管理したら良いか考えたところ、 AWS CDK が便利だったので、今回の記事ではその方法を紹介します。 Amazon Verified Permissions とは スキーマ Amazon Verified Permissions を CDK でデプロイする ディレクトリ構成 Cedar 言語の VS Code 拡張 スキーマ定義 ポリシー CDK コンストラクト SDKで認可を判定させてみた さいごに Amazon Verified Permissions とは ポリシーを事前に登録することで、アプリケーションの認可の判定を、アプリケーションの外側で行うことができるマネージドサービスです。 ポリシーは Cedar という DSL で記述します。例えば次のようなポリシー: permit ( principal in MyApp::User::"alice", action in [MyApp::Action::"GET"], resource in MyApp::Album::"animals" ); これは alice というユーザーに animals というアルバムへの GET アクセスを permit (許可する)という意味になります。 principal 「誰に」、 resource 「どのリソースに対する」、 action 「どんな操作」を許可・拒否するかを基本的には書いていくことになります。 定義したポリシーに対して、アプリケーションコードから認可結果を判定させるリク エス トを送信できます。 例として次のように AWS SDK for JavaScript の IsAuthorizedCommand を使って、特定の状況における認可を判定させることができます。 // authorize.ts import { VerifiedPermissionsClient , IsAuthorizedCommand } from "@aws-sdk/client-verifiedpermissions" ; const client = new VerifiedPermissionsClient ( { region: "ap-northeast-1" } ); const command = new IsAuthorizedCommand ( { policyStoreId: "DUMMYSTOREID" , principal: { entityType: "MyApp::User" , entityId: "alice" } , action: { actionType: "MyApp::Action" , actionId: "GET" } , resource: { entityType: "MyApp::Album" , entityId: "animals" } , } ); client.send ( command ) .then (( result ) => { console .log ( result.decision ); // ALLOW } ); この場合ユーザーは alice 、操作対象のリソースは animals というアルバム、操作は GET なので、先ほど例に示した許可ポリシーに当てはまり、コマンドの結果の decision プロパティには ALLOW が返ってきます。(拒否される場合は DENY が返ります。) この結果を見て、後続の処理は完全にアプリケーション側の裁量で行うことになります。 ( principal を Cognito と連携したり、 principal や resource に属性を追加したり、 context でコンテキストを追加したりすることもできますが、今回はこれぐらいのシンプルな説明に留めておきます。) スキーマ Amazon Verified Permissions には スキーマ と呼ばれる概念があります。 スキーマ は前述の principal 、 resource 、 action のフォーマットを定義するものです。言葉で説明しても理解が難しいと思うので、実際にマネジメントコンソールで見てみると分かりやすいと思います。 定義済みの スキーマ の画面です。 principal と resource で使った User や Album は「エンティティタイプ」セクションで定義し、 action で使った GET は「アクション」セクションで定義します。マネジメントコンソールで追加した スキーマ 定義は JSON フォーマットと相互変換可能であり、上の スキーマ 定義は次の JSON と同等です。(画面の「 JSON モード」から確認でき、 決まったフォーマット が使われます) { " MyApp ": { " actions ": { " GET ": { " appliesTo ": { " context ": { " attributes ": {} , " type ": " Record " } , " resourceTypes ": [ " Album " ] , " principalTypes ": [ " User " ] } } } , " entityTypes ": { " User ": { " memberOfTypes ": [] , " shape ": { " attributes ": {} , " type ": " Record " } } , " Album ": { " shape ": { " type ": " Record ", " attributes ": {} } , " memberOfTypes ": [] } } } } Amazon Verified Permissions を CDK でデプロイする Amazon Verified Permissions の スキーマ ( JSON )とポリシー(Cedar)はいずれも宣言型のコードで表現されるので、そのデプロイを CDK で行うと相性が良いのではないかと思いました。そう考えた理由は以下です。 認可のための スキーマ やポリシーは、アプリケーションデータのように頻繁に変わるわけではない そのためコード リポジトリ で管理し、レビューと変更を確認できる状態にしたい コード リポジトリ で管理するなら、デプロイも自動化したい IaC でデプロイしよう まず、 Amazon Verified Permissions のリソース構築は CloudFormation ではサポート されています。しかし、 JSON のスキーマ定義 や Cedar のポリシー定義 を String で記載する必要があり、CloudFormation テンプレートで管理するのはつらそうです。そこで プログラミング言語 で IaC を実現できる CDK を活用して、管理しやすくしてみましょう。 ディレクト リ構成 以下の CDK アプリの リポジトリ 構成で話を進めます。(重要なファイル/ ディレクト リ以外は省略しています。) sample-app/  ├ .vscode/  │ └ settings.json <- VS Code で Cedar を書きやすくするための設定ファイル  ├ bin/  │ └ my-app.ts  ├ cedar/  │ ├ 0001.cedar <- ポリシーその1  │ ├ 0002.cedar <- ポリシーその2  │ └ cedarschema.json <- スキーマ定義  ├ lib/  │ ├ constructs/  │ │ └ my-construct.ts <- Verified Permissionsリソースをデプロイするコンストラクト  │ └ my-stack.ts  ├ cdk.context.json  ├ cdk.json  ├ package.json  ├ tsconfig.json  └ yarn.lock Cedar 言語の VS Code 拡張 Cedar 言語を書きやすくするための VS Code 拡張があります。とりあえず入れておきましょう。 https://marketplace.visualstudio.com/items?itemName=cedar-policy.vscode-cedar 続いて .vscode/settings.json に以下の設定を追加します。ポリシーや スキーマ 定義の文法が正しくないときや、 スキーマ 定義に一致しないポリシーを書いたときにエラーを出してくれるようになります。保存時にファイルのフォーマットもそろえてくれます。 // .vscode/settings.json { " [cedar] ": { " editor.tabSize ": 2 , " editor.wordWrapColumn ": 80 , " editor.formatOnSave ": true , " editor.defaultFormatter ": " cedar-policy.vscode-cedar " , } , " cedar.schemaFile ": " /cedar/cedarschema.json ", " cedar.autodetectSchemaFile ": true , } スキーマ 定義 前述の例の JSON スキーマ 定義をそのまま cedar/cedarschema.json に書きます。 cedarschema.json もしくは *.cedarschema.json というファイル名にすることで、 Cedar の JSON スキーマ ファイルとして VS Code 拡張に認識されるようになります。 // cedar/cedarschema.json { " MyApp ": { " actions ": { " GET ": { " appliesTo ": { " context ": { " attributes ": {} , " type ": " Record " } , " resourceTypes ": [ " Album " ] , " principalTypes ": [ " User " ] } } } , " entityTypes ": { " User ": { " memberOfTypes ": [] , " shape ": { " attributes ": {} , " type ": " Record " } } , " Album ": { " shape ": { " type ": " Record ", " attributes ": {} } , " memberOfTypes ": [] } } } } ポリシー ポリシーを2つ追加してみます。 *.cedar 拡張子のファイル名が Cedar のポリシーファイルとして VS Code 拡張に認識されます。1つの *.cedar ファイルには1つのポリシーしか書けないので、2つのファイルを追加します。 1つ目は alice というユーザーに animals というアルバムへの GET アクセスを許可するポリシーです。( cedar/0001.cedar ) コメントも自由に書けるので、ポリシーの説明などを記載しておくと後から分かりやすいでしょう。 // alice に animals アルバムへのアクセスを許可する permit ( principal in MyApp::User::"alice", action in [MyApp::Action::"GET"], resource in MyApp::Album::"animals" ); 2つ目は bob というユーザーに flowers というアルバムへの GET アクセスを許可するポリシーです。( cedar/0002.cedar ) // bob に flowers アルバムへのアクセスを許可する permit ( principal in MyApp::User::"bob", action in [MyApp::Action::"GET"], resource in MyApp::Album::"flowers" ); CDK コンスト ラク ト いよいよ Verified Permissions のリソースを CDK でデプロイするコンスト ラク トです。 CDK v2.94.0 では Verified Permissions のコンスト ラク トは4つ利用できますが、L2 コンスト ラク トはなく、いずれも L1 です。 CfnPolicyStore CfnPolicy CfnPolicyTemplate CfnIdentitySource 今回は上の2つを利用します。 まずは Verified Permissions のポリシーストアを作成します。 // lib/constructs/my-construct.ts import * as fs from "fs" ; import * as path from "path" ; import * as vp from "aws-cdk-lib/aws-verifiedpermissions" ; import { Construct } from "constructs" ; export class VerifiedPermissionsConstruct extends Construct { constructor( scope: Construct , id: string ) { super( scope , id ); const cedarDir = path.join ( __dirname , ".." , ".." , "cedar" ); // スキーマ定義をファイルから読み込む const schema = fs.readFileSync ( path.join ( cedarDir , "cedarschema.json" ), "utf-8" ); // ポリシーストア const policyStore = new vp.CfnPolicyStore ( this , "PolicyStore" , { validationSettings: { mode: "STRICT" } , schema: { cedarJson: schema } , } ); // ... } } スキーマ 定義はポリシーストア作成時に渡す必要があり、 fs モジュールの readFileSync メソッドで先ほど定義した cedarschema.json ファイルを読み込ませています。 デプロイすると、マネジメントコンソールでも作成されたポリシーストアを確認できました。ポリシーストア ID は自動で採番されます。 続いてポリシーを追加していきます。以下のコードは、 cedar ディレクト リにある *.cedar ファイルのみをループして読み込んで、ファイルの数だけポリシーをポリシーストアに追加しています。 // lib/constructs/my-construct.ts export class VerifiedPermissionsConstruct extends Construct { constructor( scope: Construct , id: string ) { // ... // cedar ディレクトリのファイル一覧を読み込む const files = fs.readdirSync ( cedarDir ); files .filter (( file ) => file.endsWith ( ".cedar" )) // .cedar ファイルのみを抽出 .map (( file ) => { const content = fs.readFileSync ( path.join ( cedarDir , file ), "utf-8" ); // ポリシー new vp.CfnPolicy ( this , `Policy- ${ file.split( "." )[ 0 ] } ` , { policyStoreId: policyStore.attrPolicyStoreId , // ポリシーストア ID を参照 definition: { static : { statement: content } } , // 静的ポリシーを追加 } ); } ); } } ポリシーのリソースIDはファイル名を利用して付けることで重複がないようにしています。また map 関数で cedar ディレクト リ内の全 Cedar ファイルをループして処理することで、ポリシーを追加・削除したいときは cedar ディレクト リ内を変更するだけでよく、CDK コードの修正は必要ありません。このようなことができるのも プログラミング言語 で処理を書ける CDK ならではの強みです。 デプロイすると、定義した通りのポリシーがコンソールでも確認できました。ポリシー ID は自動で採番されています。 以上で CDK を使った Verified Permission の構築は完了です。 SDK で認可を判定させてみた 構築した Verified Permissions を確認するために、簡単にローカル環境から SDK でリク エス トを投げてみます。 // authorize.ts import { VerifiedPermissionsClient , IsAuthorizedCommand } from "@aws-sdk/client-verifiedpermissions" ; const client = new VerifiedPermissionsClient ( { region: "ap-northeast-1" } ); const command = new IsAuthorizedCommand ( { policyStoreId: "DUMMYSTOREID" , // マネジメントコンソールから確認できるポリシーストアID principal: { entityType: "MyApp::User" , entityId: "alice" } , action: { actionType: "MyApp::Action" , actionId: "GET" } , resource: { entityType: "MyApp::Album" , entityId: "animals" } , } ); client.send ( command ) .then (( result ) => { console .log ( result.decision ); // ALLOW console .log ( JSON .stringify ( result.determiningPolicies )); // [{"policyId":"JLu29q39V62CChNcpFgk3x"}] } ); ユーザーが alice 、リソースが animals アルバムの場合、判定結果は ALLOW となり、どのポリシーで判定されたのかも determiningPolicies プロパティで確認できます。 今度は alice に許可していない flowers アルバムへの認可を判定させてみます。 // authorize.ts const command = new IsAuthorizedCommand ( { policyStoreId: "DUMMYSTOREID" , principal: { entityType: "MyApp::User" , entityId: "alice" } , action: { actionType: "MyApp::Action" , actionId: "GET" } , resource: { entityType: "MyApp::Album" , entityId: "flowers" } , } ); client.send ( command ) .then (( result ) => { console .log ( result.decision ); // DENY console .log ( JSON .stringify ( result.determiningPolicies )); // [] } ); どのポリシーにも当てはまらないため determiningPolicies は空の配列であり、判定結果はデフォルトの DENY となりました。 ちなみに GetPolicyCommand を使うことで、ポリシーの内容を SDK からも取得することができます。 // authorize.ts const command = new GetPolicyCommand ( { policyStoreId: "DUMMYSTOREID" , policyId: "JLu29q39V62CChNcpFgk3x" , } ); client.send ( command ) .then (( result ) => { console .log ( result.definition?. static ?.statement ); } ); // (結果) // // alice に animals アルバムへのアクセスを許可する // permit ( // principal in MyApp::User::"alice", // action in [MyApp::Action::"GET"], // resource in MyApp::Album::"animals" // ); さいごに まだまだ利用が少ないと思われる Amazon Verified Permissions ですが、CDK を使って管理・デプロイすることと相性が良く、使ってみたいという気持ちになりました。 DSL なので提供されている VS Code 拡張を利用したいですし、そうするとファイルの読み込みや、ポリシーファイルの数だけループ処理ができる CDK でデプロイするのが最も便利だと感じました。 Amazon Verified Permissions 自体も、工夫次第では様々な使い方ができそうなので、今後が楽しみなサービスです。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア 執筆: @kou.kinyo 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 2023年6月に Amazon Verified Permissions というサービスがGAしましたが、まだ利用している方は少ないのではないでしょうか。 アプリケーションとは切り離して認可ポリシーを管理できるこのサービスですが、ではどうやってポリシーや スキーマ を管理したら良いか考えたところ、 AWS CDK が便利だったので、今回の記事ではその方法を紹介します。 Amazon Verified Permissions とは スキーマ Amazon Verified Permissions を CDK でデプロイする ディレクトリ構成 Cedar 言語の VS Code 拡張 スキーマ定義 ポリシー CDK コンストラクト SDKで認可を判定させてみた さいごに Amazon Verified Permissions とは ポリシーを事前に登録することで、アプリケーションの認可の判定を、アプリケーションの外側で行うことができるマネージドサービスです。 ポリシーは Cedar という DSL で記述します。例えば次のようなポリシー: permit ( principal in MyApp::User::"alice", action in [MyApp::Action::"GET"], resource in MyApp::Album::"animals" ); これは alice というユーザーに animals というアルバムへの GET アクセスを permit (許可する)という意味になります。 principal 「誰に」、 resource 「どのリソースに対する」、 action 「どんな操作」を許可・拒否するかを基本的には書いていくことになります。 定義したポリシーに対して、アプリケーションコードから認可結果を判定させるリク エス トを送信できます。 例として次のように AWS SDK for JavaScript の IsAuthorizedCommand を使って、特定の状況における認可を判定させることができます。 // authorize.ts import { VerifiedPermissionsClient , IsAuthorizedCommand } from "@aws-sdk/client-verifiedpermissions" ; const client = new VerifiedPermissionsClient ( { region: "ap-northeast-1" } ); const command = new IsAuthorizedCommand ( { policyStoreId: "DUMMYSTOREID" , principal: { entityType: "MyApp::User" , entityId: "alice" } , action: { actionType: "MyApp::Action" , actionId: "GET" } , resource: { entityType: "MyApp::Album" , entityId: "animals" } , } ); client.send ( command ) .then (( result ) => { console .log ( result.decision ); // ALLOW } ); この場合ユーザーは alice 、操作対象のリソースは animals というアルバム、操作は GET なので、先ほど例に示した許可ポリシーに当てはまり、コマンドの結果の decision プロパティには ALLOW が返ってきます。(拒否される場合は DENY が返ります。) この結果を見て、後続の処理は完全にアプリケーション側の裁量で行うことになります。 ( principal を Cognito と連携したり、 principal や resource に属性を追加したり、 context でコンテキストを追加したりすることもできますが、今回はこれぐらいのシンプルな説明に留めておきます。) スキーマ Amazon Verified Permissions には スキーマ と呼ばれる概念があります。 スキーマ は前述の principal 、 resource 、 action のフォーマットを定義するものです。言葉で説明しても理解が難しいと思うので、実際にマネジメントコンソールで見てみると分かりやすいと思います。 定義済みの スキーマ の画面です。 principal と resource で使った User や Album は「エンティティタイプ」セクションで定義し、 action で使った GET は「アクション」セクションで定義します。マネジメントコンソールで追加した スキーマ 定義は JSON フォーマットと相互変換可能であり、上の スキーマ 定義は次の JSON と同等です。(画面の「 JSON モード」から確認でき、 決まったフォーマット が使われます) { " MyApp ": { " actions ": { " GET ": { " appliesTo ": { " context ": { " attributes ": {} , " type ": " Record " } , " resourceTypes ": [ " Album " ] , " principalTypes ": [ " User " ] } } } , " entityTypes ": { " User ": { " memberOfTypes ": [] , " shape ": { " attributes ": {} , " type ": " Record " } } , " Album ": { " shape ": { " type ": " Record ", " attributes ": {} } , " memberOfTypes ": [] } } } } Amazon Verified Permissions を CDK でデプロイする Amazon Verified Permissions の スキーマ ( JSON )とポリシー(Cedar)はいずれも宣言型のコードで表現されるので、そのデプロイを CDK で行うと相性が良いのではないかと思いました。そう考えた理由は以下です。 認可のための スキーマ やポリシーは、アプリケーションデータのように頻繁に変わるわけではない そのためコード リポジトリ で管理し、レビューと変更を確認できる状態にしたい コード リポジトリ で管理するなら、デプロイも自動化したい IaC でデプロイしよう まず、 Amazon Verified Permissions のリソース構築は CloudFormation ではサポート されています。しかし、 JSON のスキーマ定義 や Cedar のポリシー定義 を String で記載する必要があり、CloudFormation テンプレートで管理するのはつらそうです。そこで プログラミング言語 で IaC を実現できる CDK を活用して、管理しやすくしてみましょう。 ディレクト リ構成 以下の CDK アプリの リポジトリ 構成で話を進めます。(重要なファイル/ ディレクト リ以外は省略しています。) sample-app/  ├ .vscode/  │ └ settings.json <- VS Code で Cedar を書きやすくするための設定ファイル  ├ bin/  │ └ my-app.ts  ├ cedar/  │ ├ 0001.cedar <- ポリシーその1  │ ├ 0002.cedar <- ポリシーその2  │ └ cedarschema.json <- スキーマ定義  ├ lib/  │ ├ constructs/  │ │ └ my-construct.ts <- Verified Permissionsリソースをデプロイするコンストラクト  │ └ my-stack.ts  ├ cdk.context.json  ├ cdk.json  ├ package.json  ├ tsconfig.json  └ yarn.lock Cedar 言語の VS Code 拡張 Cedar 言語を書きやすくするための VS Code 拡張があります。とりあえず入れておきましょう。 https://marketplace.visualstudio.com/items?itemName=cedar-policy.vscode-cedar 続いて .vscode/settings.json に以下の設定を追加します。ポリシーや スキーマ 定義の文法が正しくないときや、 スキーマ 定義に一致しないポリシーを書いたときにエラーを出してくれるようになります。保存時にファイルのフォーマットもそろえてくれます。 // .vscode/settings.json { " [cedar] ": { " editor.tabSize ": 2 , " editor.wordWrapColumn ": 80 , " editor.formatOnSave ": true , " editor.defaultFormatter ": " cedar-policy.vscode-cedar " , } , " cedar.schemaFile ": " /cedar/cedarschema.json ", " cedar.autodetectSchemaFile ": true , } スキーマ 定義 前述の例の JSON スキーマ 定義をそのまま cedar/cedarschema.json に書きます。 cedarschema.json もしくは *.cedarschema.json というファイル名にすることで、 Cedar の JSON スキーマ ファイルとして VS Code 拡張に認識されるようになります。 // cedar/cedarschema.json { " MyApp ": { " actions ": { " GET ": { " appliesTo ": { " context ": { " attributes ": {} , " type ": " Record " } , " resourceTypes ": [ " Album " ] , " principalTypes ": [ " User " ] } } } , " entityTypes ": { " User ": { " memberOfTypes ": [] , " shape ": { " attributes ": {} , " type ": " Record " } } , " Album ": { " shape ": { " type ": " Record ", " attributes ": {} } , " memberOfTypes ": [] } } } } ポリシー ポリシーを2つ追加してみます。 *.cedar 拡張子のファイル名が Cedar のポリシーファイルとして VS Code 拡張に認識されます。1つの *.cedar ファイルには1つのポリシーしか書けないので、2つのファイルを追加します。 1つ目は alice というユーザーに animals というアルバムへの GET アクセスを許可するポリシーです。( cedar/0001.cedar ) コメントも自由に書けるので、ポリシーの説明などを記載しておくと後から分かりやすいでしょう。 // alice に animals アルバムへのアクセスを許可する permit ( principal in MyApp::User::"alice", action in [MyApp::Action::"GET"], resource in MyApp::Album::"animals" ); 2つ目は bob というユーザーに flowers というアルバムへの GET アクセスを許可するポリシーです。( cedar/0002.cedar ) // bob に flowers アルバムへのアクセスを許可する permit ( principal in MyApp::User::"bob", action in [MyApp::Action::"GET"], resource in MyApp::Album::"flowers" ); CDK コンスト ラク ト いよいよ Verified Permissions のリソースを CDK でデプロイするコンスト ラク トです。 CDK v2.94.0 では Verified Permissions のコンスト ラク トは4つ利用できますが、L2 コンスト ラク トはなく、いずれも L1 です。 CfnPolicyStore CfnPolicy CfnPolicyTemplate CfnIdentitySource 今回は上の2つを利用します。 まずは Verified Permissions のポリシーストアを作成します。 // lib/constructs/my-construct.ts import * as fs from "fs" ; import * as path from "path" ; import * as vp from "aws-cdk-lib/aws-verifiedpermissions" ; import { Construct } from "constructs" ; export class VerifiedPermissionsConstruct extends Construct { constructor( scope: Construct , id: string ) { super( scope , id ); const cedarDir = path.join ( __dirname , ".." , ".." , "cedar" ); // スキーマ定義をファイルから読み込む const schema = fs.readFileSync ( path.join ( cedarDir , "cedarschema.json" ), "utf-8" ); // ポリシーストア const policyStore = new vp.CfnPolicyStore ( this , "PolicyStore" , { validationSettings: { mode: "STRICT" } , schema: { cedarJson: schema } , } ); // ... } } スキーマ 定義はポリシーストア作成時に渡す必要があり、 fs モジュールの readFileSync メソッドで先ほど定義した cedarschema.json ファイルを読み込ませています。 デプロイすると、マネジメントコンソールでも作成されたポリシーストアを確認できました。ポリシーストア ID は自動で採番されます。 続いてポリシーを追加していきます。以下のコードは、 cedar ディレクト リにある *.cedar ファイルのみをループして読み込んで、ファイルの数だけポリシーをポリシーストアに追加しています。 // lib/constructs/my-construct.ts export class VerifiedPermissionsConstruct extends Construct { constructor( scope: Construct , id: string ) { // ... // cedar ディレクトリのファイル一覧を読み込む const files = fs.readdirSync ( cedarDir ); files .filter (( file ) => file.endsWith ( ".cedar" )) // .cedar ファイルのみを抽出 .map (( file ) => { const content = fs.readFileSync ( path.join ( cedarDir , file ), "utf-8" ); // ポリシー new vp.CfnPolicy ( this , `Policy- ${ file.split( "." )[ 0 ] } ` , { policyStoreId: policyStore.attrPolicyStoreId , // ポリシーストア ID を参照 definition: { static : { statement: content } } , // 静的ポリシーを追加 } ); } ); } } ポリシーのリソースIDはファイル名を利用して付けることで重複がないようにしています。また map 関数で cedar ディレクト リ内の全 Cedar ファイルをループして処理することで、ポリシーを追加・削除したいときは cedar ディレクト リ内を変更するだけでよく、CDK コードの修正は必要ありません。このようなことができるのも プログラミング言語 で処理を書ける CDK ならではの強みです。 デプロイすると、定義した通りのポリシーがコンソールでも確認できました。ポリシー ID は自動で採番されています。 以上で CDK を使った Verified Permission の構築は完了です。 SDK で認可を判定させてみた 構築した Verified Permissions を確認するために、簡単にローカル環境から SDK でリク エス トを投げてみます。 // authorize.ts import { VerifiedPermissionsClient , IsAuthorizedCommand } from "@aws-sdk/client-verifiedpermissions" ; const client = new VerifiedPermissionsClient ( { region: "ap-northeast-1" } ); const command = new IsAuthorizedCommand ( { policyStoreId: "DUMMYSTOREID" , // マネジメントコンソールから確認できるポリシーストアID principal: { entityType: "MyApp::User" , entityId: "alice" } , action: { actionType: "MyApp::Action" , actionId: "GET" } , resource: { entityType: "MyApp::Album" , entityId: "animals" } , } ); client.send ( command ) .then (( result ) => { console .log ( result.decision ); // ALLOW console .log ( JSON .stringify ( result.determiningPolicies )); // [{"policyId":"JLu29q39V62CChNcpFgk3x"}] } ); ユーザーが alice 、リソースが animals アルバムの場合、判定結果は ALLOW となり、どのポリシーで判定されたのかも determiningPolicies プロパティで確認できます。 今度は alice に許可していない flowers アルバムへの認可を判定させてみます。 // authorize.ts const command = new IsAuthorizedCommand ( { policyStoreId: "DUMMYSTOREID" , principal: { entityType: "MyApp::User" , entityId: "alice" } , action: { actionType: "MyApp::Action" , actionId: "GET" } , resource: { entityType: "MyApp::Album" , entityId: "flowers" } , } ); client.send ( command ) .then (( result ) => { console .log ( result.decision ); // DENY console .log ( JSON .stringify ( result.determiningPolicies )); // [] } ); どのポリシーにも当てはまらないため determiningPolicies は空の配列であり、判定結果はデフォルトの DENY となりました。 ちなみに GetPolicyCommand を使うことで、ポリシーの内容を SDK からも取得することができます。 // authorize.ts const command = new GetPolicyCommand ( { policyStoreId: "DUMMYSTOREID" , policyId: "JLu29q39V62CChNcpFgk3x" , } ); client.send ( command ) .then (( result ) => { console .log ( result.definition?. static ?.statement ); } ); // (結果) // // alice に animals アルバムへのアクセスを許可する // permit ( // principal in MyApp::User::"alice", // action in [MyApp::Action::"GET"], // resource in MyApp::Album::"animals" // ); さいごに まだまだ利用が少ないと思われる Amazon Verified Permissions ですが、CDK を使って管理・デプロイすることと相性が良く、使ってみたいという気持ちになりました。 DSL なので提供されている VS Code 拡張を利用したいですし、そうするとファイルの読み込みや、ポリシーファイルの数だけループ処理ができる CDK でデプロイするのが最も便利だと感じました。 Amazon Verified Permissions 自体も、工夫次第では様々な使い方ができそうなので、今後が楽しみなサービスです。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア 執筆: @kou.kinyo 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
XI本部 スマート ソサエティ センターの飯田です。 私は「DigSports」というAIによって運動能力を測定し、どのスポーツに向いているかを提案する製品のプロダクトオーナーを担当しています。 DigSportsは、運動が苦手な子どもたちや運動から遠ざかってしまった働く世代の方々に対して、自分に合ったスポーツを見つけられる機会を提供し、運動を始めるきっかけや運動習慣化を後押ししています。 smart-society.dentsusoken.com そんなDigSportsなのですが、技術的な アーキテクチャ をガラッと変えた新しいバージョンをリリースしました。 AIで適性のあるスポーツを判定する「DigSports」の新バージョンを提供開始 | お知らせ | 電通総研 本エントリーでは、新バージョン開発をプロダクトオーナーとして経験した悩みや苦悩を書いてみます。 不安との闘い 要約すると、「不安との闘い」が悩みの根源でした。 技術的なこと、BizDev的なこと、営業戦略、様々な部分で決断が求められました。 自分の選択が間違っていないか? 機能開発の優先度を間違えてないか? 「UX的に良いものをつくる」「技術的に良いものをつくる」「つくったものを広める」そのバランスが良いものになっているか? ...等です。 アジャイル で試行錯誤しながらプロダクトを カイゼン していけるとしても、投資対効果を最大にするために妥当な判断ができているか、「これでいい!」「あ、やっぱダメかも」を繰り返しながら、開発を進めていました。 その中で感じた書いてみます。 1. 資産を作っているか?負債を作っていないか? DigSportsは エンタープライズ なプロダクトと毛色が違うため、 エンタープライズ 製品に比べ、ソフトウェアの規模はそこまで大きくありません。そのため、開発にあたっては「修正対応のしやすさのため、シンプルさを維持する」ことを意識しています。 また、技術的な側面だけでなく、ビジネス開発の観点からもシンプルさを意識しています。 アジャイル など試行錯誤していく開発アプローチは、開発・修正のサイクルを短く回すことができますが、 一方、資産となる市場販売目的のソフトウェア開発にかかった費用は、 3年間の減価償却が行われます。 つまり、今回の開発コストが3年間に按分されることになるので、不要なものを開発すると“ゴミ”・“負債”を作っていることになるので、“資産”として意味のあるものを残していかないといけません。 「今、行おうとしている開発は、改善なのか?改悪なのか?」 もちろん不確実性の中での判断なので、正解はありませんが、3年後を意識して決断していくのは悩みがつきませんでした。 2. 同じ結論になったとしても、1周回ってくると景色やブレなさが違う そのように「資産を作れているか?」と悩みながら進めていく中で、ユーザー・顧客の反応や開発メンバーとの議論は、悩みながら決断をしていく際の大きな拠り所でした。 ユーザー・顧客の反応を見る前後でアイディアが変わらなかったとしても、 ヒアリ ングや議論を経てみると、一周回って自分自身への納得感(ひいては、他人に説得する際の自信)がついている感覚がありました。 3. 「ユーザーの声を聞いている」ということに逃げていないか プロダクト開発における教科書的な模範解答としては「ユーザーの声を聞いて、 アジャイル 的に PDCA を回して改善する」だと思います。実際、DigSportsのリニューアルにおいてもそこは意識しながら進めました。 しかし、ふとした時に、「ユーザーがXXXって言っていたから」というフレーズを多用している自分に気がつきました。 よくよく考えてみると「ユーザーの声を聞いている」という言い訳を手に入れたことによって、自分で意思決定せず、意思を持った決断から逃げていました。プロダクトオーナとしては、考え抜いて意思決定を全うするべきです。 それからは、“ ヒアリ ングの結果”という第三の何かに委ねてしまっていないか?という不安を意識して意思決定するようになりました。 迷ったら議論できる仲間・戻ってくれる軸があるといい プロダクト開発は、正解が見えない不確実性が高い中で判断を迫られることが多く、いろいろな不安と闘いながら意思決定をしました。判断に迷ったときや自信を持てない時、立ち戻ることができる基準や製品コンセプトを拠り所としました。 それでも自分の意思決定がブレてしまっていると感じた時は、一緒に開発していた社内のデザインチーム・社外の開発パートナーさんと相談し、自分の意思決定を整えてもらいました。 DigSportsのリニューアルは製品コンセプトや仲間のおかげで、悩みながらも良い意思決定・開発ができたと思っています。 機会があれば、リニューアルしたDigSportsをぜひご体験ください! www.isid.co.jp 最後に、私たちは一緒に働いてくれる仲間を募集しています! デジタル技術を社会課題解決につなげるようなプロジェクトを推進していきたいプロジェクトマネージャーやエンジニアを募集しています。 ぜひご応募ください! スマートシティ導入コンサルタント/スマートシティ戦略コンサルタント 執筆: @iida.michitaka 、レビュー: @takeda.hideyuki ( Shodo で執筆されました )
XI本部 スマート ソサエティ センターの飯田です。 私は「DigSports」というAIによって運動能力を測定し、どのスポーツに向いているかを提案する製品のプロダクトオーナーを担当しています。 DigSportsは、運動が苦手な子どもたちや運動から遠ざかってしまった働く世代の方々に対して、自分に合ったスポーツを見つけられる機会を提供し、運動を始めるきっかけや運動習慣化を後押ししています。 isid-ssc.jp そんなDigSportsなのですが、技術的な アーキテクチャ をガラッと変えた新しいバージョンをリリースしました。 AIで適性のあるスポーツを判定する「DigSports」の新バージョンを提供開始 本エントリーでは、新バージョン開発をプロダクトオーナーとして経験した悩みや苦悩を書いてみます。 不安との闘い 要約すると、「不安との闘い」が悩みの根源でした。 技術的なこと、BizDev的なこと、営業戦略、様々な部分で決断が求められました。 自分の選択が間違っていないか? 機能開発の優先度を間違えてないか? 「UX的に良いものをつくる」「技術的に良いものをつくる」「つくったものを広める」そのバランスが良いものになっているか? ...等です。 アジャイル で試行錯誤しながらプロダクトを カイゼン していけるとしても、投資対効果を最大にするために妥当な判断ができているか、「これでいい!」「あ、やっぱダメかも」を繰り返しながら、開発を進めていました。 その中で感じた書いてみます。 1. 資産を作っているか?負債を作っていないか? DigSportsは エンタープライズ なプロダクトと毛色が違うため、 エンタープライズ 製品に比べ、ソフトウェアの規模はそこまで大きくありません。そのため、開発にあたっては「修正対応のしやすさのため、シンプルさを維持する」ことを意識しています。 また、技術的な側面だけでなく、ビジネス開発の観点からもシンプルさを意識しています。 アジャイル など試行錯誤していく開発アプローチは、開発・修正のサイクルを短く回すことができますが、 一方、資産となる市場販売目的のソフトウェア開発にかかった費用は、 3年間の減価償却が行われます。 つまり、今回の開発コストが3年間に按分されることになるので、不要なものを開発すると“ゴミ”・“負債”を作っていることになるので、“資産”として意味のあるものを残していかないといけません。 「今、行おうとしている開発は、改善なのか?改悪なのか?」 もちろん不確実性の中での判断なので、正解はありませんが、3年後を意識して決断していくのは悩みがつきませんでした。 2. 同じ結論になったとしても、1周回ってくると景色やブレなさが違う そのように「資産を作れているか?」と悩みながら進めていく中で、ユーザー・顧客の反応や開発メンバーとの議論は、悩みながら決断をしていく際の大きな拠り所でした。 ユーザー・顧客の反応を見る前後でアイディアが変わらなかったとしても、 ヒアリ ングや議論を経てみると、一周回って自分自身への納得感(ひいては、他人に説得する際の自信)がついている感覚がありました。 3. 「ユーザーの声を聞いている」ということに逃げていないか プロダクト開発における教科書的な模範解答としては「ユーザーの声を聞いて、 アジャイル 的に PDCA を回して改善する」だと思います。実際、DigSportsのリニューアルにおいてもそこは意識しながら進めました。 しかし、ふとした時に、「ユーザーがXXXって言っていたから」というフレーズを多用している自分に気がつきました。 よくよく考えてみると「ユーザーの声を聞いている」という言い訳を手に入れたことによって、自分で意思決定せず、意思を持った決断から逃げていました。プロダクトオーナとしては、考え抜いて意思決定を全うするべきです。 それからは、“ ヒアリ ングの結果”という第三の何かに委ねてしまっていないか?という不安を意識して意思決定するようになりました。 迷ったら議論できる仲間・戻ってくれる軸があるといい プロダクト開発は、正解が見えない不確実性が高い中で判断を迫られることが多く、いろいろな不安と闘いながら意思決定をしました。判断に迷ったときや自信を持てない時、立ち戻ることができる基準や製品コンセプトを拠り所としました。 それでも自分の意思決定がブレてしまっていると感じた時は、一緒に開発していた社内のデザインチーム・社外の開発パートナーさんと相談し、自分の意思決定を整えてもらいました。 DigSportsのリニューアルは製品コンセプトや仲間のおかげで、悩みながらも良い意思決定・開発ができたと思っています。 機会があれば、リニューアルしたDigSportsをぜひご体験ください! www.isid.co.jp 最後に、私たちは一緒に働いてくれる仲間を募集しています! デジタル技術を社会課題解決につなげるようなプロジェクトを推進していきたいプロジェクトマネージャーやエンジニアを募集しています。 ぜひご応募ください! スマートシティ導入コンサルタント/スマートシティ戦略コンサルタント 執筆: @iida.michitaka 、レビュー: @takeda.hideyuki ( Shodo で執筆されました )
こんにちは、X(クロス) イノベーション 本部 クラウド イノベーション センターの田村です。 Microsoft Fabric では Git 統合が提供されており、 ワークスペース 内のアイテムに対して Git によるソース管理が可能です。 本記事では、実際に Git 統合を利用したソース管理の手順や制約事項についてご紹介します。 「 Microsoft Fabric とは?」という方は下記の紹介記事や Microsoft Fabric のドキュメントをご参照ください。 Microsoft Fabric を利用して自分の勤怠データを分析する Microsoft Fabric とは なお、本記事に記載されている情報は 2023 年 8 月時点のものとなります。 Microsoft Fabric のアップデートや一般公開(GA)で情報が更新される場合もありますのでご注意ください。 Microsoft Fabric の Git 統合について 概要 ソース管理対象 制約事項 Git 統合の実践 Azure Repos の準備 Microsoft Fabric ワークスペースと Git リポジトリの接続 アイテムに対する変更適用 ソース管理時の注意点 まとめ Microsoft Fabric の Git 統合について 概要 Git 統合は Microsoft Fabric の ワークスペース と Git リポジトリ を接続することで実現します。 両者を接続することで ワークスペース 内のアイテムが Git リポジトリ と同期され、アイテムの変更差分が検出されます。 また、Git におけるコミットやコミット前の変更取り消しは Microsoft Fabric の UI 上から実施できるように設計されています。 ソース管理対象 Git 統合はプレビュー中のため、ソース管理がサポートされる Microsoft Fabric のアイテムは 2 つのみとなっています。 レポート:デー タセット をもとに Power BI により可視化されたグラフや表、マップなどのビジュアル群 デー タセット :Power BI での検出・可視化が可能となったデータソース 現時点ではデータ分析における可視化領域のみで Git 統合を利用できますが、 Microsoft のドキュメント には次の記載があるため、今後サポート対象となるアイテムの追加に期待したいと思います。 現在、プレビュー段階ではいくつかのアイテムのみがサポートされていますが、一覧のサポートされているアイテムは増えています。 制約事項 ワークスペース と接続できる Git リポジトリ は Azure Repos のみがサポートされており、 GitHub 上の リポジトリ は現在サポート外となっています。 Microsoft Fabric のコミュニティサイト では、 GitHub を含めたサポートの拡大要望が寄せられていますが、 Microsoft 側の対応可否や時期は不明です。 Support GitHub in the git integration for workspaces Support for git repositories other than Azure DevOps Git 統合の実践 Azure Repos の準備 前述したとおり Git 統合にてサポートされるのは Azure Repos のみであるため、そちらで Git リポジトリ を準備します。 Azure Repos とは、 Microsoft が提供する DevOps 支援サービスである Azure DevOps に含まれる Git の ホスティング サービスです。 Azure DevOps Services Azure Repos – Git リポジトリ 今回は検証のために Azure DevOps プロジェクトを作成し、Azure Repos 上で Git 統合用の リポジトリ を構成しました。 プロジェクト名と Git リポジトリ 名は msfabric-test としています。 Azure Repos の初期設定については下記ドキュメントをご参照ください。 Azure Repos の使用を開始する なお、以降 Azure Repos に対する操作はすべてプロジェクト管理者権限で実施しています。 Microsoft Fabric ワークスペース と Git リポジトリ の接続 こちら のドキュメントに従って接続設定を実施します。 以降の Microsoft Fabric における操作は、作業者アカウントに ワークスペース の管理者権限があることを前提としています。 Microsoft Fabric の[ ワークスペース の設定] > [Git 統合]より、接続先の Azure Repos の情報を入力し[接続と同期]を実行します。 同期が完了すると、 ワークスペース 内のレポートとデー タセット が Azure Repos の Git リポジトリ へ反映されます。 このとき、末尾が .Dataset の ディレクト リと .Report の ディレクト リにデー タセット とレポートの構成ファイルがそれぞれ格納されています。 アイテムに対する変更適用 レポートとデー タセット に対してそれぞれ変更を加え、ソース管理の流れを確認します。 検証用のデー タセット は Microsoft Fabric のチュートリアル で提供されている 卸売業の売上データ を利用しました。 売上データが格納された 1 つのファクトテーブルと、商品や顧客、従業員といった情報が格納された 5 つのディメンションテーブルで構成されています。 検証用のレポートは前述のデー タセット から自動生成しており、複数のグラフとカードおよびテキストビジュアルから構成されています。 まずはデー タセット に対して変更を加えてみます。 今回は、 Microsoft Fabric の GUI からデー タセット に構成されているリレーションシップを 1 件削除し、構成ファイルにどのような変更があるか確認しました。 デー タセット に変更を加えると、 ワークスペース のアイテム一覧画面で検出されます。 同画面でトピックブランチへチェックアウトして変更をコミットすると、同期先の Azure Repos にて変更された構成ファイルや箇所を確認できます。 検証用のデー タセット は 4 つのファイルで構成されており、リレーションシップの設定については model.bim というファイルのみに変更が適用されていました。 各ファイルの説明を下表に記載します。 ファイル名  必須  説明 model.bim 〇 データベースオブジェクトの定義が Tabular Model Scripting Language(TMSL)で記述されている definition.pbidataset 〇 デー タセット に関連付けられた Q&A や スキーマ 共有に関する設定が記述されている item.config.json ファイルのバージョン情報と同期先の ワークスペース ID が記述されている item.metadata.json Git 統合されているアイテムの種別(ここではデー タセット )とアイテム名、説明が記述されている 上記をふまえると、デー タセット に対して想定される下記のような操作については、その変更が model.bim の記述に反映されると推測できます。 テーブルの追加・削除 データの挿入・更新や削除 データモデルでのメジャー作成 リレーションシップの変更 次にレポートに対しても変更を加えます。 Microsoft Fabric の Power BI にて、前述したレポートに下図のカードとグラフビジュアルを追加しました。 デー タセット に対する変更と同様、上記の変更は ワークスペース のアイテム一覧画面で検出され、コミット後に Azure Repos 側で詳細を確認できます。 検証用のレポートは 5 つのファイルで構成されており、今回の変更については report.json というファイルのみに変更が適用されていました。 各ファイルの説明を下表に記載します。 ファイル名  必須  説明 CY23SU08.json レポートのカスタムテーマ、イメージ、カスタムビジュアルなどの固有情報が記述されている definition.pbir 〇 参照するデー タセット に関する情報が記述されている report.json 〇 レポートに含まれるページ、ビジュアルの情報が記述されている item.config.json ファイルのバージョン情報と同期先の ワークスペース ID が記述されている item.metadata.json Git 統合されているアイテムの種別(ここではレポート)とアイテム名、説明が記述されている 今回はレポートのビジュアルに関する変更のみを実施したため、 report.json の記述のみが影響を受けたと考えられます。 また、デー タセット およびレポートの設定によっては上記に加えて追加の構成ファイルが必要になる場合もあります。 詳細は下記ドキュメントをご参照ください。 Git 統合ソース コード フォーマット Power BI Desktop プロジェクト データセット フォルダー Power BI Desktop プロジェクト レポート フォルダー 変更内容をコミットした後は、Pull Request による一般的なレビュー手順に沿って main ブランチへ変更を適用できました。 トピックブランチとマージ先のブランチ( main )およびレビュアーを指定し、Pull Request を作成 レビュアーは Pull Request の内容を確認し、指摘やコメントをレビュイーに伝える レビューに問題がなければレビュアーの承認をもって Pull Request を完了し、変更を main ブランチへマージする また、今回は実施しませんでしたが Microsoft Fabric では Azure Pipelines を利用したテスト・デプロイの自動化についてもサポートしています。 Azure Pipelines は Azure Repos と同様 Azure DevOps に含まれるサービスのため、Git 統合とうまく組み合わせて CI/CD 環境を構築することで開発やテストの効率化が期待できます。 Azure Pipelines デプロイ パイプラインの概要 ソース管理時の注意点 Microsoft Fabric 上ではレポートやデー タセット といったアイテムの構成ファイルを管理していないため、Git クライアントアプリケーションのように具体的な変更箇所がわかりません。 そのため、作業者自身が変更内容を正しく把握し、適切なコミットメッセージの記述等で円滑なコミュニケーションを図るといった意識が重要になります。 また、 Microsoft Fabric の UI で可能な Git 操作は現状 git commit と git checkout のみです。 コミットの取り消しやリベースなどの操作が必要になった際は、Git コマンドライン ツールや Git クライアントアプリケーションを Azure Repos と併用する必要があります。 まとめ 本記事では、 Microsoft Fabric の Git 統合を利用したソース管理について、実際に利用した際の手順や注意事項、所感についてご紹介しました。 これまで Power BI のデー タセット やレポートといったアイテムはテキストベースでのソース管理ができませんでしたが、Git 統合により GUI 上で可能になった点は魅力的だと感じています。 一方で、一般的なソース管理ではコミットの取り消しやその他 Git コマンドを利用した作業も多いため、Git クライアントや コマンドライン ツールと併用した管理方法も選択肢になり得ます。 現時点では可視化領域のアイテムのみが Git 統合に対応していますが、将来的にはデータ加工や変換、分析といった領域で必要となる ETL パイプラインやデータフロー、各種クエリについてもソース管理の対象となることを期待します。 私たちは一緒に働いてくれる仲間を募集しています! クラウドアーキテクト 執筆: @tamura.kohei 、レビュー: @kano.nanami ( Shodo で執筆されました )
こんにちは、X(クロス) イノベーション 本部 クラウド イノベーション センターの田村です。 Microsoft Fabric では Git 統合が提供されており、 ワークスペース 内のアイテムに対して Git によるソース管理が可能です。 本記事では、実際に Git 統合を利用したソース管理の手順や制約事項についてご紹介します。 「 Microsoft Fabric とは?」という方は下記の紹介記事や Microsoft Fabric のドキュメントをご参照ください。 Microsoft Fabric を利用して自分の勤怠データを分析する Microsoft Fabric とは なお、本記事に記載されている情報は 2023 年 8 月時点のものとなります。 Microsoft Fabric のアップデートや一般公開(GA)で情報が更新される場合もありますのでご注意ください。 Microsoft Fabric の Git 統合について 概要 ソース管理対象 制約事項 Git 統合の実践 Azure Repos の準備 Microsoft Fabric ワークスペースと Git リポジトリの接続 アイテムに対する変更適用 ソース管理時の注意点 まとめ Microsoft Fabric の Git 統合について 概要 Git 統合は Microsoft Fabric の ワークスペース と Git リポジトリ を接続することで実現します。 両者を接続することで ワークスペース 内のアイテムが Git リポジトリ と同期され、アイテムの変更差分が検出されます。 また、Git におけるコミットやコミット前の変更取り消しは Microsoft Fabric の UI 上から実施できるように設計されています。 ソース管理対象 Git 統合はプレビュー中のため、ソース管理がサポートされる Microsoft Fabric のアイテムは 2 つのみとなっています。 レポート:デー タセット をもとに Power BI により可視化されたグラフや表、マップなどのビジュアル群 デー タセット :Power BI での検出・可視化が可能となったデータソース 現時点ではデータ分析における可視化領域のみで Git 統合を利用できますが、 Microsoft のドキュメント には次の記載があるため、今後サポート対象となるアイテムの追加に期待したいと思います。 現在、プレビュー段階ではいくつかのアイテムのみがサポートされていますが、一覧のサポートされているアイテムは増えています。 制約事項 ワークスペース と接続できる Git リポジトリ は Azure Repos のみがサポートされており、 GitHub 上の リポジトリ は現在サポート外となっています。 Microsoft Fabric のコミュニティサイト では、 GitHub を含めたサポートの拡大要望が寄せられていますが、 Microsoft 側の対応可否や時期は不明です。 Support GitHub in the git integration for workspaces Support for git repositories other than Azure DevOps Git 統合の実践 Azure Repos の準備 前述したとおり Git 統合にてサポートされるのは Azure Repos のみであるため、そちらで Git リポジトリ を準備します。 Azure Repos とは、 Microsoft が提供する DevOps 支援サービスである Azure DevOps に含まれる Git の ホスティング サービスです。 Azure DevOps Services Azure Repos – Git リポジトリ 今回は検証のために Azure DevOps プロジェクトを作成し、Azure Repos 上で Git 統合用の リポジトリ を構成しました。 プロジェクト名と Git リポジトリ 名は msfabric-test としています。 Azure Repos の初期設定については下記ドキュメントをご参照ください。 Azure Repos の使用を開始する なお、以降 Azure Repos に対する操作はすべてプロジェクト管理者権限で実施しています。 Microsoft Fabric ワークスペース と Git リポジトリ の接続 こちら のドキュメントに従って接続設定を実施します。 以降の Microsoft Fabric における操作は、作業者アカウントに ワークスペース の管理者権限があることを前提としています。 Microsoft Fabric の[ ワークスペース の設定] > [Git 統合]より、接続先の Azure Repos の情報を入力し[接続と同期]を実行します。 同期が完了すると、 ワークスペース 内のレポートとデー タセット が Azure Repos の Git リポジトリ へ反映されます。 このとき、末尾が .Dataset の ディレクト リと .Report の ディレクト リにデー タセット とレポートの構成ファイルがそれぞれ格納されています。 アイテムに対する変更適用 レポートとデー タセット に対してそれぞれ変更を加え、ソース管理の流れを確認します。 検証用のデー タセット は Microsoft Fabric のチュートリアル で提供されている 卸売業の売上データ を利用しました。 売上データが格納された 1 つのファクトテーブルと、商品や顧客、従業員といった情報が格納された 5 つのディメンションテーブルで構成されています。 検証用のレポートは前述のデー タセット から自動生成しており、複数のグラフとカードおよびテキストビジュアルから構成されています。 まずはデー タセット に対して変更を加えてみます。 今回は、 Microsoft Fabric の GUI からデー タセット に構成されているリレーションシップを 1 件削除し、構成ファイルにどのような変更があるか確認しました。 デー タセット に変更を加えると、 ワークスペース のアイテム一覧画面で検出されます。 同画面でトピックブランチへチェックアウトして変更をコミットすると、同期先の Azure Repos にて変更された構成ファイルや箇所を確認できます。 検証用のデー タセット は 4 つのファイルで構成されており、リレーションシップの設定については model.bim というファイルのみに変更が適用されていました。 各ファイルの説明を下表に記載します。 ファイル名  必須  説明 model.bim 〇 データベースオブジェクトの定義が Tabular Model Scripting Language(TMSL)で記述されている definition.pbidataset 〇 デー タセット に関連付けられた Q&A や スキーマ 共有に関する設定が記述されている item.config.json ファイルのバージョン情報と同期先の ワークスペース ID が記述されている item.metadata.json Git 統合されているアイテムの種別(ここではデー タセット )とアイテム名、説明が記述されている 上記をふまえると、デー タセット に対して想定される下記のような操作については、その変更が model.bim の記述に反映されると推測できます。 テーブルの追加・削除 データの挿入・更新や削除 データモデルでのメジャー作成 リレーションシップの変更 次にレポートに対しても変更を加えます。 Microsoft Fabric の Power BI にて、前述したレポートに下図のカードとグラフビジュアルを追加しました。 デー タセット に対する変更と同様、上記の変更は ワークスペース のアイテム一覧画面で検出され、コミット後に Azure Repos 側で詳細を確認できます。 検証用のレポートは 5 つのファイルで構成されており、今回の変更については report.json というファイルのみに変更が適用されていました。 各ファイルの説明を下表に記載します。 ファイル名  必須  説明 CY23SU08.json レポートのカスタムテーマ、イメージ、カスタムビジュアルなどの固有情報が記述されている definition.pbir 〇 参照するデー タセット に関する情報が記述されている report.json 〇 レポートに含まれるページ、ビジュアルの情報が記述されている item.config.json ファイルのバージョン情報と同期先の ワークスペース ID が記述されている item.metadata.json Git 統合されているアイテムの種別(ここではレポート)とアイテム名、説明が記述されている 今回はレポートのビジュアルに関する変更のみを実施したため、 report.json の記述のみが影響を受けたと考えられます。 また、デー タセット およびレポートの設定によっては上記に加えて追加の構成ファイルが必要になる場合もあります。 詳細は下記ドキュメントをご参照ください。 Git 統合ソース コード フォーマット Power BI Desktop プロジェクト データセット フォルダー Power BI Desktop プロジェクト レポート フォルダー 変更内容をコミットした後は、Pull Request による一般的なレビュー手順に沿って main ブランチへ変更を適用できました。 トピックブランチとマージ先のブランチ( main )およびレビュアーを指定し、Pull Request を作成 レビュアーは Pull Request の内容を確認し、指摘やコメントをレビュイーに伝える レビューに問題がなければレビュアーの承認をもって Pull Request を完了し、変更を main ブランチへマージする また、今回は実施しませんでしたが Microsoft Fabric では Azure Pipelines を利用したテスト・デプロイの自動化についてもサポートしています。 Azure Pipelines は Azure Repos と同様 Azure DevOps に含まれるサービスのため、Git 統合とうまく組み合わせて CI/CD 環境を構築することで開発やテストの効率化が期待できます。 Azure Pipelines デプロイ パイプラインの概要 ソース管理時の注意点 Microsoft Fabric 上ではレポートやデー タセット といったアイテムの構成ファイルを管理していないため、Git クライアントアプリケーションのように具体的な変更箇所がわかりません。 そのため、作業者自身が変更内容を正しく把握し、適切なコミットメッセージの記述等で円滑なコミュニケーションを図るといった意識が重要になります。 また、 Microsoft Fabric の UI で可能な Git 操作は現状 git commit と git checkout のみです。 コミットの取り消しやリベースなどの操作が必要になった際は、Git コマンドライン ツールや Git クライアントアプリケーションを Azure Repos と併用する必要があります。 まとめ 本記事では、 Microsoft Fabric の Git 統合を利用したソース管理について、実際に利用した際の手順や注意事項、所感についてご紹介しました。 これまで Power BI のデー タセット やレポートといったアイテムはテキストベースでのソース管理ができませんでしたが、Git 統合により GUI 上で可能になった点は魅力的だと感じています。 一方で、一般的なソース管理ではコミットの取り消しやその他 Git コマンドを利用した作業も多いため、Git クライアントや コマンドライン ツールと併用した管理方法も選択肢になり得ます。 現時点では可視化領域のアイテムのみが Git 統合に対応していますが、将来的にはデータ加工や変換、分析といった領域で必要となる ETL パイプラインやデータフロー、各種クエリについてもソース管理の対象となることを期待します。 私たちは一緒に働いてくれる仲間を募集しています! クラウドアーキテクト 執筆: @tamura.kohei 、レビュー: @kano.nanami ( Shodo で執筆されました )
はじめに ISID X(クロス) イノベーション 本部 の池田です。 Azure Portal から Azure Load Testing を操作する方法については以下の記事もご覧ください。 tech.isid.co.jp 負荷試験 の計画の中でいろいろな利用パターンでの試験により環境を分析するケースがあります。 しかし、Azure Portal からの操作では JMeter スクリプト のアップロードや参照できるリソース選択など選択項目が多く、複数パターンを何度も手動で作成するのは手間です。 そこで CLI から操作して複数テストを一括登録する方法を試しましたのでご紹介します。 利用ツール 通常のAzureリソースの CLI 操作としては、Azure CLI や Azure Power Shell, ARMテンプレート を利用するのが一般的です。しかし、2023年2月にGAしたばかりの Azure Load Testing においては 2023年5月時点でこれらのツールでAzureリソース作成はできるものの詳細な設定ができませんでした。Azure Load Testing REST API では先行してテスト作成の機能が提供されていたため、こちらを利用して作成します。(現在はAzure CLI によるテスト作成もサポートされるようになっています) 今回の スクリプト 実行環境は以下のとおりです。 スクリプト の記載は環境依存のものが含まれますので、参考にされる際はご自身の環境に合わせて調整してください。 PowerShell 7.3.4 azure-cli 2.49.0 curl 8.0.1 (Windows) libcurl/8.0.1 Schannel WinIDN 試験環境の概要 今回使用するテスト環境の構成は以下のとおりです。App Service 既定のWebサイトをテスト対象としてAzure Load TestingからHTTPリク エス トを連続送信します。その上でAzureリソースの負荷状況を Azure Load Testing の ダッシュ ボードに表示する構成となります。 Azure Load Testingリソースの作成 Azureリソース作成はAzure CLI で機能提供されていましたので、この部分はこちらのコマンドを利用して作成します。 az load create --name ${loadTestName} -- resource-group ${resourceGroupName} --location eastasia データプレーン URI の取得 Azure リソースマネージャーに対する要求では az rest コマンドを利用すると認証ヘッダーを自動付与できて便利です。 az rest で REST API を呼び出して作成されたリソースのデータプレーンエンドポイントを取得します。 $loadTest = ( az rest ` --method get ` --uri https: // management.azure.com / subscriptions / ${subscriptionId} / resourceGroups / ${resourceGroupName} / providers / Microsoft.LoadTestService / loadTests / ${loadTestName} ? api-version = 2022 - 12 - 01 ` | ConvertFrom-Json ) $Endpoint = $loadTest .properties.dataPlaneURI $Endpoint 以下のような出力が得られます。これでデータプレーン操作に必要なエンドポイントが取得できました。 XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.eastasia.cnt-prod.loadtesting.azure.com データプレーン用アクセス トーク ンの取得 ここからは Azureリソースマネージャー へのアクセスではなくデータプレーンエンドポイントへの操作となります。データプレーン用のアクセス トーク ンを取得する必要があります。 必要なアクセス トーク ン Azure Load Testing REST API のリファレンスを参照すると、「 https://cnt-prod.loadtesting.azure.com/.default%E3%80%8D%E3%82%92Scope%E3%81%A8%E3%81%97%E3%81%9F%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%A8%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82 Load Test Administration - Create Or Update Test アクセス トーク ンの取得 az account get- access -token コマンドを利用して、該当スコープに対するアクセス トーク ンを取得します。 ( 参考: az account get-access-token ) $accessToken = (az account get-access-token ` --scope 'https://cnt-prod.loadtesting.azure.com/.default' ` --query 'accessToken' ` --output tsv) テストの構成 データプレーンエンドポイントとアクセス トーク ンがそろいましたので、続いてデータプレーンの操作をします。 データプレーンはTemplateファイルを用意してそれを curl コマンドで投げ込む形で操作します。 ID採番 まずは、テストIDとして使用するGuidを採番します。 $testId = ( New-Guid ) テスト作成 実際にAzure Load Testing REST API を呼び出してテストを作成していきます。 (参考: Load Test Administration - Create Or Update Test ) curl ` --request PATCH ` --header "Authorization: Bearer $accessToken " ` --header "Content-Type: application/merge-patch+json" ` --url "https:// ${Endpoint} /tests/ ${testId} ?api-version=2022-11-01" ` -- upload-file TestTemplate.json 送信ファイルに記述するパラメータが以下となります。Azure Portal 上で作成したテストの内容を API で取得して参考にしつつ作るのがおすすめです。 (参考: Load Test Administration - Get Test ) { "displayName": "displayName_test1", "description": "", "keyvaultReferenceIdentityType": "SystemAssigned", "passFailCriteria": { "passFailMetrics": {} }, "loadTestConfiguration": { "engineInstances": 10, "splitAllCSVs": false, "quickStartTest": false } } アプリ コンポーネント 登録 次にテスト結果画面に表示するアプリ コンポーネント (Azure リソースのコレクション)を登録します。 (参考: Load Test Administration - Create Or Update App Components ) curl ` --request PATCH ` --header "Authorization: Bearer $accessToken " ` --header "Content-Type: application/merge-patch+json" ` --url "https:// ${Endpoint} /tests/ ${testId} /app-components?api-version=2022-11-01" ` -- upload-file AppComponents.json アプリ コンポーネント は以下のように列挙していく形で登録します。参考とする既存設定の取得 API は以下です。 (参考: Load Test Administration - Get App Components ) { "components": { "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda": { "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda", "resourceName": "app-demo-ikeda", "resourceType": "Microsoft.Web/sites", "resourceGroup": "rg-load-test", "subscriptionId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "kind": "app" }, "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo": { "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo", "resourceName": "plan-load-demo", "resourceType": "Microsoft.Web/serverfarms", "resourceGroup": "rg-load-test", "subscriptionId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "kind": "app" } } } メトリック登録 続いて、テスト結果画面に表示するメトリックを登録します。 (参考: Load Test Administration - Create Or Update Server Metrics Config ) curl ` --request PATCH ` --header "Authorization: Bearer $accessToken " ` --header "Content-Type: application/merge-patch+json" ` --url "https:// ${Endpoint} /tests/ ${testId} /server-metrics-config?api-version=2022-11-01" ` -- upload-file ServerMetricsConfig.json 前段で登録したアプリ コンポーネント に対するメトリックを列挙します。参考とする既存設定の取得 API は下記です。 (参考: Load Test Administration - Get Server Metrics Config ) { "metrics": { "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda/providers/microsoft.insights/metricdefinitions/Http5xx": { "id": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda/providers/microsoft.insights/metricdefinitions/Http5xx", "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda", "metricNamespace": "microsoft.web/sites", "name": "Http5xx", "aggregation": "Total", "resourceType": "microsoft.web/sites" }, "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda/providers/microsoft.insights/metricdefinitions/Requests": { "id": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda/providers/microsoft.insights/metricdefinitions/Requests", "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda", "metricNamespace": "microsoft.web/sites", "name": "Requests", "aggregation": "Total", "resourceType": "microsoft.web/sites" }, "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda/providers/microsoft.insights/metricdefinitions/HttpResponseTime": { "id": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda/providers/microsoft.insights/metricdefinitions/HttpResponseTime", "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda", "metricNamespace": "microsoft.web/sites", "name": "HttpResponseTime", "aggregation": "Average", "resourceType": "microsoft.web/sites" }, "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo/providers/microsoft.insights/metricdefinitions/CpuPercentage": { "id": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo/providers/microsoft.insights/metricdefinitions/CpuPercentage", "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo", "metricNamespace": "microsoft.web/serverfarms", "name": "CpuPercentage", "aggregation": "Average", "resourceType": "microsoft.web/serverfarms" }, "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo/providers/microsoft.insights/metricdefinitions/MemoryPercentage": { "id": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo/providers/microsoft.insights/metricdefinitions/MemoryPercentage", "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo", "metricNamespace": "microsoft.web/serverfarms", "name": "MemoryPercentage", "aggregation": "Average", "resourceType": "microsoft.web/serverfarms" } } } JMeter スクリプト 登録 以下の操作でテストで実行する JMeter スクリプト をアップロードします。 (参考: Load Test Administration - Upload Test File ) curl ` --request PUT ` --header "Authorization: Bearer $accessToken " ` --header "Content-Type: application/octet-stream content" ` --url "https:// ${Endpoint} /tests/ ${testId} /files/ ${NAME_JMX} ?api-version=2022-11-01&fileType=JMX_FILE" ` -- upload-file lt-demo .jmx こちらが JMeter で作成した JMX ファイルです。今回はHTTP Getリク エス トをループするシンプルな スクリプト を使用しています。 <?xml version="1.0" encoding="UTF-8"?> <jmeterTestPlan version="1.2" properties="5.0" jmeter="5.5"> <hashTree> <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true"> <stringProp name="TestPlan.comments"></stringProp> <boolProp name="TestPlan.functional_mode">false</boolProp> <boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp> <boolProp name="TestPlan.serialize_threadgroups">false</boolProp> <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> <collectionProp name="Arguments.arguments"/> </elementProp> <stringProp name="TestPlan.user_define_classpath"></stringProp> </TestPlan> <hashTree> <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true"> <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true"> <boolProp name="LoopController.continue_forever">false</boolProp> <intProp name="LoopController.loops">-1</intProp> </elementProp> <stringProp name="ThreadGroup.num_threads">10</stringProp> <stringProp name="ThreadGroup.ramp_time">1</stringProp> <boolProp name="ThreadGroup.scheduler">false</boolProp> <stringProp name="ThreadGroup.duration"></stringProp> <stringProp name="ThreadGroup.delay"></stringProp> <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp> </ThreadGroup> <hashTree> <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true"> <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> <collectionProp name="Arguments.arguments"/> </elementProp> <stringProp name="HTTPSampler.domain">app-demo-ikeda.azurewebsites.net</stringProp> <stringProp name="HTTPSampler.port">443</stringProp> <stringProp name="HTTPSampler.protocol">https</stringProp> <stringProp name="HTTPSampler.contentEncoding"></stringProp> <stringProp name="HTTPSampler.path"></stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> <boolProp name="HTTPSampler.use_keepalive">true</boolProp> <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> <stringProp name="HTTPSampler.embedded_url_re"></stringProp> <stringProp name="HTTPSampler.connect_timeout"></stringProp> <stringProp name="HTTPSampler.response_timeout"></stringProp> </HTTPSamplerProxy> <hashTree/> </hashTree> </hashTree> </hashTree> </jmeterTestPlan> テストの実行 テスト実行 ボディ部にテストIDを指定することで登録したテスト内容を実行できます。テスト表示名は スクリプト 内で日時から作成しています。 (参考: Load Test Run - Create Or Update Test Run ) curl ` --request PATCH ` --header "Authorization: Bearer $accessToken" ` --header "Content-Type: application/merge-patch+json" ` --url "https://${Endpoint}/test-runs/$(New-Guid)?api-version=2022-11-01" ` --data "{'testId': '${testId}','displayName':'TestRun_$((Get-Date).ToString("yyyy/MM/dd_HH:mm:ss"))'}" 実行結果の確認 Azure Portal での確認 Azure Portal 上で作成したテストが実行されていることを確認できます。 アプリ コンポーネント テスト画面の「アプリ コンポーネント 」から REST API で登録したアプリ コンポーネント が確認できます。 メトリックの構成 同じく「メトリックの構成」から表示メトリックも反映されていることが確認できます。 備考 ドキュメントが整備されていない場合、 API の利用方法を調べるのにブラウザのDevToolsでAzure Portal の通信内容を見るのも参考になります。今回もAzure Portal 上での設定項目と API 項目に差異があり、通信内容を見てみると新しい Preview バージョンの API を利用していました。新しいサービスではドキュメント化が遅れているケースもありますので必要に応じて参考にしましょう。 まとめ Azure Load Testing の操作を REST API を利用して スクリプト 化することで、複数パターンの試験登録が簡単にできるようになりました。新しいサービスでは周辺ツールがまだ提供されていないケースもありますが、ベースとなる REST API が先行して提供されていることがあります。 REST API による操作も活用することでAzureでできることが広がります。ご参考になれば幸いです。 私たちは同じチームで働いてくれる仲間を探しています。 クラウド アーキテクトの業務に興味がある方のご応募をお待ちしています。 クラウドアーキテクト 執筆: @ikeda.jun 、レビュー: @mizuno.kazuhiro ( Shodo で執筆されました )
はじめに ISID X(クロス) イノベーション 本部 の池田です。 Azure Portal から Azure Load Testing を操作する方法については以下の記事もご覧ください。 tech.isid.co.jp 負荷試験 の計画の中でいろいろな利用パターンでの試験により環境を分析するケースがあります。 しかし、Azure Portal からの操作では JMeter スクリプト のアップロードや参照できるリソース選択など選択項目が多く、複数パターンを何度も手動で作成するのは手間です。 そこで CLI から操作して複数テストを一括登録する方法を試しましたのでご紹介します。 利用ツール 通常のAzureリソースの CLI 操作としては、Azure CLI や Azure Power Shell, ARMテンプレート を利用するのが一般的です。しかし、2023年2月にGAしたばかりの Azure Load Testing においては 2023年5月時点でこれらのツールでAzureリソース作成はできるものの詳細な設定ができませんでした。Azure Load Testing REST API では先行してテスト作成の機能が提供されていたため、こちらを利用して作成します。(現在はAzure CLI によるテスト作成もサポートされるようになっています) 今回の スクリプト 実行環境は以下のとおりです。 スクリプト の記載は環境依存のものが含まれますので、参考にされる際はご自身の環境に合わせて調整してください。 PowerShell 7.3.4 azure-cli 2.49.0 curl 8.0.1 (Windows) libcurl/8.0.1 Schannel WinIDN 試験環境の概要 今回使用するテスト環境の構成は以下のとおりです。App Service 既定のWebサイトをテスト対象としてAzure Load TestingからHTTPリク エス トを連続送信します。その上でAzureリソースの負荷状況を Azure Load Testing の ダッシュ ボードに表示する構成となります。 Azure Load Testingリソースの作成 Azureリソース作成はAzure CLI で機能提供されていましたので、この部分はこちらのコマンドを利用して作成します。 az load create --name ${loadTestName} -- resource-group ${resourceGroupName} --location eastasia データプレーン URI の取得 Azure リソースマネージャーに対する要求では az rest コマンドを利用すると認証ヘッダーを自動付与できて便利です。 az rest で REST API を呼び出して作成されたリソースのデータプレーンエンドポイントを取得します。 $loadTest = ( az rest ` --method get ` --uri https: // management.azure.com / subscriptions / ${subscriptionId} / resourceGroups / ${resourceGroupName} / providers / Microsoft.LoadTestService / loadTests / ${loadTestName} ? api-version = 2022 - 12 - 01 ` | ConvertFrom-Json ) $Endpoint = $loadTest .properties.dataPlaneURI $Endpoint 以下のような出力が得られます。これでデータプレーン操作に必要なエンドポイントが取得できました。 XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.eastasia.cnt-prod.loadtesting.azure.com データプレーン用アクセス トーク ンの取得 ここからは Azureリソースマネージャー へのアクセスではなくデータプレーンエンドポイントへの操作となります。データプレーン用のアクセス トーク ンを取得する必要があります。 必要なアクセス トーク ン Azure Load Testing REST API のリファレンスを参照すると、「 https://cnt-prod.loadtesting.azure.com/.default%E3%80%8D%E3%82%92Scope%E3%81%A8%E3%81%97%E3%81%9F%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%A8%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82 Load Test Administration - Create Or Update Test アクセス トーク ンの取得 az account get- access -token コマンドを利用して、該当スコープに対するアクセス トーク ンを取得します。 ( 参考: az account get-access-token ) $accessToken = (az account get-access-token ` --scope 'https://cnt-prod.loadtesting.azure.com/.default' ` --query 'accessToken' ` --output tsv) テストの構成 データプレーンエンドポイントとアクセス トーク ンがそろいましたので、続いてデータプレーンの操作をします。 データプレーンはTemplateファイルを用意してそれを curl コマンドで投げ込む形で操作します。 ID採番 まずは、テストIDとして使用するGuidを採番します。 $testId = ( New-Guid ) テスト作成 実際にAzure Load Testing REST API を呼び出してテストを作成していきます。 (参考: Load Test Administration - Create Or Update Test ) curl ` --request PATCH ` --header "Authorization: Bearer $accessToken " ` --header "Content-Type: application/merge-patch+json" ` --url "https:// ${Endpoint} /tests/ ${testId} ?api-version=2022-11-01" ` -- upload-file TestTemplate.json 送信ファイルに記述するパラメータが以下となります。Azure Portal 上で作成したテストの内容を API で取得して参考にしつつ作るのがおすすめです。 (参考: Load Test Administration - Get Test ) { "displayName": "displayName_test1", "description": "", "keyvaultReferenceIdentityType": "SystemAssigned", "passFailCriteria": { "passFailMetrics": {} }, "loadTestConfiguration": { "engineInstances": 10, "splitAllCSVs": false, "quickStartTest": false } } アプリ コンポーネント 登録 次にテスト結果画面に表示するアプリ コンポーネント (Azure リソースのコレクション)を登録します。 (参考: Load Test Administration - Create Or Update App Components ) curl ` --request PATCH ` --header "Authorization: Bearer $accessToken " ` --header "Content-Type: application/merge-patch+json" ` --url "https:// ${Endpoint} /tests/ ${testId} /app-components?api-version=2022-11-01" ` -- upload-file AppComponents.json アプリ コンポーネント は以下のように列挙していく形で登録します。参考とする既存設定の取得 API は以下です。 (参考: Load Test Administration - Get App Components ) { "components": { "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda": { "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda", "resourceName": "app-demo-ikeda", "resourceType": "Microsoft.Web/sites", "resourceGroup": "rg-load-test", "subscriptionId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "kind": "app" }, "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo": { "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo", "resourceName": "plan-load-demo", "resourceType": "Microsoft.Web/serverfarms", "resourceGroup": "rg-load-test", "subscriptionId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "kind": "app" } } } メトリック登録 続いて、テスト結果画面に表示するメトリックを登録します。 (参考: Load Test Administration - Create Or Update Server Metrics Config ) curl ` --request PATCH ` --header "Authorization: Bearer $accessToken " ` --header "Content-Type: application/merge-patch+json" ` --url "https:// ${Endpoint} /tests/ ${testId} /server-metrics-config?api-version=2022-11-01" ` -- upload-file ServerMetricsConfig.json 前段で登録したアプリ コンポーネント に対するメトリックを列挙します。参考とする既存設定の取得 API は下記です。 (参考: Load Test Administration - Get Server Metrics Config ) { "metrics": { "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda/providers/microsoft.insights/metricdefinitions/Http5xx": { "id": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda/providers/microsoft.insights/metricdefinitions/Http5xx", "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda", "metricNamespace": "microsoft.web/sites", "name": "Http5xx", "aggregation": "Total", "resourceType": "microsoft.web/sites" }, "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda/providers/microsoft.insights/metricdefinitions/Requests": { "id": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda/providers/microsoft.insights/metricdefinitions/Requests", "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda", "metricNamespace": "microsoft.web/sites", "name": "Requests", "aggregation": "Total", "resourceType": "microsoft.web/sites" }, "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda/providers/microsoft.insights/metricdefinitions/HttpResponseTime": { "id": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda/providers/microsoft.insights/metricdefinitions/HttpResponseTime", "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/sites/app-demo-ikeda", "metricNamespace": "microsoft.web/sites", "name": "HttpResponseTime", "aggregation": "Average", "resourceType": "microsoft.web/sites" }, "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo/providers/microsoft.insights/metricdefinitions/CpuPercentage": { "id": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo/providers/microsoft.insights/metricdefinitions/CpuPercentage", "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo", "metricNamespace": "microsoft.web/serverfarms", "name": "CpuPercentage", "aggregation": "Average", "resourceType": "microsoft.web/serverfarms" }, "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo/providers/microsoft.insights/metricdefinitions/MemoryPercentage": { "id": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo/providers/microsoft.insights/metricdefinitions/MemoryPercentage", "resourceId": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/rg-load-test/providers/Microsoft.Web/serverfarms/plan-load-demo", "metricNamespace": "microsoft.web/serverfarms", "name": "MemoryPercentage", "aggregation": "Average", "resourceType": "microsoft.web/serverfarms" } } } JMeter スクリプト 登録 以下の操作でテストで実行する JMeter スクリプト をアップロードします。 (参考: Load Test Administration - Upload Test File ) curl ` --request PUT ` --header "Authorization: Bearer $accessToken " ` --header "Content-Type: application/octet-stream content" ` --url "https:// ${Endpoint} /tests/ ${testId} /files/ ${NAME_JMX} ?api-version=2022-11-01&fileType=JMX_FILE" ` -- upload-file lt-demo .jmx こちらが JMeter で作成した JMX ファイルです。今回はHTTP Getリク エス トをループするシンプルな スクリプト を使用しています。 <?xml version="1.0" encoding="UTF-8"?> <jmeterTestPlan version="1.2" properties="5.0" jmeter="5.5"> <hashTree> <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true"> <stringProp name="TestPlan.comments"></stringProp> <boolProp name="TestPlan.functional_mode">false</boolProp> <boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp> <boolProp name="TestPlan.serialize_threadgroups">false</boolProp> <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> <collectionProp name="Arguments.arguments"/> </elementProp> <stringProp name="TestPlan.user_define_classpath"></stringProp> </TestPlan> <hashTree> <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true"> <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true"> <boolProp name="LoopController.continue_forever">false</boolProp> <intProp name="LoopController.loops">-1</intProp> </elementProp> <stringProp name="ThreadGroup.num_threads">10</stringProp> <stringProp name="ThreadGroup.ramp_time">1</stringProp> <boolProp name="ThreadGroup.scheduler">false</boolProp> <stringProp name="ThreadGroup.duration"></stringProp> <stringProp name="ThreadGroup.delay"></stringProp> <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp> </ThreadGroup> <hashTree> <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true"> <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> <collectionProp name="Arguments.arguments"/> </elementProp> <stringProp name="HTTPSampler.domain">app-demo-ikeda.azurewebsites.net</stringProp> <stringProp name="HTTPSampler.port">443</stringProp> <stringProp name="HTTPSampler.protocol">https</stringProp> <stringProp name="HTTPSampler.contentEncoding"></stringProp> <stringProp name="HTTPSampler.path"></stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> <boolProp name="HTTPSampler.use_keepalive">true</boolProp> <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> <stringProp name="HTTPSampler.embedded_url_re"></stringProp> <stringProp name="HTTPSampler.connect_timeout"></stringProp> <stringProp name="HTTPSampler.response_timeout"></stringProp> </HTTPSamplerProxy> <hashTree/> </hashTree> </hashTree> </hashTree> </jmeterTestPlan> テストの実行 テスト実行 ボディ部にテストIDを指定することで登録したテスト内容を実行できます。テスト表示名は スクリプト 内で日時から作成しています。 (参考: Load Test Run - Create Or Update Test Run ) curl ` --request PATCH ` --header "Authorization: Bearer $accessToken" ` --header "Content-Type: application/merge-patch+json" ` --url "https://${Endpoint}/test-runs/$(New-Guid)?api-version=2022-11-01" ` --data "{'testId': '${testId}','displayName':'TestRun_$((Get-Date).ToString("yyyy/MM/dd_HH:mm:ss"))'}" 実行結果の確認 Azure Portal での確認 Azure Portal 上で作成したテストが実行されていることを確認できます。 アプリ コンポーネント テスト画面の「アプリ コンポーネント 」から REST API で登録したアプリ コンポーネント が確認できます。 メトリックの構成 同じく「メトリックの構成」から表示メトリックも反映されていることが確認できます。 備考 ドキュメントが整備されていない場合、 API の利用方法を調べるのにブラウザのDevToolsでAzure Portal の通信内容を見るのも参考になります。今回もAzure Portal 上での設定項目と API 項目に差異があり、通信内容を見てみると新しい Preview バージョンの API を利用していました。新しいサービスではドキュメント化が遅れているケースもありますので必要に応じて参考にしましょう。 まとめ Azure Load Testing の操作を REST API を利用して スクリプト 化することで、複数パターンの試験登録が簡単にできるようになりました。新しいサービスでは周辺ツールがまだ提供されていないケースもありますが、ベースとなる REST API が先行して提供されていることがあります。 REST API による操作も活用することでAzureでできることが広がります。ご参考になれば幸いです。 私たちは同じチームで働いてくれる仲間を探しています。 クラウド アーキテクトの業務に興味がある方のご応募をお待ちしています。 クラウドアーキテクト 執筆: @ikeda.jun 、レビュー: @mizuno.kazuhiro ( Shodo で執筆されました )
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 Terraform で Amazon RDS インスタンス / クラスタ ーを作る時に、 password または master_password 属性に指定したマスターユーザーのパスワードが tfstate ファイルに平文で残ってしまう問題がありました。 (参考) https://speakerdeck.com/harukasakihara/sekiyuanaterraformfalseshi-ifang-ji-mi-qing-bao-wokodonihan-mezuhuan-jing-gou-zhu-surunihadousitaraiifalse しかしこれも過去の話。Terraform AWS Provider v4.61.0 からこの問題を解消する方法が提供されているので、それについてご紹介します。 RDS と Secrets Manager の統合 manage_master_user_password を使わない場合 manage_master_user_password を使った場合 aws_rds_cluster にも利用できる KMSキーを指定する 既存の Secrets Manager シークレットを利用する場合 既存のDBに使ったらどうなるか スナップショットからリストアする時の挙動 さいごに RDS と Secrets Manager の統合 事の始まりは 2022年12月に発表された Amazon RDS と AWS Secrets Manager の統合 というアップデートでした。DB 作成時に、RDS への API コールにてマスターユーザーのパスワードを Secrets Manager で作成・保存してくれるようになりました。 Terraform AWS Provider でもこれへの 対応 として、2023年3月にリリースされた v4.61.0 で manage_master_user_password 属性が aws_rds_cluster と aws_db_instance で利用できるようになりました! 実際にリソースを作成して試してみます。 manage_master_user_password を使わない場合 以下のように平文で password を指定し、 aws_db_instance リソースを作成してみます。(BADプ ラク ティスです) resource "aws_db_instance" "my-db-1" { allocated_storage = 10 engine = "mysql" engine_version = "5.7" instance_class = "db.t3.micro" username = "master" # password に平文でパスワードを記述する悪い例 password = "MyPassword123" vpc_security_group_ids = [ aws_security_group.db.id ] skip_final_snapshot = true db_subnet_group_name = "db-subnet-group" } デプロイ後の terraform.tfstate ファイルには、次のとおり平文でパスワード文字列が記録されてしまっています。 例のように .tf ファイルに直接 password を書くのは最悪ですが、他の回避策(Variablesを利用して apply 時にターミナルで指定する、Secrets Manager シークレットに先に登録してから Datasource で取得する、etc)をとったところでどうしても tfstate ファイルに記録されてしまうのは問題でした。 manage_master_user_password を使った場合 password 属性を削除し、 manage_master_user_password 属性に true を設定します。 resource "aws_db_instance" "my-db-2" { allocated_storage = 10 engine = "mysql" engine_version = "5.7" instance_class = "db.t3.micro" username = "master" # manage_master_user_password を利用する manage_master_user_password = true vpc_security_group_ids = [ aws_security_group.db.id ] skip_final_snapshot = true db_subnet_group_name = "db-subnet-group" } デプロイすると、Secrets Manager にシークレットが作成されていることがわかります。 シークレットのキーには username と password が登録されており、 password は RDS が生成したランダムな値になっていました。また 7日間の間隔でシークレットローテーションも設定されていました。 デプロイ後の terraform.tfstate ファイルでは、 password は null となっており、実際のパスワード文字列はどこにも記録されていません。 aws _rds_cluster にも利用できる aws_rds_cluster リソースに対しても、従来の master_password の代わりに manage_master_user_password を利用できるようになっています。 (参考) https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster#rdsaurora-managed-master-passwords-via-secrets-manager-default-kms-key KMSキーを指定する master_user_secret_kms_key_id 属性を利用することで、デフォルトキーではなく指定した KMS キーでシークレットの暗号化をすることもできます。 (参考) https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#managed-master-passwords-via-secrets-manager-specific-kms-key https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster#rdsaurora-managed-master-passwords-via-secrets-manager-specific-kms-key 既存の Secrets Manager シークレットを利用する場合 今回は試していませんが、 master_user_secret の設定ブロックを使うと、既存の Secrets Manager シークレットをマスターユーザーのパスワードにできるようです。 (参考) https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#master_user_secret 既存のDBに使ったらどうなるか 既に password や master_password を使って作成した DB に対して、 manage_master_user_password を利用する方法に切り替えることは可能です。DB は再作成されませんが、マスターユーザーのパスワードは新規に作成され(現在のパスワードは引き継がれずに) Secrets Manager に新しいシークレットとして保存されるようです。既存の DB で manage_master_user_password を使う方法に切り替える場合は、アプリケーションの動作や運用に影響がないか慎重に確認しましょう。 スナップショットからリストアする時の挙動 manage_master_user_password を利用して作成した DB のスナップショットからリストアする場合、マスターユーザーのパスワードは少し特殊な状態になるようです。次のように manage_master_user_password = true を使ってスナップショットから DB インスタンス を作成しても、Secrets Manager にパスワードは統合されず、RDS がパスワードを管理している状態で DB が復元しました。パスワードはスナップショット取得時点のものでした。 data "aws_db_snapshot" "my-snapshot" { db_snapshot_identifier = "snapshot1" } resource "aws_db_instance" "my-db-from-snapshot" { allocated_storage = 10 engine = "mysql" engine_version = "5.7" instance_class = "db.t3.micro" username = "master" # snapshot_identifier を利用してリストアする時、 # manage_master_user_password 属性を使っても # パスワードが Secrets Manager で管理されない。 manage_master_user_password = true skip_final_snapshot = true db_subnet_group_name = "db-subnet-group" # スナップショットから復元する snapshot_identifier = "$ { data.aws_db_snapshot.my-snapshot.id } " } この現象は GitHub の Issue にも報告されており、記事執筆時点では未解決です。ただし tfstate ファイルに記録された password は null だったため、Terraform AWS Provider のバグというよりは RDS の制約なのかもしれません。 リストアした DB のパスワードも Secrets Manager で管理するためには、今のところの次の ワークアラウンド が良さそうです。 manage_master_user_password を付けずに Terraform でスナップショットをリストア( apply ) スナップショット取得時点のパスワードを、RDS が管理している状態でリストアされる manage_master_user_password = true を付けてもう一度 apply する マスターユーザーのパスワードは新規に作成されて Secrets Manager に保存される data "aws_db_snapshot" "my-snapshot" { db_snapshot_identifier = "snapshot1" } resource "aws_db_instance" "my-db-from-snapshot" { allocated_storage = 10 engine = "mysql" engine_version = "5.7" instance_class = "db.t3.micro" username = "master" # リストア時は manage_master_user_password を付けずに apply # リストア後 manage_master_user_password を付けて再度 apply # manage_master_user_password = true skip_final_snapshot = true db_subnet_group_name = "db-subnet-group" snapshot_identifier = "$ { data.aws_db_snapshot.my-snapshot.id } " } さいごに Terraform において tfstate ファイルに平文の DB パスワードが残ってしまう問題を簡単に解消する manage_master_user_password 属性についてご紹介しました。今後新たに Terraform で RDS インスタンス / クラスタ ーを作る時には、ぜひ利用を検討しましょう。ただしスナップショットからリストアする時の動きは(記事執筆時点で)少し特殊なので要注意です。 お読みいただいてありがとうございました。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア 執筆: @kou.kinyo 、レビュー: 寺山 輝 (@terayama.akira) ( Shodo で執筆されました )
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 Terraform で Amazon RDS インスタンス / クラスタ ーを作る時に、 password または master_password 属性に指定したマスターユーザーのパスワードが tfstate ファイルに平文で残ってしまう問題がありました。 (参考) https://speakerdeck.com/harukasakihara/sekiyuanaterraformfalseshi-ifang-ji-mi-qing-bao-wokodonihan-mezuhuan-jing-gou-zhu-surunihadousitaraiifalse しかしこれも過去の話。Terraform AWS Provider v4.61.0 からこの問題を解消する方法が提供されているので、それについてご紹介します。 RDS と Secrets Manager の統合 manage_master_user_password を使わない場合 manage_master_user_password を使った場合 aws_rds_cluster にも利用できる KMSキーを指定する 既存の Secrets Manager シークレットを利用する場合 既存のDBに使ったらどうなるか スナップショットからリストアする時の挙動 さいごに RDS と Secrets Manager の統合 事の始まりは 2022年12月に発表された Amazon RDS と AWS Secrets Manager の統合 というアップデートでした。DB 作成時に、RDS への API コールにてマスターユーザーのパスワードを Secrets Manager で作成・保存してくれるようになりました。 Terraform AWS Provider でもこれへの 対応 として、2023年3月にリリースされた v4.61.0 で manage_master_user_password 属性が aws_rds_cluster と aws_db_instance で利用できるようになりました! 実際にリソースを作成して試してみます。 manage_master_user_password を使わない場合 以下のように平文で password を指定し、 aws_db_instance リソースを作成してみます。(BADプ ラク ティスです) resource "aws_db_instance" "my-db-1" { allocated_storage = 10 engine = "mysql" engine_version = "5.7" instance_class = "db.t3.micro" username = "master" # password に平文でパスワードを記述する悪い例 password = "MyPassword123" vpc_security_group_ids = [ aws_security_group.db.id ] skip_final_snapshot = true db_subnet_group_name = "db-subnet-group" } デプロイ後の terraform.tfstate ファイルには、次のとおり平文でパスワード文字列が記録されてしまっています。 例のように .tf ファイルに直接 password を書くのは最悪ですが、他の回避策(Variablesを利用して apply 時にターミナルで指定する、Secrets Manager シークレットに先に登録してから Datasource で取得する、etc)をとったところでどうしても tfstate ファイルに記録されてしまうのは問題でした。 manage_master_user_password を使った場合 password 属性を削除し、 manage_master_user_password 属性に true を設定します。 resource "aws_db_instance" "my-db-2" { allocated_storage = 10 engine = "mysql" engine_version = "5.7" instance_class = "db.t3.micro" username = "master" # manage_master_user_password を利用する manage_master_user_password = true vpc_security_group_ids = [ aws_security_group.db.id ] skip_final_snapshot = true db_subnet_group_name = "db-subnet-group" } デプロイすると、Secrets Manager にシークレットが作成されていることがわかります。 シークレットのキーには username と password が登録されており、 password は RDS が生成したランダムな値になっていました。また 7日間の間隔でシークレットローテーションも設定されていました。 デプロイ後の terraform.tfstate ファイルでは、 password は null となっており、実際のパスワード文字列はどこにも記録されていません。 aws _rds_cluster にも利用できる aws_rds_cluster リソースに対しても、従来の master_password の代わりに manage_master_user_password を利用できるようになっています。 (参考) https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster#rdsaurora-managed-master-passwords-via-secrets-manager-default-kms-key KMSキーを指定する master_user_secret_kms_key_id 属性を利用することで、デフォルトキーではなく指定した KMS キーでシークレットの暗号化をすることもできます。 (参考) https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#managed-master-passwords-via-secrets-manager-specific-kms-key https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster#rdsaurora-managed-master-passwords-via-secrets-manager-specific-kms-key 既存の Secrets Manager シークレットを利用する場合 今回は試していませんが、 master_user_secret の設定ブロックを使うと、既存の Secrets Manager シークレットをマスターユーザーのパスワードにできるようです。 (参考) https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#master_user_secret 既存のDBに使ったらどうなるか 既に password や master_password を使って作成した DB に対して、 manage_master_user_password を利用する方法に切り替えることは可能です。DB は再作成されませんが、マスターユーザーのパスワードは新規に作成され(現在のパスワードは引き継がれずに) Secrets Manager に新しいシークレットとして保存されるようです。既存の DB で manage_master_user_password を使う方法に切り替える場合は、アプリケーションの動作や運用に影響がないか慎重に確認しましょう。 スナップショットからリストアする時の挙動 manage_master_user_password を利用して作成した DB のスナップショットからリストアする場合、マスターユーザーのパスワードは少し特殊な状態になるようです。次のように manage_master_user_password = true を使ってスナップショットから DB インスタンス を作成しても、Secrets Manager にパスワードは統合されず、RDS がパスワードを管理している状態で DB が復元しました。パスワードはスナップショット取得時点のものでした。 data "aws_db_snapshot" "my-snapshot" { db_snapshot_identifier = "snapshot1" } resource "aws_db_instance" "my-db-from-snapshot" { allocated_storage = 10 engine = "mysql" engine_version = "5.7" instance_class = "db.t3.micro" username = "master" # snapshot_identifier を利用してリストアする時、 # manage_master_user_password 属性を使っても # パスワードが Secrets Manager で管理されない。 manage_master_user_password = true skip_final_snapshot = true db_subnet_group_name = "db-subnet-group" # スナップショットから復元する snapshot_identifier = "$ { data.aws_db_snapshot.my-snapshot.id } " } この現象は GitHub の Issue にも報告されており、記事執筆時点では未解決です。ただし tfstate ファイルに記録された password は null だったため、Terraform AWS Provider のバグというよりは RDS の制約なのかもしれません。 リストアした DB のパスワードも Secrets Manager で管理するためには、今のところの次の ワークアラウンド が良さそうです。 manage_master_user_password を付けずに Terraform でスナップショットをリストア( apply ) スナップショット取得時点のパスワードを、RDS が管理している状態でリストアされる manage_master_user_password = true を付けてもう一度 apply する マスターユーザーのパスワードは新規に作成されて Secrets Manager に保存される data "aws_db_snapshot" "my-snapshot" { db_snapshot_identifier = "snapshot1" } resource "aws_db_instance" "my-db-from-snapshot" { allocated_storage = 10 engine = "mysql" engine_version = "5.7" instance_class = "db.t3.micro" username = "master" # リストア時は manage_master_user_password を付けずに apply # リストア後 manage_master_user_password を付けて再度 apply # manage_master_user_password = true skip_final_snapshot = true db_subnet_group_name = "db-subnet-group" snapshot_identifier = "$ { data.aws_db_snapshot.my-snapshot.id } " } さいごに Terraform において tfstate ファイルに平文の DB パスワードが残ってしまう問題を簡単に解消する manage_master_user_password 属性についてご紹介しました。今後新たに Terraform で RDS インスタンス / クラスタ ーを作る時には、ぜひ利用を検討しましょう。ただしスナップショットからリストアする時の動きは(記事執筆時点で)少し特殊なので要注意です。 お読みいただいてありがとうございました。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア 執筆: @kou.kinyo 、レビュー: 寺山 輝 (@terayama.akira) ( Shodo で執筆されました )
みなさんこんにちは! ISID 金融ソリューション事業部の松崎です。 前回 の記事では、「フォトグラメトリによる3Dモデル作成ワークフロー(後編)」と題し、 フォトグラメトリで使用する写真撮影後の現像や加工手法、RealityCaptureを使ってのモデル作成方法について紹介しました。 まだご覧になっていない方は、是非読んでいただけると嬉しいです! 今回は、回転台とグリーンバックを使ったお手軽フォトグラメトリ方法を紹介します。 はじめに フォトグラメトリにおいて、「写真撮影」は手間のかかる工程の1つです。 なぜなら、フォトグラメトリでは物体を様々なアングルで撮影する必要があるからです。 1、2つの物体を対象としている場合ならまだしも、小物を量産するなどで対象物体が何個もある場合は、撮影だけで非常に時間がかかってしまいます。 今回は、そんな撮影の手間を大幅に減らす方法を試してみました。 具体的な方法としては、「回転台を使って物体側を回転させる」というものです。 物体の方を回転させてしまえば、カメラは固定したまま様々なアングルで撮影することが出来ますので、簡単にハイクオリティな撮影が可能になります。 しかし、通常フォトグラメトリでは物体を静止させる必要があります。 理由としては、物体の位置・向きや背景情報を使って写真の撮影位置を推定しているからです。 物体の位置や向きが途中で変わってしまうと、背景情報との整合性が保てなくなり、位置推定が出来なくなってしまいます。 そこで、今回はグリーンバックを使用しました。 あらかじめ背景をグリーンバックで統一しておき、現像時にマスク化を行います。 これにより、対象物体の位置・向きのみを用いて撮影位置を推定させることが出来ます。 以下、その手順を説明していきます。 手順 写真撮影 RAW現像 マスク・切り抜き処理 モデル作成 使用機材 回転台 3DCGモデルを作成したい対象物体を載せる回転台です。 後に写真の回転台部分もマスク化処理をかけることを考えると、単色のものが望ましいです。 また、種類によって「プレートの大きさ」「耐荷重」「回転できる角度」などが異なります。 乗せる物体をある程度想定し、適切なものを購入しましょう。 参考までに、私達が所持している回転台のスペックを記載します。 プレートの大きさ:40cm 耐荷重:100kg 回転できる角度:0.1°~90° グリーンバック 回転台や対象物体の背景に設置するグリーンバックです。 グリーンバックは、なるべくシワや折り目がないようにしましょう。 (クロマキー合成をするわけではないので、過剰に気にする必要はないです) 私達の購入したグリーンバックは、購入時は折り目とシワがクッキリとついてしまっていました。 色々とシワ解消方法を試しましたが、衣類用のスチームアイロンを使用するのが最も効果的でした。 三脚 カメラを固定する用の三脚です。 今回はカメラを乗せて置いておくだけですので、ある程度の高さ調節機能があれば良いです。 1.写真撮影 まず初めに写真撮影を行います。 前述した通り、以下のように「カメラ/三脚」「対象物」「回転台」「グリーンバック」を配置しました。 三脚の最低高さが30㎝ほどあり、回転台を床に置くとかなり下向きな視点になってしまう為、ローテーブルの上に回転台を置いています。 上記の配置にて撮影した写真例を以下に示します。 ブログに載せる過程で画質が下がっておりますが、実際にはテーブルやグリーンバックまで鮮明に撮影しております。 回転台を用いた撮影では、 対象物と周囲の境界が明確になること を心がけました。 (今回は黒・白・緑で周囲が埋まる様にした) これにより、マスク処理を行う際に対象物が正確にマスクされやすくなります。(詳細は3章にて説明します) 今回は裏面も含めてモデル化を行いますので、対象物を裏返した状態でも撮影を行いました。 全て合計すると、230枚ほど撮影しています。(回転台は約7度ずつ回転) 以前、回転台を使わずに撮影した際は1時間以上かかりましたが、今回は 20分 ほどで撮影完了しました。 以上で、写真撮影は完了です。 2.RAW現像 次に、写真のRAW現像を行います。 今回は簡略化して紹介していますので、RAW現像に関する詳細な手順を知りたい方は こちらの記事 をご参照ください。 撮影した写真を Adobe Bridgeにて開き、ファイル名を一括変更します。 ファイル名を変更した写真を Adobe Lightroom で開き、 ヒストグラム を見ながら明暗・色味を調整して現像します。 RAW現像を行い、下記のようにJPG形式のファイルを生成しました。 3.マスク・切り抜き処理 写真のRAW現像が完了しましたら、マスク・切り抜き処理を行います。 なお、マスク・切り抜きには以下のアプリを使用します。 Adobe Photoshop (Win/ mac 対応 有料:1078円/月 ※2023年7月現在) まず初めに、 Adobe Photoshop を起動します。 起動しましたら、「ウィンドウ → アクション」でアクションウィンドウを表示させます。 アクションとは Photoshop の自動化ツールの1つです。 今回は、「RAW現像した写真からモデル化対象物のみを切り抜く処理」をアクションとして記録していきます。 アクションウィンドウが表示されましたら、「新規アクションを作成」を選択します。 アクションの設定画面が表示されますので、アクション名を入力して、アクションの記録を開始します。 今回は、「背景削除」と 命名 しました。 記録が開始されましたら、先ほどRAW現像したファイルの一つを開きます。 「ファイル → 開く」でファイル選択画面を表示し、RAW現像したファイルが格納されているフォルダから、1つ選択します。 (この1つは、上記フォルダ内のファイルであればどれでも大丈夫です) ファイルが開けましたら、「選択範囲 → 選択とマスク」を押下しマスク選択状態にします。 「被写体を選択」モードにした上で、画面上の被写体(モデル化の対象物)を左クリックします。 この際、被写体とその周辺の境界が曖昧ですと、被写体の選択範囲が正確に推定されないことがあります。 被写体の選択範囲が正確でない場合は、写真撮影をやり直すことが望ましいです。 (他の写真でも同様の事象が発生する可能性が高い為) 被写体が選択できましたら「出力設定 → 不要なカラーの除去」にチェックを入れ、「OK」を押してマスク選択を終了します。 「ファイル → コピーを保存」を選択し、RAW現像ファイルが格納されているフォルダと同じ ディレクト リに別フォルダを作成して保存します。ファイル形式は JPEG を選択します。 「保存」を押すと、 JPEG オプションが表示されます。今回は最高画質を選択しました。 保存が完了しましたら、アクションの記録を停止します。 これにて、「RAW現像した写真からモデル化対象物のみを切り抜く処理」をアクションとして記録できました。 記録したアクションを利用して、RAW現像したすべての写真に切り抜き処理を適用させます。 「ファイル → 自動処理 → バッチ」を選択します。 バッチでは、以下のように設定を行います。 アクション:背景削除(上記で記録したアクション) ソース:フォルダ ソースフォルダ選択:RAW現像した写真が格納されているフォルダ(Developed) "開く"コマンドを無視:ON 実行後:フォルダ 実行後フォルダ選択:アクション記録中に作成した別フォルダ(Masked) "別名で保存"コマンドを省略:ON 設定が完了しましたら、「OK」を押して自動処理が完了するまで待ちます。 約230枚の自動処理に 1時間30分 ほどかかりました。 自動処理が完了すると、以下のように対象物のみ切り抜かれたファイルが作成されています。 念のため、対象物が正確に切り抜かれていることを確認します。 下図のように不自然に白くなっている部分がある場合などは、手動で切り抜き処理をやり直します。 何度試しても上手く切り抜けない写真は、テクスチャ作成時に使用しないなどの対処を検討しましょう。 4.モデル作成 前章にてモデル化に必要な素材写真がそろいましたので、RealityCaptureを用いてモデル・テクスチャを作成します。 今回は簡略化して紹介していますので、モデル・テクスチャの作成に関する詳細な手順を知りたい方は こちらの記事 をご参照ください。 RealityCaptureを開き、対象物を切り抜いた写真が格納されているファイルを取り込みます。 続いて、アライメントを実行します。 アライメントの結果、2つの コンポーネント が作成されました。(所要時間:3分) これは、対象物の表面・裏面を両方撮影している為です。 このままでは表面・裏面のモデルがそれぞれ作成されてしまうので、コン トロール ポイントを用いて2つの コンポーネント をマージします。 コン トロール ポイントとは、写真や コンポーネント 上に目印となるポイントを設定できる機能です。 コンポーネント 間の共通ポイントをコン トロール ポイントとして指定することで、異なる コンポーネント をマージすることが出来ます。 まず、「IMAGE 2D → TOOLS → Add Control Points」を選択し、コン トロール ポイント設定状態にします。 コンポーネント 1にコン トロール ポイントを設定していきます。 画面上の点群から、コン トロール ポイントとして設定する点を左クリックします。 コン トロール ポイントの設定基準としては、以下を意識しましょう。 両方の コンポーネント に存在する点 対象物の中で特徴的な点(角部分や、色が変化している部分) 今回は、コン トロール ポイントを3点設定しました。 3次元の コンポーネント をマージすることを踏まえると、最低3点は設定することが望ましいです。 同様に、 コンポーネント 0にもコン トロール ポイントを設定します。 コンポーネント 1で設定したコン トロール ポイントと同じ部分を設定しましょう。 ( コンポーネント 間でコン トロール ポイントの位置が異なると、ズレた状態でマージされてしまいます) 次に、設定したコン トロール ポイントと各写真の位置推定結果に差がないことを検証します。 写真1枚ごとに「+」を押し、提案された全ての写真に対して検証を行いましょう。 検証結果に問題がない場合は、「アライメント → コンポーネント のマージ」から コンポーネント のマージを行います。 コンポーネント がマージされ、表面・裏面の両方が含まれる新規 コンポーネント が作成されました。 マージ時にズレが発生していないことを確認した後、「Mesh Model → High Detail」を選択しメッシュモデルを作成します。 メッシュモデルが作成できました。(所要時間:80分) 次に、「Mesh Model → Tecture」からテクスチャを作成します。 テクスチャが作成されました。(所要時間:20分) 以上で、3DCGモデルの作成完了です。 おわりに 本記事では、回転台とグリーンバッグを用いたお手軽フォトグラメトリ方法について紹介しました。 回転台を使った撮影方法と、通常の撮影方法における「写真撮影~現像などの後処理」までの所要時間は以下になります。 通常方式:写真撮影(約80分) + 後処理(約20分)= 約100分(人の作業時間:100分) 回転台方式:写真撮影(約20分) + 後処理(約20分+約90分)= 約130分(人の作業時間:40分) 回転台方式では撮影時間を短縮できる一方、 Photoshop での切り抜き処理に時間を要してしまい、合計所要時間では通常方式が早い結果となりました。 一方、通常の撮影方法では人が100分間作業し続ける必要があるのに対し、回転台方式では後処理の90分間を Photoshop が自動的に進めてくれます。人が作業する時間で考えると、回転台方式が圧倒的に早い結果となりました。 実際に運用する際は、 Photoshop での処理中に次の撮影を行うなど、時間を工夫して効率化を図ることが望ましいです。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ 参考文献 ・ 初歩からのフォトグラメトリ~RealityCaptureの使い方(ターンテーブル編) ・ フォトグラメトリによる3Dモデル作成ワークフロー(後編) ・ Adobe Photoshop 公式サイト ・ 最新版Photoshopの「選択とマスク」で画像を切り抜く方法 ・ Photoshopのアクション機能(自動処理)を使って複数の画像を一括編集する方法 ・ はじめてのRealityCapture - 完全なモデルを作成する手順 執筆: @matsuzaki.shota 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
みなさんこんにちは! ISID 金融ソリューション事業部の松崎です。 前回 の記事では、「フォトグラメトリによる3Dモデル作成ワークフロー(後編)」と題し、 フォトグラメトリで使用する写真撮影後の現像や加工手法、RealityCaptureを使ってのモデル作成方法について紹介しました。 まだご覧になっていない方は、是非読んでいただけると嬉しいです! 今回は、回転台とグリーンバックを使ったお手軽フォトグラメトリ方法を紹介します。 はじめに フォトグラメトリにおいて、「写真撮影」は手間のかかる工程の1つです。 なぜなら、フォトグラメトリでは物体を様々なアングルで撮影する必要があるからです。 1、2つの物体を対象としている場合ならまだしも、小物を量産するなどで対象物体が何個もある場合は、撮影だけで非常に時間がかかってしまいます。 今回は、そんな撮影の手間を大幅に減らす方法を試してみました。 具体的な方法としては、「回転台を使って物体側を回転させる」というものです。 物体の方を回転させてしまえば、カメラは固定したまま様々なアングルで撮影することが出来ますので、簡単にハイクオリティな撮影が可能になります。 しかし、通常フォトグラメトリでは物体を静止させる必要があります。 理由としては、物体の位置・向きや背景情報を使って写真の撮影位置を推定しているからです。 物体の位置や向きが途中で変わってしまうと、背景情報との整合性が保てなくなり、位置推定が出来なくなってしまいます。 そこで、今回はグリーンバックを使用しました。 あらかじめ背景をグリーンバックで統一しておき、現像時にマスク化を行います。 これにより、対象物体の位置・向きのみを用いて撮影位置を推定させることが出来ます。 以下、その手順を説明していきます。 手順 写真撮影 RAW現像 マスク・切り抜き処理 モデル作成 使用機材 回転台 3DCGモデルを作成したい対象物体を載せる回転台です。 後に写真の回転台部分もマスク化処理をかけることを考えると、単色のものが望ましいです。 また、種類によって「プレートの大きさ」「耐荷重」「回転できる角度」などが異なります。 乗せる物体をある程度想定し、適切なものを購入しましょう。 参考までに、私達が所持している回転台のスペックを記載します。 プレートの大きさ:40cm 耐荷重:100kg 回転できる角度:0.1°~90° グリーンバック 回転台や対象物体の背景に設置するグリーンバックです。 グリーンバックは、なるべくシワや折り目がないようにしましょう。 (クロマキー合成をするわけではないので、過剰に気にする必要はないです) 私達の購入したグリーンバックは、購入時は折り目とシワがクッキリとついてしまっていました。 色々とシワ解消方法を試しましたが、衣類用のスチームアイロンを使用するのが最も効果的でした。 三脚 カメラを固定する用の三脚です。 今回はカメラを乗せて置いておくだけですので、ある程度の高さ調節機能があれば良いです。 1.写真撮影 まず初めに写真撮影を行います。 前述した通り、以下のように「カメラ/三脚」「対象物」「回転台」「グリーンバック」を配置しました。 三脚の最低高さが30㎝ほどあり、回転台を床に置くとかなり下向きな視点になってしまう為、ローテーブルの上に回転台を置いています。 上記の配置にて撮影した写真例を以下に示します。 ブログに載せる過程で画質が下がっておりますが、実際にはテーブルやグリーンバックまで鮮明に撮影しております。 回転台を用いた撮影では、 対象物と周囲の境界が明確になること を心がけました。 (今回は黒・白・緑で周囲が埋まる様にした) これにより、マスク処理を行う際に対象物が正確にマスクされやすくなります。(詳細は3章にて説明します) 今回は裏面も含めてモデル化を行いますので、対象物を裏返した状態でも撮影を行いました。 全て合計すると、230枚ほど撮影しています。(回転台は約7度ずつ回転) 以前、回転台を使わずに撮影した際は1時間以上かかりましたが、今回は 20分 ほどで撮影完了しました。 以上で、写真撮影は完了です。 2.RAW現像 次に、写真のRAW現像を行います。 今回は簡略化して紹介していますので、RAW現像に関する詳細な手順を知りたい方は こちらの記事 をご参照ください。 撮影した写真を Adobe Bridgeにて開き、ファイル名を一括変更します。 ファイル名を変更した写真を Adobe Lightroom で開き、 ヒストグラム を見ながら明暗・色味を調整して現像します。 RAW現像を行い、下記のようにJPG形式のファイルを生成しました。 3.マスク・切り抜き処理 写真のRAW現像が完了しましたら、マスク・切り抜き処理を行います。 なお、マスク・切り抜きには以下のアプリを使用します。 Adobe Photoshop (Win/ mac 対応 有料:1078円/月 ※2023年7月現在) まず初めに、 Adobe Photoshop を起動します。 起動しましたら、「ウィンドウ → アクション」でアクションウィンドウを表示させます。 アクションとは Photoshop の自動化ツールの1つです。 今回は、「RAW現像した写真からモデル化対象物のみを切り抜く処理」をアクションとして記録していきます。 アクションウィンドウが表示されましたら、「新規アクションを作成」を選択します。 アクションの設定画面が表示されますので、アクション名を入力して、アクションの記録を開始します。 今回は、「背景削除」と 命名 しました。 記録が開始されましたら、先ほどRAW現像したファイルの一つを開きます。 「ファイル → 開く」でファイル選択画面を表示し、RAW現像したファイルが格納されているフォルダから、1つ選択します。 (この1つは、上記フォルダ内のファイルであればどれでも大丈夫です) ファイルが開けましたら、「選択範囲 → 選択とマスク」を押下しマスク選択状態にします。 「被写体を選択」モードにした上で、画面上の被写体(モデル化の対象物)を左クリックします。 この際、被写体とその周辺の境界が曖昧ですと、被写体の選択範囲が正確に推定されないことがあります。 被写体の選択範囲が正確でない場合は、写真撮影をやり直すことが望ましいです。 (他の写真でも同様の事象が発生する可能性が高い為) 被写体が選択できましたら「出力設定 → 不要なカラーの除去」にチェックを入れ、「OK」を押してマスク選択を終了します。 「ファイル → コピーを保存」を選択し、RAW現像ファイルが格納されているフォルダと同じ ディレクト リに別フォルダを作成して保存します。ファイル形式は JPEG を選択します。 「保存」を押すと、 JPEG オプションが表示されます。今回は最高画質を選択しました。 保存が完了しましたら、アクションの記録を停止します。 これにて、「RAW現像した写真からモデル化対象物のみを切り抜く処理」をアクションとして記録できました。 記録したアクションを利用して、RAW現像したすべての写真に切り抜き処理を適用させます。 「ファイル → 自動処理 → バッチ」を選択します。 バッチでは、以下のように設定を行います。 アクション:背景削除(上記で記録したアクション) ソース:フォルダ ソースフォルダ選択:RAW現像した写真が格納されているフォルダ(Developed) "開く"コマンドを無視:ON 実行後:フォルダ 実行後フォルダ選択:アクション記録中に作成した別フォルダ(Masked) "別名で保存"コマンドを省略:ON 設定が完了しましたら、「OK」を押して自動処理が完了するまで待ちます。 約230枚の自動処理に 1時間30分 ほどかかりました。 自動処理が完了すると、以下のように対象物のみ切り抜かれたファイルが作成されています。 念のため、対象物が正確に切り抜かれていることを確認します。 下図のように不自然に白くなっている部分がある場合などは、手動で切り抜き処理をやり直します。 何度試しても上手く切り抜けない写真は、テクスチャ作成時に使用しないなどの対処を検討しましょう。 4.モデル作成 前章にてモデル化に必要な素材写真がそろいましたので、RealityCaptureを用いてモデル・テクスチャを作成します。 今回は簡略化して紹介していますので、モデル・テクスチャの作成に関する詳細な手順を知りたい方は こちらの記事 をご参照ください。 RealityCaptureを開き、対象物を切り抜いた写真が格納されているファイルを取り込みます。 続いて、アライメントを実行します。 アライメントの結果、2つの コンポーネント が作成されました。(所要時間:3分) これは、対象物の表面・裏面を両方撮影している為です。 このままでは表面・裏面のモデルがそれぞれ作成されてしまうので、コン トロール ポイントを用いて2つの コンポーネント をマージします。 コン トロール ポイントとは、写真や コンポーネント 上に目印となるポイントを設定できる機能です。 コンポーネント 間の共通ポイントをコン トロール ポイントとして指定することで、異なる コンポーネント をマージすることが出来ます。 まず、「IMAGE 2D → TOOLS → Add Control Points」を選択し、コン トロール ポイント設定状態にします。 コンポーネント 1にコン トロール ポイントを設定していきます。 画面上の点群から、コン トロール ポイントとして設定する点を左クリックします。 コン トロール ポイントの設定基準としては、以下を意識しましょう。 両方の コンポーネント に存在する点 対象物の中で特徴的な点(角部分や、色が変化している部分) 今回は、コン トロール ポイントを3点設定しました。 3次元の コンポーネント をマージすることを踏まえると、最低3点は設定することが望ましいです。 同様に、 コンポーネント 0にもコン トロール ポイントを設定します。 コンポーネント 1で設定したコン トロール ポイントと同じ部分を設定しましょう。 ( コンポーネント 間でコン トロール ポイントの位置が異なると、ズレた状態でマージされてしまいます) 次に、設定したコン トロール ポイントと各写真の位置推定結果に差がないことを検証します。 写真1枚ごとに「+」を押し、提案された全ての写真に対して検証を行いましょう。 検証結果に問題がない場合は、「アライメント → コンポーネント のマージ」から コンポーネント のマージを行います。 コンポーネント がマージされ、表面・裏面の両方が含まれる新規 コンポーネント が作成されました。 マージ時にズレが発生していないことを確認した後、「Mesh Model → High Detail」を選択しメッシュモデルを作成します。 メッシュモデルが作成できました。(所要時間:80分) 次に、「Mesh Model → Tecture」からテクスチャを作成します。 テクスチャが作成されました。(所要時間:20分) 以上で、3DCGモデルの作成完了です。 おわりに 本記事では、回転台とグリーンバッグを用いたお手軽フォトグラメトリ方法について紹介しました。 回転台を使った撮影方法と、通常の撮影方法における「写真撮影~現像などの後処理」までの所要時間は以下になります。 通常方式:写真撮影(約80分) + 後処理(約20分)= 約100分(人の作業時間:100分) 回転台方式:写真撮影(約20分) + 後処理(約20分+約90分)= 約130分(人の作業時間:40分) 回転台方式では撮影時間を短縮できる一方、 Photoshop での切り抜き処理に時間を要してしまい、合計所要時間では通常方式が早い結果となりました。 一方、通常の撮影方法では人が100分間作業し続ける必要があるのに対し、回転台方式では後処理の90分間を Photoshop が自動的に進めてくれます。人が作業する時間で考えると、回転台方式が圧倒的に早い結果となりました。 実際に運用する際は、 Photoshop での処理中に次の撮影を行うなど、時間を工夫して効率化を図ることが望ましいです。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ 参考文献 ・ 初歩からのフォトグラメトリ~RealityCaptureの使い方(ターンテーブル編) ・ フォトグラメトリによる3Dモデル作成ワークフロー(後編) ・ Adobe Photoshop 公式サイト ・ 最新版Photoshopの「選択とマスク」で画像を切り抜く方法 ・ Photoshopのアクション機能(自動処理)を使って複数の画像を一括編集する方法 ・ はじめてのRealityCapture - 完全なモデルを作成する手順 執筆: @matsuzaki.shota 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 CDK にはローカルファイルを S3 バケット にデプロイできる BucketDeployment という便利なコンスト ラク トがあり、静的ファイルの配信などの用途に利用できます。 これを使って JavaScript ファイルを CDK リポジトリ からデプロイする時に、ついでにファイルの SRI ハッシュ も計算して、Web アプリから参照できるように SSM パラメータストア or Secrets Manager に保存してみました。 SRI とは 想定している構成例 自前で管理している S3 バケットではどれほどの効果があるのか 実装例 BucketDeployment で静的ファイルを S3 にデプロイ ファイルの SRI ハッシュを計算する SRI ハッシュを SSM Parameter Store か Secrets Manager に登録 (おまけ) aws-actions/aws-secretsmanager-get-secrets SRI とは 参考: サブリソース完全性 (MDN) サブリソース完全性 (Subresource Integrity) のことであり、取得したリソースが改ざんされていないかを検出するブラウザの仕組みです。 Webサイトが配信する <script> タグの integrity 属性にリソースの ハッシュ値 を付加することで、ブラウザは実際に取得したリソースの ハッシュ値 と一致するかどうかを確認し、一致しなかった場合にはリソースは実行されません。 < script src = "https://static.my-domain.com/myscript.js" crossorigin = "anonymous" integrity= "sha384-mY5hfCTEoihIw/5Wlnjpg/00npMlmKiMCZFMqFRYQRbvhDpcA/JkTv7utYUF+/kA" ></ script > これにより、例えば JavaScript の中身が改ざんされた場合、悪意のあるコードが実行されることを防ぐことができます。 想定している構成例 Web アプリケーションとは別 ドメイン から スクリプト ファイルをロードする構成を想定します。 スクリプト ファイルは自組織が管理する S3 バケット に配置され、CloudFront ディストリビューション 経由で配信されているとします。 CDK で S3 バケット に スクリプト をデプロイする時に、ついでに スクリプト ファイルの SRI ハッシュを計算し、SSM パラメータストア or Secrets Manager に保存しておきます。Web アプリケーションのデプロイ時などにパラメータにアクセスし、読み込む スクリプト の <script> タグの integrity 属性に埋め込むように構成すれば、S3 バケット もしくは CloudFront ディストリビューション が配信するファイルが改ざんされた場合には スクリプト は実行されません。 ※ Web アプリのデプロイ時に SRI ハッシュを取得する場合、配信する スクリプト ファイルを更新したら Web アプリも再デプロイする必要があることにご注意ください。 図で赤枠に囲まれている、 スクリプト ファイルと ハッシュ値 のデプロイが、この記事で紹介したい部分です。 自前で管理している S3 バケット ではどれほどの効果があるのか SRI はパブリックな CDN が配信するリソースに対して利用されることが多い印象です。そのようなリソースは攻撃の対象として狙われやすく、改ざんが成功した時の影響範囲が広いため、SRI をサポートしているリソースの場合は是非 Web アプリで対応しておきたいです。 一方で今回想定しているような、自前で管理している S3 バケット からリソースを配信する場合、SRI を利用することで防げることは比較的限定されます。いくつかの攻撃パターンに対する SRI の効果を考えてみます。 意図しない第 三者 が AWS の強いアクセス権を獲得してしまった SRI はあまり意味がない。Web アプリが付与する SRI ハッシュ値 を書き換えるか、 integrity 属性を完全に削除してしまえば スクリプト は改ざんし放題 そもそも スクリプト の改ざん以外にも、直接データベースの中身を盗んだりいろいろできてしまう CloudFront / S3 の設定ミスで、意図せず スクリプト が外部から改ざんできる状態になっていた SRI の効果がある。CloudFront と S3 は一般アクセスを許可する構成を取れるため、基本プライベートな SSM パラメータストアや Secrets Manager、Webアプリのデプロイパイプラインよりも外部から侵入できる状態になっているリスクが高い CloudFront / S3 自体のバグで、意図せず スクリプト が外部から改ざんできる状態になっていた SRI の効果がある 自前で管理している S3 バケット に対しての効果は限定的とはいえ、 プログラミング言語 で IaC を書ける CDK であれば、さほど難しくない実装で SRI ハッシュの計算と保存をすることができます。効果が限定的だからこそ、悩まず簡単に実装できるようにブログ記事に書き残しておく意味はあるんだろうなと思っています。 ちなみに S3 の設定ミスによる外部公開を防ぐために、 パブリックアクセスブロック設定 を利用しましょう。 OAC を使えば CloudFront のオリジンとなっている S3 バケット にもパブリックアクセスブロックを設定できます。 実装例 ここから実装例を紹介していきます。 BucketDeployment で静的ファイルを S3 にデプロイ BucketDeployment コンスト ラク トを利用し、 ./static フォルダの中身を S3 バケット にデプロイするコンスト ラク ト例です。S3 バケット と CloudFront ディストリビューション は別コンスト ラク トで作成済みの前提です。 distribution プロパティに CloudFront ディストリビューション を渡すことで、デプロイ時に CloudFront のキャッシュの無効化もやってくれます。 Cache-Control ヘッダーに no-store を設定するのがポイントで、これによりクライアント(ブラウザ)側でリソースがキャッシュされなくなり、 スクリプト を更新した時にキャッシュされた古い スクリプト と新しい SRI ハッシュの整合性が取れなくなる事態を回避できます。 import { StackProps } from "aws-cdk-lib" ; import * as cloudfront from "aws-cdk-lib/aws-cloudfront" ; import * as s3 from "aws-cdk-lib/aws-s3" ; import * as s3deployment from "aws-cdk-lib/aws-s3-deployment" ; import { Construct } from "constructs" ; export interface MyConstructProps extends StackProps { bucket: s3.Bucket ; distribution: cloudfront.Distribution ; } export class MyConstruct extends Construct { constructor( scope: Construct , id: string , props: MyConstructProps ) { super( scope , id ); new s3deployment.BucketDeployment ( this , "MyBucketDeployment" , { destinationBucket: props.bucket , sources: [ s3deployment.Source.asset ( "./static" ) ] , prune: false , retainOnDelete: false , distribution: props.distribution , cacheControl: [ s3deployment.CacheControl.noStore () ] , contentType: "text/javascript" , } ); } } ファイルの SRI ハッシュを計算する TypeScript では次のような関数で、ファイルの SRI 用の ハッシュ値 を計算できます。 import * as crypto from "crypto" ; import * as fs from "fs" ; export const sriHash = ( filePath: string ) : string => { const data = fs.readFileSync ( filePath ); const digest = crypto.createHash ( "sha384" ) .update ( data ) .digest ( "base64" ); return `sha384- ${ digest } ` ; } ; SRI ハッシュを SSM Parameter Store か Secrets Manager に登録 上に記載した SRI ハッシュを計算する関数は同期処理なので CDK スタック内から普通に呼び出せます。 new secretsmanager.Secret ( this , "MySRISecret" , { secretName: "MySRI" , secretStringValue: SecretValue.unsafePlainText ( sriHash ( "./static/myscript.js" )), removalPolicy: RemovalPolicy.DESTROY , } ); new ssm.StringParameter ( this , "MySRIParameter" , { parameterName: "/MY_SRI" , stringValue: sriHash ( "./static/myscript.js" ), } ); (おまけ) aws -actions/ aws -secretsmanager-get-secrets パラメータとして登録された SRI ハッシュを、Web アプリのデプロイ時に取得して <script> タグに埋め込む方法はいろいろあると思いますが、 GitHub Actions では aws-actions/aws-secretsmanager-get-secrets という AWS 公式の Action を利用するのが便利です。SSM パラメータストアには使えず Secrets Manager 限定ですが、次の例のようにシークレット名 MySRI を取得し、 MY_SCRIPT_SRI という 環境変数 に格納してくれます。 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 with: role-to-assume: arn:aws:iam::111122223333:role/my-deployment-role aws-region: ap-northeast-1 - name: Get secrets by name and by ARN uses: aws-actions/aws-secretsmanager-get-secrets@v1 with: secret-ids: | MY_SCRIPT_SRI, MySRI parse-json-secrets: false 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア 執筆: @kou.kinyo 、レビュー: @kano.nanami ( Shodo で執筆されました )
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 CDK にはローカルファイルを S3 バケット にデプロイできる BucketDeployment という便利なコンスト ラク トがあり、静的ファイルの配信などの用途に利用できます。 これを使って JavaScript ファイルを CDK リポジトリ からデプロイする時に、ついでにファイルの SRI ハッシュ も計算して、Web アプリから参照できるように SSM パラメータストア or Secrets Manager に保存してみました。 SRI とは 想定している構成例 自前で管理している S3 バケットではどれほどの効果があるのか 実装例 BucketDeployment で静的ファイルを S3 にデプロイ ファイルの SRI ハッシュを計算する SRI ハッシュを SSM Parameter Store か Secrets Manager に登録 (おまけ) aws-actions/aws-secretsmanager-get-secrets SRI とは 参考: サブリソース完全性 (MDN) サブリソース完全性 (Subresource Integrity) のことであり、取得したリソースが改ざんされていないかを検出するブラウザの仕組みです。 Webサイトが配信する <script> タグの integrity 属性にリソースの ハッシュ値 を付加することで、ブラウザは実際に取得したリソースの ハッシュ値 と一致するかどうかを確認し、一致しなかった場合にはリソースは実行されません。 < script src = "https://static.my-domain.com/myscript.js" crossorigin = "anonymous" integrity= "sha384-mY5hfCTEoihIw/5Wlnjpg/00npMlmKiMCZFMqFRYQRbvhDpcA/JkTv7utYUF+/kA" ></ script > これにより、例えば JavaScript の中身が改ざんされた場合、悪意のあるコードが実行されることを防ぐことができます。 想定している構成例 Web アプリケーションとは別 ドメイン から スクリプト ファイルをロードする構成を想定します。 スクリプト ファイルは自組織が管理する S3 バケット に配置され、CloudFront ディストリビューション 経由で配信されているとします。 CDK で S3 バケット に スクリプト をデプロイする時に、ついでに スクリプト ファイルの SRI ハッシュを計算し、SSM パラメータストア or Secrets Manager に保存しておきます。Web アプリケーションのデプロイ時などにパラメータにアクセスし、読み込む スクリプト の <script> タグの integrity 属性に埋め込むように構成すれば、S3 バケット もしくは CloudFront ディストリビューション が配信するファイルが改ざんされた場合には スクリプト は実行されません。 ※ Web アプリのデプロイ時に SRI ハッシュを取得する場合、配信する スクリプト ファイルを更新したら Web アプリも再デプロイする必要があることにご注意ください。 図で赤枠に囲まれている、 スクリプト ファイルと ハッシュ値 のデプロイが、この記事で紹介したい部分です。 自前で管理している S3 バケット ではどれほどの効果があるのか SRI はパブリックな CDN が配信するリソースに対して利用されることが多い印象です。そのようなリソースは攻撃の対象として狙われやすく、改ざんが成功した時の影響範囲が広いため、SRI をサポートしているリソースの場合は是非 Web アプリで対応しておきたいです。 一方で今回想定しているような、自前で管理している S3 バケット からリソースを配信する場合、SRI を利用することで防げることは比較的限定されます。いくつかの攻撃パターンに対する SRI の効果を考えてみます。 意図しない第 三者 が AWS の強いアクセス権を獲得してしまった SRI はあまり意味がない。Web アプリが付与する SRI ハッシュ値 を書き換えるか、 integrity 属性を完全に削除してしまえば スクリプト は改ざんし放題 そもそも スクリプト の改ざん以外にも、直接データベースの中身を盗んだりいろいろできてしまう CloudFront / S3 の設定ミスで、意図せず スクリプト が外部から改ざんできる状態になっていた SRI の効果がある。CloudFront と S3 は一般アクセスを許可する構成を取れるため、基本プライベートな SSM パラメータストアや Secrets Manager、Webアプリのデプロイパイプラインよりも外部から侵入できる状態になっているリスクが高い CloudFront / S3 自体のバグで、意図せず スクリプト が外部から改ざんできる状態になっていた SRI の効果がある 自前で管理している S3 バケット に対しての効果は限定的とはいえ、 プログラミング言語 で IaC を書ける CDK であれば、さほど難しくない実装で SRI ハッシュの計算と保存をすることができます。効果が限定的だからこそ、悩まず簡単に実装できるようにブログ記事に書き残しておく意味はあるんだろうなと思っています。 ちなみに S3 の設定ミスによる外部公開を防ぐために、 パブリックアクセスブロック設定 を利用しましょう。 OAC を使えば CloudFront のオリジンとなっている S3 バケット にもパブリックアクセスブロックを設定できます。 実装例 ここから実装例を紹介していきます。 BucketDeployment で静的ファイルを S3 にデプロイ BucketDeployment コンスト ラク トを利用し、 ./static フォルダの中身を S3 バケット にデプロイするコンスト ラク ト例です。S3 バケット と CloudFront ディストリビューション は別コンスト ラク トで作成済みの前提です。 distribution プロパティに CloudFront ディストリビューション を渡すことで、デプロイ時に CloudFront のキャッシュの無効化もやってくれます。 Cache-Control ヘッダーに no-store を設定するのがポイントで、これによりクライアント(ブラウザ)側でリソースがキャッシュされなくなり、 スクリプト を更新した時にキャッシュされた古い スクリプト と新しい SRI ハッシュの整合性が取れなくなる事態を回避できます。 import { StackProps } from "aws-cdk-lib" ; import * as cloudfront from "aws-cdk-lib/aws-cloudfront" ; import * as s3 from "aws-cdk-lib/aws-s3" ; import * as s3deployment from "aws-cdk-lib/aws-s3-deployment" ; import { Construct } from "constructs" ; export interface MyConstructProps extends StackProps { bucket: s3.Bucket ; distribution: cloudfront.Distribution ; } export class MyConstruct extends Construct { constructor( scope: Construct , id: string , props: MyConstructProps ) { super( scope , id ); new s3deployment.BucketDeployment ( this , "MyBucketDeployment" , { destinationBucket: props.bucket , sources: [ s3deployment.Source.asset ( "./static" ) ] , prune: false , retainOnDelete: false , distribution: props.distribution , cacheControl: [ s3deployment.CacheControl.noStore () ] , contentType: "text/javascript" , } ); } } ファイルの SRI ハッシュを計算する TypeScript では次のような関数で、ファイルの SRI 用の ハッシュ値 を計算できます。 import * as crypto from "crypto" ; import * as fs from "fs" ; export const sriHash = ( filePath: string ) : string => { const data = fs.readFileSync ( filePath ); const digest = crypto.createHash ( "sha384" ) .update ( data ) .digest ( "base64" ); return `sha384- ${ digest } ` ; } ; SRI ハッシュを SSM Parameter Store か Secrets Manager に登録 上に記載した SRI ハッシュを計算する関数は同期処理なので CDK スタック内から普通に呼び出せます。 new secretsmanager.Secret ( this , "MySRISecret" , { secretName: "MySRI" , secretStringValue: SecretValue.unsafePlainText ( sriHash ( "./static/myscript.js" )), removalPolicy: RemovalPolicy.DESTROY , } ); new ssm.StringParameter ( this , "MySRIParameter" , { parameterName: "/MY_SRI" , stringValue: sriHash ( "./static/myscript.js" ), } ); (おまけ) aws -actions/ aws -secretsmanager-get-secrets パラメータとして登録された SRI ハッシュを、Web アプリのデプロイ時に取得して <script> タグに埋め込む方法はいろいろあると思いますが、 GitHub Actions では aws-actions/aws-secretsmanager-get-secrets という AWS 公式の Action を利用するのが便利です。SSM パラメータストアには使えず Secrets Manager 限定ですが、次の例のようにシークレット名 MySRI を取得し、 MY_SCRIPT_SRI という 環境変数 に格納してくれます。 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 with: role-to-assume: arn:aws:iam::111122223333:role/my-deployment-role aws-region: ap-northeast-1 - name: Get secrets by name and by ARN uses: aws-actions/aws-secretsmanager-get-secrets@v1 with: secret-ids: | MY_SCRIPT_SRI, MySRI parse-json-secrets: false 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア 執筆: @kou.kinyo 、レビュー: @kano.nanami ( Shodo で執筆されました )
みなさんこんにちは! ISID 金融ソリューション事業部の松崎です。 前回 の記事では「フォトグラメトリによる3Dモデル作成ワークフロー(前編)」と題し、フォトグラメトリを行う上で必要となる機材や、写真撮影前・撮影時の注意ポイントを紹介しました。 まだご覧になっていない方は、是非前編から読んでいただけると嬉しいです! 今回は後編になります。 撮影後のRAW現像手法、RealityCaptureを用いてのモデル作成方法について紹介します。 ワークフロー一覧 フォトグラメトリによる3Dモデル作成のワークフローは、大きく分けて以下に分けられます。 機材の準備 被写体の確認、撮影環境のロケハン 被写体の撮影 撮影画像のRAW現像 メッシュモデル作成 テクスチャ貼り付け・ローポリ化 後編では、4~6についてご紹介します。 4.撮影画像のRAW現像 写真撮影後のRAW現像手順を紹介します。 RAW現像とは、撮影した生データ(RAWデータ)の明暗・ホワイトバランス・ コントラ ストなどを調整し、 JPEG 形式などの閲覧可能な形式で出力する処理を指します。 ※この手順はRAW形式で撮影したときのみ実施します。 JPEG 形式などで撮影した(カメラ側で現像が完了している)場合はスキップしてください。 なお、現像・加工時には以下のアプリを使用します。 Adobe Bridge (Win/ mac 対応 無料) Adobe Photoshop Lightroom (Win/ mac 対応 有料:1078円/月 ※2023年7月現在) 次行より手順を紹介していきます。 まず初めに、撮影した写真をPCへ移動させ、 Adobe Bridgeを使用して開きます。 Adobe Bridgeでは、現像の前準備を行います。 「ツール → ファイル名をバッチで変更」を選択し、写真のファイル名を変更します。 ファイル名を変更するのは、モデル作成時に他の写真が混ざってしまうのを防止するためです。 ファイル名が変更できたら、必要に応じて写真の メタデータ にキーワードを設定します。 キーワードを設定することで、写真の絞り込みが容易になります。 今回撮影した写真は メタデータ にロックがかかっているので、全写真を選択した後に「右クリック→すべての項目のロックを解除」を選択し、ロックを解除します。 ロックが解除できたら、キーワードを設定します。 今回は全写真に「花壇小」のキーワードを設定しました。 次に、 Adobe Photoshop Lightroom を開き、「ファイル→新規カタログ」で新規カタログを作成します。 Adobe Photoshop Lightroom では、写真の加工・現像を行います。 カタログを作成したら、「読み込み」から写真の読み込みを行います。 ファイル名を変更したファイルを格納したフォルダを選択し、「全てをチェック」で全写真を選択後、「読み込み」を押します。 読み込んだ写真は、「 メタデータ →キーワード」から絞り込みを行うことができます。 今回はキーワードを1つしか設定していない為、絞り込みは行いません。 全写真を選択した後に、「現像」を押して写真の加工調整画面に移ります。 画面右の ヒストグラム を見ながら、加工調整を行います。 加工を行う際は、可能な限り 黒つぶれ・白飛びをなくすよう に各項目を設定します。 また、広角レンズを使っている場合は適宜レンズ補正も行います。 今回は以下の設定値で加工調整を行いました。 露光量:+0.15 コントラ スト:+6 ハイライト:-50 シャドウ:+20 白レベル:-20 黒レベル:+20 レンズ補正:プロファイル補正を使用( Adobe Sony FE 16-35mm F4 ZA OSS ) 調整が完了したら、右下の「同期」を選択し、設定値を他の写真にも同期します。 全写真への同期が完了したら写真を一通り確認し、黒つぶれ・白飛びが発生していないかチェックします。 問題ない場合は、「ファイル→書き出し」を選択し、 JPEG 画像として現像を行います。 現像を行う際、画質設定は「100」としましょう。(劣化を防ぐため) 現像が完了しましたら、 Adobe Bridgeにて現像したファイルを確認します。 この際、ボケなどが発生してフォトグラメトリに適さないファイルは、キーワードを付けて取り除けるようにしておきます。 「 geometry」と「 texture」のフォルダを作成します。(このフォルダ名は固定です) 「 geometry」はRealityCaptureに取り込んだ際に、自動で点群やメッシュモデルの作成用に使われる写真です。 対して、「 texture」はテクスチャ作成用に使われます。 メッシュモデル作成時とテクスチャ作成時で別々の写真を使いたい場合は、各フォルダに使用対象の写真を入れましょう。 例:テクスチャ作成時に黒つぶれ・白飛びした写真を使いたい など 今回はモデル作成用とテクスチャ用で写真を分けないので、両方のフォルダにすべての写真を入れます。 以上で加工・現像の手順は完了です。 なお、ボケ画像などを取り除きたい場合は、適宜「 geometry」と「 texture」フォルダから取り除きましょう。 5.メッシュモデル作成 素材となる写真が揃いましたので、RealityCaptureでメッシュモデルを作成していきます。 なお、RealityCaptureの基本に関しては こちらの記事 にて紹介しておりますので、気になる方はご一読頂ければと思います。 まず初めにRealityCaptureを開き、インポート設定を行います。 「Application→Setting」で設定メニューを開き、インポート設定を以下のように設定します。 Group calibration by Exif :Yes Copy the imported components to the cache:No Ignore GPS Exif :Yes 次に、「ALIGHNMENT→Setting」からアライメント設定を開き、以下を設定します。 Image overlap:Low これにより、アライメントの際に写真同士が繋がり易くなります。 (十分な重なりを担保出来ている場合は、「Normal」や「High」でもよい) 設定が完了しましたら、フォルダを読み込みます。 「WORKFLOW→Folder」と選択し、現像した写真を格納しているフォルダを読み込みます。 この際、「 geometry」や「 texture」フォルダは直接読み込まず、一階層上のフォルダを読み込みましょう。 読み込みが完了したら、「ALIGNMENT→Allign Images」を選択し、アライメントを開始します。 アライメント完了です。(所要時間:約3分) 今回は コンポーネント が分かれることなく、全ての写真が繋がりました。 コンポーネント が2つや3つに分かれてしまった場合は、適宜コン トロール ポイントを設定しましょう。 全ての写真が繋がっていることが確認出来たら、写真のグループ化を解除して再アライメントを行います。 これにより、写真の位置推定結果がより正確に行われます。 「Images」を選択し、「Ungroup」で写真のグループ解除を行い、再度「Allign Images」を実行します。 再アライメントが完了しました。(所要時間:約3秒) すべての写真が繋がった コンポーネント が増えていることを確認します。 問題なければ、次にメッシュの構築範囲指定を行います。 赤青緑のポイントがそれぞれXYZ軸に対応しておりますので、これらを動かして範囲を選択します。 メッシュ作成の処理時間を短縮するためにも、メッシュ構築範囲はなるべく狭く設定しましょう。 適切に範囲選択ができましたら、メッシュモデルを作成します。 「MESH MODEL→High Detail」を選択し、ハイクオリティのメッシュモデルを作成します。 メッシュモデルが完成しました。(所要時間:約60分) 特に問題がなければ、次はメッシュの不必要な部分を除去します。 「SCENE 3D TOOLS→Mesh Model→Lasso」を選択します。 Lassoは投げなわツールです。「Shift+左クリック」で範囲追加、「Ctrl+左クリック」で範囲除外を行うことが出来ます。 今回は花壇下の床部分(オレンジで選択されている範囲)が不要であった為、選択した後に「Filter Selection」で取り除きました。 取り除いた後のモデルがこちらです。 なお、RealityCaptureではFilter Selectionを行うごとに新しいモデルが作成される為、取り除く前のモデルも残っています。 除去後のモデル(Model 3)を見ると、およそ5800万ポリゴンであることが確認できます。 6.テクスチャ貼り付け・ローポリ化 最後に、テクスチャの貼り付けを行います。 また、モデルのポリゴン数が多すぎて扱いづらい場合は、適宜ローポリ化を行います。 まず初めに、「Mesh Model→Setting」からテクスチャ設定を変更します。 以下のように設定し、その他はデフォルト設定のままにします。 Minimal texture resolution:16384×16384 Maximal texture resolution:16384×16384 Large triangle removal threshold:100 Style:Fixed texcel size Texel Size:Optimal これにより、最高解像度のテクスチャを作成することが出来ます。 設定が完了しましたら、「MESH MODEL→Texture」よりテクスチャ作成を実行します。 テクスチャが作成されました(所要時間:約10分) モデルのテクスチャ部分を見ると、2枚のテクスチャが作成されていることが確認できます。 1枚は diffuse Map(Albedo Map) と呼ばれるもので、物体の基本色を表しています。 もう1枚は Normal Map と呼ばれるもので、物体の法線方向ベクトル(細かい凹凸)を表しています。 テクスチャマップの詳細については こちらの記事 にて紹介されておりますので、是非ご一読ください。 ポリゴン数を減らす必要がない場合は上記で完成です、以下ではローポリ化を行う場合の手順を紹介します。 今回は「5800万ポリゴン→1000万ポリゴン」へのローポリ化を行っていきます。 なお、ローポリ化を行う際に見た目上のクオリティが劣化しないよう、ハイポリ状態で作成したテクスチャのベイク(焼きこみ)を行います。 まず初めに、「SCENE 3D TOOLS→Mesh Model→Simplify Tools」を選択して以下の設定を行います。 Type:Absolute Target triangles count:10000000(ローポリ化後のポリゴン数) Color reprojection:Disable Normal reprojection:Disable 設定が完了したら、「simplify」でローポリ化を実行します。 ローポリ化後のモデルが作成されました。(Model5) 1000万ポリゴンになっていることが確認できます。 上記でテクスチャの再投影を無効にしているため、テクスチャは作成されておりません。 (画像で色が見えている部分は頂点カラーになります) 次に、ハイポリ状態で作成したテクスチャをベイクする為のUVマップを作成します。 「MESH MODEL→Unwrap」を選択し、以下のようにUVパラメータの設定を行います。 設定値はテクスチャ作成時とほぼ同様ですが、Styleだけ変化している点に注意してください。 Minimal texture resolution:16384×16384 Maximal texture resolution:16384×16384 Large triangle removal threshold:100 Style:Maximal texture count Maximal texture count:1 設定が完了したら、「Unwrap」を押してUVマップ作成を実行します。 UVマップの作成が完了したら、ハイポリ状態で作成したテクスチャをベイクします。 「SCENE 3D TOOLS→Mesh Model→Texture Reprojection」を選択し、以下のように設定を変更します。 Source model:Model4 (ハイポリ状態のテクスチャを作成したモデル) Result model:Model5 (ローポリモデル) Normal reprojection:Enable 設定が完了したら、「Reproject」を押してテクスチャのベイクを実行します。 ベイク完了です。 ハイポリ状態の見た目クオリティを保ったまま、ローポリ化することができました。 おわりに 本記事では、フォトグラメトリワークフローの後編として、撮影後のRAW現像手法、RealityCaptureを使ってのモデル作成方法について紹介しました。 写真撮影の際には注意すべき点が多く、また屋外の場合は天候に左右されてしまうのが難しいところです。 たとえ写真撮影が複数日に渡ってしまったとしても、RAW形式で撮影しておけば現像段階である程度明るさを揃えることができますので、基本はRAW撮影がおすすめです。 前後編を通して、一通りのワークフローを紹介できたかと思います。 これからフォトグラメトリを始める方や、既に始めていて悩んでいる方の一助となれば幸いです。 次回以降もフォトグラメトリやその他の3Dモデル作成手法を紹介していく予定ですので、楽しみにしていてください! 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ 参考資料 ・ 日本写真印刷コミュニケーションズ株式会社:「フォトグラメトリ」とはどういうもの? ・ Adobe Bridge 公式サイト ・ Adobe Photoshop Lightroom 公式サイト ・ lileaLab フォトグラメトリ手順書 ・ RealityCapture to Unreal Engine 5 ・ PBR(物理ベースレンダリング)マテリアル 入門編 執筆: @matsuzaki.shota 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
みなさんこんにちは! ISID 金融ソリューション事業部の松崎です。 前回 の記事では「フォトグラメトリによる3Dモデル作成ワークフロー(前編)」と題し、フォトグラメトリを行う上で必要となる機材や、写真撮影前・撮影時の注意ポイントを紹介しました。 まだご覧になっていない方は、是非前編から読んでいただけると嬉しいです! 今回は後編になります。 撮影後のRAW現像手法、RealityCaptureを用いてのモデル作成方法について紹介します。 ワークフロー一覧 フォトグラメトリによる3Dモデル作成のワークフローは、大きく分けて以下に分けられます。 機材の準備 被写体の確認、撮影環境のロケハン 被写体の撮影 撮影画像のRAW現像 メッシュモデル作成 テクスチャ貼り付け・ローポリ化 後編では、4~6についてご紹介します。 4.撮影画像のRAW現像 写真撮影後のRAW現像手順を紹介します。 RAW現像とは、撮影した生データ(RAWデータ)の明暗・ホワイトバランス・ コントラ ストなどを調整し、 JPEG 形式などの閲覧可能な形式で出力する処理を指します。 ※この手順はRAW形式で撮影したときのみ実施します。 JPEG 形式などで撮影した(カメラ側で現像が完了している)場合はスキップしてください。 なお、現像・加工時には以下のアプリを使用します。 Adobe Bridge (Win/ mac 対応 無料) Adobe Photoshop Lightroom (Win/ mac 対応 有料:1078円/月 ※2023年7月現在) 次行より手順を紹介していきます。 まず初めに、撮影した写真をPCへ移動させ、 Adobe Bridgeを使用して開きます。 Adobe Bridgeでは、現像の前準備を行います。 「ツール → ファイル名をバッチで変更」を選択し、写真のファイル名を変更します。 ファイル名を変更するのは、モデル作成時に他の写真が混ざってしまうのを防止するためです。 ファイル名が変更できたら、必要に応じて写真の メタデータ にキーワードを設定します。 キーワードを設定することで、写真の絞り込みが容易になります。 今回撮影した写真は メタデータ にロックがかかっているので、全写真を選択した後に「右クリック→すべての項目のロックを解除」を選択し、ロックを解除します。 ロックが解除できたら、キーワードを設定します。 今回は全写真に「花壇小」のキーワードを設定しました。 次に、 Adobe Photoshop Lightroom を開き、「ファイル→新規カタログ」で新規カタログを作成します。 Adobe Photoshop Lightroom では、写真の加工・現像を行います。 カタログを作成したら、「読み込み」から写真の読み込みを行います。 ファイル名を変更したファイルを格納したフォルダを選択し、「全てをチェック」で全写真を選択後、「読み込み」を押します。 読み込んだ写真は、「 メタデータ →キーワード」から絞り込みを行うことができます。 今回はキーワードを1つしか設定していない為、絞り込みは行いません。 全写真を選択した後に、「現像」を押して写真の加工調整画面に移ります。 画面右の ヒストグラム を見ながら、加工調整を行います。 加工を行う際は、可能な限り 黒つぶれ・白飛びをなくすよう に各項目を設定します。 また、広角レンズを使っている場合は適宜レンズ補正も行います。 今回は以下の設定値で加工調整を行いました。 露光量:+0.15 コントラ スト:+6 ハイライト:-50 シャドウ:+20 白レベル:-20 黒レベル:+20 レンズ補正:プロファイル補正を使用( Adobe Sony FE 16-35mm F4 ZA OSS ) 調整が完了したら、右下の「同期」を選択し、設定値を他の写真にも同期します。 全写真への同期が完了したら写真を一通り確認し、黒つぶれ・白飛びが発生していないかチェックします。 問題ない場合は、「ファイル→書き出し」を選択し、 JPEG 画像として現像を行います。 現像を行う際、画質設定は「100」としましょう。(劣化を防ぐため) 現像が完了しましたら、 Adobe Bridgeにて現像したファイルを確認します。 この際、ボケなどが発生してフォトグラメトリに適さないファイルは、キーワードを付けて取り除けるようにしておきます。 「 geometry」と「 texture」のフォルダを作成します。(このフォルダ名は固定です) 「 geometry」はRealityCaptureに取り込んだ際に、自動で点群やメッシュモデルの作成用に使われる写真です。 対して、「 texture」はテクスチャ作成用に使われます。 メッシュモデル作成時とテクスチャ作成時で別々の写真を使いたい場合は、各フォルダに使用対象の写真を入れましょう。 例:テクスチャ作成時に黒つぶれ・白飛びした写真を使いたい など 今回はモデル作成用とテクスチャ用で写真を分けないので、両方のフォルダにすべての写真を入れます。 以上で加工・現像の手順は完了です。 なお、ボケ画像などを取り除きたい場合は、適宜「 geometry」と「 texture」フォルダから取り除きましょう。 5.メッシュモデル作成 素材となる写真が揃いましたので、RealityCaptureでメッシュモデルを作成していきます。 なお、RealityCaptureの基本に関しては こちらの記事 にて紹介しておりますので、気になる方はご一読頂ければと思います。 まず初めにRealityCaptureを開き、インポート設定を行います。 「Application→Setting」で設定メニューを開き、インポート設定を以下のように設定します。 Group calibration by Exif :Yes Copy the imported components to the cache:No Ignore GPS Exif :Yes 次に、「ALIGHNMENT→Setting」からアライメント設定を開き、以下を設定します。 Image overlap:Low これにより、アライメントの際に写真同士が繋がり易くなります。 (十分な重なりを担保出来ている場合は、「Normal」や「High」でもよい) 設定が完了しましたら、フォルダを読み込みます。 「WORKFLOW→Folder」と選択し、現像した写真を格納しているフォルダを読み込みます。 この際、「 geometry」や「 texture」フォルダは直接読み込まず、一階層上のフォルダを読み込みましょう。 読み込みが完了したら、「ALIGNMENT→Allign Images」を選択し、アライメントを開始します。 アライメント完了です。(所要時間:約3分) 今回は コンポーネント が分かれることなく、全ての写真が繋がりました。 コンポーネント が2つや3つに分かれてしまった場合は、適宜コン トロール ポイントを設定しましょう。 全ての写真が繋がっていることが確認出来たら、写真のグループ化を解除して再アライメントを行います。 これにより、写真の位置推定結果がより正確に行われます。 「Images」を選択し、「Ungroup」で写真のグループ解除を行い、再度「Allign Images」を実行します。 再アライメントが完了しました。(所要時間:約3秒) すべての写真が繋がった コンポーネント が増えていることを確認します。 問題なければ、次にメッシュの構築範囲指定を行います。 赤青緑のポイントがそれぞれXYZ軸に対応しておりますので、これらを動かして範囲を選択します。 メッシュ作成の処理時間を短縮するためにも、メッシュ構築範囲はなるべく狭く設定しましょう。 適切に範囲選択ができましたら、メッシュモデルを作成します。 「MESH MODEL→High Detail」を選択し、ハイクオリティのメッシュモデルを作成します。 メッシュモデルが完成しました。(所要時間:約60分) 特に問題がなければ、次はメッシュの不必要な部分を除去します。 「SCENE 3D TOOLS→Mesh Model→Lasso」を選択します。 Lassoは投げなわツールです。「Shift+左クリック」で範囲追加、「Ctrl+左クリック」で範囲除外を行うことが出来ます。 今回は花壇下の床部分(オレンジで選択されている範囲)が不要であった為、選択した後に「Filter Selection」で取り除きました。 取り除いた後のモデルがこちらです。 なお、RealityCaptureではFilter Selectionを行うごとに新しいモデルが作成される為、取り除く前のモデルも残っています。 除去後のモデル(Model 3)を見ると、およそ5800万ポリゴンであることが確認できます。 6.テクスチャ貼り付け・ローポリ化 最後に、テクスチャの貼り付けを行います。 また、モデルのポリゴン数が多すぎて扱いづらい場合は、適宜ローポリ化を行います。 まず初めに、「Mesh Model→Setting」からテクスチャ設定を変更します。 以下のように設定し、その他はデフォルト設定のままにします。 Minimal texture resolution:16384×16384 Maximal texture resolution:16384×16384 Large triangle removal threshold:100 Style:Fixed texcel size Texel Size:Optimal これにより、最高解像度のテクスチャを作成することが出来ます。 設定が完了しましたら、「MESH MODEL→Texture」よりテクスチャ作成を実行します。 テクスチャが作成されました(所要時間:約10分) モデルのテクスチャ部分を見ると、2枚のテクスチャが作成されていることが確認できます。 1枚は diffuse Map(Albedo Map) と呼ばれるもので、物体の基本色を表しています。 もう1枚は Normal Map と呼ばれるもので、物体の法線方向ベクトル(細かい凹凸)を表しています。 テクスチャマップの詳細については こちらの記事 にて紹介されておりますので、是非ご一読ください。 ポリゴン数を減らす必要がない場合は上記で完成です、以下ではローポリ化を行う場合の手順を紹介します。 今回は「5800万ポリゴン→1000万ポリゴン」へのローポリ化を行っていきます。 なお、ローポリ化を行う際に見た目上のクオリティが劣化しないよう、ハイポリ状態で作成したテクスチャのベイク(焼きこみ)を行います。 まず初めに、「SCENE 3D TOOLS→Mesh Model→Simplify Tools」を選択して以下の設定を行います。 Type:Absolute Target triangles count:10000000(ローポリ化後のポリゴン数) Color reprojection:Disable Normal reprojection:Disable 設定が完了したら、「simplify」でローポリ化を実行します。 ローポリ化後のモデルが作成されました。(Model5) 1000万ポリゴンになっていることが確認できます。 上記でテクスチャの再投影を無効にしているため、テクスチャは作成されておりません。 (画像で色が見えている部分は頂点カラーになります) 次に、ハイポリ状態で作成したテクスチャをベイクする為のUVマップを作成します。 「MESH MODEL→Unwrap」を選択し、以下のようにUVパラメータの設定を行います。 設定値はテクスチャ作成時とほぼ同様ですが、Styleだけ変化している点に注意してください。 Minimal texture resolution:16384×16384 Maximal texture resolution:16384×16384 Large triangle removal threshold:100 Style:Maximal texture count Maximal texture count:1 設定が完了したら、「Unwrap」を押してUVマップ作成を実行します。 UVマップの作成が完了したら、ハイポリ状態で作成したテクスチャをベイクします。 「SCENE 3D TOOLS→Mesh Model→Texture Reprojection」を選択し、以下のように設定を変更します。 Source model:Model4 (ハイポリ状態のテクスチャを作成したモデル) Result model:Model5 (ローポリモデル) Normal reprojection:Enable 設定が完了したら、「Reproject」を押してテクスチャのベイクを実行します。 ベイク完了です。 ハイポリ状態の見た目クオリティを保ったまま、ローポリ化することができました。 おわりに 本記事では、フォトグラメトリワークフローの後編として、撮影後のRAW現像手法、RealityCaptureを使ってのモデル作成方法について紹介しました。 写真撮影の際には注意すべき点が多く、また屋外の場合は天候に左右されてしまうのが難しいところです。 たとえ写真撮影が複数日に渡ってしまったとしても、RAW形式で撮影しておけば現像段階である程度明るさを揃えることができますので、基本はRAW撮影がおすすめです。 前後編を通して、一通りのワークフローを紹介できたかと思います。 これからフォトグラメトリを始める方や、既に始めていて悩んでいる方の一助となれば幸いです。 次回以降もフォトグラメトリやその他の3Dモデル作成手法を紹介していく予定ですので、楽しみにしていてください! 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ 参考資料 ・ 日本写真印刷コミュニケーションズ株式会社:「フォトグラメトリ」とはどういうもの? ・ Adobe Bridge 公式サイト ・ Adobe Photoshop Lightroom 公式サイト ・ lileaLab フォトグラメトリ手順書 ・ RealityCapture to Unreal Engine 5 ・ PBR(物理ベースレンダリング)マテリアル 入門編 執筆: @matsuzaki.shota 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
みなさんこんにちは! ISID 金融ソリューション事業部の松崎です。 前回 の記事では、フォトグラメトリを用いた3Dモデル作成手法を紹介しました。まだご覧になっていない方は、是非読んでいただけると嬉しいです! 今回からは、2回に分けて フォトグラメトリを用いた3Dモデル作成のワークフロー を紹介していきます。 この記事は前編です。 はじめに まず初めに、フォトグラメトリについて概要を説明します。 フォトグラメトリとは、被写体をさまざまなアングルから撮影し、そのデジタル画像を解析・統合して写実的な3Dモデルを作成する技術です。3Dスキャナのような特殊な機器が不要で、通常の写真だけで生成できることが特徴です。 被写体の大きさや求める3Dモデルのクオリティにもよりますが、およそ数十枚~数百枚のデジタル画像を用いて3Dモデルを生成します。 LIDERなどセンサーを用いた手法と比較した場合、フォトグラメトリの強みとしては写真からハイクオリティなテクスチャを抽出できることが挙げられます。 (画像:被写体(車)に対してフォトグラメトリ用の写真を撮影するイメージ) ワークフロー一覧 フォトグラメトリによる3Dモデル作成のワークフローは、大きく分けて以下に分けられます。 機材の準備 被写体の確認、撮影環境のロケハン 被写体の撮影 撮影画像のRAW現像 メッシュモデル作成 テクスチャ貼り付け・ローポリ化 前編では、1~3についてご紹介します。 1.機材の準備 フォトグラメトリを行うにあたって、必要となる機材を紹介します。 本記事で紹介する機材一覧を以下に示します。 カメラ PC 三脚/一脚 コンパクトカメラ (+ 伸縮ロッド) ドローン 回転台 (+ 背景布/グリーンバック) 照明 露出計/カラーチェッカー 偏光フィルター 1-1.カメラ フォトグラメトリ用の写真を撮影する際、重要になるポイントを2つ紹介します。 また、参考として私たちが使用しているカメラを紹介します。 1-1-1.画質 フォトグラメトリは、写真から3Dモデルを作成するという特性上、写真の画質が3Dモデルのクオリティに直結します。 その為、よりリアルな3Dモデルを作成したい場合は高画 素数 のカメラを使うことが望ましいです。 一方、高画 素数 カメラは一つ一つの受光素子が小さくなる関係で、「ノイズや手振れが起こりやすくなり、暗所にも弱い」というデメリットが存在します。 「とりあえず高画 素数 のカメラを使う」のではなく、自分が求めるクオリティと撮影環境(明るさ確保や手振れ対策が可能か等)を照らし合わせて選定することが望ましいです。 (画像:低画素と高画素の受光素子イメージ) 1-1-2.画角 フォトグラメトリのワークフローにおいて、大きく手間がかかる部分は写真の「撮影」と「処理」になります。 被写体が小さい場合はそこまで手間もないですが、部屋や建物を対象とする場合は、数百~数千の写真を撮影・処理することになります。その為、「撮影枚数をいかに減らすか」といった部分がとても重要になります。 通常のズームレンズの 焦点距離 は35~50mmが一般的ですが、広角レンズ( 焦点距離 14~20mm程度)を用いることで、画角を1.5~2倍程広くできます。この場合、単純概算で写真枚数を0.5~0.7倍程に減らせることになりますので、可能な限り広角レンズを使うことが望ましいです。 (画像:レンズの 焦点距離 と画角の変化) 一方、広角レンズを用いると写真の端部分を中心に歪みが発生するといったデメリットが存在します。 フォトグラメトリソフトウェアによっては広角写真を補正する機能(RealityCaptureにて確認済)がありますので、各種機能を駆使しながら、できる限りの広角レンズを使用することが望ましいです。 1-1-3.カメラ紹介(参考) 私たちが使用しているカメラ・レンズを紹介します。 カメラやレンズ選定のご参考にしていただければと思います。 Sony α7R IV 約6100万画素、35mmフルサイズセンサーのデジタル一眼ミラーレ スカメ ラ 高解像度・高感度・低ノイズ性能が特徴です。 Sony Vario-Tessar T* FE 16-35mm F4 ZA OSS (SEL1635Z) 35mmフルサイズ イメージセンサー とマッチする、 焦点距離 16~35mmの広角ズームレンズ 非球面レンズ 5枚とEDガラス3枚が使用されており、画面端の高解像度化や歪曲収差が低減される点が特徴的です。 1-2.PC PCは主にフォトグラメトリソフトウェアを動かす為に使用します。 私たちのチームでは、フォトグラメトリソフトウェアとして RealiyCapture を使用しておりますが、その要求スペックは以下のとおりです。( 出典元 ) PC:64bit PC/ ワークステーション OS:64bit Microsoft Windows version 7 / 8 / 8.1 / 10 / 11 または Windows Server version 2008+ CPU:SSE4.2(Streaming SIMD Extensions 4.2)以降 RAM(メモリ):8GB以上 GPU : NVIDIA graphics card with CUDA 3.0+ capabilities で 、VRAMが1GB以上 DRIVER:CUDA Toolkit 10.2, minimal driver version 441.22. また、推奨スペックは以下になります。 RAM:16GB以上 CPU:4コア以上 CUDAコア数:1024以上 GPU : NVIDIA グラフィックカード ( NVIDIA 以外の グラフィックカード では、テクスチャ・3Dモデルを作成できません) 前回の記事にて紹介した通り、RealiyCaptureは「アライメント ⇒ メッシュ作成 ⇒ テクスチャ作成」の順で処理を行います。この内、メッシュ作成はCPU・ GPU の両方が使われていますが、アライメントとテクスチャ作成はCPUのみで行われます。その為、処理時間を短縮したい場合はCPU性能から向上させることが望ましいです。 また上記出典元では、RAMに関して「数千枚の高画質写真(30~80MP)を扱うには16GBで十分」と記載されています。 数千枚より多くの写真を用いる場合や、100MPを超えるような超高画質写真を使用する場合は32GB以上のRAMを検討しましょう。 こちらも参考までに、私たちが使用しているPCスペックを紹介します。 私たちの場合は、RealityCapture側のアップデート(1億ポリゴン対応等)に備え、推奨より高いスペックにて構成しています。 OS: Windows 11 Pro 64bit CPU: Intel Core i9 -13900KF CPUコア数:24 RAM:64GB GPU : NVIDIA GeForce RTX 4090 CUDAコア数:16384 グラフィックメモリ:24GB 1-3.その他 フォトグラメトリを行う上で必須となる機材はカメラ・PCですが、それ以外にも「あったら便利」な機材がいくつか存在します。私たちのチームで検討中の機材も含め、そんな便利機材を紹介していきます。 1-3-1.三脚/一脚 フォトグラメトリにおいて「手振れ」は3Dモデルのクオリティ劣化に直結します。最近のカメラは手振れ補正機能が充実していますが、それでも激しく揺れた場合はブレが発生してしまいます。 また、同じ高さを維持して撮影したい時や、 シャッタースピード を落として撮影したい時にも三脚/一脚が便利です。 1-3-2.コンパクトカメラ (+ 伸縮ロッド) 5m~10mの高所から撮影する場合、コンパクトカメラ等の小型・軽量カメラがあると便利です。 数枚程度の高所撮影であれば通常サイズのカメラで問題ないと思いますが、数十~数百枚の高所撮影をするときは軽いカメラが役立ちます。 1-3-3.ドローン 10m以上の高所から撮影する必要がある場合は、ドローンでの空撮が選択肢に入ります。 ただし、2023年6月現在はドローン使用に関する規制が厳しく、場所によってはドローン空撮が許可されない場合もあります。また、人が歩いている上空で行う「レベル4」の飛行には、国家資格「一等 無人 航空機操縦士」が必要になります。 その為、ドローンを用いた空撮は非常に難易度の高い要件と言えます。 (画像: ドローンとは何か ) 1-3-4.回転台 (+ 背景布/グリーンバック) 単体の物体、かつ回転台に乗る大きさの物体を被写体とする場合は、回転台を使用すると撮影の手間が省けます。 フォトグラメトリでは360°すべての角度から撮影する必要がありますが、回転台を用いることで、カメラを固定したまま一周分撮影することが出来ます。 注意点としては、被写体以外の背景部分が回転しないことです。この場合、被写体と背景部分の特徴点変化が釣り合わず、特徴点抽出が正常に働きません。 その為、グリーンバック等を用いて一色に統一し、背景部分をマスク処理等で除去できるようにする必要があります。 1-3-5.照明 屋内撮影にて単物体を被写体とする場合は、照明を用いることで光量を調節することが出来ます。 フォトグラメトリの特性上、物体に当たっている光量は直接テクスチャへ影響します。 カメラ側での露出設定や現像後処理にて調整することも可能ですが、最初から目的の光量に統一して撮影すると、細かい調整作業を省くことが出来ます。 また、設置式の定常光が置ける場合は問題ないですが、空間的制約などによって設置できない場合はストロボを使用することも検討します。 1-3-6.露出計/カラーチェッカー 露出計は、その場の光の強度を測定し、カメラの露出設定を導出する機械です。 フォトグラメトリでは対象を様々な角度から撮影する為、カメラの露出設定を一定にしている場合、角度によって露出が変化してしまいます。 適正露出を保つためには、露出計を用いて角度毎に露出設定を変更することが望ましいです。 また、ホワイトバランスの調整としてはカラーチェッカーがあると便利です。 1-3-7.偏光フィルター 偏光フィルター(PLフィルター)は、反射光を調整するフィルターです。 晴れの日の屋外で撮影する時など、物体からの反射光が強く出てしまうときに使用すると、物体本来の色彩を捉えやすくなります。 (画像: PL(偏光)フィルターの効果と使い方、使用例、選び方、特徴 ) 2.被写体の確認、撮影環境のロケハン ここからは被写体の大きさや屋内/屋外の条件ごとに、撮影前に必要となるロケハン内容を紹介します。 2-1.単体の物体 単体の物体を被写体とする際は、 その物体を全方位から撮影できるだけの空間が必要 になります。 コップや花瓶のような小物体の場合は机の上に置いて撮影できますが、大きなデスク等を被写体とする際は、周囲にそれなりの空間が要求されます。(被写体全体を写すには、ある程度離れる必要がある為) 全方位から撮影できる空間が取れない場合は、被写体を広い部屋に移動させて撮影することが望ましいです。 (画像:部屋の大きさに対して物体が大きい例) その他の点として、光量を一定に保ちたい場合は照明を用いて調整しましょう。 また被写体が回転台に乗る大きさであれば、上記で紹介した「回転台 + グリーンバック」の機材で撮影を効率化できますので、これらの設置可否を検討しましょう。 2-2.部屋内観(屋内) 部屋全体を撮影する際に重要なポイントは、 可能な限り部屋内の物体を減らすこと です。 小さな物体がいくつかある程度であればよいですが、大きな物体があると撮影時に影部分(部屋全体を撮った写真内に映らない部屋の壁・床など)が増えてしまい、細かく何か所も撮影する必要が出てしまいます。 また、詳細は3章にて説明しますが、「部屋全体を撮影した写真」と「影部分となった箇所を個別に撮影した写真」をフォトグラメトリ上で1つのモデルとして繋げるには、それぞれの写真の間となる写真も必要になります。(RealityCaptureの場合は前後の写真にて最低60%の重なりを維持する必要あり) その為、「影部分」は可能な限り作らないようにすることが最重要です。 3Dモデル化した後の利用を考えた際にも「既に物体が置かれている部屋」というのは扱いづらいですので、「部屋」と「内部にある物体」は別々にモデル化する方式が望ましいです。 (画像:部屋中央に物体がある際に発生する「影部分」) また、 GSD(地上解像度)の計算 を行うことで、作成する3Dモデルのテクスチャ解像度を事前に想定することが出来ます。 GSDは「センサー幅」「 焦点距離 」「高さ」「画像幅(pixel)」「画像高さ(pixel)」を元に地上面における解像度を導出するものですが、「高さ」部分を「部屋内の最も遠い部分までの距離」とすることで、作成テクスチャ内の最も低い解像度箇所が導出できます。 テクスチャで担保したい解像度ラインがある場合は、あらかじめGSDを計算しておくと安心です。 参考: PIX4D TOOLS - GSD calculator その他のポイントとしては、以下が挙げられます。 ガラス面や白一色の壁はフォトグラメトリで再現不可(特徴点として認識できない為)。 対象箇所は新聞紙などでマスキング を施しておく(後続フローにて手作業の修正が必要)。 撮影時の移動経路をあらかじめ想定し、目印としてマステなどを貼っておく。 移動経路は一筆書きの動きでイメージ し、写真同士が連続するように組む 撮影の「始まりの写真」と「終わりの写真」がきちんと連続するように、 撮影開始地点は特徴点の多い場所 に定める。 部屋の高さを確認し、場合によっては伸縮ロッドやコンパクトカメラの利用を検討する。 2-3.建物外観(屋外) 屋外にて建物外観を撮影する際のポイントは、 太陽光と人払い です。 屋内と異なり、屋外では太陽光の影響を取り除くことが出来ません。 時間による太陽の位置変化や、雲から出た/隠れた際の日照量変化によって露出が変わってしまいます。 その為、屋外で撮影を行う際はあらかじめ移動ルートを確立しておき、 曇りの日に短時間で 撮影を完了させることが望ましいです。 また、写真は後処理で明るくすることが出来ます。(逆に暗くするのは難しいです) 黒潰れしない範囲で全体的に暗めに撮影しておき、後処理で明るくして全体の調整を行いましょう。 2点目は人払いです。 こちらは公共の屋内(美術館など)でも関係する話ですが、屋外での撮影は基本的に公共の場所・建築物の撮影になります。 フォトグラメトリ用の写真では、途中で人が入り込んでしまうと写真の連続性が維持できなくなってしまう為、公共の場所であっても人(その他の動く物体含む)が映らないよう配慮する必要があります。 上記のような場所を長時間に渡って占有する(他の人を近づけさせない)場合、大抵は管理者の許可が必要です。 占有自体が禁止されている場合もありますので、撮影対象に関するルールは事前に確認しておきましょう。 また、高所を撮影する際にドローンを使いたくなりますが、2023年6月現在ドローン使用に関する法規制はかなり厳しいです。(場合によっては国家資格も求められます) その為、高所撮影は基本的に「小型カメラ+伸縮ロッド(1~10m程)」での実施を検討しましょう。 3.被写体の撮影 被写体の撮影時に注意するポイントを、場面ごとに紹介していきます。 3-1.共通 まず初めに、全場面に共通するポイントを紹介します。 3-1-1.前後の写真で70%以上の重なりを維持する RealityCaptureに取り込んでアライメントを行う場合、最低60%(可能なら70%)の重なりが必要です。 ただし実際に「70%」を感覚的に行うのは難しいと思いますので、私なりのやり方を紹介します。 フォトグラメトリ用の写真撮影では、カメラの向きと垂直方向への移動(動きとしては横移動)が基本になります。 その為、まずは1撮影ごとの横移動可能な距離を感覚的に把握する必要があります。 カメラの向きを変えないまま横移動する場合、移動距離がそのまま写真に反映されるので、写真の横幅1/3程度(約30%)分移動できることになります。 (画像:カメラと垂直方向への移動イメージ) 実際に移動できる距離はカメラの広角性能によりますので、試しに何回か横移動しながら写真を撮り、 「写真上で横幅1/3分移動する自分の歩幅」を確認しましょう。 3-1-2.カメラの向きを変える際は、細かく移動(撮影)しながら変える カメラの向きを変える際に、一点に留まって向きだけを変えると重なりのバランスが悪くなってしまいます。 向きを変える際は、細かめに移動しながら少しずつ向きを変えましょう。 (画像:方向転換方法による重なりの違い) 3-1-3.全体が鮮明な写真を撮影する(被写体深度を深くする) フォトグラメトリにおいて、ボケ感のある写真は望ましくありません。 ポートレート のような被写体深度の浅い写真ではなく、全体がクッキリと映った写真を撮影しましょう。 具体的な設定方法としては、 F値 (絞り)を大きくします。 設定値はカメラの性能や周囲の明るさによりますが、低くても4以上が望ましいです。(可能であれば11や14など) (画像:被写体深度によるピンボケの違い) 3-1-4.ノイズや手振れを発生させない 前項と関係する話になりますが、 F値 を上げるためにレンズを絞ると、レンズから入る光の量が減少してしまいます。 そのままだと暗い写真になってしまう場合、 ISO感度 と シャッタースピード を調整して明るくする必要があります。 しかし、 ISO感度 は上げすぎるとノイズが発生してしまい、 シャッタースピード は遅くすると手振れが発生しやすくなります。 両方をバランスよく調整して、ノイズや手振れを発生させないようにしましょう。 また、どうしてもノイズや手振れが発生してしまう場合は、「 F値 を下げる」または「三脚を使用する」ことを検討しましょう。 (画像:ノイズや手振れの発生例) 3-1-5.動く物体を撮影しない 上述した通り、フォトグラメトリでは画像の重なりがとても重要です。 人間などの動く物体が映り込んでいると写真間の重なりが正しく判定されなくなる恐れがあるので、静止した物体のみを撮影するようにしましょう。 また、撮影の途中で物体の向きや場所を変えるのも厳禁です。 撮影の開始と終了時で、撮影範囲内のすべての物体が変化しないようにしましょう。 ※グリーンバックなどを用いて背景をマスク処理する場合は例外です (画像:動物の映り込みが発生した例) 3-1-6.黒つぶれや白飛びを発生させない 黒つぶれや白飛びは現像段階にて調整することが出来ない為、撮影段階で発生させないようにしましょう。 最近のカメラは撮影時や再生時に ヒストグラム (輝度分布)が表示されますので、黒つぶれ・白飛びが見られる場合は露出補正をかけて対処します。 なお、蛍光灯のように常に白飛びしてしまう部分や、テクスチャのクオリティを求めない黒い部分などは適宜妥協しましょう。 (画像:黒つぶれ・白飛びの発生例) 3-1-7.写真間の「明暗」・「色調」に大きな差を出さない 写真間の「明暗」・「色調」が異なる場合、テクスチャの色合いが場所ごとに変わってしまいます。 現像段階にて調整することも可能ですが、可能な限り撮影時に揃えておくことが望ましいです。 露出変化が激しい場所での撮影では、適宜露出計を用いての調整も検討しましょう。 (画像:撮影中に明暗が変化した例) 3-2.屋内空間(部屋全体など) 次に、屋内空間を撮影する時に意識するポイントを紹介します。 3-2-1.カメラの向きと垂直方向へ移動しながら撮影する 3-1-1で記載した通り、フォトグラメトリではカメラの向きと垂直方向への移動(横移動)が基本です。 屋内の場合、壁面の地上解像度が求めるクオリティに収まる範囲で壁面から距離を取り、そこから横移動して撮影します。 1セットで足りない場合は、壁面からの距離を変えて横移動の撮影を繰り返しましょう。 (画像:横移動のイメージ) 3-2-2.撮影開始地点と終了地点は特徴点の多い場所を選ぶ 1セットの開始地点と終了地点を特徴点の多い場所にすると、他セットの写真と接続しやすくなります。 部屋内に特徴点の多い場所が一点しかない場合は、常にその一点に行き着くようにセットを組みましょう。 注意点として、ある程度特徴点の多い場所であっても、部屋内に同じような場所が存在する場合は候補から外しましょう。 ※この場合横移動だけでは撮影できない部分が出てきますので、適宜方向転換も行いながらの撮影になります。 (画像:特徴点の多い場所・少ない場所) 3-2-3.写真は「低い位置からの斜め上目線」と「高い位置からの斜め下目線」で撮影する カメラの位置と向きに関しては、「低い位置からの斜め上目線」と「高い位置からの斜め下目線」の2パターンで撮影します。 理由としては、この目線が最も広範囲に部屋内を撮影できるからです。 「低い位置」は床付近、「高い位置」は可能な限り天井に近い場所になります。(適宜三脚などを利用しましょう) なお、天井が高く上記の視点では天井・床に対して必要な地上解像度が得られない場合は、別の高さでの撮影も行う必要があります。 (画像:屋内撮影時のカメラ視点イメージ) 3-3.屋外 最後に、屋外での撮影時に気を付けるポイントを紹介します。 3-3-1.曇りの日に撮影する 3-1-7で記載した通り、写真間の「明暗」・「色調」は撮影段階で揃えておくことが望ましいです。 晴れの日など、短時間の日射量変化が激しい日は撮影に適しません。 また、快晴の日は晴れほど日射量変化は激しくないですが、後述するフレア・ゴーストが発生しやすくなります。 その為、曇りで日射量の少ない日が撮影に適しています。 (画像:理想の天気状況と、晴れた日の影響例) 3-3-2.逆光による光条・ゴーストを発生させない 逆光状態で撮影を行うと、光条やゴーストが発生してしまいます。 これはテクスチャに影響にしてしまうので、撮影角度や撮影時間を変えて、可能な限り発生させないようにします。 どうしても発生してしまう場合は、その写真をアライメントのみに利用し、テクスチャ作成時には取り除きましょう。 (画像:光条・ゴーストの発生例) 3-3-3無風状態のときに撮影する 3-1-5で記載した通り、フォトグラメトリでは撮影物体を静止させる必要があります。 屋外で撮影する場合、風の影響で木や葉が揺れてしまいますので、無風状態を狙って撮影しましょう。 無風状態で撮影できない場合は、別日での撮影を検討します。 (画像:撮影毎に木の枝が動いてしまった例) おわりに 本記事では、フォトグラメトリワークフローの前編として、機材選定に関する部分と撮影前・撮影時の注意ポイントについて紹介しました。 これからフォトグラメトリを始める方で、「どんな機材を揃えたらいいんだろう?」「撮影前や撮影時に何を意識すればいいんだろう?」という悩みを持つ方の一助となれば幸いです。 後編 ではRealityCaptureに取り込む前の現像処理や、RealityCaptureを用いてのモデル作成部分を紹介していきますので、こちらも御一読いただければ幸いです。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ 参考資料 ・ 日本写真印刷コミュニケーションズ株式会社:「フォトグラメトリ」とはどういうもの? ・ フジヤカメラ:カメラの画素数は多ければいいの!?画素数と画質の関係 高画素のメリット・デメリット ・ フジヤカメラ:PL(偏光)フィルターの効果と使い方、使用例、選び方、特徴 ・ 姫野ばら園:カメラコラム「レンズの焦点距離と画角の変化」 ・ EpicGames:OS and hardware requirements ・ DRONE NAVIGATOR:ドローンとは何か ・ PIX4D:TOOLS - GSD calculator ・ STUDIO DUCKBILL 広域・環境フォトグラメトリの世界 執筆: @matsuzaki.shota 、レビュー: @yamashita.yuki ( Shodo で執筆されました )
みなさんこんにちは! ISID 金融ソリューション事業部の松崎です。 前回 の記事では、フォトグラメトリを用いた3Dモデル作成手法を紹介しました。まだご覧になっていない方は、是非読んでいただけると嬉しいです! 今回からは、2回に分けて フォトグラメトリを用いた3Dモデル作成のワークフロー を紹介していきます。 この記事は前編です。 はじめに まず初めに、フォトグラメトリについて概要を説明します。 フォトグラメトリとは、被写体をさまざまなアングルから撮影し、そのデジタル画像を解析・統合して写実的な3Dモデルを作成する技術です。3Dスキャナのような特殊な機器が不要で、通常の写真だけで生成できることが特徴です。 被写体の大きさや求める3Dモデルのクオリティにもよりますが、およそ数十枚~数百枚のデジタル画像を用いて3Dモデルを生成します。 LIDERなどセンサーを用いた手法と比較した場合、フォトグラメトリの強みとしては写真からハイクオリティなテクスチャを抽出できることが挙げられます。 (画像:被写体(車)に対してフォトグラメトリ用の写真を撮影するイメージ) ワークフロー一覧 フォトグラメトリによる3Dモデル作成のワークフローは、大きく分けて以下に分けられます。 機材の準備 被写体の確認、撮影環境のロケハン 被写体の撮影 撮影画像のRAW現像 メッシュモデル作成 テクスチャ貼り付け・ローポリ化 前編では、1~3についてご紹介します。 1.機材の準備 フォトグラメトリを行うにあたって、必要となる機材を紹介します。 本記事で紹介する機材一覧を以下に示します。 カメラ PC 三脚/一脚 コンパクトカメラ (+ 伸縮ロッド) ドローン 回転台 (+ 背景布/グリーンバック) 照明 露出計/カラーチェッカー 偏光フィルター 1-1.カメラ フォトグラメトリ用の写真を撮影する際、重要になるポイントを2つ紹介します。 また、参考として私たちが使用しているカメラを紹介します。 1-1-1.画質 フォトグラメトリは、写真から3Dモデルを作成するという特性上、写真の画質が3Dモデルのクオリティに直結します。 その為、よりリアルな3Dモデルを作成したい場合は高画 素数 のカメラを使うことが望ましいです。 一方、高画 素数 カメラは一つ一つの受光素子が小さくなる関係で、「ノイズや手振れが起こりやすくなり、暗所にも弱い」というデメリットが存在します。 「とりあえず高画 素数 のカメラを使う」のではなく、自分が求めるクオリティと撮影環境(明るさ確保や手振れ対策が可能か等)を照らし合わせて選定することが望ましいです。 (画像:低画素と高画素の受光素子イメージ) 1-1-2.画角 フォトグラメトリのワークフローにおいて、大きく手間がかかる部分は写真の「撮影」と「処理」になります。 被写体が小さい場合はそこまで手間もないですが、部屋や建物を対象とする場合は、数百~数千の写真を撮影・処理することになります。その為、「撮影枚数をいかに減らすか」といった部分がとても重要になります。 通常のズームレンズの 焦点距離 は35~50mmが一般的ですが、広角レンズ( 焦点距離 14~20mm程度)を用いることで、画角を1.5~2倍程広くできます。この場合、単純概算で写真枚数を0.5~0.7倍程に減らせることになりますので、可能な限り広角レンズを使うことが望ましいです。 (画像:レンズの 焦点距離 と画角の変化) 一方、広角レンズを用いると写真の端部分を中心に歪みが発生するといったデメリットが存在します。 フォトグラメトリソフトウェアによっては広角写真を補正する機能(RealityCaptureにて確認済)がありますので、各種機能を駆使しながら、できる限りの広角レンズを使用することが望ましいです。 1-1-3.カメラ紹介(参考) 私たちが使用しているカメラ・レンズを紹介します。 カメラやレンズ選定のご参考にしていただければと思います。 Sony α7R IV 約6100万画素、35mmフルサイズセンサーのデジタル一眼ミラーレ スカメ ラ 高解像度・高感度・低ノイズ性能が特徴です。 Sony Vario-Tessar T* FE 16-35mm F4 ZA OSS (SEL1635Z) 35mmフルサイズ イメージセンサー とマッチする、 焦点距離 16~35mmの広角ズームレンズ 非球面レンズ 5枚とEDガラス3枚が使用されており、画面端の高解像度化や歪曲収差が低減される点が特徴的です。 1-2.PC PCは主にフォトグラメトリソフトウェアを動かす為に使用します。 私たちのチームでは、フォトグラメトリソフトウェアとして RealiyCapture を使用しておりますが、その要求スペックは以下のとおりです。( 出典元 ) PC:64bit PC/ ワークステーション OS:64bit Microsoft Windows version 7 / 8 / 8.1 / 10 / 11 または Windows Server version 2008+ CPU:SSE4.2(Streaming SIMD Extensions 4.2)以降 RAM(メモリ):8GB以上 GPU : NVIDIA graphics card with CUDA 3.0+ capabilities で 、VRAMが1GB以上 DRIVER:CUDA Toolkit 10.2, minimal driver version 441.22. また、推奨スペックは以下になります。 RAM:16GB以上 CPU:4コア以上 CUDAコア数:1024以上 GPU : NVIDIA グラフィックカード ( NVIDIA 以外の グラフィックカード では、テクスチャ・3Dモデルを作成できません) 前回の記事にて紹介した通り、RealiyCaptureは「アライメント ⇒ メッシュ作成 ⇒ テクスチャ作成」の順で処理を行います。この内、メッシュ作成はCPU・ GPU の両方が使われていますが、アライメントとテクスチャ作成はCPUのみで行われます。その為、処理時間を短縮したい場合はCPU性能から向上させることが望ましいです。 また上記出典元では、RAMに関して「数千枚の高画質写真(30~80MP)を扱うには16GBで十分」と記載されています。 数千枚より多くの写真を用いる場合や、100MPを超えるような超高画質写真を使用する場合は32GB以上のRAMを検討しましょう。 こちらも参考までに、私たちが使用しているPCスペックを紹介します。 私たちの場合は、RealityCapture側のアップデート(1億ポリゴン対応等)に備え、推奨より高いスペックにて構成しています。 OS: Windows 11 Pro 64bit CPU: Intel Core i9 -13900KF CPUコア数:24 RAM:64GB GPU : NVIDIA GeForce RTX 4090 CUDAコア数:16384 グラフィックメモリ:24GB 1-3.その他 フォトグラメトリを行う上で必須となる機材はカメラ・PCですが、それ以外にも「あったら便利」な機材がいくつか存在します。私たちのチームで検討中の機材も含め、そんな便利機材を紹介していきます。 1-3-1.三脚/一脚 フォトグラメトリにおいて「手振れ」は3Dモデルのクオリティ劣化に直結します。最近のカメラは手振れ補正機能が充実していますが、それでも激しく揺れた場合はブレが発生してしまいます。 また、同じ高さを維持して撮影したい時や、 シャッタースピード を落として撮影したい時にも三脚/一脚が便利です。 1-3-2.コンパクトカメラ (+ 伸縮ロッド) 5m~10mの高所から撮影する場合、コンパクトカメラ等の小型・軽量カメラがあると便利です。 数枚程度の高所撮影であれば通常サイズのカメラで問題ないと思いますが、数十~数百枚の高所撮影をするときは軽いカメラが役立ちます。 1-3-3.ドローン 10m以上の高所から撮影する必要がある場合は、ドローンでの空撮が選択肢に入ります。 ただし、2023年6月現在はドローン使用に関する規制が厳しく、場所によってはドローン空撮が許可されない場合もあります。また、人が歩いている上空で行う「レベル4」の飛行には、国家資格「一等 無人 航空機操縦士」が必要になります。 その為、ドローンを用いた空撮は非常に難易度の高い要件と言えます。 (画像: ドローンとは何か ) 1-3-4.回転台 (+ 背景布/グリーンバック) 単体の物体、かつ回転台に乗る大きさの物体を被写体とする場合は、回転台を使用すると撮影の手間が省けます。 フォトグラメトリでは360°すべての角度から撮影する必要がありますが、回転台を用いることで、カメラを固定したまま一周分撮影することが出来ます。 注意点としては、被写体以外の背景部分が回転しないことです。この場合、被写体と背景部分の特徴点変化が釣り合わず、特徴点抽出が正常に働きません。 その為、グリーンバック等を用いて一色に統一し、背景部分をマスク処理等で除去できるようにする必要があります。 1-3-5.照明 屋内撮影にて単物体を被写体とする場合は、照明を用いることで光量を調節することが出来ます。 フォトグラメトリの特性上、物体に当たっている光量は直接テクスチャへ影響します。 カメラ側での露出設定や現像後処理にて調整することも可能ですが、最初から目的の光量に統一して撮影すると、細かい調整作業を省くことが出来ます。 また、設置式の定常光が置ける場合は問題ないですが、空間的制約などによって設置できない場合はストロボを使用することも検討します。 1-3-6.露出計/カラーチェッカー 露出計は、その場の光の強度を測定し、カメラの露出設定を導出する機械です。 フォトグラメトリでは対象を様々な角度から撮影する為、カメラの露出設定を一定にしている場合、角度によって露出が変化してしまいます。 適正露出を保つためには、露出計を用いて角度毎に露出設定を変更することが望ましいです。 また、ホワイトバランスの調整としてはカラーチェッカーがあると便利です。 1-3-7.偏光フィルター 偏光フィルター(PLフィルター)は、反射光を調整するフィルターです。 晴れの日の屋外で撮影する時など、物体からの反射光が強く出てしまうときに使用すると、物体本来の色彩を捉えやすくなります。 (画像: PL(偏光)フィルターの効果と使い方、使用例、選び方、特徴 ) 2.被写体の確認、撮影環境のロケハン ここからは被写体の大きさや屋内/屋外の条件ごとに、撮影前に必要となるロケハン内容を紹介します。 2-1.単体の物体 単体の物体を被写体とする際は、 その物体を全方位から撮影できるだけの空間が必要 になります。 コップや花瓶のような小物体の場合は机の上に置いて撮影できますが、大きなデスク等を被写体とする際は、周囲にそれなりの空間が要求されます。(被写体全体を写すには、ある程度離れる必要がある為) 全方位から撮影できる空間が取れない場合は、被写体を広い部屋に移動させて撮影することが望ましいです。 (画像:部屋の大きさに対して物体が大きい例) その他の点として、光量を一定に保ちたい場合は照明を用いて調整しましょう。 また被写体が回転台に乗る大きさであれば、上記で紹介した「回転台 + グリーンバック」の機材で撮影を効率化できますので、これらの設置可否を検討しましょう。 2-2.部屋内観(屋内) 部屋全体を撮影する際に重要なポイントは、 可能な限り部屋内の物体を減らすこと です。 小さな物体がいくつかある程度であればよいですが、大きな物体があると撮影時に影部分(部屋全体を撮った写真内に映らない部屋の壁・床など)が増えてしまい、細かく何か所も撮影する必要が出てしまいます。 また、詳細は3章にて説明しますが、「部屋全体を撮影した写真」と「影部分となった箇所を個別に撮影した写真」をフォトグラメトリ上で1つのモデルとして繋げるには、それぞれの写真の間となる写真も必要になります。(RealityCaptureの場合は前後の写真にて最低60%の重なりを維持する必要あり) その為、「影部分」は可能な限り作らないようにすることが最重要です。 3Dモデル化した後の利用を考えた際にも「既に物体が置かれている部屋」というのは扱いづらいですので、「部屋」と「内部にある物体」は別々にモデル化する方式が望ましいです。 (画像:部屋中央に物体がある際に発生する「影部分」) また、 GSD(地上解像度)の計算 を行うことで、作成する3Dモデルのテクスチャ解像度を事前に想定することが出来ます。 GSDは「センサー幅」「 焦点距離 」「高さ」「画像幅(pixel)」「画像高さ(pixel)」を元に地上面における解像度を導出するものですが、「高さ」部分を「部屋内の最も遠い部分までの距離」とすることで、作成テクスチャ内の最も低い解像度箇所が導出できます。 テクスチャで担保したい解像度ラインがある場合は、あらかじめGSDを計算しておくと安心です。 参考: PIX4D TOOLS - GSD calculator その他のポイントとしては、以下が挙げられます。 ガラス面や白一色の壁はフォトグラメトリで再現不可(特徴点として認識できない為)。 対象箇所は新聞紙などでマスキング を施しておく(後続フローにて手作業の修正が必要)。 撮影時の移動経路をあらかじめ想定し、目印としてマステなどを貼っておく。 移動経路は一筆書きの動きでイメージ し、写真同士が連続するように組む 撮影の「始まりの写真」と「終わりの写真」がきちんと連続するように、 撮影開始地点は特徴点の多い場所 に定める。 部屋の高さを確認し、場合によっては伸縮ロッドやコンパクトカメラの利用を検討する。 2-3.建物外観(屋外) 屋外にて建物外観を撮影する際のポイントは、 太陽光と人払い です。 屋内と異なり、屋外では太陽光の影響を取り除くことが出来ません。 時間による太陽の位置変化や、雲から出た/隠れた際の日照量変化によって露出が変わってしまいます。 その為、屋外で撮影を行う際はあらかじめ移動ルートを確立しておき、 曇りの日に短時間で 撮影を完了させることが望ましいです。 また、写真は後処理で明るくすることが出来ます。(逆に暗くするのは難しいです) 黒潰れしない範囲で全体的に暗めに撮影しておき、後処理で明るくして全体の調整を行いましょう。 2点目は人払いです。 こちらは公共の屋内(美術館など)でも関係する話ですが、屋外での撮影は基本的に公共の場所・建築物の撮影になります。 フォトグラメトリ用の写真では、途中で人が入り込んでしまうと写真の連続性が維持できなくなってしまう為、公共の場所であっても人(その他の動く物体含む)が映らないよう配慮する必要があります。 上記のような場所を長時間に渡って占有する(他の人を近づけさせない)場合、大抵は管理者の許可が必要です。 占有自体が禁止されている場合もありますので、撮影対象に関するルールは事前に確認しておきましょう。 また、高所を撮影する際にドローンを使いたくなりますが、2023年6月現在ドローン使用に関する法規制はかなり厳しいです。(場合によっては国家資格も求められます) その為、高所撮影は基本的に「小型カメラ+伸縮ロッド(1~10m程)」での実施を検討しましょう。 3.被写体の撮影 被写体の撮影時に注意するポイントを、場面ごとに紹介していきます。 3-1.共通 まず初めに、全場面に共通するポイントを紹介します。 3-1-1.前後の写真で70%以上の重なりを維持する RealityCaptureに取り込んでアライメントを行う場合、最低60%(可能なら70%)の重なりが必要です。 ただし実際に「70%」を感覚的に行うのは難しいと思いますので、私なりのやり方を紹介します。 フォトグラメトリ用の写真撮影では、カメラの向きと垂直方向への移動(動きとしては横移動)が基本になります。 その為、まずは1撮影ごとの横移動可能な距離を感覚的に把握する必要があります。 カメラの向きを変えないまま横移動する場合、移動距離がそのまま写真に反映されるので、写真の横幅1/3程度(約30%)分移動できることになります。 (画像:カメラと垂直方向への移動イメージ) 実際に移動できる距離はカメラの広角性能によりますので、試しに何回か横移動しながら写真を撮り、 「写真上で横幅1/3分移動する自分の歩幅」を確認しましょう。 3-1-2.カメラの向きを変える際は、細かく移動(撮影)しながら変える カメラの向きを変える際に、一点に留まって向きだけを変えると重なりのバランスが悪くなってしまいます。 向きを変える際は、細かめに移動しながら少しずつ向きを変えましょう。 (画像:方向転換方法による重なりの違い) 3-1-3.全体が鮮明な写真を撮影する(被写体深度を深くする) フォトグラメトリにおいて、ボケ感のある写真は望ましくありません。 ポートレート のような被写体深度の浅い写真ではなく、全体がクッキリと映った写真を撮影しましょう。 具体的な設定方法としては、 F値 (絞り)を大きくします。 設定値はカメラの性能や周囲の明るさによりますが、低くても4以上が望ましいです。(可能であれば11や14など) (画像:被写体深度によるピンボケの違い) 3-1-4.ノイズや手振れを発生させない 前項と関係する話になりますが、 F値 を上げるためにレンズを絞ると、レンズから入る光の量が減少してしまいます。 そのままだと暗い写真になってしまう場合、 ISO感度 と シャッタースピード を調整して明るくする必要があります。 しかし、 ISO感度 は上げすぎるとノイズが発生してしまい、 シャッタースピード は遅くすると手振れが発生しやすくなります。 両方をバランスよく調整して、ノイズや手振れを発生させないようにしましょう。 また、どうしてもノイズや手振れが発生してしまう場合は、「 F値 を下げる」または「三脚を使用する」ことを検討しましょう。 (画像:ノイズや手振れの発生例) 3-1-5.動く物体を撮影しない 上述した通り、フォトグラメトリでは画像の重なりがとても重要です。 人間などの動く物体が映り込んでいると写真間の重なりが正しく判定されなくなる恐れがあるので、静止した物体のみを撮影するようにしましょう。 また、撮影の途中で物体の向きや場所を変えるのも厳禁です。 撮影の開始と終了時で、撮影範囲内のすべての物体が変化しないようにしましょう。 ※グリーンバックなどを用いて背景をマスク処理する場合は例外です (画像:動物の映り込みが発生した例) 3-1-6.黒つぶれや白飛びを発生させない 黒つぶれや白飛びは現像段階にて調整することが出来ない為、撮影段階で発生させないようにしましょう。 最近のカメラは撮影時や再生時に ヒストグラム (輝度分布)が表示されますので、黒つぶれ・白飛びが見られる場合は露出補正をかけて対処します。 なお、蛍光灯のように常に白飛びしてしまう部分や、テクスチャのクオリティを求めない黒い部分などは適宜妥協しましょう。 (画像:黒つぶれ・白飛びの発生例) 3-1-7.写真間の「明暗」・「色調」に大きな差を出さない 写真間の「明暗」・「色調」が異なる場合、テクスチャの色合いが場所ごとに変わってしまいます。 現像段階にて調整することも可能ですが、可能な限り撮影時に揃えておくことが望ましいです。 露出変化が激しい場所での撮影では、適宜露出計を用いての調整も検討しましょう。 (画像:撮影中に明暗が変化した例) 3-2.屋内空間(部屋全体など) 次に、屋内空間を撮影する時に意識するポイントを紹介します。 3-2-1.カメラの向きと垂直方向へ移動しながら撮影する 3-1-1で記載した通り、フォトグラメトリではカメラの向きと垂直方向への移動(横移動)が基本です。 屋内の場合、壁面の地上解像度が求めるクオリティに収まる範囲で壁面から距離を取り、そこから横移動して撮影します。 1セットで足りない場合は、壁面からの距離を変えて横移動の撮影を繰り返しましょう。 (画像:横移動のイメージ) 3-2-2.撮影開始地点と終了地点は特徴点の多い場所を選ぶ 1セットの開始地点と終了地点を特徴点の多い場所にすると、他セットの写真と接続しやすくなります。 部屋内に特徴点の多い場所が一点しかない場合は、常にその一点に行き着くようにセットを組みましょう。 注意点として、ある程度特徴点の多い場所であっても、部屋内に同じような場所が存在する場合は候補から外しましょう。 ※この場合横移動だけでは撮影できない部分が出てきますので、適宜方向転換も行いながらの撮影になります。 (画像:特徴点の多い場所・少ない場所) 3-2-3.写真は「低い位置からの斜め上目線」と「高い位置からの斜め下目線」で撮影する カメラの位置と向きに関しては、「低い位置からの斜め上目線」と「高い位置からの斜め下目線」の2パターンで撮影します。 理由としては、この目線が最も広範囲に部屋内を撮影できるからです。 「低い位置」は床付近、「高い位置」は可能な限り天井に近い場所になります。(適宜三脚などを利用しましょう) なお、天井が高く上記の視点では天井・床に対して必要な地上解像度が得られない場合は、別の高さでの撮影も行う必要があります。 (画像:屋内撮影時のカメラ視点イメージ) 3-3.屋外 最後に、屋外での撮影時に気を付けるポイントを紹介します。 3-3-1.曇りの日に撮影する 3-1-7で記載した通り、写真間の「明暗」・「色調」は撮影段階で揃えておくことが望ましいです。 晴れの日など、短時間の日射量変化が激しい日は撮影に適しません。 また、快晴の日は晴れほど日射量変化は激しくないですが、後述するフレア・ゴーストが発生しやすくなります。 その為、曇りで日射量の少ない日が撮影に適しています。 (画像:理想の天気状況と、晴れた日の影響例) 3-3-2.逆光による光条・ゴーストを発生させない 逆光状態で撮影を行うと、光条やゴーストが発生してしまいます。 これはテクスチャに影響にしてしまうので、撮影角度や撮影時間を変えて、可能な限り発生させないようにします。 どうしても発生してしまう場合は、その写真をアライメントのみに利用し、テクスチャ作成時には取り除きましょう。 (画像:光条・ゴーストの発生例) 3-3-3無風状態のときに撮影する 3-1-5で記載した通り、フォトグラメトリでは撮影物体を静止させる必要があります。 屋外で撮影する場合、風の影響で木や葉が揺れてしまいますので、無風状態を狙って撮影しましょう。 無風状態で撮影できない場合は、別日での撮影を検討します。 (画像:撮影毎に木の枝が動いてしまった例) おわりに 本記事では、フォトグラメトリワークフローの前編として、機材選定に関する部分と撮影前・撮影時の注意ポイントについて紹介しました。 これからフォトグラメトリを始める方で、「どんな機材を揃えたらいいんだろう?」「撮影前や撮影時に何を意識すればいいんだろう?」という悩みを持つ方の一助となれば幸いです。 後編 ではRealityCaptureに取り込む前の現像処理や、RealityCaptureを用いてのモデル作成部分を紹介していきますので、こちらも御一読いただければ幸いです。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ 参考資料 ・ 日本写真印刷コミュニケーションズ株式会社:「フォトグラメトリ」とはどういうもの? ・ フジヤカメラ:カメラの画素数は多ければいいの!?画素数と画質の関係 高画素のメリット・デメリット ・ フジヤカメラ:PL(偏光)フィルターの効果と使い方、使用例、選び方、特徴 ・ 姫野ばら園:カメラコラム「レンズの焦点距離と画角の変化」 ・ EpicGames:OS and hardware requirements ・ DRONE NAVIGATOR:ドローンとは何か ・ PIX4D:TOOLS - GSD calculator ・ STUDIO DUCKBILL 広域・環境フォトグラメトリの世界 執筆: @matsuzaki.shota 、レビュー: @yamashita.yuki ( Shodo で執筆されました )