ノート:
- このチュートリアルでは、Oracle Cloudへのアクセスが必要です。無料アカウントにサインアップするには、Oracle Cloud Infrastructure Free Tierの開始を参照してください。
- Oracle Cloud Infrastructureの資格証明、テナンシおよびコンパートメントに例の値を使用します。演習を完了するときは、これらの値をクラウド環境に固有の値に置き換えます。
Pythonを使用したUserStatusおよびLastSuccessfulLoginDateに基づくOCI IAMアイデンティティ・ドメインからの非アクティブなユーザーの削除
イントロダクション
Oracle Cloud Infrastructure(OCI)は、ストレージ、ネットワーキング、インフラストラクチャなど、さまざまなクラウドサービスを提供できるクラウド・プラットフォームです。Oracle Cloud Infrastructure Identity and Access Management (OCI IAM)は、OCIリソースへのアクセスを管理できるサービスです。ユーザーおよびグループに対する認証および認可を提供します。
OCI IAMアイデンティティ・ドメインで最後に成功したログイン日に基づいて非アクティブ・ユーザーを削除することは、いくつかの理由でOCIのユーザーごとの価格設定モデルのコンテキストで特に役立ちます:
-
コスト最適化: OCIはユーザーごとの価格設定モデルに従っています。つまり、ユーザー・アカウントごとに請求されます。非アクティブなユーザーを定期的に特定および削除することで、サブスクリプションのコストを大幅に削減できます。組織の規模が拡大し、ユーザー数が増加するにつれて、このコスト最適化はさらに重要になります。
-
セキュリティの拡張:非アクティブなユーザー・アカウントは潜在的なセキュリティ・リスクです。これらのアカウントは、正当なユーザーによって使用されなくなる可能性がありますが、無人のままにした場合でも、悪意のあるアクターによって悪用される可能性があります。非アクティブなユーザーを削除すると、承認された個人のみがOCIリソースにアクセスできるようになり、攻撃対象領域が削減され、全体的なセキュリティが向上します。
-
Resource Management:多数の非アクティブなユーザー・アカウントを管理することは、管理上負担になる可能性があります。これにより、IAM環境が混乱し、アクティブなユーザー・アカウントに集中することが難しくなります。非アクティブなユーザーを識別して削除するプロセスを自動化すると、管理リソースが解放され、チームはより戦略的なタスクに集中できます。
-
コンプライアンス:多くの場合、コンプライアンス標準では、組織はユーザー・アカウントを定期的に確認および管理する必要があります。非アクティブなユーザーを削除すると、コンプライアンス要件を満たし、データ・セキュリティとガバナンスに対するコミットメントを示すのに役立ちます。
このプロセスを効果的に実装するために、pythonスニペットの下では、最後の成功したログイン日とアクティブ/非アクティブ・ステータスに基づいて、非アクティブ・ユーザーの識別および削除を自動化できます。このようなスクリプトを定期的に実行することで、OCIのコストを最適化しながら、無駄のないセキュアなIAM環境を維持できます。
対象読者
IAMプロフェッショナルおよび管理者。
目標
Pythonを使用したREST APIを使用して、ユーザー・ステータスおよびlastSuccessfulLoginDateに基づいてユーザーをOCI IAMアイデンティティ・ドメインから削除します。
前提条件
-
アクティブなOCIサブスクリプション。
-
OCI IAMおよびPythonに精通しています。
-
OCI IAMアイデンティティ・ドメインREST APIの使用に関する知識が必要です。
-
アプリケーションを管理する権限を持つIAMユーザー(Identity Domain Administrator、セキュリティ管理者またはアプリケーション管理者)。
-
システムにインストールされているPython 3.x。
-
urllib3、requestsおよびdatetimePythonパッケージがインストールされています。
タスク1: OCI IAMアイデンティティ・ドメインでの機密アプリケーションの作成
Oracle Identity Cloud Service: 最初のREST APIコールに従って、機密アプリケーションを作成し、クライアントIDおよびクライアント・シークレットを取得します。これを使用して、アクセス・トークンおよび後続のAPIエンドポイントを取得するためにOCI IAMへのREST APIコールを実行できます。
タスク2: config.jsonファイルの設定
ローカル・マシンで構成ファイルを設定します。config.jsonファイルには、アクセス・トークンの生成に使用されるアイデンティティ・ドメインURL、クライアントIDおよびクライアント・シークレットに関する情報が含まれます。
{
"iamurl" : "https://idcs-###########.identity.oraclecloud.com",
"client_id" : "#######################",
"client_secret" : "#######################"
}
タスク3: アクセス・トークンの取得
config.jsonファイルが配置されたら、最初に実行する必要があるのは、アクセス・トークンを生成することです。これを使用して、OCI IAMエンドポイントへのREST APIコールをさらに実行できます。
次のコード・スニペットでは、関数get_encodedは引数としてクライアントIDとクライアント・シークレットを取り、base64-encoded文字列を返します。このエンコードされた文字列は、POSTリクエストを実行してアクセス・トークンを取得するために、認可ヘッダーとして関数get_access_tokenに引数としてさらに渡されます。
#get base64 encoded
def get_encoded(self,clid, clsecret):
encoded = clid + ":" + clsecret
baseencoded = base64.urlsafe_b64encode(encoded.encode('UTF-8')).decode('ascii')
return baseencoded
#get access token
def get_access_token(self,url, header):
para = "grant_type=client_credentials&scope=urn:opc:idm:__myscopes__"
response = requests.post(url, headers=header, data=para, verify=False)
jsonresp = json.loads(response.content)
access_token = jsonresp.get('access_token')
return access_token
#print access token
def printaccesstoken(self):
obj = IAM()
encodedtoken = obj.get_encoded(clientID, clientSecret)
extra = "/oauth2/v1/token"
headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
'Authorization': 'Basic %s' % encodedtoken, 'Accept': '*/*'}
accesstoken = obj.get_access_token(idcsURL + extra, headers)
return accesstoken
タスク4: ユーザー情報のバッチ(lastSuccessfulLoginDate、userStatus)を処理してユーザーをクリーンアップする
アクセス・トークンがあり、これを使用して、様々なOCI IAMアイデンティティ・ドメインRESTエンドポイントに対してさらにREST APIコールを実行できます。次のコード・スニペットは、/admin/v1/Usersエンドポイントに対して必要なヘッダーおよびパラメータを使用してGETリクエストを実行する方法を示しています。これにより、ユーザーの合計数を取得し、必要な反復数を計算し、結果の合計数とリクエスト当たりの件数に基づいて計算します。
-
この関数は、現在の日付をYYYY-MM-DD形式で取得し、変数
current_dateに格納し、各ユーザー・データを反復処理します。 -
反復ごとに、適切なパラメータを指定してGETリクエストを送信し、ユーザー情報(ユーザー名、lastSuccessfulLoginDateおよびアクティブ)のバッチを取得してから、いくつかの条件付きチェックを実行します。
-
次に、定義された条件付きチェックに基づいて、
username.txtおよびuserid.txtファイルで一致したすべてのユーザーのユーザー名およびユーザーIDをそれぞれ追加し、削除対象としてマークします。まず、ユーザー・ステータスをチェックし、False (inactiveを意味する)の場合は、ユーザーをtxtファイルに追加し、ループの次の反復に進みます。次に、ユーザーの状態をチェックし、「なし」の場合は、_{user}がアクセスしたことがないことを出力します。コンソールで手動で削除し、ループの次の反復に進むことを決定します。 -
このスクリプトは、ユーザーの最終正常ログイン日を取得し、最終ログインからの日数を計算してコンソールに出力します。90日を超えてログインしていないユーザーの場合、スクリプトは、ユーザーに特定の管理ロールが関連付けられているかどうかをチェックします。90日間ログインしておらず、管理ロールなしのユーザーは、識別されて削除対象としてマークされます。
-
すべてのユーザーの処理後、削除される一連のユーザーIDがJSONファイル
username.JSONに書き込まれます。一括削除リクエストは、データを特定のJSON形式でフォーマットすることで準備されます。次に、一括削除リクエストが/admin/v1/Bulkエンドポイントに送信され、識別されたユーザーが削除されたことを示すメッセージが出力されます。 -
すべての反復後、関数はメイン関数に戻ります。
ノート: システムにアクセスしたことのないユーザーの処理など、将来のアクションまたは手動決定の説明を含むコード・セクションがあります。管理者がOCI IAMにアクセスしていない可能性があり、削除したくない場合があります。
def get_successfullogindate(self): extra = "/admin/v1/Users" obj = IAM() accesstoken = obj.printaccesstoken() headers = {'Authorization': 'Bearer ' + accesstoken} resp = requests.get(idcsURL+extra, headers=headers, verify=False) jsonresp = json.loads(resp.content) totalCount = jsonresp.get("totalResults") print("Total number of users: " + str(totalCount)) current_date = datetime.now().date() startIndex = 1 count = 50 loop = int(totalCount / count) extra2 = "/admin/v1/AppRoles" for i in range(loop + 1): param = { 'attributes': "username,active,urn:ietf:params:scim:schemas:oracle:idcs:extension:userState:User:lastSuccessfulLoginDate", 'startIndex': startIndex, 'count': count} resp = requests.get(idcsURL+extra, headers=headers, verify=False, params=param) startIndex += count jsonresp = json.loads(resp.content) tempjsn = jsonresp.get("Resources") for x in tempjsn: trimjsn = {} username = trimjsn['userName'] = x.get("userName") userid = x.get("id") userStatus = x.get("active") userState = x.get("urn:ietf:params:scim:schemas:oracle:idcs:extension:userState:User") if userStatus is False: print(username + " is in inactive state. Will be deleted.") with open('username.txt', 'a') as c: c.write(username) c.write('\n') with open('userID.txt', 'a') as d: d.write(userid) d.write('\n') continue if userState is None: print(username + " has never accessed. Kindly make a decision to delete it manually on console") # with open('username.txt', 'a') as c: # c.write(username) # c.write('\n') # with open('userID.txt', 'a') as d: # d.write(userid) # d.write('\n') # ** Uncomment the lines commented above to delete the users who have never accessed ** continue lastLoginDate = x['urn:ietf:params:scim:schemas:oracle:idcs:extension:userState:User']['lastSuccessfulLoginDate'] target_dates = datetime.strptime(lastLoginDate, '%Y-%m-%dT%H:%M:%S.%fZ').date() num_days = (current_date - target_dates).days #print(username + " Last logged in " + str(num_days) + " days ago") if num_days > 30: #print("entered if condition for more than 90 days ") params2 = {"filter": f'members[value eq "{userid}"] and app.value eq "IDCSAppId" and (displayName eq "Identity Domain Administrator" or displayName eq "Security Administrator" or displayName eq "Application Administrator" or displayName eq "User Administrator" or displayName eq "User Manager" or displayName eq "Help Desk Administrator" or displayName eq "Audit Administrator")'} resp2 = requests.get(idcsURL+extra2, headers=headers, verify=False, params=params2) jsonresp3 = json.loads(resp2.content) totalCount1 = jsonresp3.get("totalResults") if totalCount1 < 1: print(username + " has not Logged in for 90 days and has NO admin role. Will be deleted.") with open('username.txt', 'a') as c: c.write(username) c.write('\n') with open('userID.txt', 'a') as d: d.write(userid) d.write('\n') try: with open('userID.txt','r') as g: lines = len(g.readlines()) except FileNotFoundError: print("No users found to be Deleted by the Script") exit() x=0 while x<lines: with open('userID.txt','r') as e: content = e.readlines() id=content[x] id=id.rstrip(id[-1]) paradelete=json.dumps( { "method": "DELETE", "path": "/Users/"+id+"?forceDelete=true" } ) with open('username.json', 'a') as j: j.writelines(paradelete) j.write(',') x=x+1 with open('username.json', 'r') as file: data = file.read() with open('formatedData.json', 'a') as file1: file1.writelines('{ \n') file1.writelines('"schemas": ["urn:ietf:params:scim:api:messages:2.0:BulkRequest"],\n') file1.writelines('"Operations": [\n') data2=data.rstrip(data[-1]) file1.write(data2) file1.write(']}') headers = {'Authorization': 'Bearer ' + accesstoken} bulkdata = json.load(open('formatedData.json')) param = {'forceDelete': True} payload = json.dumps(bulkdata) headers2 = {'Content-Type': 'application/json','Authorization': 'Bearer ' + accesstoken, 'Accept': '*/*'} extra2="/admin/v1/Bulk" #respdelete = requests.request("POST",idcsURL + extra2, headers=headers2, verify=False, params=param,data=payload) print("The identified users has been deleted ") try: os.remove('userID.txt') os.remove('username.txt') os.remove('formatedData.json') os.remove('username.json') except FileNotFoundError: exit()
タスク5: OCI Cloud Shellでのスクリプトの使用
スクリプトの準備ができたら、ローカル・マシン(Pythonインストール済)またはPython開発をサポートする任意のIDEで簡単に実行できます。OCI Cloud Shellを使用してスクリプトを実行し、目的のレポートを取得しています。
-
OCIコンソールにログインし、画面の右上隅からクラウド・シェルを開き、Pythonスクリプトおよび
config.jsonファイルをアップロードします。


-
python DeleteUser_PriorCheck_InactivePeriod60days_IsAdmin.pyを実行します。ノート:
DeleteUser_PriorCheck_InactivePeriod60days_IsAdmin.pyは、前述のコード・スニペットを使用して開発されたPythonスクリプトです。
関連リンク
確認
-
著者 - Gautam Mishra (シニア・クラウド・エンジニア)
-
貢献者 - Chetan Soni (クラウド・ソリューション・エンジニア)
その他の学習リソース
docs.oracle.com/learnの他のラボをご覧いただくか、Oracle Learning YouTubeチャネルで無料のラーニング・コンテンツにアクセスしてください。また、education.oracle.com/learning-explorerにアクセスしてOracle Learning Explorerになります。
製品ドキュメントは、Oracle Help Centerを参照してください。
Delete Inactive Users from OCI IAM Identity Domains Based on UserStatus and LastSuccessfulLoginDate Using Python
F89514-01
November 2023
Copyright © 2023, Oracle and/or its affiliates.