Skip to content

Commit

Permalink
Merge pull request #98 from almenscorner/dev
Browse files Browse the repository at this point in the history
v1.4.0
  • Loading branch information
almenscorner authored Mar 8, 2023
2 parents d4a3429 + 9f56c54 commit 58ccb79
Show file tree
Hide file tree
Showing 27 changed files with 660 additions and 150 deletions.
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = IntuneCD
version = 1.3.5
version = 1.4.0
author = Tobias Almén
author_email = [email protected]
description = Tool to backup and update configurations in Intune
Expand Down
98 changes: 98 additions & 0 deletions src/IntuneCD/assignment_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/env python3

"""
This module creates a report of all groups found and their assginment.
"""

import os
import platform

from .save_output import save_output
from .check_file import check_file
from .load_file import load_file


def get_group_report(path, output):
"""
This function is used to get a report of all groups and what they are assigned to.
Args:
path (str): Path to save the report to
output (str): Output format for the report
"""

report_path = f"{path}/Assignment Report/"

def process_file(path, name, payload_type, groups):
file_check = check_file(path, name)
if file_check:
with open(os.path.join(path, name), "r") as f:
data = load_file(name, f)
if type(data) is dict and data.get("assignments"):
for assignment in data["assignments"]:
if assignment["target"].get("groupName"):
if data.get("displayName"):
name = data["displayName"]
elif data.get("name"):
name = data["name"]
data = {
"groupName": assignment["target"]["groupName"],
"groupType": assignment["target"].get("groupType"),
"membershipRule": assignment["target"].get(
"membershipRule", None
),
"assignedTo": {},
}

payload_added = False # flag to track whether payload_type has been added

if not groups:
groups.append(data)
data["assignedTo"][payload_type] = [name]
payload_added = True
else:
for item in groups:
if item["groupName"] == data["groupName"]:
if not payload_added and item["assignedTo"].get(
payload_type
):
item["assignedTo"][payload_type].append(
name
)
payload_added = True
elif not payload_added and not item[
"assignedTo"
].get(payload_type):
item["assignedTo"][payload_type] = [name]
payload_added = True

if not payload_added:
data["assignedTo"][payload_type] = [name]
groups.append(data)

def collect_groups(path):
groups = []
slash = "/"
run_os = platform.uname().system
if run_os == "Windows":
slash = "\\"
abs_path = os.path.abspath(path)
for root, dirs, files in os.walk(path, topdown=True):
abs_root = os.path.abspath(root)
for file in files:
os.path.abspath(root)
payload_type = abs_root.replace(abs_path, "").split(slash)
if len(payload_type) > 1:
payload_type = payload_type[1]
process_file(
str(root),
file,
payload_type,
groups,
)
return groups

groups = collect_groups(path)

if groups:
save_output(output, report_path, "report", groups)
13 changes: 9 additions & 4 deletions src/IntuneCD/backup_appConfiguration.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
from .remove_keys import remove_keys

# Set MS Graph endpoint
ENDPOINT = "https://graph.microsoft.com/beta/deviceAppManagement/mobileAppConfigurations"
ENDPOINT = (
"https://graph.microsoft.com/beta/deviceAppManagement/mobileAppConfigurations"
)
APP_ENDPOINT = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps"


Expand All @@ -31,8 +33,9 @@ def savebackup(path, output, exclude, token):
data = makeapirequest(ENDPOINT, token)

if data["value"]:

assignment_responses = batch_assignment(data, "deviceAppManagement/mobileAppConfigurations/", "/assignments", token)
assignment_responses = batch_assignment(
data, "deviceAppManagement/mobileAppConfigurations/", "/assignments", token
)

for profile in data["value"]:
config_count += 1
Expand All @@ -58,7 +61,9 @@ def savebackup(path, output, exclude, token):
print("Backing up App Configuration: " + profile["displayName"])

