【AWSリソース操作自動化】不要なIAMユーザーをすべて削除する

# はじめに

皆さん、業務でAWSは使われているでしょうか。
よくセキュリティ的な安全性を保つために、不要なユーザーは削除しましょうとかいいますよね。
これは実際業務でも注意しなければならないところです。
たとえば僕も新しいAWSアカウントをセットアップして、それを起点となるアカウントからアシュームロールして使用するとなると、個人ユーザーは不要だったりするのですべて削除しています。

# 問題提起

さて、ユーザーをすべて削除するとは言ったものの、実はユーザー削除は手順を踏む必要があり結構面倒です。
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_users_manage.html#id_users_deleting_cli
`ユーザーのパスワード（使用していた場合）を削除します.
ユーザーのアクセスキーを削除します (使用していた場合)。
ユーザーの署名証明書を削除します。
ユーザーの SSH パブリックキーを削除します (使用していた場合)。
ユーザーの Git 認証情報を削除します。
ユーザーの 多要素認証 (MFA) デバイスを無効にします (使用していた場合)。
ユーザーのインラインポリシーを削除します。
ユーザーにアタッチされているすべての管理ポリシーをデタッチします。
すべてのグループからユーザーを削除します。
ユーザーを削除します。`

これらを削除対象すべてのユーザーに対して行う必要があります。
今回は、最初に必要なユーザーを定義しておき、それ以外のユーザーをすべて削除するスクリプトを紹介しようと思います。
実装はgroovyで行っていますが、処理的にはshでcliを投げて、結果のjsonをパースしているだけなので応用は聞くと思います。

# スクリプト

