Skip to content

Commit

Permalink
Merge pull request #323 from ropable/master
Browse files Browse the repository at this point in the history
Conditionally allow skip of Microsoft licence availability check
  • Loading branch information
ropable authored Nov 6, 2023
2 parents 8e4e74d + 763e381 commit e0df562
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 44 deletions.
3 changes: 3 additions & 0 deletions itassets/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@

# Threshold value below which to warn Service Desk about available Microsoft licenses.
LICENCE_NOTIFY_THRESHOLD = env('LICENCE_NOTIFY_THRESHOLD', 5)
# Flag to enable checking for available M365 licence availability before creating users (default True).
# We may need to override this if MS Graph results are incorrect.
CHECK_AVAILABLE_LICENCES = env('CHECK_AVAILABLE_LICENCES', True)

# Flag to control whether Azure AD accounts should be deactivated during sync
# processes if their associated job in Ascender has a termination date in the past.
Expand Down
9 changes: 8 additions & 1 deletion kustomize/base/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,12 @@ spec:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
readOnlyRootFilesystem: true
volumeMounts:
- mountPath: /tmp
name: tmpfs-ram
volumes:
- name: tmpfs-ram
emptyDir:
medium: "Memory"
restartPolicy: Always
67 changes: 35 additions & 32 deletions organisation/ascender.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,8 @@ def ascender_db_import(employee_iter=None):

# If we have everything we need, embark on setting up the new user account.
if cc and job_start_date and licence_type and manager and location:
create_ad_user_account(job, cc, job_start_date, licence_type, manager, location, ascender_record, job_end_date, token)
check_available_licences = settings.CHECK_AVAILABLE_LICENCES
create_ad_user_account(job, cc, job_start_date, licence_type, manager, location, ascender_record, job_end_date, token, check_available_licences)


def ascender_user_import(employee_id, ignore_job_start_date=False):
Expand Down Expand Up @@ -510,12 +511,13 @@ def ascender_user_import(employee_id, ignore_job_start_date=False):

# If we have everything we need, embark on setting up the new user account.
if cc and job_start_date and licence_type and manager and location:
user = create_ad_user_account(job, cc, job_start_date, licence_type, manager, location, ascender_record, job_end_date, token)
check_available_licences = settings.CHECK_AVAILABLE_LICENCES
user = create_ad_user_account(job, cc, job_start_date, licence_type, manager, location, ascender_record, job_end_date, token, check_available_licences)

return user