# Get filename without illegal characters
fname = clean_filename(f"{profile['displayName']}_{str(profile['@odata.type'].split('.')[2])}")
fname = clean_filename(
f"{profile['displayName']}_{str(profile['@odata.type'].split('.')[2])}"
)
# Save App Configuration as JSON or YAML depending on configured value
# in "-o"
save_output(output, configpath, fname, profile)
Expand Down
12 changes: 9 additions & 3 deletions src/IntuneCD/backup_powershellScripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,19 @@ def savebackup(path, output, exclude, token):
for script in data["value"]:
script_ids.append(script["id"])

assignment_responses = batch_assignment(data, "deviceManagement/intents/", "/assignments", token)
script_data_responses = batch_request(script_ids, "deviceManagement/deviceManagementScripts/", "", token)
assignment_responses = batch_assignment(
data, "deviceManagement/deviceManagementScripts/", "/assignments", token
)
script_data_responses = batch_request(
script_ids, "deviceManagement/deviceManagementScripts/", "", token
)

for script_data in script_data_responses:
config_count += 1
if "assignments" not in exclude:
assignments = get_object_assignment(script_data["id"], assignment_responses)
assignments = get_object_assignment(
script_data["id"], assignment_responses
)
if assignments:
script_data["assignments"] = assignments

Expand Down
12 changes: 9 additions & 3 deletions src/IntuneCD/backup_shellScripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@

# Set MS Graph endpoint
ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceShellScripts/"
ASSIGNMENT_ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts"
ASSIGNMENT_ENDPOINT = (
"https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts"
)


# Get all Shell scripts and save them in specified path
Expand All @@ -36,8 +38,12 @@ def savebackup(path, output, exclude, token):
for script in data["value"]:
script_ids.append(script["id"])

assignment_responses = batch_assignment(data, "deviceManagement/deviceManagementScripts/", "/assignments", token)
script_data_responses = batch_request(script_ids, "deviceManagement/deviceShellScripts/", "", token)
assignment_responses = batch_assignment(
data, "deviceManagement/deviceShellScripts/", "?$expand=assignments", token
)
script_data_responses = batch_request(
script_ids, "deviceManagement/deviceShellScripts/", "", token
)

for script_data in script_data_responses:
config_count += 1
Expand Down
94 changes: 80 additions & 14 deletions src/IntuneCD/graph_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ def batch_request(data, url, extra_url, token, method="GET") -> list:

# POST to the graph batch endpoint
json_data = json.dumps(query_data)
request = makeapirequestPost("https://graph.microsoft.com/beta/$batch", token, jdata=json_data)
request = makeapirequestPost(
"https://graph.microsoft.com/beta/$batch", token, jdata=json_data
)
request_data = sorted(request["responses"], key=lambda item: item.get("id"))

# Append each successful request to responses list
Expand Down Expand Up @@ -69,9 +71,15 @@ def batch_assignment(data, url, extra_url, token, app_protection=False) -> list:
# If getting App Protection Assignments, get the platform
if app_protection is True:
for id in data["value"]:
if id["@odata.type"] == "#microsoft.graph.mdmWindowsInformationProtectionPolicy":
if (
id["@odata.type"]
== "#microsoft.graph.mdmWindowsInformationProtectionPolicy"
):
data_ids.append(f"mdmWindowsInformationProtectionPolicies/{id['id']}")
if id["@odata.type"] == "#microsoft.graph.windowsInformationProtectionPolicy":
if (
id["@odata.type"]
== "#microsoft.graph.windowsInformationProtectionPolicy"
):
data_ids.append(f"windowsInformationProtectionPolicies/{id['id']}")
else:
data_ids.append(f"{str(id['@odata.type']).split('.')[2]}s/{id['id']}")
Expand All @@ -83,6 +91,17 @@ def batch_assignment(data, url, extra_url, token, app_protection=False) -> list:
if data_ids:
responses = batch_request(data_ids, url, extra_url, token)
if responses:
if extra_url == "?$expand=assignments":
response_values = []
for value in responses:
response_values.append(
{
"value": value["assignments"],
"@odata.context": value["[email protected]"],
}
)
responses = response_values

group_ids = [
val
for list in responses
Expand All @@ -105,25 +124,49 @@ def batch_assignment(data, url, extra_url, token, app_protection=False) -> list:

# Batch get name of the groups
if group_ids:
group_responses = batch_request(group_ids, "groups/", "?$select=displayName,id", token)
group_responses = batch_request(
group_ids,
"groups/",
"?$select=displayName,id,groupTypes,membershipRule",
token,
)
for value in responses:
if value["value"]:
for val in value["value"]:
if "groupId" in val["target"]:
for id in group_responses:
if id["id"] == val["target"]["groupId"]:
val["target"]["groupName"] = id["displayName"]
if "DynamicMembership" in id["groupTypes"]:
val["target"]["groupType"] = "DynamicMembership"
val["target"]["membershipRule"] = id[
"membershipRule"
]
else:
val["target"]["groupType"] = "StaticMembership"

# Batch get name of the Filters
if filter_ids:
filter_responses = batch_request(filter_ids, "deviceManagement/assignmentFilters/", "?$select=displayName", token)
filter_responses = batch_request(
filter_ids,
"deviceManagement/assignmentFilters/",
"?$select=displayName",
token,
)
for value in responses:
if value["value"]:
for val in value["value"]:
if "deviceAndAppManagementAssignmentFilterId" in val["target"]:
for id in filter_responses:
if id["id"] == val["target"]["deviceAndAppManagementAssignmentFilterId"]:
val["target"]["deviceAndAppManagementAssignmentFilterId"] = id["displayName"]
if (
id["id"]
== val["target"][
"deviceAndAppManagementAssignmentFilterId"
]
):
val["target"][
"deviceAndAppManagementAssignmentFilterId"
] = id["displayName"]

return responses

Expand All @@ -145,20 +188,28 @@ def batch_intents(data, token) -> dict:
intent_values = {"value": []}

# Get each template ID
filtered_data = [val for list in data["value"] for key, val in list.items() if "templateId" in key and val is not None]
filtered_data = [
val
for list in data["value"]
for key, val in list.items()
if "templateId" in key and val is not None
]
template_ids = list(dict.fromkeys(filtered_data))

# Batch get all categories from templates
if template_ids:
categories_responses = batch_request(template_ids, f"{base_url}/templates/", "/categories", token)
categories_responses = batch_request(
template_ids, f"{base_url}/templates/", "/categories", token
)

# Build ID for requesting settings for each Intent
if categories_responses:
for intent in data["value"]:
settings_ids = [
val
for list in categories_responses
if intent["templateId"] is not None and intent["templateId"] in list["@odata.context"]
if intent["templateId"] is not None
and intent["templateId"] in list["@odata.context"]
for val in list["value"]
for keys, val in val.items()
if "id" in keys
Expand All @@ -168,13 +219,18 @@ def batch_intents(data, token) -> dict:

# Batch get all settings for all Intents
if settings_id:
settings_responses = batch_request(settings_id, f"{base_url}/intents/", "/settings", token)
settings_responses = batch_request(
settings_id, f"{base_url}/intents/", "/settings", token
)

# If the Intent ID is in the responses, save the settings to settingsDelta for the Intent
if settings_responses:
for intent in data["value"]:
settingsDelta = [
val for list in settings_responses if intent["id"] in list["@odata.context"] for val in list["value"]
val
for list in settings_responses
if intent["id"] in list["@odata.context"]
for val in list["value"]
]
intent_values["value"].append(
{
Expand All @@ -200,7 +256,12 @@ def get_object_assignment(id, responses) -> list:
"""

remove_keys = {"id", "groupId", "sourceId"}
assignments_list = [val for list in responses if id in list["@odata.context"] for val in list["value"]]
assignments_list = [
val
for list in responses
if id in list["@odata.context"]
for val in list["value"]
]
for value in assignments_list:
for k in remove_keys:
value.pop(k, None)
Expand All @@ -218,5 +279,10 @@ def get_object_details(id, responses) -> list:
:return: List of details for the object
"""

details = [val for list in responses if id in list["@odata.context"] for val in list["value"]]
details = [
val
for list in responses
if id in list["@odata.context"]
for val in list["value"]
]
return details
Loading

0 comments on commit 58ccb79

Please sign in to comment.