```groovy

    parameters {/*パラメータ*/
        string(name: 'AWS_ACCESS_KEY', defaultValue: "", description: '')
        password(name: 'AWS_SECRET_ACCESS_KEY', defaultValue: "", description: '')
    }

    stages {
        stage('Check access key') {/*パラメータ確認*/
            steps {
                script {
                    /*Get access key*/
                    if ("${params.AWS_ACCESS_KEY}".isEmpty() || !params.AWS_ACCESS_KEY) {
                        error "AWS_ACCESS_KEY is empty!"
                    }
                    if ("${params.AWS_SECRET_ACCESS_KEY}".isEmpty() || !params.AWS_SECRET_ACCESS_KEY) {
                        error "AWS_SECRET_ACCESS_KEY is empty!"
                    }
                    access_key_id     = "${params.AWS_ACCESS_KEY}"
                    secret_access_key = "${params.AWS_SECRET_ACCESS_KEY}"
                }
            }
        }

        stage('delete user') {
            environment {
                AWS_ACCESS_KEY_ID      = "${access_key_id}"
                AWS_SECRET_ACCESS_KEY  = "${secret_access_key}"
                AWS_DEFAULT_REGION     = "ap-northeast-1"
                AWS_DEFAULT_OUTPUT     = "json"
            }
            steps {
                script {
                    def dont_delete_user_list = []
                    def users_list            = []
                   　//削除対象外のユーザー一覧を作成
                    def dont_delete_user_list_yml = readYaml(file: "./UserList.yml")//消さないユーザー名を定義しています
                    dont_delete_user_list         = dont_delete_user_list_yml."name_list"
                    //ユーザー一覧を作成
                    def users_json = sh (
                         script: "aws iam list-users",
                         returnStdout: true,
                     )
                     users_list = readJSON text:users_json
                     print(users_list["Users"])

                   //ユーザーの数だけ削除処理を実行する
                    for (user_info in users_list["Users"]){
                        def user = user_info["UserName"]
                        if(dont_delete_user_list.contains(user)){
                            print("${user} out of target.")
                        } else {
                            //証明書削除
                            def certificates_json = sh (
                                script: "aws iam list-signing-certificates --user-name ${user}",
                                returnStdout: true,
                            )
                            def certificates = readJSON text:certificates_json
                            if (certificates["Certificates"].size() > 0 ){
                                print("証明書の削除を行います。")
                                for (info in certificates["Certificates"]){
                                    sh ( script: "aws iam delete-signing-certificate --user-name ${user} --certificate-id ${info['CertificateId']}", returnStdout: true )
                                }
                            } else {
                                print("not found Certificates")
                            }

                            //SSH パブリックキー削除
                            def ssh_pub_json = sh (
                                script: "aws iam list-ssh-public-keys --user-name ${user}",
                                returnStdout: true,
                            )
                            def ssh_pub = readJSON text: ssh_pub_json
                            if (ssh_pub["SSHPublicKeys"].size() > 0 ){
                                print("SSH パブリックキーの削除を行います。")
                                for (info in ssh_pub["SSHPublicKeys"]){
                                    sh ( script: "aws iam delete-ssh-public-key --user-name ${user} --ssh-public-key-id ${info['SSHPublicKeyId']}", returnStdout: true )
                                }
                            } else {
                                print("not found ssh pub key")
                            }

                            //Git アクセス情報削除
                            def git_credential_json = sh (
                                script: "aws iam list-service-specific-credentials --user-name ${user} --service-name codecommit.amazonaws.com",
                                returnStdout: true,
                            )
                            def git_credential = readJSON text: git_credential_json
                            if (git_credential["ServiceSpecificCredentials"].size() > 0 ){
                                print("Gitアクセス情報の削除を行います。")
                                for (info in git_credential["ServiceSpecificCredentials"]){
                                    sh ( script: "aws iam delete-service-specific-credential --user-name ${user} --service-specific-credential-id ${info['ServiceSpecificCredentialId']}", returnStdout: true )
                                }
                            } else {
                                print("not found git credential")
                            }

                            //MFA報削除
                            def mfa_json = sh (
                                script: "aws iam list-mfa-devices --user-name ${user}",
                                returnStdout: true,
                            )
                            def mfa = readJSON text: mfa_json
                            if (mfa["MFADevices"].size() > 0 ){
                                print("MFA情報の削除を行います。")
                                for (info in mfa["MFADevices"]){
                                    sh ( script: "aws iam deactivate-mfa-device --user-name ${user} --serial-number ${info['SerialNumber']}", returnStdout: true )
                                    sh ( script: "aws iam delete-virtual-mfa-device --serial-number ${info['SerialNumber']}", returnStdout: true )
                                }
                            } else {
                                print("not found mfa")
                            }

                            //グループから削除
                            def group_json = sh (
                                script: "aws iam list-groups-for-user --user-name ${user}",
                                returnStdout: true,
                            )
                            def group = readJSON text: group_json
                            if (group["Groups"].size() > 0 ){
                                print("groupからの削除を行います。")
                                for (info in group["Groups"]){
                                    sh ( script: "aws iam remove-user-from-group --user-name ${user} --group-name ${info['GroupName']}", returnStdout: true )
                                }
                            } else {
                                print("not found group")
                            }

                            //インラインポリシー削除
                            def inline_policy_json = sh (
                                script: "aws iam list-user-policies --user-name ${user}",
                                returnStdout: true,
                            )
                            def inline_policy = readJSON text: inline_policy_json
                            if (inline_policy["PolicyNames"].size() > 0 ){
                                print("インラインポリシーの削除を行います。")
                                for (info in inline_policy["PolicyNames"]){
                                    sh ( script: "aws iam delete-user-policy --user-name ${user} --policy-name ${info}", returnStdout: true )
                                }
                            } else {
                                print("not found inline policy")
                            }

                            //マネジメントポリシー削除
                            def management_policy_json = sh (
                                script: "aws iam list-attached-user-policies --user-name ${user}",
                                returnStdout: true,
                            )
                            def management_policy = readJSON text: management_policy_json
                            if (management_policy["AttachedPolicies"].size() > 0 ){
                                print("管理ポリシーの削除を行います。")
                                for (info in management_policy["AttachedPolicies"]){
                                    sh ( script: "aws iam detach-user-policy --user-name ${user} --policy-arn ${info['PolicyArn']}", returnStdout: true )
                                }
                            } else {
                                print("not found management policy")
                            }

                            //アクセスキー削除
                            def access_key_json = sh (
                                script: "aws iam list-access-keys --user-name ${user}",
                                returnStdout: true,
                            )
                            def access_key = readJSON text:access_key_json
                            if (access_key["AccessKeyMetadata"].size() > 0 ){
                                print("アクセスキーの削除を行います。")
                                for (info in access_key["AccessKeyMetadata"]){
                                    sh ( script: "aws iam delete-access-key --access-key-id ${info['AccessKeyId']} --user-name ${user}", returnStdout: true )
                                }
                            } else {
                                print("not found AccessKey")
                            }

                            //パスワード削除
                            sh """aws iam get-login-profile --user-name ${user} && aws iam delete-login-profile --user-name ${user}
                                :"""

                            //ユーザー削除
                            sh "aws iam delete-user --user-name ${user}"
                        }
                    }
                }
            }
        }
    }


```

これでdont_delete_user_list に定義されているユーザー以外のすべてのユーザーを削除することができます。
基本的には途中で失敗してからの再実行や、そもそもある要素を持っていないユーザーなども想定して、まず要素の存在チェックをしてから削除するようにしています。
ちなみにパスワード削除のところだけ怠けた書き方をしていますが、これはこういう書き方もできるということで、特に推奨するわけではありません。
:をつけることでパスワードが無くて失敗したときも成功扱いで次の処理にいきます。

# おわりに

不要なIAMユーザーを一括で削除しようとおもったとき、誰かしら書いてるやろと思って検索したら案外ヒットしなかったので書いてみました。
誰かのお役に立てれば幸いです。

私もAWS初心者（クラウドプラクティショナー程度）であるため、ご指摘・ご質問・ご感想はどのようなものでもありがたくいただく所存です。
特に「ここも確認したほうが良い」といった内容はぜひお伺いしたく存じます。
何かございましたら、ぜひコメントを頂ければと存じます。

以上、お疲れ様でした。