こんにちは、t.yoshidaです。
クラウド環境において、不要なアカウントは削除しないと第三者にアクセスされる可能性が高まります。
そのため、セキュリティを高めるためにも定期的なアカウントの棚卸しは効果的な手法です。
Oracle CloudではユーザーアカウントごとにAPIキーが生成できますが、このAPI キーは自動で削除されることはありません。
なので、使われず放置されているAPIキーは潜在的なセキュリティリスクとなる可能性があります。
しかし、Oracle CloudコンソールにはAPIキーの一覧を表示する機能がなく、ユーザーアカウントをひとつひとつ開いて確認する必要がありちょっと面倒ですよね。
そこで、本記事ではアカウント棚卸の負荷を低減するため、OCI CLI を活用してドメイン内のユーザーアカウントと API キーの一覧を収集するスクリプトを作成し、一覧出力してみたいと思います。
※本記事は2025年9月時点での情報を基に記載しています。
目次
目的
本記事では、以下を目的としています。
- ドメインごとのアカウント一覧を取得
- アカウントごとに紐付いた API キー一覧の取得
- 取得コマンドをスクリプトにして自動化可能にする
OCI CLI のインストール
コマンドを実行するために必要なOCI CLIをインストールします。
参考ドキュメント:OCI CLIクイックスタート
- Windows 実行コマンド
Powershellにて以下をコマンドレットを実行します。# PowerShellのリモート実行ポリシーを構成 Set-ExecutionPolicy RemoteSigned # インストーラ・スクリプトのダウンロード Invoke-WebRequest https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.ps1 -OutFile install.ps1 # スクリプトの実行 ./install.ps1 -AcceptAllDefaults
※プロンプトを表示してスクリプトを実行する場合は以下コマンドで実行してください
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.ps1'))
- Mac/Linux 実行コマンド
#dnfコマンドによるインストール sudo dnf -y install oraclelinux-developer-release-el9 sudo dnf install python39-oci-cli
導入後、oci cliが実行可能か確認します。
実行コマンドと実行結果
$ oci --version
3.48.2
バージョン情報が返ってきたら実行可能な状態になっています。
テナンシー OCIDの確認
OCI CLIの実行に必要なテナンシーのOCIDを確認します。
Oracle Cloudコンソールメニューより、[ガバナンスと管理] > [アカウント管理] > [テナンシ詳細]画面で確認できます。
API キーを配置
OCI CLIのconfigで指定するAPIキーを配置します。
[アイデンティティとセキュリティ] > [ドメイン] > [Defaultドメイン] > [ユーザー]画面にて、管理者権限を持つアカウントを選択してください。
左ペインより[APIキー]を選択し、画面右側の[APIキーの追加]ボタンを押下します。
新規でAPIキーを発行したい場合、[APIキー・ペアの生成]を選択し、秘密キー、公開キーをダウンロードしてください。
任意のAPIキーを使用したい場合は[公開キーの選択]または[公開キーの貼り付け]を選択し。使いたいAPIキーを指定します。
設定できたら、OCI CLIを使いたいサーバ/PCにAPIキーの秘密鍵を配置してください。
構成ファイルの作成
OCI CLIコマンドを使い、対話形式でconfigのセットアップを行います。
※対話形式の必要がない場合、事前に作成したconfigファイルを/home/opc/.oci配下に配置するだけで大丈夫です
実行コマンド
oci setup config
入力すると対話形式でconfigを設定していきます。
以下のように適宜入力してほしい内容が表示されるので、それぞれ入力してEnterを押下していきます。
# config格納パスを入力(デフォルトで問題なければ何も入力せずにEnterを押下)
Enter a location for your config [/home/opc/.oci/config]:
# 使用するアカウントのOCIDを入力
Enter a user OCID:
# テナンシOCIDを入力
Enter a tenancy OCID:
# リージョンを番号またはリージョン名で入力
# 例:ap-tokyo-1の場合は[18]か[ap-tokyo-1]
Enter a region by index or name(e.g.
1: af-johannesburg-1, 2: ap-chiyoda-1, 3: ap-chuncheon-1, 4: ap-chuncheon-2, 5: ap-dcc-canberra-1,
6: ap-dcc-gazipur-1, 7: ap-hyderabad-1, 8: ap-ibaraki-1, 9: ap-melbourne-1, 10: ap-mumbai-1,
11: ap-osaka-1, 12: ap-seoul-1, 13: ap-seoul-2, 14: ap-singapore-1, 15: ap-singapore-2,
16: ap-suwon-1, 17: ap-sydney-1, 18: ap-tokyo-1, 19: ca-montreal-1, 20: ca-toronto-1,
(中略)
66: us-gov-phoenix-1, 67: us-langley-1, 68: us-luke-1, 69: us-phoenix-1, 70: us-saltlake-2,
71: us-sanjose-1, 72: us-somerset-1, 73: us-thames-1):
# APIキーを生成する場合はYを入力(今回は既存のAPIキーを利用するためnを入力)
Do you want to generate a new API Signing RSA key pair? (If you decline you will be asked to supply the path to an existing key.) [Y/n]:
# APIキーの格納パスを入力 (デフォルトで問題なければ何も入力せずにEnterを押下)
Enter a directory for your keys to be created [/home/opc/.oci]:
# APIキーのファイル名を入力(デフォルトで問題なければ何も入力せずにEnterを押下)
Enter a name for your key [oci_api_key]:
Public key written to: /home/opc/.oci/oci_api_key_public.pem
# API秘密鍵のパスワードを入力(パスワードを設定しない場合は何も入力せずにEnterを押下)
Enter a passphrase for your private key (empty for no passphrase):
Private key written to: /home/opc/.oci/oci_api_key.pem
Fingerprint: # <ここにフィンガープリントが出力される>
Config written to /home/opc/.oci/config
ここまでの作業で、OCI CLIを実行するための準備が整いました。
OCI CLI コマンドを実行する
iamコマンドとidentity-domainsコマンドの違い
OCI CLIコマンドリファレンスを確認すると、2つのコマンドがあることがわかります。
- oci iam
対象ドメイン:Defaultのみ 使用API:IAM API エンドポイント指定:不要
- oci identity-domains
対象ドメイン:全てのドメイン 使用 API:Identity Domains REST API エンドポイント指定:ドメイン URL の指定が必要
今回は全ドメインのアカウント・APIキーを取得したいため、「oci identity-domains」を使用します。
ドメイン一覧の取得
まず、下記コマンドを実行し、テナンシー内のすべてのドメインを取得します。
出力されたdata.urlを後続のコマンドで使用します。
実行コマンド
oci iam domain list -c <tenancy_ocid>
出力例
{
"data": [
{
"compartment-id": "ocid1.tenancy.oc1..<unique_ID>",
"description": "Default Identity domain",
"display-name": "Default",
"home-region-url": "https://identity.us-ashburn-1.oci.oraclecloud.com",
"id": "ocid1.domain.oc1..<unique_ID>",
"lifecycle-state": "ACTIVE",
"url": "https://idcs-abcd1234.identity.oraclecloud.com:443"
}
]
}
ドメイン内のユーザー一覧取得
コマンドを実行し、ユーザーアカウントのidの内容を取得します。
# domain_URLは先程取得したdata.urlの内容を入力
oci identity-domains users list --endpoint <domain_URL>
出力例:
{
"Resources": [
{
"id": "12345abcde",
"userName": "user.name@example.com",
"displayName": "User Name",
"active": true,
"meta": {
"created": "2022-01-01T00:00:00.000Z",
"lastModified": "2022-01-01T00:00:00.000Z"
}
},
(中略)
"created": "2025-04-01T00:00:00.000Z",
"lastModified": "2025-06-01T00:00:00.000Z"
}
}
]
}
各ユーザーデータにあるResources.id を次のステップで使用します。
ユーザーに紐づく API キーの取得
各ユーザーについて、紐づく API キーを取得します:
※ここで取得できるAPIのmodifiedはあくまでメタデータの更新日付です。
(APIキーの使用履歴は監査ログを取得しないとわかりません)
なので、ここはあくまでAPIキーがあるかどうかの判断材料としてだけ考えてください!
# user_idは先程取得したResources.idの内容を入力
oci identity-domains api-keys list --endpoint <domain_URL> --filter 'user.value eq "<user_id>"'
出力例:
{
"Resources": [
{
"id": "apikey12345",
"fingerprint": "ab:cd:ef:12:34:56:...",
"user": {
"value": "12345abcde"
},
"meta": {
"created": "2022-01-01T00:00:00.000Z",
"lastModified": "2022-01-01T00:00:00.000Z"
}
}
]
}
情報取得の自動化
ここまででユーザー単位でAPIキーの出力まで確認できました。
このまま全アカウント情報を取得したいところですが、OCI CLIでは全アカウント情報を一度に取得できません。
そのため、ドメインごとに全アカウント情報を取得するPythonスクリプトを作成して上記のコマンドを再帰的に実行します。
今回作成したスクリプトは以下です。
プロファイルはデフォルトで、取得したいテナンシーOCIDを入力して実行する想定で作成しています。
#!/usr/bin/env python3
import subprocess
import json
import csv
import datetime
import sys
import os
# ======== ここにテナンシーOCIDを設定 ========
TENANCY_OCID = "<Tenancy_OCID>"
# ==========================================
def run_command(command):
"""コマンドを実行して結果をJSONとして返す"""
try:
result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True)
return json.loads(result.stdout)
except subprocess.CalledProcessError as e:
print(f"Error executing command: {command}")
print(f"Error message: {e.stderr}")
sys.exit(1)
except json.JSONDecodeError:
print(f"Error parsing JSON from command: {command}")
sys.exit(1)
def get_domains(tenancy_ocid):
"""テナンシー内のドメイン一覧を取得"""
command = f'oci iam domain list -c "{tenancy_ocid}"'
result = run_command(command)
return result['data']
def get_users(domain_url):
"""ドメイン内のユーザー一覧を取得"""
command = f'oci identity-domains users list --endpoint "{domain_url}"'
result = run_command(command)
return result['data']['resources']
def get_api_keys(domain_url, user_id):
"""ユーザーに紐づくAPIキー一覧を取得"""
command = f'oci identity-domains api-keys list --endpoint "{domain_url}" --filter \'user.value eq "{user_id}"\''
result = run_command(command)
return result['data']['resources']
def get_email(emails):
"""プライマリメールアドレスを取得"""
for email in emails:
if email.get('primary', False):
return email.get('value', '')
return ''
def format_datetime(dt_str):
"""日時フォーマットをJST(日本標準時)に変換して整形(dateutil使用せず)"""
if not dt_str:
return ''
try:
# ISO形式のUTC時間からdatetime型に変換
dt = datetime.datetime.fromisoformat(dt_str.replace('Z', '+00:00'))
# UTCからJSTに変換(+9時間)
jst_dt = dt + datetime.timedelta(hours=9)
return jst_dt.strftime('%Y-%m-%d %H:%M:%S JST')
except (ValueError, TypeError):
return dt_str
def main():
# 現在時刻を取得して出力ファイル名を生成
current_time = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
output_file = f"oci_user_report_{current_time}.csv"
# CSVファイルの準備(BOM付きUTF-8で保存)
with open(output_file, 'w', newline='', encoding='utf-8-sig') as csvfile:
fieldnames = [
'Domain Name', 'User Display Name', 'User Email', 'User ID', 'User OCID',
'User Created', 'User Last Modified',
'API Key Fingerprint', 'API Key OCID', 'API Key Created', 'API Key Last Modified'
]
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
# ドメイン一覧を取得
domains = get_domains(TENANCY_OCID)
for domain in domains:
domain_name = domain.get('display-name', '')
domain_url = domain.get('url', '')
if not domain_url:
continue
print(f"Processing domain: {domain_name}")
# ユーザー一覧を取得
users = get_users(domain_url)
for user in users:
user_id = user.get('id', '')
user_display_name = user.get('display-name', '')
user_email = get_email(user.get('emails', []))
user_ocid = user.get('ocid', '')
# ユーザーのメタデータ
meta = user.get('meta', {})
user_created = format_datetime(meta.get('created', ''))
user_last_modified = format_datetime(meta.get('last-modified', ''))
# APIキー一覧を取得
api_keys = get_api_keys(domain_url, user_id)
if api_keys:
for api_key in api_keys:
fingerprint = api_key.get('fingerprint', '')
api_key_ocid = api_key.get('ocid', '')
# APIキーのメタデータ
api_meta = api_key.get('meta', {})
api_created = format_datetime(api_meta.get('created', ''))
api_last_modified = format_datetime(api_meta.get('last-modified', ''))
# CSVに書き込み
writer.writerow({
'Domain Name': domain_name,
'User Display Name': user_display_name,
'User Email': user_email,
'User ID': user_id,
'User OCID': user_ocid,
'User Created': user_created,
'User Last Modified': user_last_modified,
'API Key Fingerprint': fingerprint,
'API Key OCID': api_key_ocid,
'API Key Created': api_created,
'API Key Last Modified': api_last_modified
})
else:
# APIキーがないユーザーの情報も書き込み
writer.writerow({
'Domain Name': domain_name,
'User Display Name': user_display_name,
'User Email': user_email,
'User ID': user_id,
'User OCID': user_ocid,
'User Created': user_created,
'User Last Modified': user_last_modified,
'API Key Fingerprint': '',
'API Key OCID': '',
'API Key Created': '',
'API Key Last Modified': ''
})
print(f"User and API key information has been written to {output_file}")
if __name__ == "__main__":
main()
以下は、取得した情報を CSV 形式で出力した例です:
Domain Name,User Display Name,User Email,User Created,User Last Modified,API Key Fingerprint,API Key Created,API Key Last Modified
Default,管理者ユーザー,admin@example.com,2021-10-15 09:30:45 JST,2023-01-20 14:22:18 JST,a1:b2:c3:d4:...,2022-04-10 11:45:22 JST,2022-04-10 11:45:22 JST
Default,開発者ユーザー,dev@example.com,2022-03-05 16:10:33 JST,2023-02-15 10:05:41 JST,e5:f6:g7:h8:...,2022-06-18 08:30:15 JST,2023-01-05 17:22:40 JST
DevDomain,テスト用ユーザー,test@example.com,2022-05-22 11:22:08 JST,2022-12-03 13:45:29 JST,,,
ProdDomain,運用ユーザー,ops@example.com,2022-01-30 08:15:27 JST,2023-03-10 09:50:33 JST,i9:j0:k1:l2:...,2022-02-05 14:30:18 JST,2022-11-12 16:40:22 JST
この CSV データを分析することで、 現存しているAPI キーや、不要になったユーザーアカウントを特定できます。
活用シーン
今回CLIによる情報取得を行いましたが、この情報が何に使えるか考えてみました。
-
定期的なセキュリティレビュー
月次・四半期ごとの棚卸作業を効率化します。特に、大規模な環境では手動で棚卸は実質的に不可能なので自動化したいところです。 -
監査対応
「いつ・誰が・どのような権限を持っているか」の証跡として記録を残せます。コンプライアンス要件を満たすためのドキュメントとしても活用できます。 -
組織変更時の引継ぎ
退職者のアカウント・アクセス権の確認と削除に役立ちます。組織変更時のアクセス権整理も効率的に行えます。 -
複数環境の統合管理
開発・検証・本番など複数ドメインのアカウント管理を一元的に行えます。
環境のアカウントやAPIキーを整理したい場合に使えそうですね。
まとめ
今回はOCI CLI を使用したアカウント棚卸方法についてまとめました。
参考になれば幸いです。
アカウントやAPIに限らず、権限の定期的な棚卸はセキュリティリスクを低減できます。
こういった継続的な取り組みが必要な業務はできるだけ自動化してコスト削減していきたいですね。