diff --git a/.gitignore b/.gitignore index 82f9275..666bce7 100644 --- a/.gitignore +++ b/.gitignore @@ -160,3 +160,7 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +debug_app_registrations.json +app_registrations.html +.env +.env diff --git a/aio.py b/aio.py index b3e8d62..c4b67ff 100644 --- a/aio.py +++ b/aio.py @@ -78,6 +78,10 @@ def sort_app_registrations(app_registrations): if app["passwordCredentials"]: expiry_date_str = app["passwordCredentials"][0]["endDateTime"] try: + if '.' in expiry_date_str: + expiry_date_str = expiry_date_str.split('.')[0] + '.' + expiry_date_str.split('.')[1][:6] + 'Z' + if expiry_date_str.endswith('ZZ'): + expiry_date_str = expiry_date_str[:-1] expiry_date = datetime.strptime(expiry_date_str, '%Y-%m-%dT%H:%M:%S.%fZ').replace(tzinfo=timezone.utc) except ValueError: expiry_date = datetime.strptime(expiry_date_str, '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=timezone.utc) @@ -183,6 +187,10 @@ def generate_html(app_registrations): expiry_date = password_credentials[0].get('endDateTime') if expiry_date: try: + if '.' in expiry_date: + expiry_date = expiry_date.split('.')[0] + '.' + expiry_date.split('.')[1][:6] + 'Z' + if expiry_date.endswith('ZZ'): + expiry_date = expiry_date[:-1] expiry_date = datetime.strptime(expiry_date, '%Y-%m-%dT%H:%M:%S.%fZ').replace(tzinfo=timezone.utc) except ValueError: expiry_date = datetime.strptime(expiry_date, '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=timezone.utc) @@ -228,4 +236,4 @@ if __name__ == "__main__": # Write to HTML file for inspection html_output = generate_html(sorted_app_registrations) with open('app_registrations.html', 'w') as f: - f.write(html_output) + f.write(html_output) \ No newline at end of file diff --git a/azure_client.py b/azure_client.py index f5a7c1a..c4b67ff 100644 --- a/azure_client.py +++ b/azure_client.py @@ -4,6 +4,7 @@ import logging import json import msal import requests +from datetime import datetime, timezone # Load environment variables load_dotenv() @@ -71,8 +72,168 @@ def get_app_registrations(): logging.error(f"Error fetching app registrations: {e}") return [] +def sort_app_registrations(app_registrations): + current_date = datetime.now(timezone.utc) + for app in app_registrations: + if app["passwordCredentials"]: + expiry_date_str = app["passwordCredentials"][0]["endDateTime"] + try: + if '.' in expiry_date_str: + expiry_date_str = expiry_date_str.split('.')[0] + '.' + expiry_date_str.split('.')[1][:6] + 'Z' + if expiry_date_str.endswith('ZZ'): + expiry_date_str = expiry_date_str[:-1] + expiry_date = datetime.strptime(expiry_date_str, '%Y-%m-%dT%H:%M:%S.%fZ').replace(tzinfo=timezone.utc) + except ValueError: + expiry_date = datetime.strptime(expiry_date_str, '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=timezone.utc) + days_to_expiry = (expiry_date - current_date).days + app["days_to_expiry"] = days_to_expiry + app["expiry_date"] = expiry_date.isoformat() + else: + app["days_to_expiry"] = None + app["expiry_date"] = None + + sorted_apps = sorted(app_registrations, key=lambda x: (x["days_to_expiry"] is None, x["days_to_expiry"]), reverse=False) + return sorted_apps + +def generate_html(app_registrations): + current_time = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S') + html = f""" + +
+This is an automated notification regarding expiring Azure App Registrations that you own or manage.
+ +Why am I receiving this?
+ You are receiving this email because you are listed as an owner of one or more Azure App Registrations that are approaching their expiration date or have already expired.
Required Actions:
+Color Coding:
+If you need assistance, please contact the IT Support team.
+Exported on: {current_time}
+| Display Name | +Expiry Date | +Days to Expiry | +Owners | +
|---|---|---|---|
| {app['displayName']} | +{expiry_date.strftime('%Y-%m-%d')} | +{days_to_expiry} | +{owner_list} | +