Skip to content

Commit

Permalink
Merge pull request #165 from almenscorner/feature-roles
Browse files Browse the repository at this point in the history
2.0.9.b2
  • Loading branch information
almenscorner authored Feb 7, 2024
2 parents 5424e3d + cf97a52 commit bf39ea4
Show file tree
Hide file tree
Showing 9 changed files with 418 additions and 35 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 = 2.0.9.beta1
version = 2.0.9.beta2
author = Tobias Almén
author_email = [email protected]
description = Tool to backup and update configurations in Intune
Expand Down
46 changes: 45 additions & 1 deletion src/IntuneCD/backup/Intune/backup_roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,31 @@ def savebackup(path, output, exclude, token, append_id):
:param token: Token to use for authenticating the request
"""

def _get_group_names(obj):
"""
This function gets the group name from the object.
:param object: The object to get the group name from.
:return: The group name.
"""

groups = []

for group in obj:
group_name = makeapirequest(
f"https://graph.microsoft.com/beta/groups/{group}",
token,
"?$select=displayName",
)["displayName"]

groups.append(group_name)

return groups

results = {"config_count": 0, "outputs": []}
configpath = path + "/" + "Roles/"
data = makeapirequest(ENDPOINT, token)
q_param = {"$filter": "isBuiltIn eq false"}
data = makeapirequest(ENDPOINT, token, q_param)

for role in data["value"]:
results["config_count"] += 1
Expand All @@ -47,8 +69,30 @@ def savebackup(path, output, exclude, token, append_id):

role["roleAssignments"].append(role_assignment)

# Get the scopeMembers and resourceScopes ids
scope_member_names = ""
member_names = ""
for assignment in role["roleAssignments"]:
remove_keys(assignment)
if assignment.get("scopeMembers"):
scope_member_names = _get_group_names(
assignment["scopeMembers"]
)

if scope_member_names:
assignment["scopeMembers"] = scope_member_names
assignment.pop("resourceScopes", None)

for assignment in role["roleAssignments"]:
if assignment.get("members"):
member_names = _get_group_names(assignment["members"])

assignment["members"] = member_names

graph_id = role["id"]
role = remove_keys(role)
role.pop("permissions", None)
role["rolePermissions"][0].pop("actions", None)

# Get filename without illegal characters
fname = clean_filename(role["displayName"])
Expand Down
2 changes: 1 addition & 1 deletion src/IntuneCD/backup_intune.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def backup_intune(results, path, output, exclude, token, prefix, append_id, args
if args.activationlock:
from .backup.Intune.backup_activationLock import savebackup

results.append(savebackup(path, output, token))
savebackup(path, output, token)

if "AppConfigurations" not in exclude:
from .backup.Intune.backup_appConfiguration import savebackup
Expand Down
21 changes: 11 additions & 10 deletions src/IntuneCD/document_intune.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,6 @@ def document_intune(configpath, outpath, maxlength, split, cleanup, decode):
This function is used to document Intune configuration.
"""

document_configs(
f"{configpath}/Roles",
outpath,
"Roles",
maxlength,
split,
cleanup,
decode,
)

# Document App Configuration
document_configs(
f"{configpath}/App Configuration",
Expand Down Expand Up @@ -376,3 +366,14 @@ def document_intune(configpath, outpath, maxlength, split, cleanup, decode):
cleanup,
decode,
)

