Steampipe で実現する select * from cloud; の世界

## Steampipe とは
標準 SQL を使用して、対応するクラウドサービス (AWS, Azure Google Cloud, and more!) にクエリを実行できるオープンソース の CLI ツールです。マルチクラウドのガバナンスプラットフォームを提供する Turbot (Turbot HQ, Inc.) が中心となって開発しています。

https://steampipe.io/

類似するサービスに [IaSQL](https://iasql.com/) などがあります。IaSQL は SQL でインフラ構成を管理する、つまりリソースの作成や維持も目的としています。

対して Steampipe は API やサービスからデータを調査、評価し、洞察を得るという参照に特化しています。また単に CLI ツールであるため、ローカルや AWS CloudShell などの環境にインストールし、認証情報を与えるだけで利用できます。ユーザー自身で別途データベースを用意する必要もありません。

実際にはローカルに Steampipe が管理する PostgreSQL が作成されます。単にデータベースとしても使用できるので、その他のクライアントツールから接続したり、データのエクスポートなども可能です。

と言ってもデータベースにクラウドサービスのデータが保存されているわけではありません。Steampipe は [Postgres Foreign Data Wrapper](https://wiki.postgresql.org/wiki/Foreign_data_wrappers) を使用して外部サービス API からデータを取得し、テーブルとして表示します。

SaaS 版の [Steampipe Cloud](https://cloud.steampipe.io/) も提供予定とのことで現在プレビュー中です。

## Steampipe のセットアップ
### CLI のインストール
mac OS または各種 Linux ディストリビューションで利用できます。Linux の場合、シェルスクリプトを実行するだけです。

```shell-session
$ sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/turbot/steampipe/main/install.sh)"

$ steampipe -v
steampipe version 0.15.0
```

### Plugin による連携サービスの追加
Steampipe は Plugin によって様々なサービスと連携します (2022 年 6 月時点で 78)。[steampipe-plugin-sdk](https://github.com/turbot/steampipe-plugin-sdk) が公開されており、コミュニティベースでもプラグインが開発されています。

Steampipe Hub に利用可能なサービス一覧の記載があります。

https://hub.steampipe.io/plugins

デフォルトでは何もインストールされていないため、ここでは AWS プラグインをインストールしてみます。

```shell-session
$ steampipe plugin install steampipe aws

aws             [====================================================================] Done

Installed plugin: aws@latest v0.66.0
Documentation:    https://hub.steampipe.io/plugins/turbot/aws
```

### select * from cloud;
Steampipe でクエリを実効するには `steampipe query` でインタラクティブシェルを開くか、`steampipe query "select vpc_id, cidr_block, state from aws_vpc"` のような形でバッチ実行することもできます。インタラクティブシェルではオートコンプリートや構文の強調表示、history などが使えるので便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/9583f79c-4e38-bf1e-a650-6190e97f99d1.png)


## AWS プラグインの使用例
各プラグインは [steampipe-postgres-fdw](https://github.com/turbot/steampipe-postgres-fdw) を使用し、対応サービスからデータを取得し、テーブルとして表示しています。AWS プラグインの場合、すべてのサービスや API に対応しているわけではありませんが、300 以上のテーブルが定義済みです。

`select * from aws_ec2_instance;` が CLI の `aws ec2 describe-instances` に対応しているというようなイメージを持ってもらうとわかりやすいかと思います。さらにドキュメントにはそれぞれのテーブルで豊富なクエリ例が記載されています。

https://hub.steampipe.io/plugins/turbot/aws/tables


`aws_iam_user` テーブルから MFA を設定していない IAM ユーザーを確認してみます。

```shell-session
$ steampipe query
Welcome to Steampipe v0.15.0
For more information, type .help
> select name, user_id, mfa_enabled from aws.aws_iam_user where not mfa_enabled;
+---------------+-----------------------+-------------+
| name          | user_id               | mfa_enabled |
+---------------+-----------------------+-------------+
| user01        | AIDAXXXXXXXXXXXXXXXXX | false       |
| test-bot      | AIDAYYYYYYYYYYYYYYYYY | false       |
| amplify-XXXXX | AIDZZZZZZZZZZZZZZZZZZ | false       |
+---------------+-----------------------+-------------+
```

`.inspect table_name` でテーブル定義を確認できます。

```shell-session
> .inspect aws_iam_user
+---------------------------+--------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| column                    | type                     | description                                                                                                                                                                     |
+---------------------------+--------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| _ctx                      | jsonb                    | Steampipe context in JSON form, e.g. connection_name.                                                                                                                           |
| account_id                | text                     | The AWS Account ID in which the resource is located.                                                                                                                            |
| akas                      | jsonb                    | Array of globally unique identifier strings (also known as) for the resource.                                                                                                   |
| arn                       | text                     | The Amazon Resource Name (ARN) that identifies the user.                                                                                                                        |
| attached_policy_arns      | jsonb                    | A list of managed policies attached to the user.                                                                                                                                |
| create_date               | timestamp with time zone | The date and time, when the user was created.                                                                                                                                   |
| groups                    | jsonb                    | A list of groups attached to the user.                                                                                                                                          |
| inline_policies           | jsonb                    | A list of policy documents that are embedded as inline policies for the user.                                                                                                   |
| inline_policies_std       | jsonb                    | Inline policies in canonical form for the user.                                                                                                                                 |
| login_profile             | jsonb                    | Contains the user name and password create date for a user.                                                                                                                     |
| mfa_devices               | jsonb                    | A list of MFA devices attached to the user.                                                                                                                                     |
| mfa_enabled               | boolean                  | The MFA status of the user.                                                                                                                                                     |
| name                      | text                     | The friendly name identifying the user.                                                                                                                                         |
| partition                 | text                     | The AWS partition in which the resource is located (aws, aws-cn, or aws-us-gov).                                                                                                |
| password_last_used        | timestamp with time zone | The date and time, when the user's password was last used to sign in to an AWS website.                                                                                         |
| path                      | text                     | The path to the user.                                                                                                                                                           |
| permissions_boundary_arn  | text                     | The ARN of the policy used to set the permissions boundary for the user.                                                                                                        |
| permissions_boundary_type | text                     | The permissions boundary usage type that indicates what type of IAM resource is used as the permissions boundary for an entity. This data type can only have a value of Policy. |
| region                    | text                     | The AWS Region in which the resource is located.                                                                                                                                |
| tags                      | jsonb                    | A map of tags for the resource.                                                                                                                                                 |
| tags_src                  | jsonb                    | A list of tags that are attached to the user.                                                                                                                                   |
| title                     | text                     | Title of the resource.                                                                                                                                                          |
| user_id                   | text                     | The stable and unique string identifying the user.                                                                                                                              |
+---------------------------+--------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
```

`jsonb` というデータ型に着目してください。AWS に限らず、多くの API では JSON で構造化データが返ります。PostgreSQL は JSON をネイティブにサポートしているので SQL で操作できます。

以下の例では IAM ユーザーが所属するグループの詳細を表示しています。

```sql:query
select
  name as user_name,
  iam_group ->> 'GroupName' as group_name,
  iam_group ->> 'GroupId' as group_id,
  iam_group ->> 'CreateDate' as create_date
from
  aws_iam_user
  cross join jsonb_array_elements(groups) as iam_group;
```
```shell-session:result
+---------------+---------------+-----------------------+----------------------+
| user_name     | group_name    | group_id              | create_date          |
+---------------+---------------+-----------------------+----------------------+
| admin-user    | Administrator | AGPABBBBBBBBBBBBBBBBB | 2021-10-01T06:52:13Z |
| amplify-XXXXX | Administrator | AGPABBBBBBBBBBBBBBBBB | 2021-03-12T01:30:56Z |
| test-bot      | Bot           | AGPACCCCCCCCCCCCCCCCC | 2021-10-02T00:37:38Z |
| user01        | DefaultUser   | AGPADDDDDDDDDDDDDDDDD | 2021-03-12T01:30:56Z |
| user02        | DefaultUser   | AGPADDDDDDDDDDDDDDDDD | 2021-10-03T04:49:24Z |
| user02        | Operator      | AGPAEEEEEEEEEEEEEEEEE | 2021-10-02T00:42:02Z |
| user03        | Audit         | AGPAFFFFFFFFFFFFFFFFF | 2021-03-12T01:30:56Z |
| user03        | DefaultUser   | AGPADDDDDDDDDDDDDDDDD | 2021-03-18T06:14:03Z |
+---------------+---------------+-----------------------+----------------------+
```

JSON クエリの詳細はドキュメントを参照してください。

https://steampipe.io/docs/sql/querying-json


### テーブルの結合
もちろん可能です。これが Steampipe の大きな魅力の 1 つでもあります。

https://steampipe.io/docs/sql/steampipe-sql

以下の例では、`aws_ec2_instance` テーブルと `aws_vpc_subnet` テーブルから EC2 インスタンスが所属するサブネットとサブネットのアベイラビリティゾーンをクエリしています。

```sql:query
select
  instance.instance_id,
  instance.subnet_id,
  subnet.availability_zone
from
  aws_ec2_instance as instance
  join aws_vpc_subnet as subnet on instance.subnet_id = subnet.subnet_id;
```

```shell-session:result
+---------------------+--------------------------+-------------------+
| instance_id         | subnet_id                | availability_zone |
+---------------------+--------------------------+-------------------+
| i-01111111111111111 | subnet-xxxxxxxxxxxxxxxxx | ap-northeast-1a   |
| i-02222222222222222 | subnet-xxxxxxxxxxxxxxxxx | ap-northeast-1a   |
| i-03333333333333333 | subnet-xxxxxxxxxxxxxxxxx | ap-northeast-1a   |
| i-04444444444444444 | subnet-yyyyyyyyyyyyyyyyy | ap-northeast-1c   |
| i-05555555555555555 | subnet-yyyyyyyyyyyyyyyyy | ap-northeast-1c   |
| i-06666666666666666 | subnet-zzzzzzzzzzzzzzzzz | ap-northeast-1d   |
+---------------------+--------------------------+-------------------+
```

以下の例では外部結合を使用し、存在しない EBS ボリュームのスナップショット ID を取得しています。不要スナップショットの整理が捗りそうです。

```sql:query
select
  s.snapshot_id,
  s.volume_id
from
  aws_ebs_volume as v
  right join aws_ebs_snapshot as s on v.volume_id = s.volume_id
where
  v.volume_id is null;
```

```shell-session:result
+------------------------+-----------------------+
| snapshot_id            | volume_id             |
+------------------------+-----------------------+
| snap-01111111111111111 | vol-0abcdefghijklmnop |
| snap-02222222222222222 | vol-0abcdefghijklmnop |
| snap-03333333333333333 | vol-0qrstuvwxyzabcdef |
| snap-04444444444444444 | vol-0xxxxxxxxxxxxxxxx |
| snap-05555555555555555 | vol-0xxxxxxxxxxxxxxxx |
| snap-06666666666666666 | vol-0yyyyyyyyyyyyyyyy |
| snap-07777777777777777 | vol-0zzzzzzzzzzzzzzzz |
+------------------------+-----------------------+
```
 
### クラウドサービス間のデータを結合する
テーブルの結合は特定のクラウド (プラグイン) 内のみという制限はありません。つまり複数のクラウドサービス間でデータを結合して新たな洞察を得ることができます。Steampipe の Web サイトの例では Slack と AWS 両方にアカウントが存在するユーザーをクエリする例が紹介されています。

```sql:query
select
  aws.name aws_user_name,
  slack.id as slack_user_id,
  slack.display_name as slack_name
from
  aws_iam_user as aws,
  slack_user as slack
where
  aws.name = slack.email;
```

```shell-session:reulst
+--------------------------+---------------+------------+
| aws_user_name            | slack_user_id | slack_name |
+--------------------------+---------------+------------+
| dwight@dundermifflin.com | U2EMB8HLP     | dwight     |
| jim@dundermifflin.com    | U02HE4Z7E     | jim        |
+--------------------------+---------------+------------+
```

### 認証情報のカスタマイズ
AWS プラグインはデフォルトでは AWS CLI と同じ認証情報 (環境変数または Default Profile) を使用し、Default Region に接続します。しかしマルチアカウントで接続したい、異なるプロファイルを使用したい、複数リージョンに接続したいといった要件を満たすことができません。

プラグインインストール時に作成される `~/.steampipe/config/aws.spc` を編集することで認証情報や接続するリージョンをカスタマイズすることができます。

Steampipe 関連の設定ファイルは HCL (Hashicorp Configuration Language) で記述します。

例えばマルチリージョン/アカウントで接続したい場合、以下のように定義します。

```hcl:aws.spc
connection "aws_01" {
  plugin  = "aws"
  profile = "aws_01"
  regions = ["us-east-1", "us-west-2"]
}

connection "aws_02" {
  plugin  = "aws"
  profile = "aws_02"
  regions = ["*"]
}

connection "aws_all" {
  type        = "aggregator"
  plugin      = "aws"
  connections = ["aws_*"]
}
```

* `profile` で任意の名前付きプロファイルを指定します
  * AWS SSO も対応していますが、認証情報の更新 (`aws sso login`) は手動で行う必要があります
* `regions` で任意のリージョンを指定できます
* `aggregator` を作成すると、マルチアカウントで接続できます

Connection ごとに Steampipe 内部の Postgresデータベースに個別の名前空間が作成されるため、このためスキーマの指定を変えることで各アカウントに接続することができます。

```sql:query
select
  title as instance_name,
  instance_type,
  region,
  account_id
from
  aws_01.aws_ec2_instance;
```
``` shell-session:result
+-------------------------+---------------+----------------+--------------+
| instance_name           | instance_type | region         | account_id   |
+-------------------------+---------------+----------------+--------------+
| heuristic_sammet        | t3.micro      | us-east-1      | 111111111111 |
| condescending_antonelli | t3.medium     | us-east-1      | 111111111111 |
| xenodochial_shtern      | t3.medium     | us-west-2      | 111111111111 |
+-------------------------+---------------+----------------+--------------+
```

aggregator として作成した Connection を指定すると、合致するすべてのアカウントおよびリージョンの情報を参照します。
```sql:query
select
  title as instance_name,
  instance_type,
  region,
  account_id
from
  aws_all.aws_ec2_instance;
```

```shell-session:result
+-------------------------+---------------+----------------+--------------+
| instance_name           | instance_type | region         | account_id   |
+-------------------------+---------------+----------------+--------------+
| heuristic_sammet        | t3.micro      | us-east-1      | 111111111111 |
| condescending_antonelli | t3.medium     | us-east-1      | 111111111111 |
| xenodochial_shtern      | t3.medium     | us-west-2      | 111111111111 |
| clever_wilson           | t2.small      | ap-northeast-1 | 222222222222 |
| sharp_jackson           | t2.small      | ap-northeast-1 | 222222222222 |
| zealous_ritchie         | t3.medium     | ap-northeast-3 | 222222222222 |
| interesting_golick      | t2.micro      | sa-east-1      | 222222222222 |
+-------------------------+---------------+----------------+--------------+
```

## Steampipe Mods による Dasahboard as Code
### Mod とは
Steampipe Mod は事前定義されたクエリ (Named queris) によるコンプライアンスチェックやベンチマークの実行 (Controls)、それらの実行結果の Dashboard をまとめて提供する機能です。実体は HCL による定義ファイルおよび SQL のコレクションです。

2022年6月時点で 33 の Mod が公開されています。

https://hub.steampipe.io/mods

例えば AWS に関連する Mod では以下の様なものが提供されています

* AWS Compliance: セキュリティ周りのコンプライアンスベンチマークを実行し、結果を可視化します
* AWS Insights: 各種 AWS リソースの使用状況をレポートします
* AWS Thrifty: 未使用のリソースチェックなどコスト最適化できる要素をチェックします

### AWS Compliance Mod の実行例
各 Mod は GitHub で公開されており、Steampipe の実行環境に Clone して利用します。

```shell-session
$ git clone https://github.com/turbot/steampipe-mod-aws-compliance.git
$ cd steampipe-mod-aws-compliance
```

AWS Compliance Mod は CIS Benchmark や FedRAMP、GDPR、HIPPA など各種コンプライアンスに対応したベンチマークを実行できます。

https://hub.steampipe.io/mods/turbot/aws_compliance/controls

コンプライアンスチェックを実行する前に、IAM 認証情報レポートを取得しておきます。

```shell-session
$ aws iam generate-credential-report
{
    "State": "STARTED",
    "Description": "No report exists. Starting a new report generation task"
}
```

今回は CIS AWS Foundations Benchmark 1.4.0 への準拠状況を確認してみます。

```shell-session
$ steampipe check aws_compliance.benchmark.cis_v140 --export=cis_v140.html
```

コンソールに各項目のチェック結果と最後にサマリーが出力されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/e84ca13c-490c-d49e-efc0-60d470d742e4.png)

:::note info
`--export=` で html, json, csv 形式で結果を出力できます
`steampipe check all` で Mod で提供されているすべてのチェックを実行することもできます
:::

html でレポートを確認できるのは非常に便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/19045a84-64fd-5561-e3b5-5e224b22aca4.png)

`steampipe dashboard` でダッシュボードサーバーを起動すると、実行結果が可視化されたダッシュボードに接続することもできます。

```shell-session
$ steampipe dashboard
[ Wait    ] Loading Workspace
[ Wait    ] Starting Dashboard Server
[ Message ] Workspace loaded
[ Message ] Initialization complete
[ Ready   ] Dashboard server started on 9194 and listening on local
[ Message ] Visit http://localhost:9194
[ Message ] Press Ctrl+C to exit
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/5541aa1f-c2aa-fcd6-0873-624fc3188aad.png)


### NW 経由で Dashboard に接続する
Steampipe の Dashboard や 組み込みの PostgreSQL データベースはデフォルトではローカルホストでリッスンされます。`steampipe service start --dashboard` を実行し、Steampipe をサービスモードで実行すると NW 経由の接続を許可できます。

```shell-session
$ steampipe service start --dashboard
Steampipe service is running:

Database:

  Host(s):            localhost, 127.0.0.1, 172.31.47.239
  Port:               9193
  Database:           steampipe
  User:               steampipe
  Password:           ********* [use --show-password to reveal]
  Connection string:  postgres://steampipe@localhost:9193/steampipe

Dashboard:

  Host(s):  localhost, 127.0.0.1, 172.31.47.239
  Port:     9194
  URL:      http://localhost:9194/

Managing the Steampipe service:

  # Get status of the service
  steampipe service status

  # View database password for connecting from another machine
  steampipe service status --show-password

  # Restart the service
  steampipe service restart

  # Stop the service
  steampipe service stop
```

https://steampipe.io/docs/managing/service

## まとめ
* Steampipe は 標準 SQL を使用し、対応するクラウドサービスの API から情報を取得します
* 利用者はクラウドサービスの API 仕様を意識することなく、SQL でデータを参照、結合できます
* Steampipe Mod を使用すればユーザー自身で SQL を書かずともリソースの使用状況やセキュリティのチェックを実行し、可視化できます
* サービスモードで起動すると、Dashborad や 組み込みの PostgreSQL を外部公開し、別のツールから利用することもできます

## 参考
https://aws.amazon.com/jp/blogs/opensource/querying-aws-at-scale-across-apis-regions-and-accounts/

https://aws.amazon.com/jp/blogs/opensource/compliance-auditing-with-steampipe-and-sql/

https://qiita.com/yoshii0110/items/72a6c7b527e83b4edfe9

以上です。
参考になれば幸いです