def create_ad_user_account(job, cc, job_start_date, licence_type, manager, location, ascender_record, job_end_date, token=None):
def create_ad_user_account(job, cc, job_start_date, licence_type, manager, location, ascender_record, job_end_date, token=None, check_available_licences=True):
"""Function to create new Azure/onprem AD user accounts, based on passed-in user info.
Returns the associated DepartmentUser object, or None.
Expand Down Expand Up @@ -588,35 +590,36 @@ def create_ad_user_account(job, cc, job_start_date, licence_type, manager, locat
return
title = title_except(job['occup_pos_title'])

# We need to have an available M365 licence to allocate.
LOGGER.info("Querying Microsoft 365 licence availability")
e5_sku = ms_graph_subscribed_sku(MS_PRODUCTS['MICROSOFT 365 E5'], token)
f3_sku = ms_graph_subscribed_sku(MS_PRODUCTS['MICROSOFT 365 F3'], token)
eo_sku = ms_graph_subscribed_sku(MS_PRODUCTS['EXCHANGE ONLINE (PLAN 2)'], token)
sec_sku = ms_graph_subscribed_sku(MS_PRODUCTS['MICROSOFT 365 SECURITY AND COMPLIANCE FOR FLW'], token)
if job['licence_type'] == 'ONPUL':
licence_type = 'On-premise'
# Short circuit: no available licences, abort.
if e5_sku['consumedUnits'] >= e5_sku['prepaidUnits']['enabled']:
log = f"Creation of new Azure AD account aborted, no E5 licences available ({ascender_record})"
LOGGER.warning(log)
return
elif job['licence_type'] == 'CLDUL':
licence_type = 'Cloud'
# Short circuit: no available licences, abort.
if f3_sku['consumedUnits'] >= f3_sku['prepaidUnits']['enabled']:
log = f"Creation of new Azure AD account aborted, no Cloud F3 licences available ({ascender_record})"
LOGGER.warning(log)
return
if eo_sku['consumedUnits'] >= eo_sku['prepaidUnits']['enabled']:
log = f"Creation of new Azure AD account aborted, no Cloud Exchange Online licences available ({ascender_record})"
LOGGER.warning(log)
return
if sec_sku['consumedUnits'] >= sec_sku['prepaidUnits']['enabled']:
log = f"Creation of new Azure AD account aborted, no Cloud Security & Compliance licences available ({ascender_record})"
LOGGER.warning(log)
return
LOGGER.info("Sufficient licences are available")
# We may check if there is available M365 licence(s) to allocate. We might also skip this check.
if check_available_licences:
LOGGER.info("Querying Microsoft 365 licence availability")
e5_sku = ms_graph_subscribed_sku(MS_PRODUCTS["MICROSOFT 365 E5"], token)
f3_sku = ms_graph_subscribed_sku(MS_PRODUCTS["MICROSOFT 365 F3"], token)
eo_sku = ms_graph_subscribed_sku(MS_PRODUCTS["EXCHANGE ONLINE (PLAN 2)"], token)
sec_sku = ms_graph_subscribed_sku(MS_PRODUCTS["MICROSOFT 365 SECURITY AND COMPLIANCE FOR FLW"], token)
if job["licence_type"] == "ONPUL":
licence_type = "On-premise"
# Short circuit: no available licences, abort.
if e5_sku["consumedUnits"] >= e5_sku["prepaidUnits"]["enabled"]:
log = f"Creation of new Azure AD account aborted, no E5 licences available ({ascender_record})"
LOGGER.warning(log)
return
elif job["licence_type"] == "CLDUL":
licence_type = "Cloud"
# Short circuit: no available licences, abort.
if f3_sku["consumedUnits"] >= f3_sku["prepaidUnits"]["enabled"]:
log = f"Creation of new Azure AD account aborted, no Cloud F3 licences available ({ascender_record})"
LOGGER.warning(log)
return
if eo_sku["consumedUnits"] >= eo_sku["prepaidUnits"]["enabled"]:
log = f"Creation of new Azure AD account aborted, no Cloud Exchange Online licences available ({ascender_record})"
LOGGER.warning(log)
return
if sec_sku["consumedUnits"] >= sec_sku["prepaidUnits"]["enabled"]:
log = f"Creation of new Azure AD account aborted, no Cloud Security & Compliance for FLW licences available ({ascender_record})"
LOGGER.warning(log)
return
LOGGER.info("Sufficient licences are available")

# Configuration setting to explicitly allow creation of new AD users.
if settings.ASCENDER_CREATE_AZURE_AD:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import logging
import os
import pysftp
from tempfile import NamedTemporaryFile

from organisation.models import DepartmentUser
from organisation.utils import department_user_ascender_sync
Expand All @@ -16,8 +15,6 @@ def handle(self, *args, **options):
logger.info('Generating CSV of department user data')
users = DepartmentUser.objects.filter(employee_id__isnull=False).order_by('employee_id')
data = department_user_ascender_sync(users)
f = NamedTemporaryFile()
f.write(data.getbuffer())

host = os.environ.get('ASCENDER_SFTP_HOSTNAME')
port = int(os.environ.get('ASCENDER_SFTP_PORT'))
Expand All @@ -30,5 +27,5 @@ def handle(self, *args, **options):
dir = os.environ.get('ASCENDER_SFTP_DIRECTORY')
sftp.chdir(dir)
logger.info('Uploading CSV to Ascender SFTP')
sftp.put(localpath=f.name, remotepath='department_users_details.csv')
sftp.putfo(data, remotepath='department_users_details.csv')
sftp.close()
5 changes: 3 additions & 2 deletions organisation/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,8 @@ def parse_windows_ts(s):


def department_user_ascender_sync(users):
"""For a passed-in queryset of Department Users and a file-like object, return a CSV containing
data that should be synced to Ascender.
"""For a passed-in queryset of Department Users and a file-like object, returns a file-like
object of CSV data that should be synced to Ascender.
"""
f = BytesIO()
writer = csv.writer(f, quoting=csv.QUOTE_ALL, encoding='utf-8')
Expand All @@ -383,6 +383,7 @@ def department_user_ascender_sync(users):
user.telephone,
user.get_licence(),
])
f.seek(0)
return f


Expand Down
8 changes: 4 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ license = "Apache-2.0"

[tool.poetry.dependencies]
python = "^3.10"
django = "3.2.22"
django = "3.2.23"
psycopg2 = "2.9.6"
dbca-utils = "1.1.6"
django-extensions = "3.2.3"
Expand Down

0 comments on commit e0df562

Please sign in to comment.