# Document Roles
document_configs(
f"{configpath}/Roles",
outpath,
"Roles",
maxlength,
split,
cleanup,
decode,
)
1 change: 1 addition & 0 deletions src/IntuneCD/run_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def start():
"DeviceManagementSettings",
"CustomAttributes",
"DeviceCategories",
"Roles",
"entraAuthenticationFlowsPolicy",
"entraAuthenticationMethods",
"entraAuthorizationPolicy",
Expand Down
132 changes: 132 additions & 0 deletions src/IntuneCD/update/Intune/update_roles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
This module is used to update all Roles in Intune.
"""

import json
import os

from deepdiff import DeepDiff

from ...intunecdlib.check_file import check_file
from ...intunecdlib.diff_summary import DiffSummary
from ...intunecdlib.graph_request import (
makeapirequest,
makeapirequestDelete,
makeapirequestPatch,
makeapirequestPost,
)
from ...intunecdlib.load_file import load_file
from ...intunecdlib.remove_keys import remove_keys

# Set MS Graph endpoint
ENDPOINT = "https://graph.microsoft.com/beta/deviceManagement/roleDefinitions"


def update(path, token, report, remove=False):
"""
This function updates all Roles in Intune if the configuration in Intune differs from the JSON/YAML file.
:param path: Path to where the backup is saved
:param token: Token to use for authenticating the request
"""

diff_summary = []
# Set Roles path
configpath = path + "/" + "Roles"
# If App Configuration path exists, continue
if os.path.exists(configpath):
# get all Roles
q_param = {"$filter": "isBuiltIn eq false"}
mem_data = makeapirequest(ENDPOINT, token, q_param)

for filename in os.listdir(configpath):
file = check_file(configpath, filename)
if file is False:
continue
# Check which format the file is saved as then open file, load data
# and set query parameter
with open(file, encoding="utf-8") as f:
repo_data = load_file(filename, f)

role_value = {}

# If Filter exists, continue
if mem_data["value"]:
for val in mem_data["value"]:
if repo_data["displayName"] == val["displayName"]:
role_value = val
mem_data["value"].remove(val)

if role_value:
print("-" * 90)
role_id = role_value["id"]
role_value = remove_keys(role_value)

repo_data.pop("roleAssignments", None)
role_value.pop("permissions", None)
role_value["rolePermissions"][0].pop("actions", None)

diff = DeepDiff(
role_value,
repo_data,
ignore_order=True,
exclude_paths="root['rolePermissions']",
).get("values_changed", {})

actions_diff = DeepDiff(
role_value["rolePermissions"][0]["resourceActions"],
repo_data["rolePermissions"][0]["resourceActions"],
ignore_order=True,
)

# If any changed values are found, push them to Intune
if diff or actions_diff and report is False:
repo_data.pop("isBuiltInRoleDefinition", None)
repo_data.pop("isBuiltIn", None)
request_data = json.dumps(repo_data)
makeapirequestPatch(
ENDPOINT + "/" + role_id,
token,
q_param=None,
jdata=request_data,
)

diff_config = DiffSummary(
data=diff,
name=repo_data["displayName"],
type="Role",
)

if actions_diff:
print("Resource Actions changed, check commit history for details")

diff_summary.append(diff_config)

# If Filter does not exist, create it
else:
print("-" * 90)
print("Role not found, creating role: " + repo_data["displayName"])
if report is False:
request_json = json.dumps(repo_data)
post_request = makeapirequestPost(
ENDPOINT,
token,
q_param=None,
jdata=request_json,
)
print("Role created with id: " + post_request["id"])

# If any Roles are left in mem_data, remove them from Intune as they are not in the repo
if mem_data.get("value", None) is not None and remove is True:
for val in mem_data["value"]:
print("-" * 90)
print("Removing Role from Intune: " + val["displayName"])
if report is False:
makeapirequestDelete(
f"{ENDPOINT}/{val['id']}", token, status_code=200
)

return diff_summary
6 changes: 6 additions & 0 deletions src/IntuneCD/update_intune.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def update_intune(
"""
Imports all the update functions and runs them
"""

if "AppConfigurations" not in exclude:
from .update.Intune.update_appConfiguration import update

Expand Down Expand Up @@ -167,3 +168,8 @@ def update_intune(
diff_summary.append(
update(path, token, assignment, report, create_groups, remove)
)

if "Roles" not in exclude:
from .update.Intune.update_roles import update

diff_summary.append(update(path, token, report, remove))
72 changes: 50 additions & 22 deletions tests/Backup/Intune/test_backup_roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,54 @@ def setUp(self):
self.expected_data = {
"@odata.type": "#microsoft.graph.deviceAndAppManagementRoleDefinition",
"displayName": "test",
"description": "test.",
"isBuiltInRoleDefinition": True,
"isBuiltIn": True,
"roleScopeTagIds": [],
"permissions": [
"description": "",
"isBuiltInRoleDefinition": False,
"isBuiltIn": False,
"roleScopeTagIds": ["0"],
"rolePermissions": [
{
"actions": ["test"],
"resourceActions": ["test"],
"resourceActions": [
{
"allowedResourceActions": [
"Microsoft.Intune_DeviceConfigurations_Read"
],
"notAllowedResourceActions": [],
}
]
}
],
"roleAssignments": [
{"id": "test", "roleAssignments": {"displayName": "test", "id": "test"}}
{
"displayName": "test",
"description": "",
"scopeMembers": ["admin"],
"scopeType": "resourceScope",
"members": ["test"],
}
],
}
self.role = {
"value": [
{
"@odata.type": "#microsoft.graph.deviceAndAppManagementRoleDefinition",
"id": "0",
"displayName": "test",
"description": "test.",
"isBuiltInRoleDefinition": True,
"isBuiltIn": True,
"roleScopeTagIds": [],
"permissions": [
"description": "",
"id": "0",
"isBuiltInRoleDefinition": False,
"isBuiltIn": False,
"roleScopeTagIds": ["0"],
"permissions": [],
"rolePermissions": [
{
"actions": ["test"],
"resourceActions": ["test"],
"actions": [],
"resourceActions": [
{
"allowedResourceActions": [
"Microsoft.Intune_DeviceConfigurations_Read"
],
"notAllowedResourceActions": [],
}
],
}
],
}
Expand All @@ -62,18 +82,26 @@ def setUp(self):
self.assignment = {
"value": [
{
"id": "test",
"roleAssignments": {
"id": "test",
"displayName": "test",
},
"id": "0",
"displayName": "test",
"description": "",
"scopeMembers": ["1"],
"scopeType": "resourceScope",
"resourceScopes": ["222"],
"members": ["111"],
}
]
}

self.patch_makeapirequest = patch(
"src.IntuneCD.backup.Intune.backup_roles.makeapirequest",
side_effect=[self.role, self.assignment, self.assignment["value"][0]],
side_effect=[
self.role,
self.assignment,
self.assignment["value"][0],
{"displayName": "admin"},
{"displayName": "test"},
],
)
self.makeapirequest = self.patch_makeapirequest.start()

Expand Down
Loading

0 comments on commit bf39ea4

Please sign in to comment.