使われていないAWSセキュリティグループを洗い出す

# 野良セキュリティグループはありませんか

組織で共用しているAWSの開発環境・テスト環境では、使われなくなったまま放置されているリソースが出てくるもので、管理者のみなさまは定期的に棚卸しをされていることと思います。

しかし、セキュリティグループが棚卸しされていない環境は少なくないのではないか、と思っています。

というのも、セキュリティグループ側からは紐付いているインスタンス・ネットワークインタフェースを辿れないので、なかなか手間がかかります。しかもインスタンスを作る時にできた `launch-wizard-xxx` が山ほどあって、一覧を見るだけで心が折れそうになります。
最後には、どうせセキュリティグループはあっても課金されないから、という理由で見棚卸しの対象外にされる、と。

でも整理できるものなら整理したいですよね。
なので、ここはひとつツールを使って整理しちゃいましょう。

## まずリストアップする

### 1. すべてのセキュリティグループ

AWS CLI を使ってすべてのセキュリティグループを `all.txt` に保存します。

```console
aws ec2 describe-security-groups --query 'SecurityGroups[].[GroupId, GroupName]' --output text > all.txt
```

### 2. 使われているセキュリティグループ

すべてのネットワークインターフェイス(ENI)で使っているセキュリティグループを `using.txt` に保存します。

```console
aws ec2 describe-network-interfaces --query 'NetworkInterfaces[].[Groups]' --output text > using.txt
```

## 洗い出し

あとはフィルタをちょこっとかけます。

```console
sort all.txt using.txt | uniq -u
```

`default` を使わない人(環境)も多いと思うので、それを削ってしまうのもいいかもしれません。

```console
sort all.txt using.txt | grep -v default | uniq -u
```

以上です。簡単ですね。

1と2の出力が同じ形式なのがみそです。この結果を同時に出力すると、使われているセキュリティグループは重複します。ここで `uniq -u` して重複していない行だけ表示すれば、使われていないものだけ洗い出せるというわけです。

## (おまけ)

弊社の環境で試してみました。

```console
$ cat all.txt | wc -l
176
$ sort all.txt using.txt | grep -v default | uniq -u | wc -l
117
$ perl -le 'print 117/174'
0.672413793103448
```

`default` を除くと、使われていないセキュリティグループが67%……( ^o^)ﾉ

（ちなみにVPCが37個ありました。これも棚卸しがいりそうです）

## では、消しましょうか

あとは消すだけですが、手で消すのは面倒ですし、といってスクリプトで一括削除するには勇気がいります。ここでは、セキュリティグループを削除するためのシェルスクリプトを作ることにしました。

まず、使われていないセキュリティグループの一覧を保存します。
この段階で、本当に消しても大丈夫なものかを目視で確認するといいと思います。

```console
sort all.txt using.txt | grep -v default | uniq -u > targets.txt
```

シェルスクリプトは Perl のワンライナーで作ります。

念には念を入れて、まず dry-run[^dry-run] 用のものを作ります。

[^dry-run]: dry run とは「リハーサル」「予行演習」といった意味で、実際の変更は行われないのですが、それまでの部分（構文のチェックや対象の存在確認）をきっちり実行することです。だいたいの場合、dry run が成功したときは本番も成功するように作られています。

```console
dryrun="--dry-run" perl -lane 'print "# $F[1]\naws $ENV{dryrun} ec2 delete-security-group --group-id $F[0]\n"' targets.txt > delete_sgs.sh
```

中身はというと、

```console
$ head delete_sgs.sh
# adtest-site02
aws --dry-run ec2 delete-security-group --group-id sg-0011eb5bcXXXXXXXX

# launch-wizard-35
aws --dry-run ec2 delete-security-group --group-id sg-004c8e17bXXXXXXXX

# AD-DCsg-hoge
aws --dry-run ec2 delete-security-group --group-id sg-0078856e3XXXXXXXX

# test-hoge-auth-sg
```

よさそうですね。
dry-run なので遠慮なく `bash -x delete_sgs.sh` で実行してみましょう。


```console
$ bash -x delete_sgs.sh
+ aws --dry-run ec2 delete-security-group --group-id sg-0011eb5bcXXXXXXXX

An error occurred (DryRunOperation) when calling the DeleteSecurityGroup operation: Request would have succeeded, but DryRun flag is set.
+ aws --dry-run ec2 delete-security-group --group-id sg-004c8e17bXXXXXXXX

An error occurred (DryRunOperation) when calling the DeleteSecurityGroup operation: Request would have succeeded, but DryRun flag is set.
+ aws --dry-run ec2 delete-security-group --group-id sg-0078856e3XXXXXXXX

An error occurred (DryRunOperation) when calling the DeleteSecurityGroup operation: Request would have succeeded, but DryRun flag is set.
:
:
```

これで問題なければ、`dry-run` ではないものを作りましょう。
さきほどの先頭の `dryrun="--dry-run"` を取り除いて実行します。

```console
perl -lane 'print "# $F[1]\naws $ENV{dryrun} ec2 delete-security-group --group-id $F[0]\n"' targets.txt > delete_sgs.sh
```

中を確認します。

```console
$ head delete_sgs.sh
# adtest-site02
aws  ec2 delete-security-group --group-id sg-0011eb5bcXXXXXXXX

# launch-wizard-35
aws  ec2 delete-security-group --group-id sg-004c8e17bXXXXXXXX

# AD-DCsg-hoge
aws  ec2 delete-security-group --group-id sg-0078856e3XXXXXXXX

# test-hoge-auth-sg
```

よさそうですか？　よさそうですね？

では、あとは実行するのみです。
祈りながら `bash -x delete_sgs.sh` と叩きましょう！

God bless you! :smile: