-
-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #98 from almenscorner/dev
v1.4.0
- Loading branch information
Showing
27 changed files
with
660 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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']}") | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
||
|
@@ -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 | ||
|
@@ -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( | ||
{ | ||
|
@@ -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) | ||
|
@@ -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 |
Oops, something went wrong.